diff options
Diffstat (limited to 'third_party')
513 files changed, 50924 insertions, 47765 deletions
diff --git a/third_party/webrender/Cargo.lock b/third_party/webrender/Cargo.lock index a4357db7a30..b7055733e57 100644 --- a/third_party/webrender/Cargo.lock +++ b/third_party/webrender/Cargo.lock @@ -4,29 +4,26 @@ name = "adler32" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" [[package]] name = "aho-corasick" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" dependencies = [ - "memchr", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "andrew" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" dependencies = [ - "bitflags", - "line_drawing", - "rusttype 0.7.9", - "walkdir", - "xdg", - "xml-rs", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusttype 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -38,1868 +35,2233 @@ source = "git+https://github.com/rust-windowing/android-rs-glue.git?rev=e3ac6ede name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" dependencies = [ - "winapi", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "app_units" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3ec9d4c47b25a5a9e5c848e053640331c7cedb1637434d75db68b79fee8a7f" dependencies = [ - "num-traits", - "serde", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "approx" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" dependencies = [ - "num-traits", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" dependencies = [ - "backtrace-sys", - "cfg-if 0.1.10", - "libc", - "rustc-demangle", + "backtrace-sys 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" dependencies = [ - "cc", - "libc", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "base64" -version = "0.12.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "binary-space-partition" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff" [[package]] name = "bincode" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" dependencies = [ - "byteorder", - "serde", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "block" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "build-parallel" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e3ff9db740167616e528c509b3618046fc05d337f8f3182d300f4aa977d2bb" dependencies = [ - "crossbeam-utils", - "jobserver", - "num_cpus", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "bytemuck" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431" [[package]] name = "byteorder" version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] -name = "cc" -version = "1.0.50" +name = "bytes" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" dependencies = [ - "jobserver", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "cfg-if" -version = "0.1.10" +name = "cc" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +dependencies = [ + "jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cgl" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" dependencies = [ - "gleam 0.6.19", - "libc", + "gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "chrono" version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" dependencies = [ - "num", - "time", + "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "clap" version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", - "yaml-rust 0.3.5", + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cmake" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" dependencies = [ - "cc", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cocoa" version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54" dependencies = [ - "bitflags", - "block", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "foreign-types", - "libc", - "objc", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "compositor" version = "0.1.0" dependencies = [ - "compositor-wayland", - "compositor-windows", - "gleam 0.13.1", - "webrender", -] - -[[package]] -name = "compositor-wayland" -version = "0.1.0" -dependencies = [ - "cc", - "pkg-config", + "compositor-windows 0.1.0", + "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", ] [[package]] name = "compositor-windows" version = "0.1.0" dependencies = [ - "cc", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "const_fn" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" - -[[package]] name = "core-foundation" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" dependencies = [ - "core-foundation-sys 0.6.2", - "libc", + "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" dependencies = [ - "core-foundation-sys 0.7.0", - "libc", + "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b5ed8e7e76c45974e15e41bfa8d5b0483cd90191639e01d8f5f1e606299d3fb" dependencies = [ - "core-foundation-sys 0.8.2", - "libc", + "core-foundation-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation-sys" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" [[package]] name = "core-foundation-sys" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" [[package]] name = "core-graphics" version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" dependencies = [ - "bitflags", - "core-foundation 0.6.4", - "foreign-types", - "libc", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-graphics" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6082396a349fa49674ba1bda4077332a18bf150e8fa75745ece07085e29a113" dependencies = [ - "bitflags", - "core-foundation 0.9.0", - "core-graphics-types", - "foreign-types", - "libc", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics-types 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-graphics-types" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e92f5d519093a4178296707dbaa3880eae85a5ef5386675f361a1cf25376e93c" dependencies = [ - "bitflags", - "core-foundation 0.9.0", - "foreign-types", - "libc", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-text" version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dfae50af11e72657fe7174cddb1ecddc5398037f7f6f39533ad69207c9a4e2" dependencies = [ - "core-foundation 0.9.0", - "core-graphics 0.22.0", - "foreign-types", - "libc", + "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crc32fast" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be" [[package]] -name = "crossbeam-channel" -version = "0.5.0" +name = "crossbeam-deque" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", + "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "crossbeam-deque" -version = "0.8.0" +name = "crossbeam-epoch" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.1" +name = "crossbeam-queue" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" dependencies = [ - "cfg-if 1.0.0", - "const_fn", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.8.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" dependencies = [ - "autocfg", - "cfg-if 1.0.0", - "lazy_static", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cstr" -version = "0.2.8" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cstr-macros 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cstr-macros" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c11a39d776a3b35896711da8a04dc1835169dcd36f710878187637314e47941b" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", + "procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "deflate" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e5d2a2273fed52a7f947ee55b092c4057025d7a3e04e5ecdbd25d6c3fb1bd7" dependencies = [ - "adler32", - "byteorder", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "derive_more" version = "0.99.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "direct-composition" +version = "0.1.0" +dependencies = [ + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.12.1 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "dlib" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" dependencies = [ - "libloading", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "downcast-rs" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6" [[package]] name = "dwrote" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" dependencies = [ - "lazy_static", - "libc", - "serde", - "serde_derive", - "winapi", - "wio", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "wio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "either" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" [[package]] name = "env_logger" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "etagere" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520d7de540904fd09b11c03a47d50a7ce4ff37d1aa763f454fa60d9088ef8356" dependencies = [ - "euclid", - "serde", - "svg_fmt", + "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "euclid" version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab0e07e345fb061928646949fdf5fb888e5d75a57385e7f5856e45be289e745" dependencies = [ - "num-traits", - "serde", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "expat-sys" version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" dependencies = [ - "cmake", - "pkg-config", + "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "font-loader" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49d6b4c11dca1a1dd931a34a9f397e2da91abe3de4110505f3530a80e560b52" dependencies = [ - "core-foundation 0.9.0", - "core-text", - "libc", - "servo-fontconfig", - "winapi", + "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-fontconfig 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "foreign-types-shared" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "freetype" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" dependencies = [ - "freetype-sys", - "libc", + "freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "freetype-sys" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" dependencies = [ - "cmake", - "libc", - "pkg-config", + "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "fxhash" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" dependencies = [ - "byteorder", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gl_generator" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a" dependencies = [ - "khronos_api", - "log", - "xml-rs", + "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gl_generator" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" dependencies = [ - "khronos_api", - "log", - "xml-rs", + "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gleam" version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" dependencies = [ - "gl_generator 0.13.1", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gleam" -version = "0.13.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "992c40589d382354958af7c60910026ed4cc3c58751355db21a4c2fbef15727a" dependencies = [ - "gl_generator 0.14.0", + "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" -checksum = "766443890761b3c4edcce86cafaac97971b200662fbdd0446eb7c6b99b4401ea" dependencies = [ - "nom", + "nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glsl-to-cxx" version = "0.1.0" dependencies = [ - "glsl", + "glsl 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glslopt" -version = "0.1.9" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74a3f5c04450dfdadb4b08f6e5ee6f5110f674de1acbd6199bfec68392a8cbaf" dependencies = [ - "cc", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glutin" version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5371b35b309dace06be1b81b5f6adb1c9de578b7dbe1e74bf7e4ef762cf6febd" -dependencies = [ - "android_glue", - "cgl", - "cocoa", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "glutin_egl_sys", - "glutin_emscripten_sys", - "glutin_gles2_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "lazy_static", - "libloading", - "objc", - "osmesa-sys", - "parking_lot", - "wayland-client", - "winapi", - "winit", +dependencies = [ + "android_glue 0.2.3 (git+https://github.com/rust-windowing/android-rs-glue.git?rev=e3ac6edea5814e1faca0c31ea8fac6877cb929ea)", + "cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_egl_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_emscripten_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_gles2_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_glx_sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_wgl_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glutin_egl_sys" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "772edef3b28b8ad41e4ea202748e65eefe8e5ffd1f4535f1219793dbb20b3d4c" dependencies = [ - "gl_generator 0.13.1", - "winapi", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glutin_emscripten_sys" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" [[package]] name = "glutin_gles2_sys" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e853d96bebcb8e53e445225c3009758c6f5960d44f2543245f6f07b567dae0" dependencies = [ - "gl_generator 0.13.1", - "objc", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glutin_glx_sys" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c243de74d6cf5ea100c788826d2fb9319de315485dd4b310811a663b3809c3" dependencies = [ - "gl_generator 0.13.1", - "x11-dl", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "x11-dl 2.18.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glutin_wgl_sys" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93dba7ee3a0feeac0f437141ff25e71ce2066bcf1a706acab1559ffff94eb6a" dependencies = [ - "gl_generator 0.13.1", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "hermit-abi" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" dependencies = [ - "libc", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "humantime" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" dependencies = [ - "quick-error", + "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "image" version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfc5483f8d5afd3653b38a196c52294dcb239c3e1a5bade1990353ea13bcf387" dependencies = [ - "bytemuck", - "byteorder", - "num-iter", - "num-rational", - "num-traits", - "png", + "bytemuck 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "png 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "inflate" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" dependencies = [ - "adler32", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "itoa" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "jobserver" version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" dependencies = [ - "libc", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "khronos_api" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" version = "0.2.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "libloading" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" dependencies = [ - "cc", - "winapi", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "line_drawing" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" dependencies = [ - "num-traits", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "linked-hash-map" -version = "0.5.3" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lock_api" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ - "scopeguard", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "malloc_buf" version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" dependencies = [ - "libc", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "malloc_size_of_derive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e37c5d4cd9473c5f4c9c111f033f15d4df9bd378fdf615944e360a4f55a05f0b" dependencies = [ - "proc-macro2 1.0.10", - "syn", - "synstructure", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "maybe-uninit" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" [[package]] name = "memchr" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" dependencies = [ - "libc", - "winapi", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memoffset" -version = "0.6.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" dependencies = [ - "autocfg", + "autocfg 1.0.0 (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" -checksum = "647da2489d438eb707e9bcffe0e47fb6f3f355ed288120740fc1e614cfadb9e9" + +[[package]] +name = "mio" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "mozangle" -version = "0.3.3" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef98797da14500fb5eaabc90dc91cf7c42710c11330351521d72f4aa268d689c" dependencies = [ - "cc", - "gl_generator 0.13.1", - "lazy_static", - "walkdir", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "nix" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", - "libc", - "void", + "bitflags 1.2.1 (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.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (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" -checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" dependencies = [ - "memchr", - "version_check", + "memchr 2.3.3 (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" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" dependencies = [ - "num-integer", - "num-iter", - "num-traits", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-integer" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" dependencies = [ - "autocfg", - "num-traits", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-iter" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" dependencies = [ - "autocfg", - "num-integer", - "num-traits", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-rational" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ - "autocfg", - "num-integer", - "num-traits", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" dependencies = [ - "autocfg", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num_cpus" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" dependencies = [ - "hermit-abi", - "libc", + "hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "objc" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ - "malloc_buf", + "malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "ordered-float" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" dependencies = [ - "num-traits", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "osmesa-src" -version = "0.2.0" -source = "git+https://github.com/servo/osmesa-src#cde678e924ae7e8f0203399992ecee4b14621ed8" +version = "0.1.1" +source = "git+https://github.com/servo/osmesa-src#1a9519c3675ebc1117cbb18ed6db420b5941cb8b" [[package]] name = "osmesa-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" dependencies = [ - "shared_library", + "shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ - "lock_api", - "parking_lot_core", - "rustc_version", + "lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot_core" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", - "libc", - "redox_syscall", - "rustc_version", - "smallvec 0.6.13", - "winapi", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "peek-poke" version = "0.2.0" dependencies = [ - "euclid", - "peek-poke-derive", + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "peek-poke-derive 0.2.1", ] [[package]] name = "peek-poke-derive" version = "0.2.1" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn", - "synstructure", - "unicode-xid 0.2.0", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "percent-encoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pkg-config" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] name = "plane-split" -version = "0.17.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f7075ec146b897b6e0faca47adeb7ed3d4f6eaa8145bf19db17311081b3f63" dependencies = [ - "binary-space-partition", - "euclid", - "log", - "num-traits", + "binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "png" version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "910f09135b1ed14bb16be445a8c23ddf0777eca485fbfc7cee00d81fecab158a" dependencies = [ - "bitflags", - "crc32fast", - "deflate", - "inflate", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "deflate 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", + "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "ppv-lite86" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "proc-macro2" version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid 0.1.0", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro2" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" dependencies = [ - "unicode-xid 0.2.0", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "procedural-masquerade" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" dependencies = [ - "proc-macro2 0.4.30", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" dependencies = [ - "proc-macro2 1.0.10", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ - "rand_core 0.4.2", + "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "raw-window-handle" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211" dependencies = [ - "libc", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon" -version = "1.5.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" -version = "1.9.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ - "rand_core 0.3.1", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "regex" version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", + "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" [[package]] name = "ron" -version = "0.6.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8a58080b7bb83b2ea28c3b7a9a994fd5e310330b7c8ca5258d99b98128ecfe4" dependencies = [ - "base64", - "bitflags", - "serde", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" [[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rusttype" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" dependencies = [ - "rusttype 0.8.3", + "rusttype 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rusttype" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f61411055101f7b60ecf1041d87fb74205fb20b0c7a723f07ef39174cf6b4c0" dependencies = [ - "approx", - "ordered-float", - "stb_truetype", + "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "stb_truetype 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ryu" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ - "winapi-util", + "winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "semver-parser" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" dependencies = [ - "serde_derive", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_bytes" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325a073952621257820e7a3469f55ba4726d8b28657e7e36653d1c36dc2c84ae" dependencies = [ - "serde", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" dependencies = [ - "itoa", - "ryu", - "serde", + "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "servo-fontconfig" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" dependencies = [ - "libc", - "servo-fontconfig-sys", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-fontconfig-sys 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "servo-fontconfig-sys" version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" dependencies = [ - "expat-sys", - "freetype-sys", - "pkg-config", + "expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "shared_library" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" dependencies = [ - "lazy_static", - "libc", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sig" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "slab" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "smallvec" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" dependencies = [ - "maybe-uninit", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "smallvec" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" dependencies = [ - "serde", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "smithay-client-toolkit" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" dependencies = [ - "andrew", - "bitflags", - "dlib", - "lazy_static", - "memmap", - "nix", - "wayland-client", - "wayland-commons", - "wayland-protocols", + "andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-protocols 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "stb_truetype" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" dependencies = [ - "byteorder", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "svg_fmt" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" [[package]] name = "swgl" version = "0.1.0" dependencies = [ - "cc", - "gleam 0.13.1", - "glsl-to-cxx", - "webrender_build", + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-cxx 0.1.0", + "webrender_build 0.0.1", ] [[package]] name = "syn" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "unicode-xid 0.2.0", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn", - "unicode-xid 0.2.0", + "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "termcolor" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" dependencies = [ - "winapi-util", + "winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ - "unicode-width", + "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thread_local" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" dependencies = [ - "lazy_static", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tileview" version = "0.1.0" dependencies = [ - "euclid", - "ron", - "serde", - "webrender", - "webrender_api", + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", + "webrender_api 0.61.0", ] [[package]] name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" dependencies = [ - "libc", - "redox_syscall", - "winapi", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tracy-rs" -version = "0.1.2" +version = "0.1.1" +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.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce607aae8ab0ab3abf3a2723a9ab6f09bb8639ed83fdd888d857b8e556c868d8" dependencies = [ - "minidl", + "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" [[package]] name = "version_check" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "walkdir" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ - "same-file", - "winapi", - "winapi-util", + "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "wayland-client" version = "0.21.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" dependencies = [ - "bitflags", - "downcast-rs", - "libc", - "nix", - "wayland-commons", - "wayland-scanner", - "wayland-sys", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-commons" version = "0.21.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" dependencies = [ - "nix", - "wayland-sys", + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-protocols" version = "0.21.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145" dependencies = [ - "bitflags", - "wayland-client", - "wayland-commons", - "wayland-scanner", - "wayland-sys", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-scanner" version = "0.21.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "xml-rs", + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "xml-rs 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wayland-sys" version = "0.21.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628" dependencies = [ - "dlib", - "lazy_static", + "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "webrender" version = "0.61.0" dependencies = [ - "bincode", - "bitflags", - "build-parallel", - "byteorder", - "core-foundation 0.9.0", - "core-graphics 0.22.0", - "core-text", - "cstr", - "derive_more", - "dwrote", - "etagere", - "euclid", - "freetype", - "fxhash", - "gleam 0.13.1", - "glslopt", - "lazy_static", - "libc", - "log", - "malloc_size_of_derive", - "mozangle", - "num-traits", - "objc", - "plane-split", - "png", - "rand", - "rayon", - "ron", - "serde", - "smallvec 1.3.0", - "svg_fmt", - "swgl", - "time", - "tracy-rs", - "webrender_api", - "webrender_build", - "wr_malloc_size_of", + "backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "build-parallel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cstr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype 0.7.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.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mozangle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "plane-split 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "png 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", + "sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "svg_fmt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tracy-rs 0.1.1 (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", + "ws 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "webrender-examples" version = "0.1.0" dependencies = [ - "app_units", - "core-foundation 0.7.0", - "env_logger", - "euclid", - "gleam 0.13.1", - "glutin", - "rayon", - "webrender", - "winit", + "app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", + "winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "webrender_api" version = "0.61.0" dependencies = [ - "app_units", - "bitflags", - "byteorder", - "core-foundation 0.9.0", - "core-graphics 0.22.0", - "crossbeam-channel", - "derive_more", - "euclid", - "malloc_size_of_derive", - "peek-poke", - "serde", - "serde_bytes", - "serde_derive", - "time", - "wr_malloc_size_of", + "app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "peek-poke 0.2.0", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "wr_malloc_size_of 0.0.1", ] [[package]] name = "webrender_build" version = "0.0.1" dependencies = [ - "bitflags", - "lazy_static", - "serde", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" dependencies = [ - "winapi", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "winit" version = "0.19.5" -source = "git+https://github.com/jrmuizel/winit?branch=wr#947af776e1a3ab47000b893068b4d0750861bdb0" -dependencies = [ - "android_glue", - "backtrace", - "bitflags", - "cocoa", - "core-foundation 0.6.4", - "core-graphics 0.17.3", - "lazy_static", - "libc", - "log", - "objc", - "parking_lot", - "percent-encoding", - "raw-window-handle", - "smithay-client-toolkit", - "wayland-client", - "winapi", - "x11-dl", +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "android_glue 0.2.3 (git+https://github.com/rust-windowing/android-rs-glue.git?rev=e3ac6edea5814e1faca0c31ea8fac6877cb929ea)", + "backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "x11-dl 2.18.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wio" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" dependencies = [ - "winapi", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wr_malloc_size_of" version = "0.0.1" dependencies = [ - "app_units", - "euclid", + "app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wrench" version = "0.3.0" dependencies = [ - "base64", - "chrono", - "clap", - "core-foundation 0.9.0", - "core-graphics 0.22.0", - "crossbeam", - "dwrote", - "env_logger", - "font-loader", - "gleam 0.13.1", - "glutin", - "image", - "log", - "mozangle", - "osmesa-src", - "osmesa-sys", - "semver", - "serde", - "serde_json", - "swgl", - "time", - "tracy-rs", - "webrender", - "winit", - "yaml-rust 0.4.3", + "app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "font-loader 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mozangle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "osmesa-src 0.1.1 (git+https://github.com/servo/osmesa-src)", + "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", + "swgl 0.1.0", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", + "webrender_api 0.61.0", + "winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)", + "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ws" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "x11-dl" version = "2.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" dependencies = [ - "lazy_static", - "libc", - "maybe-uninit", - "pkg-config", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "xdg" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" [[package]] name = "xml-rs" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb76e5c421bbbeb8924c60c030331b345555024d56261dae8f3e786ed817c23" [[package]] name = "yaml-rust" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" [[package]] name = "yaml-rust" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" dependencies = [ - "linked-hash-map", -] + "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" +"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" +"checksum andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" +"checksum android_glue 0.2.3 (git+https://github.com/rust-windowing/android-rs-glue.git?rev=e3ac6edea5814e1faca0c31ea8fac6877cb929ea)" = "<none>" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc3ec9d4c47b25a5a9e5c848e053640331c7cedb1637434d75db68b79fee8a7f" +"checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" +"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" +"checksum backtrace-sys 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" +"checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" +"checksum binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff" +"checksum bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5753e2a71534719bf3f4e57006c3a4f0d2c672a4b676eec84161f763eca87dbf" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +"checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +"checksum block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +"checksum block-padding 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +"checksum build-parallel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4b5f8b148c1884e660514fb1205f36b595e96df4daac5ad526a23db44937de77" +"checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" +"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.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" +"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" +"checksum cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54" +"checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" +"checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +"checksum core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5ed8e7e76c45974e15e41bfa8d5b0483cd90191639e01d8f5f1e606299d3fb" +"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" +"checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" +"checksum core-foundation-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6" +"checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" +"checksum core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6082396a349fa49674ba1bda4077332a18bf150e8fa75745ece07085e29a113" +"checksum core-graphics-types 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e92f5d519093a4178296707dbaa3880eae85a5ef5386675f361a1cf25376e93c" +"checksum core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "04dfae50af11e72657fe7174cddb1ecddc5398037f7f6f39533ad69207c9a4e2" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +"checksum crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be" +"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +"checksum cstr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "19f7a08ed4ecd7e077d4cee63937473e6f7cf57b702a9114ef41751b2cbc0f60" +"checksum cstr-macros 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "cd670e5ff58768ef624207fb95709ce63b8d05573fb9a05165f0eef471ea6a3a" +"checksum deflate 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e5d2a2273fed52a7f947ee55b092c4057025d7a3e04e5ecdbd25d6c3fb1bd7" +"checksum derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7" +"checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +"checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" +"checksum downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6" +"checksum dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" +"checksum euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ab0e07e345fb061928646949fdf5fb888e5d75a57385e7f5856e45be289e745" +"checksum expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" +"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +"checksum font-loader 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c49d6b4c11dca1a1dd931a34a9f397e2da91abe3de4110505f3530a80e560b52" +"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +"checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +"checksum freetype 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +"checksum freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +"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.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdef5b9df6d3a261b80a5ac55e13bf93945725df2463c1b0a2e5a527dce0d37" +"checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" +"checksum glsl 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "766443890761b3c4edcce86cafaac97971b200662fbdd0446eb7c6b99b4401ea" +"checksum glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f22b383fcf6f85c4a268af39a0758ec40970e5f9f8fe9809e4415d48409b8379" +"checksum glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5371b35b309dace06be1b81b5f6adb1c9de578b7dbe1e74bf7e4ef762cf6febd" +"checksum glutin_egl_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "772edef3b28b8ad41e4ea202748e65eefe8e5ffd1f4535f1219793dbb20b3d4c" +"checksum glutin_emscripten_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" +"checksum glutin_gles2_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07e853d96bebcb8e53e445225c3009758c6f5960d44f2543245f6f07b567dae0" +"checksum glutin_glx_sys 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "08c243de74d6cf5ea100c788826d2fb9319de315485dd4b310811a663b3809c3" +"checksum glutin_wgl_sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a93dba7ee3a0feeac0f437141ff25e71ce2066bcf1a706acab1559ffff94eb6a" +"checksum hermit-abi 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" +"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +"checksum image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bfc5483f8d5afd3653b38a196c52294dcb239c3e1a5bade1990353ea13bcf387" +"checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +"checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +"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 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" +"checksum libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)" = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" +"checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +"checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" +"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +"checksum malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e37c5d4cd9473c5f4c9c111f033f15d4df9bd378fdf615944e360a4f55a05f0b" +"checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" +"checksum minidl 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "647da2489d438eb707e9bcffe0e47fb6f3f355ed288120740fc1e614cfadb9e9" +"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +"checksum mio-extras 2.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum mozangle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b74826530a7c9fffbe28d27d0499433bd51f67b4f45d658ba23e62499307cc17" +"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +"checksum nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" +"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" +"checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +"checksum num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" +"checksum num-rational 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +"checksum objc 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" +"checksum osmesa-src 0.1.1 (git+https://github.com/servo/osmesa-src)" = "<none>" +"checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" +"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" +"checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +"checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +"checksum plane-split 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2211e7ccc9b6260779dd9bad59f7b10889d6361974623b9e405afd7e7e764654" +"checksum png 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)" = "910f09135b1ed14bb16be445a8c23ddf0777eca485fbfc7cee00d81fecab158a" +"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +"checksum procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1383dff4092fe903ac180e391a8d4121cc48f08ccf850614b0290c6673b69d" +"checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +"checksum raw-window-handle 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211" +"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex 1.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" +"checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" +"checksum ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ece421e0c4129b90e4a35b6f625e472e96c552136f5093a2f4fa2bbb75a62d5" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum rusttype 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" +"checksum rusttype 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f61411055101f7b60ecf1041d87fb74205fb20b0c7a723f07ef39174cf6b4c0" +"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" +"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +"checksum serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "325a073952621257820e7a3469f55ba4726d8b28657e7e36653d1c36dc2c84ae" +"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +"checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" +"checksum servo-fontconfig 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" +"checksum servo-fontconfig-sys 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" +"checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +"checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +"checksum sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6567e29578f9bfade6a5d94a32b9a4256348358d2a3f448cab0021f9a02614a2" +"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +"checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" +"checksum smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" +"checksum smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" +"checksum stb_truetype 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" +"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum svg_fmt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" +"checksum syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" +"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum tracy-rs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dbbb439715d4c258415c7cbf137be32a4f54f7348724076145ede565ee2142e8" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" +"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +"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.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +"checksum wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" +"checksum wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" +"checksum wayland-protocols 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145" +"checksum wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e" +"checksum wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum winit 0.19.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1e96eb4bb472fa43e718e8fa4aef82f86cd9deac9483a1e1529230babdb394a8" +"checksum wio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +"checksum ws 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a2c47b5798ccc774ffb93ff536aec7c4275d722fd9c740c83cdd1af1f2d94" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +"checksum x11-dl 2.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8" +"checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" +"checksum xml-rs 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2bb76e5c421bbbeb8924c60c030331b345555024d56261dae8f3e786ed817c23" +"checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" +"checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" diff --git a/third_party/webrender/Cargo.toml b/third_party/webrender/Cargo.toml index 5ad5abe0baa..bb6b204412a 100644 --- a/third_party/webrender/Cargo.toml +++ b/third_party/webrender/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "direct-composition", "examples", "webrender", "webrender_api", @@ -15,13 +16,6 @@ panic = "abort" [profile.dev] panic = "abort" -# optimizing glsl more makes a big difference in swgl build times -[profile.dev.package.glsl] -opt-level = 2 - -[profile.release.package.glsl] -opt-level = 2 - # Running wrench on android built with master cargo-apk results in a crash # due to a mismatched version of android_glue (a dependency of winit). # Override it to use a suitable version of android_glue. @@ -29,5 +23,3 @@ opt-level = 2 # This can be removed once a new version of android_glue is published to crates.io. [patch.crates-io] android_glue = { git = "https://github.com/rust-windowing/android-rs-glue.git", rev = "e3ac6edea5814e1faca0c31ea8fac6877cb929ea" } -# this is a version that fixes some incompatibilites with newer rust/aarch64 -winit = { version = "0.19", git = "https://github.com/jrmuizel/winit", branch="wr" } diff --git a/third_party/webrender/ci-scripts/docker-image/setup.sh b/third_party/webrender/ci-scripts/docker-image/setup.sh index 11adb1c269b..4a20147f3b0 100755 --- a/third_party/webrender/ci-scripts/docker-image/setup.sh +++ b/third_party/webrender/ci-scripts/docker-image/setup.sh @@ -14,19 +14,15 @@ test "$(whoami)" == 'root' # Install stuff we need apt-get -y update apt-get install -y \ - bison \ bzip2 \ cmake \ curl \ - flex \ gcc \ git \ g++ \ libfontconfig1-dev \ libgl1-mesa-dev \ libx11-dev \ - llvm-dev \ - ninja-build \ openjdk-8-jdk \ pkg-config \ python \ @@ -35,10 +31,7 @@ apt-get install -y \ python-setuptools \ python-voluptuous \ python-yaml \ - python3-pip \ - python3-mako \ - software-properties-common \ - clang + software-properties-common # Other stuff we need pip install servo-tidy==0.3.0 diff --git a/third_party/webrender/ci-scripts/etc/wr-darwin.meson b/third_party/webrender/ci-scripts/etc/wr-darwin.meson deleted file mode 100644 index 54499822254..00000000000 --- a/third_party/webrender/ci-scripts/etc/wr-darwin.meson +++ /dev/null @@ -1,18 +0,0 @@ -[binaries] -llvm-config = '/builds/worker/fetches/clang/bin/llvm-config' - -[properties] -# When linking `libOSMesa.dylib` Meson uses options provided by `llvm-config`. -# The binary for `llvm-config` in Firefox CI comes from a native Linux clang, -# which gives the link options for the Linux libLLVM-11.so in the Linux clang. -# However, we want to link against a native macOS clang's libLLVM.dylib, which -# we have available in a separate directory. -# Meson will still have -lLLVM-11 on the command line, but the linker will -# only warn that it has the wrong format (because it's not for macOS). -cpp_link_args = ['-L/builds/worker/fetches/clang-mac/clang/lib', '-lLLVM'] - -[host_machine] -system = 'darwin' -cpu_family = 'x86_64' -cpu = 'i686' -endian = 'little' diff --git a/third_party/webrender/ci-scripts/install-meson.sh b/third_party/webrender/ci-scripts/install-meson.sh deleted file mode 100755 index a0171d3a8e4..00000000000 --- a/third_party/webrender/ci-scripts/install-meson.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -# 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/. */ - -# This file downloads and installs meson which is required for building -# osmesa-src, a dependency of wrench. - -set -o errexit -set -o nounset -set -o pipefail -set -o xtrace - -MESON_VER=0.55.1 -MESON_BASE_URL="https://github.com/mesonbuild/meson/releases/download" - -curl -L ${MESON_BASE_URL}/${MESON_VER}/meson-${MESON_VER}.tar.gz -o meson.tar.gz -tar -xf meson.tar.gz -mv meson-${MESON_VER} meson -cd meson -ln -s meson.py meson diff --git a/third_party/webrender/ci-scripts/linux-debug-tests.sh b/third_party/webrender/ci-scripts/linux-debug-tests.sh index d402d728f9e..2c2fc756b63 100755 --- a/third_party/webrender/ci-scripts/linux-debug-tests.sh +++ b/third_party/webrender/ci-scripts/linux-debug-tests.sh @@ -32,5 +32,5 @@ cargo build ${CARGOFLAGS} popd cargo test ${CARGOFLAGS} \ - --all --exclude compositor --exclude compositor-wayland \ - --exclude compositor-windows --exclude glsl-to-cxx --exclude swgl + --all --exclude compositor-windows --exclude compositor \ + --exclude glsl-to-cxx --exclude swgl diff --git a/third_party/webrender/ci-scripts/linux-release-tests.sh b/third_party/webrender/ci-scripts/linux-release-tests.sh index 665fc88b5fe..f89fc43c44f 100755 --- a/third_party/webrender/ci-scripts/linux-release-tests.sh +++ b/third_party/webrender/ci-scripts/linux-release-tests.sh @@ -16,13 +16,7 @@ set -o xtrace CARGOFLAGS=${CARGOFLAGS:-""} # default to empty if not set pushd wrench -# Test that all shaders compile successfully. -python script/headless.py --precache test_init -python script/headless.py --precache --use-unoptimized-shaders test_init - python script/headless.py reftest python script/headless.py rawtest -python script/headless.py test_invalidation -CXX=clang++ cargo run ${CARGOFLAGS} --release --features=software -- \ - --software --headless reftest +cargo build ${CARGOFLAGS} --release popd diff --git a/third_party/webrender/ci-scripts/macos-debug-tests.sh b/third_party/webrender/ci-scripts/macos-debug-tests.sh index 6dbf1f12311..2325475d90d 100755 --- a/third_party/webrender/ci-scripts/macos-debug-tests.sh +++ b/third_party/webrender/ci-scripts/macos-debug-tests.sh @@ -38,5 +38,5 @@ cargo check ${CARGOFLAGS} popd cargo test ${CARGOFLAGS} ${CARGOTESTFLAGS} \ - --all --exclude compositor --exclude compositor-wayland \ - --exclude compositor-windows --exclude glsl-to-cxx --exclude swgl + --all --exclude compositor-windows --exclude compositor \ + --exclude glsl-to-cxx --exclude swgl diff --git a/third_party/webrender/ci-scripts/macos-release-tests.sh b/third_party/webrender/ci-scripts/macos-release-tests.sh index 5eb22c9f889..dc8c205ad53 100755 --- a/third_party/webrender/ci-scripts/macos-release-tests.sh +++ b/third_party/webrender/ci-scripts/macos-release-tests.sh @@ -19,13 +19,7 @@ CARGOFLAGS=${CARGOFLAGS:-""} # default to empty if not set WRENCH_BINARY=${WRENCH_BINARY:-""} pushd wrench - -# Test that all shaders compile successfully. -python script/headless.py --precache test_init -python script/headless.py --precache --use-unoptimized-shaders test_init - python script/headless.py reftest -python script/headless.py test_invalidation if [[ -z "${WRENCH_BINARY}" ]]; then cargo build ${CARGOFLAGS} --release WRENCH_BINARY="../target/release/wrench" diff --git a/third_party/webrender/ci-scripts/windows-tests.cmd b/third_party/webrender/ci-scripts/windows-tests.cmd index cf3997b11b7..c3607a90287 100755 --- a/third_party/webrender/ci-scripts/windows-tests.cmd +++ b/third_party/webrender/ci-scripts/windows-tests.cmd @@ -21,13 +21,6 @@ popd pushd wrench cargo test --verbose if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL% -:: Test that all shaders compile successfully. --precache compiles all shaders -:: during initialization, therefore if init is successful then the shaders compile. -cargo run --release -- --angle --precache test_init -if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL% -cargo run --release -- --angle --precache --use-unoptimized-shaders test_init -if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL% - cargo run --release -- --angle reftest if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL% popd @@ -36,3 +29,8 @@ pushd examples cargo check --verbose if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL% popd + +pushd direct-composition +cargo check --verbose +if %ERRORLEVEL% NEQ 0 EXIT /b %ERRORLEVEL% +popd diff --git a/third_party/webrender/debugger/.babelrc b/third_party/webrender/debugger/.babelrc new file mode 100644 index 00000000000..e81239406e3 --- /dev/null +++ b/third_party/webrender/debugger/.babelrc @@ -0,0 +1,6 @@ +{ + "presets": [ + ["env", { "modules": false }], + "stage-3" + ] +} diff --git a/third_party/webrender/debugger/.gitignore b/third_party/webrender/debugger/.gitignore new file mode 100644 index 00000000000..15115487ab7 --- /dev/null +++ b/third_party/webrender/debugger/.gitignore @@ -0,0 +1,11 @@ +.DS_Store +node_modules/ +npm-debug.log +yarn-error.log + +# Editor directories and files +.idea +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/third_party/webrender/debugger/README.md b/third_party/webrender/debugger/README.md new file mode 100644 index 00000000000..d41b5ce16aa --- /dev/null +++ b/third_party/webrender/debugger/README.md @@ -0,0 +1,23 @@ +# WebRender Debugger +A web based debugger for WebRender. + +## Using the debugger +Build your application with the debugger feature enabled, for example in wrench: + +``` +cargo build --features=debugger +``` + +Now, open your browser and open the debugger/index.html file. Click Connect and +the debugger will attempt to connect to WR via websocket. + +## Using the debugger with Gecko + +In the Gecko source tree, open ```gfx/webrender_bindings/Cargo.toml``` in a text editor. + +Add ```features = ['debugger']``` to the end of the file (in the ```dependencies.webrender``` section). + +Vendor the rust dependencies locally for the debugger (we don't want these committed to the repo): +```./mach vendor rust``` + +Now, build and run as usual, and the debugger will be available. diff --git a/third_party/webrender/debugger/dist/build.js b/third_party/webrender/debugger/dist/build.js new file mode 100644 index 00000000000..e8d4439034d --- /dev/null +++ b/third_party/webrender/debugger/dist/build.js @@ -0,0 +1,13 @@ +!function(e){function t(n){if(i[n])return i[n].exports;var o=i[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var i={};t.m=e,t.c=i,t.d=function(e,i,n){t.o(e,i)||Object.defineProperty(e,i,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var i=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(i,"a",i),i},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/dist/",t(t.s=16)}([function(e,t){function i(e,t){var i=e[1]||"",o=e[3];if(!o)return i;if(t&&"function"==typeof btoa){var a=n(o);return[i].concat(o.sources.map(function(e){return"/*# sourceURL="+o.sourceRoot+e+" */"})).concat([a]).join("\n")}return[i].join("\n")}function n(e){return"/*# sourceMappingURL=data:application/json;charset=utf-8;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(e))))+" */"}e.exports=function(e){var t=[];return t.toString=function(){return this.map(function(t){var n=i(t,e);return t[2]?"@media "+t[2]+"{"+n+"}":n}).join("")},t.i=function(e,i){"string"==typeof e&&(e=[[null,e,""]]);for(var n={},o=0;o<this.length;o++){var a=this[o][0];"number"==typeof a&&(n[a]=!0)}for(o=0;o<e.length;o++){var r=e[o];"number"==typeof r[0]&&n[r[0]]||(i&&!r[2]?r[2]=i:i&&(r[2]="("+r[2]+") and ("+i+")"),t.push(r))}},t}},function(e,t,i){function n(e){for(var t=0;t<e.length;t++){var i=e[t],n=d[i.id];if(n){n.refs++;for(var o=0;o<n.parts.length;o++)n.parts[o](i.parts[o]);for(;o<i.parts.length;o++)n.parts.push(a(i.parts[o]));n.parts.length>i.parts.length&&(n.parts.length=i.parts.length)}else{for(var r=[],o=0;o<i.parts.length;o++)r.push(a(i.parts[o]));d[i.id]={id:i.id,refs:1,parts:r}}}}function o(){var e=document.createElement("style");return e.type="text/css",u.appendChild(e),e}function a(e){var t,i,n=document.querySelector("style["+g+'~="'+e.id+'"]');if(n){if(h)return b;n.parentNode.removeChild(n)}if(v){var a=p++;n=f||(f=o()),t=r.bind(null,n,a,!1),i=r.bind(null,n,a,!0)}else n=o(),t=s.bind(null,n),i=function(){n.parentNode.removeChild(n)};return t(e),function(n){if(n){if(n.css===e.css&&n.media===e.media&&n.sourceMap===e.sourceMap)return;t(e=n)}else i()}}function r(e,t,i,n){var o=i?"":n.css;if(e.styleSheet)e.styleSheet.cssText=k(t,o);else{var a=document.createTextNode(o),r=e.childNodes;r[t]&&e.removeChild(r[t]),r.length?e.insertBefore(a,r[t]):e.appendChild(a)}}function s(e,t){var i=t.css,n=t.media,o=t.sourceMap;if(n&&e.setAttribute("media",n),m.ssrId&&e.setAttribute(g,t.id),o&&(i+="\n/*# sourceURL="+o.sources[0]+" */",i+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */"),e.styleSheet)e.styleSheet.cssText=i;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(i))}}var l="undefined"!=typeof document;if("undefined"!=typeof DEBUG&&DEBUG&&!l)throw new Error("vue-style-loader cannot be used in a non-browser environment. Use { target: 'node' } in your Webpack config to indicate a server-rendering environment.");var c=i(23),d={},u=l&&(document.head||document.getElementsByTagName("head")[0]),f=null,p=0,h=!1,b=function(){},m=null,g="data-vue-ssr-id",v="undefined"!=typeof navigator&&/msie [6-9]\b/.test(navigator.userAgent.toLowerCase());e.exports=function(e,t,i,o){h=i,m=o||{};var a=c(e,t);return n(a),function(t){for(var i=[],o=0;o<a.length;o++){var r=a[o],s=d[r.id];s.refs--,i.push(s)}t?(a=c(e,t),n(a)):a=[];for(var o=0;o<i.length;o++){var s=i[o];if(0===s.refs){for(var l=0;l<s.parts.length;l++)s.parts[l]();delete d[s.id]}}}};var k=function(){var e=[];return function(t,i){return e[t]=i,e.filter(Boolean).join("\n")}}()},function(e,t){e.exports=function(e,t,i,n,o,a){var r,s=e=e||{},l=typeof e.default;"object"!==l&&"function"!==l||(r=e,s=e.default);var c="function"==typeof s?s.options:s;t&&(c.render=t.render,c.staticRenderFns=t.staticRenderFns,c._compiled=!0),i&&(c.functional=!0),o&&(c._scopeId=o);var d;if(a?(d=function(e){e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,e||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),n&&n.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(a)},c._ssrRegister=d):n&&(d=n),d){var u=c.functional,f=u?c.render:c.beforeCreate;u?(c._injectStyles=d,c.render=function(e,t){return d.call(t),f(e,t)}):c.beforeCreate=f?[].concat(f,d):[d]}return{esModule:r,exports:s,options:c}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e,i){function n(e){return void 0===e||null===e}function o(e){return void 0!==e&&null!==e}function a(e){return!0===e}function r(e){return!1===e}function s(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function l(e){return null!==e&&"object"==typeof e}function c(e){return"[object Object]"===zn.call(e)}function d(e){return"[object RegExp]"===zn.call(e)}function u(e){var t=parseFloat(String(e));return t>=0&&Math.floor(t)===t&&isFinite(e)}function f(e){return o(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function p(e){return null==e?"":Array.isArray(e)||c(e)&&e.toString===zn?JSON.stringify(e,null,2):String(e)}function h(e){var t=parseFloat(e);return isNaN(t)?e:t}function b(e,t){for(var i=Object.create(null),n=e.split(","),o=0;o<n.length;o++)i[n[o]]=!0;return t?function(e){return i[e.toLowerCase()]}:function(e){return i[e]}}function m(e,t){if(e.length){var i=e.indexOf(t);if(i>-1)return e.splice(i,1)}}function g(e,t){return An.call(e,t)}function v(e){var t=Object.create(null);return function(i){return t[i]||(t[i]=e(i))}}function k(e,t){function i(i){var n=arguments.length;return n?n>1?e.apply(t,arguments):e.call(t,i):e.call(t)}return i._length=e.length,i}function x(e,t){return e.bind(t)}function w(e,t){t=t||0;for(var i=e.length-t,n=new Array(i);i--;)n[i]=e[i+t];return n}function y(e,t){for(var i in t)e[i]=t[i];return e}function _(e){for(var t={},i=0;i<e.length;i++)e[i]&&y(t,e[i]);return t}function C(e,t,i){}function S(e,t){if(e===t)return!0;var i=l(e),n=l(t);if(!i||!n)return!i&&!n&&String(e)===String(t);try{var o=Array.isArray(e),a=Array.isArray(t);if(o&&a)return e.length===t.length&&e.every(function(e,i){return S(e,t[i])});if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if(o||a)return!1;var r=Object.keys(e),s=Object.keys(t);return r.length===s.length&&r.every(function(i){return S(e[i],t[i])})}catch(e){return!1}}function $(e,t){for(var i=0;i<e.length;i++)if(S(e[i],t))return i;return-1}function D(e){var t=!1;return function(){t||(t=!0,e.apply(this,arguments))}}function z(e){var t=(e+"").charCodeAt(0);return 36===t||95===t}function O(e,t,i,n){Object.defineProperty(e,t,{value:i,enumerable:!!n,writable:!0,configurable:!0})}function A(e){if(!qn.test(e)){var t=e.split(".");return function(e){for(var i=0;i<t.length;i++){if(!e)return;e=e[t[i]]}return e}}}function T(e){return"function"==typeof e&&/native code/.test(e.toString())}function M(e){po.push(e),fo.target=e}function j(){po.pop(),fo.target=po[po.length-1]}function P(e){return new ho(void 0,void 0,void 0,String(e))}function E(e){var t=new ho(e.tag,e.data,e.children&&e.children.slice(),e.text,e.elm,e.context,e.componentOptions,e.asyncFactory);return t.ns=e.ns,t.isStatic=e.isStatic,t.key=e.key,t.isComment=e.isComment,t.fnContext=e.fnContext,t.fnOptions=e.fnOptions,t.fnScopeId=e.fnScopeId,t.asyncMeta=e.asyncMeta,t.isCloned=!0,t}function F(e){xo=e}function B(e,t){e.__proto__=t}function V(e,t,i){for(var n=0,o=i.length;n<o;n++){var a=i[n];O(e,a,t[a])}}function I(e,t){if(l(e)&&!(e instanceof ho)){var i;return g(e,"__ob__")&&e.__ob__ instanceof wo?i=e.__ob__:xo&&!ro()&&(Array.isArray(e)||c(e))&&Object.isExtensible(e)&&!e._isVue&&(i=new wo(e)),t&&i&&i.vmCount++,i}}function N(e,t,i,n,o){var a=new fo,r=Object.getOwnPropertyDescriptor(e,t);if(!r||!1!==r.configurable){var s=r&&r.get,l=r&&r.set;s&&!l||2!==arguments.length||(i=e[t]);var c=!o&&I(i);Object.defineProperty(e,t,{enumerable:!0,configurable:!0,get:function(){var t=s?s.call(e):i;return fo.target&&(a.depend(),c&&(c.dep.depend(),Array.isArray(t)&&H(t))),t},set:function(t){var n=s?s.call(e):i;t===n||t!==t&&n!==n||s&&!l||(l?l.call(e,t):i=t,c=!o&&I(t),a.notify())}})}}function R(e,t,i){if(Array.isArray(e)&&u(t))return e.length=Math.max(e.length,t),e.splice(t,1,i),i;if(t in e&&!(t in Object.prototype))return e[t]=i,i;var n=e.__ob__;return e._isVue||n&&n.vmCount?i:n?(N(n.value,t,i),n.dep.notify(),i):(e[t]=i,i)}function L(e,t){if(Array.isArray(e)&&u(t))return void e.splice(t,1);var i=e.__ob__;e._isVue||i&&i.vmCount||g(e,t)&&(delete e[t],i&&i.dep.notify())}function H(e){for(var t=void 0,i=0,n=e.length;i<n;i++)t=e[i],t&&t.__ob__&&t.__ob__.dep.depend(),Array.isArray(t)&&H(t)}function q(e,t){if(!t)return e;for(var i,n,o,a=lo?Reflect.ownKeys(t):Object.keys(t),r=0;r<a.length;r++)"__ob__"!==(i=a[r])&&(n=e[i],o=t[i],g(e,i)?n!==o&&c(n)&&c(o)&&q(n,o):R(e,i,o));return e}function U(e,t,i){return i?function(){var n="function"==typeof t?t.call(i,i):t,o="function"==typeof e?e.call(i,i):e;return n?q(n,o):o}:t?e?function(){return q("function"==typeof t?t.call(this,this):t,"function"==typeof e?e.call(this,this):e)}:t:e}function W(e,t){var i=t?e?e.concat(t):Array.isArray(t)?t:[t]:e;return i?Y(i):i}function Y(e){for(var t=[],i=0;i<e.length;i++)-1===t.indexOf(e[i])&&t.push(e[i]);return t}function G(e,t,i,n){var o=Object.create(e||null);return t?y(o,t):o}function K(e,t){var i=e.props;if(i){var n,o,a,r={};if(Array.isArray(i))for(n=i.length;n--;)"string"==typeof(o=i[n])&&(a=Mn(o),r[a]={type:null});else if(c(i))for(var s in i)o=i[s],a=Mn(s),r[a]=c(o)?o:{type:o};e.props=r}}function X(e,t){var i=e.inject;if(i){var n=e.inject={};if(Array.isArray(i))for(var o=0;o<i.length;o++)n[i[o]]={from:i[o]};else if(c(i))for(var a in i){var r=i[a];n[a]=c(r)?y({from:a},r):{from:r}}}}function J(e){var t=e.directives;if(t)for(var i in t){var n=t[i];"function"==typeof n&&(t[i]={bind:n,update:n})}}function Q(e,t,i){function n(n){var o=yo[n]||Co;s[n]=o(e[n],t[n],i,n)}if("function"==typeof t&&(t=t.options),K(t,i),X(t,i),J(t),!t._base&&(t.extends&&(e=Q(e,t.extends,i)),t.mixins))for(var o=0,a=t.mixins.length;o<a;o++)e=Q(e,t.mixins[o],i);var r,s={};for(r in e)n(r);for(r in t)g(e,r)||n(r);return s}function Z(e,t,i,n){if("string"==typeof i){var o=e[t];if(g(o,i))return o[i];var a=Mn(i);if(g(o,a))return o[a];var r=jn(a);if(g(o,r))return o[r];return o[i]||o[a]||o[r]}}function ee(e,t,i,n){var o=t[e],a=!g(i,e),r=i[e],s=oe(Boolean,o.type);if(s>-1)if(a&&!g(o,"default"))r=!1;else if(""===r||r===En(e)){var l=oe(String,o.type);(l<0||s<l)&&(r=!0)}if(void 0===r){r=te(n,o,e);var c=xo;F(!0),I(r),F(c)}return r}function te(e,t,i){if(g(t,"default")){var n=t.default;return e&&e.$options.propsData&&void 0===e.$options.propsData[i]&&void 0!==e._props[i]?e._props[i]:"function"==typeof n&&"Function"!==ie(t.type)?n.call(e):n}}function ie(e){var t=e&&e.toString().match(/^\s*function (\w+)/);return t?t[1]:""}function ne(e,t){return ie(e)===ie(t)}function oe(e,t){if(!Array.isArray(t))return ne(t,e)?0:-1;for(var i=0,n=t.length;i<n;i++)if(ne(t[i],e))return i;return-1}function ae(e,t,i){M();try{if(t)for(var n=t;n=n.$parent;){var o=n.$options.errorCaptured;if(o)for(var a=0;a<o.length;a++)try{var r=!1===o[a].call(n,e,t,i);if(r)return}catch(e){se(e,n,"errorCaptured hook")}}se(e,t,i)}finally{j()}}function re(e,t,i,n,o){var a;try{a=i?e.apply(t,i):e.call(t),a&&!a._isVue&&f(a)&&!a._handled&&(a.catch(function(e){return ae(e,n,o+" (Promise/async)")}),a._handled=!0)}catch(e){ae(e,n,o)}return a}function se(e,t,i){if(Ln.errorHandler)try{return Ln.errorHandler.call(null,e,t,i)}catch(t){t!==e&&le(t,null,"config.errorHandler")}le(e,t,i)}function le(e,t,i){if(!Wn&&!Yn||"undefined"==typeof console)throw e;console.error(e)}function ce(){Do=!1;var e=$o.slice(0);$o.length=0;for(var t=0;t<e.length;t++)e[t]()}function de(e,t){var i;if($o.push(function(){if(e)try{e.call(t)}catch(e){ae(e,t,"nextTick")}else i&&i(t)}),Do||(Do=!0,_o()),!e&&"undefined"!=typeof Promise)return new Promise(function(e){i=e})}function ue(e){fe(e,Mo),Mo.clear()}function fe(e,t){var i,n,o=Array.isArray(e);if(!(!o&&!l(e)||Object.isFrozen(e)||e instanceof ho)){if(e.__ob__){var a=e.__ob__.dep.id;if(t.has(a))return;t.add(a)}if(o)for(i=e.length;i--;)fe(e[i],t);else for(n=Object.keys(e),i=n.length;i--;)fe(e[n[i]],t)}}function pe(e,t){function i(){var e=arguments,n=i.fns;if(!Array.isArray(n))return re(n,null,arguments,t,"v-on handler");for(var o=n.slice(),a=0;a<o.length;a++)re(o[a],null,e,t,"v-on handler")}return i.fns=e,i}function he(e,t,i,o,r,s){var l,c,d,u;for(l in e)c=e[l],d=t[l],u=jo(l),n(c)||(n(d)?(n(c.fns)&&(c=e[l]=pe(c,s)),a(u.once)&&(c=e[l]=r(u.name,c,u.capture)),i(u.name,c,u.capture,u.passive,u.params)):c!==d&&(d.fns=c,e[l]=d));for(l in t)n(e[l])&&(u=jo(l),o(u.name,t[l],u.capture))}function be(e,t,i){function r(){i.apply(this,arguments),m(s.fns,r)}e instanceof ho&&(e=e.data.hook||(e.data.hook={}));var s,l=e[t];n(l)?s=pe([r]):o(l.fns)&&a(l.merged)?(s=l,s.fns.push(r)):s=pe([l,r]),s.merged=!0,e[t]=s}function me(e,t,i){var a=t.options.props;if(!n(a)){var r={},s=e.attrs,l=e.props;if(o(s)||o(l))for(var c in a){var d=En(c);ge(r,l,c,d,!0)||ge(r,s,c,d,!1)}return r}}function ge(e,t,i,n,a){if(o(t)){if(g(t,i))return e[i]=t[i],a||delete t[i],!0;if(g(t,n))return e[i]=t[n],a||delete t[n],!0}return!1}function ve(e){for(var t=0;t<e.length;t++)if(Array.isArray(e[t]))return Array.prototype.concat.apply([],e);return e}function ke(e){return s(e)?[P(e)]:Array.isArray(e)?we(e):void 0}function xe(e){return o(e)&&o(e.text)&&r(e.isComment)}function we(e,t){var i,r,l,c,d=[];for(i=0;i<e.length;i++)r=e[i],n(r)||"boolean"==typeof r||(l=d.length-1,c=d[l],Array.isArray(r)?r.length>0&&(r=we(r,(t||"")+"_"+i),xe(r[0])&&xe(c)&&(d[l]=P(c.text+r[0].text),r.shift()),d.push.apply(d,r)):s(r)?xe(c)?d[l]=P(c.text+r):""!==r&&d.push(P(r)):xe(r)&&xe(c)?d[l]=P(c.text+r.text):(a(e._isVList)&&o(r.tag)&&n(r.key)&&o(t)&&(r.key="__vlist"+t+"_"+i+"__"),d.push(r)));return d}function ye(e){var t=e.$options.provide;t&&(e._provided="function"==typeof t?t.call(e):t)}function _e(e){var t=Ce(e.$options.inject,e);t&&(F(!1),Object.keys(t).forEach(function(i){N(e,i,t[i])}),F(!0))}function Ce(e,t){if(e){for(var i=Object.create(null),n=lo?Reflect.ownKeys(e):Object.keys(e),o=0;o<n.length;o++){var a=n[o];if("__ob__"!==a){for(var r=e[a].from,s=t;s;){if(s._provided&&g(s._provided,r)){i[a]=s._provided[r];break}s=s.$parent}if(!s&&"default"in e[a]){var l=e[a].default;i[a]="function"==typeof l?l.call(t):l}}}return i}}function Se(e,t){if(!e||!e.length)return{};for(var i={},n=0,o=e.length;n<o;n++){var a=e[n],r=a.data;if(r&&r.attrs&&r.attrs.slot&&delete r.attrs.slot,a.context!==t&&a.fnContext!==t||!r||null==r.slot)(i.default||(i.default=[])).push(a);else{var s=r.slot,l=i[s]||(i[s]=[]);"template"===a.tag?l.push.apply(l,a.children||[]):l.push(a)}}for(var c in i)i[c].every($e)&&delete i[c];return i}function $e(e){return e.isComment&&!e.asyncFactory||" "===e.text}function De(e,t,i){var n,o=Object.keys(t).length>0,a=e?!!e.$stable:!o,r=e&&e.$key;if(e){if(e._normalized)return e._normalized;if(a&&i&&i!==Dn&&r===i.$key&&!o&&!i.$hasNormal)return i;n={};for(var s in e)e[s]&&"$"!==s[0]&&(n[s]=ze(t,s,e[s]))}else n={};for(var l in t)l in n||(n[l]=Oe(t,l));return e&&Object.isExtensible(e)&&(e._normalized=n),O(n,"$stable",a),O(n,"$key",r),O(n,"$hasNormal",o),n}function ze(e,t,i){var n=function(){var e=arguments.length?i.apply(null,arguments):i({});return e=e&&"object"==typeof e&&!Array.isArray(e)?[e]:ke(e),e&&(0===e.length||1===e.length&&e[0].isComment)?void 0:e};return i.proxy&&Object.defineProperty(e,t,{get:n,enumerable:!0,configurable:!0}),n}function Oe(e,t){return function(){return e[t]}}function Ae(e,t){var i,n,a,r,s;if(Array.isArray(e)||"string"==typeof e)for(i=new Array(e.length),n=0,a=e.length;n<a;n++)i[n]=t(e[n],n);else if("number"==typeof e)for(i=new Array(e),n=0;n<e;n++)i[n]=t(n+1,n);else if(l(e))if(lo&&e[Symbol.iterator]){i=[];for(var c=e[Symbol.iterator](),d=c.next();!d.done;)i.push(t(d.value,i.length)),d=c.next()}else for(r=Object.keys(e),i=new Array(r.length),n=0,a=r.length;n<a;n++)s=r[n],i[n]=t(e[s],s,n);return o(i)||(i=[]),i._isVList=!0,i}function Te(e,t,i,n){var o,a=this.$scopedSlots[e];a?(i=i||{},n&&(i=y(y({},n),i)),o=a(i)||t):o=this.$slots[e]||t;var r=i&&i.slot;return r?this.$createElement("template",{slot:r},o):o}function Me(e){return Z(this.$options,"filters",e,!0)||Vn}function je(e,t){return Array.isArray(e)?-1===e.indexOf(t):e!==t}function Pe(e,t,i,n,o){var a=Ln.keyCodes[t]||i;return o&&n&&!Ln.keyCodes[t]?je(o,n):a?je(a,e):n?En(n)!==t:void 0}function Ee(e,t,i,n,o){if(i)if(l(i)){Array.isArray(i)&&(i=_(i));var a;for(var r in i)!function(r){if("class"===r||"style"===r||On(r))a=e;else{var s=e.attrs&&e.attrs.type;a=n||Ln.mustUseProp(t,s,r)?e.domProps||(e.domProps={}):e.attrs||(e.attrs={})}var l=Mn(r),c=En(r);if(!(l in a||c in a)&&(a[r]=i[r],o)){(e.on||(e.on={}))["update:"+r]=function(e){i[r]=e}}}(r)}else;return e}function Fe(e,t){var i=this._staticTrees||(this._staticTrees=[]),n=i[e];return n&&!t?n:(n=i[e]=this.$options.staticRenderFns[e].call(this._renderProxy,null,this),Ve(n,"__static__"+e,!1),n)}function Be(e,t,i){return Ve(e,"__once__"+t+(i?"_"+i:""),!0),e}function Ve(e,t,i){if(Array.isArray(e))for(var n=0;n<e.length;n++)e[n]&&"string"!=typeof e[n]&&Ie(e[n],t+"_"+n,i);else Ie(e,t,i)}function Ie(e,t,i){e.isStatic=!0,e.key=t,e.isOnce=i}function Ne(e,t){if(t)if(c(t)){var i=e.on=e.on?y({},e.on):{};for(var n in t){var o=i[n],a=t[n];i[n]=o?[].concat(o,a):a}}else;return e}function Re(e,t,i,n){t=t||{$stable:!i};for(var o=0;o<e.length;o++){var a=e[o];Array.isArray(a)?Re(a,t,i):a&&(a.proxy&&(a.fn.proxy=!0),t[a.key]=a.fn)}return n&&(t.$key=n),t}function Le(e,t){for(var i=0;i<t.length;i+=2){var n=t[i];"string"==typeof n&&n&&(e[t[i]]=t[i+1])}return e}function He(e,t){return"string"==typeof e?t+e:e}function qe(e){e._o=Be,e._n=h,e._s=p,e._l=Ae,e._t=Te,e._q=S,e._i=$,e._m=Fe,e._f=Me,e._k=Pe,e._b=Ee,e._v=P,e._e=mo,e._u=Re,e._g=Ne,e._d=Le,e._p=He}function Ue(e,t,i,n,o){var r,s=this,l=o.options;g(n,"_uid")?(r=Object.create(n),r._original=n):(r=n,n=n._original);var c=a(l._compiled),d=!c;this.data=e,this.props=t,this.children=i,this.parent=n,this.listeners=e.on||Dn,this.injections=Ce(l.inject,n),this.slots=function(){return s.$slots||De(e.scopedSlots,s.$slots=Se(i,n)),s.$slots},Object.defineProperty(this,"scopedSlots",{enumerable:!0,get:function(){return De(e.scopedSlots,this.slots())}}),c&&(this.$options=l,this.$slots=this.slots(),this.$scopedSlots=De(e.scopedSlots,this.$slots)),l._scopeId?this._c=function(e,t,i,o){var a=et(r,e,t,i,o,d);return a&&!Array.isArray(a)&&(a.fnScopeId=l._scopeId,a.fnContext=n),a}:this._c=function(e,t,i,n){return et(r,e,t,i,n,d)}}function We(e,t,i,n,a){var r=e.options,s={},l=r.props;if(o(l))for(var c in l)s[c]=ee(c,l,t||Dn);else o(i.attrs)&&Ge(s,i.attrs),o(i.props)&&Ge(s,i.props);var d=new Ue(i,s,a,n,e),u=r.render.call(null,d._c,d);if(u instanceof ho)return Ye(u,i,d.parent,r,d);if(Array.isArray(u)){for(var f=ke(u)||[],p=new Array(f.length),h=0;h<f.length;h++)p[h]=Ye(f[h],i,d.parent,r,d);return p}}function Ye(e,t,i,n,o){var a=E(e);return a.fnContext=i,a.fnOptions=n,t.slot&&((a.data||(a.data={})).slot=t.slot),a}function Ge(e,t){for(var i in t)e[Mn(i)]=t[i]}function Ke(e,t,i,r,s){if(!n(e)){var c=i.$options._base;if(l(e)&&(e=c.extend(e)),"function"==typeof e){var d;if(n(e.cid)&&(d=e,void 0===(e=st(d,c))))return rt(d,t,i,r,s);t=t||{},Lt(e),o(t.model)&&Ze(e.options,t);var u=me(t,e,s);if(a(e.options.functional))return We(e,u,t,i,r);var f=t.on;if(t.on=t.nativeOn,a(e.options.abstract)){var p=t.slot;t={},p&&(t.slot=p)}Je(t);var h=e.options.name||s;return new ho("vue-component-"+e.cid+(h?"-"+h:""),t,void 0,void 0,void 0,i,{Ctor:e,propsData:u,listeners:f,tag:s,children:r},d)}}}function Xe(e,t){var i={_isComponent:!0,_parentVnode:e,parent:t},n=e.data.inlineTemplate;return o(n)&&(i.render=n.render,i.staticRenderFns=n.staticRenderFns),new e.componentOptions.Ctor(i)}function Je(e){for(var t=e.hook||(e.hook={}),i=0;i<Fo.length;i++){var n=Fo[i],o=t[n],a=Eo[n];o===a||o&&o._merged||(t[n]=o?Qe(a,o):a)}}function Qe(e,t){var i=function(i,n){e(i,n),t(i,n)};return i._merged=!0,i}function Ze(e,t){var i=e.model&&e.model.prop||"value",n=e.model&&e.model.event||"input";(t.attrs||(t.attrs={}))[i]=t.model.value;var a=t.on||(t.on={}),r=a[n],s=t.model.callback;o(r)?(Array.isArray(r)?-1===r.indexOf(s):r!==s)&&(a[n]=[s].concat(r)):a[n]=s}function et(e,t,i,n,o,r){return(Array.isArray(i)||s(i))&&(o=n,n=i,i=void 0),a(r)&&(o=Vo),tt(e,t,i,n,o)}function tt(e,t,i,n,a){if(o(i)&&o(i.__ob__))return mo();if(o(i)&&o(i.is)&&(t=i.is),!t)return mo();Array.isArray(n)&&"function"==typeof n[0]&&(i=i||{},i.scopedSlots={default:n[0]},n.length=0),a===Vo?n=ke(n):a===Bo&&(n=ve(n));var r,s;if("string"==typeof t){var l;s=e.$vnode&&e.$vnode.ns||Ln.getTagNamespace(t),r=Ln.isReservedTag(t)?new ho(Ln.parsePlatformTagName(t),i,n,void 0,void 0,e):i&&i.pre||!o(l=Z(e.$options,"components",t))?new ho(t,i,n,void 0,void 0,e):Ke(l,i,e,n,t)}else r=Ke(t,i,e,n);return Array.isArray(r)?r:o(r)?(o(s)&&it(r,s),o(i)&&nt(i),r):mo()}function it(e,t,i){if(e.ns=t,"foreignObject"===e.tag&&(t=void 0,i=!0),o(e.children))for(var r=0,s=e.children.length;r<s;r++){var l=e.children[r];o(l.tag)&&(n(l.ns)||a(i)&&"svg"!==l.tag)&&it(l,t,i)}}function nt(e){l(e.style)&&ue(e.style),l(e.class)&&ue(e.class)}function ot(e){e._vnode=null,e._staticTrees=null;var t=e.$options,i=e.$vnode=t._parentVnode,n=i&&i.context;e.$slots=Se(t._renderChildren,n),e.$scopedSlots=Dn,e._c=function(t,i,n,o){return et(e,t,i,n,o,!1)},e.$createElement=function(t,i,n,o){return et(e,t,i,n,o,!0)};var o=i&&i.data;N(e,"$attrs",o&&o.attrs||Dn,null,!0),N(e,"$listeners",t._parentListeners||Dn,null,!0)}function at(e,t){return(e.__esModule||lo&&"Module"===e[Symbol.toStringTag])&&(e=e.default),l(e)?t.extend(e):e}function rt(e,t,i,n,o){var a=mo();return a.asyncFactory=e,a.asyncMeta={data:t,context:i,children:n,tag:o},a}function st(e,t){if(a(e.error)&&o(e.errorComp))return e.errorComp;if(o(e.resolved))return e.resolved;var i=Io;if(i&&o(e.owners)&&-1===e.owners.indexOf(i)&&e.owners.push(i),a(e.loading)&&o(e.loadingComp))return e.loadingComp;if(i&&!o(e.owners)){var r=e.owners=[i],s=!0,c=null,d=null;i.$on("hook:destroyed",function(){return m(r,i)});var u=function(e){for(var t=0,i=r.length;t<i;t++)r[t].$forceUpdate();e&&(r.length=0,null!==c&&(clearTimeout(c),c=null),null!==d&&(clearTimeout(d),d=null))},p=D(function(i){e.resolved=at(i,t),s?r.length=0:u(!0)}),h=D(function(t){o(e.errorComp)&&(e.error=!0,u(!0))}),b=e(p,h);return l(b)&&(f(b)?n(e.resolved)&&b.then(p,h):f(b.component)&&(b.component.then(p,h),o(b.error)&&(e.errorComp=at(b.error,t)),o(b.loading)&&(e.loadingComp=at(b.loading,t),0===b.delay?e.loading=!0:c=setTimeout(function(){c=null,n(e.resolved)&&n(e.error)&&(e.loading=!0,u(!1))},b.delay||200)),o(b.timeout)&&(d=setTimeout(function(){d=null,n(e.resolved)&&h(null)},b.timeout)))),s=!1,e.loading?e.loadingComp:e.resolved}}function lt(e){return e.isComment&&e.asyncFactory}function ct(e){if(Array.isArray(e))for(var t=0;t<e.length;t++){var i=e[t];if(o(i)&&(o(i.componentOptions)||lt(i)))return i}}function dt(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&ht(e,t)}function ut(e,t){Po.$on(e,t)}function ft(e,t){Po.$off(e,t)}function pt(e,t){var i=Po;return function n(){null!==t.apply(null,arguments)&&i.$off(e,n)}}function ht(e,t,i){Po=e,he(t,i||{},ut,ft,pt,e),Po=void 0}function bt(e){var t=No;return No=e,function(){No=t}}function mt(e){var t=e.$options,i=t.parent;if(i&&!t.abstract){for(;i.$options.abstract&&i.$parent;)i=i.$parent;i.$children.push(e)}e.$parent=i,e.$root=i?i.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}function gt(e,t,i){e.$el=t,e.$options.render||(e.$options.render=mo),yt(e,"beforeMount");var n;return n=function(){e._update(e._render(),i)},new Jo(e,n,C,{before:function(){e._isMounted&&!e._isDestroyed&&yt(e,"beforeUpdate")}},!0),i=!1,null==e.$vnode&&(e._isMounted=!0,yt(e,"mounted")),e}function vt(e,t,i,n,o){var a=n.data.scopedSlots,r=e.$scopedSlots,s=!!(a&&!a.$stable||r!==Dn&&!r.$stable||a&&e.$scopedSlots.$key!==a.$key),l=!!(o||e.$options._renderChildren||s);if(e.$options._parentVnode=n,e.$vnode=n,e._vnode&&(e._vnode.parent=n),e.$options._renderChildren=o,e.$attrs=n.data.attrs||Dn,e.$listeners=i||Dn,t&&e.$options.props){F(!1);for(var c=e._props,d=e.$options._propKeys||[],u=0;u<d.length;u++){var f=d[u],p=e.$options.props;c[f]=ee(f,p,t,e)}F(!0),e.$options.propsData=t}i=i||Dn;var h=e.$options._parentListeners;e.$options._parentListeners=i,ht(e,i,h),l&&(e.$slots=Se(o,n.context),e.$forceUpdate())}function kt(e){for(;e&&(e=e.$parent);)if(e._inactive)return!0;return!1}function xt(e,t){if(t){if(e._directInactive=!1,kt(e))return}else if(e._directInactive)return;if(e._inactive||null===e._inactive){e._inactive=!1;for(var i=0;i<e.$children.length;i++)xt(e.$children[i]);yt(e,"activated")}}function wt(e,t){if(!(t&&(e._directInactive=!0,kt(e))||e._inactive)){e._inactive=!0;for(var i=0;i<e.$children.length;i++)wt(e.$children[i]);yt(e,"deactivated")}}function yt(e,t){M();var i=e.$options[t],n=t+" hook";if(i)for(var o=0,a=i.length;o<a;o++)re(i[o],e,null,e,n);e._hasHookEvent&&e.$emit("hook:"+t),j()}function _t(){Wo=Ro.length=Lo.length=0,Ho={},qo=Uo=!1}function Ct(){Yo=Go(),Uo=!0;var e,t;for(Ro.sort(function(e,t){return e.id-t.id}),Wo=0;Wo<Ro.length;Wo++)e=Ro[Wo],e.before&&e.before(),t=e.id,Ho[t]=null,e.run();var i=Lo.slice(),n=Ro.slice();_t(),Dt(i),St(n),so&&Ln.devtools&&so.emit("flush")}function St(e){for(var t=e.length;t--;){var i=e[t],n=i.vm;n._watcher===i&&n._isMounted&&!n._isDestroyed&&yt(n,"updated")}}function $t(e){e._inactive=!1,Lo.push(e)}function Dt(e){for(var t=0;t<e.length;t++)e[t]._inactive=!0,xt(e[t],!0)}function zt(e){var t=e.id;if(null==Ho[t]){if(Ho[t]=!0,Uo){for(var i=Ro.length-1;i>Wo&&Ro[i].id>e.id;)i--;Ro.splice(i+1,0,e)}else Ro.push(e);qo||(qo=!0,de(Ct))}}function Ot(e,t,i){Qo.get=function(){return this[t][i]},Qo.set=function(e){this[t][i]=e},Object.defineProperty(e,i,Qo)}function At(e){e._watchers=[];var t=e.$options;t.props&&Tt(e,t.props),t.methods&&Vt(e,t.methods),t.data?Mt(e):I(e._data={},!0),t.computed&&Pt(e,t.computed),t.watch&&t.watch!==to&&It(e,t.watch)}function Tt(e,t){var i=e.$options.propsData||{},n=e._props={},o=e.$options._propKeys=[],a=!e.$parent;a||F(!1);for(var r in t)!function(a){o.push(a);var r=ee(a,t,i,e);N(n,a,r),a in e||Ot(e,"_props",a)}(r);F(!0)}function Mt(e){var t=e.$options.data;t=e._data="function"==typeof t?jt(t,e):t||{},c(t)||(t={});for(var i=Object.keys(t),n=e.$options.props,o=(e.$options.methods,i.length);o--;){var a=i[o];n&&g(n,a)||z(a)||Ot(e,"_data",a)}I(t,!0)}function jt(e,t){M();try{return e.call(t,t)}catch(e){return ae(e,t,"data()"),{}}finally{j()}}function Pt(e,t){var i=e._computedWatchers=Object.create(null),n=ro();for(var o in t){var a=t[o],r="function"==typeof a?a:a.get;n||(i[o]=new Jo(e,r||C,C,Zo)),o in e||Et(e,o,a)}}function Et(e,t,i){var n=!ro();"function"==typeof i?(Qo.get=n?Ft(t):Bt(i),Qo.set=C):(Qo.get=i.get?n&&!1!==i.cache?Ft(t):Bt(i.get):C,Qo.set=i.set||C),Object.defineProperty(e,t,Qo)}function Ft(e){return function(){var t=this._computedWatchers&&this._computedWatchers[e];if(t)return t.dirty&&t.evaluate(),fo.target&&t.depend(),t.value}}function Bt(e){return function(){return e.call(this,this)}}function Vt(e,t){e.$options.props;for(var i in t)e[i]="function"!=typeof t[i]?C:Fn(t[i],e)}function It(e,t){for(var i in t){var n=t[i];if(Array.isArray(n))for(var o=0;o<n.length;o++)Nt(e,i,n[o]);else Nt(e,i,n)}}function Nt(e,t,i,n){return c(i)&&(n=i,i=i.handler),"string"==typeof i&&(i=e[i]),e.$watch(t,i,n)}function Rt(e,t){var i=e.$options=Object.create(e.constructor.options),n=t._parentVnode;i.parent=t.parent,i._parentVnode=n;var o=n.componentOptions;i.propsData=o.propsData,i._parentListeners=o.listeners,i._renderChildren=o.children,i._componentTag=o.tag,t.render&&(i.render=t.render,i.staticRenderFns=t.staticRenderFns)}function Lt(e){var t=e.options;if(e.super){var i=Lt(e.super);if(i!==e.superOptions){e.superOptions=i;var n=Ht(e);n&&y(e.extendOptions,n),t=e.options=Q(i,e.extendOptions),t.name&&(t.components[t.name]=e)}}return t}function Ht(e){var t,i=e.options,n=e.sealedOptions;for(var o in i)i[o]!==n[o]&&(t||(t={}),t[o]=i[o]);return t}function qt(e){this._init(e)}function Ut(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var i=w(arguments,1);return i.unshift(this),"function"==typeof e.install?e.install.apply(e,i):"function"==typeof e&&e.apply(null,i),t.push(e),this}}function Wt(e){e.mixin=function(e){return this.options=Q(this.options,e),this}}function Yt(e){e.cid=0;var t=1;e.extend=function(e){e=e||{};var i=this,n=i.cid,o=e._Ctor||(e._Ctor={});if(o[n])return o[n];var a=e.name||i.options.name,r=function(e){this._init(e)};return r.prototype=Object.create(i.prototype),r.prototype.constructor=r,r.cid=t++,r.options=Q(i.options,e),r.super=i,r.options.props&&Gt(r),r.options.computed&&Kt(r),r.extend=i.extend,r.mixin=i.mixin,r.use=i.use,Nn.forEach(function(e){r[e]=i[e]}),a&&(r.options.components[a]=r),r.superOptions=i.options,r.extendOptions=e,r.sealedOptions=y({},r.options),o[n]=r,r}}function Gt(e){var t=e.options.props;for(var i in t)Ot(e.prototype,"_props",i)}function Kt(e){var t=e.options.computed;for(var i in t)Et(e.prototype,i,t[i])}function Xt(e){Nn.forEach(function(t){e[t]=function(e,i){return i?("component"===t&&c(i)&&(i.name=i.name||e,i=this.options._base.extend(i)),"directive"===t&&"function"==typeof i&&(i={bind:i,update:i}),this.options[t+"s"][e]=i,i):this.options[t+"s"][e]}})}function Jt(e){return e&&(e.Ctor.options.name||e.tag)}function Qt(e,t){return Array.isArray(e)?e.indexOf(t)>-1:"string"==typeof e?e.split(",").indexOf(t)>-1:!!d(e)&&e.test(t)}function Zt(e,t){var i=e.cache,n=e.keys,o=e._vnode;for(var a in i){var r=i[a];if(r){var s=Jt(r.componentOptions);s&&!t(s)&&ei(i,a,n,o)}}}function ei(e,t,i,n){var o=e[t];!o||n&&o.tag===n.tag||o.componentInstance.$destroy(),e[t]=null,m(i,t)}function ti(e){for(var t=e.data,i=e,n=e;o(n.componentInstance);)(n=n.componentInstance._vnode)&&n.data&&(t=ii(n.data,t));for(;o(i=i.parent);)i&&i.data&&(t=ii(t,i.data));return ni(t.staticClass,t.class)}function ii(e,t){return{staticClass:oi(e.staticClass,t.staticClass),class:o(e.class)?[e.class,t.class]:t.class}}function ni(e,t){return o(e)||o(t)?oi(e,ai(t)):""}function oi(e,t){return e?t?e+" "+t:e:t||""}function ai(e){return Array.isArray(e)?ri(e):l(e)?si(e):"string"==typeof e?e:""}function ri(e){for(var t,i="",n=0,a=e.length;n<a;n++)o(t=ai(e[n]))&&""!==t&&(i&&(i+=" "),i+=t);return i}function si(e){var t="";for(var i in e)e[i]&&(t&&(t+=" "),t+=i);return t}function li(e){return xa(e)?"svg":"math"===e?"math":void 0}function ci(e){if(!Wn)return!0;if(wa(e))return!1;if(e=e.toLowerCase(),null!=ya[e])return ya[e];var t=document.createElement(e);return e.indexOf("-")>-1?ya[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:ya[e]=/HTMLUnknownElement/.test(t.toString())}function di(e){if("string"==typeof e){var t=document.querySelector(e);return t||document.createElement("div")}return e}function ui(e,t){var i=document.createElement(e);return"select"!==e?i:(t.data&&t.data.attrs&&void 0!==t.data.attrs.multiple&&i.setAttribute("multiple","multiple"),i)}function fi(e,t){return document.createElementNS(va[e],t)}function pi(e){return document.createTextNode(e)}function hi(e){return document.createComment(e)}function bi(e,t,i){e.insertBefore(t,i)}function mi(e,t){e.removeChild(t)}function gi(e,t){e.appendChild(t)}function vi(e){return e.parentNode}function ki(e){return e.nextSibling}function xi(e){return e.tagName}function wi(e,t){e.textContent=t}function yi(e,t){e.setAttribute(t,"")}function _i(e,t){var i=e.data.ref;if(o(i)){var n=e.context,a=e.componentInstance||e.elm,r=n.$refs;t?Array.isArray(r[i])?m(r[i],a):r[i]===a&&(r[i]=void 0):e.data.refInFor?Array.isArray(r[i])?r[i].indexOf(a)<0&&r[i].push(a):r[i]=[a]:r[i]=a}}function Ci(e,t){return e.key===t.key&&(e.tag===t.tag&&e.isComment===t.isComment&&o(e.data)===o(t.data)&&Si(e,t)||a(e.isAsyncPlaceholder)&&e.asyncFactory===t.asyncFactory&&n(t.asyncFactory.error))}function Si(e,t){if("input"!==e.tag)return!0;var i,n=o(i=e.data)&&o(i=i.attrs)&&i.type,a=o(i=t.data)&&o(i=i.attrs)&&i.type;return n===a||_a(n)&&_a(a)}function $i(e,t,i){var n,a,r={};for(n=t;n<=i;++n)a=e[n].key,o(a)&&(r[a]=n);return r}function Di(e,t){(e.data.directives||t.data.directives)&&zi(e,t)}function zi(e,t){var i,n,o,a=e===$a,r=t===$a,s=Oi(e.data.directives,e.context),l=Oi(t.data.directives,t.context),c=[],d=[];for(i in l)n=s[i],o=l[i],n?(o.oldValue=n.value,o.oldArg=n.arg,Ti(o,"update",t,e),o.def&&o.def.componentUpdated&&d.push(o)):(Ti(o,"bind",t,e),o.def&&o.def.inserted&&c.push(o));if(c.length){var u=function(){for(var i=0;i<c.length;i++)Ti(c[i],"inserted",t,e)};a?be(t,"insert",u):u()}if(d.length&&be(t,"postpatch",function(){for(var i=0;i<d.length;i++)Ti(d[i],"componentUpdated",t,e)}),!a)for(i in s)l[i]||Ti(s[i],"unbind",e,e,r)}function Oi(e,t){var i=Object.create(null);if(!e)return i;var n,o;for(n=0;n<e.length;n++)o=e[n],o.modifiers||(o.modifiers=Oa),i[Ai(o)]=o,o.def=Z(t.$options,"directives",o.name,!0);return i}function Ai(e){return e.rawName||e.name+"."+Object.keys(e.modifiers||{}).join(".")}function Ti(e,t,i,n,o){var a=e.def&&e.def[t];if(a)try{a(i.elm,e,i,n,o)}catch(n){ae(n,i.context,"directive "+e.name+" "+t+" hook")}}function Mi(e,t){var i=t.componentOptions;if(!(o(i)&&!1===i.Ctor.options.inheritAttrs||n(e.data.attrs)&&n(t.data.attrs))){var a,r,s=t.elm,l=e.data.attrs||{},c=t.data.attrs||{};o(c.__ob__)&&(c=t.data.attrs=y({},c));for(a in c)r=c[a],l[a]!==r&&ji(s,a,r);(Xn||Qn)&&c.value!==l.value&&ji(s,"value",c.value);for(a in l)n(c[a])&&(ba(a)?s.removeAttributeNS(ha,ma(a)):da(a)||s.removeAttribute(a))}}function ji(e,t,i){e.tagName.indexOf("-")>-1?Pi(e,t,i):pa(t)?ga(i)?e.removeAttribute(t):(i="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,i)):da(t)?e.setAttribute(t,fa(t,i)):ba(t)?ga(i)?e.removeAttributeNS(ha,ma(t)):e.setAttributeNS(ha,t,i):Pi(e,t,i)}function Pi(e,t,i){if(ga(i))e.removeAttribute(t);else{if(Xn&&!Jn&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==i&&!e.__ieph){var n=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",n)};e.addEventListener("input",n),e.__ieph=!0}e.setAttribute(t,i)}}function Ei(e,t){var i=t.elm,a=t.data,r=e.data;if(!(n(a.staticClass)&&n(a.class)&&(n(r)||n(r.staticClass)&&n(r.class)))){var s=ti(t),l=i._transitionClasses;o(l)&&(s=oi(s,ai(l))),s!==i._prevClass&&(i.setAttribute("class",s),i._prevClass=s)}}function Fi(e){if(o(e[ja])){var t=Xn?"change":"input";e[t]=[].concat(e[ja],e[t]||[]),delete e[ja]}o(e[Pa])&&(e.change=[].concat(e[Pa],e.change||[]),delete e[Pa])}function Bi(e,t,i){var n=oa;return function o(){null!==t.apply(null,arguments)&&Ii(e,o,i,n)}}function Vi(e,t,i,n){if(Ea){var o=Yo,a=t;t=a._wrapper=function(e){if(e.target===e.currentTarget||e.timeStamp>=o||e.timeStamp<=0||e.target.ownerDocument!==document)return a.apply(this,arguments)}}oa.addEventListener(e,t,io?{capture:i,passive:n}:i)}function Ii(e,t,i,n){(n||oa).removeEventListener(e,t._wrapper||t,i)}function Ni(e,t){if(!n(e.data.on)||!n(t.data.on)){var i=t.data.on||{},o=e.data.on||{};oa=t.elm,Fi(i),he(i,o,Vi,Ii,Bi,t.context),oa=void 0}}function Ri(e,t){if(!n(e.data.domProps)||!n(t.data.domProps)){var i,a,r=t.elm,s=e.data.domProps||{},l=t.data.domProps||{};o(l.__ob__)&&(l=t.data.domProps=y({},l));for(i in s)i in l||(r[i]="");for(i in l){if(a=l[i],"textContent"===i||"innerHTML"===i){if(t.children&&(t.children.length=0),a===s[i])continue;1===r.childNodes.length&&r.removeChild(r.childNodes[0])}if("value"===i&&"PROGRESS"!==r.tagName){r._value=a;var c=n(a)?"":String(a);Li(r,c)&&(r.value=c)}else if("innerHTML"===i&&xa(r.tagName)&&n(r.innerHTML)){aa=aa||document.createElement("div"),aa.innerHTML="<svg>"+a+"</svg>";for(var d=aa.firstChild;r.firstChild;)r.removeChild(r.firstChild);for(;d.firstChild;)r.appendChild(d.firstChild)}else if(a!==s[i])try{r[i]=a}catch(e){}}}}function Li(e,t){return!e.composing&&("OPTION"===e.tagName||Hi(e,t)||qi(e,t))}function Hi(e,t){var i=!0;try{i=document.activeElement!==e}catch(e){}return i&&e.value!==t}function qi(e,t){var i=e.value,n=e._vModifiers;if(o(n)){if(n.number)return h(i)!==h(t);if(n.trim)return i.trim()!==t.trim()}return i!==t}function Ui(e){var t=Wi(e.style);return e.staticStyle?y(e.staticStyle,t):t}function Wi(e){return Array.isArray(e)?_(e):"string"==typeof e?Va(e):e}function Yi(e,t){var i,n={};if(t)for(var o=e;o.componentInstance;)(o=o.componentInstance._vnode)&&o.data&&(i=Ui(o.data))&&y(n,i);(i=Ui(e.data))&&y(n,i);for(var a=e;a=a.parent;)a.data&&(i=Ui(a.data))&&y(n,i);return n}function Gi(e,t){var i=t.data,a=e.data;if(!(n(i.staticStyle)&&n(i.style)&&n(a.staticStyle)&&n(a.style))){var r,s,l=t.elm,c=a.staticStyle,d=a.normalizedStyle||a.style||{},u=c||d,f=Wi(t.data.style)||{};t.data.normalizedStyle=o(f.__ob__)?y({},f):f;var p=Yi(t,!0);for(s in u)n(p[s])&&Ra(l,s,"");for(s in p)(r=p[s])!==u[s]&&Ra(l,s,null==r?"":r)}}function Ki(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(Ua).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var i=" "+(e.getAttribute("class")||"")+" ";i.indexOf(" "+t+" ")<0&&e.setAttribute("class",(i+t).trim())}}function Xi(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(Ua).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var i=" "+(e.getAttribute("class")||"")+" ",n=" "+t+" ";i.indexOf(n)>=0;)i=i.replace(n," ");i=i.trim(),i?e.setAttribute("class",i):e.removeAttribute("class")}}function Ji(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&y(t,Wa(e.name||"v")),y(t,e),t}return"string"==typeof e?Wa(e):void 0}}function Qi(e){er(function(){er(e)})}function Zi(e,t){var i=e._transitionClasses||(e._transitionClasses=[]);i.indexOf(t)<0&&(i.push(t),Ki(e,t))}function en(e,t){e._transitionClasses&&m(e._transitionClasses,t),Xi(e,t)}function tn(e,t,i){var n=nn(e,t),o=n.type,a=n.timeout,r=n.propCount;if(!o)return i();var s=o===Ga?Ja:Za,l=0,c=function(){e.removeEventListener(s,d),i()},d=function(t){t.target===e&&++l>=r&&c()};setTimeout(function(){l<r&&c()},a+1),e.addEventListener(s,d)}function nn(e,t){var i,n=window.getComputedStyle(e),o=(n[Xa+"Delay"]||"").split(", "),a=(n[Xa+"Duration"]||"").split(", "),r=on(o,a),s=(n[Qa+"Delay"]||"").split(", "),l=(n[Qa+"Duration"]||"").split(", "),c=on(s,l),d=0,u=0;return t===Ga?r>0&&(i=Ga,d=r,u=a.length):t===Ka?c>0&&(i=Ka,d=c,u=l.length):(d=Math.max(r,c),i=d>0?r>c?Ga:Ka:null,u=i?i===Ga?a.length:l.length:0),{type:i,timeout:d,propCount:u,hasTransform:i===Ga&&tr.test(n[Xa+"Property"])}}function on(e,t){for(;e.length<t.length;)e=e.concat(e);return Math.max.apply(null,t.map(function(t,i){return an(t)+an(e[i])}))}function an(e){return 1e3*Number(e.slice(0,-1).replace(",","."))}function rn(e,t){var i=e.elm;o(i._leaveCb)&&(i._leaveCb.cancelled=!0,i._leaveCb());var a=Ji(e.data.transition);if(!n(a)&&!o(i._enterCb)&&1===i.nodeType){for(var r=a.css,s=a.type,c=a.enterClass,d=a.enterToClass,u=a.enterActiveClass,f=a.appearClass,p=a.appearToClass,b=a.appearActiveClass,m=a.beforeEnter,g=a.enter,v=a.afterEnter,k=a.enterCancelled,x=a.beforeAppear,w=a.appear,y=a.afterAppear,_=a.appearCancelled,C=a.duration,S=No,$=No.$vnode;$&&$.parent;)S=$.context,$=$.parent;var z=!S._isMounted||!e.isRootInsert;if(!z||w||""===w){var O=z&&f?f:c,A=z&&b?b:u,T=z&&p?p:d,M=z?x||m:m,j=z&&"function"==typeof w?w:g,P=z?y||v:v,E=z?_||k:k,F=h(l(C)?C.enter:C),B=!1!==r&&!Jn,V=cn(j),I=i._enterCb=D(function(){B&&(en(i,T),en(i,A)),I.cancelled?(B&&en(i,O),E&&E(i)):P&&P(i),i._enterCb=null});e.data.show||be(e,"insert",function(){var t=i.parentNode,n=t&&t._pending&&t._pending[e.key];n&&n.tag===e.tag&&n.elm._leaveCb&&n.elm._leaveCb(),j&&j(i,I)}),M&&M(i),B&&(Zi(i,O),Zi(i,A),Qi(function(){en(i,O),I.cancelled||(Zi(i,T),V||(ln(F)?setTimeout(I,F):tn(i,s,I)))})),e.data.show&&(t&&t(),j&&j(i,I)),B||V||I()}}}function sn(e,t){function i(){_.cancelled||(!e.data.show&&a.parentNode&&((a.parentNode._pending||(a.parentNode._pending={}))[e.key]=e),p&&p(a),x&&(Zi(a,d),Zi(a,f),Qi(function(){en(a,d),_.cancelled||(Zi(a,u),w||(ln(y)?setTimeout(_,y):tn(a,c,_)))})),b&&b(a,_),x||w||_())}var a=e.elm;o(a._enterCb)&&(a._enterCb.cancelled=!0,a._enterCb());var r=Ji(e.data.transition);if(n(r)||1!==a.nodeType)return t();if(!o(a._leaveCb)){var s=r.css,c=r.type,d=r.leaveClass,u=r.leaveToClass,f=r.leaveActiveClass,p=r.beforeLeave,b=r.leave,m=r.afterLeave,g=r.leaveCancelled,v=r.delayLeave,k=r.duration,x=!1!==s&&!Jn,w=cn(b),y=h(l(k)?k.leave:k),_=a._leaveCb=D(function(){a.parentNode&&a.parentNode._pending&&(a.parentNode._pending[e.key]=null),x&&(en(a,u),en(a,f)),_.cancelled?(x&&en(a,d),g&&g(a)):(t(),m&&m(a)),a._leaveCb=null});v?v(i):i()}}function ln(e){return"number"==typeof e&&!isNaN(e)}function cn(e){if(n(e))return!1;var t=e.fns;return o(t)?cn(Array.isArray(t)?t[0]:t):(e._length||e.length)>1}function dn(e,t){!0!==t.data.show&&rn(t)}function un(e,t,i){fn(e,t,i),(Xn||Qn)&&setTimeout(function(){fn(e,t,i)},0)}function fn(e,t,i){var n=t.value,o=e.multiple;if(!o||Array.isArray(n)){for(var a,r,s=0,l=e.options.length;s<l;s++)if(r=e.options[s],o)a=$(n,hn(r))>-1,r.selected!==a&&(r.selected=a);else if(S(hn(r),n))return void(e.selectedIndex!==s&&(e.selectedIndex=s));o||(e.selectedIndex=-1)}}function pn(e,t){return t.every(function(t){return!S(t,e)})}function hn(e){return"_value"in e?e._value:e.value}function bn(e){e.target.composing=!0}function mn(e){e.target.composing&&(e.target.composing=!1,gn(e.target,"input"))}function gn(e,t){var i=document.createEvent("HTMLEvents");i.initEvent(t,!0,!0),e.dispatchEvent(i)}function vn(e){return!e.componentInstance||e.data&&e.data.transition?e:vn(e.componentInstance._vnode)}function kn(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?kn(ct(t.children)):e}function xn(e){var t={},i=e.$options;for(var n in i.propsData)t[n]=e[n];var o=i._parentListeners;for(var a in o)t[Mn(a)]=o[a];return t}function wn(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}function yn(e){for(;e=e.parent;)if(e.data.transition)return!0}function _n(e,t){return t.key===e.key&&t.tag===e.tag}function Cn(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function Sn(e){e.data.newPos=e.elm.getBoundingClientRect()}function $n(e){var t=e.data.pos,i=e.data.newPos,n=t.left-i.left,o=t.top-i.top;if(n||o){e.data.moved=!0;var a=e.elm.style;a.transform=a.WebkitTransform="translate("+n+"px,"+o+"px)",a.transitionDuration="0s"}}/*! + * Vue.js v2.6.10 + * (c) 2014-2019 Evan You + * Released under the MIT License. + */ +var Dn=Object.freeze({}),zn=Object.prototype.toString,On=(b("slot,component",!0),b("key,ref,slot,slot-scope,is")),An=Object.prototype.hasOwnProperty,Tn=/-(\w)/g,Mn=v(function(e){return e.replace(Tn,function(e,t){return t?t.toUpperCase():""})}),jn=v(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),Pn=/\B([A-Z])/g,En=v(function(e){return e.replace(Pn,"-$1").toLowerCase()}),Fn=Function.prototype.bind?x:k,Bn=function(e,t,i){return!1},Vn=function(e){return e},In="data-server-rendered",Nn=["component","directive","filter"],Rn=["beforeCreate","created","beforeMount","mounted","beforeUpdate","updated","beforeDestroy","destroyed","activated","deactivated","errorCaptured","serverPrefetch"],Ln={optionMergeStrategies:Object.create(null),silent:!1,productionTip:!1,devtools:!1,performance:!1,errorHandler:null,warnHandler:null,ignoredElements:[],keyCodes:Object.create(null),isReservedTag:Bn,isReservedAttr:Bn,isUnknownElement:Bn,getTagNamespace:C,parsePlatformTagName:Vn,mustUseProp:Bn,async:!0,_lifecycleHooks:Rn},Hn=/a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/,qn=new RegExp("[^"+Hn.source+".$_\\d]"),Un="__proto__"in{},Wn="undefined"!=typeof window,Yn="undefined"!=typeof WXEnvironment&&!!WXEnvironment.platform,Gn=Yn&&WXEnvironment.platform.toLowerCase(),Kn=Wn&&window.navigator.userAgent.toLowerCase(),Xn=Kn&&/msie|trident/.test(Kn),Jn=Kn&&Kn.indexOf("msie 9.0")>0,Qn=Kn&&Kn.indexOf("edge/")>0,Zn=(Kn&&Kn.indexOf("android"),Kn&&/iphone|ipad|ipod|ios/.test(Kn)||"ios"===Gn),eo=(Kn&&/chrome\/\d+/.test(Kn),Kn&&/phantomjs/.test(Kn),Kn&&Kn.match(/firefox\/(\d+)/)),to={}.watch,io=!1;if(Wn)try{var no={};Object.defineProperty(no,"passive",{get:function(){io=!0}}),window.addEventListener("test-passive",null,no)}catch(e){}var oo,ao,ro=function(){return void 0===oo&&(oo=!Wn&&!Yn&&void 0!==e&&(e.process&&"server"===e.process.env.VUE_ENV)),oo},so=Wn&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,lo="undefined"!=typeof Symbol&&T(Symbol)&&"undefined"!=typeof Reflect&&T(Reflect.ownKeys);ao="undefined"!=typeof Set&&T(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var co=C,uo=0,fo=function(){this.id=uo++,this.subs=[]};fo.prototype.addSub=function(e){this.subs.push(e)},fo.prototype.removeSub=function(e){m(this.subs,e)},fo.prototype.depend=function(){fo.target&&fo.target.addDep(this)},fo.prototype.notify=function(){for(var e=this.subs.slice(),t=0,i=e.length;t<i;t++)e[t].update()},fo.target=null;var po=[],ho=function(e,t,i,n,o,a,r,s){this.tag=e,this.data=t,this.children=i,this.text=n,this.elm=o,this.ns=void 0,this.context=a,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=t&&t.key,this.componentOptions=r,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1},bo={child:{configurable:!0}};bo.child.get=function(){return this.componentInstance},Object.defineProperties(ho.prototype,bo);var mo=function(e){void 0===e&&(e="");var t=new ho;return t.text=e,t.isComment=!0,t},go=Array.prototype,vo=Object.create(go);["push","pop","shift","unshift","splice","sort","reverse"].forEach(function(e){var t=go[e];O(vo,e,function(){for(var i=[],n=arguments.length;n--;)i[n]=arguments[n];var o,a=t.apply(this,i),r=this.__ob__;switch(e){case"push":case"unshift":o=i;break;case"splice":o=i.slice(2)}return o&&r.observeArray(o),r.dep.notify(),a})});var ko=Object.getOwnPropertyNames(vo),xo=!0,wo=function(e){this.value=e,this.dep=new fo,this.vmCount=0,O(e,"__ob__",this),Array.isArray(e)?(Un?B(e,vo):V(e,vo,ko),this.observeArray(e)):this.walk(e)};wo.prototype.walk=function(e){for(var t=Object.keys(e),i=0;i<t.length;i++)N(e,t[i])},wo.prototype.observeArray=function(e){for(var t=0,i=e.length;t<i;t++)I(e[t])};var yo=Ln.optionMergeStrategies;yo.data=function(e,t,i){return i?U(e,t,i):t&&"function"!=typeof t?e:U(e,t)},Rn.forEach(function(e){yo[e]=W}),Nn.forEach(function(e){yo[e+"s"]=G}),yo.watch=function(e,t,i,n){if(e===to&&(e=void 0),t===to&&(t=void 0),!t)return Object.create(e||null);if(!e)return t;var o={};y(o,e);for(var a in t){var r=o[a],s=t[a];r&&!Array.isArray(r)&&(r=[r]),o[a]=r?r.concat(s):Array.isArray(s)?s:[s]}return o},yo.props=yo.methods=yo.inject=yo.computed=function(e,t,i,n){if(!e)return t;var o=Object.create(null);return y(o,e),t&&y(o,t),o},yo.provide=U;var _o,Co=function(e,t){return void 0===t?e:t},So=!1,$o=[],Do=!1;if("undefined"!=typeof Promise&&T(Promise)){var zo=Promise.resolve();_o=function(){zo.then(ce),Zn&&setTimeout(C)},So=!0}else if(Xn||"undefined"==typeof MutationObserver||!T(MutationObserver)&&"[object MutationObserverConstructor]"!==MutationObserver.toString())_o=void 0!==i&&T(i)?function(){i(ce)}:function(){setTimeout(ce,0)};else{var Oo=1,Ao=new MutationObserver(ce),To=document.createTextNode(String(Oo));Ao.observe(To,{characterData:!0}),_o=function(){Oo=(Oo+1)%2,To.data=String(Oo)},So=!0}var Mo=new ao,jo=v(function(e){var t="&"===e.charAt(0);e=t?e.slice(1):e;var i="~"===e.charAt(0);e=i?e.slice(1):e;var n="!"===e.charAt(0);return e=n?e.slice(1):e,{name:e,once:i,capture:n,passive:t}});qe(Ue.prototype);var Po,Eo={init:function(e,t){if(e.componentInstance&&!e.componentInstance._isDestroyed&&e.data.keepAlive){var i=e;Eo.prepatch(i,i)}else{(e.componentInstance=Xe(e,No)).$mount(t?e.elm:void 0,t)}},prepatch:function(e,t){var i=t.componentOptions;vt(t.componentInstance=e.componentInstance,i.propsData,i.listeners,t,i.children)},insert:function(e){var t=e.context,i=e.componentInstance;i._isMounted||(i._isMounted=!0,yt(i,"mounted")),e.data.keepAlive&&(t._isMounted?$t(i):xt(i,!0))},destroy:function(e){var t=e.componentInstance;t._isDestroyed||(e.data.keepAlive?wt(t,!0):t.$destroy())}},Fo=Object.keys(Eo),Bo=1,Vo=2,Io=null,No=null,Ro=[],Lo=[],Ho={},qo=!1,Uo=!1,Wo=0,Yo=0,Go=Date.now;if(Wn&&!Xn){var Ko=window.performance;Ko&&"function"==typeof Ko.now&&Go()>document.createEvent("Event").timeStamp&&(Go=function(){return Ko.now()})}var Xo=0,Jo=function(e,t,i,n,o){this.vm=e,o&&(e._watcher=this),e._watchers.push(this),n?(this.deep=!!n.deep,this.user=!!n.user,this.lazy=!!n.lazy,this.sync=!!n.sync,this.before=n.before):this.deep=this.user=this.lazy=this.sync=!1,this.cb=i,this.id=++Xo,this.active=!0,this.dirty=this.lazy,this.deps=[],this.newDeps=[],this.depIds=new ao,this.newDepIds=new ao,this.expression="","function"==typeof t?this.getter=t:(this.getter=A(t),this.getter||(this.getter=C)),this.value=this.lazy?void 0:this.get()};Jo.prototype.get=function(){M(this);var e,t=this.vm;try{e=this.getter.call(t,t)}catch(e){if(!this.user)throw e;ae(e,t,'getter for watcher "'+this.expression+'"')}finally{this.deep&&ue(e),j(),this.cleanupDeps()}return e},Jo.prototype.addDep=function(e){var t=e.id;this.newDepIds.has(t)||(this.newDepIds.add(t),this.newDeps.push(e),this.depIds.has(t)||e.addSub(this))},Jo.prototype.cleanupDeps=function(){for(var e=this.deps.length;e--;){var t=this.deps[e];this.newDepIds.has(t.id)||t.removeSub(this)}var i=this.depIds;this.depIds=this.newDepIds,this.newDepIds=i,this.newDepIds.clear(),i=this.deps,this.deps=this.newDeps,this.newDeps=i,this.newDeps.length=0},Jo.prototype.update=function(){this.lazy?this.dirty=!0:this.sync?this.run():zt(this)},Jo.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||l(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){ae(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},Jo.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},Jo.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},Jo.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||m(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var Qo={enumerable:!0,configurable:!0,get:C,set:C},Zo={lazy:!0},ea=0;!function(e){e.prototype._init=function(e){var t=this;t._uid=ea++,t._isVue=!0,e&&e._isComponent?Rt(t,e):t.$options=Q(Lt(t.constructor),e||{},t),t._renderProxy=t,t._self=t,mt(t),dt(t),ot(t),yt(t,"beforeCreate"),_e(t),At(t),ye(t),yt(t,"created"),t.$options.el&&t.$mount(t.$options.el)}}(qt),function(e){var t={};t.get=function(){return this._data};var i={};i.get=function(){return this._props},Object.defineProperty(e.prototype,"$data",t),Object.defineProperty(e.prototype,"$props",i),e.prototype.$set=R,e.prototype.$delete=L,e.prototype.$watch=function(e,t,i){var n=this;if(c(t))return Nt(n,e,t,i);i=i||{},i.user=!0;var o=new Jo(n,e,t,i);if(i.immediate)try{t.call(n,o.value)}catch(e){ae(e,n,'callback for immediate watcher "'+o.expression+'"')}return function(){o.teardown()}}}(qt),function(e){var t=/^hook:/;e.prototype.$on=function(e,i){var n=this;if(Array.isArray(e))for(var o=0,a=e.length;o<a;o++)n.$on(e[o],i);else(n._events[e]||(n._events[e]=[])).push(i),t.test(e)&&(n._hasHookEvent=!0);return n},e.prototype.$once=function(e,t){function i(){n.$off(e,i),t.apply(n,arguments)}var n=this;return i.fn=t,n.$on(e,i),n},e.prototype.$off=function(e,t){var i=this;if(!arguments.length)return i._events=Object.create(null),i;if(Array.isArray(e)){for(var n=0,o=e.length;n<o;n++)i.$off(e[n],t);return i}var a=i._events[e];if(!a)return i;if(!t)return i._events[e]=null,i;for(var r,s=a.length;s--;)if((r=a[s])===t||r.fn===t){a.splice(s,1);break}return i},e.prototype.$emit=function(e){var t=this,i=t._events[e];if(i){i=i.length>1?w(i):i;for(var n=w(arguments,1),o='event handler for "'+e+'"',a=0,r=i.length;a<r;a++)re(i[a],t,n,t,o)}return t}}(qt),function(e){e.prototype._update=function(e,t){var i=this,n=i.$el,o=i._vnode,a=bt(i);i._vnode=e,i.$el=o?i.__patch__(o,e):i.__patch__(i.$el,e,t,!1),a(),n&&(n.__vue__=null),i.$el&&(i.$el.__vue__=i),i.$vnode&&i.$parent&&i.$vnode===i.$parent._vnode&&(i.$parent.$el=i.$el)},e.prototype.$forceUpdate=function(){var e=this;e._watcher&&e._watcher.update()},e.prototype.$destroy=function(){var e=this;if(!e._isBeingDestroyed){yt(e,"beforeDestroy"),e._isBeingDestroyed=!0;var t=e.$parent;!t||t._isBeingDestroyed||e.$options.abstract||m(t.$children,e),e._watcher&&e._watcher.teardown();for(var i=e._watchers.length;i--;)e._watchers[i].teardown();e._data.__ob__&&e._data.__ob__.vmCount--,e._isDestroyed=!0,e.__patch__(e._vnode,null),yt(e,"destroyed"),e.$off(),e.$el&&(e.$el.__vue__=null),e.$vnode&&(e.$vnode.parent=null)}}}(qt),function(e){qe(e.prototype),e.prototype.$nextTick=function(e){return de(e,this)},e.prototype._render=function(){var e=this,t=e.$options,i=t.render,n=t._parentVnode;n&&(e.$scopedSlots=De(n.data.scopedSlots,e.$slots,e.$scopedSlots)),e.$vnode=n;var o;try{Io=e,o=i.call(e._renderProxy,e.$createElement)}catch(t){ae(t,e,"render"),o=e._vnode}finally{Io=null}return Array.isArray(o)&&1===o.length&&(o=o[0]),o instanceof ho||(o=mo()),o.parent=n,o}}(qt);var ta=[String,RegExp,Array],ia={name:"keep-alive",abstract:!0,props:{include:ta,exclude:ta,max:[String,Number]},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var e in this.cache)ei(this.cache,e,this.keys)},mounted:function(){var e=this;this.$watch("include",function(t){Zt(e,function(e){return Qt(t,e)})}),this.$watch("exclude",function(t){Zt(e,function(e){return!Qt(t,e)})})},render:function(){var e=this.$slots.default,t=ct(e),i=t&&t.componentOptions;if(i){var n=Jt(i),o=this,a=o.include,r=o.exclude;if(a&&(!n||!Qt(a,n))||r&&n&&Qt(r,n))return t;var s=this,l=s.cache,c=s.keys,d=null==t.key?i.Ctor.cid+(i.tag?"::"+i.tag:""):t.key;l[d]?(t.componentInstance=l[d].componentInstance,m(c,d),c.push(d)):(l[d]=t,c.push(d),this.max&&c.length>parseInt(this.max)&&ei(l,c[0],c,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}},na={KeepAlive:ia};!function(e){var t={};t.get=function(){return Ln},Object.defineProperty(e,"config",t),e.util={warn:co,extend:y,mergeOptions:Q,defineReactive:N},e.set=R,e.delete=L,e.nextTick=de,e.observable=function(e){return I(e),e},e.options=Object.create(null),Nn.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,y(e.options.components,na),Ut(e),Wt(e),Yt(e),Xt(e)}(qt),Object.defineProperty(qt.prototype,"$isServer",{get:ro}),Object.defineProperty(qt.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(qt,"FunctionalRenderContext",{value:Ue}),qt.version="2.6.10";var oa,aa,ra,sa=b("style,class"),la=b("input,textarea,option,select,progress"),ca=function(e,t,i){return"value"===i&&la(e)&&"button"!==t||"selected"===i&&"option"===e||"checked"===i&&"input"===e||"muted"===i&&"video"===e},da=b("contenteditable,draggable,spellcheck"),ua=b("events,caret,typing,plaintext-only"),fa=function(e,t){return ga(t)||"false"===t?"false":"contenteditable"===e&&ua(t)?t:"true"},pa=b("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),ha="http://www.w3.org/1999/xlink",ba=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},ma=function(e){return ba(e)?e.slice(6,e.length):""},ga=function(e){return null==e||!1===e},va={svg:"http://www.w3.org/2000/svg",math:"http://www.w3.org/1998/Math/MathML"},ka=b("html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,menuitem,summary,content,element,shadow,template,blockquote,iframe,tfoot"),xa=b("svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view",!0),wa=function(e){return ka(e)||xa(e)},ya=Object.create(null),_a=b("text,number,password,search,email,tel,url"),Ca=Object.freeze({createElement:ui,createElementNS:fi,createTextNode:pi,createComment:hi,insertBefore:bi,removeChild:mi,appendChild:gi,parentNode:vi,nextSibling:ki,tagName:xi,setTextContent:wi,setStyleScope:yi}),Sa={create:function(e,t){_i(t)},update:function(e,t){e.data.ref!==t.data.ref&&(_i(e,!0),_i(t))},destroy:function(e){_i(e,!0)}},$a=new ho("",{},[]),Da=["create","activate","update","remove","destroy"],za={create:Di,update:Di,destroy:function(e){Di(e,$a)}},Oa=Object.create(null),Aa=[Sa,za],Ta={create:Mi,update:Mi},Ma={create:Ei,update:Ei},ja="__r",Pa="__c",Ea=So&&!(eo&&Number(eo[1])<=53),Fa={create:Ni,update:Ni},Ba={create:Ri,update:Ri},Va=v(function(e){var t={},i=/;(?![^(]*\))/g,n=/:(.+)/;return e.split(i).forEach(function(e){if(e){var i=e.split(n);i.length>1&&(t[i[0].trim()]=i[1].trim())}}),t}),Ia=/^--/,Na=/\s*!important$/,Ra=function(e,t,i){if(Ia.test(t))e.style.setProperty(t,i);else if(Na.test(i))e.style.setProperty(En(t),i.replace(Na,""),"important");else{var n=Ha(t);if(Array.isArray(i))for(var o=0,a=i.length;o<a;o++)e.style[n]=i[o];else e.style[n]=i}},La=["Webkit","Moz","ms"],Ha=v(function(e){if(ra=ra||document.createElement("div").style,"filter"!==(e=Mn(e))&&e in ra)return e;for(var t=e.charAt(0).toUpperCase()+e.slice(1),i=0;i<La.length;i++){var n=La[i]+t;if(n in ra)return n}}),qa={create:Gi,update:Gi},Ua=/\s+/,Wa=v(function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}}),Ya=Wn&&!Jn,Ga="transition",Ka="animation",Xa="transition",Ja="transitionend",Qa="animation",Za="animationend";Ya&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(Xa="WebkitTransition",Ja="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Qa="WebkitAnimation",Za="webkitAnimationEnd"));var er=Wn?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()},tr=/\b(transform|all)(,|$)/,ir=Wn?{create:dn,activate:dn,remove:function(e,t){!0!==e.data.show?sn(e,t):t()}}:{},nr=[Ta,Ma,Fa,Ba,qa,ir],or=nr.concat(Aa),ar=function(e){function t(e){return new ho(T.tagName(e).toLowerCase(),{},[],void 0,e)}function i(e,t){function i(){0==--i.listeners&&r(e)}return i.listeners=t,i}function r(e){var t=T.parentNode(e);o(t)&&T.removeChild(t,e)}function l(e,t,i,n,r,s,l){if(o(e.elm)&&o(s)&&(e=s[l]=E(e)),e.isRootInsert=!r,!c(e,t,i,n)){var d=e.data,u=e.children,h=e.tag;o(h)?(e.elm=e.ns?T.createElementNS(e.ns,h):T.createElement(h,e),g(e),p(e,u,t),o(d)&&m(e,t),f(i,e.elm,n)):a(e.isComment)?(e.elm=T.createComment(e.text),f(i,e.elm,n)):(e.elm=T.createTextNode(e.text),f(i,e.elm,n))}}function c(e,t,i,n){var r=e.data;if(o(r)){var s=o(e.componentInstance)&&r.keepAlive;if(o(r=r.hook)&&o(r=r.init)&&r(e,!1),o(e.componentInstance))return d(e,t),f(i,e.elm,n),a(s)&&u(e,t,i,n),!0}}function d(e,t){o(e.data.pendingInsert)&&(t.push.apply(t,e.data.pendingInsert),e.data.pendingInsert=null),e.elm=e.componentInstance.$el,h(e)?(m(e,t),g(e)):(_i(e),t.push(e))}function u(e,t,i,n){for(var a,r=e;r.componentInstance;)if(r=r.componentInstance._vnode,o(a=r.data)&&o(a=a.transition)){for(a=0;a<O.activate.length;++a)O.activate[a]($a,r);t.push(r);break}f(i,e.elm,n)}function f(e,t,i){o(e)&&(o(i)?T.parentNode(i)===e&&T.insertBefore(e,t,i):T.appendChild(e,t))}function p(e,t,i){if(Array.isArray(t))for(var n=0;n<t.length;++n)l(t[n],i,e.elm,null,!0,t,n);else s(e.text)&&T.appendChild(e.elm,T.createTextNode(String(e.text)))}function h(e){for(;e.componentInstance;)e=e.componentInstance._vnode;return o(e.tag)}function m(e,t){for(var i=0;i<O.create.length;++i)O.create[i]($a,e);D=e.data.hook,o(D)&&(o(D.create)&&D.create($a,e),o(D.insert)&&t.push(e))}function g(e){var t;if(o(t=e.fnScopeId))T.setStyleScope(e.elm,t);else for(var i=e;i;)o(t=i.context)&&o(t=t.$options._scopeId)&&T.setStyleScope(e.elm,t),i=i.parent;o(t=No)&&t!==e.context&&t!==e.fnContext&&o(t=t.$options._scopeId)&&T.setStyleScope(e.elm,t)}function v(e,t,i,n,o,a){for(;n<=o;++n)l(i[n],a,e,t,!1,i,n)}function k(e){var t,i,n=e.data;if(o(n))for(o(t=n.hook)&&o(t=t.destroy)&&t(e),t=0;t<O.destroy.length;++t)O.destroy[t](e);if(o(t=e.children))for(i=0;i<e.children.length;++i)k(e.children[i])}function x(e,t,i,n){for(;i<=n;++i){var a=t[i];o(a)&&(o(a.tag)?(w(a),k(a)):r(a.elm))}}function w(e,t){if(o(t)||o(e.data)){var n,a=O.remove.length+1;for(o(t)?t.listeners+=a:t=i(e.elm,a),o(n=e.componentInstance)&&o(n=n._vnode)&&o(n.data)&&w(n,t),n=0;n<O.remove.length;++n)O.remove[n](e,t);o(n=e.data.hook)&&o(n=n.remove)?n(e,t):t()}else r(e.elm)}function y(e,t,i,a,r){for(var s,c,d,u,f=0,p=0,h=t.length-1,b=t[0],m=t[h],g=i.length-1,k=i[0],w=i[g],y=!r;f<=h&&p<=g;)n(b)?b=t[++f]:n(m)?m=t[--h]:Ci(b,k)?(C(b,k,a,i,p),b=t[++f],k=i[++p]):Ci(m,w)?(C(m,w,a,i,g),m=t[--h],w=i[--g]):Ci(b,w)?(C(b,w,a,i,g),y&&T.insertBefore(e,b.elm,T.nextSibling(m.elm)),b=t[++f],w=i[--g]):Ci(m,k)?(C(m,k,a,i,p),y&&T.insertBefore(e,m.elm,b.elm),m=t[--h],k=i[++p]):(n(s)&&(s=$i(t,f,h)),c=o(k.key)?s[k.key]:_(k,t,f,h),n(c)?l(k,a,e,b.elm,!1,i,p):(d=t[c],Ci(d,k)?(C(d,k,a,i,p),t[c]=void 0,y&&T.insertBefore(e,d.elm,b.elm)):l(k,a,e,b.elm,!1,i,p)),k=i[++p]);f>h?(u=n(i[g+1])?null:i[g+1].elm,v(e,u,i,p,g,a)):p>g&&x(e,t,f,h)}function _(e,t,i,n){for(var a=i;a<n;a++){var r=t[a];if(o(r)&&Ci(e,r))return a}}function C(e,t,i,r,s,l){if(e!==t){o(t.elm)&&o(r)&&(t=r[s]=E(t));var c=t.elm=e.elm;if(a(e.isAsyncPlaceholder))return void(o(t.asyncFactory.resolved)?$(e.elm,t,i):t.isAsyncPlaceholder=!0);if(a(t.isStatic)&&a(e.isStatic)&&t.key===e.key&&(a(t.isCloned)||a(t.isOnce)))return void(t.componentInstance=e.componentInstance);var d,u=t.data;o(u)&&o(d=u.hook)&&o(d=d.prepatch)&&d(e,t);var f=e.children,p=t.children;if(o(u)&&h(t)){for(d=0;d<O.update.length;++d)O.update[d](e,t);o(d=u.hook)&&o(d=d.update)&&d(e,t)}n(t.text)?o(f)&&o(p)?f!==p&&y(c,f,p,i,l):o(p)?(o(e.text)&&T.setTextContent(c,""),v(c,null,p,0,p.length-1,i)):o(f)?x(c,f,0,f.length-1):o(e.text)&&T.setTextContent(c,""):e.text!==t.text&&T.setTextContent(c,t.text),o(u)&&o(d=u.hook)&&o(d=d.postpatch)&&d(e,t)}}function S(e,t,i){if(a(i)&&o(e.parent))e.parent.data.pendingInsert=t;else for(var n=0;n<t.length;++n)t[n].data.hook.insert(t[n])}function $(e,t,i,n){var r,s=t.tag,l=t.data,c=t.children;if(n=n||l&&l.pre,t.elm=e,a(t.isComment)&&o(t.asyncFactory))return t.isAsyncPlaceholder=!0,!0;if(o(l)&&(o(r=l.hook)&&o(r=r.init)&&r(t,!0),o(r=t.componentInstance)))return d(t,i),!0;if(o(s)){if(o(c))if(e.hasChildNodes())if(o(r=l)&&o(r=r.domProps)&&o(r=r.innerHTML)){if(r!==e.innerHTML)return!1}else{for(var u=!0,f=e.firstChild,h=0;h<c.length;h++){if(!f||!$(f,c[h],i,n)){u=!1;break}f=f.nextSibling}if(!u||f)return!1}else p(t,c,i);if(o(l)){var b=!1;for(var g in l)if(!M(g)){b=!0,m(t,i);break}!b&&l.class&&ue(l.class)}}else e.data!==t.text&&(e.data=t.text);return!0}var D,z,O={},A=e.modules,T=e.nodeOps;for(D=0;D<Da.length;++D)for(O[Da[D]]=[],z=0;z<A.length;++z)o(A[z][Da[D]])&&O[Da[D]].push(A[z][Da[D]]);var M=b("attrs,class,staticClass,staticStyle,key");return function(e,i,r,s){if(n(i))return void(o(e)&&k(e));var c=!1,d=[];if(n(e))c=!0,l(i,d);else{var u=o(e.nodeType);if(!u&&Ci(e,i))C(e,i,d,null,null,s);else{if(u){if(1===e.nodeType&&e.hasAttribute(In)&&(e.removeAttribute(In),r=!0),a(r)&&$(e,i,d))return S(i,d,!0),e;e=t(e)}var f=e.elm,p=T.parentNode(f);if(l(i,d,f._leaveCb?null:p,T.nextSibling(f)),o(i.parent))for(var b=i.parent,m=h(i);b;){for(var g=0;g<O.destroy.length;++g)O.destroy[g](b);if(b.elm=i.elm,m){for(var v=0;v<O.create.length;++v)O.create[v]($a,b);var w=b.data.hook.insert;if(w.merged)for(var y=1;y<w.fns.length;y++)w.fns[y]()}else _i(b);b=b.parent}o(p)?x(p,[e],0,0):o(e.tag)&&k(e)}}return S(i,d,c),i.elm}}({nodeOps:Ca,modules:or});Jn&&document.addEventListener("selectionchange",function(){var e=document.activeElement;e&&e.vmodel&&gn(e,"input")});var rr={inserted:function(e,t,i,n){"select"===i.tag?(n.elm&&!n.elm._vOptions?be(i,"postpatch",function(){rr.componentUpdated(e,t,i)}):un(e,t,i.context),e._vOptions=[].map.call(e.options,hn)):("textarea"===i.tag||_a(e.type))&&(e._vModifiers=t.modifiers,t.modifiers.lazy||(e.addEventListener("compositionstart",bn),e.addEventListener("compositionend",mn),e.addEventListener("change",mn),Jn&&(e.vmodel=!0)))},componentUpdated:function(e,t,i){if("select"===i.tag){un(e,t,i.context);var n=e._vOptions,o=e._vOptions=[].map.call(e.options,hn);if(o.some(function(e,t){return!S(e,n[t])})){(e.multiple?t.value.some(function(e){return pn(e,o)}):t.value!==t.oldValue&&pn(t.value,o))&&gn(e,"change")}}}},sr={bind:function(e,t,i){var n=t.value;i=vn(i);var o=i.data&&i.data.transition,a=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;n&&o?(i.data.show=!0,rn(i,function(){e.style.display=a})):e.style.display=n?a:"none"},update:function(e,t,i){var n=t.value;!n!=!t.oldValue&&(i=vn(i),i.data&&i.data.transition?(i.data.show=!0,n?rn(i,function(){e.style.display=e.__vOriginalDisplay}):sn(i,function(){e.style.display="none"})):e.style.display=n?e.__vOriginalDisplay:"none")},unbind:function(e,t,i,n,o){o||(e.style.display=e.__vOriginalDisplay)}},lr={model:rr,show:sr},cr={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]},dr=function(e){return e.tag||lt(e)},ur=function(e){return"show"===e.name},fr={name:"transition",props:cr,abstract:!0,render:function(e){var t=this,i=this.$slots.default;if(i&&(i=i.filter(dr),i.length)){var n=this.mode,o=i[0];if(yn(this.$vnode))return o;var a=kn(o);if(!a)return o;if(this._leaving)return wn(e,o);var r="__transition-"+this._uid+"-";a.key=null==a.key?a.isComment?r+"comment":r+a.tag:s(a.key)?0===String(a.key).indexOf(r)?a.key:r+a.key:a.key;var l=(a.data||(a.data={})).transition=xn(this),c=this._vnode,d=kn(c);if(a.data.directives&&a.data.directives.some(ur)&&(a.data.show=!0),d&&d.data&&!_n(a,d)&&!lt(d)&&(!d.componentInstance||!d.componentInstance._vnode.isComment)){var u=d.data.transition=y({},l);if("out-in"===n)return this._leaving=!0,be(u,"afterLeave",function(){t._leaving=!1,t.$forceUpdate()}),wn(e,o);if("in-out"===n){if(lt(a))return c;var f,p=function(){f()};be(l,"afterEnter",p),be(l,"enterCancelled",p),be(u,"delayLeave",function(e){f=e})}}return o}}},pr=y({tag:String,moveClass:String},cr);delete pr.mode;var hr={props:pr,beforeMount:function(){var e=this,t=this._update;this._update=function(i,n){var o=bt(e);e.__patch__(e._vnode,e.kept,!1,!0),e._vnode=e.kept,o(),t.call(e,i,n)}},render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",i=Object.create(null),n=this.prevChildren=this.children,o=this.$slots.default||[],a=this.children=[],r=xn(this),s=0;s<o.length;s++){var l=o[s];if(l.tag)if(null!=l.key&&0!==String(l.key).indexOf("__vlist"))a.push(l),i[l.key]=l,(l.data||(l.data={})).transition=r;else;}if(n){for(var c=[],d=[],u=0;u<n.length;u++){var f=n[u];f.data.transition=r,f.data.pos=f.elm.getBoundingClientRect(),i[f.key]?c.push(f):d.push(f)}this.kept=e(t,null,c),this.removed=d}return e(t,null,a)},updated:function(){var e=this.prevChildren,t=this.moveClass||(this.name||"v")+"-move";e.length&&this.hasMove(e[0].elm,t)&&(e.forEach(Cn),e.forEach(Sn),e.forEach($n),this._reflow=document.body.offsetHeight,e.forEach(function(e){if(e.data.moved){var i=e.elm,n=i.style;Zi(i,t),n.transform=n.WebkitTransform=n.transitionDuration="",i.addEventListener(Ja,i._moveCb=function e(n){n&&n.target!==i||n&&!/transform$/.test(n.propertyName)||(i.removeEventListener(Ja,e),i._moveCb=null,en(i,t))})}}))},methods:{hasMove:function(e,t){if(!Ya)return!1;if(this._hasMove)return this._hasMove;var i=e.cloneNode();e._transitionClasses&&e._transitionClasses.forEach(function(e){Xi(i,e)}),Ki(i,t),i.style.display="none",this.$el.appendChild(i);var n=nn(i);return this.$el.removeChild(i),this._hasMove=n.hasTransform}}},br={Transition:fr,TransitionGroup:hr};qt.config.mustUseProp=ca,qt.config.isReservedTag=wa,qt.config.isReservedAttr=sa,qt.config.getTagNamespace=li,qt.config.isUnknownElement=ci,y(qt.options.directives,lr),y(qt.options.components,br),qt.prototype.__patch__=Wn?ar:C,qt.prototype.$mount=function(e,t){return e=e&&Wn?di(e):void 0,gt(this,e,t)},Wn&&setTimeout(function(){Ln.devtools&&so&&so.emit("init",qt)},0),t.default=qt}.call(t,i(4),i(17).setImmediate)},function(e,t){var i;i=function(){return this}();try{i=i||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(i=window)}e.exports=i},function(e,t,i){"use strict";function n(e){i(48)}var o=i(12),a=i(50),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){"use strict";var n=i(29),o=i(33),a=i(37),r=i(41),s=i(45),l=i(52),c=i(56),d=i(60);t.a={name:"app",components:{"app-navbar":n.a,"app-navmenu":o.a,"app-options":a.a,"app-passview":r.a,"app-rendertaskview":s.a,"app-documentview":l.a,"app-clipscrolltreeview":c.a,"app-screenshotview":d.a},computed:{page:function(){return this.$store.state.page}}}},function(e,t,i){"use strict";t.a={computed:{isConnected:function(){return this.$store.state.connected}},methods:{connect:function(){this.$store.dispatch("connect")},disconnect:function(){this.$store.dispatch("disconnect")}}}},function(e,t,i){"use strict";t.a={methods:{setPage:function(e){this.$store.commit("setPage",e)}},computed:{page:function(){return this.$store.state.page}}}},function(e,t,i){"use strict";t.a={computed:{disabled:function(){return!this.$store.state.connected}},methods:{setProfiler:function(e){e?this.$store.dispatch("sendMessage","enable_profiler"):this.$store.dispatch("sendMessage","disable_profiler")},setTextureCacheDebugger:function(e){e?this.$store.dispatch("sendMessage","enable_texture_cache_debug"):this.$store.dispatch("sendMessage","disable_texture_cache_debug")},setRenderTargetDebugger:function(e){e?this.$store.dispatch("sendMessage","enable_render_target_debug"):this.$store.dispatch("sendMessage","disable_render_target_debug")},setAlphaRectsDebugger:function(e){e?this.$store.dispatch("sendMessage","enable_alpha_rects_debug"):this.$store.dispatch("sendMessage","disable_alpha_rects_debug")},setGpuTimeQueries:function(e){e?this.$store.dispatch("sendMessage","enable_gpu_time_queries"):this.$store.dispatch("sendMessage","disable_gpu_time_queries")},setGpuSampleQueries:function(e){e?this.$store.dispatch("sendMessage","enable_gpu_sample_queries"):this.$store.dispatch("sendMessage","disable_gpu_sample_queries")},setOpaquePass:function(e){e?this.$store.dispatch("sendMessage","enable_opaque_pass"):this.$store.dispatch("sendMessage","disable_opaque_pass")},setAlphaPass:function(e){e?this.$store.dispatch("sendMessage","enable_alpha_pass"):this.$store.dispatch("sendMessage","disable_alpha_pass")},setClipMasks:function(e){e?this.$store.dispatch("sendMessage","enable_clip_masks"):this.$store.dispatch("sendMessage","disable_clip_masks")},setTextPrims:function(e){e?this.$store.dispatch("sendMessage","enable_text_prims"):this.$store.dispatch("sendMessage","disable_text_prims")},setGradientPrims:function(e){e?this.$store.dispatch("sendMessage","enable_gradient_prims"):this.$store.dispatch("sendMessage","disable_gradient_prims")}}}},function(e,t,i){"use strict";t.a={methods:{fetch:function(){this.$store.dispatch("sendMessage","fetch_passes")}},computed:{disabled:function(){return!this.$store.state.connected},passes:function(){return this.$store.state.passes}}}},function(e,t,i){"use strict";var n=i(5);t.a={components:{"app-treeview":n.a},methods:{fetch:function(){this.$store.dispatch("sendMessage","fetch_render_tasks")}},computed:{disabled:function(){return!this.$store.state.connected},render_tasks:function(){return this.$store.state.render_tasks}}}},function(e,t,i){"use strict";t.a={name:"treeview",props:["model"],data:function(){return{open:!1}},computed:{isFolder:function(){return this.model.children&&this.model.children.length}},methods:{toggle:function(){this.isFolder&&(this.open=!this.open)}}}},function(e,t,i){"use strict";var n=i(5);t.a={components:{"app-treeview":n.a},methods:{fetch:function(){this.$store.dispatch("sendMessage","fetch_documents")}},computed:{disabled:function(){return!this.$store.state.connected},documents:function(){return this.$store.state.documents}}}},function(e,t,i){"use strict";var n=i(5);t.a={components:{"app-treeview":n.a},methods:{fetch:function(){this.$store.dispatch("sendMessage","fetch_clip_scroll_tree")}},computed:{disabled:function(){return!this.$store.state.connected},clip_scroll_tree:function(){return this.$store.state.clip_scroll_tree}}}},function(e,t,i){"use strict";t.a={computed:{disabled:function(){return!this.$store.state.connected},screenshot:function(){return this.$store.state.screenshot}},methods:{fetch:function(){this.$store.dispatch("sendMessage","fetch_screenshot")}}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(3),o=i(20),a=i.n(o),r=i(21),s=(i.n(r),i(24)),l=(i.n(s),i(26)),c=i(65);n.default.use(a.a),new n.default({el:"#app",store:c.a,render:function(e){return e(l.a)}})},function(e,t,i){(function(e){function n(e,t){this._id=e,this._clearFn=t}var o=void 0!==e&&e||"undefined"!=typeof self&&self||window,a=Function.prototype.apply;t.setTimeout=function(){return new n(a.call(setTimeout,o,arguments),clearTimeout)},t.setInterval=function(){return new n(a.call(setInterval,o,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},n.prototype.unref=n.prototype.ref=function(){},n.prototype.close=function(){this._clearFn.call(o,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},i(18),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,i(4))},function(e,t,i){(function(e,t){!function(e,i){"use strict";function n(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),i=0;i<t.length;i++)t[i]=arguments[i+1];var n={callback:e,args:t};return c[l]=n,s(l),l++}function o(e){delete c[e]}function a(e){var t=e.callback,n=e.args;switch(n.length){case 0:t();break;case 1:t(n[0]);break;case 2:t(n[0],n[1]);break;case 3:t(n[0],n[1],n[2]);break;default:t.apply(i,n)}}function r(e){if(d)setTimeout(r,0,e);else{var t=c[e];if(t){d=!0;try{a(t)}finally{o(e),d=!1}}}}if(!e.setImmediate){var s,l=1,c={},d=!1,u=e.document,f=Object.getPrototypeOf&&Object.getPrototypeOf(e);f=f&&f.setTimeout?f:e,"[object process]"==={}.toString.call(e.process)?function(){s=function(e){t.nextTick(function(){r(e)})}}():function(){if(e.postMessage&&!e.importScripts){var t=!0,i=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=i,t}}()?function(){var t="setImmediate$"+Math.random()+"$",i=function(i){i.source===e&&"string"==typeof i.data&&0===i.data.indexOf(t)&&r(+i.data.slice(t.length))};e.addEventListener?e.addEventListener("message",i,!1):e.attachEvent("onmessage",i),s=function(i){e.postMessage(t+i,"*")}}():e.MessageChannel?function(){var e=new MessageChannel;e.port1.onmessage=function(e){r(e.data)},s=function(t){e.port2.postMessage(t)}}():u&&"onreadystatechange"in u.createElement("script")?function(){var e=u.documentElement;s=function(t){var i=u.createElement("script");i.onreadystatechange=function(){r(t),i.onreadystatechange=null,e.removeChild(i),i=null},e.appendChild(i)}}():function(){s=function(e){setTimeout(r,0,e)}}(),f.setImmediate=n,f.clearImmediate=o}}("undefined"==typeof self?void 0===e?this:e:self)}).call(t,i(4),i(19))},function(e,t){function i(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}function o(e){if(d===setTimeout)return setTimeout(e,0);if((d===i||!d)&&setTimeout)return d=setTimeout,setTimeout(e,0);try{return d(e,0)}catch(t){try{return d.call(null,e,0)}catch(t){return d.call(this,e,0)}}}function a(e){if(u===clearTimeout)return clearTimeout(e);if((u===n||!u)&&clearTimeout)return u=clearTimeout,clearTimeout(e);try{return u(e)}catch(t){try{return u.call(null,e)}catch(t){return u.call(this,e)}}}function r(){b&&p&&(b=!1,p.length?h=p.concat(h):m=-1,h.length&&s())}function s(){if(!b){var e=o(r);b=!0;for(var t=h.length;t;){for(p=h,h=[];++m<t;)p&&p[m].run();m=-1,t=h.length}p=null,b=!1,a(e)}}function l(e,t){this.fun=e,this.array=t}function c(){}var d,u,f=e.exports={};!function(){try{d="function"==typeof setTimeout?setTimeout:i}catch(e){d=i}try{u="function"==typeof clearTimeout?clearTimeout:n}catch(e){u=n}}();var p,h=[],b=!1,m=-1;f.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var i=1;i<arguments.length;i++)t[i-1]=arguments[i];h.push(new l(e,t)),1!==h.length||b||o(s)},l.prototype.run=function(){this.fun.apply(null,this.array)},f.title="browser",f.browser=!0,f.env={},f.argv=[],f.version="",f.versions={},f.on=c,f.addListener=c,f.once=c,f.off=c,f.removeListener=c,f.removeAllListeners=c,f.emit=c,f.prependListener=c,f.prependOnceListener=c,f.listeners=function(e){return[]},f.binding=function(e){throw new Error("process.binding is not supported")},f.cwd=function(){return"/"},f.chdir=function(e){throw new Error("process.chdir is not supported")},f.umask=function(){return 0}},function(e,t,i){/*! Buefy v0.6.7 | MIT License | github.com/buefy/buefy */ +!function(t,n){e.exports=n(i(3))}("undefined"!=typeof self&&self,function(e){return function(e){function t(n){if(i[n])return i[n].exports;var o=i[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var i={};return t.m=e,t.c=i,t.d=function(e,i,n){t.o(e,i)||Object.defineProperty(e,i,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var i=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(i,"a",i),i},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="/",t(t.s=68)}([function(e,t){e.exports=function(e,t,i,n,o){var a,r=e=e||{},s=typeof e.default;"object"!==s&&"function"!==s||(a=e,r=e.default);var l="function"==typeof r?r.options:r;t&&(l.render=t.render,l.staticRenderFns=t.staticRenderFns),n&&(l._scopeId=n);var c;if(o?(c=function(e){e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,e||"undefined"==typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),i&&i.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(o)},l._ssrRegister=c):i&&(c=i),c){var d=l.functional,u=d?l.render:l.beforeCreate;d?l.render=function(e,t){return c.call(t),u(e,t)}:l.beforeCreate=u?[].concat(u,c):[c]}return{esModule:a,exports:r,options:l}}},function(e,t,i){"use strict";t.__esModule=!0;var n=i(100),o=function(e){return e&&e.__esModule?e:{default:e}}(n);t.default=function(e,t,i){return t in e?(0,o.default)(e,t,{value:i,enumerable:!0,configurable:!0,writable:!0}):e[t]=i,e}},function(e,t,i){"use strict";i.d(t,"b",function(){return o});var n={defaultContainerElement:null,defaultIconPack:"mdi",defaultDialogConfirmText:null,defaultDialogCancelText:null,defaultSnackbarDuration:3500,defaultToastDuration:2e3,defaultTooltipType:"is-primary",defaultTooltipAnimated:!1,defaultInputAutocomplete:"on",defaultDateFormatter:null,defaultDateParser:null,defaultDayNames:null,defaultMonthNames:null,defaultFirstDayOfWeek:null,defaultUnselectableDaysOfWeek:null,defaultTimeFormatter:null,defaultTimeParser:null,defaultModalScroll:null,defaultDatepickerMobileNative:!0,defaultTimepickerMobileNative:!0,defaultNoticeQueue:!0,defaultInputHasCounter:!0};t.a=n;var o=function(e){n=e}},function(e,t,i){var n=i(0)(i(104),i(105),null,null,null);e.exports=n.exports},function(e,t,i){var n=i(34)("wks"),o=i(25),a=i(7).Symbol,r="function"==typeof a;(e.exports=function(e){return n[e]||(n[e]=r&&a[e]||(r?a:o)("Symbol."+e))}).store=n},function(e,t,i){e.exports={default:i(87),__esModule:!0}},function(e,t,i){"use strict";function n(e,t){return t.split(".").reduce(function(e,t){return e[t]},e)}function o(e,t,i){if(!e)return-1;if(!i||"function"!=typeof i)return e.indexOf(t);for(var n=0;n<e.length;n++)if(i(e[n],t))return n;return-1}function a(e){void 0!==e.remove?e.remove():e.parentNode.removeChild(e)}function r(e){return e?e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&"):e}t.b=n,t.c=o,i.d(t,"d",function(){return s}),t.e=a,t.a=r;var s={Android:function(){return"undefined"!=typeof window&&window.navigator.userAgent.match(/Android/i)},BlackBerry:function(){return"undefined"!=typeof window&&window.navigator.userAgent.match(/BlackBerry/i)},iOS:function(){return"undefined"!=typeof window&&window.navigator.userAgent.match(/iPhone|iPad|iPod/i)},Opera:function(){return"undefined"!=typeof window&&window.navigator.userAgent.match(/Opera Mini/i)},Windows:function(){return"undefined"!=typeof window&&window.navigator.userAgent.match(/IEMobile/i)},any:function(){return s.Android()||s.BlackBerry()||s.iOS()||s.Opera()||s.Windows()}}},function(e,t){var i=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=i)},function(e,t){var i=e.exports={version:"2.5.5"};"number"==typeof __e&&(__e=i)},function(e,t,i){var n=i(16),o=i(47),a=i(29),r=Object.defineProperty;t.f=i(10)?Object.defineProperty:function(e,t,i){if(n(e),t=a(t,!0),n(i),o)try{return r(e,t,i)}catch(e){}if("get"in i||"set"in i)throw TypeError("Accessors not supported!");return"value"in i&&(e[t]=i.value),e}},function(e,t,i){e.exports=!i(20)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t){var i={}.hasOwnProperty;e.exports=function(e,t){return i.call(e,t)}},function(e,t,i){"use strict";var n=i(13);t.a={mixins:[n.a],props:{size:String,expanded:Boolean,loading:Boolean,rounded:Boolean,icon:String,autocomplete:String,maxlength:[Number,String]},data:function(){return{isValid:!0,isFocused:!1}},computed:{parentField:function(){for(var e=this.$parent,t=0;t<3;t++)e&&!e.$data._isField&&(e=e.$parent);return e},statusType:function(){if(this.parentField)return this.parentField.newType},statusMessage:function(){if(this.parentField)return this.parentField.newMessage},iconSize:function(){switch(this.size){case"is-small":return this.size;case"is-medium":return;case"is-large":return"mdi"===this.newIconPack?"is-medium":""}}},methods:{focus:function(){var e=this;void 0!==this.$data._elementRef&&this.$nextTick(function(){return e.$el.querySelector(e.$data._elementRef).focus()})},onBlur:function(e){this.isFocused=!1,this.$emit("blur",e),this.checkHtml5Validity()},onFocus:function(e){this.isFocused=!0,this.$emit("focus",e)},checkHtml5Validity:function(){if(void 0!==this.$refs[this.$data._elementRef]){var e=this.$el.querySelector(this.$data._elementRef),t=null,i=null,n=!0;return e.checkValidity()||(t="is-danger",i=e.validationMessage,n=!1),this.isValid=n,this.parentField&&(this.parentField.type||(this.parentField.newType=t),this.parentField.message||(this.parentField.newMessage=i)),this.isValid}}}}},function(e,t,i){"use strict";var n=i(2);t.a={props:{iconPack:String},data:function(){return{newIconPack:this.iconPack||n.a.defaultIconPack}}}},function(e,t,i){e.exports={default:i(69),__esModule:!0}},function(e,t,i){var n=i(9),o=i(21);e.exports=i(10)?function(e,t,i){return n.f(e,t,o(1,i))}:function(e,t,i){return e[t]=i,e}},function(e,t,i){var n=i(19);e.exports=function(e){if(!n(e))throw TypeError(e+" is not an object!");return e}},function(e,t,i){var n=i(50),o=i(31);e.exports=function(e){return n(o(e))}},function(e,t,i){var n=i(7),o=i(8),a=i(46),r=i(15),s=i(11),l=function(e,t,i){var c,d,u,f=e&l.F,p=e&l.G,h=e&l.S,b=e&l.P,m=e&l.B,g=e&l.W,v=p?o:o[t]||(o[t]={}),k=v.prototype,x=p?n:h?n[t]:(n[t]||{}).prototype;p&&(i=t);for(c in i)(d=!f&&x&&void 0!==x[c])&&s(v,c)||(u=d?x[c]:i[c],v[c]=p&&"function"!=typeof x[c]?i[c]:m&&d?a(u,n):g&&x[c]==u?function(e){var t=function(t,i,n){if(this instanceof e){switch(arguments.length){case 0:return new e;case 1:return new e(t);case 2:return new e(t,i)}return new e(t,i,n)}return e.apply(this,arguments)};return t.prototype=e.prototype,t}(u):b&&"function"==typeof u?a(Function.call,u):u,b&&((v.virtual||(v.virtual={}))[c]=u,e&l.R&&k&&!k[c]&&r(k,c,u)))};l.F=1,l.G=2,l.S=4,l.P=8,l.B=16,l.W=32,l.U=64,l.R=128,e.exports=l},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t){e.exports={}},function(t,i){t.exports=e},function(e,t,i){var n=i(49),o=i(35);e.exports=Object.keys||function(e){return n(e,o)}},function(e,t){var i=0,n=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++i+n).toString(36))}},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,i){var n=i(0)(i(103),i(106),null,null,null);e.exports=n.exports},function(e,t,i){var n=i(0)(i(126),i(127),null,null,null);e.exports=n.exports},function(e,t,i){var n=i(19);e.exports=function(e,t){if(!n(e))return e;var i,o;if(t&&"function"==typeof(i=e.toString)&&!n(o=i.call(e)))return o;if("function"==typeof(i=e.valueOf)&&!n(o=i.call(e)))return o;if(!t&&"function"==typeof(i=e.toString)&&!n(o=i.call(e)))return o;throw TypeError("Can't convert object to primitive value")}},function(e,t){var i={}.toString;e.exports=function(e){return i.call(e).slice(8,-1)}},function(e,t){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},function(e,t){var i=Math.ceil,n=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?n:i)(e)}},function(e,t,i){var n=i(34)("keys"),o=i(25);e.exports=function(e){return n[e]||(n[e]=o(e))}},function(e,t,i){var n=i(7),o=n["__core-js_shared__"]||(n["__core-js_shared__"]={});e.exports=function(e){return o[e]||(o[e]={})}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t,i){var n=i(31);e.exports=function(e){return Object(n(e))}},function(e,t,i){"use strict";var n=i(79)(!0);i(54)(String,"String",function(e){this._t=String(e),this._i=0},function(){var e,t=this._t,i=this._i;return i>=t.length?{value:void 0,done:!0}:(e=n(t,i),this._i+=e.length,{value:e,done:!1})})},function(e,t){e.exports=!0},function(e,t,i){var n=i(9).f,o=i(11),a=i(4)("toStringTag");e.exports=function(e,t,i){e&&!o(e=i?e:e.prototype,a)&&n(e,a,{configurable:!0,value:t})}},function(e,t,i){t.f=i(4)},function(e,t,i){var n=i(7),o=i(8),a=i(39),r=i(41),s=i(9).f;e.exports=function(e){var t=o.Symbol||(o.Symbol=a?{}:n.Symbol||{});"_"==e.charAt(0)||e in t||s(t,e,{value:r.f(e)})}},function(e,t,i){var n=i(0)(i(118),i(119),null,null,null);e.exports=n.exports},function(e,t,i){var n=i(0)(i(120),i(121),null,null,null);e.exports=n.exports},function(e,t,i){var n=i(0)(i(122),i(125),null,null,null);e.exports=n.exports},function(e,t,i){var n=i(71);e.exports=function(e,t,i){if(n(e),void 0===t)return e;switch(i){case 1:return function(i){return e.call(t,i)};case 2:return function(i,n){return e.call(t,i,n)};case 3:return function(i,n,o){return e.call(t,i,n,o)}}return function(){return e.apply(t,arguments)}}},function(e,t,i){e.exports=!i(10)&&!i(20)(function(){return 7!=Object.defineProperty(i(48)("div"),"a",{get:function(){return 7}}).a})},function(e,t,i){var n=i(19),o=i(7).document,a=n(o)&&n(o.createElement);e.exports=function(e){return a?o.createElement(e):{}}},function(e,t,i){var n=i(11),o=i(17),a=i(73)(!1),r=i(33)("IE_PROTO");e.exports=function(e,t){var i,s=o(e),l=0,c=[];for(i in s)i!=r&&n(s,i)&&c.push(i);for(;t.length>l;)n(s,i=t[l++])&&(~a(c,i)||c.push(i));return c}},function(e,t,i){var n=i(30);e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==n(e)?e.split(""):Object(e)}},function(e,t,i){var n=i(32),o=Math.min;e.exports=function(e){return e>0?o(n(e),9007199254740991):0}},function(e,t,i){var n=i(0)(i(76),i(107),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var o=i(77),a=n(o),r=i(5),s=n(r),l="function"==typeof s.default&&"symbol"==typeof a.default?function(e){return typeof e}:function(e){return e&&"function"==typeof s.default&&e.constructor===s.default&&e!==s.default.prototype?"symbol":typeof e};t.default="function"==typeof s.default&&"symbol"===l(a.default)?function(e){return void 0===e?"undefined":l(e)}:function(e){return e&&"function"==typeof s.default&&e.constructor===s.default&&e!==s.default.prototype?"symbol":void 0===e?"undefined":l(e)}},function(e,t,i){"use strict";var n=i(39),o=i(18),a=i(55),r=i(15),s=i(22),l=i(80),c=i(40),d=i(83),u=i(4)("iterator"),f=!([].keys&&"next"in[].keys()),p=function(){return this};e.exports=function(e,t,i,h,b,m,g){l(i,t,h);var v,k,x,w=function(e){if(!f&&e in S)return S[e];switch(e){case"keys":case"values":return function(){return new i(this,e)}}return function(){return new i(this,e)}},y=t+" Iterator",_="values"==b,C=!1,S=e.prototype,$=S[u]||S["@@iterator"]||b&&S[b],D=$||w(b),z=b?_?w("entries"):D:void 0,O="Array"==t?S.entries||$:$;if(O&&(x=d(O.call(new e)))!==Object.prototype&&x.next&&(c(x,y,!0),n||"function"==typeof x[u]||r(x,u,p)),_&&$&&"values"!==$.name&&(C=!0,D=function(){return $.call(this)}),n&&!g||!f&&!C&&S[u]||r(S,u,D),s[t]=D,s[y]=p,b)if(v={values:_?D:w("values"),keys:m?D:w("keys"),entries:z},g)for(k in v)k in S||a(S,k,v[k]);else o(o.P+o.F*(f||C),t,v);return v}},function(e,t,i){e.exports=i(15)},function(e,t,i){var n=i(16),o=i(81),a=i(35),r=i(33)("IE_PROTO"),s=function(){},l=function(){var e,t=i(48)("iframe"),n=a.length;for(t.style.display="none",i(82).appendChild(t),t.src="javascript:",e=t.contentWindow.document,e.open(),e.write("<script>document.F=Object<\/script>"),e.close(),l=e.F;n--;)delete l.prototype[a[n]];return l()};e.exports=Object.create||function(e,t){var i;return null!==e?(s.prototype=n(e),i=new s,s.prototype=null,i[r]=e):i=l(),void 0===t?i:o(i,t)}},function(e,t,i){i(84);for(var n=i(7),o=i(15),a=i(22),r=i(4)("toStringTag"),s="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),l=0;l<s.length;l++){var c=s[l],d=n[c],u=d&&d.prototype;u&&!u[r]&&o(u,r,c),a[c]=a.Array}},function(e,t,i){var n=i(49),o=i(35).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return n(e,o)}},function(e,t,i){e.exports={default:i(97),__esModule:!0}},function(e,t,i){var n=i(99),o=i(4)("iterator"),a=i(22);e.exports=i(8).getIteratorMethod=function(e){if(void 0!=e)return e[o]||e["@@iterator"]||a[n(e)]}},function(e,t,i){var n=i(0)(i(108),i(109),null,null,null);e.exports=n.exports},function(e,t,i){var n=i(0)(i(137),i(138),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";var n=i(1),o=i.n(n),a=i(3),r=i.n(a),s=i(13);t.a={mixins:[s.a],components:o()({},r.a.name,r.a),props:{active:{type:Boolean,default:!0},title:String,closable:{type:Boolean,default:!0},type:String,hasIcon:Boolean,size:String,iconSize:String,autoClose:{type:Boolean,default:!1},duration:{type:Number,default:5e3}},data:function(){return{isActive:this.active}},watch:{active:function(e){this.isActive=e},isActive:function(e){e?this.setAutoClose():this.timer&&clearTimeout(this.timer)}},computed:{icon:function(){switch(this.type){case"is-info":return"information";case"is-success":return"check-circle";case"is-warning":return"alert";case"is-danger":return"alert-circle";default:return null}}},methods:{close:function(){this.isActive=!1,this.$emit("close"),this.$emit("update:active",!1)},setAutoClose:function(){var e=this;this.autoClose&&(this.timer=setTimeout(function(){e.isActive&&e.close()},this.duration))}},mounted:function(){this.setAutoClose()}}},function(e,t,i){var n=i(0)(i(149),i(150),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";var n=i(2),o=i(6);t.a={props:{type:{type:String,default:"is-dark"},message:String,duration:Number,queue:{type:Boolean,default:void 0},position:{type:String,default:"is-top",validator:function(e){return["is-top-right","is-top","is-top-left","is-bottom-right","is-bottom","is-bottom-left"].indexOf(e)>-1}},container:String},data:function(){return{isActive:!1,parentTop:null,parentBottom:null,newContainer:this.container||n.a.defaultContainerElement}},computed:{correctParent:function(){switch(this.position){case"is-top-right":case"is-top":case"is-top-left":return this.parentTop;case"is-bottom-right":case"is-bottom":case"is-bottom-left":return this.parentBottom}},transition:function(){switch(this.position){case"is-top-right":case"is-top":case"is-top-left":return{enter:"fadeInDown",leave:"fadeOut"};case"is-bottom-right":case"is-bottom":case"is-bottom-left":return{enter:"fadeInUp",leave:"fadeOut"}}}},methods:{shouldQueue:function(){return!!(void 0!==this.queue?this.queue:n.a.defaultNoticeQueue)&&(this.parentTop.childElementCount>0||this.parentBottom.childElementCount>0)},close:function(){var e=this;clearTimeout(this.timer),this.isActive=!1,setTimeout(function(){e.$destroy(),Object(o.e)(e.$el)},150)},showNotice:function(){var e=this;if(this.shouldQueue())return void setTimeout(function(){return e.showNotice()},250);this.correctParent.insertAdjacentElement("afterbegin",this.$el),this.isActive=!0,this.indefinite||(this.timer=setTimeout(function(){return e.close()},this.newDuration))},setupContainer:function(){if(this.parentTop=document.querySelector(".notices.is-top"),this.parentBottom=document.querySelector(".notices.is-bottom"),!this.parentTop||!this.parentBottom){this.parentTop||(this.parentTop=document.createElement("div"),this.parentTop.className="notices is-top"),this.parentBottom||(this.parentBottom=document.createElement("div"),this.parentBottom.className="notices is-bottom");var e=document.querySelector(this.newContainer)||document.body;e.appendChild(this.parentTop),e.appendChild(this.parentBottom),this.newContainer&&(this.parentTop.classList.add("has-custom-container"),this.parentBottom.classList.add("has-custom-container"))}}},beforeMount:function(){this.setupContainer()},mounted:function(){this.showNotice()}}},function(e,t,i){var n=i(0)(i(179),i(180),null,null,null);e.exports=n.exports},function(e,t,i){var n=i(0)(i(188),i(189),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";function n(e){return new(("undefined"!=typeof window&&window.Vue?window.Vue:O.a).extend(T.a))({el:document.createElement("div"),propsData:e})}Object.defineProperty(t,"__esModule",{value:!0});var o={};i.d(o,"Autocomplete",function(){return p}),i.d(o,"Checkbox",function(){return k}),i.d(o,"Collapse",function(){return _}),i.d(o,"Datepicker",function(){return D}),i.d(o,"Dialog",function(){return P}),i.d(o,"Dropdown",function(){return N}),i.d(o,"Field",function(){return q}),i.d(o,"Icon",function(){return G}),i.d(o,"Input",function(){return Q}),i.d(o,"Loading",function(){return ne}),i.d(o,"Message",function(){return se}),i.d(o,"Modal",function(){return fe}),i.d(o,"Notification",function(){return me}),i.d(o,"Pagination",function(){return xe}),i.d(o,"Panel",function(){return Ce}),i.d(o,"Radio",function(){return Ae}),i.d(o,"Select",function(){return Pe}),i.d(o,"Snackbar",function(){return Ie}),i.d(o,"Switch",function(){return He}),i.d(o,"Table",function(){return Ke}),i.d(o,"Tabs",function(){return tt}),i.d(o,"Tag",function(){return st}),i.d(o,"Taginput",function(){return ut}),i.d(o,"Timepicker",function(){return bt}),i.d(o,"Toast",function(){return xt}),i.d(o,"Tooltip",function(){return Ct}),i.d(o,"Upload",function(){return zt});var a=i(14),r=i.n(a),s=(i(75),i(52)),l=i.n(s),c=function(e){"undefined"!=typeof window&&window.Vue&&window.Vue.use(e)},d=function(e,t){e.component(t.name,t)},u=function(e,t,i){e.prototype[t]=i},f={install:function(e){d(e,l.a)}};c(f);var p=f,h=i(61),b=i.n(h),m=i(110),g=i.n(m),v={install:function(e){d(e,b.a),d(e,g.a)}};c(v);var k=v,x=i(113),w=i.n(x),y={install:function(e){d(e,w.a)}};c(y);var _=y,C=i(116),S=i.n(C),$={install:function(e){d(e,S.a)}};c($);var D=$,z=i(23),O=i.n(z),A=i(135),T=i.n(A),M={alert:function(e){var t=void 0;"string"==typeof e&&(t=e);var i={canCancel:!1,message:t};return n(r()(i,e))},confirm:function(e){var t={};return n(r()(t,e))},prompt:function(e){var t={hasInput:!0,confirmText:"Done"};return n(r()(t,e))}},j={install:function(e){d(e,T.a),u(e,"$dialog",M)}};c(j);var P=j,E=i(43),F=i.n(E),B=i(44),V=i.n(B),I={install:function(e){d(e,F.a),d(e,V.a)}};c(I);var N=I,R=i(45),L=i.n(R),H={install:function(e){d(e,L.a)}};c(H);var q=H,U=i(3),W=i.n(U),Y={install:function(e){d(e,W.a)}};c(Y);var G=Y,K=i(27),X=i.n(K),J={install:function(e){d(e,X.a)}};c(J);var Q=J,Z=i(140),ee=i.n(Z),te={open:function(e){var t={programmatic:!0},i=r()(t,e);return new(("undefined"!=typeof window&&window.Vue?window.Vue:O.a).extend(ee.a))({el:document.createElement("div"),propsData:i})}},ie={install:function(e){d(e,ee.a),u(e,"$loading",te)}};c(ie);var ne=ie,oe=i(143),ae=i.n(oe),re={install:function(e){d(e,ae.a)}};c(re);var se=re,le=i(62),ce=i.n(le),de={open:function(e){var t=void 0,i=void 0;"string"==typeof e&&(t=e);var n={programmatic:!0,content:t};e.parent&&(i=e.parent,delete e.parent);var o=r()(n,e);return new(("undefined"!=typeof window&&window.Vue?window.Vue:O.a).extend(ce.a))({parent:i,el:document.createElement("div"),propsData:o})}},ue={install:function(e){d(e,ce.a),u(e,"$modal",de)}};c(ue);var fe=ue,pe=i(146),he=i.n(pe),be={install:function(e){d(e,he.a)}};c(be);var me=be,ge=i(64),ve=i.n(ge),ke={install:function(e){d(e,ve.a)}};c(ke);var xe=ke,we=i(151),ye=i.n(we),_e={install:function(e){d(e,ye.a)}};c(_e);var Ce=_e,Se=i(154),$e=i.n(Se),De=i(157),ze=i.n(De),Oe={install:function(e){d(e,$e.a),d(e,ze.a)}};c(Oe);var Ae=Oe,Te=i(28),Me=i.n(Te),je={install:function(e){d(e,Me.a)}};c(je);var Pe=je,Ee=i(160),Fe=i.n(Ee),Be={open:function(e){var t=void 0;"string"==typeof e&&(t=e);var i={type:"is-success",position:"is-bottom-right",message:t},n=r()(i,e);return new(("undefined"!=typeof window&&window.Vue?window.Vue:O.a).extend(Fe.a))({el:document.createElement("div"),propsData:n})}},Ve={install:function(e){u(e,"$snackbar",Be)}};c(Ve);var Ie=Ve,Ne=i(163),Re=i.n(Ne),Le={install:function(e){d(e,Re.a)}};c(Le);var He=Le,qe=i(166),Ue=i.n(qe),We=i(66),Ye=i.n(We),Ge={install:function(e){d(e,Ue.a),d(e,Ye.a)}};c(Ge);var Ke=Ge,Xe=i(182),Je=i.n(Xe),Qe=i(185),Ze=i.n(Qe),et={install:function(e){d(e,Je.a),d(e,Ze.a)}};c(et);var tt=et,it=i(67),nt=i.n(it),ot=i(190),at=i.n(ot),rt={install:function(e){d(e,nt.a),d(e,at.a)}};c(rt);var st=rt,lt=i(193),ct=i.n(lt),dt={install:function(e){d(e,ct.a)}};c(dt);var ut=dt,ft=i(196),pt=i.n(ft),ht={install:function(e){d(e,pt.a)}};c(ht);var bt=ht,mt=i(199),gt=i.n(mt),vt={open:function(e){var t=void 0;"string"==typeof e&&(t=e);var i={message:t},n=r()(i,e);return new(("undefined"!=typeof window&&window.Vue?window.Vue:O.a).extend(gt.a))({el:document.createElement("div"),propsData:n})}},kt={install:function(e){u(e,"$toast",vt)}};c(kt);var xt=kt,wt=i(202),yt=i.n(wt),_t={install:function(e){d(e,yt.a)}};c(_t);var Ct=_t,St=i(205),$t=i.n(St),Dt={install:function(e){d(e,$t.a)}};c(Dt);var zt=Dt,Ot=i(2),At={install:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};Object(Ot.b)(r()(Ot.a,t));for(var i in o)e.use(o[i])}};c(At);t.default=At},function(e,t,i){i(70),e.exports=i(8).Object.assign},function(e,t,i){var n=i(18);n(n.S+n.F,"Object",{assign:i(72)})},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,i){"use strict";var n=i(24),o=i(36),a=i(26),r=i(37),s=i(50),l=Object.assign;e.exports=!l||i(20)(function(){var e={},t={},i=Symbol(),n="abcdefghijklmnopqrst";return e[i]=7,n.split("").forEach(function(e){t[e]=e}),7!=l({},e)[i]||Object.keys(l({},t)).join("")!=n})?function(e,t){for(var i=r(e),l=arguments.length,c=1,d=o.f,u=a.f;l>c;)for(var f,p=s(arguments[c++]),h=d?n(p).concat(d(p)):n(p),b=h.length,m=0;b>m;)u.call(p,f=h[m++])&&(i[f]=p[f]);return i}:l},function(e,t,i){var n=i(17),o=i(51),a=i(74);e.exports=function(e){return function(t,i,r){var s,l=n(t),c=o(l.length),d=a(r,c);if(e&&i!=i){for(;c>d;)if((s=l[d++])!=s)return!0}else for(;c>d;d++)if((e||d in l)&&l[d]===i)return e||d||0;return!e&&-1}}},function(e,t,i){var n=i(32),o=Math.max,a=Math.min;e.exports=function(e,t){return e=n(e),e<0?o(e+t,0):a(e,t)}},function(e,t){},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(53),o=i.n(n),a=i(59),r=i.n(a),s=i(1),l=i.n(s),c=i(6),d=i(12),u=i(27),f=i.n(u);t.default={name:"BAutocomplete",components:l()({},f.a.name,f.a),mixins:[d.a],inheritAttrs:!1,props:{value:[Number,String],data:{type:Array,default:function(){return[]}},field:{type:String,default:"value"},keepFirst:Boolean,clearOnSelect:Boolean,openOnFocus:Boolean},data:function(){return{selected:null,hovered:null,isActive:!1,newValue:this.value,isListInViewportVertically:!0,hasFocus:!1,_isAutocomplete:!0,_elementRef:"input"}},computed:{whiteList:function(){var e=[];if(e.push(this.$refs.input.$el.querySelector("input")),e.push(this.$refs.dropdown),void 0!==this.$refs.dropdown){var t=this.$refs.dropdown.querySelectorAll("*"),i=!0,n=!1,o=void 0;try{for(var a,s=r()(t);!(i=(a=s.next()).done);i=!0){var l=a.value;e.push(l)}}catch(e){n=!0,o=e}finally{try{!i&&s.return&&s.return()}finally{if(n)throw o}}}return e},hasDefaultSlot:function(){return!!this.$scopedSlots.default},hasEmptySlot:function(){return!!this.$slots.empty},hasHeaderSlot:function(){return!!this.$slots.header}},watch:{isActive:function(e){var t=this;e?this.calcDropdownInViewportVertical():(this.$nextTick(function(){return t.setHovered(null)}),setTimeout(function(){t.calcDropdownInViewportVertical()},100))},newValue:function(e){this.$emit("input",e);var t=this.getValue(this.selected);t&&t!==e&&this.setSelected(null,!1),!this.hasFocus||this.openOnFocus&&!e||(this.isActive=!!e)},value:function(e){this.newValue=e,!this.isValid&&this.$refs.input.checkHtml5Validity()},data:function(e){this.keepFirst&&this.selectFirstOption(e)}},methods:{setHovered:function(e){void 0!==e&&(this.hovered=e)},setSelected:function(e){var t=this,i=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];void 0!==e&&(this.selected=e,this.$emit("select",this.selected),null!==this.selected&&(this.newValue=this.clearOnSelect?"":this.getValue(this.selected)),i&&this.$nextTick(function(){t.isActive=!1}))},selectFirstOption:function(e){var t=this;this.$nextTick(function(){e.length?(t.openOnFocus||""!==t.newValue&&t.hovered!==e[0])&&t.setHovered(e[0]):t.setHovered(null)})},enterPressed:function(){null!==this.hovered&&this.setSelected(this.hovered)},tabPressed:function(){if(null===this.hovered)return void(this.isActive=!1);this.setSelected(this.hovered)},clickedOutside:function(e){this.whiteList.indexOf(e.target)<0&&(this.isActive=!1)},getValue:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(e){var i="object"===(void 0===e?"undefined":o()(e))?Object(c.b)(e,this.field):e,n="string"==typeof this.newValue?Object(c.a)(this.newValue):this.newValue,a=new RegExp("("+n+")","gi");return t?i.replace(a,"<b>$1</b>"):i}},calcDropdownInViewportVertical:function(){var e=this;this.$nextTick(function(){if(void 0!==e.$refs.dropdown){var t=e.$refs.dropdown.getBoundingClientRect();e.isListInViewportVertically=t.top>=0&&t.bottom<=(window.innerHeight||document.documentElement.clientHeight)}})},keyArrows:function(e){var t="down"===e?1:-1;if(this.isActive){var i=this.data.indexOf(this.hovered)+t;i=i>this.data.length-1?this.data.length:i,i=i<0?0:i,this.setHovered(this.data[i]);var n=this.$refs.dropdown.querySelector(".dropdown-content"),o=n.querySelectorAll(".dropdown-item:not(.is-disabled)")[i];if(!o)return;var a=n.scrollTop,r=n.scrollTop+n.clientHeight-o.clientHeight;o.offsetTop<a?n.scrollTop=o.offsetTop:o.offsetTop>=r&&(n.scrollTop=o.offsetTop-n.clientHeight+o.clientHeight)}else this.isActive=!0},focused:function(e){this.getValue(this.selected)===this.newValue&&this.$el.querySelector("input").select(),this.openOnFocus&&(this.isActive=!0,this.keepFirst&&this.selectFirstOption(this.data)),this.hasFocus=!0,this.$emit("focus",e)},onBlur:function(e){this.hasFocus=!1,this.$emit("blur",e)}},created:function(){"undefined"!=typeof window&&(document.addEventListener("click",this.clickedOutside),window.addEventListener("resize",this.calcDropdownInViewportVertical))},beforeDestroy:function(){"undefined"!=typeof window&&(document.removeEventListener("click",this.clickedOutside),window.removeEventListener("resize",this.calcDropdownInViewportVertical))}}},function(e,t,i){e.exports={default:i(78),__esModule:!0}},function(e,t,i){i(38),i(57),e.exports=i(41).f("iterator")},function(e,t,i){var n=i(32),o=i(31);e.exports=function(e){return function(t,i){var a,r,s=String(o(t)),l=n(i),c=s.length;return l<0||l>=c?e?"":void 0:(a=s.charCodeAt(l),a<55296||a>56319||l+1===c||(r=s.charCodeAt(l+1))<56320||r>57343?e?s.charAt(l):a:e?s.slice(l,l+2):r-56320+(a-55296<<10)+65536)}}},function(e,t,i){"use strict";var n=i(56),o=i(21),a=i(40),r={};i(15)(r,i(4)("iterator"),function(){return this}),e.exports=function(e,t,i){e.prototype=n(r,{next:o(1,i)}),a(e,t+" Iterator")}},function(e,t,i){var n=i(9),o=i(16),a=i(24);e.exports=i(10)?Object.defineProperties:function(e,t){o(e);for(var i,r=a(t),s=r.length,l=0;s>l;)n.f(e,i=r[l++],t[i]);return e}},function(e,t,i){var n=i(7).document;e.exports=n&&n.documentElement},function(e,t,i){var n=i(11),o=i(37),a=i(33)("IE_PROTO"),r=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=o(e),n(e,a)?e[a]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?r:null}},function(e,t,i){"use strict";var n=i(85),o=i(86),a=i(22),r=i(17);e.exports=i(54)(Array,"Array",function(e,t){this._t=r(e),this._i=0,this._k=t},function(){var e=this._t,t=this._k,i=this._i++;return!e||i>=e.length?(this._t=void 0,o(1)):"keys"==t?o(0,i):"values"==t?o(0,e[i]):o(0,[i,e[i]])},"values"),a.Arguments=a.Array,n("keys"),n("values"),n("entries")},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,i){i(88),i(94),i(95),i(96),e.exports=i(8).Symbol},function(e,t,i){"use strict";var n=i(7),o=i(11),a=i(10),r=i(18),s=i(55),l=i(89).KEY,c=i(20),d=i(34),u=i(40),f=i(25),p=i(4),h=i(41),b=i(42),m=i(90),g=i(91),v=i(16),k=i(19),x=i(17),w=i(29),y=i(21),_=i(56),C=i(92),S=i(93),$=i(9),D=i(24),z=S.f,O=$.f,A=C.f,T=n.Symbol,M=n.JSON,j=M&&M.stringify,P=p("_hidden"),E=p("toPrimitive"),F={}.propertyIsEnumerable,B=d("symbol-registry"),V=d("symbols"),I=d("op-symbols"),N=Object.prototype,R="function"==typeof T,L=n.QObject,H=!L||!L.prototype||!L.prototype.findChild,q=a&&c(function(){return 7!=_(O({},"a",{get:function(){return O(this,"a",{value:7}).a}})).a})?function(e,t,i){var n=z(N,t);n&&delete N[t],O(e,t,i),n&&e!==N&&O(N,t,n)}:O,U=function(e){var t=V[e]=_(T.prototype);return t._k=e,t},W=R&&"symbol"==typeof T.iterator?function(e){return"symbol"==typeof e}:function(e){return e instanceof T},Y=function(e,t,i){return e===N&&Y(I,t,i),v(e),t=w(t,!0),v(i),o(V,t)?(i.enumerable?(o(e,P)&&e[P][t]&&(e[P][t]=!1),i=_(i,{enumerable:y(0,!1)})):(o(e,P)||O(e,P,y(1,{})),e[P][t]=!0),q(e,t,i)):O(e,t,i)},G=function(e,t){v(e);for(var i,n=m(t=x(t)),o=0,a=n.length;a>o;)Y(e,i=n[o++],t[i]);return e},K=function(e,t){return void 0===t?_(e):G(_(e),t)},X=function(e){var t=F.call(this,e=w(e,!0));return!(this===N&&o(V,e)&&!o(I,e))&&(!(t||!o(this,e)||!o(V,e)||o(this,P)&&this[P][e])||t)},J=function(e,t){if(e=x(e),t=w(t,!0),e!==N||!o(V,t)||o(I,t)){var i=z(e,t);return!i||!o(V,t)||o(e,P)&&e[P][t]||(i.enumerable=!0),i}},Q=function(e){for(var t,i=A(x(e)),n=[],a=0;i.length>a;)o(V,t=i[a++])||t==P||t==l||n.push(t);return n},Z=function(e){for(var t,i=e===N,n=A(i?I:x(e)),a=[],r=0;n.length>r;)!o(V,t=n[r++])||i&&!o(N,t)||a.push(V[t]);return a};R||(T=function(){if(this instanceof T)throw TypeError("Symbol is not a constructor!");var e=f(arguments.length>0?arguments[0]:void 0),t=function(i){this===N&&t.call(I,i),o(this,P)&&o(this[P],e)&&(this[P][e]=!1),q(this,e,y(1,i))};return a&&H&&q(N,e,{configurable:!0,set:t}),U(e)},s(T.prototype,"toString",function(){return this._k}),S.f=J,$.f=Y,i(58).f=C.f=Q,i(26).f=X,i(36).f=Z,a&&!i(39)&&s(N,"propertyIsEnumerable",X,!0),h.f=function(e){return U(p(e))}),r(r.G+r.W+r.F*!R,{Symbol:T});for(var ee="hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables".split(","),te=0;ee.length>te;)p(ee[te++]);for(var ie=D(p.store),ne=0;ie.length>ne;)b(ie[ne++]);r(r.S+r.F*!R,"Symbol",{for:function(e){return o(B,e+="")?B[e]:B[e]=T(e)},keyFor:function(e){if(!W(e))throw TypeError(e+" is not a symbol!");for(var t in B)if(B[t]===e)return t},useSetter:function(){H=!0},useSimple:function(){H=!1}}),r(r.S+r.F*!R,"Object",{create:K,defineProperty:Y,defineProperties:G,getOwnPropertyDescriptor:J,getOwnPropertyNames:Q,getOwnPropertySymbols:Z}),M&&r(r.S+r.F*(!R||c(function(){var e=T();return"[null]"!=j([e])||"{}"!=j({a:e})||"{}"!=j(Object(e))})),"JSON",{stringify:function(e){for(var t,i,n=[e],o=1;arguments.length>o;)n.push(arguments[o++]);if(i=t=n[1],(k(t)||void 0!==e)&&!W(e))return g(t)||(t=function(e,t){if("function"==typeof i&&(t=i.call(this,e,t)),!W(t))return t}),n[1]=t,j.apply(M,n)}}),T.prototype[E]||i(15)(T.prototype,E,T.prototype.valueOf),u(T,"Symbol"),u(Math,"Math",!0),u(n.JSON,"JSON",!0)},function(e,t,i){var n=i(25)("meta"),o=i(19),a=i(11),r=i(9).f,s=0,l=Object.isExtensible||function(){return!0},c=!i(20)(function(){return l(Object.preventExtensions({}))}),d=function(e){r(e,n,{value:{i:"O"+ ++s,w:{}}})},u=function(e,t){if(!o(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!a(e,n)){if(!l(e))return"F";if(!t)return"E";d(e)}return e[n].i},f=function(e,t){if(!a(e,n)){if(!l(e))return!0;if(!t)return!1;d(e)}return e[n].w},p=function(e){return c&&h.NEED&&l(e)&&!a(e,n)&&d(e),e},h=e.exports={KEY:n,NEED:!1,fastKey:u,getWeak:f,onFreeze:p}},function(e,t,i){var n=i(24),o=i(36),a=i(26);e.exports=function(e){var t=n(e),i=o.f;if(i)for(var r,s=i(e),l=a.f,c=0;s.length>c;)l.call(e,r=s[c++])&&t.push(r);return t}},function(e,t,i){var n=i(30);e.exports=Array.isArray||function(e){return"Array"==n(e)}},function(e,t,i){var n=i(17),o=i(58).f,a={}.toString,r="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[],s=function(e){try{return o(e)}catch(e){return r.slice()}};e.exports.f=function(e){return r&&"[object Window]"==a.call(e)?s(e):o(n(e))}},function(e,t,i){var n=i(26),o=i(21),a=i(17),r=i(29),s=i(11),l=i(47),c=Object.getOwnPropertyDescriptor;t.f=i(10)?c:function(e,t){if(e=a(e),t=r(t,!0),l)try{return c(e,t)}catch(e){}if(s(e,t))return o(!n.f.call(e,t),e[t])}},function(e,t){},function(e,t,i){i(42)("asyncIterator")},function(e,t,i){i(42)("observable")},function(e,t,i){i(57),i(38),e.exports=i(98)},function(e,t,i){var n=i(16),o=i(60);e.exports=i(8).getIterator=function(e){var t=o(e);if("function"!=typeof t)throw TypeError(e+" is not iterable!");return n(t.call(e))}},function(e,t,i){var n=i(30),o=i(4)("toStringTag"),a="Arguments"==n(function(){return arguments}()),r=function(e,t){try{return e[t]}catch(e){}};e.exports=function(e){var t,i,s;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(i=r(t=Object(e),o))?i:a?n(t):"Object"==(s=n(t))&&"function"==typeof t.callee?"Arguments":s}},function(e,t,i){e.exports={default:i(101),__esModule:!0}},function(e,t,i){i(102);var n=i(8).Object;e.exports=function(e,t,i){return n.defineProperty(e,t,i)}},function(e,t,i){var n=i(18);n(n.S+n.F*!i(10),"Object",{defineProperty:i(9).f})},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1),o=i.n(n),a=i(3),r=i.n(a),s=i(2),l=i(12);t.default={name:"BInput",components:o()({},r.a.name,r.a),mixins:[l.a],inheritAttrs:!1,props:{value:[Number,String],type:{type:String,default:"text"},passwordReveal:Boolean,hasCounter:{type:Boolean,default:function(){return s.a.defaultInputHasCounter}}},data:function(){return{newValue:this.value,newType:this.type,newAutocomplete:this.autocomplete||s.a.defaultInputAutocomplete,isPasswordVisible:!1,_elementRef:"textarea"===this.type?"textarea":"input"}},computed:{rootClasses:function(){return[this.iconPosition,this.size,{"is-expanded":this.expanded,"is-loading":this.loading,"is-clearfix":!this.hasMessage}]},inputClasses:function(){return[this.statusType,this.size,{"is-rounded":this.rounded}]},hasIconRight:function(){return this.passwordReveal||this.loading||this.statusType},iconPosition:function(){return this.icon&&this.hasIconRight?"has-icons-left has-icons-right":!this.icon&&this.hasIconRight?"has-icons-right":this.icon?"has-icons-left":void 0},statusTypeIcon:function(){switch(this.statusType){case"is-success":return"check";case"is-danger":return"alert-circle";case"is-info":return"information";case"is-warning":return"alert"}},hasMessage:function(){return!!this.statusMessage},passwordVisibleIcon:function(){return this.isPasswordVisible?"eye-off":"eye"},valueLength:function(){return"string"==typeof this.newValue?this.newValue.length:"number"==typeof this.newValue?this.newValue.toString().length:0}},watch:{value:function(e){this.newValue=e},newValue:function(e){this.$emit("input",e),!this.isValid&&this.checkHtml5Validity()}},methods:{togglePasswordVisibility:function(){var e=this;this.isPasswordVisible=!this.isPasswordVisible,this.newType=this.isPasswordVisible?"text":"password",this.$nextTick(function(){e.$refs.input.focus()})},onInput:function(e){var t=this;this.$nextTick(function(){t.newValue=e.target.value})}}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(2);t.default={name:"BIcon",props:{type:String,pack:String,icon:String,size:String,customSize:String,customClass:String,both:Boolean},computed:{newIcon:function(){return this.both?"mdi"===this.newPack?this.newPack+"-"+this.icon:"fa-"+this.getEquivalentIconOf(this.icon):"mdi"===this.newPack?this.newPack+"-"+this.icon:"fa-"+this.icon},newPack:function(){return this.pack||n.a.defaultIconPack},newType:function(){if(this.type){var e=this.type.split("-");if(e.length)return"has-text-"+e[1]}},newCustomSize:function(){return this.customSize||this.customSizeByPack},customSizeByPack:function(){var e="mdi"===this.newPack?"mdi-24px":"fa-lg",t="mdi"===this.newPack?"mdi-36px":"fa-2x",i="mdi"===this.newPack?"mdi-48px":"fa-3x";switch(this.size){case"is-small":return;case"is-medium":return t;case"is-large":return i;default:return e}}},methods:{getEquivalentIconOf:function(e){switch(e){case"check":return"check";case"information":return"info-circle";case"check-circle":return"check-circle";case"alert":return"exclamation-triangle";case"alert-circle":return"exclamation-circle";case"arrow-up":return"arrow-up";case"chevron-right":return"angle-right";case"chevron-left":return"angle-left";case"chevron-down":return"angle-down";case"eye":return"eye";case"eye-off":return"eye-slash";case"menu-down":return"caret-down";case"menu-up":return"caret-up";default:return e}}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("span",{staticClass:"icon",class:[e.newType,e.size]},[i("i",{class:[e.newPack,e.newIcon,e.newCustomSize,e.customClass]})])},staticRenderFns:[]}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"control",class:e.rootClasses},["textarea"!==e.type?i("input",e._b({ref:"input",staticClass:"input",class:e.inputClasses,attrs:{type:e.newType,autocomplete:e.newAutocomplete,maxlength:e.maxlength},domProps:{value:e.newValue},on:{input:e.onInput,blur:e.onBlur,focus:e.onFocus}},"input",e.$attrs,!1)):i("textarea",e._b({ref:"textarea",staticClass:"textarea",class:e.inputClasses,attrs:{maxlength:e.maxlength},domProps:{value:e.newValue},on:{input:e.onInput,blur:e.onBlur,focus:e.onFocus}},"textarea",e.$attrs,!1)),e._v(" "),e.icon?i("b-icon",{staticClass:"is-left",attrs:{icon:e.icon,pack:e.iconPack,size:e.iconSize}}):e._e(),e._v(" "),e.loading||!e.passwordReveal&&!e.statusType?e._e():i("b-icon",{staticClass:"is-right",class:{"is-clickable":e.passwordReveal},attrs:{icon:e.passwordReveal?e.passwordVisibleIcon:e.statusTypeIcon,pack:e.iconPack,size:e.iconSize,type:e.passwordReveal?"is-primary":e.statusType,both:""},nativeOn:{click:function(t){e.togglePasswordVisibility(t)}}}),e._v(" "),e.maxlength&&e.hasCounter&&"number"!==e.type?i("small",{staticClass:"help counter",class:{"is-invisible":!e.isFocused}},[e._v("\n "+e._s(e.valueLength)+" / "+e._s(e.maxlength)+"\n ")]):e._e()],1)},staticRenderFns:[]}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"autocomplete control",class:{"is-expanded":e.expanded}},[i("b-input",e._b({ref:"input",attrs:{size:e.size,loading:e.loading,rounded:e.rounded,icon:e.icon,"icon-pack":e.iconPack,maxlength:e.maxlength,autocomplete:"off"},on:{focus:e.focused,blur:e.onBlur},nativeOn:{keyup:function(t){if(!("button"in t)&&e._k(t.keyCode,"esc",27,t.key))return null;t.preventDefault(),e.isActive=!1},keydown:[function(t){if(!("button"in t)&&e._k(t.keyCode,"tab",9,t.key))return null;e.tabPressed(t)},function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key))return null;t.preventDefault(),e.enterPressed(t)},function(t){if(!("button"in t)&&e._k(t.keyCode,"up",38,t.key))return null;t.preventDefault(),e.keyArrows("up")},function(t){if(!("button"in t)&&e._k(t.keyCode,"down",40,t.key))return null;t.preventDefault(),e.keyArrows("down")}]},model:{value:e.newValue,callback:function(t){e.newValue=t},expression:"newValue"}},"b-input",e.$attrs,!1)),e._v(" "),i("transition",{attrs:{name:"fade"}},[i("div",{directives:[{name:"show",rawName:"v-show",value:e.isActive&&(e.data.length>0||e.hasEmptySlot||e.hasHeaderSlot),expression:"isActive && (data.length > 0 || hasEmptySlot || hasHeaderSlot)"}],ref:"dropdown",staticClass:"dropdown-menu",class:{"is-opened-top":!e.isListInViewportVertically}},[i("div",{staticClass:"dropdown-content"},[e.hasHeaderSlot?i("div",{staticClass:"dropdown-item"},[e._t("header")],2):e._e(),e._v(" "),e._l(e.data,function(t,n){return i("a",{key:n,staticClass:"dropdown-item",class:{"is-hovered":t===e.hovered},on:{click:function(i){e.setSelected(t)}}},[e.hasDefaultSlot?e._t("default",null,{option:t,index:n}):i("span",{domProps:{innerHTML:e._s(e.getValue(t,!0))}})],2)}),e._v(" "),0===e.data.length&&e.hasEmptySlot?i("div",{staticClass:"dropdown-item is-disabled"},[e._t("empty")],2):e._e()],2)])])],1)},staticRenderFns:[]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(5),o=i.n(n);t.default={name:"BCheckbox",props:{value:[String,Number,Boolean,Function,Object,Array,o.a],nativeValue:[String,Number,Boolean,Function,Object,Array,o.a],type:String,disabled:Boolean,required:Boolean,name:String,size:String,trueValue:{type:[String,Number,Boolean,Function,Object,Array,o.a],default:!0},falseValue:{type:[String,Number,Boolean,Function,Object,Array,o.a],default:!1}},data:function(){return{newValue:this.value}},watch:{value:function(e){this.newValue=e},newValue:function(e){this.$emit("input",e)}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("label",{ref:"label",staticClass:"b-checkbox checkbox",class:[e.size,{"is-disabled":e.disabled}],attrs:{disabled:e.disabled,tabindex:!e.disabled&&0},on:{keydown:function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key)&&e._k(t.keyCode,"space",32,t.key))return null;t.preventDefault(),e.$refs.label.click()}}},[i("input",{directives:[{name:"model",rawName:"v-model",value:e.newValue,expression:"newValue"}],attrs:{type:"checkbox",disabled:e.disabled,required:e.required,name:e.name,"true-value":e.trueValue,"false-value":e.falseValue},domProps:{value:e.nativeValue,checked:Array.isArray(e.newValue)?e._i(e.newValue,e.nativeValue)>-1:e._q(e.newValue,e.trueValue)},on:{change:function(t){var i=e.newValue,n=t.target,o=n.checked?e.trueValue:e.falseValue;if(Array.isArray(i)){var a=e.nativeValue,r=e._i(i,a);n.checked?r<0&&(e.newValue=i.concat([a])):r>-1&&(e.newValue=i.slice(0,r).concat(i.slice(r+1)))}else e.newValue=o}}}),e._v(" "),i("span",{staticClass:"check",class:e.type}),e._v(" "),i("span",{staticClass:"control-label"},[e._t("default")],2)])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(111),i(112),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(5),o=i.n(n);t.default={name:"BCheckboxButton",props:{value:[String,Number,Boolean,Function,Object,Array,o.a],nativeValue:[String,Number,Boolean,Function,Object,Array,o.a],disabled:Boolean,name:String,size:String,type:{type:String,default:"is-primary"}},data:function(){return{newValue:this.value}},computed:{checked:function(){return Array.isArray(this.newValue)?this.newValue.indexOf(this.nativeValue)>=0:this.newValue===this.nativeValue}},watch:{value:function(e){this.newValue=e},newValue:function(e){this.$emit("input",e)}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"control"},[i("label",{ref:"label",staticClass:"b-checkbox checkbox button",class:[e.checked?e.type:null,e.size,{"is-disabled":e.disabled}],attrs:{disabled:e.disabled,tabindex:!e.disabled&&0},on:{keydown:function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key)&&e._k(t.keyCode,"space",32,t.key))return null;t.preventDefault(),e.$refs.label.click()}}},[e._t("default"),e._v(" "),i("input",{directives:[{name:"model",rawName:"v-model",value:e.newValue,expression:"newValue"}],attrs:{type:"checkbox",disabled:e.disabled,name:e.name},domProps:{value:e.nativeValue,checked:Array.isArray(e.newValue)?e._i(e.newValue,e.nativeValue)>-1:e.newValue},on:{change:function(t){var i=e.newValue,n=t.target,o=!!n.checked;if(Array.isArray(i)){var a=e.nativeValue,r=e._i(i,a);n.checked?r<0&&(e.newValue=i.concat([a])):r>-1&&(e.newValue=i.slice(0,r).concat(i.slice(r+1)))}else e.newValue=o}}})],2)])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(114),i(115),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={name:"BCollapse",props:{open:{type:Boolean,default:!0},animation:{type:String,default:"fade"}},data:function(){return{isOpen:this.open}},watch:{open:function(e){this.isOpen=e}},methods:{toggle:function(){this.isOpen=!this.isOpen,this.$emit("update:open",this.isOpen),this.$emit(this.isOpen?"open":"close")}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"collapse"},[i("div",{staticClass:"collapse-trigger",on:{click:e.toggle}},[e._t("trigger",null,{open:e.isOpen})],2),e._v(" "),i("transition",{attrs:{name:e.animation}},[i("div",{directives:[{name:"show",rawName:"v-show",value:e.isOpen,expression:"isOpen"}],staticClass:"collapse-content"},[e._t("default")],2)])],1)},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(117),i(134),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=i(1),a=i.n(o),r=i(12),s=i(6),l=i(2),c=i(43),d=i.n(c),u=i(44),f=i.n(u),p=i(27),h=i.n(p),b=i(45),m=i.n(b),g=i(28),v=i.n(g),k=i(3),x=i.n(k),w=i(128),y=i.n(w);t.default={name:"BDatepicker",components:(n={},a()(n,y.a.name,y.a),a()(n,h.a.name,h.a),a()(n,m.a.name,m.a),a()(n,v.a.name,v.a),a()(n,x.a.name,x.a),a()(n,d.a.name,d.a),a()(n,f.a.name,f.a),n),mixins:[r.a],inheritAttrs:!1,props:{value:Date,dayNames:{type:Array,default:function(){return Array.isArray(l.a.defaultDayNames)?l.a.defaultDayNames:["Su","M","Tu","W","Th","F","S"]}},monthNames:{type:Array,default:function(){return Array.isArray(l.a.defaultMonthNames)?l.a.defaultMonthNames:["January","February","March","April","May","June","July","August","September","October","November","December"]}},firstDayOfWeek:{type:Number,default:function(){return"number"==typeof l.a.defaultFirstDayOfWeek?l.a.defaultFirstDayOfWeek:0}},inline:Boolean,minDate:Date,maxDate:Date,focusedDate:Date,placeholder:String,readonly:{type:Boolean,default:!0},disabled:{type:Boolean,default:!1},unselectableDates:Array,unselectableDaysOfWeek:{type:Array,default:function(){return l.a.defaultUnselectableDaysOfWeek}},selectableDates:Array,dateFormatter:{type:Function,default:function(e){if("function"==typeof l.a.defaultDateFormatter)return l.a.defaultDateFormatter(e);var t=e.getFullYear()+"/"+(e.getMonth()+1)+"/"+e.getDate();return new Date(t).toLocaleDateString()}},dateParser:{type:Function,default:function(e){return"function"==typeof l.a.defaultDateParser?l.a.defaultDateParser(e):new Date(Date.parse(e))}},mobileNative:{type:Boolean,default:function(){return l.a.defaultDatepickerMobileNative}},position:String,events:Array,indicators:{type:String,default:"dots"}},data:function(){var e=this.value||this.focusedDate||new Date;return{dateSelected:this.value,focusedDateData:{month:e.getMonth(),year:e.getFullYear()},_elementRef:"input",_isDatepicker:!0}},computed:{listOfYears:function(){for(var e=this.maxDate?this.maxDate.getFullYear():Math.max((new Date).getFullYear(),this.focusedDateData.year)+3,t=this.minDate?this.minDate.getFullYear():1900,i=[],n=t;n<=e;n++)i.push(n);return i.reverse()},isFirstMonth:function(){return!!this.minDate&&new Date(this.focusedDateData.year,this.focusedDateData.month)<=new Date(this.minDate.getFullYear(),this.minDate.getMonth())},isLastMonth:function(){return!!this.maxDate&&new Date(this.focusedDateData.year,this.focusedDateData.month)>=new Date(this.maxDate.getFullYear(),this.maxDate.getMonth())},isMobile:function(){return this.mobileNative&&s.d.any()}},watch:{dateSelected:function(e){var t=e||new Date;this.focusedDateData={month:t.getMonth(),year:t.getFullYear()},this.$emit("input",e),this.$refs.dropdown&&(this.$refs.dropdown.isActive=!1)},value:function(e){this.dateSelected=e,!this.isValid&&this.$refs.input.checkHtml5Validity()},focusedDate:function(e){e&&(this.focusedDateData={month:e.getMonth(),year:e.getFullYear()})},"focusedDateData.month":function(e){this.$emit("change-month",e)},"focusedDateData.year":function(e){this.$emit("change-year",e)}},methods:{updateSelectedDate:function(e){this.dateSelected=e},onChange:function(e){var t=this.dateParser(e);t&&!isNaN(t)?this.dateSelected=t:(this.dateSelected=null,this.$refs.input.newValue=this.dateSelected)},formatValue:function(e){return e&&!isNaN(e)?this.dateFormatter(e):null},decrementMonth:function(){this.disabled||(this.focusedDateData.month>0?this.focusedDateData.month-=1:(this.focusedDateData.month=11,this.focusedDateData.year-=1))},incrementMonth:function(){this.disabled||(this.focusedDateData.month<11?this.focusedDateData.month+=1:(this.focusedDateData.month=0,this.focusedDateData.year+=1))},formatYYYYMMDD:function(e){var t=new Date(e);if(e&&!isNaN(t)){var i=t.getFullYear(),n=t.getMonth()+1,o=t.getDate();return i+"-"+(n<10?"0":"")+n+"-"+(o<10?"0":"")+o}return""},onChangeNativePicker:function(e){var t=e.target.value;this.dateSelected=t?new Date(t.replace(/-/g,"/")):null}}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(59),o=i.n(n),a=i(5),r=i.n(a);t.default={name:"BDropdown",props:{value:{type:[String,Number,Boolean,Object,Array,r.a,Function],default:null},disabled:Boolean,hoverable:Boolean,inline:Boolean,position:{type:String,validator:function(e){return["is-top-right","is-top-left","is-bottom-left"].indexOf(e)>-1}},mobileModal:{type:Boolean,default:!0}},data:function(){return{selected:this.value,isActive:!1,_isDropdown:!0}},computed:{rootClasses:function(){return[this.position,{"is-disabled":this.disabled,"is-hoverable":this.hoverable,"is-inline":this.inline,"is-active":this.isActive||this.inline,"is-mobile-modal":this.isMobileModal}]},isMobileModal:function(){return this.mobileModal&&!this.inline&&!this.hoverable}},watch:{value:function(e){this.selected=e},isActive:function(e){this.$emit("active-change",e)}},methods:{selectItem:function(e){this.selected!==e&&(this.$emit("change",e),this.selected=e),this.$emit("input",e),this.isActive=!1},isInWhiteList:function(e){if(e===this.$refs.dropdownMenu)return!0;if(e===this.$refs.trigger)return!0;if(void 0!==this.$refs.dropdownMenu){var t=this.$refs.dropdownMenu.querySelectorAll("*"),i=!0,n=!1,a=void 0;try{for(var r,s=o()(t);!(i=(r=s.next()).done);i=!0){if(e===r.value)return!0}}catch(e){n=!0,a=e}finally{try{!i&&s.return&&s.return()}finally{if(n)throw a}}}if(void 0!==this.$refs.trigger){var l=this.$refs.trigger.querySelectorAll("*"),c=!0,d=!1,u=void 0;try{for(var f,p=o()(l);!(c=(f=p.next()).done);c=!0){if(e===f.value)return!0}}catch(e){d=!0,u=e}finally{try{!c&&p.return&&p.return()}finally{if(d)throw u}}}return!1},clickedOutside:function(e){this.inline||this.isInWhiteList(e.target)||(this.isActive=!1)},toggle:function(){var e=this;this.disabled||this.hoverable||(this.isActive?this.isActive=!this.isActive:this.$nextTick(function(){e.isActive=!e.isActive}))}},created:function(){"undefined"!=typeof window&&document.addEventListener("click",this.clickedOutside)},beforeDestroy:function(){"undefined"!=typeof window&&document.removeEventListener("click",this.clickedOutside)}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"dropdown",class:e.rootClasses},[e.inline?e._e():i("div",{ref:"trigger",staticClass:"dropdown-trigger",attrs:{role:"button"},on:{click:e.toggle}},[e._t("trigger")],2),e._v(" "),i("transition",{attrs:{name:"fade"}},[e.isMobileModal?i("div",{directives:[{name:"show",rawName:"v-show",value:e.isActive,expression:"isActive"}],staticClass:"background"}):e._e()]),e._v(" "),i("transition",{attrs:{name:"fade"}},[i("div",{directives:[{name:"show",rawName:"v-show",value:!e.disabled&&(e.isActive||e.hoverable)||e.inline,expression:"(!disabled && (isActive || hoverable)) || inline"}],ref:"dropdownMenu",staticClass:"dropdown-menu"},[i("div",{staticClass:"dropdown-content"},[e._t("default")],2)])])],1)},staticRenderFns:[]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(5),o=i.n(n);t.default={name:"BDropdownItem",props:{value:{type:[String,Number,Boolean,Object,Array,o.a,Function],default:null},separator:Boolean,disabled:Boolean,custom:Boolean,paddingless:Boolean,hasLink:Boolean},computed:{anchorClasses:function(){return{"is-disabled":this.$parent.disabled||this.disabled,"is-paddingless":this.paddingless,"is-active":null!==this.value&&this.value===this.$parent.selected}},itemClasses:function(){return{"dropdown-item":!this.hasLink,"is-disabled":this.disabled,"is-paddingless":this.paddingless,"is-active":null!==this.value&&this.value===this.$parent.selected,"has-link":this.hasLink}},isClickable:function(){return!(this.$parent.disabled||this.separator||this.disabled||this.custom)}},methods:{selectItem:function(){this.isClickable&&(this.$parent.selectItem(this.value),this.$emit("click"))}},created:function(){if(!this.$parent.$data._isDropdown)throw this.$destroy(),new Error("You should wrap bDropdownItem on a bDropdown")}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return e.separator?i("hr",{staticClass:"dropdown-divider"}):e.custom||e.hasLink?i("div",{class:e.itemClasses,on:{click:e.selectItem}},[e._t("default")],2):i("a",{staticClass:"dropdown-item",class:e.anchorClasses,on:{click:e.selectItem}},[e._t("default")],2)},staticRenderFns:[]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1),o=i.n(n),a=i(123),r=i.n(a);t.default={name:"BField",components:o()({},r.a.name,r.a),props:{type:String,label:String,labelFor:String,message:[String,Array],grouped:Boolean,groupMultiline:Boolean,position:String,expanded:Boolean,horizontal:Boolean,addons:{type:Boolean,default:!0},customClass:String},data:function(){return{newType:this.type,newMessage:this.message,fieldLabelSize:null,_isField:!0}},computed:{rootClasses:function(){return[this.newPosition,{"is-expanded":this.expanded,"is-grouped-multiline":this.groupMultiline,"is-horizontal":this.horizontal}]},newPosition:function(){if(void 0!==this.position){var e=this.position.split("-");if(!(e.length<1)){var t=this.grouped?"is-grouped-":"has-addons-";return this.position?t+e[1]:void 0}}},formattedMessage:function(){return this.newMessage&&Array.isArray(this.newMessage)?this.newMessage.filter(function(e){if(e)return e}).join(" <br> "):this.newMessage}},watch:{type:function(e){this.newType=e},message:function(e){this.newMessage=e}},methods:{fieldType:function(){if(this.grouped)return"is-grouped";var e=0;return this.$slots.default&&(e=this.$slots.default.reduce(function(e,t){return t.tag?e+1:e},0)),e>1&&this.addons&&!this.horizontal?"has-addons":void 0}},mounted:function(){if(this.horizontal){this.$el.querySelectorAll(".input, .select, .button, .textarea").length>0&&(this.fieldLabelSize="is-normal")}}}},function(e,t,i){var n=i(0)(i(124),null,null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={name:"BFieldBody",props:{message:{type:[String]},type:{type:[String]}},render:function(e){var t=this;return e("div",{attrs:{class:"field-body"}},this.$slots.default.map(function(i){return i.tag?t.message?e("b-field",{attrs:{message:t.message,type:t.type}},[i]):e("b-field",{attrs:{type:t.type}},[i]):i}))}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"field",class:[e.rootClasses,e.fieldType()]},[e.horizontal?i("div",{staticClass:"field-label",class:[e.customClass,e.fieldLabelSize]},[e.label?i("label",{staticClass:"label",attrs:{for:e.labelFor}},[e._v("\n "+e._s(e.label)+"\n ")]):e._e()]):[e.label?i("label",{staticClass:"label",class:e.customClass,attrs:{for:e.labelFor}},[e._v("\n "+e._s(e.label)+"\n ")]):e._e()],e._v(" "),e.horizontal?i("b-field-body",{attrs:{message:e.newMessage?e.formattedMessage:"",type:e.newType}},[e._t("default")],2):[e._t("default")],e._v(" "),e.newMessage&&!e.horizontal?i("p",{staticClass:"help",class:e.newType,domProps:{innerHTML:e._s(e.formattedMessage)}}):e._e()],2)},staticRenderFns:[]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(5),o=i.n(n),a=i(1),r=i.n(a),s=i(3),l=i.n(s),c=i(12);t.default={name:"BSelect",components:r()({},l.a.name,l.a),mixins:[c.a],inheritAttrs:!1,props:{value:{type:[String,Number,Boolean,Object,Array,o.a,Function],default:null},placeholder:String,multiple:Boolean,nativeSize:[String,Number]},data:function(){return{selected:this.value,_isSelect:!0,_elementRef:"select"}},computed:{spanClasses:function(){return[this.size,this.statusType,{"is-fullwidth":this.expanded,"is-loading":this.loading,"is-multiple":this.multiple,"is-rounded":this.rounded,"is-empty":null===this.selected}]}},watch:{value:function(e){this.selected=e,!this.isValid&&this.checkHtml5Validity()},selected:function(e){this.$emit("input",e),!this.isValid&&this.checkHtml5Validity()}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"control",class:{"is-expanded":e.expanded,"has-icons-left":e.icon}},[i("span",{staticClass:"select",class:e.spanClasses},[i("select",e._b({directives:[{name:"model",rawName:"v-model",value:e.selected,expression:"selected"}],ref:"select",attrs:{multiple:e.multiple,size:e.nativeSize},on:{blur:function(t){e.$emit("blur",t)&&e.checkHtml5Validity()},focus:function(t){e.$emit("focus",t)},change:function(t){var i=Array.prototype.filter.call(t.target.options,function(e){return e.selected}).map(function(e){return"_value"in e?e._value:e.value});e.selected=t.target.multiple?i:i[0]}}},"select",e.$attrs,!1),[e.placeholder?i("option",{attrs:{selected:"",disabled:"",hidden:""},domProps:{value:null}},[e._v("\n "+e._s(e.placeholder)+"\n ")]):e._e(),e._v(" "),e._t("default")],2)]),e._v(" "),e.icon?i("b-icon",{staticClass:"is-left",attrs:{icon:e.icon,pack:e.iconPack,size:e.iconSize}}):e._e()],1)},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(129),i(133),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1),o=i.n(n),a=i(130),r=i.n(a);t.default={name:"BDatepickerTable",components:o()({},r.a.name,r.a),props:{value:Date,dayNames:Array,monthNames:Array,firstDayOfWeek:Number,events:Array,indicators:String,minDate:Date,maxDate:Date,focused:Object,disabled:Boolean,unselectableDates:Array,unselectableDaysOfWeek:Array,selectableDates:Array},computed:{visibleDayNames:function(){for(var e=[],t=this.firstDayOfWeek;e.length<this.dayNames.length;){var i=this.dayNames[t%this.dayNames.length];e.push(i),t++}return e},hasEvents:function(){return this.events&&this.events.length},eventsInThisMonth:function(){if(!this.events)return[];for(var e=[],t=0;t<this.events.length;t++){var i=this.events[t];i.hasOwnProperty("date")||(i={date:i}),i.hasOwnProperty("type")||(i.type="is-primary"),i.date.getMonth()===this.focused.month&&i.date.getFullYear()===this.focused.year&&e.push(i)}return e}},methods:{updateSelectedDate:function(e){this.$emit("input",e)},weekBuilder:function(e,t,i){for(var n=new Date(i,t),o=[],a=new Date(i,t,e).getDay(),r=a>=this.firstDayOfWeek?a-this.firstDayOfWeek:7-this.firstDayOfWeek+a,s=1,l=0;l<r;l++)o.unshift(new Date(n.getFullYear(),n.getMonth(),e-s)),s++;o.push(new Date(i,t,e));for(var c=1;o.length<7;)o.push(new Date(i,t,e+c)),c++;return o},weeksInThisMonth:function(e,t){for(var i=[],n=new Date(t,e+1,0).getDate(),o=1;o<=n+6;){var a=this.weekBuilder(o,e,t),r=!1;a.forEach(function(t){t.getMonth()===e&&(r=!0)}),r&&i.push(a),o+=7}return i},eventsInThisWeek:function(e,t){if(!this.eventsInThisMonth.length)return[];var i=[],n=[];n=this.weeksInThisMonth(this.focused.month,this.focused.year);for(var o=0;o<n[t].length;o++)for(var a=0;a<this.eventsInThisMonth.length;a++){var r=this.eventsInThisMonth[a].date.getTime();r===n[t][o].getTime()&&i.push(this.eventsInThisMonth[a])}return i}}}},function(e,t,i){var n=i(0)(i(131),i(132),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={name:"BDatepickerTableRow",props:{selectedDate:Date,week:{type:Array,required:!0},month:{type:Number,required:!0},minDate:Date,maxDate:Date,disabled:Boolean,unselectableDates:Array,unselectableDaysOfWeek:Array,selectableDates:Array,events:Array,indicators:String},methods:{selectableDate:function(e){var t=[];if(this.minDate&&t.push(e>=this.minDate),this.maxDate&&t.push(e<=this.maxDate),t.push(e.getMonth()===this.month),this.selectableDates)for(var i=0;i<this.selectableDates.length;i++){var n=this.selectableDates[i];if(e.getDate()===n.getDate()&&e.getFullYear()===n.getFullYear()&&e.getMonth()===n.getMonth())return!0;t.push(!1)}if(this.unselectableDates)for(var o=0;o<this.unselectableDates.length;o++){var a=this.unselectableDates[o];t.push(e.getDate()!==a.getDate()||e.getFullYear()!==a.getFullYear()||e.getMonth()!==a.getMonth())}if(this.unselectableDaysOfWeek)for(var r=0;r<this.unselectableDaysOfWeek.length;r++){var s=this.unselectableDaysOfWeek[r];t.push(e.getDay()!==s)}return t.indexOf(!1)<0},emitChosenDate:function(e){this.disabled||this.selectableDate(e)&&this.$emit("select",e)},eventsDateMatch:function(e){if(!this.events.length)return!1;for(var t=[],i=0;i<this.events.length;i++)this.events[i].date.getDay()===e.getDay()&&t.push(this.events[i]);return!!t.length&&t},classObject:function(e){function t(e,t){return!(!e||!t)&&(e.getDate()===t.getDate()&&e.getFullYear()===t.getFullYear()&&e.getMonth()===t.getMonth())}return{"is-selected":t(e,this.selectedDate),"is-today":t(e,new Date),"is-selectable":this.selectableDate(e)&&!this.disabled,"is-unselectable":!this.selectableDate(e)||this.disabled}}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"datepicker-row"},[e._l(e.week,function(t,n){return[e.selectableDate(t)&&!e.disabled?i("a",{key:n,staticClass:"datepicker-cell",class:[e.classObject(t),{"has-event":e.eventsDateMatch(t)},e.indicators],attrs:{role:"button",href:"#",disabled:e.disabled},on:{click:function(i){i.preventDefault(),e.emitChosenDate(t)},keydown:[function(i){if(!("button"in i)&&e._k(i.keyCode,"enter",13,i.key))return null;i.preventDefault(),e.emitChosenDate(t)},function(i){if(!("button"in i)&&e._k(i.keyCode,"space",32,i.key))return null;i.preventDefault(),e.emitChosenDate(t)}]}},[e._v("\n "+e._s(t.getDate())+"\n\n "),e.eventsDateMatch(t)?i("div",{staticClass:"events"},e._l(e.eventsDateMatch(t),function(e,t){return i("div",{key:t,staticClass:"event",class:e.type})})):e._e()]):i("div",{key:n,staticClass:"datepicker-cell",class:e.classObject(t)},[e._v("\n "+e._s(t.getDate())+"\n ")])]})],2)},staticRenderFns:[]}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("section",{staticClass:"datepicker-table"},[i("header",{staticClass:"datepicker-header"},e._l(e.visibleDayNames,function(t,n){return i("div",{key:n,staticClass:"datepicker-cell"},[e._v("\n "+e._s(t)+"\n ")])})),e._v(" "),i("div",{staticClass:"datepicker-body",class:{"has-events":e.hasEvents}},e._l(e.weeksInThisMonth(e.focused.month,e.focused.year),function(t,n){return i("b-datepicker-table-row",{key:n,attrs:{"selected-date":e.value,week:t,month:e.focused.month,"min-date":e.minDate,"max-date":e.maxDate,disabled:e.disabled,"unselectable-dates":e.unselectableDates,"unselectable-days-of-week":e.unselectableDaysOfWeek,"selectable-dates":e.selectableDates,events:e.eventsInThisWeek(t,n),indicators:e.indicators},on:{select:e.updateSelectedDate}})}))])},staticRenderFns:[]}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"datepicker control",class:[e.size,{"is-expanded":e.expanded}]},[!e.isMobile||e.inline?i("b-dropdown",{ref:"dropdown",attrs:{position:e.position,disabled:e.disabled,inline:e.inline}},[e.inline?e._e():i("b-input",e._b({ref:"input",attrs:{slot:"trigger",autocomplete:"off",value:e.formatValue(e.dateSelected),placeholder:e.placeholder,size:e.size,icon:e.icon,"icon-pack":e.iconPack,rounded:e.rounded,loading:e.loading,disabled:e.disabled,readonly:e.readonly},on:{focus:function(t){e.$emit("focus",t)},blur:function(t){e.$emit("blur",t)&&e.checkHtml5Validity()}},nativeOn:{change:function(t){e.onChange(t.target.value)}},slot:"trigger"},"b-input",e.$attrs,!1)),e._v(" "),i("b-dropdown-item",{attrs:{disabled:e.disabled,custom:""}},[i("header",{staticClass:"datepicker-header"},[void 0!==e.$slots.header&&e.$slots.header.length?[e._t("header")]:i("div",{staticClass:"pagination field is-centered"},[e.isFirstMonth||e.disabled?e._e():i("a",{staticClass:"pagination-previous",attrs:{role:"button",href:"#",disabled:e.disabled},on:{click:function(t){t.preventDefault(),e.decrementMonth(t)},keydown:[function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key))return null;t.preventDefault(),e.decrementMonth(t)},function(t){if(!("button"in t)&&e._k(t.keyCode,"space",32,t.key))return null;t.preventDefault(),e.decrementMonth(t)}]}},[i("b-icon",{attrs:{icon:"chevron-left",pack:e.iconPack,both:"",type:"is-primary is-clickable"}})],1),e._v(" "),i("a",{directives:[{name:"show",rawName:"v-show",value:!e.isLastMonth&&!e.disabled,expression:"!isLastMonth && !disabled"}],staticClass:"pagination-next",attrs:{role:"button",href:"#",disabled:e.disabled},on:{click:function(t){t.preventDefault(),e.incrementMonth(t)},keydown:[function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key))return null;t.preventDefault(),e.incrementMonth(t)},function(t){if(!("button"in t)&&e._k(t.keyCode,"space",32,t.key))return null;t.preventDefault(),e.incrementMonth(t)}]}},[i("b-icon",{attrs:{icon:"chevron-right",pack:e.iconPack,both:"",type:"is-primary is-clickable"}})],1),e._v(" "),i("div",{staticClass:"pagination-list"},[i("b-field",[i("b-select",{attrs:{disabled:e.disabled},model:{value:e.focusedDateData.month,callback:function(t){e.$set(e.focusedDateData,"month",t)},expression:"focusedDateData.month"}},e._l(e.monthNames,function(t,n){return i("option",{key:t,domProps:{value:n}},[e._v("\n "+e._s(t)+"\n ")])})),e._v(" "),i("b-select",{attrs:{disabled:e.disabled},model:{value:e.focusedDateData.year,callback:function(t){e.$set(e.focusedDateData,"year",t)},expression:"focusedDateData.year"}},e._l(e.listOfYears,function(t){return i("option",{key:t,domProps:{value:t}},[e._v("\n "+e._s(t)+"\n ")])}))],1)],1)])],2),e._v(" "),i("b-datepicker-table",{attrs:{"day-names":e.dayNames,"month-names":e.monthNames,"first-day-of-week":e.firstDayOfWeek,"min-date":e.minDate,"max-date":e.maxDate,focused:e.focusedDateData,disabled:e.disabled,"unselectable-dates":e.unselectableDates,"unselectable-days-of-week":e.unselectableDaysOfWeek,"selectable-dates":e.selectableDates,events:e.events,indicators:e.indicators},on:{close:function(t){e.$refs.dropdown.isActive=!1}},model:{value:e.dateSelected,callback:function(t){e.dateSelected=t},expression:"dateSelected"}}),e._v(" "),void 0!==e.$slots.default&&e.$slots.default.length?i("footer",{staticClass:"datepicker-footer"},[e._t("default")],2):e._e()],1)],1):i("b-input",e._b({ref:"input",attrs:{type:"date",autocomplete:"off",value:e.formatYYYYMMDD(e.value),placeholder:e.placeholder,size:e.size,icon:e.icon,"icon-pack":e.iconPack,loading:e.loading,max:e.formatYYYYMMDD(e.maxDate),min:e.formatYYYYMMDD(e.minDate),disabled:e.disabled,readonly:!1},on:{focus:function(t){e.$emit("focus",t)},blur:function(t){e.$emit("blur",t)&&e.checkHtml5Validity()}},nativeOn:{change:function(t){e.onChangeNativePicker(t)}}},"b-input",e.$attrs,!1))],1)},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(136),i(139),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1),o=i.n(n),a=i(3),r=i.n(a),s=i(62),l=i.n(s),c=i(2),d=i(6),u=i(13);t.default={name:"BDialog",components:o()({},r.a.name,r.a),extends:l.a,mixins:[u.a],props:{title:String,message:String,icon:String,hasIcon:Boolean,type:{type:String,default:"is-primary"},size:String,confirmText:{type:String,default:function(){return c.a.defaultDialogConfirmText?c.a.defaultDialogConfirmText:"OK"}},cancelText:{type:String,default:function(){return c.a.defaultDialogCancelText?c.a.defaultDialogCancelText:"Cancel"}},hasInput:Boolean,inputAttrs:{type:Object,default:function(){return{}}},onConfirm:{type:Function,default:function(){}},focusOn:{type:String,default:"confirm"}},data:function(){return{prompt:this.hasInput?this.inputAttrs.value||"":"",isActive:!1,validationMessage:""}},computed:{iconByType:function(){switch(this.type){case"is-info":return"information";case"is-success":return"check-circle";case"is-warning":return"alert";case"is-danger":return"alert-circle";default:return null}},showCancel:function(){return this.cancelOptions.indexOf("button")>=0}},methods:{confirm:function(){var e=this;if(void 0!==this.$refs.input&&!this.$refs.input.checkValidity())return this.validationMessage=this.$refs.input.validationMessage,void this.$nextTick(function(){return e.$refs.input.select()});this.onConfirm(this.prompt),this.close()},close:function(){var e=this;this.isActive=!1,setTimeout(function(){e.$destroy(),Object(d.e)(e.$el)},150)}},beforeMount:function(){document.body.appendChild(this.$el)},mounted:function(){var e=this;this.isActive=!0,void 0===this.inputAttrs.required&&this.$set(this.inputAttrs,"required",!0),this.$nextTick(function(){e.hasInput?e.$refs.input.focus():"cancel"===e.focusOn&&e.showCancel?e.$refs.cancelButton.focus():e.$refs.confirmButton.focus()})}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(6),o=i(2);t.default={name:"BModal",props:{active:Boolean,component:[Object,Function],content:String,programmatic:Boolean,props:Object,events:Object,width:{type:[String,Number],default:960},hasModalCard:Boolean,animation:{type:String,default:"zoom-out"},canCancel:{type:[Array,Boolean],default:function(){return["escape","x","outside","button"]}},onCancel:{type:Function,default:function(){}},scroll:{type:String,default:function(){return o.a.defaultModalScroll?o.a.defaultModalScroll:"clip"},validator:function(e){return["clip","keep"].indexOf(e)>=0}}},data:function(){return{isActive:this.active||!1,savedScrollTop:null,newWidth:"number"==typeof this.width?this.width+"px":this.width}},computed:{cancelOptions:function(){return"boolean"==typeof this.canCancel?this.canCancel?["escape","x","outside","button"]:[]:this.canCancel},showX:function(){return this.cancelOptions.indexOf("x")>=0}},watch:{active:function(e){this.isActive=e},isActive:function(){this.handleScroll()}},methods:{handleScroll:function(){if("undefined"!=typeof window){if("clip"===this.scroll)return void(this.isActive?document.documentElement.classList.add("is-clipped"):document.documentElement.classList.remove("is-clipped"));if(this.savedScrollTop=this.savedScrollTop?this.savedScrollTop:document.documentElement.scrollTop,this.isActive?document.body.classList.add("is-noscroll"):document.body.classList.remove("is-noscroll"),this.isActive)return void(document.body.style.top="-"+this.savedScrollTop+"px");document.documentElement.scrollTop=this.savedScrollTop,document.body.style.top=null,this.savedScrollTop=null}},cancel:function(e){this.cancelOptions.indexOf(e)<0||(this.onCancel.apply(null,arguments),this.close())},close:function(){var e=this;this.$emit("close"),this.$emit("update:active",!1),this.programmatic&&(this.isActive=!1,setTimeout(function(){e.$destroy(),Object(n.e)(e.$el)},150))},keyPress:function(e){this.isActive&&27===e.keyCode&&this.cancel("escape")}},created:function(){"undefined"!=typeof window&&document.addEventListener("keyup",this.keyPress)},beforeMount:function(){this.programmatic&&document.body.appendChild(this.$el)},mounted:function(){this.programmatic?this.isActive=!0:this.isActive&&this.handleScroll()},beforeDestroy:function(){if("undefined"!=typeof window){document.removeEventListener("keyup",this.keyPress),document.documentElement.classList.remove("is-clipped");var e=this.savedScrollTop?this.savedScrollTop:document.documentElement.scrollTop;document.body.classList.remove("is-noscroll"),document.documentElement.scrollTop=e,document.body.style.top=null}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("transition",{attrs:{name:e.animation}},[e.isActive?i("div",{staticClass:"modal is-active"},[i("div",{staticClass:"modal-background",on:{click:function(t){e.cancel("outside")}}}),e._v(" "),i("div",{staticClass:"animation-content",class:{"modal-content":!e.hasModalCard},style:{maxWidth:e.newWidth}},[e.component?i(e.component,e._g(e._b({tag:"component",on:{close:e.close}},"component",e.props,!1),e.events)):e.content?i("div",{domProps:{innerHTML:e._s(e.content)}}):e._t("default")],2),e._v(" "),e.showX?i("button",{staticClass:"modal-close is-large",attrs:{type:"button"},on:{click:function(t){e.cancel("x")}}}):e._e()]):e._e()])},staticRenderFns:[]}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("transition",{attrs:{name:e.animation}},[e.isActive?i("div",{staticClass:"dialog modal is-active",class:e.size},[i("div",{staticClass:"modal-background",on:{click:function(t){e.cancel("outside")}}}),e._v(" "),i("div",{staticClass:"modal-card animation-content"},[e.title?i("header",{staticClass:"modal-card-head"},[i("p",{staticClass:"modal-card-title"},[e._v(e._s(e.title))])]):e._e(),e._v(" "),i("section",{staticClass:"modal-card-body",class:{"is-titleless":!e.title,"is-flex":e.hasIcon}},[i("div",{staticClass:"media"},[e.hasIcon?i("div",{staticClass:"media-left"},[i("b-icon",{attrs:{icon:e.icon?e.icon:e.iconByType,pack:e.iconPack,type:e.type,both:!e.icon,size:"is-large"}})],1):e._e(),e._v(" "),i("div",{staticClass:"media-content"},[i("p",{domProps:{innerHTML:e._s(e.message)}}),e._v(" "),e.hasInput?i("div",{staticClass:"field"},[i("div",{staticClass:"control"},[i("input",e._b({directives:[{name:"model",rawName:"v-model",value:e.prompt,expression:"prompt"}],ref:"input",staticClass:"input",class:{"is-danger":e.validationMessage},domProps:{value:e.prompt},on:{keyup:function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key))return null;e.confirm(t)},input:function(t){t.target.composing||(e.prompt=t.target.value)}}},"input",e.inputAttrs,!1))]),e._v(" "),i("p",{staticClass:"help is-danger"},[e._v(e._s(e.validationMessage))])]):e._e()])])]),e._v(" "),i("footer",{staticClass:"modal-card-foot"},[e.showCancel?i("button",{ref:"cancelButton",staticClass:"button",on:{click:function(t){e.cancel("button")}}},[e._v("\n "+e._s(e.cancelText)+"\n ")]):e._e(),e._v(" "),i("button",{ref:"confirmButton",staticClass:"button",class:e.type,on:{click:e.confirm}},[e._v("\n "+e._s(e.confirmText)+"\n ")])])])]):e._e()])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(141),i(142),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(6),o="undefined"==typeof window,a=o?Object:window.HTMLElement;t.default={name:"BLoading",props:{active:Boolean,programmatic:Boolean,container:[Object,Function,a],isFullPage:{type:Boolean,default:!0},animation:{type:String,default:"fade"},canCancel:{type:Boolean,default:!1},onCancel:{type:Function,default:function(){}}},data:function(){return{isActive:this.active||!1}},watch:{active:function(e){this.isActive=e}},methods:{cancel:function(){this.canCancel&&this.isActive&&this.close()},close:function(){var e=this;this.onCancel.apply(null,arguments),this.$emit("close"),this.$emit("update:active",!1),this.programmatic&&(this.isActive=!1,setTimeout(function(){e.$destroy(),Object(n.e)(e.$el)},150))},keyPress:function(e){27===e.keyCode&&this.cancel()}},created:function(){"undefined"!=typeof window&&document.addEventListener("keyup",this.keyPress)},beforeMount:function(){this.programmatic&&(this.container?(this.isFullPage=!1,this.container.appendChild(this.$el)):document.body.appendChild(this.$el))},mounted:function(){this.programmatic&&(this.isActive=!0)},beforeDestroy:function(){"undefined"!=typeof window&&document.removeEventListener("keyup",this.keyPress)}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("transition",{attrs:{name:e.animation}},[e.isActive?i("div",{staticClass:"loading-overlay is-active",class:{"is-full-page":e.isFullPage}},[i("div",{staticClass:"loading-background",on:{click:e.cancel}}),e._v(" "),i("div",{staticClass:"loading-icon"})]):e._e()])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(144),i(145),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(63);t.default={name:"BMessage",mixins:[n.a],data:function(){return{newIconSize:this.iconSize||this.size||"is-large"}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("transition",{attrs:{name:"fade"}},[e.isActive?i("article",{staticClass:"message",class:[e.type,e.size]},[e.title?i("header",{staticClass:"message-header"},[i("p",[e._v(e._s(e.title))]),e._v(" "),e.closable?i("button",{staticClass:"delete",attrs:{type:"button"},on:{click:e.close}}):e._e()]):e._e(),e._v(" "),i("section",{staticClass:"message-body"},[i("div",{staticClass:"media"},[e.icon&&e.hasIcon?i("div",{staticClass:"media-left"},[i("b-icon",{class:e.type,attrs:{icon:e.icon,"icon-pack":e.iconPack,both:"",size:e.newIconSize}})],1):e._e(),e._v(" "),i("div",{staticClass:"media-content"},[e._t("default")],2)])])]):e._e()])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(147),i(148),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(63);t.default={name:"BNotification",mixins:[n.a]}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("transition",{attrs:{name:"fade"}},[e.isActive?i("article",{staticClass:"notification",class:e.type},[e.closable?i("button",{staticClass:"delete",attrs:{type:"button"},on:{click:e.close}}):e._e(),e._v(" "),i("div",{staticClass:"media"},[e.icon&&e.hasIcon?i("div",{staticClass:"media-left"},[i("b-icon",{attrs:{icon:e.icon,"icon-pack":e.iconPack,both:"",size:"is-large"}})],1):e._e(),e._v(" "),i("div",{staticClass:"media-content"},[e._t("default")],2)])]):e._e()])},staticRenderFns:[]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1),o=i.n(n),a=i(3),r=i.n(a),s=i(13);t.default={name:"BPagination",components:o()({},r.a.name,r.a),mixins:[s.a],props:{total:[Number,String],perPage:{type:[Number,String],default:20},current:{type:[Number,String],default:1},size:String,simple:Boolean,rounded:Boolean,order:String},computed:{rootClasses:function(){return[this.order,this.size,{"is-simple":this.simple,"is-rounded":this.rounded}]},pageCount:function(){return Math.ceil(this.total/this.perPage)},firstItem:function(){var e=this.current*this.perPage-this.perPage+1;return e>=0?e:0},hasPrev:function(){return this.current>1},hasFirst:function(){return this.current>=3},hasFirstEllipsis:function(){return this.current>=4},hasLast:function(){return this.current<=this.pageCount-2},hasLastEllipsis:function(){return this.current<this.pageCount-2&&this.current<=this.pageCount-3},hasNext:function(){return this.current<this.pageCount},pagesInRange:function(){var e=this;if(!this.simple){for(var t=Math.max(1,this.current-1),i=Math.min(this.current+1,this.pageCount),n=[],o=t;o<=i;o++)!function(t){n.push({number:t,isCurrent:e.current===t,click:function(i){e.current!==t&&(e.$emit("change",t),e.$emit("update:current",t),e.$nextTick(function(){return i.target.focus()}))}})}(o);return n}}},watch:{pageCount:function(e){this.current>e&&this.last()}},methods:{prev:function(){this.hasPrev&&(this.$emit("change",this.current-1),this.$emit("update:current",this.current-1))},first:function(){this.$emit("change",1),this.$emit("update:current",1)},last:function(){this.$emit("change",this.pageCount),this.$emit("update:current",this.pageCount)},next:function(){this.hasNext&&(this.$emit("change",this.current+1),this.$emit("update:current",this.current+1))}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"pagination",class:e.rootClasses},[i("a",{staticClass:"pagination-previous",attrs:{role:"button",href:"#",disabled:!e.hasPrev},on:{click:function(t){t.preventDefault(),e.prev(t)}}},[i("b-icon",{attrs:{icon:"chevron-left","icon-pack":e.iconPack,both:""}})],1),e._v(" "),i("a",{staticClass:"pagination-next",attrs:{role:"button",href:"#",disabled:!e.hasNext},on:{click:function(t){t.preventDefault(),e.next(t)}}},[i("b-icon",{attrs:{icon:"chevron-right","icon-pack":e.iconPack,both:""}})],1),e._v(" "),e.simple?e._e():i("ul",{staticClass:"pagination-list"},[e.hasFirst?i("li",[i("a",{staticClass:"pagination-link",attrs:{role:"button",href:"#"},on:{click:function(t){t.preventDefault(),e.first(t)}}},[e._v("\n 1\n ")])]):e._e(),e._v(" "),e.hasFirstEllipsis?i("li",[i("span",{staticClass:"pagination-ellipsis"},[e._v("…")])]):e._e(),e._v(" "),e._l(e.pagesInRange,function(t){return i("li",{key:t.number},[i("a",{staticClass:"pagination-link",class:{"is-current":t.isCurrent},attrs:{role:"button",href:"#"},on:{click:function(e){e.preventDefault(),t.click(e)}}},[e._v("\n "+e._s(t.number)+"\n ")])])}),e._v(" "),e.hasLastEllipsis?i("li",[i("span",{staticClass:"pagination-ellipsis"},[e._v("…")])]):e._e(),e._v(" "),e.hasLast?i("li",[i("a",{staticClass:"pagination-link",attrs:{role:"button",href:"#"},on:{click:function(t){t.preventDefault(),e.last(t)}}},[e._v("\n "+e._s(e.pageCount)+"\n ")])]):e._e()],2),e._v(" "),e.simple?i("small",{staticClass:"info"},[1==e.perPage?[e._v("\n "+e._s(e.firstItem)+" / "+e._s(e.total)+"\n ")]:[e._v("\n "+e._s(e.firstItem)+"-"+e._s(Math.min(e.current*e.perPage,e.total))+" / "+e._s(e.total)+"\n ")]],2):e._e()])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(152),i(153),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1),o=i.n(n),a=i(3),r=i.n(a),s=i(13);t.default={name:"BPanel",components:o()({},r.a.name,r.a),mixins:[s.a],props:{collapsible:{type:Boolean,default:!1},open:{type:Boolean,default:!0},hasCustomTemplate:{type:Boolean,default:!1},header:String,content:String,animation:{type:String,default:"fade"}},data:function(){return{isOpen:this.open}},watch:{open:function(e){this.isOpen=e}},methods:{toggle:function(){this.collapsible&&(this.isOpen=!this.isOpen,this.$emit("update:open",this.isOpen),this.isOpen?this.$emit("open"):this.$emit("close"))}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("nav",{staticClass:"panel"},[i("div",{staticClass:"panel-heading",class:{"is-collapsible":e.collapsible},on:{click:e.toggle}},[e.header?i("span",{domProps:{innerHTML:e._s(e.header)}}):e._t("header"),e._v(" "),e.collapsible?i("b-icon",{staticClass:"is-pulled-right",attrs:{both:"",icon:e.isOpen?"menu-up":"menu-down","icon-pack":e.iconPack}}):e._e()],2),e._v(" "),i("transition",{attrs:{name:e.animation}},[i("div",{directives:[{name:"show",rawName:"v-show",value:e.isOpen,expression:"isOpen"}],staticClass:"panel-content",class:{"panel-block":!e.hasCustomTemplate}},[e.content?i("div",{domProps:{innerHTML:e._s(e.content)}}):e._t("default")],2)])],1)},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(155),i(156),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(5),o=i.n(n);t.default={name:"BRadio",props:{value:[String,Number,Boolean,Function,Object,Array,o.a],nativeValue:[String,Number,Boolean,Function,Object,Array,o.a],type:String,disabled:Boolean,required:Boolean,name:String,size:String},data:function(){return{newValue:this.value}},watch:{value:function(e){this.newValue=e},newValue:function(e){e===this.nativeValue&&this.$emit("input",e)}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("label",{ref:"label",staticClass:"b-radio radio",class:[e.size,{"is-disabled":e.disabled}],attrs:{disabled:e.disabled,tabindex:!e.disabled&&0},on:{keydown:function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key)&&e._k(t.keyCode,"space",32,t.key))return null;t.preventDefault(),e.$refs.label.click()}}},[i("input",{directives:[{name:"model",rawName:"v-model",value:e.newValue,expression:"newValue"}],attrs:{type:"radio",disabled:e.disabled,required:e.required,name:e.name},domProps:{value:e.nativeValue,checked:e._q(e.newValue,e.nativeValue)},on:{change:function(t){e.newValue=e.nativeValue}}}),e._v(" "),i("span",{staticClass:"check",class:e.type}),e._v(" "),i("span",{staticClass:"control-label"},[e._t("default")],2)])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(158),i(159),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(5),o=i.n(n);t.default={name:"BRadioButton",props:{value:[String,Number,Boolean,Function,Object,Array,o.a],nativeValue:[String,Number,Boolean,Function,Object,Array,o.a],type:{type:String,default:"is-primary"},disabled:Boolean,name:String,size:String},data:function(){return{newValue:this.value}},watch:{value:function(e){this.newValue=e},newValue:function(e){e===this.nativeValue&&this.$emit("input",e)}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"control"},[i("label",{ref:"label",staticClass:"b-radio radio button",class:[e.newValue===e.nativeValue?e.type:null,e.size],attrs:{disabled:e.disabled,tabindex:!e.disabled&&0},on:{keydown:function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key)&&e._k(t.keyCode,"space",32,t.key))return null;t.preventDefault(),e.$refs.label.click()}}},[e._t("default"),e._v(" "),i("input",{directives:[{name:"model",rawName:"v-model",value:e.newValue,expression:"newValue"}],attrs:{type:"radio",disabled:e.disabled,name:e.name},domProps:{value:e.nativeValue,checked:e._q(e.newValue,e.nativeValue)},on:{change:function(t){e.newValue=e.nativeValue}}})],2)])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(161),i(162),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(2),o=i(65);t.default={name:"BSnackbar",mixins:[o.a],props:{actionText:{type:String,default:"OK"},onAction:{type:Function,default:function(){}},indefinite:{type:Boolean,default:!1}},data:function(){return{newDuration:this.duration||n.a.defaultSnackbarDuration}},methods:{action:function(){this.onAction(),this.close()}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("transition",{attrs:{"enter-active-class":e.transition.enter,"leave-active-class":e.transition.leave}},[i("div",{directives:[{name:"show",rawName:"v-show",value:e.isActive,expression:"isActive"}],staticClass:"snackbar",class:[e.type,e.position]},[i("p",{staticClass:"text"},[e._v(e._s(e.message))]),e._v(" "),e.actionText?i("div",{staticClass:"action",class:e.type,on:{click:e.action}},[i("button",{staticClass:"button is-dark"},[e._v(e._s(e.actionText))])]):e._e()])])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(164),i(165),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(5),o=i.n(n);t.default={name:"BSwitch",props:{value:[String,Number,Boolean,Function,Object,Array,o.a],nativeValue:[String,Number,Boolean,Function,Object,Array,o.a],disabled:Boolean,type:String,name:String,size:String,trueValue:{type:[String,Number,Boolean,Function,Object,Array,o.a],default:!0},falseValue:{type:[String,Number,Boolean,Function,Object,Array,o.a],default:!1}},data:function(){return{newValue:this.value,isMouseDown:!1}},watch:{value:function(e){this.newValue=e},newValue:function(e){this.$emit("input",e)}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("label",{ref:"label",staticClass:"switch",class:[e.size,{"is-disabled":e.disabled}],attrs:{disabled:e.disabled,tabindex:!e.disabled&&0},on:{keydown:function(t){if(!("button"in t)&&e._k(t.keyCode,"enter",13,t.key)&&e._k(t.keyCode,"space",32,t.key))return null;t.preventDefault(),e.$refs.label.click()},mousedown:function(t){e.isMouseDown=!0},mouseup:function(t){e.isMouseDown=!1},mouseout:function(t){e.isMouseDown=!1},blur:function(t){e.isMouseDown=!1}}},[i("input",{directives:[{name:"model",rawName:"v-model",value:e.newValue,expression:"newValue"}],attrs:{type:"checkbox",disabled:e.disabled,name:e.name,"true-value":e.trueValue,"false-value":e.falseValue},domProps:{value:e.nativeValue,checked:Array.isArray(e.newValue)?e._i(e.newValue,e.nativeValue)>-1:e._q(e.newValue,e.trueValue)},on:{click:function(e){e.stopPropagation()},change:function(t){var i=e.newValue,n=t.target,o=n.checked?e.trueValue:e.falseValue;if(Array.isArray(i)){var a=e.nativeValue,r=e._i(i,a);n.checked?r<0&&(e.newValue=i.concat([a])):r>-1&&(e.newValue=i.slice(0,r).concat(i.slice(r+1)))}else e.newValue=o}}}),e._v(" "),i("span",{staticClass:"check",class:[{"is-elastic":e.isMouseDown&&!e.disabled},e.type]}),e._v(" "),i("span",{staticClass:"control-label"},[e._t("default")],2)])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(167),i(181),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=i(168),a=i.n(o),r=i(1),s=i.n(r),l=i(6),c=i(61),d=i.n(c),u=i(3),f=i.n(u),p=i(64),h=i.n(p),b=i(176),m=i.n(b),g=i(66),v=i.n(g),k=i(13);t.default={name:"BTable",components:(n={},s()(n,d.a.name,d.a),s()(n,f.a.name,f.a),s()(n,h.a.name,h.a),s()(n,m.a.name,m.a),s()(n,v.a.name,v.a),n),mixins:[k.a],props:{data:{type:Array,default:function(){return[]}},columns:{type:Array,default:function(){return[]}},bordered:Boolean,striped:Boolean,narrowed:Boolean,hoverable:Boolean,loading:Boolean,detailed:Boolean,checkable:Boolean,selected:Object,focusable:Boolean,customIsChecked:Function,isRowCheckable:{type:Function,default:function(){return!0}},checkedRows:{type:Array,default:function(){return[]}},mobileCards:{type:Boolean,default:!0},defaultSort:[String,Array],defaultSortDirection:{type:String,default:"asc"},paginated:Boolean,currentPage:{type:Number,default:1},perPage:{type:[Number,String],default:20},paginationSimple:Boolean,paginationSize:String,backendSorting:Boolean,rowClass:{type:Function,default:function(){return""}},openedDetailed:{type:Array,default:function(){return[]}},hasDetailedVisible:{type:Function,default:function(){return!0}},detailKey:{type:String,default:""},backendPagination:Boolean,total:{type:[Number,String],default:0}},data:function(){return{getValueByPath:l.b,newColumns:[].concat(a()(this.columns)),visibleDetailRows:this.openedDetailed,newData:this.data,newDataTotal:this.backendPagination?this.total:this.data.length,newCheckedRows:[].concat(a()(this.checkedRows)),newCurrentPage:this.currentPage,currentSortColumn:{},isAsc:!0,firstTimeSort:!0,_isTable:!0}},computed:{tableClasses:function(){return{"is-bordered":this.bordered,"is-striped":this.striped,"is-narrow":this.narrowed,"has-mobile-cards":this.mobileCards,"is-hoverable":(this.hoverable||this.focusable)&&this.visibleData.length}},visibleData:function(){if(!this.paginated)return this.newData;var e=this.newCurrentPage,t=this.perPage;if(this.newData.length<=t)return this.newData;var i=(e-1)*t,n=parseInt(i,10)+parseInt(t,10);return this.newData.slice(i,n)},isAllChecked:function(){var e=this,t=this.visibleData.filter(function(t){return e.isRowCheckable(t)});return 0!==t.length&&!t.some(function(t){return Object(l.c)(e.newCheckedRows,t,e.customIsChecked)<0})},isAllUncheckable:function(){var e=this;return 0===this.visibleData.filter(function(t){return e.isRowCheckable(t)}).length},hasSortablenewColumns:function(){return this.newColumns.some(function(e){return e.sortable})},columnCount:function(){var e=this.newColumns.length;return e+=this.checkable?1:0,e+=this.detailed?1:0}},watch:{data:function(e){var t=this,i=this.newColumns;this.newColumns=[],this.newData=e,this.$nextTick(function(){t.newColumns.length||(t.newColumns=i)}),this.backendSorting||this.sort(this.currentSortColumn,!0),this.backendPagination||(this.newDataTotal=e.length)},total:function(e){this.backendPagination&&(this.newDataTotal=e)},checkedRows:function(e){this.newCheckedRows=[].concat(a()(e))},columns:function(e){this.newColumns=[].concat(a()(e))},newColumns:function(e){if(e.length&&this.firstTimeSort)this.initSort(),this.firstTimeSort=!1;else if(e.length&&this.currentSortColumn.field)for(var t=0;t<e.length;t++)if(e[t].field===this.currentSortColumn.field){this.currentSortColumn=e[t];break}},openedDetailed:function(e){this.visibleDetailRows=e},currentPage:function(e){this.newCurrentPage=e}},methods:{sortBy:function(e,t,i,n){return i&&"function"==typeof i?[].concat(a()(e)).sort(function(e,t){return i(e,t,n)}):[].concat(a()(e)).sort(function(e,i){var o=Object(l.b)(e,t),a=Object(l.b)(i,t);return o||0===o?a||0===a?o===a?0:(o="string"==typeof o?o.toUpperCase():o,a="string"==typeof a?a.toUpperCase():a,n?o>a?1:-1:o>a?-1:1):-1:1})},sort:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];e&&e.sortable&&(t||(this.isAsc=e===this.currentSortColumn?!this.isAsc:"desc"!==this.defaultSortDirection.toLowerCase()),this.firstTimeSort||this.$emit("sort",e.field,this.isAsc?"asc":"desc"),this.backendSorting||(this.newData=this.sortBy(this.newData,e.field,e.customSort,this.isAsc)),this.currentSortColumn=e)},isRowChecked:function(e){return Object(l.c)(this.newCheckedRows,e,this.customIsChecked)>=0},removeCheckedRow:function(e){var t=Object(l.c)(this.newCheckedRows,e,this.customIsChecked);t>=0&&this.newCheckedRows.splice(t,1)},checkAll:function(){var e=this,t=this.isAllChecked;this.visibleData.forEach(function(i){e.removeCheckedRow(i),t||e.isRowCheckable(i)&&e.newCheckedRows.push(i)}),this.$emit("check",this.newCheckedRows),this.$emit("check-all",this.newCheckedRows),this.$emit("update:checkedRows",this.newCheckedRows)},checkRow:function(e){this.isRowChecked(e)?this.removeCheckedRow(e):this.newCheckedRows.push(e),this.$emit("check",this.newCheckedRows,e),this.$emit("update:checkedRows",this.newCheckedRows)},selectRow:function(e,t){this.$emit("click",e),this.selected!==e&&(this.$emit("select",e,this.selected),this.$emit("update:selected",e))},pageChanged:function(e){this.newCurrentPage=e>0?e:1,this.$emit("page-change",this.newCurrentPage),this.$emit("update:currentPage",this.newCurrentPage)},toggleDetails:function(e){this.isVisibleDetailRow(e)?(this.closeDetailRow(e),this.$emit("details-close",e)):(this.openDetailRow(e),this.$emit("details-open",e)),this.$emit("update:openedDetailed",this.visibleDetailRows)},openDetailRow:function(e){var t=this.handleDetailKey(e);this.visibleDetailRows.push(t)},closeDetailRow:function(e){var t=this.handleDetailKey(e),i=this.visibleDetailRows.indexOf(t);this.visibleDetailRows.splice(i,1)},isVisibleDetailRow:function(e){var t=this.handleDetailKey(e);return this.visibleDetailRows.indexOf(t)>=0},handleDetailKey:function(e){var t=this.detailKey;return t.length?e[t]:e},checkPredefinedDetailedRows:function(){if(this.openedDetailed.length>0&&!this.detailKey.length)throw new Error('If you set a predefined opened-detailed, you must provide an unique key using the prop "detail-key"')},hasCustomFooterSlot:function(){if(this.$slots.footer.length>1)return!0;var e=this.$slots.footer[0].tag;return"th"===e||"td"===e},hasBottomLeftSlot:function(){return void 0!==this.$slots["bottom-left"]},pressedArrow:function(e){if(this.visibleData.length){var t=this.visibleData.indexOf(this.selected)+e;t=t<0?0:t>this.visibleData.length-1?this.visibleData.length-1:t,this.selectRow(this.visibleData[t])}},focus:function(){this.focusable&&this.$el.querySelector("table").focus()},initSort:function(){var e=this;if(this.defaultSort){var t="",i=this.defaultSortDirection;Array.isArray(this.defaultSort)?(t=this.defaultSort[0],this.defaultSort[1]&&(i=this.defaultSort[1])):t=this.defaultSort,this.newColumns.forEach(function(n){n.field===t&&(e.isAsc="desc"!==i.toLowerCase(),e.sort(n,!0))})}}},mounted:function(){this.checkPredefinedDetailedRows()}}},function(e,t,i){"use strict";t.__esModule=!0;var n=i(169),o=function(e){return e&&e.__esModule?e:{default:e}}(n);t.default=function(e){if(Array.isArray(e)){for(var t=0,i=Array(e.length);t<e.length;t++)i[t]=e[t];return i}return(0,o.default)(e)}},function(e,t,i){e.exports={default:i(170),__esModule:!0}},function(e,t,i){i(38),i(171),e.exports=i(8).Array.from},function(e,t,i){"use strict";var n=i(46),o=i(18),a=i(37),r=i(172),s=i(173),l=i(51),c=i(174),d=i(60);o(o.S+o.F*!i(175)(function(e){Array.from(e)}),"Array",{from:function(e){var t,i,o,u,f=a(e),p="function"==typeof this?this:Array,h=arguments.length,b=h>1?arguments[1]:void 0,m=void 0!==b,g=0,v=d(f);if(m&&(b=n(b,h>2?arguments[2]:void 0,2)),void 0==v||p==Array&&s(v))for(t=l(f.length),i=new p(t);t>g;g++)c(i,g,m?b(f[g],g):f[g]);else for(u=v.call(f),i=new p;!(o=u.next()).done;g++)c(i,g,m?r(u,b,[o.value,g],!0):o.value);return i.length=g,i}})},function(e,t,i){var n=i(16);e.exports=function(e,t,i,o){try{return o?t(n(i)[0],i[1]):t(i)}catch(t){var a=e.return;throw void 0!==a&&n(a.call(e)),t}}},function(e,t,i){var n=i(22),o=i(4)("iterator"),a=Array.prototype;e.exports=function(e){return void 0!==e&&(n.Array===e||a[o]===e)}},function(e,t,i){"use strict";var n=i(9),o=i(21);e.exports=function(e,t,i){t in e?n.f(e,t,o(0,i)):e[t]=i}},function(e,t,i){var n=i(4)("iterator"),o=!1;try{var a=[7][n]();a.return=function(){o=!0},Array.from(a,function(){throw 2})}catch(e){}e.exports=function(e,t){if(!t&&!o)return!1;var i=!1;try{var a=[7],r=a[n]();r.next=function(){return{done:i=!0}},a[n]=function(){return r},e(a)}catch(e){}return i}},function(e,t,i){var n=i(0)(i(177),i(178),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=i(1),a=i.n(o),r=i(28),s=i.n(r),l=i(3),c=i.n(l);t.default={name:"BTableMobileSort",components:(n={},a()(n,s.a.name,s.a),a()(n,c.a.name,c.a),n),props:{currentSortColumn:Object,isAsc:Boolean,columns:Array},data:function(){return{mobileSort:this.currentSortColumn}},watch:{mobileSort:function(e){this.currentSortColumn!==e&&this.$emit("sort",e)},currentSortColumn:function(e){this.mobileSort=e}},methods:{sort:function(){this.$emit("sort",this.mobileSort)}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"field table-mobile-sort"},[i("div",{staticClass:"field has-addons"},[i("b-select",{attrs:{expanded:""},model:{value:e.mobileSort,callback:function(t){e.mobileSort=t},expression:"mobileSort"}},e._l(e.columns,function(t,n){return t.sortable?i("option",{key:n,domProps:{value:t}},[e._v("\n "+e._s(t.label)+"\n ")]):e._e()})),e._v(" "),i("div",{staticClass:"control"},[i("button",{staticClass:"button is-primary",on:{click:e.sort}},[i("b-icon",{directives:[{name:"show",rawName:"v-show",value:e.currentSortColumn===e.mobileSort,expression:"currentSortColumn === mobileSort"}],class:{"is-desc":!e.isAsc},attrs:{icon:"arrow-up",size:"is-small",both:""}})],1)])],1)])},staticRenderFns:[]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(5),o=i.n(n);t.default={name:"BTableColumn",props:{label:String,customKey:[String,Number],field:String,meta:[String,Number,Boolean,Function,Object,Array,o.a],width:[Number,String],numeric:Boolean,centered:Boolean,sortable:Boolean,visible:{type:Boolean,default:!0},customSort:Function,internal:Boolean},data:function(){return{newKey:this.customKey||this.label}},computed:{rootClasses:function(){return{"has-text-right":this.numeric&&!this.centered,"has-text-centered":this.centered}}},beforeMount:function(){var e=this;if(!this.$parent.$data._isTable)throw this.$destroy(),new Error("You should wrap bTableColumn on a bTable");if(!this.internal){!this.$parent.columns.some(function(t){return t.newKey===e.newKey})&&this.$parent.columns.push(this)}},beforeDestroy:function(){var e=this.$parent.columns.map(function(e){return e.newKey}).indexOf(this.newKey);e>=0&&this.$parent.columns.splice(e,1)}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return e.visible?i("td",{class:e.rootClasses,attrs:{"data-label":e.label}},[i("span",[e._t("default")],2)]):e._e()},staticRenderFns:[]}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"b-table",class:{"is-loading":e.loading}},[e.mobileCards&&e.hasSortablenewColumns?i("b-table-mobile-sort",{attrs:{"current-sort-column":e.currentSortColumn,"is-asc":e.isAsc,columns:e.newColumns},on:{sort:function(t){return e.sort(t)}}}):e._e(),e._v(" "),i("div",{staticClass:"table-wrapper"},[i("table",{staticClass:"table",class:e.tableClasses,attrs:{tabindex:!!e.focusable&&0},on:{keydown:[function(t){if(!("button"in t)&&e._k(t.keyCode,"up",38,t.key))return null;t.preventDefault(),e.pressedArrow(-1)},function(t){if(!("button"in t)&&e._k(t.keyCode,"down",40,t.key))return null;t.preventDefault(),e.pressedArrow(1)}]}},[e.newColumns.length?i("thead",[i("tr",[e.detailed?i("th",{attrs:{width:"40px"}}):e._e(),e._v(" "),e.checkable?i("th",{staticClass:"checkbox-cell"},[i("b-checkbox",{attrs:{value:e.isAllChecked,disabled:e.isAllUncheckable},nativeOn:{change:function(t){e.checkAll(t)}}})],1):e._e(),e._v(" "),e._l(e.newColumns,function(t,n){return t.visible||void 0===t.visible?i("th",{key:n,class:{"is-current-sort":e.currentSortColumn===t,"is-sortable":t.sortable},style:{width:t.width+"px"},on:{click:function(i){i.stopPropagation(),e.sort(t)}}},[i("div",{staticClass:"th-wrap",class:{"is-numeric":t.numeric,"is-centered":t.centered}},[e.$scopedSlots.header?e._t("header",null,{column:t,index:n}):[e._v(e._s(t.label))],e._v(" "),i("b-icon",{directives:[{name:"show",rawName:"v-show",value:e.currentSortColumn===t,expression:"currentSortColumn === column"}],class:{"is-desc":!e.isAsc},attrs:{icon:"arrow-up","icon-pack":e.iconPack,both:"",size:"is-small"}})],2)]):e._e()})],2)]):e._e(),e._v(" "),e.visibleData.length?i("tbody",[e._l(e.visibleData,function(t,n){return[i("tr",{key:n,class:[e.rowClass(t,n),{"is-selected":t===e.selected,"is-checked":e.isRowChecked(t)}],on:{click:function(i){e.selectRow(t)},dblclick:function(i){e.$emit("dblclick",t)}}},[e.detailed?i("td",{staticClass:"chevron-cell"},[e.hasDetailedVisible(t)?i("a",{attrs:{role:"button"},on:{click:function(i){i.stopPropagation(),e.toggleDetails(t)}}},[i("b-icon",{class:{"is-expanded":e.isVisibleDetailRow(t)},attrs:{icon:"chevron-right","icon-pack":e.iconPack,both:""}})],1):e._e()]):e._e(),e._v(" "),e.checkable?i("td",{staticClass:"checkbox-cell"},[i("b-checkbox",{attrs:{disabled:!e.isRowCheckable(t),value:e.isRowChecked(t)},nativeOn:{change:function(i){e.checkRow(t)}}})],1):e._e(),e._v(" "),e.$scopedSlots.default?e._t("default",null,{row:t,index:n}):e._l(e.newColumns,function(n){return i("BTableColumn",e._b({key:n.field,attrs:{internal:""}},"BTableColumn",n,!1),[n.renderHtml?i("span",{domProps:{innerHTML:e._s(e.getValueByPath(t,n.field))}}):[e._v("\n "+e._s(e.getValueByPath(t,n.field))+"\n ")]],2)})],2),e._v(" "),e.detailed&&e.isVisibleDetailRow(t)?i("tr",{staticClass:"detail"},[i("td",{attrs:{colspan:e.columnCount}},[i("div",{staticClass:"detail-container"},[e._t("detail",null,{row:t,index:n})],2)])]):e._e()]})],2):i("tbody",[i("tr",{staticClass:"is-empty"},[i("td",{attrs:{colspan:e.columnCount}},[e._t("empty")],2)])]),e._v(" "),void 0!==e.$slots.footer?i("tfoot",[i("tr",{staticClass:"table-footer"},[e.hasCustomFooterSlot()?e._t("footer"):i("th",{attrs:{colspan:e.columnCount}},[e._t("footer")],2)],2)]):e._e()])]),e._v(" "),e.checkable&&e.hasBottomLeftSlot()||e.paginated?i("div",{staticClass:"level"},[i("div",{staticClass:"level-left"},[e._t("bottom-left")],2),e._v(" "),i("div",{staticClass:"level-right"},[e.paginated?i("div",{staticClass:"level-item"},[i("b-pagination",{attrs:{"icon-pack":e.iconPack,total:e.newDataTotal,"per-page":e.perPage,simple:e.paginationSimple,size:e.paginationSize,current:e.newCurrentPage},on:{change:e.pageChanged}})],1):e._e()])]):e._e()],1)},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(183),i(184),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=i(1),a=i.n(o),r=i(3),s=i.n(r),l={name:"BSlotComponent",props:{component:{type:Object,required:!0},name:{type:String,default:"default"},tag:{type:String,default:"div"},event:{type:String,default:"hook:updated"}},methods:{refresh:function(){this.$forceUpdate()},isVueComponent:function(){return this.component&&this.component._isVue}},created:function(){this.isVueComponent()&&this.component.$on(this.event,this.refresh)},beforeDestroy:function(){this.isVueComponent()&&this.component.$off(this.event,this.refresh)},render:function(e){if(this.isVueComponent()){var t=this.component.$slots[this.name];return e(this.tag,{},t)}}};t.default={name:"BTabs",components:(n={},a()(n,s.a.name,s.a),a()(n,l.name,l),n),props:{value:[String,Number],expanded:Boolean,type:String,size:String,position:String,animated:{type:Boolean,default:!0}},data:function(){return{activeTab:this.value||0,tabItems:[],contentHeight:0,_isTabs:!0}},computed:{navClasses:function(){return[this.type,this.size,this.position,{"is-fullwidth":this.expanded,"is-toggle-rounded is-toggle":"is-toggle-rounded"===this.type}]}},watch:{value:function(e){this.changeTab(e)},tabItems:function(){this.tabItems.length&&(this.tabItems[this.activeTab].isActive=!0)}},methods:{changeTab:function(e){this.activeTab!==e&&(this.tabItems[this.activeTab].deactivate(this.activeTab,e),this.tabItems[e].activate(this.activeTab,e),this.activeTab=e,this.$emit("change",e))},tabClick:function(e){this.$emit("input",e),this.changeTab(e)}},mounted:function(){this.tabItems.length&&(this.tabItems[this.activeTab].isActive=!0)}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"b-tabs",class:{"is-fullwidth":e.expanded}},[i("nav",{staticClass:"tabs",class:e.navClasses},[i("ul",e._l(e.tabItems,function(t,n){return i("li",{directives:[{name:"show",rawName:"v-show",value:t.visible,expression:"tabItem.visible"}],key:n,class:{"is-active":e.activeTab===n,"is-disabled":t.disabled}},[i("a",{on:{click:function(t){e.tabClick(n)}}},[t.$slots.header?[i("b-slot-component",{attrs:{component:t,name:"header",tag:"span"}})]:[t.icon?i("b-icon",{attrs:{icon:t.icon,pack:t.iconPack,size:e.size}}):e._e(),e._v(" "),i("span",[e._v(e._s(t.label))])]],2)])}))]),e._v(" "),i("section",{staticClass:"tab-content"},[e._t("default")],2)])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(186),i(187),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(13);t.default={name:"BTabItem",mixins:[n.a],props:{label:String,icon:String,disabled:Boolean,visible:{type:Boolean,default:!0}},data:function(){return{isActive:!1,transitionName:null}},methods:{activate:function(e,t){this.$parent.animated?this.transitionName=t<e?"slide-next":"slide-prev":this.transitionName=null,this.isActive=!0},deactivate:function(e,t){this.$parent.animated?this.transitionName=t<e?"slide-next":"slide-prev":this.transitionName=null,this.isActive=!1}},created:function(){if(!this.$parent.$data._isTabs)throw this.$destroy(),new Error("You should wrap bTabItem on a bTabs");this.$parent.tabItems.push(this)},beforeDestroy:function(){var e=this.$parent.tabItems.indexOf(this);e>=0&&this.$parent.tabItems.splice(e,1)}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("transition",{attrs:{name:e.transitionName}},[i("div",{directives:[{name:"show",rawName:"v-show",value:e.isActive&&e.visible,expression:"isActive && visible"}],staticClass:"tab-item"},[e._t("default")],2)])},staticRenderFns:[]}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={name:"BTag",props:{attached:Boolean,closable:Boolean,type:String,size:String,rounded:Boolean,disabled:Boolean,ellipsis:Boolean,tabstop:{type:Boolean,default:!0}},methods:{close:function(){this.disabled||this.$emit("close")}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return e.attached&&e.closable?i("div",{staticClass:"tags has-addons"},[i("span",{staticClass:"tag",class:[e.type,e.size,{"is-rounded":e.rounded}]},[i("span",{class:{"has-ellipsis":e.ellipsis}},[e._t("default")],2)]),e._v(" "),i("a",{staticClass:"tag is-delete",class:[e.size,{"is-rounded":e.rounded}],attrs:{role:"button",tabindex:!!e.tabstop&&0,disabled:e.disabled},on:{click:function(t){e.close()},keyup:function(t){if(!("button"in t)&&e._k(t.keyCode,"delete",[8,46],t.key))return null;t.preventDefault(),e.close()}}})]):i("span",{staticClass:"tag",class:[e.type,e.size,{"is-rounded":e.rounded}]},[i("span",{class:{"has-ellipsis":e.ellipsis}},[e._t("default")],2),e._v(" "),e.closable?i("a",{staticClass:"delete is-small",attrs:{role:"button",disabled:e.disabled,tabindex:!!e.tabstop&&0},on:{click:function(t){e.close()},keyup:function(t){if(!("button"in t)&&e._k(t.keyCode,"delete",[8,46],t.key))return null;t.preventDefault(),e.close()}}}):e._e()])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(191),i(192),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={name:"BTaglist",props:{attached:Boolean}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement;return(e._self._c||t)("div",{staticClass:"tags",class:{"has-addons":e.attached}},[e._t("default")],2)},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(194),i(195),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=i(53),a=i.n(o),r=i(1),s=i.n(r),l=i(6),c=i(67),d=i.n(c),u=i(52),f=i.n(u),p=i(12);t.default={name:"BTaginput",components:(n={},s()(n,f.a.name,f.a),s()(n,d.a.name,d.a),n),mixins:[p.a],inheritAttrs:!1,props:{value:{type:Array,default:function(){return[]}},data:{type:Array,default:function(){return[]}},type:String,rounded:{type:Boolean,default:!1},attached:{type:Boolean,default:!1},maxtags:{type:[Number,String],required:!1},field:{type:String,default:"value"},autocomplete:Boolean,disabled:Boolean,ellipsis:Boolean,confirmKeyCodes:{type:Array,default:function(){return[13,188]}},removeOnKeys:{type:Array,default:function(){return[8]}},allowNew:Boolean,onPasteSeparators:{type:Array,default:function(){return[","]}},beforeAdding:{type:Function,default:function(){return!0}},allowDuplicates:{type:Boolean,default:!1}},data:function(){return{tags:this.value||[],newTag:"",_elementRef:"input",_isTaginput:!0}},computed:{rootClasses:function(){return{"is-expanded":this.expanded}},containerClasses:function(){return{"is-focused":this.isFocused,"is-focusable":this.hasInput}},valueLength:function(){return this.newTag.trim().length},defaultSlotName:function(){return this.hasDefaultSlot?"default":"dontrender"},emptySlotName:function(){return this.hasEmptySlot?"empty":"dontrender"},hasDefaultSlot:function(){return!!this.$scopedSlots.default},hasEmptySlot:function(){return!!this.$slots.empty},hasInput:function(){return null==this.maxtags||this.tagsLength<this.maxtags},tagsLength:function(){return this.tags.length},separatorsAsRegExp:function(){var e=this.onPasteSeparators;return e.length?new RegExp(e.map(function(e){return e?e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"):null}).join("|"),"g"):null}},watch:{value:function(e){this.tags=e},newTag:function(e){this.$emit("typing",e.trim())},hasInput:function(){this.hasInput||this.onBlur()}},methods:{addTag:function(e){var t=e||this.newTag.trim();if(t){if(!this.autocomplete){var i=this.separatorsAsRegExp;if(i&&t.match(i))return void t.split(i).map(function(e){return e.trim()}).filter(function(e){return 0!==e.length}).map(this.addTag)}(!!this.allowDuplicates||-1===this.tags.indexOf(t))&&this.beforeAdding(t)&&(this.tags.push(t),this.$emit("input",this.tags),this.$emit("add",t))}this.newTag=""},getNormalizedTagText:function(e){return"object"===(void 0===e?"undefined":a()(e))?Object(l.b)(e,this.field):e},customOnBlur:function(e){this.autocomplete||this.addTag(),this.onBlur(e)},onSelect:function(e){var t=this;e&&(this.addTag(e),this.$nextTick(function(){t.newTag=""}))},removeTag:function(e){var t=this.tags.splice(e,1)[0];return this.$emit("input",this.tags),this.$emit("remove",t),t},removeLastTag:function(){this.tagsLength>0&&this.removeTag(this.tagsLength-1)},keydown:function(e){-1===this.removeOnKeys.indexOf(e.keyCode)||this.newTag.length||this.removeLastTag(),this.autocomplete&&!this.allowNew||this.confirmKeyCodes.indexOf(e.keyCode)>=0&&(e.preventDefault(),this.addTag())}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"taginput control",class:e.rootClasses},[i("div",{staticClass:"taginput-container",class:[e.statusType,e.size,e.containerClasses],attrs:{disabled:e.disabled},on:{click:function(t){e.hasInput&&e.focus(t)}}},[e._l(e.tags,function(t,n){return i("b-tag",{key:n,attrs:{type:e.type,size:e.size,rounded:e.rounded,attached:e.attached,tabstop:!1,disabled:e.disabled,ellipsis:e.ellipsis,closable:""},on:{close:function(t){e.removeTag(n)}}},[e._v("\n "+e._s(e.getNormalizedTagText(t))+"\n ")])}),e._v(" "),e.hasInput?i("b-autocomplete",e._b({ref:"autocomplete",attrs:{data:e.data,field:e.field,icon:e.icon,"icon-pack":e.iconPack,maxlength:e.maxlength,"has-counter":!1,size:e.size,disabled:e.disabled,loading:e.loading,"keep-first":""},on:{focus:e.onFocus,blur:e.customOnBlur,select:e.onSelect},nativeOn:{keydown:function(t){e.keydown(t)}},scopedSlots:e._u([{key:e.defaultSlotName,fn:function(t){return[e._t("default",null,{option:t.option,index:t.index})]}}]),model:{value:e.newTag,callback:function(t){e.newTag=t},expression:"newTag"}},"b-autocomplete",e.$attrs,!1),[i("template",{slot:e.emptySlotName},[e._t("empty")],2)],2):e._e()],2),e._v(" "),e.maxtags||e.maxlength?i("p",{staticClass:"help counter"},[e.maxlength&&e.valueLength>0?[e._v("\n "+e._s(e.valueLength)+" / "+e._s(e.maxlength)+"\n ")]:e.maxtags?[e._v("\n "+e._s(e.tagsLength)+" / "+e._s(e.maxtags)+"\n ")]:e._e()],2):e._e()])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(197),i(198),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=i(1),a=i.n(o),r=i(12),s=i(6),l=i(2),c=i(43),d=i.n(c),u=i(44),f=i.n(u),p=i(27),h=i.n(p),b=i(45),m=i.n(b),g=i(28),v=i.n(g),k=i(3),x=i.n(k),w="AM",y="PM",_=function(e){return(e<10?"0":"")+e},C=function(e,t){var i=e.getHours(),n=e.getMinutes(),o=!1;return"12"===t.hourFormat&&(o=i<12,i>12?i-=12:0===i&&(i=12)),_(i)+":"+_(n)+("12"===t.hourFormat?" "+(o?w:y):"")},S=function(e,t){if(e){var i=e,n=!1;if("12"===t.hourFormat){var o=e.split(" ");i=o[0],n=o[1]===w}var a=i.split(":"),r=parseInt(a[0],10),s=parseInt(a[1],10);if(isNaN(r)||r<0||r>23||"12"===t.hourFormat&&(r<1||r>12)||isNaN(s)||s<0||s>59)return null;var l=null;return t.dateSelected&&!isNaN(t.dateSelected)?l=new Date(t.dateSelected):(l=new Date,l.setMilliseconds(0),l.setSeconds(0)),l.setMinutes(s),"12"===t.hourFormat&&(n&&12===r?r=0:n||12===r||(r+=12)),l.setHours(r),l}return null};t.default={name:"BTimepicker",components:(n={},a()(n,h.a.name,h.a),a()(n,m.a.name,m.a),a()(n,v.a.name,v.a),a()(n,x.a.name,x.a),a()(n,d.a.name,d.a),a()(n,f.a.name,f.a),n),mixins:[r.a],inheritAttrs:!1,props:{value:Date,inline:Boolean,minTime:Date,maxTime:Date,placeholder:String,readonly:{type:Boolean,default:!0},disabled:{type:Boolean,default:!1},hourFormat:{type:String,default:"24",validator:function(e){return"24"===e||"12"===e}},incrementMinutes:{type:Number,default:1},timeFormatter:{type:Function,default:function(e,t){return"function"==typeof l.a.defaultTimeFormatter?l.a.defaultTimeFormatter(e):C(e,t)}},timeParser:{type:Function,default:function(e,t){return"function"==typeof l.a.defaultTimeParser?l.a.defaultTimeParser(e):S(e,t)}},mobileNative:{type:Boolean,default:function(){return l.a.defaultTimepickerMobileNative}},position:String,unselectableTimes:Array},data:function(){return{dateSelected:this.value,hoursSelected:null,minutesSelected:null,meridienSelected:null,_elementRef:"input",_isTimepicker:!0}},computed:{hours:function(){for(var e=[],t=this.isHourFormat24?24:12,i=0;i<t;i++){var n=i,o=n;this.isHourFormat24||(n=i+1,o=n,this.meridienSelected===w?12===n&&(n=0):this.meridienSelected===y&&12!==n&&(n+=12)),e.push({label:_(o),value:n})}return e},minutes:function(){for(var e=[],t=0;t<60;t+=this.incrementMinutes)e.push({label:_(t),value:t});return e},meridiens:function(){return[w,y]},isMobile:function(){return this.mobileNative&&s.d.any()},isHourFormat24:function(){return"24"===this.hourFormat}},watch:{hourFormat:function(e){null!==this.hoursSelected&&(this.meridienSelected=this.hoursSelected>=12?y:w)},dateSelected:function(e){this.$emit("input",e)},value:function(e){this.updateInternalState(e),this.dateSelected=e,!this.isValid&&this.$refs.input.checkHtml5Validity()}},methods:{onMeridienChange:function(e){null!==this.hoursSelected&&(e===y?0===this.hoursSelected?this.hoursSelected=12:this.hoursSelected+=12:e===w&&(12===this.hoursSelected?this.hoursSelected=0:this.hoursSelected-=12)),this.updateDateSelected(this.hoursSelected,this.minutesSelected,e)},onHoursChange:function(e){this.updateDateSelected(parseInt(e,10),this.minutesSelected,this.meridienSelected)},onMinutesChange:function(e){this.updateDateSelected(this.hoursSelected,parseInt(e,10),this.meridienSelected)},updateDateSelected:function(e,t,i){null!=e&&null!=t&&(!this.isHourFormat24&&null!==i||this.isHourFormat24)&&(this.dateSelected&&!isNaN(this.dateSelected)?this.dateSelected=new Date(this.dateSelected):(this.dateSelected=new Date,this.dateSelected.setMilliseconds(0),this.dateSelected.setSeconds(0)),this.dateSelected.setHours(e),this.dateSelected.setMinutes(t))},updateInternalState:function(e){e?(this.hoursSelected=e.getHours(),this.minutesSelected=e.getMinutes(),this.meridienSelected=e.getHours()>=12?y:w):(this.hoursSelected=null,this.minutesSelected=null,this.meridienSelected=w)},isHourDisabled:function(e){var t=this,i=!1;if(this.minTime){var n=this.minTime.getHours();i=e<n}if(this.maxTime&&!i){var o=this.maxTime.getHours();i=e>o}if(this.unselectableTimes&&!i)if(null!==this.minutesSelected){var a=this.unselectableTimes.filter(function(i){return i.getHours()===e&&i.getMinutes()===t.minutesSelected});i=a.length>0}else{var r=this.unselectableTimes.filter(function(t){return t.getHours()===e});i=r.length===this.minutes.length}return i},isMinuteDisabled:function(e){var t=this,i=!1;if(null!==this.hoursSelected){if(this.isHourDisabled(this.hoursSelected))i=!0;else{if(this.minTime){var n=this.minTime.getHours(),o=this.minTime.getMinutes();i=this.hoursSelected===n&&e<o}if(this.maxTime&&!i){var a=this.maxTime.getHours(),r=this.maxTime.getMinutes();i=this.hoursSelected===a&&e>r}}if(this.unselectableTimes&&!i){i=this.unselectableTimes.filter(function(i){return i.getHours()===t.hoursSelected&&i.getMinutes()===e}).length>0}}return i},onChange:function(e){var t=this.timeParser(e,this);this.updateInternalState(t),t&&!isNaN(t)?this.dateSelected=t:(this.dateSelected=null,this.$refs.input.newValue=this.dateSelected)},formatValue:function(e){return e&&!isNaN(e)?this.timeFormatter(e,this):null},close:function(){this.$refs.dropdown&&(this.$refs.dropdown.isActive=!1)},formatHHMMSS:function(e){var t=new Date(e);if(e&&!isNaN(t)){var i=t.getHours(),n=t.getMinutes();return _(i)+":"+_(n)+":00"}return""},onChangeNativePicker:function(e){var t=e.target.value;if(t){this.dateSelected&&!isNaN(this.dateSelected)?this.dateSelected=new Date(this.dateSelected):(this.dateSelected=new Date,this.dateSelected.setMilliseconds(0),this.dateSelected.setSeconds(0));var i=t.split(":");this.dateSelected.setHours(parseInt(i[0],10)),this.dateSelected.setMinutes(parseInt(i[1],10))}else this.dateSelected=null}},mounted:function(){this.updateInternalState(this.value)}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"timepicker control",class:[e.size,{"is-expanded":e.expanded}]},[!e.isMobile||e.inline?i("b-dropdown",{ref:"dropdown",attrs:{position:e.position,disabled:e.disabled,inline:e.inline}},[e.inline?e._e():i("b-input",e._b({ref:"input",attrs:{slot:"trigger",autocomplete:"off",value:e.formatValue(e.dateSelected),placeholder:e.placeholder,size:e.size,icon:e.icon,"icon-pack":e.iconPack,loading:e.loading,disabled:e.disabled,readonly:e.readonly,rounded:e.rounded},on:{focus:function(t){e.$emit("focus",t)},blur:function(t){e.$emit("blur",t)&&e.checkHtml5Validity()}},nativeOn:{change:function(t){e.onChange(t.target.value)}},slot:"trigger"},"b-input",e.$attrs,!1)),e._v(" "),i("b-dropdown-item",{attrs:{disabled:e.disabled,custom:""}},[i("div",{staticClass:"pagination-list"},[i("b-field",[i("b-select",{attrs:{disabled:e.disabled,placeholder:"00"},nativeOn:{change:function(t){e.onHoursChange(t.target.value)}},model:{value:e.hoursSelected,callback:function(t){e.hoursSelected=t},expression:"hoursSelected"}},e._l(e.hours,function(t){return i("option",{key:t.value,attrs:{disabled:e.isHourDisabled(t.value)},domProps:{value:t.value}},[e._v("\n "+e._s(t.label)+"\n ")])})),e._v(" "),i("b-select",{attrs:{disabled:e.disabled,placeholder:"00"},nativeOn:{change:function(t){e.onMinutesChange(t.target.value)}},model:{value:e.minutesSelected,callback:function(t){e.minutesSelected=t},expression:"minutesSelected"}},e._l(e.minutes,function(t){return i("option",{key:t.value,attrs:{disabled:e.isMinuteDisabled(t.value)},domProps:{value:t.value}},[e._v("\n "+e._s(t.label)+"\n ")])})),e._v(" "),e.isHourFormat24?e._e():i("b-select",{attrs:{disabled:e.disabled},nativeOn:{change:function(t){e.onMeridienChange(t.target.value)}},model:{value:e.meridienSelected,callback:function(t){e.meridienSelected=t},expression:"meridienSelected"}},e._l(e.meridiens,function(t){return i("option",{key:t,domProps:{value:t}},[e._v("\n "+e._s(t)+"\n ")])}))],1)],1),e._v(" "),void 0!==e.$slots.default&&e.$slots.default.length?i("footer",{staticClass:"timepicker-footer"},[e._t("default")],2):e._e()])],1):i("b-input",e._b({ref:"input",attrs:{type:"time",autocomplete:"off",value:e.formatHHMMSS(e.value),placeholder:e.placeholder,size:e.size,icon:e.icon,"icon-pack":e.iconPack,loading:e.loading,max:e.formatHHMMSS(e.maxTime),min:e.formatHHMMSS(e.minTime),disabled:e.disabled,readonly:!1},on:{focus:function(t){e.$emit("focus",t)},blur:function(t){e.$emit("blur",t)&&e.checkHtml5Validity()}},nativeOn:{change:function(t){e.onChangeNativePicker(t)}}},"b-input",e.$attrs,!1))],1)},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(200),i(201),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(2),o=i(65);t.default={name:"BToast",mixins:[o.a],data:function(){return{newDuration:this.duration||n.a.defaultToastDuration}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("transition",{attrs:{"enter-active-class":e.transition.enter,"leave-active-class":e.transition.leave}},[i("div",{directives:[{name:"show",rawName:"v-show",value:e.isActive,expression:"isActive"}],staticClass:"toast",class:[e.type,e.position]},[i("div",{domProps:{innerHTML:e._s(e.message)}})])])},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(203),i(204),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(2);t.default={name:"BTooltip",props:{active:{type:Boolean,default:!0},type:String,label:String,position:{type:String,default:"is-top",validator:function(e){return["is-top","is-bottom","is-left","is-right"].indexOf(e)>-1}},always:Boolean,animated:Boolean,square:Boolean,dashed:Boolean,multilined:Boolean,size:{type:String,default:"is-medium"}},computed:{newType:function(){return this.type||n.a.defaultTooltipType},newAnimated:function(){return this.animated||n.a.defaultTooltipAnimated}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement;return(e._self._c||t)("span",{class:[e.newType,e.position,e.size,{tooltip:e.active,"is-square":e.square,"is-animated":e.newAnimated,"is-always":e.always,"is-multiline":e.multilined,"is-dashed":e.dashed}],attrs:{"data-label":e.label}},[e._t("default")],2)},staticRenderFns:[]}},function(e,t,i){var n=i(0)(i(206),i(207),null,null,null);e.exports=n.exports},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(12);t.default={name:"BUpload",mixins:[n.a],inheritAttrs:!1,props:{value:{type:Array,default:function(){return[]}},multiple:Boolean,disabled:Boolean,accept:String,dragDrop:Boolean,type:{type:String,default:"is-primary"},native:{type:Boolean,default:!1}},data:function(){return{newValue:this.value||[],dragDropFocus:!1,_elementRef:"input"}},watch:{value:function(e){this.newValue=e,this.newValue&&0!==this.newValue.length||(this.$refs.input.value=null),!this.isValid&&!this.dragDrop&&this.checkHtml5Validity()}},methods:{onFileChange:function(e){if(!this.disabled&&!this.loading){this.dragDrop&&this.updateDragDropFocus(!1);var t=e.target.files||e.dataTransfer.files;if(t&&t.length){if(this.multiple)this.native&&(this.newValue=[]);else if(this.dragDrop){if(1!==t.length)return!1;this.newValue=[]}else this.newValue=[];for(var i=0;i<t.length;i++){var n=t[i];this.checkType(n)&&this.newValue.push(n)}}this.$emit("input",this.newValue),!this.dragDrop&&this.checkHtml5Validity()}},updateDragDropFocus:function(e){this.disabled||this.loading||(this.dragDropFocus=e)},checkType:function(e){if(!this.accept)return!0;var t=this.accept.split(",");if(0===t.length)return!0;for(var i=!1,n=0;n<t.length&&!i;n++){var o=t[n].trim();if(o)if("."===o.substring(0,1)){var a=e.name.lastIndexOf(".");a>=0&&e.name.substring(a)===o&&(i=!0)}else e.type.match(o)&&(i=!0)}return i}}}},function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("label",{staticClass:"upload control"},[e.dragDrop?i("div",{staticClass:"upload-draggable",class:[e.type,{"is-loading":e.loading,"is-disabled":e.disabled,"is-hovered":e.dragDropFocus}],on:{dragover:function(t){t.preventDefault(),e.updateDragDropFocus(!0)},dragleave:function(t){t.preventDefault(),e.updateDragDropFocus(!1)},dragenter:function(t){t.preventDefault(),e.updateDragDropFocus(!0)},drop:function(t){t.preventDefault(),e.onFileChange(t)}}},[e._t("default")],2):[e._t("default")],e._v(" "),i("input",e._b({ref:"input",attrs:{type:"file",multiple:e.multiple,accept:e.accept,disabled:e.disabled},on:{change:e.onFileChange}},"input",e.$attrs,!1))],2)},staticRenderFns:[]}}])})},function(e,t,i){var n=i(22);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("6e4843aa",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"/*! Buefy v0.6.7 | MIT License | github.com/buefy/buefy */ \n/*! bulma.io v0.7.1 | MIT License | github.com/jgthms/bulma */@keyframes spinAround{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.b-checkbox.checkbox,.b-radio.radio,.breadcrumb,.button,.delete,.file,.icon,.is-unselectable,.modal-close,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.switch,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link:after,.select:not(.is-multiple):not(.is-loading):after{border:3px solid transparent;border-radius:2px;border-right:0;border-top:0;content:\" \";display:block;height:.625em;margin-top:-.4375em;pointer-events:none;position:absolute;top:50%;transform:rotate(-45deg);transform-origin:center;width:.625em}.block:not(:last-child),.box:not(:last-child),.breadcrumb:not(:last-child),.content:not(:last-child),.highlight:not(:last-child),.level:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.progress:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.tabs:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.delete,.modal-close{-moz-appearance:none;-webkit-appearance:none;background-color:hsla(0,0%,4%,.2);border:none;border-radius:290486px;cursor:pointer;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:none;position:relative;vertical-align:top;width:20px}.delete:after,.delete:before,.modal-close:after,.modal-close:before{background-color:#fff;content:\"\";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.delete:before,.modal-close:before{height:2px;width:50%}.delete:after,.modal-close:after{height:50%;width:2px}.delete:focus,.delete:hover,.modal-close:focus,.modal-close:hover{background-color:hsla(0,0%,4%,.3)}.delete:active,.modal-close:active{background-color:hsla(0,0%,4%,.4)}.is-small.delete,.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.delete,.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.delete,.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.b-table.is-loading:after,.button.is-loading:after,.control.is-loading:after,.loader,.loading-overlay .loading-icon:after,.select.is-loading:after,.upload .upload-draggable.is-loading:after{animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:\"\";display:block;height:1em;position:relative;width:1em}.dropdown .background,.hero-video,.image.is-1by1 img,.image.is-1by2 img,.image.is-1by3 img,.image.is-2by1 img,.image.is-2by3 img,.image.is-3by1 img,.image.is-3by2 img,.image.is-3by4 img,.image.is-3by5 img,.image.is-4by3 img,.image.is-4by5 img,.image.is-5by3 img,.image.is-5by4 img,.image.is-9by16 img,.image.is-16by9 img,.image.is-square img,.is-overlay,.loading-overlay,.loading-overlay .loading-background,.modal,.modal-background{bottom:0;left:0;position:absolute;right:0;top:0}.button,.file-cta,.file-name,.input,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.select select,.taginput .taginput-container.is-focusable,.textarea{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.25em;justify-content:flex-start;line-height:1.5;padding:calc(.375em - 1px) calc(.625em - 1px);position:relative;vertical-align:top}.button:active,.button:focus,.file-cta:active,.file-cta:focus,.file-name:active,.file-name:focus,.input:active,.input:focus,.is-active.button,.is-active.file-cta,.is-active.file-name,.is-active.input,.is-active.pagination-ellipsis,.is-active.pagination-link,.is-active.pagination-next,.is-active.pagination-previous,.is-active.textarea,.is-focused.button,.is-focused.file-cta,.is-focused.file-name,.is-focused.input,.is-focused.pagination-ellipsis,.is-focused.pagination-link,.is-focused.pagination-next,.is-focused.pagination-previous,.is-focused.textarea,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link:active,.pagination-link:focus,.pagination-next:active,.pagination-next:focus,.pagination-previous:active,.pagination-previous:focus,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.taginput .is-active.taginput-container.is-focusable,.taginput .is-focused.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:active,.taginput .taginput-container.is-focusable:focus,.textarea:active,.textarea:focus{outline:none}.select select[disabled],.taginput [disabled].taginput-container.is-focusable,[disabled].button,[disabled].file-cta,[disabled].file-name,[disabled].input,[disabled].pagination-ellipsis,[disabled].pagination-link,[disabled].pagination-next,[disabled].pagination-previous,[disabled].textarea{cursor:not-allowed}\n\n/*! minireset.css v0.0.3 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}audio,img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0;text-align:left}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,select,textarea{font-family:BlinkMacSystemFont,-apple-system,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1rem;font-weight:400;line-height:1.5}a{color:#3273dc;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{color:#ff3860;font-size:.875em;font-weight:400;padding:.25em .5em}code,hr{background-color:#f5f5f5}hr{border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{text-align:left;vertical-align:top}table th{color:#363636}.is-clearfix:after{clear:both;content:\" \";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-clipped{overflow:hidden!important}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media print,screen and (min-width:769px){.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1087px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1088px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1280px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1472px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}.has-text-justified{text-align:justify!important}.has-text-left{text-align:left!important}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media print,screen and (min-width:769px){.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1087px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1088px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1280px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1472px){.has-text-centered-fullhd{text-align:center!important}}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media print,screen and (min-width:769px){.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1087px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1088px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1280px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1472px){.has-text-justified-fullhd{text-align:justify!important}}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media print,screen and (min-width:769px){.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1087px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1088px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1280px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1472px){.has-text-left-fullhd{text-align:left!important}}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media print,screen and (min-width:769px){.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1087px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1088px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1280px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1472px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-background-white{background-color:#fff!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-background-black{background-color:#0a0a0a!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-background-light{background-color:#f5f5f5!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-background-dark{background-color:#363636!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-background-primary{background-color:#00d1b2!important}.has-text-link{color:#3273dc!important}a.has-text-link:focus,a.has-text-link:hover{color:#205bbc!important}.has-background-link{background-color:#3273dc!important}.has-text-info{color:#209cee!important}a.has-text-info:focus,a.has-text-info:hover{color:#0f81cc!important}.has-background-info{background-color:#209cee!important}.has-text-success{color:#23d160!important}a.has-text-success:focus,a.has-text-success:hover{color:#1ca64c!important}.has-background-success{background-color:#23d160!important}.has-text-warning{color:#ffdd57!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd324!important}.has-background-warning{background-color:#ffdd57!important}.has-text-danger{color:#ff3860!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ff0537!important}.has-background-danger{background-color:#ff3860!important}.has-text-black-bis{color:#121212!important}.has-background-black-bis{background-color:#121212!important}.has-text-black-ter{color:#242424!important}.has-background-black-ter{background-color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-background-grey-darker{background-color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-background-grey-dark{background-color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-background-grey{background-color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-background-grey-light{background-color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-background-grey-lighter{background-color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-background-white-ter{background-color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-background-white-bis{background-color:#fafafa!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media print,screen and (min-width:769px){.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1087px){.is-block-touch{display:block!important}}@media screen and (min-width:1088px){.is-block-desktop{display:block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1280px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1472px){.is-block-fullhd{display:block!important}}.is-flex{display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:flex!important}}@media print,screen and (min-width:769px){.is-flex-tablet{display:flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-flex-tablet-only{display:flex!important}}@media screen and (max-width:1087px){.is-flex-touch{display:flex!important}}@media screen and (min-width:1088px){.is-flex-desktop{display:flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-flex-desktop-only{display:flex!important}}@media screen and (min-width:1280px){.is-flex-widescreen{display:flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-flex-widescreen-only{display:flex!important}}@media screen and (min-width:1472px){.is-flex-fullhd{display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media print,screen and (min-width:769px){.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1087px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1088px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1280px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1472px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media print,screen and (min-width:769px){.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1087px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1088px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1280px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1472px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:inline-flex!important}}@media print,screen and (min-width:769px){.is-inline-flex-tablet{display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-flex-tablet-only{display:inline-flex!important}}@media screen and (max-width:1087px){.is-inline-flex-touch{display:inline-flex!important}}@media screen and (min-width:1088px){.is-inline-flex-desktop{display:inline-flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-flex-desktop-only{display:inline-flex!important}}@media screen and (min-width:1280px){.is-inline-flex-widescreen{display:inline-flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-flex-widescreen-only{display:inline-flex!important}}@media screen and (min-width:1472px){.is-inline-flex-fullhd{display:inline-flex!important}}.is-hidden{display:none!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media print,screen and (min-width:769px){.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1087px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1088px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1280px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1472px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media print,screen and (min-width:769px){.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1087px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1088px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1280px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1472px){.is-invisible-fullhd{visibility:hidden!important}}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.is-radiusless{border-radius:0!important}.is-shadowless{box-shadow:none!important}.box{background-color:#fff;border-radius:6px;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);color:#4a4a4a;display:block;padding:1.25rem}a.box:focus,a.box:hover{box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px #3273dc}a.box:active{box-shadow:inset 0 1px 2px hsla(0,0%,4%,.2),0 0 0 1px #3273dc}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding:calc(.375em - 1px) .75em;text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.375em - 1px);margin-right:.1875em}.button .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:calc(-.375em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.375em - 1px);margin-right:calc(-.375em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#3273dc;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled]{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled]{background-color:#fff;border-color:transparent;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled]{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading:after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled]{background-color:#0a0a0a;border-color:transparent;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading:after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:#363636}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:#363636}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:#363636}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:#363636}.button.is-light[disabled]{background-color:#f5f5f5;border-color:transparent;box-shadow:none}.button.is-light.is-inverted{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted:hover{background-color:#292929}.button.is-light.is-inverted[disabled]{background-color:#363636;border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading:after{border-color:transparent transparent #363636 #363636!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.button.is-light.is-outlined.is-loading:after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark{background-color:#363636;border-color:transparent;color:#f5f5f5}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#f5f5f5}.button.is-dark[disabled]{background-color:#363636;border-color:transparent;box-shadow:none}.button.is-dark.is-inverted{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted:hover{background-color:#e8e8e8}.button.is-dark.is-inverted[disabled]{background-color:#f5f5f5;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading:after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.button.is-dark.is-outlined.is-loading:after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined[disabled]{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled]{background-color:#00d1b2;border-color:transparent;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading:after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined[disabled]{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link{background-color:#3273dc;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#276cda;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#2366d1;border-color:transparent;color:#fff}.button.is-link[disabled]{background-color:#3273dc;border-color:transparent;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#3273dc}.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#3273dc}.button.is-link.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;color:#3273dc}.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#3273dc;border-color:#3273dc;color:#fff}.button.is-link.is-outlined.is-loading:after{border-color:transparent transparent #3273dc #3273dc!important}.button.is-link.is-outlined[disabled]{background-color:transparent;border-color:#3273dc;box-shadow:none;color:#3273dc}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info{background-color:#209cee;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#1496ed;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#118fe4;border-color:transparent;color:#fff}.button.is-info[disabled]{background-color:#209cee;border-color:transparent;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#209cee}.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#209cee}.button.is-info.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#209cee;color:#209cee}.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#209cee;border-color:#209cee;color:#fff}.button.is-info.is-outlined.is-loading:after{border-color:transparent transparent #209cee #209cee!important}.button.is-info.is-outlined[disabled]{background-color:transparent;border-color:#209cee;box-shadow:none;color:#209cee}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#209cee}.button.is-info.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success{background-color:#23d160;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#22c65b;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#20bc56;border-color:transparent;color:#fff}.button.is-success[disabled]{background-color:#23d160;border-color:transparent;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#23d160}.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#23d160}.button.is-success.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#23d160;color:#23d160}.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#23d160;border-color:#23d160;color:#fff}.button.is-success.is-outlined.is-loading:after{border-color:transparent transparent #23d160 #23d160!important}.button.is-success.is-outlined[disabled]{background-color:transparent;border-color:#23d160;box-shadow:none;color:#23d160}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#23d160}.button.is-success.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled]{background-color:#ffdd57;border-color:transparent;box-shadow:none}.button.is-warning.is-inverted{color:#ffdd57}.button.is-warning.is-inverted,.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled]{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading:after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading:after{border-color:transparent transparent #ffdd57 #ffdd57!important}.button.is-warning.is-outlined[disabled]{background-color:transparent;border-color:#ffdd57;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-danger{background-color:#ff3860;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#ff2b56;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ff1f4b;border-color:transparent;color:#fff}.button.is-danger[disabled]{background-color:#ff3860;border-color:transparent;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#ff3860}.button.is-danger.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;color:#ff3860}.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#ff3860;border-color:#ff3860;color:#fff}.button.is-danger.is-outlined.is-loading:after{border-color:transparent transparent #ff3860 #ff3860!important}.button.is-danger.is-outlined[disabled]{background-color:transparent;border-color:#ff3860;box-shadow:none;color:#ff3860}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-small{border-radius:2px;font-size:.75rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled]{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading:after{position:absolute;left:calc(50% - 0.5em);top:calc(50% - 0.5em);position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1}.buttons.is-centered{justify-content:center}.buttons.is-right{justify-content:flex-end}.container{margin:0 auto;position:relative}@media screen and (min-width:1088px){.container{max-width:960px;width:960px}.container.is-fluid{margin-left:64px;margin-right:64px;max-width:none;width:auto}}@media screen and (max-width:1279px){.container.is-widescreen{max-width:1152px;width:auto}}@media screen and (max-width:1471px){.container.is-fullhd{max-width:1344px;width:auto}}@media screen and (min-width:1280px){.container{max-width:1152px;width:1152px}}@media screen and (min-width:1472px){.container{max-width:1344px;width:1344px}}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style:decimal outside}.content ol,.content ul{margin-left:2em;margin-top:1em}.content ul{list-style:disc outside}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636;text-align:left}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content.is-small{font-size:.75rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.input,.taginput .taginput-container.is-focusable,.textarea{background-color:#fff;border-color:#dbdbdb;color:#363636;box-shadow:inset 0 1px 2px hsla(0,0%,4%,.1);max-width:100%;width:100%}.input::-moz-placeholder,.taginput .taginput-container.is-focusable::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.taginput .taginput-container.is-focusable::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.taginput .taginput-container.is-focusable:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.taginput .taginput-container.is-focusable:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input.is-hovered,.input:hover,.taginput .is-hovered.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:hover,.textarea.is-hovered,.textarea:hover{border-color:#b5b5b5}.input.is-active,.input.is-focused,.input:active,.input:focus,.taginput .is-active.taginput-container.is-focusable,.taginput .is-focused.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:active,.taginput .taginput-container.is-focusable:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input[disabled],.taginput [disabled].taginput-container.is-focusable,.textarea[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.taginput [disabled].taginput-container.is-focusable::-moz-placeholder,.textarea[disabled]::-moz-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]::-webkit-input-placeholder,.taginput [disabled].taginput-container.is-focusable::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]:-moz-placeholder,.taginput [disabled].taginput-container.is-focusable:-moz-placeholder,.textarea[disabled]:-moz-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]:-ms-input-placeholder,.taginput [disabled].taginput-container.is-focusable:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder{color:hsla(0,0%,48%,.3)}.input[readonly],.taginput [readonly].taginput-container.is-focusable,.textarea[readonly]{box-shadow:none}.input.is-white,.taginput .is-white.taginput-container.is-focusable,.textarea.is-white{border-color:#fff}.input.is-white.is-active,.input.is-white.is-focused,.input.is-white:active,.input.is-white:focus,.taginput .is-white.is-active.taginput-container.is-focusable,.taginput .is-white.is-focused.taginput-container.is-focusable,.taginput .is-white.taginput-container.is-focusable:active,.taginput .is-white.taginput-container.is-focusable:focus,.textarea.is-white.is-active,.textarea.is-white.is-focused,.textarea.is-white:active,.textarea.is-white:focus{box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.input.is-black,.taginput .is-black.taginput-container.is-focusable,.textarea.is-black{border-color:#0a0a0a}.input.is-black.is-active,.input.is-black.is-focused,.input.is-black:active,.input.is-black:focus,.taginput .is-black.is-active.taginput-container.is-focusable,.taginput .is-black.is-focused.taginput-container.is-focusable,.taginput .is-black.taginput-container.is-focusable:active,.taginput .is-black.taginput-container.is-focusable:focus,.textarea.is-black.is-active,.textarea.is-black.is-focused,.textarea.is-black:active,.textarea.is-black:focus{box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.input.is-light,.taginput .is-light.taginput-container.is-focusable,.textarea.is-light{border-color:#f5f5f5}.input.is-light.is-active,.input.is-light.is-focused,.input.is-light:active,.input.is-light:focus,.taginput .is-light.is-active.taginput-container.is-focusable,.taginput .is-light.is-focused.taginput-container.is-focusable,.taginput .is-light.taginput-container.is-focusable:active,.taginput .is-light.taginput-container.is-focusable:focus,.textarea.is-light.is-active,.textarea.is-light.is-focused,.textarea.is-light:active,.textarea.is-light:focus{box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.input.is-dark,.taginput .is-dark.taginput-container.is-focusable,.textarea.is-dark{border-color:#363636}.input.is-dark.is-active,.input.is-dark.is-focused,.input.is-dark:active,.input.is-dark:focus,.taginput .is-dark.is-active.taginput-container.is-focusable,.taginput .is-dark.is-focused.taginput-container.is-focusable,.taginput .is-dark.taginput-container.is-focusable:active,.taginput .is-dark.taginput-container.is-focusable:focus,.textarea.is-dark.is-active,.textarea.is-dark.is-focused,.textarea.is-dark:active,.textarea.is-dark:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.input.is-primary,.taginput .is-primary.taginput-container.is-focusable,.textarea.is-primary{border-color:#00d1b2}.input.is-primary.is-active,.input.is-primary.is-focused,.input.is-primary:active,.input.is-primary:focus,.taginput .is-primary.is-active.taginput-container.is-focusable,.taginput .is-primary.is-focused.taginput-container.is-focusable,.taginput .is-primary.taginput-container.is-focusable:active,.taginput .is-primary.taginput-container.is-focusable:focus,.textarea.is-primary.is-active,.textarea.is-primary.is-focused,.textarea.is-primary:active,.textarea.is-primary:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.input.is-link,.taginput .is-link.taginput-container.is-focusable,.textarea.is-link{border-color:#3273dc}.input.is-link.is-active,.input.is-link.is-focused,.input.is-link:active,.input.is-link:focus,.taginput .is-link.is-active.taginput-container.is-focusable,.taginput .is-link.is-focused.taginput-container.is-focusable,.taginput .is-link.taginput-container.is-focusable:active,.taginput .is-link.taginput-container.is-focusable:focus,.textarea.is-link.is-active,.textarea.is-link.is-focused,.textarea.is-link:active,.textarea.is-link:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input.is-info,.taginput .is-info.taginput-container.is-focusable,.textarea.is-info{border-color:#209cee}.input.is-info.is-active,.input.is-info.is-focused,.input.is-info:active,.input.is-info:focus,.taginput .is-info.is-active.taginput-container.is-focusable,.taginput .is-info.is-focused.taginput-container.is-focusable,.taginput .is-info.taginput-container.is-focusable:active,.taginput .is-info.taginput-container.is-focusable:focus,.textarea.is-info.is-active,.textarea.is-info.is-focused,.textarea.is-info:active,.textarea.is-info:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.input.is-success,.taginput .is-success.taginput-container.is-focusable,.textarea.is-success{border-color:#23d160}.input.is-success.is-active,.input.is-success.is-focused,.input.is-success:active,.input.is-success:focus,.taginput .is-success.is-active.taginput-container.is-focusable,.taginput .is-success.is-focused.taginput-container.is-focusable,.taginput .is-success.taginput-container.is-focusable:active,.taginput .is-success.taginput-container.is-focusable:focus,.textarea.is-success.is-active,.textarea.is-success.is-focused,.textarea.is-success:active,.textarea.is-success:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.input.is-warning,.taginput .is-warning.taginput-container.is-focusable,.textarea.is-warning{border-color:#ffdd57}.input.is-warning.is-active,.input.is-warning.is-focused,.input.is-warning:active,.input.is-warning:focus,.taginput .is-warning.is-active.taginput-container.is-focusable,.taginput .is-warning.is-focused.taginput-container.is-focusable,.taginput .is-warning.taginput-container.is-focusable:active,.taginput .is-warning.taginput-container.is-focusable:focus,.textarea.is-warning.is-active,.textarea.is-warning.is-focused,.textarea.is-warning:active,.textarea.is-warning:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.input.is-danger,.taginput .is-danger.taginput-container.is-focusable,.textarea.is-danger{border-color:#ff3860}.input.is-danger.is-active,.input.is-danger.is-focused,.input.is-danger:active,.input.is-danger:focus,.taginput .is-danger.is-active.taginput-container.is-focusable,.taginput .is-danger.is-focused.taginput-container.is-focusable,.taginput .is-danger.taginput-container.is-focusable:active,.taginput .is-danger.taginput-container.is-focusable:focus,.textarea.is-danger.is-active,.textarea.is-danger.is-focused,.textarea.is-danger:active,.textarea.is-danger:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.input.is-small,.taginput .is-small.taginput-container.is-focusable,.textarea.is-small{border-radius:2px;font-size:.75rem}.input.is-medium,.taginput .is-medium.taginput-container.is-focusable,.textarea.is-medium{font-size:1.25rem}.input.is-large,.taginput .is-large.taginput-container.is-focusable,.textarea.is-large{font-size:1.5rem}.input.is-fullwidth,.taginput .is-fullwidth.taginput-container.is-focusable,.textarea.is-fullwidth{display:block;width:100%}.input.is-inline,.taginput .is-inline.taginput-container.is-focusable,.textarea.is-inline{display:inline;width:auto}.input.is-rounded,.taginput .is-rounded.taginput-container.is-focusable{border-radius:290486px;padding-left:1em;padding-right:1em}.input.is-static,.taginput .is-static.taginput-container.is-focusable{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:.625em;resize:vertical}.textarea:not([rows]){max-height:600px;min-height:120px}.textarea[rows]{height:auto}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox[disabled],.radio[disabled]{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.25em}.select:not(.is-multiple):not(.is-loading):after{border-color:#3273dc;right:1.125em;z-index:4}.select.is-rounded select{border-radius:290486px;padding-left:1em}.select select{background-color:#fff;border-color:#dbdbdb;color:#363636;cursor:pointer;display:block;font-size:1em;max-width:100%;outline:none}.select select::-moz-placeholder{color:rgba(54,54,54,.3)}.select select::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.select select:-moz-placeholder{color:rgba(54,54,54,.3)}.select select:-ms-input-placeholder{color:rgba(54,54,54,.3)}.select select.is-hovered,.select select:hover{border-color:#b5b5b5}.select select.is-active,.select select.is-focused,.select select:active,.select select:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select select[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.select select[disabled]::-moz-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]::-webkit-input-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]:-moz-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]:-ms-input-placeholder{color:hsla(0,0%,48%,.3)}.select select::-ms-expand{display:none}.select select[disabled]:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:.5em 1em}.select:not(.is-multiple):not(.is-loading):hover:after{border-color:#363636}.select.is-white:not(:hover):after,.select.is-white select{border-color:#fff}.select.is-white select.is-hovered,.select.is-white select:hover{border-color:#f2f2f2}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.select.is-black:not(:hover):after,.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-hovered,.select.is-black select:hover{border-color:#000}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.select.is-light:not(:hover):after,.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-hovered,.select.is-light select:hover{border-color:#e8e8e8}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.select.is-dark:not(:hover):after,.select.is-dark select{border-color:#363636}.select.is-dark select.is-hovered,.select.is-dark select:hover{border-color:#292929}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary:not(:hover):after,.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-hovered,.select.is-primary select:hover{border-color:#00b89c}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link:not(:hover):after,.select.is-link select{border-color:#3273dc}.select.is-link select.is-hovered,.select.is-link select:hover{border-color:#2366d1}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select.is-info:not(:hover):after,.select.is-info select{border-color:#209cee}.select.is-info select.is-hovered,.select.is-info select:hover{border-color:#118fe4}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.select.is-success:not(:hover):after,.select.is-success select{border-color:#23d160}.select.is-success select.is-hovered,.select.is-success select:hover{border-color:#20bc56}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.select.is-warning:not(:hover):after,.select.is-warning select{border-color:#ffdd57}.select.is-warning select.is-hovered,.select.is-warning select:hover{border-color:#ffd83d}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.select.is-danger:not(:hover):after,.select.is-danger select{border-color:#ff3860}.select.is-danger select.is-hovered,.select.is-danger select:hover{border-color:#ff1f4b}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled:after{border-color:#7a7a7a}.select.is-fullwidth,.select.is-fullwidth select{width:100%}.select.is-loading:after{margin-top:0;position:absolute;right:.625em;top:.625em;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,100%,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,4%,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:#363636}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:#363636}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,96%,.25);color:#363636}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:#363636}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#f5f5f5}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(54,54,54,.25);color:#f5f5f5}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#f5f5f5}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#3273dc;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#276cda;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(50,115,220,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#2366d1;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#209cee;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#1496ed;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(32,156,238,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#118fe4;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#23d160;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#22c65b;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(35,209,96,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#20bc56;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,221,87,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#ff3860;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#ff2b56;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,56,96,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ff1f4b;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:.01em;left:0;outline:none;position:absolute;top:0;width:.01em}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:left;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#3273dc}.help.is-info{color:#209cee}.help.is-success{color:#23d160}.help.is-warning{color:#ffdd57}.help.is-danger{color:#ff3860}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select,.field.has-addons .control:not(:first-child):not(:last-child) .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:not(:first-child):not(:last-child) .taginput-container.is-focusable{border-radius:0}.field.has-addons .control:first-child .button,.field.has-addons .control:first-child .input,.field.has-addons .control:first-child .select select,.field.has-addons .control:first-child .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:first-child .taginput-container.is-focusable{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child .button,.field.has-addons .control:last-child .input,.field.has-addons .control:last-child .select select,.field.has-addons .control:last-child .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:last-child .taginput-container.is-focusable{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button.is-hovered,.field.has-addons .control .button:hover,.field.has-addons .control .input.is-hovered,.field.has-addons .control .input:hover,.field.has-addons .control .select select.is-hovered,.field.has-addons .control .select select:hover,.field.has-addons .control .taginput .is-hovered.taginput-container.is-focusable,.field.has-addons .control .taginput .taginput-container.is-focusable:hover,.taginput .field.has-addons .control .is-hovered.taginput-container.is-focusable,.taginput .field.has-addons .control .taginput-container.is-focusable:hover{z-index:2}.field.has-addons .control .button.is-active,.field.has-addons .control .button.is-focused,.field.has-addons .control .button:active,.field.has-addons .control .button:focus,.field.has-addons .control .input.is-active,.field.has-addons .control .input.is-focused,.field.has-addons .control .input:active,.field.has-addons .control .input:focus,.field.has-addons .control .select select.is-active,.field.has-addons .control .select select.is-focused,.field.has-addons .control .select select:active,.field.has-addons .control .select select:focus,.field.has-addons .control .taginput .is-active.taginput-container.is-focusable,.field.has-addons .control .taginput .is-focused.taginput-container.is-focusable,.field.has-addons .control .taginput .taginput-container.is-focusable:active,.field.has-addons .control .taginput .taginput-container.is-focusable:focus,.taginput .field.has-addons .control .is-active.taginput-container.is-focusable,.taginput .field.has-addons .control .is-focused.taginput-container.is-focusable,.taginput .field.has-addons .control .taginput-container.is-focusable:active,.taginput .field.has-addons .control .taginput-container.is-focusable:focus{z-index:3}.field.has-addons .control .button.is-active:hover,.field.has-addons .control .button.is-focused:hover,.field.has-addons .control .button:active:hover,.field.has-addons .control .button:focus:hover,.field.has-addons .control .input.is-active:hover,.field.has-addons .control .input.is-focused:hover,.field.has-addons .control .input:active:hover,.field.has-addons .control .input:focus:hover,.field.has-addons .control .select select.is-active:hover,.field.has-addons .control .select select.is-focused:hover,.field.has-addons .control .select select:active:hover,.field.has-addons .control .select select:focus:hover,.field.has-addons .control .taginput .is-active.taginput-container.is-focusable:hover,.field.has-addons .control .taginput .is-focused.taginput-container.is-focusable:hover,.field.has-addons .control .taginput .taginput-container.is-focusable:active:hover,.field.has-addons .control .taginput .taginput-container.is-focusable:focus:hover,.taginput .field.has-addons .control .is-active.taginput-container.is-focusable:hover,.taginput .field.has-addons .control .is-focused.taginput-container.is-focusable:hover,.taginput .field.has-addons .control .taginput-container.is-focusable:active:hover,.taginput .field.has-addons .control .taginput-container.is-focusable:focus:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media print,screen and (min-width:769px){.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media print,screen and (min-width:769px){.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media print,screen and (min-width:769px){.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{font-size:1rem;position:relative;text-align:left}.control.has-icon .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icon .input:focus+.icon,.control.has-icon .taginput .taginput-container.is-focusable:focus+.icon,.taginput .control.has-icon .taginput-container.is-focusable:focus+.icon{color:#7a7a7a}.control.has-icon .input.is-small+.icon,.control.has-icon .taginput .is-small.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-small.taginput-container.is-focusable+.icon{font-size:.75rem}.control.has-icon .input.is-medium+.icon,.control.has-icon .taginput .is-medium.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-medium.taginput-container.is-focusable+.icon{font-size:1.25rem}.control.has-icon .input.is-large+.icon,.control.has-icon .taginput .is-large.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-large.taginput-container.is-focusable+.icon{font-size:1.5rem}.control.has-icon:not(.has-icon-right) .icon{left:0}.control.has-icon:not(.has-icon-right) .input,.control.has-icon:not(.has-icon-right) .taginput .taginput-container.is-focusable,.taginput .control.has-icon:not(.has-icon-right) .taginput-container.is-focusable{padding-left:2.25em}.control.has-icon.has-icon-right .icon{right:0}.control.has-icon.has-icon-right .input,.control.has-icon.has-icon-right .taginput .taginput-container.is-focusable,.taginput .control.has-icon.has-icon-right .taginput-container.is-focusable{padding-right:2.25em}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-left .taginput .taginput-container.is-focusable:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon,.control.has-icons-right .taginput .taginput-container.is-focusable:focus~.icon,.taginput .control.has-icons-left .taginput-container.is-focusable:focus~.icon,.taginput .control.has-icons-right .taginput-container.is-focusable:focus~.icon{color:#7a7a7a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-left .taginput .is-small.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon,.control.has-icons-right .taginput .is-small.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-small.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-small.taginput-container.is-focusable~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-left .taginput .is-medium.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon,.control.has-icons-right .taginput .is-medium.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-medium.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-medium.taginput-container.is-focusable~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-left .taginput .is-large.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon,.control.has-icons-right .taginput .is-large.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-large.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-large.taginput-container.is-focusable~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select,.control.has-icons-left .taginput .taginput-container.is-focusable,.taginput .control.has-icons-left .taginput-container.is-focusable{padding-left:2.25em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select,.control.has-icons-right .taginput .taginput-container.is-focusable,.taginput .control.has-icons-right .taginput-container.is-focusable{padding-right:2.25em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading:after{position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:290486px}.image.is-1by1 img,.image.is-1by2 img,.image.is-1by3 img,.image.is-2by1 img,.image.is-2by3 img,.image.is-3by1 img,.image.is-3by2 img,.image.is-3by4 img,.image.is-3by5 img,.image.is-4by3 img,.image.is-4by5 img,.image.is-5by3 img,.image.is-5by4 img,.image.is-9by16 img,.image.is-16by9 img,.image.is-square img{height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;padding:1.25rem 2.5rem 1.25rem 1.5rem;position:relative}.notification a:not(.button){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:transparent}.notification>.delete{position:absolute;right:.5rem;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:#363636}.notification.is-dark{background-color:#363636;color:#f5f5f5}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-link{background-color:#3273dc;color:#fff}.notification.is-info{background-color:#209cee;color:#fff}.notification.is-success{background-color:#23d160;color:#fff}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.notification.is-danger{background-color:#ff3860;color:#fff}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#dbdbdb}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-link::-webkit-progress-value{background-color:#3273dc}.progress.is-link::-moz-progress-bar{background-color:#3273dc}.progress.is-link::-ms-fill{background-color:#3273dc}.progress.is-info::-webkit-progress-value{background-color:#209cee}.progress.is-info::-moz-progress-bar{background-color:#209cee}.progress.is-info::-ms-fill{background-color:#209cee}.progress.is-success::-webkit-progress-value{background-color:#23d160}.progress.is-success::-moz-progress-bar{background-color:#23d160}.progress.is-success::-ms-fill{background-color:#23d160}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-danger::-webkit-progress-value{background-color:#ff3860}.progress.is-danger::-moz-progress-bar{background-color:#ff3860}.progress.is-danger::-ms-fill{background-color:#ff3860}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#f5f5f5}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#3273dc;border-color:#3273dc;color:#fff}.table td.is-info,.table th.is-info{background-color:#209cee;border-color:#209cee;color:#fff}.table td.is-success,.table th.is-success{background-color:#23d160;border-color:#23d160;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#ff3860;border-color:#ff3860;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table th{color:#363636;text-align:left}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(2n){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.tags.has-addons .tag:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:#363636}.tag:not(body).is-dark{background-color:#363636;color:#f5f5f5}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-link{background-color:#3273dc;color:#fff}.tag:not(body).is-info{background-color:#209cee;color:#fff}.tag:not(body).is-success{background-color:#23d160;color:#fff}.tag:not(body).is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.tag:not(body).is-danger{background-color:#ff3860;color:#fff}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete:after,.tag:not(body).is-delete:before{background-color:currentColor;content:\"\";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.tag:not(body).is-delete:before{height:1px;width:50%}.tag:not(body).is-delete:after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:290486px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.subtitle sup,.title sub,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title+.highlight{margin-top:-.75rem}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-weight:400;max-width:100%;overflow:hidden;padding:0}.highlight pre{overflow:auto;max-width:100%}.number{align-items:center;background-color:#f5f5f5;border-radius:290486px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#3273dc;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li:before{color:#b5b5b5;content:\"/\"}.breadcrumb ol,.breadcrumb ul{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li:before{content:\"\\2192\"}.breadcrumb.has-bullet-separator li+li:before{content:\"\\2022\"}.breadcrumb.has-dot-separator li+li:before{content:\"\\B7\"}.breadcrumb.has-succeeds-separator li+li:before{content:\"\\227B\"}.card{background-color:#fff;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);color:#4a4a4a;max-width:100%;position:relative}.card-header{background-color:none;align-items:stretch;box-shadow:0 1px 2px hsla(0,0%,4%,.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem}.card-header-icon,.card-header-title.is-centered{justify-content:center}.card-header-icon{align-items:center;cursor:pointer;display:flex;padding:.75rem}.card-image{display:block;position:relative}.card-content{background-color:none;padding:1.5rem}.card-footer{background-color:none;border-top:1px solid #dbdbdb;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #dbdbdb}.card .media:not(:last-child){margin-bottom:.75rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:0;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item,.dropdown .dropdown-menu .has-link a{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}.dropdown .dropdown-menu .has-link a,a.dropdown-item{padding-right:3rem;white-space:nowrap}.dropdown .dropdown-menu .has-link a:hover,a.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}.dropdown .dropdown-menu .has-link a.is-active,a.dropdown-item.is-active{background-color:#3273dc;color:#fff}.dropdown-divider{background-color:#dbdbdb;border:none;display:block;height:1px;margin:.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile,.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item{margin-right:.75rem}.level.is-mobile .level-item:not(:last-child){margin-bottom:0}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media print,screen and (min-width:769px){.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media print,screen and (min-width:769px){.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media print,screen and (min-width:769px){.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media print,screen and (min-width:769px){.level-right{display:flex}}.media{align-items:flex-start;display:flex;text-align:left}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid hsla(0,0%,86%,.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid hsla(0,0%,86%,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:left}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#3273dc;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff;color:#4d4d4d}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a;color:#090909}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:#363636}.message.is-light .message-body{border-color:#f5f5f5;color:#505050}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#f5f5f5}.message.is-dark .message-body{border-color:#363636;color:#2a2a2a}.message.is-primary{background-color:#f5fffd}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#021310}.message.is-link{background-color:#f6f9fe}.message.is-link .message-header{background-color:#3273dc;color:#fff}.message.is-link .message-body{border-color:#3273dc;color:#22509a}.message.is-info{background-color:#f6fbfe}.message.is-info .message-header{background-color:#209cee;color:#fff}.message.is-info .message-body{border-color:#209cee;color:#12537e}.message.is-success{background-color:#f6fef9}.message.is-success .message-header{background-color:#23d160;color:#fff}.message.is-success .message-body{border-color:#23d160;color:#0e301a}.message.is-warning{background-color:#fffdf5}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#3b3108}.message.is-danger{background-color:#fff5f7}.message.is-danger .message-header{background-color:#ff3860;color:#fff}.message.is-danger .message-body{border-color:#ff3860;color:#cd0930}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{align-items:center;display:none;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:hsla(0,0%,4%,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media print,screen and (min-width:769px){.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:none;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden}.modal-card-foot,.modal-card-head{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:10px}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link:after{border-color:#0a0a0a}@media screen and (min-width:1088px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link:after,.navbar.is-white .navbar-start .navbar-link:after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link:after,.navbar.is-black .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:#363636}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:#363636}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-brand .navbar-link:after{border-color:#363636}@media screen and (min-width:1088px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:#363636}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-end .navbar-link:after,.navbar.is-light .navbar-start .navbar-link:after{border-color:#363636}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#363636}}.navbar.is-dark{background-color:#363636;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link:after{border-color:#f5f5f5}@media screen and (min-width:1088px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link:after,.navbar.is-dark .navbar-start .navbar-link:after{border-color:#f5f5f5}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#f5f5f5}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link:after,.navbar.is-primary .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#3273dc;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-end .navbar-link:after,.navbar.is-link .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#3273dc;color:#fff}}.navbar.is-info{background-color:#209cee;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-end .navbar-link:after,.navbar.is-info .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#209cee;color:#fff}}.navbar.is-success{background-color:#23d160;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-end .navbar-link:after,.navbar.is-success .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#23d160;color:#fff}}.navbar.is-warning{background-color:#ffdd57}.navbar.is-warning,.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link:after{border-color:rgba(0,0,0,.7)}@media screen and (min-width:1088px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link:after,.navbar.is-warning .navbar-start .navbar-link:after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#ff3860;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-end .navbar-link:after,.navbar.is-danger .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#ff3860;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}body.has-navbar-fixed-top,html.has-navbar-fixed-top{padding-top:3.25rem}body.has-navbar-fixed-bottom,html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;transform-origin:center;transition-duration:86ms;transition-property:background-color,opacity,transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:first-child{top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:first-child{transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem .75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-.25rem;margin-right:-.25rem}.navbar-link,a.navbar-item{cursor:pointer}.navbar-link.is-active,.navbar-link:hover,a.navbar-item.is-active,a.navbar-item:hover{background-color:#fafafa;color:#3273dc}.navbar-item{display:block;flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab.is-active,.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#3273dc}.navbar-item.is-tab.is-active{border-bottom-style:solid;border-bottom-width:3px;color:#3273dc;padding-bottom:calc(.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link{padding-right:2.5em}.navbar-link:after{border-color:#3273dc;margin-top:-.375em;right:1.125em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:.5rem 0}@media screen and (max-width:1087px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link:after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px hsla(0,0%,4%,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px hsla(0,0%,4%,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top-touch .navbar-menu,.navbar.is-fixed-top .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}body.has-navbar-fixed-top-touch,html.has-navbar-fixed-top-touch{padding-top:3.25rem}body.has-navbar-fixed-bottom-touch,html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1088px){.navbar,.navbar-end,.navbar-menu,.navbar-start{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-end,.navbar.is-spaced .navbar-start{align-items:center}.navbar.is-spaced .navbar-link,.navbar.is-spaced a.navbar-item{border-radius:4px}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link,.navbar.is-transparent .navbar-link.is-active,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:hover{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item{display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link:after{transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px hsla(0,0%,4%,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown{opacity:1;pointer-events:auto;transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px hsla(0,0%,4%,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-dropdown{border-radius:6px;border-top:none;box-shadow:0 8px 8px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + -4px);transform:translateY(-5px);transition-duration:86ms;transition-property:opacity,transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-1rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-1rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px hsla(0,0%,4%,.1)}.navbar.is-fixed-top-desktop{top:0}body.has-navbar-fixed-top-desktop,html.has-navbar-fixed-top-desktop{padding-top:3.25rem}body.has-navbar-fixed-bottom-desktop,html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}body.has-spaced-navbar-fixed-top,html.has-spaced-navbar-fixed-top{padding-top:5.25rem}body.has-spaced-navbar-fixed-bottom,html.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}.navbar-link.is-active,a.navbar-item.is-active{color:#0a0a0a}.navbar-link.is-active:not(:hover),a.navbar-item.is-active:not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#fafafa}}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:290486px}.pagination.is-rounded .pagination-link{border-radius:290486px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{font-size:1em;padding-left:.5em;padding-right:.5em;justify-content:center;margin:.25rem;text-align:center}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.25em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#3273dc}.pagination-link:active,.pagination-next:active,.pagination-previous:active{box-shadow:inset 0 1px 2px hsla(0,0%,4%,.2)}.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#3273dc;border-color:#3273dc;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}@media screen and (max-width:768px){.pagination{flex-wrap:wrap}.pagination-list li,.pagination-next,.pagination-previous{flex-grow:1;flex-shrink:1}}@media print,screen and (min-width:769px){.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel-block,.panel-heading,.panel-tabs{border-bottom:1px solid #dbdbdb;border-left:1px solid #dbdbdb;border-right:1px solid #dbdbdb}.panel-block:first-child,.panel-heading:first-child,.panel-tabs:first-child{border-top:1px solid #dbdbdb}.panel-heading{background-color:#f5f5f5;border-radius:4px 4px 0 0;color:#363636;font-size:1.25em;font-weight:300;line-height:1.25;padding:.5em .75em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#3273dc}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#3273dc;color:#363636}.panel-block.is-active .panel-icon{color:#3273dc}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#3273dc;color:#3273dc}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-center,.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border:1px solid #dbdbdb;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-radius:4px 0 0 4px}.tabs.is-toggle li:last-child a{border-radius:0 4px 4px 0}.tabs.is-toggle li.is-active a{background-color:#3273dc;border-color:#3273dc;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:290486px;border-top-left-radius:290486px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:290486px;border-top-right-radius:290486px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{flex:none}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media print,screen and (min-width:769px){.column.is-narrow,.column.is-narrow-tablet{flex:none}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1087px){.column.is-narrow-touch{flex:none}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1088px){.column.is-narrow-desktop{flex:none}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1280px){.column.is-narrow-widescreen{flex:none}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1472px){.column.is-narrow-fullhd{flex:none}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:0.75rem}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media print,screen and (min-width:769px){.columns:not(.is-desktop){display:flex}}@media screen and (min-width:1088px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable .column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}.columns.is-variable.is-1{--columnGap:0.25rem}.columns.is-variable.is-2{--columnGap:0.5rem}.columns.is-variable.is-3{--columnGap:0.75rem}.columns.is-variable.is-4{--columnGap:1rem}.columns.is-variable.is-5{--columnGap:1.25rem}.columns.is-variable.is-6{--columnGap:1.5rem}.columns.is-variable.is-7{--columnGap:1.75rem}.columns.is-variable.is-8{--columnGap:2rem}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media print,screen and (min-width:769px){.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:none}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:hsla(0,0%,4%,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1087px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:hsla(0,0%,4%,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover,.hero.is-white .tabs li.is-active a{opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6,#fff 71%,#fff)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6,#fff 71%,#fff)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover,.hero.is-black .tabs li.is-active a{opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000,#0a0a0a 71%,#181616)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000,#0a0a0a 71%,#181616)}}.hero.is-light{background-color:#f5f5f5;color:#363636}.hero.is-light a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-light strong{color:inherit}.hero.is-light .title{color:#363636}.hero.is-light .subtitle{color:rgba(54,54,54,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:#363636}@media screen and (max-width:1087px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(54,54,54,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.hero.is-light .tabs a{color:#363636;opacity:.9}.hero.is-light .tabs a:hover,.hero.is-light .tabs li.is-active a{opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:#363636}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9,#f5f5f5 71%,#fff)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9,#f5f5f5 71%,#fff)}}.hero.is-dark{background-color:#363636;color:#f5f5f5}.hero.is-dark a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#f5f5f5}.hero.is-dark .subtitle{color:hsla(0,0%,96%,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#f5f5f5}@media screen and (max-width:1087px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:hsla(0,0%,96%,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.hero.is-dark .tabs a{color:#f5f5f5;opacity:.9}.hero.is-dark .tabs a:hover,.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#f5f5f5}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a,#363636 71%,#46403f)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a,#363636 71%,#46403f)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover,.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c,#00d1b2 71%,#00e7eb)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c,#00d1b2 71%,#00e7eb)}}.hero.is-link{background-color:#3273dc;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-link .navbar-menu{background-color:#3273dc}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#2366d1;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover,.hero.is-link .tabs li.is-active a{opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3273dc}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#1577c6,#3273dc 71%,#4366e5)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1577c6,#3273dc 71%,#4366e5)}}.hero.is-info{background-color:#209cee;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-info .navbar-menu{background-color:#209cee}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#118fe4;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover,.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#209cee}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#04a6d7,#209cee 71%,#3287f5)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#04a6d7,#209cee 71%,#3287f5)}}.hero.is-success{background-color:#23d160;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-success .navbar-menu{background-color:#23d160}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#20bc56;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover,.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#23d160}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#12af2f,#23d160 71%,#2ce28a)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#12af2f,#23d160 71%,#2ce28a)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1087px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover,.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffaf24,#ffdd57 71%,#fffa70)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffaf24,#ffdd57 71%,#fffa70)}}.hero.is-danger{background-color:#ff3860;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-danger .navbar-menu{background-color:#ff3860}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover,.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#ff3860}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#ff0561,#ff3860 71%,#ff5257)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ff0561,#ff3860 71%,#ff5257)}}.hero.is-small .hero-body{padding-bottom:1.5rem;padding-top:1.5rem}@media print,screen and (min-width:769px){.hero.is-medium .hero-body{padding-bottom:9rem;padding-top:9rem}}@media print,screen and (min-width:769px){.hero.is-large .hero-body{padding-bottom:18rem;padding-top:18rem}}.hero.is-fullheight .hero-body,.hero.is-halfheight .hero-body{align-items:center;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-halfheight .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media print,screen and (min-width:769px){.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0}.hero-body,.section{padding:3rem 1.5rem}@media screen and (min-width:1088px){.section.is-medium{padding:9rem 1.5rem}.section.is-large{padding:18rem 1.5rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem}.is-noscroll{position:fixed;overflow-y:hidden;width:100%;bottom:0}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{animation-name:fadeOut}@keyframes fadeOutDown{0%{opacity:1}to{opacity:0;transform:translate3d(0,100%,0)}}.fadeOutDown{animation-name:fadeOutDown}@keyframes fadeOutUp{0%{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}.fadeOutUp{animation-name:fadeOutUp}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{animation-name:fadeIn}@keyframes fadeInDown{0%{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}.fadeInDown{animation-name:fadeInDown}@keyframes fadeInUp{0%{opacity:0;transform:translate3d(0,100%,0)}to{opacity:1;transform:none}}.fadeInUp{animation-name:fadeInUp}.fade-enter-active,.fade-leave-active{transition:opacity .15s ease-out}.fade-enter,.fade-leave-to{opacity:0}.zoom-in-enter-active,.zoom-in-leave-active{transition:opacity .15s ease-out}.zoom-in-enter-active .animation-content,.zoom-in-leave-active .animation-content{transition:transform .15s ease-out}.zoom-in-enter,.zoom-in-leave-active{opacity:0}.zoom-in-enter .animation-content,.zoom-in-leave-active .animation-content{transform:scale(.95)}.zoom-out-enter-active,.zoom-out-leave-active{transition:opacity .15s ease-out}.zoom-out-enter-active .animation-content,.zoom-out-leave-active .animation-content{transition:transform .15s ease-out}.zoom-out-enter,.zoom-out-leave-active{opacity:0}.zoom-out-enter .animation-content,.zoom-out-leave-active .animation-content{transform:scale(1.05)}.slide-next-enter-active,.slide-next-leave-active,.slide-prev-enter-active,.slide-prev-leave-active{transition:transform .25s cubic-bezier(.785,.135,.15,.86)}.slide-next-enter,.slide-prev-leave-to{transform:translate3d(-100%,0,0);position:absolute;width:100%}.slide-next-leave-to,.slide-prev-enter{transform:translate3d(100%,0,0);position:absolute;width:100%}.autocomplete{position:relative}.autocomplete .dropdown-menu{display:block;min-width:100%;max-width:100%}.autocomplete .dropdown-menu.is-opened-top{top:auto;bottom:100%}.autocomplete .dropdown-content{overflow:auto;max-height:200px}.autocomplete .dropdown-item,.autocomplete .dropdown .dropdown-menu .has-link a,.dropdown .dropdown-menu .has-link .autocomplete a{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.autocomplete .dropdown-item.is-hovered,.autocomplete .dropdown .dropdown-menu .has-link a.is-hovered,.dropdown .dropdown-menu .has-link .autocomplete a.is-hovered{background:#f5f5f5;color:#0a0a0a}.autocomplete .dropdown-item.is-disabled,.autocomplete .dropdown .dropdown-menu .has-link a.is-disabled,.dropdown .dropdown-menu .has-link .autocomplete a.is-disabled{opacity:.5;cursor:not-allowed}.autocomplete.is-small{border-radius:2px;font-size:.75rem}.autocomplete.is-medium{font-size:1.25rem}.autocomplete.is-large{font-size:1.5rem}.b-checkbox.checkbox{outline:none;display:inline-flex;align-items:center}.b-checkbox.checkbox+.checkbox{margin-left:.5em}.b-checkbox.checkbox input[type=checkbox]{position:absolute;left:0;opacity:0;outline:none;z-index:-1}.b-checkbox.checkbox input[type=checkbox]+.check{width:1.25em;height:1.25em;flex-shrink:0;border-radius:4px;border:2px solid #7a7a7a;transition:background .15s ease-out}.b-checkbox.checkbox input[type=checkbox]:checked+.check{background:#00d1b2 url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#00d1b2}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-white{background:#fff url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%230a0a0a'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#fff}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-black{background:#0a0a0a url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#0a0a0a}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-light{background:#f5f5f5 url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23363636'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#f5f5f5}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-dark{background:#363636 url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23f5f5f5'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#363636}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-primary{background:#00d1b2 url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#00d1b2}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-link{background:#3273dc url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#3273dc}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-info{background:#209cee url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#209cee}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-success{background:#23d160 url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#23d160}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-warning{background:#ffdd57 url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='rgba(0,0,0,.7)'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#ffdd57}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-danger{background:#ff3860 url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\") no-repeat 50%;border-color:#ff3860}.b-checkbox.checkbox .control-label{padding-left:.5em}.b-checkbox.checkbox[disabled]{opacity:.5}.b-checkbox.checkbox:hover input[type=checkbox]+.check{border-color:#00d1b2}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-white{border-color:#fff}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-black{border-color:#0a0a0a}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-light{border-color:#f5f5f5}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-dark{border-color:#363636}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-primary{border-color:#00d1b2}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-link{border-color:#3273dc}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-info{border-color:#209cee}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-success{border-color:#23d160}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-warning{border-color:#ffdd57}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-danger{border-color:#ff3860}.b-checkbox.checkbox:focus input[type=checkbox]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.b-checkbox.checkbox.is-small{border-radius:2px;font-size:.75rem}.b-checkbox.checkbox.is-medium{font-size:1.25rem}.b-checkbox.checkbox.is-large{font-size:1.5rem}.collapse .collapse-trigger{display:inline;cursor:pointer}.collapse .collapse-content{display:inherit}.datepicker{font-size:.875rem}.datepicker .dropdown,.datepicker .dropdown-trigger{width:100%}.datepicker .dropdown-item,.datepicker .dropdown .dropdown-menu .has-link a,.dropdown .dropdown-menu .has-link .datepicker a{font-size:inherit}.datepicker .datepicker-header{padding-bottom:.875rem;margin-bottom:.875rem;border-bottom:1px solid #dbdbdb}.datepicker .datepicker-footer{padding-top:.875rem;border-top:1px solid #dbdbdb}.datepicker .datepicker-table{display:table;margin:0 auto .875rem}.datepicker .datepicker-table .datepicker-cell{text-align:center;vertical-align:middle;display:table-cell;border-radius:4px;padding:.5rem .75rem}.datepicker .datepicker-table .datepicker-header{display:table-header-group}.datepicker .datepicker-table .datepicker-header .datepicker-cell{color:#7a7a7a;font-weight:600}.datepicker .datepicker-table .datepicker-body{display:table-row-group}.datepicker .datepicker-table .datepicker-body .datepicker-row{display:table-row}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-unselectable{color:#b5b5b5}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-today{border:1px solid rgba(0,209,178,.5)}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable{color:#4a4a4a}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable:focus:not(.is-selected),.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable:hover:not(.is-selected){background-color:#f5f5f5;color:#0a0a0a;cursor:pointer}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selected{background-color:#00d1b2;color:#fff}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell{padding:.3rem .75rem .75rem}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event{position:relative}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events{bottom:.425rem;display:flex;justify-content:center;left:0;padding:0 .35rem;position:absolute;width:100%}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-white{background-color:#fff}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-black{background-color:#0a0a0a}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-light{background-color:#f5f5f5}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-dark{background-color:#363636}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-primary{background-color:#00d1b2}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-link{background-color:#3273dc}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-info{background-color:#209cee}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-success{background-color:#23d160}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-warning{background-color:#ffdd57}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-danger{background-color:#ff3860}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event.dots .event{border-radius:50%;height:.35em;margin:0 .1em;width:.35em}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event.bars .event{height:.25em;width:100%}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.is-selected{overflow:hidden}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.is-selected .events .event.is-primary{background-color:#1fffdd}.datepicker.is-small{border-radius:2px;font-size:.75rem}.datepicker.is-medium{font-size:1.25rem}.datepicker.is-large{font-size:1.5rem}@media screen and (min-width:769px) and (max-width:1087px){.datepicker .datepicker-table .datepicker-cell{padding:.75rem 1rem}}@media screen and (max-width:768px){.datepicker .datepicker-table .datepicker-cell{padding:.25rem .5rem}}.dialog .modal-card{max-width:460px;width:auto}.dialog .modal-card .modal-card-head{font-size:1.25rem;font-weight:600}.dialog .modal-card .modal-card-body .field{margin-top:16px}.dialog .modal-card .modal-card-body.is-titleless{border-top-left-radius:6px;border-top-right-radius:6px}.dialog .modal-card .modal-card-foot{justify-content:flex-end}.dialog .modal-card .modal-card-foot .button{display:inline;min-width:5em;font-weight:600}@media print,screen and (min-width:769px){.dialog .modal-card{min-width:320px}}.dialog.is-small .button,.dialog.is-small .input,.dialog.is-small .modal-card,.dialog.is-small .taginput .taginput-container.is-focusable,.taginput .dialog.is-small .taginput-container.is-focusable{border-radius:2px;font-size:.75rem}.dialog.is-medium .button,.dialog.is-medium .input,.dialog.is-medium .modal-card,.dialog.is-medium .taginput .taginput-container.is-focusable,.taginput .dialog.is-medium .taginput-container.is-focusable{font-size:1.25rem}.dialog.is-large .button,.dialog.is-large .input,.dialog.is-large .modal-card,.dialog.is-large .taginput .taginput-container.is-focusable,.taginput .dialog.is-large .taginput-container.is-focusable{font-size:1.5rem}.dropdown+.dropdown{margin-left:.5em}.dropdown .background{position:fixed;background-color:hsla(0,0%,4%,.86);z-index:10;cursor:pointer}@media screen and (min-width:1088px){.dropdown .background{display:none}}.dropdown .dropdown-menu .dropdown-item.is-disabled,.dropdown .dropdown-menu .has-link a.is-disabled{cursor:not-allowed}.dropdown .dropdown-menu .dropdown-item.is-disabled:hover,.dropdown .dropdown-menu .has-link a.is-disabled:hover{background:inherit;color:inherit}.dropdown .dropdown-menu .has-link a{padding-right:3rem;white-space:nowrap}.dropdown:not(.is-disabled) .dropdown-menu .dropdown-item.is-disabled,.dropdown:not(.is-disabled) .dropdown-menu .has-link a.is-disabled{opacity:.5}.dropdown .navbar-item{height:100%}.dropdown.is-disabled{opacity:.5;cursor:not-allowed}.dropdown.is-disabled .dropdown-trigger{pointer-events:none}.dropdown.is-inline .dropdown-menu{position:static;display:inline-block;padding:0}.dropdown.is-top-right .dropdown-menu{top:auto;bottom:100%}.dropdown.is-top-left .dropdown-menu{top:auto;bottom:100%;right:0;left:auto}.dropdown.is-bottom-left .dropdown-menu{right:0;left:auto}@media screen and (max-width:1087px){.dropdown.is-mobile-modal .dropdown-menu{position:fixed;width:calc(100vw - 40px);max-width:460px;max-height:calc(100vh - 120px);top:25%!important;left:50%!important;bottom:auto!important;right:auto!important;transform:translate3d(-50%,-25%,0);white-space:normal;overflow-y:auto}.dropdown.is-mobile-modal .dropdown-menu .dropdown-item,.dropdown.is-mobile-modal .dropdown-menu .has-link a{padding:1rem 1.5rem}}.label{font-weight:600}.field.is-grouped .field{flex-shrink:0}.field.is-grouped .field+.field{margin-left:.75rem}.field.is-grouped .field.is-expanded{flex-grow:1;flex-shrink:1}.field.has-addons .control:first-child .control .button,.field.has-addons .control:first-child .control .input,.field.has-addons .control:first-child .control .select select,.field.has-addons .control:first-child .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:first-child .control .taginput-container.is-focusable{border-bottom-left-radius:4px;border-top-left-radius:4px}.field.has-addons .control:last-child .control .button,.field.has-addons .control:last-child .control .input,.field.has-addons .control:last-child .control .select select,.field.has-addons .control:last-child .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:last-child .control .taginput-container.is-focusable{border-bottom-right-radius:4px;border-top-right-radius:4px}.field.has-addons .control .control .button,.field.has-addons .control .control .input,.field.has-addons .control .control .select select,.field.has-addons .control .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control .control .taginput-container.is-focusable{border-radius:0}.control .help.counter{float:right;margin-left:.5em}.control .icon.is-clickable{pointer-events:auto;cursor:pointer}.icon{cursor:inherit}.icon svg{background-color:transparent;fill:currentColor;stroke-width:0;stroke:currentColor;pointer-events:none;width:1.5rem;height:1.5rem}.loading-overlay{align-items:center;display:none;justify-content:center;overflow:hidden}.loading-overlay.is-active{display:flex}.loading-overlay.is-full-page{z-index:999;position:fixed}.loading-overlay.is-full-page .loading-icon:after{top:calc(50% - 2.5em);left:calc(50% - 2.5em);width:5em;height:5em}.loading-overlay .loading-background{background:#7f7f7f;background:hsla(0,0%,100%,.5)}.loading-overlay .loading-icon{position:relative}.loading-overlay .loading-icon:after{position:absolute;top:calc(50% - 1.5em);left:calc(50% - 1.5em);width:3em;height:3em;border-width:.25em}.message .media,.notification .media{padding-top:0;border:0}.notification>.delete{right:.5rem!important;top:.5rem!important}.modal .animation-content{margin:0 20px}.modal .animation-content .modal-card{margin:0}@media screen and (max-width:768px){.modal .animation-content{width:100%}}.notices{position:fixed;display:flex;top:0;bottom:0;left:0;right:0;padding:2em;overflow:hidden;z-index:1000;pointer-events:none}.notices .toast{display:inline-flex;animation-duration:.15s;margin:.5em 0;text-align:center;box-shadow:0 1px 4px rgba(0,0,0,.12),0 0 6px rgba(0,0,0,.04);border-radius:2em;padding:.75em 1.5em;pointer-events:auto;opacity:.92}.notices .toast.is-white{color:#0a0a0a;background:#fff}.notices .toast.is-black{color:#fff;background:#0a0a0a}.notices .toast.is-light{color:#363636;background:#f5f5f5}.notices .toast.is-dark{color:#f5f5f5;background:#363636}.notices .toast.is-primary{color:#fff;background:#00d1b2}.notices .toast.is-link{color:#fff;background:#3273dc}.notices .toast.is-info{color:#fff;background:#209cee}.notices .toast.is-success{color:#fff;background:#23d160}.notices .toast.is-warning{color:rgba(0,0,0,.7);background:#ffdd57}.notices .toast.is-danger{color:#fff;background:#ff3860}.notices .snackbar{display:inline-flex;align-items:center;justify-content:space-around;animation-duration:.15s;margin:.5em 0;box-shadow:0 1px 4px rgba(0,0,0,.12),0 0 6px rgba(0,0,0,.04);border-radius:4px;pointer-events:auto;background:#363636;color:#f5f5f5;min-height:3em}.notices .snackbar .text{padding:.5em 1em}.notices .snackbar .action{margin-left:auto;padding:.5em;padding-left:0}.notices .snackbar .action .button{font-weight:600;text-transform:uppercase}.notices .snackbar .action.is-white .button{color:#fff}.notices .snackbar .action.is-black .button{color:#0a0a0a}.notices .snackbar .action.is-light .button{color:#f5f5f5}.notices .snackbar .action.is-dark .button{color:#363636}.notices .snackbar .action.is-primary .button{color:#00d1b2}.notices .snackbar .action.is-link .button{color:#3273dc}.notices .snackbar .action.is-info .button{color:#209cee}.notices .snackbar .action.is-success .button{color:#23d160}.notices .snackbar .action.is-warning .button{color:#ffdd57}.notices .snackbar .action.is-danger .button{color:#ff3860}@media screen and (max-width:768px){.notices .snackbar{width:100%;margin:0;border-radius:0}}@media print,screen and (min-width:769px){.notices .snackbar{min-width:350px;max-width:600px;overflow:hidden}}.notices .snackbar.is-bottom,.notices .snackbar.is-top,.notices .toast.is-bottom,.notices .toast.is-top{align-self:center}.notices .snackbar.is-bottom-right,.notices .snackbar.is-top-right,.notices .toast.is-bottom-right,.notices .toast.is-top-right{align-self:flex-end}.notices .snackbar.is-bottom-left,.notices .snackbar.is-top-left,.notices .toast.is-bottom-left,.notices .toast.is-top-left{align-self:flex-start}.notices .snackbar.is-toast,.notices .toast.is-toast{opacity:.92}.notices.is-top{flex-direction:column}.notices.is-bottom{flex-direction:column-reverse}.notices.has-custom-container{position:absolute}@media screen and (max-width:768px){.notices{padding:0;position:fixed!important}}.pagination .pagination-next,.pagination .pagination-previous{padding-left:.25em;padding-right:.25em}.pagination .pagination-next.is-disabled,.pagination .pagination-previous.is-disabled{pointer-events:none;cursor:not-allowed;opacity:.5}.pagination.is-simple{justify-content:normal}.pagination .is-current{pointer-events:none;cursor:not-allowed}.panel .panel-heading.is-collapsible{cursor:pointer}.panel .panel-content{width:100%}.b-radio.radio{outline:none;display:inline-flex;align-items:center}.b-radio.radio+.radio{margin-left:.5em}.b-radio.radio input[type=radio]{position:absolute;left:0;opacity:0;outline:none;z-index:-1}.b-radio.radio input[type=radio]+.check{display:flex;align-items:center;justify-content:center;width:1.25em;height:1.25em;border:2px solid #7a7a7a;border-radius:50%;transition:background .15s ease-out}.b-radio.radio input[type=radio]+.check:before{content:\"\";border-radius:50%;width:.625em;height:.625em;background:#00d1b2;transform:scale(0);transition:transform .15s ease-out}.b-radio.radio input[type=radio]+.check.is-white:before{background:#fff}.b-radio.radio input[type=radio]+.check.is-black:before{background:#0a0a0a}.b-radio.radio input[type=radio]+.check.is-light:before{background:#f5f5f5}.b-radio.radio input[type=radio]+.check.is-dark:before{background:#363636}.b-radio.radio input[type=radio]+.check.is-primary:before{background:#00d1b2}.b-radio.radio input[type=radio]+.check.is-link:before{background:#3273dc}.b-radio.radio input[type=radio]+.check.is-info:before{background:#209cee}.b-radio.radio input[type=radio]+.check.is-success:before{background:#23d160}.b-radio.radio input[type=radio]+.check.is-warning:before{background:#ffdd57}.b-radio.radio input[type=radio]+.check.is-danger:before{background:#ff3860}.b-radio.radio input[type=radio]:checked+.check{border-color:#00d1b2}.b-radio.radio input[type=radio]:checked+.check.is-white{border-color:#fff}.b-radio.radio input[type=radio]:checked+.check.is-black{border-color:#0a0a0a}.b-radio.radio input[type=radio]:checked+.check.is-light{border-color:#f5f5f5}.b-radio.radio input[type=radio]:checked+.check.is-dark{border-color:#363636}.b-radio.radio input[type=radio]:checked+.check.is-primary{border-color:#00d1b2}.b-radio.radio input[type=radio]:checked+.check.is-link{border-color:#3273dc}.b-radio.radio input[type=radio]:checked+.check.is-info{border-color:#209cee}.b-radio.radio input[type=radio]:checked+.check.is-success{border-color:#23d160}.b-radio.radio input[type=radio]:checked+.check.is-warning{border-color:#ffdd57}.b-radio.radio input[type=radio]:checked+.check.is-danger{border-color:#ff3860}.b-radio.radio input[type=radio]:checked+.check:before{transform:scale(1)}.b-radio.radio .control-label{padding-left:.5em}.b-radio.radio[disabled]{opacity:.5}.b-radio.radio:hover input[type=radio]+.check{border-color:#00d1b2}.b-radio.radio:hover input[type=radio]+.check.is-white{border-color:#fff}.b-radio.radio:hover input[type=radio]+.check.is-black{border-color:#0a0a0a}.b-radio.radio:hover input[type=radio]+.check.is-light{border-color:#f5f5f5}.b-radio.radio:hover input[type=radio]+.check.is-dark{border-color:#363636}.b-radio.radio:hover input[type=radio]+.check.is-primary{border-color:#00d1b2}.b-radio.radio:hover input[type=radio]+.check.is-link{border-color:#3273dc}.b-radio.radio:hover input[type=radio]+.check.is-info{border-color:#209cee}.b-radio.radio:hover input[type=radio]+.check.is-success{border-color:#23d160}.b-radio.radio:hover input[type=radio]+.check.is-warning{border-color:#ffdd57}.b-radio.radio:hover input[type=radio]+.check.is-danger{border-color:#ff3860}.b-radio.radio:focus input[type=radio]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.b-radio.radio.is-small{border-radius:2px;font-size:.75rem}.b-radio.radio.is-medium{font-size:1.25rem}.b-radio.radio.is-large{font-size:1.5rem}.select select{padding-right:2.5em}.select select option{color:#4a4a4a;padding:.25em .5em}.select select option:disabled{cursor:not-allowed;opacity:.5}.select select optgroup{color:#b5b5b5;font-weight:400;font-style:normal;padding:.25em 0}.select.is-empty select{color:hsla(0,0%,48%,.7)}.switch{cursor:pointer;display:inline-flex;align-items:center}.switch+.switch{margin-left:.5em}.switch input[type=checkbox]{display:none}.switch input[type=checkbox]+.check{display:flex;align-items:center;flex-shrink:0;width:2.75em;height:1.575em;padding:.2em;background:#b5b5b5;border-radius:1em;transition:background .15s ease-out}.switch input[type=checkbox]+.check:before{content:\"\";border-radius:1em;width:1.175em;height:1.175em;background:#f5f5f5;box-shadow:0 3px 1px 0 rgba(0,0,0,.05),0 2px 2px 0 rgba(0,0,0,.1),0 3px 3px 0 rgba(0,0,0,.05);transition:transform .15s ease-out,width .15s ease-out;will-change:transform}.switch input[type=checkbox]+.check.is-elastic:before{width:1.75em}.switch input[type=checkbox]:checked+.check{background:#00d1b2}.switch input[type=checkbox]:checked+.check.is-white{background:#fff}.switch input[type=checkbox]:checked+.check.is-black{background:#0a0a0a}.switch input[type=checkbox]:checked+.check.is-light{background:#f5f5f5}.switch input[type=checkbox]:checked+.check.is-dark{background:#363636}.switch input[type=checkbox]:checked+.check.is-primary{background:#00d1b2}.switch input[type=checkbox]:checked+.check.is-link{background:#3273dc}.switch input[type=checkbox]:checked+.check.is-info{background:#209cee}.switch input[type=checkbox]:checked+.check.is-success{background:#23d160}.switch input[type=checkbox]:checked+.check.is-warning{background:#ffdd57}.switch input[type=checkbox]:checked+.check.is-danger{background:#ff3860}.switch input[type=checkbox]:checked+.check:before{transform:translate3d(100%,0,0)}.switch input[type=checkbox]:checked+.check.is-elastic:before{transform:translate3d(36.36364%,0,0)}.switch .control-label{padding-left:.5em}.switch:hover input[type=checkbox]+.check{background:hsla(0,0%,71%,.9)}.switch:hover input[type=checkbox]:checked+.check{background:rgba(0,209,178,.9)}.switch:hover input[type=checkbox]:checked+.check.is-white{background:hsla(0,0%,100%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-black{background:hsla(0,0%,4%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-light{background:hsla(0,0%,96%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-dark{background:rgba(54,54,54,.9)}.switch:hover input[type=checkbox]:checked+.check.is-primary{background:rgba(0,209,178,.9)}.switch:hover input[type=checkbox]:checked+.check.is-link{background:rgba(50,115,220,.9)}.switch:hover input[type=checkbox]:checked+.check.is-info{background:rgba(32,156,238,.9)}.switch:hover input[type=checkbox]:checked+.check.is-success{background:rgba(35,209,96,.9)}.switch:hover input[type=checkbox]:checked+.check.is-warning{background:rgba(255,221,87,.9)}.switch:hover input[type=checkbox]:checked+.check.is-danger{background:rgba(255,56,96,.9)}.switch:focus{outline:none}.switch:focus input[type=checkbox]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.6)}.switch:focus input[type=checkbox]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.switch:focus input[type=checkbox]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.switch:focus input[type=checkbox]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.switch:focus input[type=checkbox]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.switch:focus input[type=checkbox]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.switch:focus input[type=checkbox]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.switch:focus input[type=checkbox]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.switch:focus input[type=checkbox]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.switch.is-small{border-radius:2px;font-size:.75rem}.switch.is-medium{font-size:1.25rem}.switch.is-large{font-size:1.5rem}.switch[disabled]{opacity:.5;cursor:not-allowed;color:#7a7a7a}.table-wrapper .table{margin-bottom:0}.table-wrapper:not(:last-child){margin-bottom:1.5rem}@media screen and (max-width:1087px){.table-wrapper{overflow-x:auto}}.b-table{transition:opacity 86ms ease-out}@media print,screen and (min-width:769px){.b-table .table-mobile-sort{display:none}}.b-table .icon{transition:transform .15s ease-out,opacity 86ms ease-out}.b-table .icon.is-desc{transform:rotate(180deg)}.b-table .icon.is-expanded{transform:rotate(90deg)}.b-table .table{width:100%;border:1px solid transparent;border-radius:4px;border-collapse:separate}.b-table .table th{font-weight:600}.b-table .table th .th-wrap{display:flex;align-items:center}.b-table .table th .th-wrap .icon{margin-left:.5rem;margin-right:0;font-size:1rem}.b-table .table th .th-wrap.is-numeric{flex-direction:row-reverse;text-align:right}.b-table .table th .th-wrap.is-numeric .icon{margin-left:0;margin-right:.5rem}.b-table .table th .th-wrap.is-centered{justify-content:center;text-align:center}.b-table .table th.is-current-sort{border-color:#7a7a7a;font-weight:700}.b-table .table th.is-sortable:hover{border-color:#7a7a7a}.b-table .table th.is-sortable,.b-table .table th.is-sortable .th-wrap{cursor:pointer}.b-table .table tr.is-selected .checkbox input:checked+.check{background:#fff url(\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%2300d1b2'/%3E%3C/svg%3E\") no-repeat 50%}.b-table .table tr.is-selected .checkbox input+.check{border-color:#fff}.b-table .table tr.is-empty:hover{background-color:transparent}.b-table .table .chevron-cell{vertical-align:middle}.b-table .table .checkbox-cell{width:40px}.b-table .table .checkbox-cell .checkbox{vertical-align:middle}.b-table .table .checkbox-cell .checkbox .check{transition:none}.b-table .table tr.detail{box-shadow:inset 0 1px 3px #dbdbdb;background:#fafafa}.b-table .table tr.detail .detail-container{padding:1rem}.b-table .table:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.b-table .table.is-bordered th.is-current-sort,.b-table .table.is-bordered th.is-sortable:hover{border-color:#dbdbdb;background:#f5f5f5}@media screen and (max-width:768px){.b-table .table.has-mobile-cards thead{display:none}.b-table .table.has-mobile-cards tfoot th{border:0;display:inherit}.b-table .table.has-mobile-cards tr{box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);max-width:100%;position:relative;display:block}.b-table .table.has-mobile-cards tr td{border:0;display:inherit}.b-table .table.has-mobile-cards tr td:last-child{border-bottom:0}.b-table .table.has-mobile-cards tr:not(:last-child){margin-bottom:1rem}.b-table .table.has-mobile-cards tr:not([class*=is-]){background:inherit}.b-table .table.has-mobile-cards tr:not([class*=is-]):hover{background-color:inherit}.b-table .table.has-mobile-cards tr.detail{margin-top:-1rem}.b-table .table.has-mobile-cards tr:not(.detail):not(.is-empty):not(.table-footer) td{display:flex;width:auto;justify-content:space-between;text-align:right;border-bottom:1px solid #f5f5f5}.b-table .table.has-mobile-cards tr:not(.detail):not(.is-empty):not(.table-footer) td:before{content:attr(data-label);font-weight:600;padding-right:.5em;text-align:left}}.b-table .level{padding-bottom:1.5rem}.b-table.is-loading{position:relative;pointer-events:none;opacity:.5}.b-table.is-loading:after{position:absolute;top:4em;left:calc(50% - 2.5em);width:5em;height:5em;border-width:.25em}.b-tabs .tabs{margin-bottom:0;flex-shrink:0}.b-tabs .is-disabled{pointer-events:none;cursor:not-allowed;opacity:.5}.b-tabs .tab-content{position:relative;overflow:hidden;display:flex;flex-direction:column;padding:1rem}.b-tabs .tab-content .tab-item{flex-shrink:0;flex-basis:auto}.b-tabs:not(:last-child){margin-bottom:1.5rem}.b-tabs.is-fullwidth{width:100%}.tag .has-ellipsis{max-width:10em;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.taginput .taginput-container.is-focusable{padding-bottom:0;padding-top:calc(.275em - 1px);align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start;height:auto;cursor:text}.taginput .taginput-container>.tag,.taginput .taginput-container>.tags{margin-bottom:calc(.275em - 1px);font-size:.9em;height:1.7em}.taginput .taginput-container>.tag .tag,.taginput .taginput-container>.tags .tag{margin-bottom:0;font-size:.9em;height:1.7em}.taginput .taginput-container>.tag:not(:last-child),.taginput .taginput-container>.tags:not(:last-child){margin-right:.275rem}.taginput .taginput-container .autocomplete{flex:1}.taginput .taginput-container .autocomplete input{height:1.7em;margin-bottom:calc(.275em - 1px);padding-top:0;padding-bottom:0;border:none;box-shadow:none;min-width:8em}.taginput .taginput-container .autocomplete input:focus{box-shadow:none!important}.taginput .taginput-container .autocomplete .icon{height:1.7em}.taginput .taginput-container .autocomplete>.control.is-loading:after{top:.375em}.timepicker .dropdown,.timepicker .dropdown-trigger{width:100%}.dropdown .dropdown-menu .has-link .timepicker a,.timepicker .dropdown-item,.timepicker .dropdown .dropdown-menu .has-link a{font-size:inherit}.timepicker .timepicker-footer{padding-top:.875rem}.timepicker .control .select{font-size:2.5rem}.timepicker .control .select select{font-weight:600;padding-right:calc(.625em - 1px)}.timepicker .control .select select option{font-size:1.5rem}.timepicker .control .select select option:disabled{color:hsla(0,0%,48%,.7)}.timepicker .control .select:after{display:none}.timepicker .control:first-child .select select{border-right:none}.timepicker .control:first-child .select:before{content:\":\";color:#b5b5b5;display:inline-block;position:absolute;z-index:6;font-size:3rem;right:-.325rem;top:.25rem}.timepicker .control:nth-child(2) .select select{border-left:none}.timepicker.is-small{border-radius:2px;font-size:.75rem}.timepicker.is-medium{font-size:1.25rem}.timepicker.is-large{font-size:1.5rem}.tooltip{position:relative;display:inline-flex}.tooltip.is-top:after,.tooltip.is-top:before{top:auto;right:auto;bottom:calc(100% + 5px + 2px);left:50%;transform:translateX(-50%)}.tooltip.is-top.is-white:before{border-top:5px solid #fff}.tooltip.is-top.is-black:before,.tooltip.is-top.is-white:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-black:before{border-top:5px solid #0a0a0a}.tooltip.is-top.is-light:before{border-top:5px solid #f5f5f5}.tooltip.is-top.is-dark:before,.tooltip.is-top.is-light:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-dark:before{border-top:5px solid #363636}.tooltip.is-top.is-primary:before{border-top:5px solid #00d1b2}.tooltip.is-top.is-link:before,.tooltip.is-top.is-primary:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-link:before{border-top:5px solid #3273dc}.tooltip.is-top.is-info:before{border-top:5px solid #209cee}.tooltip.is-top.is-info:before,.tooltip.is-top.is-success:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-success:before{border-top:5px solid #23d160}.tooltip.is-top.is-warning:before{border-top:5px solid #ffdd57}.tooltip.is-top.is-danger:before,.tooltip.is-top.is-warning:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-danger:before{border-top:5px solid #ff3860}.tooltip.is-top.is-multiline.is-small:after{width:180px}.tooltip.is-top.is-multiline.is-medium:after{width:240px}.tooltip.is-top.is-multiline.is-large:after{width:300px}.tooltip.is-right:after,.tooltip.is-right:before{top:50%;right:auto;bottom:auto;left:calc(100% + 5px + 2px);transform:translateY(-50%)}.tooltip.is-right.is-white:before{border-right:5px solid #fff}.tooltip.is-right.is-black:before,.tooltip.is-right.is-white:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-black:before{border-right:5px solid #0a0a0a}.tooltip.is-right.is-light:before{border-right:5px solid #f5f5f5}.tooltip.is-right.is-dark:before,.tooltip.is-right.is-light:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-dark:before{border-right:5px solid #363636}.tooltip.is-right.is-primary:before{border-right:5px solid #00d1b2}.tooltip.is-right.is-link:before,.tooltip.is-right.is-primary:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-link:before{border-right:5px solid #3273dc}.tooltip.is-right.is-info:before{border-right:5px solid #209cee}.tooltip.is-right.is-info:before,.tooltip.is-right.is-success:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-success:before{border-right:5px solid #23d160}.tooltip.is-right.is-warning:before{border-right:5px solid #ffdd57}.tooltip.is-right.is-danger:before,.tooltip.is-right.is-warning:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-danger:before{border-right:5px solid #ff3860}.tooltip.is-right.is-multiline.is-small:after{width:180px}.tooltip.is-right.is-multiline.is-medium:after{width:240px}.tooltip.is-right.is-multiline.is-large:after{width:300px}.tooltip.is-bottom:after,.tooltip.is-bottom:before{top:calc(100% + 5px + 2px);right:auto;bottom:auto;left:50%;transform:translateX(-50%)}.tooltip.is-bottom.is-white:before{border-bottom:5px solid #fff}.tooltip.is-bottom.is-black:before,.tooltip.is-bottom.is-white:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-black:before{border-bottom:5px solid #0a0a0a}.tooltip.is-bottom.is-light:before{border-bottom:5px solid #f5f5f5}.tooltip.is-bottom.is-dark:before,.tooltip.is-bottom.is-light:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-dark:before{border-bottom:5px solid #363636}.tooltip.is-bottom.is-primary:before{border-bottom:5px solid #00d1b2}.tooltip.is-bottom.is-link:before,.tooltip.is-bottom.is-primary:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-link:before{border-bottom:5px solid #3273dc}.tooltip.is-bottom.is-info:before{border-bottom:5px solid #209cee}.tooltip.is-bottom.is-info:before,.tooltip.is-bottom.is-success:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-success:before{border-bottom:5px solid #23d160}.tooltip.is-bottom.is-warning:before{border-bottom:5px solid #ffdd57}.tooltip.is-bottom.is-danger:before,.tooltip.is-bottom.is-warning:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-danger:before{border-bottom:5px solid #ff3860}.tooltip.is-bottom.is-multiline.is-small:after{width:180px}.tooltip.is-bottom.is-multiline.is-medium:after{width:240px}.tooltip.is-bottom.is-multiline.is-large:after{width:300px}.tooltip.is-left:after,.tooltip.is-left:before{top:50%;right:calc(100% + 5px + 2px);bottom:auto;left:auto;transform:translateY(-50%)}.tooltip.is-left.is-white:before{border-left:5px solid #fff}.tooltip.is-left.is-black:before,.tooltip.is-left.is-white:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-black:before{border-left:5px solid #0a0a0a}.tooltip.is-left.is-light:before{border-left:5px solid #f5f5f5}.tooltip.is-left.is-dark:before,.tooltip.is-left.is-light:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-dark:before{border-left:5px solid #363636}.tooltip.is-left.is-primary:before{border-left:5px solid #00d1b2}.tooltip.is-left.is-link:before,.tooltip.is-left.is-primary:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-link:before{border-left:5px solid #3273dc}.tooltip.is-left.is-info:before{border-left:5px solid #209cee}.tooltip.is-left.is-info:before,.tooltip.is-left.is-success:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-success:before{border-left:5px solid #23d160}.tooltip.is-left.is-warning:before{border-left:5px solid #ffdd57}.tooltip.is-left.is-danger:before,.tooltip.is-left.is-warning:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-danger:before{border-left:5px solid #ff3860}.tooltip.is-left.is-multiline.is-small:after{width:180px}.tooltip.is-left.is-multiline.is-medium:after{width:240px}.tooltip.is-left.is-multiline.is-large:after{width:300px}.tooltip:after,.tooltip:before{position:absolute;content:\"\";opacity:0;visibility:hidden;pointer-events:none}.tooltip:before{z-index:889}.tooltip:after{content:attr(data-label);width:auto;padding:.35rem .75rem;border-radius:6px;font-size:.85rem;font-weight:400;box-shadow:0 1px 2px 1px rgba(0,1,0,.2);z-index:888;white-space:nowrap}.tooltip:not([data-label=\"\"]):hover:after,.tooltip:not([data-label=\"\"]):hover:before{opacity:1;visibility:visible}.tooltip.is-white:after{background:#fff;color:#0a0a0a}.tooltip.is-black:after{background:#0a0a0a;color:#fff}.tooltip.is-light:after{background:#f5f5f5;color:#363636}.tooltip.is-dark:after{background:#363636;color:#f5f5f5}.tooltip.is-primary:after{background:#00d1b2;color:#fff}.tooltip.is-link:after{background:#3273dc;color:#fff}.tooltip.is-info:after{background:#209cee;color:#fff}.tooltip.is-success:after{background:#23d160;color:#fff}.tooltip.is-warning:after{background:#ffdd57;color:rgba(0,0,0,.7)}.tooltip.is-danger:after{background:#ff3860;color:#fff}.tooltip:not([data-label=\"\"]).is-always:after,.tooltip:not([data-label=\"\"]).is-always:before{opacity:1;visibility:visible}.tooltip.is-multiline:after{display:flex-block;text-align:center;white-space:normal}.tooltip.is-dashed{border-bottom:1px dashed #b5b5b5;cursor:default}.tooltip.is-square:after{border-radius:0}.tooltip.is-animated:after,.tooltip.is-animated:before{transition:opacity 86ms ease-out,visibility 86ms ease-out}.upload{position:relative}.upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0;outline:none;z-index:-1}.upload .upload-draggable{display:inline-block;cursor:pointer;padding:.25em;border:1px dashed #b5b5b5;border-radius:6px}.upload .upload-draggable.is-disabled{opacity:.5;cursor:not-allowed}.upload .upload-draggable.is-loading{position:relative;pointer-events:none;opacity:.5}.upload .upload-draggable.is-loading:after{top:0;left:calc(50% - 1.5em);width:3em;height:3em;border-width:.25em}.upload .upload-draggable.is-hovered.is-white,.upload .upload-draggable:hover.is-white{border-color:#fff;background:hsla(0,0%,100%,.05)}.upload .upload-draggable.is-hovered.is-black,.upload .upload-draggable:hover.is-black{border-color:#0a0a0a;background:hsla(0,0%,4%,.05)}.upload .upload-draggable.is-hovered.is-light,.upload .upload-draggable:hover.is-light{border-color:#f5f5f5;background:hsla(0,0%,96%,.05)}.upload .upload-draggable.is-hovered.is-dark,.upload .upload-draggable:hover.is-dark{border-color:#363636;background:rgba(54,54,54,.05)}.upload .upload-draggable.is-hovered.is-primary,.upload .upload-draggable:hover.is-primary{border-color:#00d1b2;background:rgba(0,209,178,.05)}.upload .upload-draggable.is-hovered.is-link,.upload .upload-draggable:hover.is-link{border-color:#3273dc;background:rgba(50,115,220,.05)}.upload .upload-draggable.is-hovered.is-info,.upload .upload-draggable:hover.is-info{border-color:#209cee;background:rgba(32,156,238,.05)}.upload .upload-draggable.is-hovered.is-success,.upload .upload-draggable:hover.is-success{border-color:#23d160;background:rgba(35,209,96,.05)}.upload .upload-draggable.is-hovered.is-warning,.upload .upload-draggable:hover.is-warning{border-color:#ffdd57;background:rgba(255,221,87,.05)}.upload .upload-draggable.is-hovered.is-danger,.upload .upload-draggable:hover.is-danger{border-color:#ff3860;background:rgba(255,56,96,.05)}",""])},function(e,t){e.exports=function(e,t){for(var i=[],n={},o=0;o<t.length;o++){var a=t[o],r=a[0],s=a[1],l=a[2],c=a[3],d={id:e+":"+o,css:s,media:l,sourceMap:c};n[r]?n[r].parts.push(d):i.push(n[r]={id:r,parts:[d]})}return i}},function(e,t,i){var n=i(25);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("bf15a564",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,".material-design-icon{display:inline-flex;align-self:center;position:relative;height:1em;width:1em;>.material-design-icon__svg{height:1em;width:1em;fill:currentColor;position:absolute;bottom:-.125em}}",""])},function(e,t,i){"use strict";function n(e){i(27)}var o=i(6),a=i(64),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(28);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("0abd6ea6",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";function n(e){i(30)}var o=i(7),a=i(32),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(31);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("432a0e31",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("nav",{staticClass:"navbar has-shadow"},[e._m(0),e._v(" "),i("div",{staticClass:"navbar-menu"},[i("div",{staticClass:"navbar-start"}),e._v(" "),i("div",{staticClass:"navbar-end"},[i("div",{staticClass:"navbar-item"},[i("p",{staticClass:"control"},[e.isConnected?i("button",{staticClass:"button is-danger",on:{click:e.disconnect}},[e._v("Disconnect")]):i("button",{staticClass:"button is-success",on:{click:e.connect}},[e._v("Connect")])])])])])])},o=[function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"navbar-brand"},[i("a",{staticClass:"navbar-item",attrs:{href:"#"}},[e._v("WebRender Debugger")])])}],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";function n(e){i(34)}var o=i(8),a=i(36),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(35);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("e4edc9ba",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("aside",{staticClass:"menu"},[i("p",{staticClass:"menu-label"},[e._v("\n Pages\n ")]),e._v(" "),i("ul",{staticClass:"menu-list"},[i("li",[i("a",{class:{"is-active":"options"==e.page},on:{click:function(t){return e.setPage("options")}}},[e._v("Debug Options")])]),e._v(" "),i("li",[i("a",{class:{"is-active":"passes"==e.page},on:{click:function(t){return e.setPage("passes")}}},[e._v("Passes")])]),e._v(" "),i("li",[i("a",{class:{"is-active":"render_tasks"==e.page},on:{click:function(t){return e.setPage("render_tasks")}}},[e._v("Render Tasks")])]),e._v(" "),i("li",[i("a",{class:{"is-active":"documents"==e.page},on:{click:function(t){return e.setPage("documents")}}},[e._v("Documents")])]),e._v(" "),i("li",[i("a",{class:{"is-active":"clip_scroll_tree"==e.page},on:{click:function(t){return e.setPage("clip_scroll_tree")}}},[e._v("Clip-Scroll Tree")])]),e._v(" "),i("li",[i("a",{class:{"is-active":"screenshot"==e.page},on:{click:function(t){return e.setPage("screenshot")}}},[e._v("Screenshot")])])])])},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";function n(e){i(38)}var o=i(9),a=i(40),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(39);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("0e6ffcbb",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"box"},[i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setProfiler(t.target.checked)}}}),e._v("\n Profiler\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setTextureCacheDebugger(t.target.checked)}}}),e._v("\n Texture cache debugger\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setRenderTargetDebugger(t.target.checked)}}}),e._v("\n Render target debugger\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setAlphaRectsDebugger(t.target.checked)}}}),e._v("\n Alpha primitive rects debugger\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setGpuTimeQueries(t.target.checked)}}}),e._v("\n Enable GPU time queries\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setGpuSampleQueries(t.target.checked)}}}),e._v("\n Enable GPU sample queries\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setOpaquePass(!t.target.checked)}}}),e._v("\n Disable opaque pass\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setAlphaPass(!t.target.checked)}}}),e._v("\n Disable alpha pass\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setClipMasks(!t.target.checked)}}}),e._v("\n Disable clip masks\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setTextPrims(!t.target.checked)}}}),e._v("\n Disable text primitives\n ")])]),e._v(" "),i("div",{staticClass:"field"},[i("label",{staticClass:"checkbox"},[i("input",{attrs:{type:"checkbox",disabled:e.disabled},on:{click:function(t){return e.setGradientPrims(!t.target.checked)}}}),e._v("\n Disable gradient primitives\n ")])])])},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";function n(e){i(42)}var o=i(10),a=i(44),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(43);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("05673d18",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"box"},[i("h1",{staticClass:"title"},[e._v("Passes "),i("a",{staticClass:"button is-info",attrs:{disabled:e.disabled},on:{click:e.fetch}},[e._v("Refresh")])]),e._v(" "),i("hr"),e._v(" "),e._l(e.passes,function(t,n){return i("div",[i("p",{staticClass:"has-text-black-bis"},[e._v("Pass "+e._s(n))]),e._v(" "),e._l(t.targets,function(t,n){return i("div",[i("p",{staticClass:"has-text-grey-dark",staticStyle:{"text-indent":"2em"}},[e._v("Target "+e._s(n)+" ("+e._s(t.kind)+")")]),e._v(" "),e._l(t.batches,function(t,n){return i("div",[i("p",{staticClass:"has-text-grey",staticStyle:{"text-indent":"4em"}},[e._v("Batch "+e._s(n)+" ("+e._s(t.description)+", "+e._s(t.kind)+", "+e._s(t.count)+" instances)")])])})],2)}),e._v(" "),i("hr")],2)})],2)},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";function n(e){i(46)}var o=i(11),a=i(51),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(47);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("0d80f44e",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){var n=i(49);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("77142b62",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("li",[i("div",{on:{click:e.toggle}},[e.isFolder?i("span",[e._v("["+e._s(e.open?"-":"+")+"]")]):e._e(),e._v("\n "+e._s(e.model.description)+"\n ")]),e._v(" "),e.isFolder?i("ul",{directives:[{name:"show",rawName:"v-show",value:e.open,expression:"open"}],staticStyle:{"padding-left":"1em","line-height":"1.5em"}},e._l(e.model.children,function(e){return i("treeview",{attrs:{model:e}})}),1):e._e()])},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"box"},[i("h1",{staticClass:"title"},[e._v("Render Tasks "),i("a",{staticClass:"button is-info",attrs:{disabled:e.disabled},on:{click:e.fetch}},[e._v("Refresh")])]),e._v(" "),i("hr"),e._v(" "),i("div",[i("ul",[i("app-treeview",{attrs:{model:e.render_tasks}})],1)])])},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";function n(e){i(53)}var o=i(13),a=i(55),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(54);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("7b986bc8",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"box"},[i("h1",{staticClass:"title"},[e._v("Documents "),i("a",{staticClass:"button is-info",attrs:{disabled:e.disabled},on:{click:e.fetch}},[e._v("Refresh")])]),e._v(" "),i("hr"),e._v(" "),i("div",[i("ul",[i("app-treeview",{attrs:{model:e.documents}})],1)])])},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";function n(e){i(57)}var o=i(14),a=i(59),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(58);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("3d63fa5b",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"box"},[i("h1",{staticClass:"title"},[e._v("Clip-Scroll Tree "),i("a",{staticClass:"button is-info",attrs:{disabled:e.disabled},on:{click:e.fetch}},[e._v("Refresh")])]),e._v(" "),i("hr"),e._v(" "),i("div",[i("ul",[i("app-treeview",{attrs:{model:e.clip_scroll_tree}})],1)])])},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";function n(e){i(61)}var o=i(15),a=i(63),r=i(2),s=n,l=r(o.a,a.a,!1,s,null,null);t.a=l.exports},function(e,t,i){var n=i(62);"string"==typeof n&&(n=[[e.i,n,""]]),n.locals&&(e.exports=n.locals);i(1)("3900c1de",n,!0,{})},function(e,t,i){t=e.exports=i(0)(!1),t.push([e.i,"",""])},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"box"},[i("h1",{staticClass:"title"},[e._v("Screenshot "),i("a",{staticClass:"button is-info",attrs:{disabled:e.disabled},on:{click:e.fetch}},[e._v("Refresh")])]),e._v(" "),i("hr"),e._v(" "),i("div",[i("ul",[e.screenshot.length>0?i("img",{staticStyle:{transform:"scaleY(-1)",width:"1024px",height:"768px"},attrs:{src:"data:image/png;base64,"+e.screenshot}}):e._e()])])])},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";var n=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",[i("app-navbar"),e._v(" "),i("div",{staticClass:"section"},[i("div",{staticClass:"container"},[i("div",{staticClass:"columns"},[i("div",{staticClass:"column is-3"},[i("app-navmenu")],1),e._v(" "),i("div",{staticClass:"column"},["options"==e.page?i("app-options"):e._e(),e._v(" "),"passes"==e.page?i("app-passview"):e._e(),e._v(" "),"render_tasks"==e.page?i("app-rendertaskview"):e._e(),e._v(" "),"documents"==e.page?i("app-documentview"):e._e(),e._v(" "),"clip_scroll_tree"==e.page?i("app-clipscrolltreeview"):e._e(),e._v(" "),"screenshot"==e.page?i("app-screenshotview"):e._e()],1)])])])],1)},o=[],a={render:n,staticRenderFns:o};t.a=a},function(e,t,i){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var o=i(3),a=i(66),r=function(){function e(e,t){for(var i=0;i<t.length;i++){var n=t[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,i,n){return i&&e(t.prototype,i),n&&e(t,n),t}}();o.default.use(a.a);var s=function(){function e(){n(this,e),this.ws=null}return r(e,[{key:"connect",value:function(e){var t=new WebSocket("ws://127.0.0.1:3583");t.onopen=function(){e.commit("setConnected",!0)},t.onmessage=function(t){var i=JSON.parse(t.data);"passes"==i.kind?e.commit("setPasses",i.passes):"render_tasks"==i.kind?e.commit("setRenderTasks",i.root):"documents"==i.kind?e.commit("setDocuments",i.root):"clip_scroll_tree"==i.kind?e.commit("setClipScrollTree",i.root):"screenshot"==i.kind?e.commit("setScreenshot",i.data):console.warn("unknown message kind: "+i.kind)},t.onclose=function(){e.commit("setConnected",!1)},this.ws=t}},{key:"send",value:function(e){null!==this.ws&&this.ws.send(e)}},{key:"disconnect",value:function(){null!==this.ws&&(this.ws.close(),this.ws=null)}}]),e}(),l=new s,c=new a.a.Store({strict:!0,state:{connected:!1,page:"options",passes:[],render_tasks:[],documents:[],clip_scroll_tree:[],screenshot:[]},mutations:{setConnected:function(e,t){e.connected=t},setPage:function(e,t){e.page=t},setPasses:function(e,t){e.passes=t},setRenderTasks:function(e,t){e.render_tasks=t},setDocuments:function(e,t){e.documents=t},setClipScrollTree:function(e,t){e.clip_scroll_tree=t},setScreenshot:function(e,t){e.screenshot=t}},actions:{connect:function(e){l.connect(e)},disconnect:function(e){l.disconnect()},sendMessage:function(e,t){l.send(t)}}});t.a=c},function(e,t,i){"use strict";/** + * vuex v3.1.0 + * (c) 2019 Evan You + * @license MIT + */ +function n(e){function t(){var e=this.$options;e.store?this.$store="function"==typeof e.store?e.store():e.store:e.parent&&e.parent.$store&&(this.$store=e.parent.$store)}if(Number(e.version.split(".")[0])>=2)e.mixin({beforeCreate:t});else{var i=e.prototype._init;e.prototype._init=function(e){void 0===e&&(e={}),e.init=e.init?[t].concat(e.init):t,i.call(this,e)}}}function o(e){S&&(e._devtoolHook=S,S.emit("vuex:init",e),S.on("vuex:travel-to-state",function(t){e.replaceState(t)}),e.subscribe(function(e,t){S.emit("vuex:mutation",e,t)}))}function a(e,t){Object.keys(e).forEach(function(i){return t(e[i],i)})}function r(e){return null!==e&&"object"==typeof e}function s(e){return e&&"function"==typeof e.then}function l(e,t,i){if(t.update(i),i.modules)for(var n in i.modules){if(!t.getChild(n))return;l(e.concat(n),t.getChild(n),i.modules[n])}}function c(e,t){return t.indexOf(e)<0&&t.push(e),function(){var i=t.indexOf(e);i>-1&&t.splice(i,1)}}function d(e,t){e._actions=Object.create(null),e._mutations=Object.create(null),e._wrappedGetters=Object.create(null),e._modulesNamespaceMap=Object.create(null);var i=e.state;f(e,i,[],e._modules.root,!0),u(e,i,t)}function u(e,t,i){var n=e._vm;e.getters={};var o=e._wrappedGetters,r={};a(o,function(t,i){r[i]=function(){return t(e)},Object.defineProperty(e.getters,i,{get:function(){return e._vm[i]},enumerable:!0})});var s=O.config.silent;O.config.silent=!0,e._vm=new O({data:{$$state:t},computed:r}),O.config.silent=s,e.strict&&v(e),n&&(i&&e._withCommit(function(){n._data.$$state=null}),O.nextTick(function(){return n.$destroy()}))}function f(e,t,i,n,o){var a=!i.length,r=e._modules.getNamespace(i);if(n.namespaced&&(e._modulesNamespaceMap[r]=n),!a&&!o){var s=k(t,i.slice(0,-1)),l=i[i.length-1];e._withCommit(function(){O.set(s,l,n.state)})}var c=n.context=p(e,r,i);n.forEachMutation(function(t,i){b(e,r+i,t,c)}),n.forEachAction(function(t,i){var n=t.root?i:r+i,o=t.handler||t;m(e,n,o,c)}),n.forEachGetter(function(t,i){g(e,r+i,t,c)}),n.forEachChild(function(n,a){f(e,t,i.concat(a),n,o)})}function p(e,t,i){var n=""===t,o={dispatch:n?e.dispatch:function(i,n,o){var a=x(i,n,o),r=a.payload,s=a.options,l=a.type;return s&&s.root||(l=t+l),e.dispatch(l,r)},commit:n?e.commit:function(i,n,o){var a=x(i,n,o),r=a.payload,s=a.options,l=a.type;s&&s.root||(l=t+l),e.commit(l,r,s)}};return Object.defineProperties(o,{getters:{get:n?function(){return e.getters}:function(){return h(e,t)}},state:{get:function(){return k(e.state,i)}}}),o}function h(e,t){var i={},n=t.length;return Object.keys(e.getters).forEach(function(o){if(o.slice(0,n)===t){var a=o.slice(n);Object.defineProperty(i,a,{get:function(){return e.getters[o]},enumerable:!0})}}),i}function b(e,t,i,n){(e._mutations[t]||(e._mutations[t]=[])).push(function(t){i.call(e,n.state,t)})}function m(e,t,i,n){(e._actions[t]||(e._actions[t]=[])).push(function(t,o){var a=i.call(e,{dispatch:n.dispatch,commit:n.commit,getters:n.getters,state:n.state,rootGetters:e.getters,rootState:e.state},t,o);return s(a)||(a=Promise.resolve(a)),e._devtoolHook?a.catch(function(t){throw e._devtoolHook.emit("vuex:error",t),t}):a})}function g(e,t,i,n){e._wrappedGetters[t]||(e._wrappedGetters[t]=function(e){return i(n.state,n.getters,e.state,e.getters)})}function v(e){e._vm.$watch(function(){return this._data.$$state},function(){},{deep:!0,sync:!0})}function k(e,t){return t.length?t.reduce(function(e,t){return e[t]},e):e}function x(e,t,i){return r(e)&&e.type&&(i=t,t=e,e=e.type),{type:e,payload:t,options:i}}function w(e){O&&e===O||(O=e,n(O))}function y(e){return Array.isArray(e)?e.map(function(e){return{key:e,val:e}}):Object.keys(e).map(function(t){return{key:t,val:e[t]}})}function _(e){return function(t,i){return"string"!=typeof t?(i=t,t=""):"/"!==t.charAt(t.length-1)&&(t+="/"),e(t,i)}}function C(e,t,i){return e._modulesNamespaceMap[i]}var S="undefined"!=typeof window&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__,$=function(e,t){this.runtime=t,this._children=Object.create(null),this._rawModule=e;var i=e.state;this.state=("function"==typeof i?i():i)||{}},D={namespaced:{configurable:!0}};D.namespaced.get=function(){return!!this._rawModule.namespaced},$.prototype.addChild=function(e,t){this._children[e]=t},$.prototype.removeChild=function(e){delete this._children[e]},$.prototype.getChild=function(e){return this._children[e]},$.prototype.update=function(e){this._rawModule.namespaced=e.namespaced,e.actions&&(this._rawModule.actions=e.actions),e.mutations&&(this._rawModule.mutations=e.mutations),e.getters&&(this._rawModule.getters=e.getters)},$.prototype.forEachChild=function(e){a(this._children,e)},$.prototype.forEachGetter=function(e){this._rawModule.getters&&a(this._rawModule.getters,e)},$.prototype.forEachAction=function(e){this._rawModule.actions&&a(this._rawModule.actions,e)},$.prototype.forEachMutation=function(e){this._rawModule.mutations&&a(this._rawModule.mutations,e)},Object.defineProperties($.prototype,D);var z=function(e){this.register([],e,!1)};z.prototype.get=function(e){return e.reduce(function(e,t){return e.getChild(t)},this.root)},z.prototype.getNamespace=function(e){var t=this.root;return e.reduce(function(e,i){return t=t.getChild(i),e+(t.namespaced?i+"/":"")},"")},z.prototype.update=function(e){l([],this.root,e)},z.prototype.register=function(e,t,i){var n=this;void 0===i&&(i=!0);var o=new $(t,i);if(0===e.length)this.root=o;else{this.get(e.slice(0,-1)).addChild(e[e.length-1],o)}t.modules&&a(t.modules,function(t,o){n.register(e.concat(o),t,i)})},z.prototype.unregister=function(e){var t=this.get(e.slice(0,-1)),i=e[e.length-1];t.getChild(i).runtime&&t.removeChild(i)};var O,A=function(e){var t=this;void 0===e&&(e={}),!O&&"undefined"!=typeof window&&window.Vue&&w(window.Vue);var i=e.plugins;void 0===i&&(i=[]);var n=e.strict;void 0===n&&(n=!1),this._committing=!1,this._actions=Object.create(null),this._actionSubscribers=[],this._mutations=Object.create(null),this._wrappedGetters=Object.create(null),this._modules=new z(e),this._modulesNamespaceMap=Object.create(null),this._subscribers=[],this._watcherVM=new O;var a=this,r=this,s=r.dispatch,l=r.commit;this.dispatch=function(e,t){return s.call(a,e,t)},this.commit=function(e,t,i){return l.call(a,e,t,i)},this.strict=n;var c=this._modules.root.state;f(this,c,[],this._modules.root),u(this,c),i.forEach(function(e){return e(t)}),(void 0!==e.devtools?e.devtools:O.config.devtools)&&o(this)},T={state:{configurable:!0}};T.state.get=function(){return this._vm._data.$$state},T.state.set=function(e){},A.prototype.commit=function(e,t,i){var n=this,o=x(e,t,i),a=o.type,r=o.payload,s=(o.options,{type:a,payload:r}),l=this._mutations[a];l&&(this._withCommit(function(){l.forEach(function(e){e(r)})}),this._subscribers.forEach(function(e){return e(s,n.state)}))},A.prototype.dispatch=function(e,t){var i=this,n=x(e,t),o=n.type,a=n.payload,r={type:o,payload:a},s=this._actions[o];if(s){try{this._actionSubscribers.filter(function(e){return e.before}).forEach(function(e){return e.before(r,i.state)})}catch(e){}return(s.length>1?Promise.all(s.map(function(e){return e(a)})):s[0](a)).then(function(e){try{i._actionSubscribers.filter(function(e){return e.after}).forEach(function(e){return e.after(r,i.state)})}catch(e){}return e})}},A.prototype.subscribe=function(e){return c(e,this._subscribers)},A.prototype.subscribeAction=function(e){return c("function"==typeof e?{before:e}:e,this._actionSubscribers)},A.prototype.watch=function(e,t,i){var n=this;return this._watcherVM.$watch(function(){return e(n.state,n.getters)},t,i)},A.prototype.replaceState=function(e){var t=this;this._withCommit(function(){t._vm._data.$$state=e})},A.prototype.registerModule=function(e,t,i){void 0===i&&(i={}),"string"==typeof e&&(e=[e]),this._modules.register(e,t),f(this,this.state,e,this._modules.get(e),i.preserveState),u(this,this.state)},A.prototype.unregisterModule=function(e){var t=this;"string"==typeof e&&(e=[e]),this._modules.unregister(e),this._withCommit(function(){var i=k(t.state,e.slice(0,-1));O.delete(i,e[e.length-1])}),d(this)},A.prototype.hotUpdate=function(e){this._modules.update(e),d(this,!0)},A.prototype._withCommit=function(e){var t=this._committing;this._committing=!0,e(),this._committing=t},Object.defineProperties(A.prototype,T);var M=_(function(e,t){var i={};return y(t).forEach(function(t){var n=t.key,o=t.val;i[n]=function(){var t=this.$store.state,i=this.$store.getters;if(e){var n=C(this.$store,"mapState",e);if(!n)return;t=n.context.state,i=n.context.getters}return"function"==typeof o?o.call(this,t,i):t[o]},i[n].vuex=!0}),i}),j=_(function(e,t){var i={};return y(t).forEach(function(t){var n=t.key,o=t.val;i[n]=function(){for(var t=[],i=arguments.length;i--;)t[i]=arguments[i];var n=this.$store.commit;if(e){var a=C(this.$store,"mapMutations",e);if(!a)return;n=a.context.commit}return"function"==typeof o?o.apply(this,[n].concat(t)):n.apply(this.$store,[o].concat(t))}}),i}),P=_(function(e,t){var i={};return y(t).forEach(function(t){var n=t.key,o=t.val;o=e+o,i[n]=function(){if(!e||C(this.$store,"mapGetters",e))return this.$store.getters[o]},i[n].vuex=!0}),i}),E=_(function(e,t){var i={};return y(t).forEach(function(t){var n=t.key,o=t.val;i[n]=function(){for(var t=[],i=arguments.length;i--;)t[i]=arguments[i];var n=this.$store.dispatch;if(e){var a=C(this.$store,"mapActions",e);if(!a)return;n=a.context.dispatch}return"function"==typeof o?o.apply(this,[n].concat(t)):n.apply(this.$store,[o].concat(t))}}),i}),F=function(e){return{mapState:M.bind(null,e),mapGetters:P.bind(null,e),mapMutations:j.bind(null,e),mapActions:E.bind(null,e)}},B={Store:A,install:w,version:"3.1.0",mapState:M,mapMutations:j,mapGetters:P,mapActions:E,createNamespacedHelpers:F};t.a=B}]); +//# sourceMappingURL=build.js.map
\ No newline at end of file diff --git a/third_party/webrender/debugger/dist/build.js.map b/third_party/webrender/debugger/dist/build.js.map new file mode 100644 index 00000000000..5c6748b94d0 --- /dev/null +++ b/third_party/webrender/debugger/dist/build.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///build.js","webpack:///webpack/bootstrap cd2236c5c64656562cc5","webpack:///./node_modules/css-loader/lib/css-base.js","webpack:///./node_modules/vue-style-loader/lib/addStylesClient.js","webpack:///./node_modules/vue-loader/lib/component-normalizer.js","webpack:///./node_modules/vue/dist/vue.runtime.esm.js","webpack:///(webpack)/buildin/global.js","webpack:///./src/components/TreeView.vue","webpack:///src/App.vue","webpack:///src/components/NavBar.vue","webpack:///src/components/NavMenu.vue","webpack:///src/components/OptionsPage.vue","webpack:///src/components/PassViewPage.vue","webpack:///src/components/RenderTaskViewPage.vue","webpack:///src/components/TreeView.vue","webpack:///src/components/DocumentViewPage.vue","webpack:///src/components/ClipScrollTreeViewPage.vue","webpack:///src/components/ScreenshotPage.vue","webpack:///./src/main.js","webpack:///./node_modules/timers-browserify/main.js","webpack:///./node_modules/setimmediate/setImmediate.js","webpack:///./node_modules/process/browser.js","webpack:///./node_modules/buefy/dist/buefy.js","webpack:///./node_modules/buefy/dist/buefy.css?4403","webpack:///./node_modules/buefy/dist/buefy.css","webpack:///./node_modules/vue-style-loader/lib/listToStyles.js","webpack:///./node_modules/vue-material-design-icons/styles.css?c376","webpack:///./node_modules/vue-material-design-icons/styles.css","webpack:///./src/App.vue","webpack:///./src/App.vue?dec0","webpack:///./src/App.vue?32a8","webpack:///./src/components/NavBar.vue","webpack:///./src/components/NavBar.vue?49e8","webpack:///./src/components/NavBar.vue?8c74","webpack:///./src/components/NavBar.vue?1e10","webpack:///./src/components/NavMenu.vue","webpack:///./src/components/NavMenu.vue?b014","webpack:///./src/components/NavMenu.vue?a863","webpack:///./src/components/NavMenu.vue?fc73","webpack:///./src/components/OptionsPage.vue","webpack:///./src/components/OptionsPage.vue?7a62","webpack:///./src/components/OptionsPage.vue?2eb4","webpack:///./src/components/OptionsPage.vue?4dbb","webpack:///./src/components/PassViewPage.vue","webpack:///./src/components/PassViewPage.vue?d45d","webpack:///./src/components/PassViewPage.vue?1474","webpack:///./src/components/PassViewPage.vue?ba8d","webpack:///./src/components/RenderTaskViewPage.vue","webpack:///./src/components/RenderTaskViewPage.vue?8e9b","webpack:///./src/components/RenderTaskViewPage.vue?9881","webpack:///./src/components/TreeView.vue?3aad","webpack:///./src/components/TreeView.vue?828d","webpack:///./src/components/TreeView.vue?618e","webpack:///./src/components/RenderTaskViewPage.vue?ffdd","webpack:///./src/components/DocumentViewPage.vue","webpack:///./src/components/DocumentViewPage.vue?34ab","webpack:///./src/components/DocumentViewPage.vue?2072","webpack:///./src/components/DocumentViewPage.vue?2e34","webpack:///./src/components/ClipScrollTreeViewPage.vue","webpack:///./src/components/ClipScrollTreeViewPage.vue?60b5","webpack:///./src/components/ClipScrollTreeViewPage.vue?36ca","webpack:///./src/components/ClipScrollTreeViewPage.vue?da15","webpack:///./src/components/ScreenshotPage.vue","webpack:///./src/components/ScreenshotPage.vue?c861","webpack:///./src/components/ScreenshotPage.vue?ef61","webpack:///./src/components/ScreenshotPage.vue?1e6f","webpack:///./src/App.vue?d93d","webpack:///./src/store/index.js","webpack:///./node_modules/vuex/dist/vuex.esm.js"],"names":["modules","__webpack_require__","moduleId","installedModules","exports","module","i","l","call","m","c","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","cssWithMappingToString","item","useSourceMap","content","cssMapping","btoa","sourceMapping","toComment","concat","sources","map","source","sourceRoot","join","sourceMap","unescape","encodeURIComponent","JSON","stringify","list","toString","this","mediaQuery","alreadyImportedModules","length","id","push","addStylesToDom","styles","domStyle","stylesInDom","refs","j","parts","addStyle","createStyleElement","styleElement","document","createElement","type","head","appendChild","obj","update","remove","querySelector","ssrIdKey","isProduction","noop","parentNode","removeChild","isOldIE","styleIndex","singletonCounter","singletonElement","applyToSingletonTag","bind","applyToTag","newObj","css","media","index","styleSheet","cssText","replaceText","cssNode","createTextNode","childNodes","insertBefore","setAttribute","options","ssrId","firstChild","hasDocument","DEBUG","Error","listToStyles","getElementsByTagName","navigator","test","userAgent","toLowerCase","parentId","_isProduction","_options","newList","mayRemove","textStore","replacement","filter","Boolean","rawScriptExports","compiledTemplate","functionalTemplate","injectStyles","scopeId","moduleIdentifier","esModule","scriptExports","default","render","staticRenderFns","_compiled","functional","_scopeId","hook","context","$vnode","ssrContext","parent","__VUE_SSR_CONTEXT__","_registeredComponents","add","_ssrRegister","existing","beforeCreate","_injectStyles","h","__webpack_exports__","value","global","setImmediate","isUndef","v","undefined","isDef","isTrue","isFalse","isPrimitive","isObject","isPlainObject","_toString","isRegExp","isValidArrayIndex","val","parseFloat","String","Math","floor","isFinite","isPromise","then","catch","Array","isArray","toNumber","isNaN","makeMap","str","expectsLowerCase","create","split","arr","indexOf","splice","hasOwn","key","cached","fn","cache","polyfillBind","ctx","boundFn","a","arguments","apply","_length","nativeBind","toArray","start","ret","extend","to","_from","toObject","res","b","looseEqual","isObjectA","isObjectB","isArrayA","isArrayB","every","e","Date","getTime","keysA","keys","keysB","looseIndexOf","once","called","isReserved","charCodeAt","def","writable","parsePath","path","bailRE","segments","isNative","Ctor","pushTarget","target","targetStack","Dep","popTarget","pop","createTextVNode","VNode","cloneVNode","vnode","cloned","tag","data","children","slice","text","elm","componentOptions","asyncFactory","ns","isStatic","isComment","fnContext","fnOptions","fnScopeId","asyncMeta","isCloned","toggleObserving","shouldObserve","protoAugment","src","__proto__","copyAugment","observe","asRootData","ob","__ob__","Observer","isServerRendering","isExtensible","_isVue","vmCount","defineReactive$$1","customSetter","shallow","dep","getOwnPropertyDescriptor","setter","set","childOb","depend","dependArray","newVal","notify","max","del","mergeData","from","toVal","fromVal","hasSymbol","Reflect","ownKeys","mergeDataOrFn","parentVal","childVal","vm","instanceData","defaultData","mergeHook","dedupeHooks","hooks","mergeAssets","normalizeProps","props","camelize","normalizeInject","inject","normalized","normalizeDirectives","dirs","directives","def$$1","mergeOptions","child","mergeField","strat","strats","defaultStrat","_base","extends","mixins","resolveAsset","warnMissing","assets","camelizedId","PascalCaseId","capitalize","validateProp","propOptions","propsData","prop","absent","booleanIndex","getTypeIndex","hyphenate","stringIndex","getPropDefaultValue","prevShouldObserve","$options","_props","getType","match","isSameType","expectedTypes","len","handleError","err","info","cur","$parent","errorCaptured","capture","globalHandleError","invokeWithErrorHandling","handler","args","_handled","config","errorHandler","logError","inBrowser","inWeex","console","error","flushCallbacks","pending","copies","callbacks","nextTick","cb","_resolve","timerFunc","Promise","resolve","traverse","_traverse","seenObjects","clear","seen","isA","isFrozen","depId","has","createFnInvoker","fns","invoker","arguments$1","updateListeners","on","oldOn","remove$$1","createOnceHandler","old","event","normalizeEvent","passive","params","mergeVNodeHook","hookKey","wrappedHook","oldHook","merged","extractPropsFromVNodeData","attrs","altKey","checkProp","hash","preserve","simpleNormalizeChildren","normalizeChildren","normalizeArrayChildren","isTextNode","node","nestedIndex","lastIndex","last","shift","_isVList","initProvide","provide","_provided","initInjections","result","resolveInject","forEach","provideKey","provideDefault","resolveSlots","slots","slot","name$1","isWhitespace","normalizeScopedSlots","normalSlots","prevSlots","hasNormalSlots","isStable","$stable","$key","_normalized","emptyObject","$hasNormal","key$1","normalizeScopedSlot","key$2","proxyNormalSlot","proxy","renderList","Symbol","iterator","next","done","renderSlot","fallback","bindObject","nodes","scopedSlotFn","$scopedSlots","$slots","$createElement","resolveFilter","identity","isKeyNotMatch","expect","actual","checkKeyCodes","eventKeyCode","builtInKeyCode","eventKeyName","builtInKeyName","mappedKeyCode","keyCodes","bindObjectProps","asProp","isSync","isReservedAttribute","mustUseProp","domProps","camelizedKey","hyphenatedKey","$event","renderStatic","isInFor","_staticTrees","tree","_renderProxy","markStatic","markOnce","isOnce","markStaticNode","bindObjectListeners","ours","resolveScopedSlots","hasDynamicKeys","contentHashKey","bindDynamicKeys","baseObj","values","prependModifier","symbol","installRenderHelpers","_o","_n","_s","_l","_t","_q","_i","_m","_f","_k","_b","_v","_e","createEmptyVNode","_u","_g","_d","_p","FunctionalRenderContext","contextVm","this$1","_original","isCompiled","needNormalization","listeners","injections","scopedSlots","_c","createFunctionalComponent","mergeProps","renderContext","cloneAndMarkFunctionalResult","vnodes","clone","createComponent","baseCtor","cid","resolveAsyncComponent","createAsyncPlaceholder","resolveConstructorOptions","model","transformModel","nativeOn","abstract","installComponentHooks","createComponentInstanceForVnode","_isComponent","_parentVnode","inlineTemplate","hooksToMerge","toMerge","componentVNodeHooks","_merged","mergeHook$1","f1","f2","callback","normalizationType","alwaysNormalize","ALWAYS_NORMALIZE","_createElement","is","SIMPLE_NORMALIZE","getTagNamespace","isReservedTag","parsePlatformTagName","pre","applyNS","registerDeepBindings","force","style","class","initRender","_vnode","parentVnode","_renderChildren","parentData","_parentListeners","ensureCtor","comp","base","toStringTag","factory","errorComp","resolved","owner","currentRenderingInstance","owners","loading","loadingComp","sync","timerLoading","timerTimeout","$on","forceRender","renderCompleted","$forceUpdate","clearTimeout","reject","reason","component","delay","setTimeout","timeout","isAsyncPlaceholder","getFirstComponentChild","initEvents","_events","_hasHookEvent","updateComponentListeners","remove$1","$off","_target","onceHandler","oldListeners","setActiveInstance","prevActiveInstance","activeInstance","initLifecycle","$children","$root","$refs","_watcher","_inactive","_directInactive","_isMounted","_isDestroyed","_isBeingDestroyed","mountComponent","el","hydrating","$el","callHook","updateComponent","_update","_render","Watcher","before","updateChildComponent","renderChildren","newScopedSlots","oldScopedSlots","hasDynamicScopedSlot","needsForceUpdate","$attrs","$listeners","propKeys","_propKeys","isInInactiveTree","activateChildComponent","direct","deactivateChildComponent","handlers","$emit","resetSchedulerState","queue","activatedChildren","waiting","flushing","flushSchedulerQueue","currentFlushTimestamp","getNow","watcher","sort","run","activatedQueue","updatedQueue","callActivatedHooks","callUpdatedHooks","devtools","emit","queueActivatedComponent","queueWatcher","sourceKey","sharedPropertyDefinition","initState","_watchers","opts","initProps","methods","initMethods","initData","_data","computed","initComputed","watch","nativeWatch","initWatch","propsOptions","isRoot","getData","watchers","_computedWatchers","isSSR","userDef","computedWatcherOptions","defineComputed","shouldCache","createComputedGetter","createGetterInvoker","dirty","evaluate","createWatcher","expOrFn","$watch","initInternalComponent","constructor","vnodeComponentOptions","_componentTag","super","superOptions","modifiedOptions","resolveModifiedOptions","extendOptions","components","modified","latest","sealed","sealedOptions","Vue","_init","initUse","use","plugin","installedPlugins","_installedPlugins","unshift","install","initMixin$1","mixin","initExtend","Super","SuperId","cachedCtors","_Ctor","Sub","initProps$1","initComputed$1","ASSET_TYPES","Comp","initAssetRegisters","definition","getComponentName","matches","pattern","pruneCache","keepAliveInstance","cachedNode","pruneCacheEntry","current","cached$$1","componentInstance","$destroy","genClassForVnode","childNode","mergeClassData","renderClass","staticClass","dynamicClass","stringifyClass","stringifyArray","stringifyObject","stringified","isSVG","isUnknownElement","unknownElementCache","window","HTMLUnknownElement","HTMLElement","query","selected","createElement$1","tagName","multiple","createElementNS","namespace","namespaceMap","createComment","newNode","referenceNode","nextSibling","setTextContent","textContent","setStyleScope","registerRef","isRemoval","ref","refInFor","sameVnode","sameInputType","typeA","typeB","isTextInputType","createKeyToOldIdx","beginIdx","endIdx","updateDirectives","oldVnode","oldDir","dir","isCreate","emptyNode","isDestroy","oldDirs","normalizeDirectives$1","newDirs","dirsWithInsert","dirsWithPostpatch","oldValue","oldArg","arg","callHook$1","componentUpdated","inserted","callInsert","modifiers","emptyModifiers","getRawDirName","rawName","updateAttrs","inheritAttrs","oldAttrs","setAttr","isIE","isEdge","isXlink","removeAttributeNS","xlinkNS","getXlinkProp","isEnumeratedAttr","removeAttribute","baseSetAttr","isBooleanAttr","isFalsyAttrValue","convertEnumeratedValue","setAttributeNS","isIE9","__ieph","blocker","stopImmediatePropagation","removeEventListener","addEventListener","updateClass","oldData","cls","transitionClass","_transitionClasses","_prevClass","normalizeEvents","RANGE_TOKEN","CHECKBOX_RADIO_TOKEN","change","createOnceHandler$1","target$1","remove$2","add$1","useMicrotaskFix","attachedTimestamp","original","_wrapper","currentTarget","timeStamp","ownerDocument","supportsPassive","updateDOMListeners","updateDOMProps","oldProps","_value","strCur","shouldUpdateValue","innerHTML","svgContainer","svg","checkVal","composing","isNotInFocusAndDirty","isDirtyWithModifiers","notInFocus","activeElement","_vModifiers","number","trim","normalizeStyleData","normalizeStyleBinding","staticStyle","bindingStyle","parseStyleText","getStyle","checkChild","styleData","updateStyle","oldStaticStyle","oldStyleBinding","normalizedStyle","oldStyle","newStyle","setProp","addClass","classList","whitespaceRE","getAttribute","removeClass","tar","replace","resolveTransition","autoCssTransition","nextFrame","raf","addTransitionClass","transitionClasses","removeTransitionClass","whenTransitionEnds","expectedType","getTransitionInfo","propCount","TRANSITION","transitionEndEvent","animationEndEvent","ended","end","onEnd","getComputedStyle","transitionDelays","transitionProp","transitionDurations","transitionTimeout","getTimeout","animationDelays","animationProp","animationDurations","animationTimeout","ANIMATION","hasTransform","transformRE","delays","durations","toMs","Number","enter","toggleDisplay","_leaveCb","cancelled","transition","_enterCb","nodeType","enterClass","enterToClass","enterActiveClass","appearClass","appearToClass","appearActiveClass","beforeEnter","afterEnter","enterCancelled","beforeAppear","appear","afterAppear","appearCancelled","duration","transitionNode","isAppear","isRootInsert","startClass","activeClass","toClass","beforeEnterHook","enterHook","afterEnterHook","enterCancelledHook","explicitEnterDuration","expectsCSS","userWantsControl","getHookArgumentsLength","show","pendingNode","_pending","isValidDuration","leave","rm","performLeave","beforeLeave","leaveClass","leaveActiveClass","leaveToClass","explicitLeaveDuration","afterLeave","leaveCancelled","delayLeave","invokerFns","_enter","_","setSelected","binding","actuallySetSelected","isMultiple","option","getValue","selectedIndex","hasNoMatchingOption","onCompositionStart","onCompositionEnd","trigger","createEvent","initEvent","dispatchEvent","locateNode","getRealChild","compOptions","extractTransitionData","placeholder","rawChild","hasParentTransition","isSameChild","oldChild","callPendingCbs","_moveCb","recordPosition","newPos","getBoundingClientRect","applyTranslation","oldPos","pos","dx","left","dy","top","moved","transform","WebkitTransform","transitionDuration","freeze","camelizeRE","toUpperCase","charAt","hyphenateRE","Function","no","SSR_ATTR","LIFECYCLE_HOOKS","optionMergeStrategies","silent","productionTip","performance","warnHandler","ignoredElements","isReservedAttr","async","_lifecycleHooks","unicodeRegExp","RegExp","hasProto","WXEnvironment","platform","weexPlatform","UA","isIOS","isFF","_isServer","_Set","env","VUE_ENV","__VUE_DEVTOOLS_GLOBAL_HOOK__","Set","warn","uid","subs","addSub","sub","removeSub","addDep","raw","prototypeAccessors","defineProperties","arrayProto","arrayMethods","method","observeArray","arrayKeys","getOwnPropertyNames","walk","items","isUsingMicroTask","MutationObserver","counter","observer","textNode","characterData","once$$1","init","keepAlive","mountedNode","prepatch","$mount","insert","destroy","now","uid$2","isRenderWatcher","deep","user","lazy","active","deps","newDeps","depIds","newDepIds","expression","cleanupDeps","tmp","teardown","uid$3","_uid","_self","dataDef","propsDef","$set","$delete","immediate","hookRE","$once","i$1","cbs","prevEl","prevVnode","restoreActiveInstance","__patch__","__vue__","$nextTick","patternTypes","KeepAlive","include","exclude","created","destroyed","mounted","ref$1","parseInt","builtInComponents","configDef","util","defineReactive","delete","observable","version","emptyStyle","acceptValue","attr","isValidContentEditableValue","math","isHTMLTag","nodeOps","baseModules","klass","events","listDelimiter","propertyDelimiter","cssVarRE","importantRE","setProperty","normalizedName","normalize","vendorNames","capName","hasTransition","ontransitionend","onwebkittransitionend","onanimationend","onwebkitanimationend","requestAnimationFrame","activate","platformModules","patch","backend","emptyNodeAt","createRmCb","childElm","removeNode","createElm","insertedVnodeQueue","parentElm","refElm","nested","ownerArray","setScope","createChildren","invokeCreateHooks","isReactivated","initComponent","reactivateComponent","pendingInsert","isPatchable","innerNode","ref$$1","ancestor","addVnodes","startIdx","invokeDestroyHook","removeVnodes","ch","removeAndInvokeRemoveHook","updateChildren","oldCh","newCh","removeOnly","oldKeyToIdx","idxInOld","vnodeToMove","oldStartIdx","newStartIdx","oldEndIdx","oldStartVnode","oldEndVnode","newEndIdx","newStartVnode","newEndVnode","canMove","patchVnode","findIdxInOld","hydrate","postpatch","invokeInsertHook","initial","inVPre","hasChildNodes","childrenMatch","fullInvoke","isRenderedModule","isInitialPatch","isRealElement","hasAttribute","oldElm","patchable","i$2","vmodel","directive","_vOptions","prevOptions","curOptions","some","transition$$1","originalDisplay","__vOriginalDisplay","display","unbind","platformDirectives","transitionProps","mode","isNotTextNode","isVShowDirective","Transition","_leaving","oldRawChild","delayedLeave","moveClass","TransitionGroup","beforeMount","kept","prevChildren","rawChildren","transitionData","removed","c$1","updated","hasMove","_reflow","body","offsetHeight","propertyName","_hasMove","cloneNode","platformComponents","g","eval","injectStyle","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_TreeView_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_44998ed8_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_TreeView_vue__","normalizeComponent","__vue_styles__","Component","__WEBPACK_IMPORTED_MODULE_0__components_NavBar_vue__","__WEBPACK_IMPORTED_MODULE_1__components_NavMenu_vue__","__WEBPACK_IMPORTED_MODULE_2__components_OptionsPage_vue__","__WEBPACK_IMPORTED_MODULE_3__components_PassViewPage_vue__","__WEBPACK_IMPORTED_MODULE_4__components_RenderTaskViewPage_vue__","__WEBPACK_IMPORTED_MODULE_5__components_DocumentViewPage_vue__","__WEBPACK_IMPORTED_MODULE_6__components_ClipScrollTreeViewPage_vue__","__WEBPACK_IMPORTED_MODULE_7__components_ScreenshotPage_vue__","app-navbar","app-navmenu","app-options","app-passview","app-rendertaskview","app-documentview","app-clipscrolltreeview","app-screenshotview","page","$store","state","isConnected","connected","connect","dispatch","disconnect","setPage","commit","disabled","setProfiler","enabled","setTextureCacheDebugger","setRenderTargetDebugger","setAlphaRectsDebugger","setGpuTimeQueries","setGpuSampleQueries","setOpaquePass","setAlphaPass","setClipMasks","setTextPrims","setGradientPrims","fetch","passes","__WEBPACK_IMPORTED_MODULE_0__TreeView_vue__","app-treeview","render_tasks","open","isFolder","toggle","documents","clip_scroll_tree","screenshot","__WEBPACK_IMPORTED_MODULE_0_vue__","__WEBPACK_IMPORTED_MODULE_1_buefy__","__WEBPACK_IMPORTED_MODULE_1_buefy___default","__WEBPACK_IMPORTED_MODULE_2_buefy_dist_buefy_css__","__WEBPACK_IMPORTED_MODULE_3_vue_material_design_icons_styles_css__","__WEBPACK_IMPORTED_MODULE_4__App_vue__","__WEBPACK_IMPORTED_MODULE_5__store__","Buefy","store","App","Timeout","clearFn","_id","_clearFn","scope","self","setInterval","clearInterval","close","unref","enroll","msecs","_idleTimeoutId","_idleTimeout","unenroll","_unrefActive","_onTimeout","clearImmediate","process","task","tasksByHandle","nextHandle","registerImmediate","handle","runIfPresent","currentlyRunningATask","doc","attachTo","getPrototypeOf","postMessage","importScripts","postMessageIsAsynchronous","oldOnMessage","onmessage","messagePrefix","random","onGlobalMessage","attachEvent","MessageChannel","channel","port1","port2","html","documentElement","script","onreadystatechange","defaultSetTimout","defaultClearTimeout","runTimeout","fun","cachedSetTimeout","runClearTimeout","marker","cachedClearTimeout","cleanUpNextTick","draining","currentQueue","queueIndex","drainQueue","Item","array","title","browser","argv","versions","addListener","off","removeListener","removeAllListeners","prependListener","prependOnceListener","cwd","chdir","umask","root","__WEBPACK_EXTERNAL_MODULE_23__","_defineProperty","_defineProperty2","setOptions","defaultContainerElement","defaultIconPack","defaultDialogConfirmText","defaultDialogCancelText","defaultSnackbarDuration","defaultToastDuration","defaultTooltipType","defaultTooltipAnimated","defaultInputAutocomplete","defaultDateFormatter","defaultDateParser","defaultDayNames","defaultMonthNames","defaultFirstDayOfWeek","defaultUnselectableDaysOfWeek","defaultTimeFormatter","defaultTimeParser","defaultModalScroll","defaultDatepickerMobileNative","defaultTimepickerMobileNative","defaultNoticeQueue","defaultInputHasCounter","USE_SYMBOL","getValueByPath","reduce","removeElement","escapeRegExpChars","isMobile","Android","BlackBerry","iOS","Opera","Windows","any","__g","core","__e","anObject","IE8_DOM_DEFINE","toPrimitive","dP","f","O","P","Attributes","TypeError","it","__WEBPACK_IMPORTED_MODULE_0__BaseElementMixin__","size","expanded","rounded","icon","autocomplete","maxlength","isValid","isFocused","parentField","$data","_isField","statusType","newType","statusMessage","newMessage","iconSize","newIconPack","focus","_this","_elementRef","onBlur","checkHtml5Validity","onFocus","message","checkValidity","validationMessage","__WEBPACK_IMPORTED_MODULE_0__config__","iconPack","createDesc","IObject","defined","hide","$export","own","out","IS_FORCED","F","IS_GLOBAL","G","IS_STATIC","S","IS_PROTO","IS_BIND","B","IS_WRAP","W","expProto","C","virtual","R","U","exec","bitmap","$keys","enumBugKeys","px","propertyIsEnumerable","valueOf","ceil","shared","getOwnPropertySymbols","$at","iterated","point","TAG","stat","LIBRARY","wksExt","$Symbol","aFunction","that","toIObject","arrayIndexOf","IE_PROTO","names","cof","toInteger","min","_interopRequireDefault","_iterator","_iterator2","_symbol","_symbol2","_typeof","redefine","Iterators","$iterCreate","setToStringTag","ITERATOR","BUGGY","returnThis","Base","NAME","Constructor","DEFAULT","IS_SET","FORCED","IteratorPrototype","getMethod","kind","proto","DEF_VALUES","VALUES_BUG","$native","$default","$entries","$anyNative","entries","dPs","Empty","createDict","iframeDocument","iframe","contentWindow","write","lt","Properties","TO_STRING_TAG","DOMIterables","Collection","hiddenKeys","classof","getIteratorMethod","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default","__WEBPACK_IMPORTED_MODULE_1__components_icon_Icon__","__WEBPACK_IMPORTED_MODULE_1__components_icon_Icon___default","__WEBPACK_IMPORTED_MODULE_2__BaseElementMixin__","closable","hasIcon","autoClose","isActive","setAutoClose","timer","__WEBPACK_IMPORTED_MODULE_1__helpers__","position","validator","container","parentTop","parentBottom","newContainer","correctParent","shouldQueue","childElementCount","showNotice","_this2","insertAdjacentElement","indefinite","newDuration","setupContainer","className","dialog_open","external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default","Dialog_default","components_namespaceObject","components_checkbox","collapse","datepicker","dialog","dropdown","field","input","components_message","modal","notification","pagination","panel","components_radio","components_select","snackbar","components_switch","table","tabs","taginput","timepicker","toast","tooltip","upload","object_assign","assign_default","Autocomplete","Autocomplete_default","registerComponent","registerComponentProgrammatic","Plugin","Checkbox","Checkbox_default","CheckboxButton","CheckboxButton_default","checkbox_Plugin","Collapse","Collapse_default","collapse_Plugin","Datepicker","Datepicker_default","datepicker_Plugin","external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue__","Dialog","DialogProgrammatic","alert","defaultParam","canCancel","confirm","prompt","hasInput","confirmText","dialog_Plugin","Dropdown","Dropdown_default","DropdownItem","DropdownItem_default","dropdown_Plugin","Field","Field_default","field_Plugin","Icon","Icon_default","icon_Plugin","Input","Input_default","input_Plugin","Loading","Loading_default","LoadingProgrammatic","programmatic","loading_Plugin","Message","Message_default","message_Plugin","Modal","Modal_default","ModalProgrammatic","modal_Plugin","Notification","Notification_default","notification_Plugin","Pagination","Pagination_default","pagination_Plugin","Panel","Panel_default","panel_Plugin","Radio","Radio_default","RadioButton","RadioButton_default","radio_Plugin","Select","Select_default","select_Plugin","Snackbar","Snackbar_default","SnackbarProgrammatic","snackbar_Plugin","Switch","Switch_default","switch_Plugin","Table","Table_default","TableColumn","TableColumn_default","table_Plugin","Tabs","Tabs_default","TabItem","TabItem_default","tabs_Plugin","Tag","Tag_default","Taglist","Taglist_default","tag_Plugin","Taginput","Taginput_default","taginput_Plugin","Timepicker","Timepicker_default","timepicker_Plugin","Toast","Toast_default","ToastProgrammatic","toast_Plugin","Tooltip","Tooltip_default","tooltip_Plugin","Upload","Upload_default","upload_Plugin","componentKey","assign","getKeys","gOPS","pIE","$assign","A","K","k","T","aLen","getSymbols","isEnum","toLength","toAbsoluteIndex","IS_INCLUDES","$this","fromIndex","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default","__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator__","__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator___default","__WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty__","__WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty___default","__WEBPACK_IMPORTED_MODULE_3__utils_helpers__","__WEBPACK_IMPORTED_MODULE_4__utils_FormElementMixin__","__WEBPACK_IMPORTED_MODULE_5__input_Input__","__WEBPACK_IMPORTED_MODULE_5__input_Input___default","keepFirst","clearOnSelect","openOnFocus","hovered","newValue","isListInViewportVertically","hasFocus","_isAutocomplete","whiteList","querySelectorAll","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_step","return","hasDefaultSlot","hasEmptySlot","empty","hasHeaderSlot","header","calcDropdownInViewportVertical","setHovered","currentValue","selectFirstOption","closeDropdown","_this3","enterPressed","tabPressed","clickedOutside","isHighlight","escapedValue","regex","_this4","rect","bottom","innerHeight","clientHeight","keyArrows","direction","sum","element","visMin","scrollTop","visMax","offsetTop","focused","select","beforeDestroy","TO_STRING","descriptor","ObjectProto","addToUnscopables","step","Arguments","DESCRIPTORS","META","KEY","$fails","wks","wksDefine","enumKeys","_create","gOPNExt","$GOPD","$DP","gOPD","gOPN","$JSON","_stringify","HIDDEN","TO_PRIMITIVE","SymbolRegistry","AllSymbols","OPSymbols","USE_NATIVE","QObject","findChild","setSymbolDesc","D","protoDesc","wrap","sym","isSymbol","$defineProperty","$defineProperties","$create","$propertyIsEnumerable","E","$getOwnPropertyDescriptor","$getOwnPropertyNames","$getOwnPropertySymbols","IS_OP","es6Symbols","wellKnownSymbols","for","keyFor","useSetter","useSimple","replacer","$replacer","setDesc","FREEZE","preventExtensions","setMeta","w","fastKey","getWeak","onFreeze","meta","NEED","symbols","windowNames","getWindowNames","getIterator","iterFn","ARG","tryGet","callee","$Object","desc","__WEBPACK_IMPORTED_MODULE_1__icon_Icon__","__WEBPACK_IMPORTED_MODULE_1__icon_Icon___default","__WEBPACK_IMPORTED_MODULE_2__utils_config__","__WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__","passwordReveal","hasCounter","newAutocomplete","isPasswordVisible","rootClasses","iconPosition","is-expanded","is-loading","is-clearfix","hasMessage","inputClasses","is-rounded","hasIconRight","statusTypeIcon","passwordVisibleIcon","valueLength","togglePasswordVisibility","onInput","__WEBPACK_IMPORTED_MODULE_0__utils_config__","pack","customSize","customClass","both","newIcon","newPack","getEquivalentIconOf","splitType","newCustomSize","customSizeByPack","defaultSize","mediumSize","largeSize","_vm","_h","blur","is-clickable","click","is-invisible","icon-pack","keyup","keyCode","preventDefault","keydown","$$v","is-opened-top","is-hovered","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default","nativeValue","required","trueValue","falseValue","is-disabled","tabindex","label","true-value","false-value","checked","$$a","$$el","$$c","$$i","animation","isOpen","_components","__WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__","__WEBPACK_IMPORTED_MODULE_2__utils_helpers__","__WEBPACK_IMPORTED_MODULE_3__utils_config__","__WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__","__WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default","__WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__","__WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default","__WEBPACK_IMPORTED_MODULE_6__input_Input__","__WEBPACK_IMPORTED_MODULE_6__input_Input___default","__WEBPACK_IMPORTED_MODULE_7__field_Field__","__WEBPACK_IMPORTED_MODULE_7__field_Field___default","__WEBPACK_IMPORTED_MODULE_8__select_Select__","__WEBPACK_IMPORTED_MODULE_8__select_Select___default","__WEBPACK_IMPORTED_MODULE_9__icon_Icon__","__WEBPACK_IMPORTED_MODULE_9__icon_Icon___default","__WEBPACK_IMPORTED_MODULE_10__DatepickerTable__","__WEBPACK_IMPORTED_MODULE_10__DatepickerTable___default","dayNames","monthNames","firstDayOfWeek","inline","minDate","maxDate","focusedDate","readonly","unselectableDates","unselectableDaysOfWeek","selectableDates","dateFormatter","date","yyyyMMdd","getFullYear","getMonth","getDate","toLocaleDateString","dateParser","parse","mobileNative","indicators","dateSelected","focusedDateData","month","year","_isDatepicker","listOfYears","latestYear","earliestYear","arrayOfYears","reverse","isFirstMonth","isLastMonth","currentDate","focusedDateData.month","focusedDateData.year","updateSelectedDate","onChange","formatValue","decrementMonth","incrementMonth","formatYYYYMMDD","day","onChangeNativePicker","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator__","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator___default","__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol__","__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol___default","hoverable","mobileModal","_isDropdown","is-hoverable","is-inline","is-active","is-mobile-modal","isMobileModal","selectItem","isInWhiteList","dropdownMenu","_children","_iteratorNormalCompletion2","_didIteratorError2","_iteratorError2","_step2","role","separator","custom","paddingless","hasLink","anchorClasses","is-paddingless","itemClasses","dropdown-item","has-link","isClickable","__WEBPACK_IMPORTED_MODULE_1__FieldBody__","__WEBPACK_IMPORTED_MODULE_1__FieldBody___default","labelFor","grouped","groupMultiline","horizontal","addons","fieldLabelSize","newPosition","is-grouped-multiline","is-horizontal","prefix","formattedMessage","fieldType","renderedNode","__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__","__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default","__WEBPACK_IMPORTED_MODULE_2__icon_Icon__","__WEBPACK_IMPORTED_MODULE_2__icon_Icon___default","nativeSize","_isSelect","spanClasses","is-fullwidth","is-multiple","is-empty","has-icons-left","$$selectedVal","hidden","__WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow__","__WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow___default","visibleDayNames","currentDayName","hasEvents","eventsInThisMonth","monthEvents","weekBuilder","startingDate","thisMonth","thisWeek","dayOfWeek","getDay","daysAgo","daysForward","weeksInThisMonth","daysInThisMonth","startingDay","newWeek","weekValid","eventsInThisWeek","week","weekEvents","selectedDate","selectableDate","validity","enabledDate","disabledDate","_i2","emitChosenDate","eventsDateMatch","dayEvents","classObject","dateMatch","dateOne","dateTwo","is-selected","is-today","is-selectable","is-unselectable","has-event","href","has-events","selected-date","min-date","max-date","unselectable-dates","unselectable-days-of-week","selectable-dates","day-names","month-names","first-day-of-week","__WEBPACK_IMPORTED_MODULE_2__modal_Modal__","__WEBPACK_IMPORTED_MODULE_2__modal_Modal___default","__WEBPACK_IMPORTED_MODULE_4__utils_helpers__","__WEBPACK_IMPORTED_MODULE_5__utils_BaseElementMixin__","cancelText","inputAttrs","onConfirm","focusOn","iconByType","showCancel","cancelOptions","cancelButton","confirmButton","__WEBPACK_IMPORTED_MODULE_0__utils_helpers__","__WEBPACK_IMPORTED_MODULE_1__utils_config__","width","hasModalCard","onCancel","scroll","savedScrollTop","newWidth","showX","handleScroll","cancel","keyPress","modal-content","maxWidth","is-titleless","is-flex","is-danger","helpers","isFullPage","is-full-page","__WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__","newIconSize","__WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__","total","perPage","simple","order","is-simple","pageCount","firstItem","hasPrev","hasFirst","hasFirstEllipsis","hasLast","hasLastEllipsis","hasNext","pagesInRange","right","pages","isCurrent","prev","first","is-current","collapsible","hasCustomTemplate","is-collapsible","panel-block","__WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__","actionText","onAction","action","enter-active-class","leave-active-class","isMouseDown","mousedown","mouseup","mouseout","stopPropagation","is-elastic","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray__","__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default","__WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox__","__WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox___default","__WEBPACK_IMPORTED_MODULE_4__icon_Icon__","__WEBPACK_IMPORTED_MODULE_4__icon_Icon___default","__WEBPACK_IMPORTED_MODULE_5__pagination_Pagination__","__WEBPACK_IMPORTED_MODULE_5__pagination_Pagination___default","__WEBPACK_IMPORTED_MODULE_6__TableMobileSort__","__WEBPACK_IMPORTED_MODULE_6__TableMobileSort___default","__WEBPACK_IMPORTED_MODULE_7__TableColumn__","__WEBPACK_IMPORTED_MODULE_7__TableColumn___default","__WEBPACK_IMPORTED_MODULE_8__utils_BaseElementMixin__","columns","bordered","striped","narrowed","detailed","checkable","focusable","customIsChecked","isRowCheckable","checkedRows","mobileCards","defaultSort","defaultSortDirection","paginated","currentPage","paginationSimple","paginationSize","backendSorting","rowClass","openedDetailed","hasDetailedVisible","detailKey","backendPagination","newColumns","visibleDetailRows","newData","newDataTotal","newCheckedRows","newCurrentPage","currentSortColumn","isAsc","firstTimeSort","_isTable","tableClasses","is-bordered","is-striped","is-narrow","has-mobile-cards","visibleData","isAllChecked","validVisibleData","row","currentVisibleRow","isAllUncheckable","hasSortablenewColumns","column","sortable","columnCount","count","newTotal","rows","_newColumns","initSort","expandedRows","sortBy","newA","newB","updatingData","customSort","isRowChecked","removeCheckedRow","checkAll","currentRow","checkRow","selectRow","pageChanged","toggleDetails","isVisibleDetailRow","closeDetailRow","openDetailRow","handleDetailKey","checkPredefinedDetailedRows","hasCustomFooterSlot","footer","hasBottomLeftSlot","pressedArrow","_this5","sortField","sortDirection","_from2","arr2","isArrayIter","createProperty","getIterFn","iter","arrayLike","mapfn","mapping","ArrayProto","SAFE_CLOSING","riter","skipClosing","safe","__WEBPACK_IMPORTED_MODULE_1__select_Select__","__WEBPACK_IMPORTED_MODULE_1__select_Select___default","mobileSort","is-desc","customKey","numeric","centered","visible","internal","newKey","has-text-right","has-text-centered","data-label","current-sort-column","is-asc","is-current-sort","is-sortable","is-numeric","is-centered","is-checked","dblclick","colspan","per-page","defineProperty_default","SlotComponent","refresh","isVueComponent","animated","activeTab","tabItems","contentHeight","_isTabs","navClasses","is-toggle-rounded is-toggle","changeTab","newIndex","deactivate","tabClick","tabItem","__WEBPACK_IMPORTED_MODULE_0__utils_BaseElementMixin__","transitionName","oldIndex","attached","ellipsis","tabstop","has-ellipsis","has-addons","__WEBPACK_IMPORTED_MODULE_3__tag_Tag__","__WEBPACK_IMPORTED_MODULE_3__tag_Tag___default","__WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete__","__WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete___default","__WEBPACK_IMPORTED_MODULE_5__utils_FormElementMixin__","maxtags","confirmKeyCodes","removeOnKeys","allowNew","onPasteSeparators","beforeAdding","allowDuplicates","tags","newTag","_isTaginput","containerClasses","is-focused","is-focusable","defaultSlotName","emptySlotName","tagsLength","separatorsAsRegExp","sep","addTag","tagToAdd","reg","t","getNormalizedTagText","customOnBlur","onSelect","removeTag","removeLastTag","has-counter","keep-first","AM","PM","formatNumber","timeFormatter","hours","getHours","minutes","getMinutes","am","hourFormat","timeParser","dateString","dateString12","time","setMilliseconds","setSeconds","setMinutes","setHours","minTime","maxTime","incrementMinutes","unselectableTimes","hoursSelected","minutesSelected","meridienSelected","_isTimepicker","numberOfHours","isHourFormat24","meridiens","updateInternalState","onMeridienChange","updateDateSelected","onHoursChange","onMinutesChange","isHourDisabled","hour","minHours","maxHours","unselectable","_unselectable","isMinuteDisabled","minute","minMinutes","_minMinutes","formatHHMMSS","meridien","always","square","dashed","multilined","newAnimated","is-square","is-animated","is-always","is-multiline","is-dashed","__WEBPACK_IMPORTED_MODULE_0__utils_FormElementMixin__","accept","dragDrop","native","dragDropFocus","onFileChange","updateDragDropFocus","files","dataTransfer","file","checkType","types","valid","substring","extIndex","lastIndexOf","dragover","dragleave","dragenter","drop","locals","newStyles","part","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_App_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_3e910c9a_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_App_vue__","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_NavBar_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_657ba0aa_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_NavBar_vue__","esExports","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_NavMenu_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_e621d982_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_NavMenu_vue__","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_OptionsPage_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_61a8a336_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_OptionsPage_vue__","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_PassViewPage_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_329e01f6_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_PassViewPage_vue__","pass","pass_index","target_index","text-indent","batch","batch_index","description","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_RenderTaskViewPage_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_49dfebc9_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_RenderTaskViewPage_vue__","padding-left","line-height","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_DocumentViewPage_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_1b68865d_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_DocumentViewPage_vue__","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_ClipScrollTreeViewPage_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_31f0a1d9_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_ClipScrollTreeViewPage_vue__","__WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_ScreenshotPage_vue__","__WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_29affa2a_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_ScreenshotPage_vue__","height","_classCallCheck","instance","__WEBPACK_IMPORTED_MODULE_1_vuex__","_createClass","protoProps","staticProps","Vuex","Connection","ws","WebSocket","onopen","evt","json","onclose","msg","send","connection","Store","strict","mutations","setConnected","setPasses","setRenderTasks","setDocuments","setClipScrollTree","setScreenshot","actions","sendMessage","applyMixin","vuexInit","devtoolPlugin","devtoolHook","_devtoolHook","targetState","replaceState","subscribe","mutation","forEachValue","targetModule","newModule","getChild","genericSubscribe","resetStore","hot","_actions","_mutations","_wrappedGetters","_modulesNamespaceMap","installModule","_modules","resetStoreVM","oldVm","getters","wrappedGetters","$$state","enableStrictMode","_withCommit","rootState","getNamespace","namespaced","parentState","getNestedState","moduleName","local","makeLocalContext","forEachMutation","registerMutation","forEachAction","registerAction","forEachGetter","registerGetter","forEachChild","noNamespace","_type","_payload","unifyObjectStyle","payload","makeLocalGetters","gettersProxy","splitPos","localType","rootGetters","rawGetter","_Vue","normalizeMap","normalizeNamespace","getModuleByNamespace","helper","Module","rawModule","runtime","_rawModule","rawState","addChild","ModuleCollection","rawRootModule","register","rawChildModule","unregister","plugins","_committing","_actionSubscribers","_subscribers","_watcherVM","prototypeAccessors$1","entry","all","after","subscribeAction","registerModule","preserveState","unregisterModule","hotUpdate","newOptions","committing","mapState","states","vuex","mapMutations","mapGetters","mapActions","createNamespacedHelpers","index_esm"],"mappings":"CAAS,SAAUA,GCInB,QAAAC,GAAAC,GAGA,GAAAC,EAAAD,GACA,MAAAC,GAAAD,GAAAE,OAGA,IAAAC,GAAAF,EAAAD,IACAI,EAAAJ,EACAK,GAAA,EACAH,WAUA,OANAJ,GAAAE,GAAAM,KAAAH,EAAAD,QAAAC,IAAAD,QAAAH,GAGAI,EAAAE,GAAA,EAGAF,EAAAD,QAvBA,GAAAD,KA4BAF,GAAAQ,EAAAT,EAGAC,EAAAS,EAAAP,EAGAF,EAAAU,EAAA,SAAAP,EAAAQ,EAAAC,GACAZ,EAAAa,EAAAV,EAAAQ,IACAG,OAAAC,eAAAZ,EAAAQ,GACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,KAMAZ,EAAAmB,EAAA,SAAAf,GACA,GAAAQ,GAAAR,KAAAgB,WACA,WAA2B,MAAAhB,GAAA,SAC3B,WAAiC,MAAAA,GAEjC,OADAJ,GAAAU,EAAAE,EAAA,IAAAA,GACAA,GAIAZ,EAAAa,EAAA,SAAAQ,EAAAC,GAAsD,MAAAR,QAAAS,UAAAC,eAAAjB,KAAAc,EAAAC,IAGtDtB,EAAAyB,EAAA,SAGAzB,IAAA0B,EAAA,MDMM,SAAUtB,EAAQD,GElBxB,QAAAwB,GAAAC,EAAAC,GACA,GAAAC,GAAAF,EAAA,OACAG,EAAAH,EAAA,EACA,KAAAG,EACA,MAAAD,EAGA,IAAAD,GAAA,kBAAAG,MAAA,CACA,GAAAC,GAAAC,EAAAH,EAKA,QAAAD,GAAAK,OAJAJ,EAAAK,QAAAC,IAAA,SAAAC,GACA,uBAAAP,EAAAQ,WAAAD,EAAA,SAGAH,QAAAF,IAAAO,KAAA,MAGA,OAAAV,GAAAU,KAAA,MAIA,QAAAN,GAAAO,GAKA,yEAHAT,KAAAU,SAAAC,mBAAAC,KAAAC,UAAAJ,MAGA,MArEArC,EAAAD,QAAA,SAAA0B,GACA,GAAAiB,KAwCA,OArCAA,GAAAC,SAAA,WACA,MAAAC,MAAAX,IAAA,SAAAT,GACA,GAAAE,GAAAH,EAAAC,EAAAC,EACA,OAAAD,GAAA,GACA,UAAAA,EAAA,OAAmCE,EAAA,IAEnCA,IAEGU,KAAA,KAIHM,EAAAzC,EAAA,SAAAN,EAAAkD,GACA,gBAAAlD,KACAA,IAAA,KAAAA,EAAA,KAEA,QADAmD,MACA7C,EAAA,EAAgBA,EAAA2C,KAAAG,OAAiB9C,IAAA,CACjC,GAAA+C,GAAAJ,KAAA3C,GAAA,EACA,iBAAA+C,KACAF,EAAAE,IAAA,GAEA,IAAA/C,EAAA,EAAYA,EAAAN,EAAAoD,OAAoB9C,IAAA,CAChC,GAAAuB,GAAA7B,EAAAM,EAKA,iBAAAuB,GAAA,IAAAsB,EAAAtB,EAAA,MACAqB,IAAArB,EAAA,GACAA,EAAA,GAAAqB,EACKA,IACLrB,EAAA,OAAAA,EAAA,aAAAqB,EAAA,KAEAH,EAAAO,KAAAzB,MAIAkB,IFuGM,SAAU1C,EAAQD,EAASH,GGhEjC,QAAAsD,GAAAC,GACA,OAAAlD,GAAA,EAAiBA,EAAAkD,EAAAJ,OAAmB9C,IAAA,CACpC,GAAAuB,GAAA2B,EAAAlD,GACAmD,EAAAC,EAAA7B,EAAAwB,GACA,IAAAI,EAAA,CACAA,EAAAE,MACA,QAAAC,GAAA,EAAqBA,EAAAH,EAAAI,MAAAT,OAA2BQ,IAChDH,EAAAI,MAAAD,GAAA/B,EAAAgC,MAAAD,GAEA,MAAYA,EAAA/B,EAAAgC,MAAAT,OAAuBQ,IACnCH,EAAAI,MAAAP,KAAAQ,EAAAjC,EAAAgC,MAAAD,IAEAH,GAAAI,MAAAT,OAAAvB,EAAAgC,MAAAT,SACAK,EAAAI,MAAAT,OAAAvB,EAAAgC,MAAAT,YAEK,CAEL,OADAS,MACAD,EAAA,EAAqBA,EAAA/B,EAAAgC,MAAAT,OAAuBQ,IAC5CC,EAAAP,KAAAQ,EAAAjC,EAAAgC,MAAAD,IAEAF,GAAA7B,EAAAwB,KAA8BA,GAAAxB,EAAAwB,GAAAM,KAAA,EAAAE,WAK9B,QAAAE,KACA,GAAAC,GAAAC,SAAAC,cAAA,QAGA,OAFAF,GAAAG,KAAA,WACAC,EAAAC,YAAAL,GACAA,EAGA,QAAAF,GAAAQ,GACA,GAAAC,GAAAC,EACAR,EAAAC,SAAAQ,cAAA,SAAAC,EAAA,MAAAJ,EAAAjB,GAAA,KAEA,IAAAW,EAAA,CACA,GAAAW,EAGA,MAAAC,EAOAZ,GAAAa,WAAAC,YAAAd,GAIA,GAAAe,EAAA,CAEA,GAAAC,GAAAC,GACAjB,GAAAkB,MAAAnB,KACAQ,EAAAY,EAAAC,KAAA,KAAApB,EAAAgB,GAAA,GACAR,EAAAW,EAAAC,KAAA,KAAApB,EAAAgB,GAAA,OAGAhB,GAAAD,IACAQ,EAAAc,EAAAD,KAAA,KAAApB,GACAQ,EAAA,WACAR,EAAAa,WAAAC,YAAAd,GAMA,OAFAO,GAAAD,GAEA,SAAAgB,GACA,GAAAA,EAAA,CACA,GAAAA,EAAAC,MAAAjB,EAAAiB,KACAD,EAAAE,QAAAlB,EAAAkB,OACAF,EAAA5C,YAAA4B,EAAA5B,UACA,MAEA6B,GAAAD,EAAAgB,OAEAd,MAcA,QAAAW,GAAAnB,EAAAyB,EAAAjB,EAAAF,GACA,GAAAiB,GAAAf,EAAA,GAAAF,EAAAiB,GAEA,IAAAvB,EAAA0B,WACA1B,EAAA0B,WAAAC,QAAAC,EAAAH,EAAAF,OACG,CACH,GAAAM,GAAA5B,SAAA6B,eAAAP,GACAQ,EAAA/B,EAAA+B,UACAA,GAAAN,IAAAzB,EAAAc,YAAAiB,EAAAN,IACAM,EAAA3C,OACAY,EAAAgC,aAAAH,EAAAE,EAAAN,IAEAzB,EAAAK,YAAAwB,IAKA,QAAAR,GAAArB,EAAAM,GACA,GAAAiB,GAAAjB,EAAAiB,IACAC,EAAAlB,EAAAkB,MACA9C,EAAA4B,EAAA5B,SAiBA,IAfA8C,GACAxB,EAAAiC,aAAA,QAAAT,GAEAU,EAAAC,OACAnC,EAAAiC,aAAAvB,EAAAJ,EAAAjB,IAGAX,IAGA6C,GAAA,mBAAA7C,EAAAL,QAAA,SAEAkD,GAAA,uDAAyDtD,KAAAU,SAAAC,mBAAAC,KAAAC,UAAAJ,MAAA,OAGzDsB,EAAA0B,WACA1B,EAAA0B,WAAAC,QAAAJ,MACG,CACH,KAAAvB,EAAAoC,YACApC,EAAAc,YAAAd,EAAAoC,WAEApC,GAAAK,YAAAJ,SAAA6B,eAAAP,KArNA,GAAAc,GAAA,mBAAApC,SAEA,uBAAAqC,gBACAD,EACA,SAAAE,OACA,0JAKA,IAAAC,GAAmBvG,EAAQ,IAe3ByD,KAQAU,EAAAiC,IAAApC,SAAAG,MAAAH,SAAAwC,qBAAA,YACAvB,EAAA,KACAD,EAAA,EACAN,GAAA,EACAC,EAAA,aACAsB,EAAA,KACAxB,EAAA,kBAIAK,EAAA,mBAAA2B,YAAA,eAAAC,KAAAD,UAAAE,UAAAC,cAEAxG,GAAAD,QAAA,SAAA0G,EAAA/D,EAAAgE,EAAAC,GACArC,EAAAoC,EAEAb,EAAAc,KAEA,IAAAxD,GAAAgD,EAAAM,EAAA/D,EAGA,OAFAQ,GAAAC,GAEA,SAAAyD,GAEA,OADAC,MACA5G,EAAA,EAAmBA,EAAAkD,EAAAJ,OAAmB9C,IAAA,CACtC,GAAAuB,GAAA2B,EAAAlD,GACAmD,EAAAC,EAAA7B,EAAAwB,GACAI,GAAAE,OACAuD,EAAA5D,KAAAG,GAEAwD,GACAzD,EAAAgD,EAAAM,EAAAG,GACA1D,EAAAC,IAEAA,IAEA,QAAAlD,GAAA,EAAmBA,EAAA4G,EAAA9D,OAAsB9C,IAAA,CACzC,GAAAmD,GAAAyD,EAAA5G,EACA,QAAAmD,EAAAE,KAAA,CACA,OAAAC,GAAA,EAAuBA,EAAAH,EAAAI,MAAAT,OAA2BQ,IAClDH,EAAAI,MAAAD,WAEAF,GAAAD,EAAAJ,OAwFA,IAAAuC,GAAA,WACA,GAAAuB,KAEA,iBAAA1B,EAAA2B,GAEA,MADAD,GAAA1B,GAAA2B,EACAD,EAAAE,OAAAC,SAAA7E,KAAA,WH6MM,SAAUpC,EAAQD,GInXxBC,EAAAD,QAAA,SACAmH,EACAC,EACAC,EACAC,EACAC,EACAC,GAEA,GAAAC,GACAC,EAAAP,QAGApD,QAAAoD,GAAAQ,OACA,YAAA5D,GAAA,aAAAA,IACA0D,EAAAN,EACAO,EAAAP,EAAAQ,QAIA,IAAA7B,GAAA,kBAAA4B,GACAA,EAAA5B,QACA4B,CAGAN,KACAtB,EAAA8B,OAAAR,EAAAQ,OACA9B,EAAA+B,gBAAAT,EAAAS,gBACA/B,EAAAgC,WAAA,GAIAT,IACAvB,EAAAiC,YAAA,GAIAR,IACAzB,EAAAkC,SAAAT,EAGA,IAAAU,EA4BA,IA3BAT,GACAS,EAAA,SAAAC,GAEAA,EACAA,GACArF,KAAAsF,QAAAtF,KAAAsF,OAAAC,YACAvF,KAAAwF,QAAAxF,KAAAwF,OAAAF,QAAAtF,KAAAwF,OAAAF,OAAAC,WAEAF,GAAA,mBAAAI,uBACAJ,EAAAI,qBAGAhB,GACAA,EAAAlH,KAAAyC,KAAAqF,GAGAA,KAAAK,uBACAL,EAAAK,sBAAAC,IAAAhB,IAKA1B,EAAA2C,aAAAR,GACGX,IACHW,EAAAX,GAGAW,EAAA,CACA,GAAAF,GAAAjC,EAAAiC,WACAW,EAAAX,EACAjC,EAAA8B,OACA9B,EAAA6C,YAEAZ,IAQAjC,EAAA8C,cAAAX,EAEAnC,EAAA8B,OAAA,SAAAiB,EAAAX,GAEA,MADAD,GAAA7H,KAAA8H,GACAQ,EAAAG,EAAAX,KAVApC,EAAA6C,aAAAD,KACA1G,OAAA0G,EAAAT,IACAA,GAaA,OACAR,WACAzH,QAAA0H,EACA5B,aJkYM,SAAU7F,EAAQ6I,EAAqBjJ,GAE7C,YKxeAc,QAAAC,eAAAkI,EAAA,cAAAC,OAAA,aAAAC,EAAAC,GAWA,QAAAC,GAAAC,GACA,WAAAC,KAAAD,GAAA,OAAAA,EAGA,QAAAE,GAAAF,GACA,WAAAC,KAAAD,GAAA,OAAAA,EAGA,QAAAG,GAAAH,GACA,WAAAA,EAGA,QAAAI,GAAAJ,GACA,WAAAA,EAMA,QAAAK,GAAAT,GACA,MACA,gBAAAA,IACA,gBAAAA,IAEA,gBAAAA,IACA,iBAAAA,GASA,QAAAU,GAAAvF,GACA,cAAAA,GAAA,gBAAAA,GAgBA,QAAAwF,GAAAxF,GACA,0BAAAyF,GAAAvJ,KAAA8D,GAGA,QAAA0F,GAAAT,GACA,0BAAAQ,GAAAvJ,KAAA+I,GAMA,QAAAU,GAAAC,GACA,GAAA9I,GAAA+I,WAAAC,OAAAF,GACA,OAAA9I,IAAA,GAAAiJ,KAAAC,MAAAlJ,QAAAmJ,SAAAL,GAGA,QAAAM,GAAAN,GACA,MACAT,GAAAS,IACA,kBAAAA,GAAAO,MACA,kBAAAP,GAAAQ,MAOA,QAAA1H,GAAAkH,GACA,aAAAA,EACA,GACAS,MAAAC,QAAAV,IAAAJ,EAAAI,MAAAlH,WAAA+G,GACAlH,KAAAC,UAAAoH,EAAA,QACAE,OAAAF,GAOA,QAAAW,GAAAX,GACA,GAAA9I,GAAA+I,WAAAD,EACA,OAAAY,OAAA1J,GAAA8I,EAAA9I,EAOA,QAAA2J,GACAC,EACAC,GAIA,OAFA3I,GAAAvB,OAAAmK,OAAA,MACAnI,EAAAiI,EAAAG,MAAA,KACA7K,EAAA,EAAiBA,EAAAyC,EAAAK,OAAiB9C,IAClCgC,EAAAS,EAAAzC,KAAA,CAEA,OAAA2K,GACA,SAAAf,GAAsB,MAAA5H,GAAA4H,EAAArD,gBACtB,SAAAqD,GAAsB,MAAA5H,GAAA4H,IAgBtB,QAAA1F,GAAA4G,EAAAvJ,GACA,GAAAuJ,EAAAhI,OAAA,CACA,GAAAqC,GAAA2F,EAAAC,QAAAxJ,EACA,IAAA4D,GAAA,EACA,MAAA2F,GAAAE,OAAA7F,EAAA,IASA,QAAA8F,GAAAjH,EAAAkH,GACA,MAAA/J,IAAAjB,KAAA8D,EAAAkH,GAMA,QAAAC,GAAAC,GACA,GAAAC,GAAA5K,OAAAmK,OAAA,KACA,iBAAAF,GAEA,MADAW,GAAAX,KACAW,EAAAX,GAAAU,EAAAV,KAoCA,QAAAY,GAAAF,EAAAG,GACA,QAAAC,GAAAC,GACA,GAAAxL,GAAAyL,UAAA5I,MACA,OAAA7C,GACAA,EAAA,EACAmL,EAAAO,MAAAJ,EAAAG,WACAN,EAAAlL,KAAAqL,EAAAE,GACAL,EAAAlL,KAAAqL,GAIA,MADAC,GAAAI,QAAAR,EAAAtI,OACA0I,EAGA,QAAAK,GAAAT,EAAAG,GACA,MAAAH,GAAAtG,KAAAyG,GAUA,QAAAO,GAAArJ,EAAAsJ,GACAA,KAAA,CAGA,KAFA,GAAA/L,GAAAyC,EAAAK,OAAAiJ,EACAC,EAAA,GAAA3B,OAAArK,GACAA,KACAgM,EAAAhM,GAAAyC,EAAAzC,EAAA+L,EAEA,OAAAC,GAMA,QAAAC,GAAAC,EAAAC,GACA,OAAAjB,KAAAiB,GACAD,EAAAhB,GAAAiB,EAAAjB,EAEA,OAAAgB,GAMA,QAAAE,GAAAtB,GAEA,OADAuB,MACArM,EAAA,EAAiBA,EAAA8K,EAAAhI,OAAgB9C,IACjC8K,EAAA9K,IACAiM,EAAAI,EAAAvB,EAAA9K,GAGA,OAAAqM,GAUA,QAAA/H,GAAAmH,EAAAa,EAAAlM,IAkBA,QAAAmM,GAAAd,EAAAa,GACA,GAAAb,IAAAa,EAAgB,QAChB,IAAAE,GAAAjD,EAAAkC,GACAgB,EAAAlD,EAAA+C,EACA,KAAAE,IAAAC,EAwBG,OAAAD,IAAAC,GACH3C,OAAA2B,KAAA3B,OAAAwC,EAxBA,KACA,GAAAI,GAAArC,MAAAC,QAAAmB,GACAkB,EAAAtC,MAAAC,QAAAgC,EACA,IAAAI,GAAAC,EACA,MAAAlB,GAAA3I,SAAAwJ,EAAAxJ,QAAA2I,EAAAmB,MAAA,SAAAC,EAAA7M,GACA,MAAAuM,GAAAM,EAAAP,EAAAtM,KAEO,IAAAyL,YAAAqB,OAAAR,YAAAQ,MACP,MAAArB,GAAAsB,YAAAT,EAAAS,SACO,IAAAL,GAAAC,EAQP,QAPA,IAAAK,GAAAvM,OAAAwM,KAAAxB,GACAyB,EAAAzM,OAAAwM,KAAAX,EACA,OAAAU,GAAAlK,SAAAoK,EAAApK,QAAAkK,EAAAJ,MAAA,SAAA1B,GACA,MAAAqB,GAAAd,EAAAP,GAAAoB,EAAApB,MAMK,MAAA2B,GAEL,UAcA,QAAAM,GAAArC,EAAAlB,GACA,OAAA5J,GAAA,EAAiBA,EAAA8K,EAAAhI,OAAgB9C,IACjC,GAAAuM,EAAAzB,EAAA9K,GAAA4J,GAAkC,MAAA5J,EAElC,UAMA,QAAAoN,GAAAhC,GACA,GAAAiC,IAAA,CACA,mBACAA,IACAA,GAAA,EACAjC,EAAAO,MAAAhJ,KAAA+I,aA0IA,QAAA4B,GAAA5C,GACA,GAAAtK,IAAAsK,EAAA,IAAA6C,WAAA,EACA,aAAAnN,GAAA,KAAAA,EAMA,QAAAoN,GAAAxJ,EAAAkH,EAAAtB,EAAAhJ,GACAH,OAAAC,eAAAsD,EAAAkH,GACArC,MAAAe,EACAhJ,eACA6M,UAAA,EACA9M,cAAA,IAQA,QAAA+M,GAAAC,GACA,IAAAC,GAAAvH,KAAAsH,GAAA,CAGA,GAAAE,GAAAF,EAAA9C,MAAA,IACA,iBAAA7G,GACA,OAAAhE,GAAA,EAAmBA,EAAA6N,EAAA/K,OAAqB9C,IAAA,CACxC,IAAAgE,EAAiB,MACjBA,KAAA6J,EAAA7N,IAEA,MAAAgE,KA6DA,QAAA8J,GAAAC,GACA,wBAAAA,IAAA,cAAA1H,KAAA0H,EAAArL,YA6KA,QAAAsL,GAAAC,GACAC,GAAAlL,KAAAiL,GACAE,GAAAF,SAGA,QAAAG,KACAF,GAAAG,MACAF,GAAAF,OAAAC,MAAApL,OAAA,GA2DA,QAAAwL,GAAA1E,GACA,UAAA2E,QAAArF,iBAAAY,OAAAF,IAOA,QAAA4E,GAAAC,GACA,GAAAC,GAAA,GAAAH,IACAE,EAAAE,IACAF,EAAAG,KAIAH,EAAAI,UAAAJ,EAAAI,SAAAC,QACAL,EAAAM,KACAN,EAAAO,IACAP,EAAAzG,QACAyG,EAAAQ,iBACAR,EAAAS,aAWA,OATAR,GAAAS,GAAAV,EAAAU,GACAT,EAAAU,SAAAX,EAAAW,SACAV,EAAAxD,IAAAuD,EAAAvD,IACAwD,EAAAW,UAAAZ,EAAAY,UACAX,EAAAY,UAAAb,EAAAa,UACAZ,EAAAa,UAAAd,EAAAc,UACAb,EAAAc,UAAAf,EAAAe,UACAd,EAAAe,UAAAhB,EAAAgB,UACAf,EAAAgB,UAAA,EACAhB,EA4DA,QAAAiB,GAAA9G,GACA+G,GAAA/G,EAqDA,QAAAgH,GAAA5B,EAAA6B,GAEA7B,EAAA8B,UAAAD,EASA,QAAAE,GAAA/B,EAAA6B,EAAA7C,GACA,OAAAjN,GAAA,EAAAC,EAAAgN,EAAAnK,OAAkC9C,EAAAC,EAAOD,IAAA,CACzC,GAAAkL,GAAA+B,EAAAjN,EACAwN,GAAAS,EAAA/C,EAAA4E,EAAA5E,KASA,QAAA+E,GAAApH,EAAAqH,GACA,GAAA3G,EAAAV,kBAAA0F,KAAA,CAGA,GAAA4B,EAeA,OAdAlF,GAAApC,EAAA,WAAAA,EAAAuH,iBAAAC,IACAF,EAAAtH,EAAAuH,OAEAR,KACAU,OACAjG,MAAAC,QAAAzB,IAAAW,EAAAX,KACApI,OAAA8P,aAAA1H,KACAA,EAAA2H,SAEAL,EAAA,GAAAE,IAAAxH,IAEAqH,GAAAC,GACAA,EAAAM,UAEAN,GAMA,QAAAO,GACA1M,EACAkH,EACAtB,EACA+G,EACAC,GAEA,GAAAC,GAAA,GAAA1C,IAEAlN,EAAAR,OAAAqQ,yBAAA9M,EAAAkH,EACA,KAAAjK,IAAA,IAAAA,EAAAN,aAAA,CAKA,GAAAJ,GAAAU,KAAAJ,IACAkQ,EAAA9P,KAAA+P,GACAzQ,KAAAwQ,GAAA,IAAArF,UAAA5I,SACA8G,EAAA5F,EAAAkH,GAGA,IAAA+F,IAAAL,GAAAX,EAAArG,EACAnJ,QAAAC,eAAAsD,EAAAkH,GACAtK,YAAA,EACAD,cAAA,EACAE,IAAA,WACA,GAAAgI,GAAAtI,IAAAL,KAAA8D,GAAA4F,CAUA,OATAuE,IAAAF,SACA4C,EAAAK,SACAD,IACAA,EAAAJ,IAAAK,SACA7G,MAAAC,QAAAzB,IACAsI,EAAAtI,KAIAA,GAEAmI,IAAA,SAAAI,GACA,GAAAvI,GAAAtI,IAAAL,KAAA8D,GAAA4F,CAEAwH,KAAAvI,GAAAuI,OAAAvI,OAQAtI,IAAAwQ,IACAA,EACAA,EAAA7Q,KAAA8D,EAAAoN,GAEAxH,EAAAwH,EAEAH,GAAAL,GAAAX,EAAAmB,GACAP,EAAAQ,cAUA,QAAAL,GAAA/C,EAAA/C,EAAAtB,GAMA,GAAAS,MAAAC,QAAA2D,IAAAtE,EAAAuB,GAGA,MAFA+C,GAAAnL,OAAAiH,KAAAuH,IAAArD,EAAAnL,OAAAoI,GACA+C,EAAAjD,OAAAE,EAAA,EAAAtB,GACAA,CAEA,IAAAsB,IAAA+C,MAAA/C,IAAAzK,QAAAS,WAEA,MADA+M,GAAA/C,GAAAtB,EACAA,CAEA,IAAAuG,GAAA,EAAAC,MACA,OAAAnC,GAAAuC,QAAAL,KAAAM,QAKA7G,EAEAuG,GAIAO,EAAAP,EAAAtH,MAAAqC,EAAAtB,GACAuG,EAAAU,IAAAQ,SACAzH,IALAqE,EAAA/C,GAAAtB,EACAA,GAUA,QAAA2H,GAAAtD,EAAA/C,GAMA,GAAAb,MAAAC,QAAA2D,IAAAtE,EAAAuB,GAEA,WADA+C,GAAAjD,OAAAE,EAAA,EAGA,IAAAiF,GAAA,EAAAC,MACAnC,GAAAuC,QAAAL,KAAAM,SAOAxF,EAAAgD,EAAA/C,WAGA+C,GAAA/C,GACAiF,GAGAA,EAAAU,IAAAQ,UAOA,QAAAF,GAAAtI,GACA,OAAAgE,OAAA,GAAA7M,EAAA,EAAAC,EAAA4I,EAAA/F,OAAiD9C,EAAAC,EAAOD,IACxD6M,EAAAhE,EAAA7I,GACA6M,KAAAuD,QAAAvD,EAAAuD,OAAAS,IAAAK,SACA7G,MAAAC,QAAAuC,IACAsE,EAAAtE,GAgCA,QAAA2E,GAAAtF,EAAAuF,GACA,IAAAA,EAAc,MAAAvF,EAOd,QANAhB,GAAAwG,EAAAC,EAEA1E,EAAA2E,GACAC,QAAAC,QAAAL,GACAhR,OAAAwM,KAAAwE,GAEAzR,EAAA,EAAiBA,EAAAiN,EAAAnK,OAAiB9C,IAGlC,YAFAkL,EAAA+B,EAAAjN,MAGA0R,EAAAxF,EAAAhB,GACAyG,EAAAF,EAAAvG,GACAD,EAAAiB,EAAAhB,GAGAwG,IAAAC,GACAnI,EAAAkI,IACAlI,EAAAmI,IAEAH,EAAAE,EAAAC,GANAX,EAAA9E,EAAAhB,EAAAyG,GASA,OAAAzF,GAMA,QAAA6F,GACAC,EACAC,EACAC,GAEA,MAAAA,GAoBA,WAEA,GAAAC,GAAA,kBAAAF,GACAA,EAAA/R,KAAAgS,KACAD,EACAG,EAAA,kBAAAJ,GACAA,EAAA9R,KAAAgS,KACAF,CACA,OAAAG,GACAX,EAAAW,EAAAC,GAEAA,GA7BAH,EAGAD,EAQA,WACA,MAAAR,GACA,kBAAAS,KAAA/R,KAAAyC,WAAAsP,EACA,kBAAAD,KAAA9R,KAAAyC,WAAAqP,IAVAC,EAHAD,EA2DA,QAAAK,GACAL,EACAC,GAEA,GAAA5F,GAAA4F,EACAD,EACAA,EAAAlQ,OAAAmQ,GACA5H,MAAAC,QAAA2H,GACAA,GACAA,GACAD,CACA,OAAA3F,GACAiG,EAAAjG,GACAA,EAGA,QAAAiG,GAAAC,GAEA,OADAlG,MACArM,EAAA,EAAiBA,EAAAuS,EAAAzP,OAAkB9C,KACnC,IAAAqM,EAAAtB,QAAAwH,EAAAvS,KACAqM,EAAArJ,KAAAuP,EAAAvS,GAGA,OAAAqM,GAcA,QAAAmG,GACAR,EACAC,EACAC,EACAhH,GAEA,GAAAmB,GAAA5L,OAAAmK,OAAAoH,GAAA,KACA,OAAAC,GAEAhG,EAAAI,EAAA4F,GAEA5F,EAwGA,QAAAoG,GAAA7M,EAAAsM,GACA,GAAAQ,GAAA9M,EAAA8M,KACA,IAAAA,EAAA,CACA,GACA1S,GAAA4J,EAAAtJ,EADA+L,IAEA,IAAAhC,MAAAC,QAAAoI,GAEA,IADA1S,EAAA0S,EAAA5P,OACA9C,KAEA,iBADA4J,EAAA8I,EAAA1S,MAEAM,EAAAqS,GAAA/I,GACAyC,EAAA/L,IAAqBuD,KAAA,WAKlB,IAAA2F,EAAAkJ,GACH,OAAAxH,KAAAwH,GACA9I,EAAA8I,EAAAxH,GACA5K,EAAAqS,GAAAzH,GACAmB,EAAA/L,GAAAkJ,EAAAI,GACAA,GACW/F,KAAA+F,EASXhE,GAAA8M,MAAArG,GAMA,QAAAuG,GAAAhN,EAAAsM,GACA,GAAAW,GAAAjN,EAAAiN,MACA,IAAAA,EAAA,CACA,GAAAC,GAAAlN,EAAAiN,SACA,IAAAxI,MAAAC,QAAAuI,GACA,OAAA7S,GAAA,EAAmBA,EAAA6S,EAAA/P,OAAmB9C,IACtC8S,EAAAD,EAAA7S,KAA+ByR,KAAAoB,EAAA7S,QAE5B,IAAAwJ,EAAAqJ,GACH,OAAA3H,KAAA2H,GAAA,CACA,GAAAjJ,GAAAiJ,EAAA3H,EACA4H,GAAA5H,GAAA1B,EAAAI,GACAqC,GAAkBwF,KAAAvG,GAAYtB,IACnB6H,KAAA7H,KAcX,QAAAmJ,GAAAnN,GACA,GAAAoN,GAAApN,EAAAqN,UACA,IAAAD,EACA,OAAA9H,KAAA8H,GAAA,CACA,GAAAE,GAAAF,EAAA9H,EACA,mBAAAgI,KACAF,EAAA9H,IAAqBpG,KAAAoO,EAAAjP,OAAAiP,KAoBrB,QAAAC,GACAhL,EACAiL,EACAlB,GAuCA,QAAAmB,GAAAnI,GACA,GAAAoI,GAAAC,GAAArI,IAAAsI,EACA5N,GAAAsF,GAAAoI,EAAAnL,EAAA+C,GAAAkI,EAAAlI,GAAAgH,EAAAhH,GAvBA,GAZA,kBAAAkI,KACAA,IAAAxN,SAGA6M,EAAAW,EAAAlB,GACAU,EAAAQ,EAAAlB,GACAa,EAAAK,IAMAA,EAAAK,QACAL,EAAAM,UACAvL,EAAAgL,EAAAhL,EAAAiL,EAAAM,QAAAxB,IAEAkB,EAAAO,QACA,OAAA3T,GAAA,EAAAC,EAAAmT,EAAAO,OAAA7Q,OAA8C9C,EAAAC,EAAOD,IACrDmI,EAAAgL,EAAAhL,EAAAiL,EAAAO,OAAA3T,GAAAkS,EAKA,IACAhH,GADAtF,IAEA,KAAAsF,IAAA/C,GACAkL,EAAAnI,EAEA,KAAAA,IAAAkI,GACAnI,EAAA9C,EAAA+C,IACAmI,EAAAnI,EAOA,OAAAtF,GAQA,QAAAgO,GACAhO,EACA/B,EACAd,EACA8Q,GAGA,mBAAA9Q,GAAA,CAGA,GAAA+Q,GAAAlO,EAAA/B,EAEA,IAAAoH,EAAA6I,EAAA/Q,GAA2B,MAAA+Q,GAAA/Q,EAC3B,IAAAgR,GAAApB,GAAA5P,EACA,IAAAkI,EAAA6I,EAAAC,GAAoC,MAAAD,GAAAC,EACpC,IAAAC,GAAAC,GAAAF,EACA,IAAA9I,EAAA6I,EAAAE,GAAqC,MAAAF,GAAAE,EASrC,OAPAF,GAAA/Q,IAAA+Q,EAAAC,IAAAD,EAAAE,IAcA,QAAAE,IACAhJ,EACAiJ,EACAC,EACAlC,GAEA,GAAAmC,GAAAF,EAAAjJ,GACAoJ,GAAArJ,EAAAmJ,EAAAlJ,GACArC,EAAAuL,EAAAlJ,GAEAqJ,EAAAC,GAAAxN,QAAAqN,EAAAxQ,KACA,IAAA0Q,GAAA,EACA,GAAAD,IAAArJ,EAAAoJ,EAAA,WACAxL,GAAA,MACK,SAAAA,OAAA4L,GAAAvJ,GAAA,CAGL,GAAAwJ,GAAAF,GAAA1K,OAAAuK,EAAAxQ,OACA6Q,EAAA,GAAAH,EAAAG,KACA7L,GAAA,GAKA,OAAAK,KAAAL,EAAA,CACAA,EAAA8L,GAAAzC,EAAAmC,EAAAnJ,EAGA,IAAA0J,GAAAhF,EACAD,IAAA,GACAM,EAAApH,GACA8G,EAAAiF,GASA,MAAA/L,GAMA,QAAA8L,IAAAzC,EAAAmC,EAAAnJ,GAEA,GAAAD,EAAAoJ,EAAA,YAGA,GAAA7G,GAAA6G,EAAA5M,OAYA,OAAAyK,MAAA2C,SAAAT,eACAlL,KAAAgJ,EAAA2C,SAAAT,UAAAlJ,QACAhC,KAAAgJ,EAAA4C,OAAA5J,GAEAgH,EAAA4C,OAAA5J,GAIA,kBAAAsC,IAAA,aAAAuH,GAAAV,EAAAxQ,MACA2J,EAAAtN,KAAAgS,GACA1E,GAqFA,QAAAuH,IAAA3J,GACA,GAAA4J,GAAA5J,KAAA1I,WAAAsS,MAAA,qBACA,OAAAA,KAAA,MAGA,QAAAC,IAAAxJ,EAAAa,GACA,MAAAyI,IAAAtJ,KAAAsJ,GAAAzI,GAGA,QAAAkI,IAAA3Q,EAAAqR,GACA,IAAA7K,MAAAC,QAAA4K,GACA,MAAAD,IAAAC,EAAArR,GAAA,IAEA,QAAA7D,GAAA,EAAAmV,EAAAD,EAAApS,OAA6C9C,EAAAmV,EAASnV,IACtD,GAAAiV,GAAAC,EAAAlV,GAAA6D,GACA,MAAA7D,EAGA,UAgDA,QAAAoV,IAAAC,EAAAnD,EAAAoD,GAGAtH,GACA,KACA,GAAAkE,EAEA,IADA,GAAAqD,GAAArD,EACAqD,IAAAC,SAAA,CACA,GAAAjD,GAAAgD,EAAAV,SAAAY,aACA,IAAAlD,EACA,OAAAvS,GAAA,EAAyBA,EAAAuS,EAAAzP,OAAkB9C,IAC3C,IACA,GAAA0V,IAAA,IAAAnD,EAAAvS,GAAAE,KAAAqV,EAAAF,EAAAnD,EAAAoD,EACA,IAAAI,EAA4B,OACf,MAAA7I,GACb8I,GAAA9I,EAAA0I,EAAA,uBAMAI,GAAAN,EAAAnD,EAAAoD,GACG,QACHlH,KAIA,QAAAwH,IACAC,EACA7N,EACA8N,EACA5D,EACAoD,GAEA,GAAAjJ,EACA,KACAA,EAAAyJ,EAAAD,EAAAlK,MAAA3D,EAAA8N,GAAAD,EAAA3V,KAAA8H,GACAqE,MAAAmE,QAAAtG,EAAAmC,OAAA0J,WACA1J,EAAAjC,MAAA,SAAAyC,GAA8B,MAAAuI,IAAAvI,EAAAqF,EAAAoD,EAAA,sBAG9BjJ,EAAA0J,UAAA,GAEG,MAAAlJ,GACHuI,GAAAvI,EAAAqF,EAAAoD,GAEA,MAAAjJ,GAGA,QAAAsJ,IAAAN,EAAAnD,EAAAoD,GACA,GAAAU,GAAAC,aACA,IACA,MAAAD,IAAAC,aAAA/V,KAAA,KAAAmV,EAAAnD,EAAAoD,GACK,MAAAzI,GAGLA,IAAAwI,GACAa,GAAArJ,EAAA,4BAIAqJ,GAAAb,EAAAnD,EAAAoD,GAGA,QAAAY,IAAAb,EAAAnD,EAAAoD,GAKA,IAAAa,KAAAC,IAAA,mBAAAC,SAGA,KAAAhB,EAFAgB,SAAAC,MAAAjB,GAaA,QAAAkB,MACAC,IAAA,CACA,IAAAC,GAAAC,GAAA5H,MAAA,EACA4H,IAAA5T,OAAA,CACA,QAAA9C,GAAA,EAAiBA,EAAAyW,EAAA3T,OAAmB9C,IACpCyW,EAAAzW,KAqEA,QAAA2W,IAAAC,EAAArL,GACA,GAAAsL,EAiBA,IAhBAH,GAAA1T,KAAA,WACA,GAAA4T,EACA,IACAA,EAAA1W,KAAAqL,GACO,MAAAsB,GACPuI,GAAAvI,EAAAtB,EAAA,gBAEKsL,IACLA,EAAAtL,KAGAiL,KACAA,IAAA,EACAM,OAGAF,GAAA,mBAAAG,SACA,UAAAA,SAAA,SAAAC,GACAH,EAAAG,IAwGA,QAAAC,IAAArN,GACAsN,GAAAtN,EAAAuN,IACAA,GAAAC,QAGA,QAAAF,IAAAtN,EAAAyN,GACA,GAAArX,GAAAiN,EACAqK,EAAAjN,MAAAC,QAAAV,EACA,OAAA0N,IAAA/N,EAAAK,IAAAnJ,OAAA8W,SAAA3N,gBAAA2E,KAAA,CAGA,GAAA3E,EAAAwG,OAAA,CACA,GAAAoH,GAAA5N,EAAAwG,OAAAS,IAAA9N,EACA,IAAAsU,EAAAI,IAAAD,GACA,MAEAH,GAAA/O,IAAAkP,GAEA,GAAAF,EAEA,IADAtX,EAAA4J,EAAA9G,OACA9C,KAAiBkX,GAAAtN,EAAA5J,GAAAqX,OAIjB,KAFApK,EAAAxM,OAAAwM,KAAArD,GACA5J,EAAAiN,EAAAnK,OACA9C,KAAiBkX,GAAAtN,EAAAqD,EAAAjN,IAAAqX,IA4CjB,QAAAK,IAAAC,EAAAzF,GACA,QAAA0F,KACA,GAAAC,GAAAnM,UAEAiM,EAAAC,EAAAD,GACA,KAAAtN,MAAAC,QAAAqN,GAOA,MAAA/B,IAAA+B,EAAA,KAAAjM,UAAAwG,EAAA,eALA,QADAxD,GAAAiJ,EAAA7I,QACA9O,EAAA,EAAqBA,EAAA0O,EAAA5L,OAAmB9C,IACxC4V,GAAAlH,EAAA1O,GAAA,KAAA6X,EAAA3F,EAAA,gBAQA,MADA0F,GAAAD,MACAC,EAGA,QAAAE,IACAC,EACAC,EACA1P,EACA2P,EACAC,EACAhG,GAEA,GAAA5R,GAAAiV,EAAA4C,EAAAC,CACA,KAAA9X,IAAAyX,GACAxC,EAAAwC,EAAAzX,GACA6X,EAAAH,EAAA1X,GACA8X,EAAAC,GAAA/X,GACA0I,EAAAuM,KAKKvM,EAAAmP,IACLnP,EAAAuM,EAAAoC,OACApC,EAAAwC,EAAAzX,GAAAoX,GAAAnC,EAAArD,IAEA9I,EAAAgP,EAAAhL,QACAmI,EAAAwC,EAAAzX,GAAA4X,EAAAE,EAAA9X,KAAAiV,EAAA6C,EAAA1C,UAEApN,EAAA8P,EAAA9X,KAAAiV,EAAA6C,EAAA1C,QAAA0C,EAAAE,QAAAF,EAAAG,SACKhD,IAAA4C,IACLA,EAAAR,IAAApC,EACAwC,EAAAzX,GAAA6X,GAGA,KAAA7X,IAAA0X,GACAhP,EAAA+O,EAAAzX,MACA8X,EAAAC,GAAA/X,GACA2X,EAAAG,EAAA9X,KAAA0X,EAAA1X,GAAA8X,EAAA1C,UAOA,QAAA8C,IAAAhL,EAAAiL,EAAA1Q,GAOA,QAAA2Q,KACA3Q,EAAA4D,MAAAhJ,KAAA+I,WAGAxH,EAAA0T,EAAAD,IAAAe,GAVAlL,YAAAe,MACAf,IAAAoB,KAAA7G,OAAAyF,EAAAoB,KAAA7G,SAEA,IAAA6P,GACAe,EAAAnL,EAAAiL,EASAzP,GAAA2P,GAEAf,EAAAF,IAAAgB,IAGAvP,EAAAwP,EAAAhB,MAAAvO,EAAAuP,EAAAC,SAEAhB,EAAAe,EACAf,EAAAD,IAAA3U,KAAA0V,IAGAd,EAAAF,IAAAiB,EAAAD,IAIAd,EAAAgB,QAAA,EACApL,EAAAiL,GAAAb,EAKA,QAAAiB,IACAjK,EACAb,EACAY,GAKA,GAAAwF,GAAApG,EAAAnI,QAAA8M,KACA,KAAA1J,EAAAmL,GAAA,CAGA,GAAA9H,MACAyM,EAAAlK,EAAAkK,MACApG,EAAA9D,EAAA8D,KACA,IAAAvJ,EAAA2P,IAAA3P,EAAAuJ,GACA,OAAAxH,KAAAiJ,GAAA,CACA,GAAA4E,GAAAtE,GAAAvJ,EAiBA8N,IAAA3M,EAAAqG,EAAAxH,EAAA6N,GAAA,IACAC,GAAA3M,EAAAyM,EAAA5N,EAAA6N,GAAA,GAGA,MAAA1M,IAGA,QAAA2M,IACA3M,EACA4M,EACA/N,EACA6N,EACAG,GAEA,GAAA/P,EAAA8P,GAAA,CACA,GAAAhO,EAAAgO,EAAA/N,GAKA,MAJAmB,GAAAnB,GAAA+N,EAAA/N,GACAgO,SACAD,GAAA/N,IAEA,CACK,IAAAD,EAAAgO,EAAAF,GAKL,MAJA1M,GAAAnB,GAAA+N,EAAAF,GACAG,SACAD,GAAAF,IAEA,EAGA,SAiBA,QAAAI,IAAAtK,GACA,OAAA7O,GAAA,EAAiBA,EAAA6O,EAAA/L,OAAqB9C,IACtC,GAAAqK,MAAAC,QAAAuE,EAAA7O,IACA,MAAAqK,OAAAnJ,UAAAY,OAAA6J,SAAAkD,EAGA,OAAAA,GAOA,QAAAuK,IAAAvK,GACA,MAAAvF,GAAAuF,IACAP,EAAAO,IACAxE,MAAAC,QAAAuE,GACAwK,GAAAxK,OACA3F,GAGA,QAAAoQ,IAAAC,GACA,MAAApQ,GAAAoQ,IAAApQ,EAAAoQ,EAAAxK,OAAA1F,EAAAkQ,EAAAlK,WAGA,QAAAgK,IAAAxK,EAAA2K,GACA,GACAxZ,GAAAI,EAAAqZ,EAAAC,EADArN,IAEA,KAAArM,EAAA,EAAaA,EAAA6O,EAAA/L,OAAqB9C,IAClCI,EAAAyO,EAAA7O,GACAgJ,EAAA5I,IAAA,iBAAAA,KACAqZ,EAAApN,EAAAvJ,OAAA,EACA4W,EAAArN,EAAAoN,GAEApP,MAAAC,QAAAlK,GACAA,EAAA0C,OAAA,IACA1C,EAAAiZ,GAAAjZ,GAAAoZ,GAAA,QAAAxZ,GAEAsZ,GAAAlZ,EAAA,KAAAkZ,GAAAI,KACArN,EAAAoN,GAAAnL,EAAAoL,EAAA3K,KAAA3O,EAAA,GAAA2O,MACA3O,EAAAuZ,SAEAtN,EAAArJ,KAAA2I,MAAAU,EAAAjM,IAEKkJ,EAAAlJ,GACLkZ,GAAAI,GAIArN,EAAAoN,GAAAnL,EAAAoL,EAAA3K,KAAA3O,GACO,KAAAA,GAEPiM,EAAArJ,KAAAsL,EAAAlO,IAGAkZ,GAAAlZ,IAAAkZ,GAAAI,GAEArN,EAAAoN,GAAAnL,EAAAoL,EAAA3K,KAAA3O,EAAA2O,OAGA3F,EAAAyF,EAAA+K,WACAzQ,EAAA/I,EAAAuO,MACA3F,EAAA5I,EAAA8K,MACA/B,EAAAqQ,KACApZ,EAAA8K,IAAA,UAAAsO,EAAA,IAAAxZ,EAAA,MAEAqM,EAAArJ,KAAA5C,IAIA,OAAAiM,GAKA,QAAAwN,IAAA3H,GACA,GAAA4H,GAAA5H,EAAA2C,SAAAiF,OACAA,KACA5H,EAAA6H,UAAA,kBAAAD,GACAA,EAAA5Z,KAAAgS,GACA4H,GAIA,QAAAE,IAAA9H,GACA,GAAA+H,GAAAC,GAAAhI,EAAA2C,SAAAhC,OAAAX,EACA+H,KACAtK,GAAA,GACAlP,OAAAwM,KAAAgN,GAAAE,QAAA,SAAAjP,GAYAwF,EAAAwB,EAAAhH,EAAA+O,EAAA/O,MAGAyE,GAAA,IAIA,QAAAuK,IAAArH,EAAAX,GACA,GAAAW,EAAA,CAOA,OALAoH,GAAAxZ,OAAAmK,OAAA,MACAqC,EAAA2E,GACAC,QAAAC,QAAAe,GACApS,OAAAwM,KAAA4F,GAEA7S,EAAA,EAAmBA,EAAAiN,EAAAnK,OAAiB9C,IAAA,CACpC,GAAAkL,GAAA+B,EAAAjN,EAEA,eAAAkL,EAAA,CAGA,IAFA,GAAAkP,GAAAvH,EAAA3H,GAAAuG,KACAxP,EAAAiQ,EACAjQ,GAAA,CACA,GAAAA,EAAA8X,WAAA9O,EAAAhJ,EAAA8X,UAAAK,GAAA,CACAH,EAAA/O,GAAAjJ,EAAA8X,UAAAK,EACA,OAEAnY,IAAAuT,QAEA,IAAAvT,GACA,WAAA4Q,GAAA3H,GAAA,CACA,GAAAmP,GAAAxH,EAAA3H,GAAAzD,OACAwS,GAAA/O,GAAA,kBAAAmP,GACAA,EAAAna,KAAAgS,GACAmI,IAMA,MAAAJ,IAWA,QAAAK,IACAzL,EACA7G,GAEA,IAAA6G,MAAA/L,OACA,QAGA,QADAyX,MACAva,EAAA,EAAAC,EAAA4O,EAAA/L,OAAsC9C,EAAAC,EAAOD,IAAA,CAC7C,GAAAoT,GAAAvE,EAAA7O,GACA4O,EAAAwE,EAAAxE,IAOA,IALAA,KAAAkK,OAAAlK,EAAAkK,MAAA0B,YACA5L,GAAAkK,MAAA0B,KAIApH,EAAApL,aAAAoL,EAAA9D,YAAAtH,IACA4G,GAAA,MAAAA,EAAA4L,MAUAD,EAAA9S,UAAA8S,EAAA9S,aAAAzE,KAAAoQ,OATA,CACA,GAAA9S,GAAAsO,EAAA4L,KACAA,EAAAD,EAAAja,KAAAia,EAAAja,MACA,cAAA8S,EAAAzE,IACA6L,EAAAxX,KAAA2I,MAAA6O,EAAApH,EAAAvE,cAEA2L,EAAAxX,KAAAoQ,IAOA,OAAAqH,KAAAF,GACAA,EAAAE,GAAA7N,MAAA8N,WACAH,GAAAE,EAGA,OAAAF,GAGA,QAAAG,IAAAnB,GACA,MAAAA,GAAAlK,YAAAkK,EAAArK,cAAA,MAAAqK,EAAAxK,KAKA,QAAA4L,IACAJ,EACAK,EACAC,GAEA,GAAAxO,GACAyO,EAAAra,OAAAwM,KAAA2N,GAAA9X,OAAA,EACAiY,EAAAR,MAAAS,SAAAF,EACA5P,EAAAqP,KAAAU,IACA,IAAAV,EAEG,IAAAA,EAAAW,YAEH,MAAAX,GAAAW,WACG,IACHH,GACAF,GACAA,IAAAM,IACAjQ,IAAA2P,EAAAI,OACAH,IACAD,EAAAO,WAIA,MAAAP,EAEAxO,KACA,QAAAgP,KAAAd,GACAA,EAAAc,IAAA,MAAAA,EAAA,KACAhP,EAAAgP,GAAAC,GAAAV,EAAAS,EAAAd,EAAAc,SAnBAhP,KAwBA,QAAAkP,KAAAX,GACAW,IAAAlP,KACAA,EAAAkP,GAAAC,GAAAZ,EAAAW,GAWA,OANAhB,IAAA9Z,OAAA8P,aAAAgK,KACA,EAAAW,YAAA7O,GAEAmB,EAAAnB,EAAA,UAAA0O,GACAvN,EAAAnB,EAAA,OAAAnB,GACAsC,EAAAnB,EAAA,aAAAyO,GACAzO,EAGA,QAAAiP,IAAAV,EAAA1P,EAAAE,GACA,GAAA0H,GAAA,WACA,GAAAzG,GAAAX,UAAA5I,OAAAsI,EAAAO,MAAA,KAAAD,WAAAN,KAIA,OAHAiB,MAAA,gBAAAA,KAAAhC,MAAAC,QAAA+B,IACAA,GACA+M,GAAA/M,GACAA,IACA,IAAAA,EAAAvJ,QACA,IAAAuJ,EAAAvJ,QAAAuJ,EAAA,GAAAgD,eACAnG,GACAmD,EAYA,OAPAjB,GAAAqQ,OACAhb,OAAAC,eAAAka,EAAA1P,GACArK,IAAAiS,EACAlS,YAAA,EACAD,cAAA,IAGAmS,EAGA,QAAA0I,IAAAjB,EAAArP,GACA,kBAAsB,MAAAqP,GAAArP,IAQtB,QAAAwQ,IACA9R,EACAlC,GAEA,GAAAsE,GAAAhM,EAAAC,EAAAgN,EAAA/B,CACA,IAAAb,MAAAC,QAAAV,IAAA,gBAAAA,GAEA,IADAoC,EAAA,GAAA3B,OAAAT,EAAA9G,QACA9C,EAAA,EAAAC,EAAA2J,EAAA9G,OAA+B9C,EAAAC,EAAOD,IACtCgM,EAAAhM,GAAA0H,EAAAkC,EAAA5J,UAEG,oBAAA4J,GAEH,IADAoC,EAAA,GAAA3B,OAAAT,GACA5J,EAAA,EAAeA,EAAA4J,EAAS5J,IACxBgM,EAAAhM,GAAA0H,EAAA1H,EAAA,EAAAA,OAEG,IAAAuJ,EAAAK,GACH,GAAAgI,IAAAhI,EAAA+R,OAAAC,UAAA,CACA5P,IAGA,KAFA,GAAA4P,GAAAhS,EAAA+R,OAAAC,YACA3B,EAAA2B,EAAAC,QACA5B,EAAA6B,MACA9P,EAAAhJ,KAAA0E,EAAAuS,EAAApR,MAAAmD,EAAAlJ,SACAmX,EAAA2B,EAAAC,WAKA,KAFA5O,EAAAxM,OAAAwM,KAAArD,GACAoC,EAAA,GAAA3B,OAAA4C,EAAAnK,QACA9C,EAAA,EAAAC,EAAAgN,EAAAnK,OAAkC9C,EAAAC,EAAOD,IACzCkL,EAAA+B,EAAAjN,GACAgM,EAAAhM,GAAA0H,EAAAkC,EAAAsB,KAAAlL,EAQA,OAJAmJ,GAAA6C,KACAA,MAEA,EAAA4N,UAAA,EACA5N,EAQA,QAAA+P,IACAzb,EACA0b,EACAtJ,EACAuJ,GAEA,GACAC,GADAC,EAAAxZ,KAAAyZ,aAAA9b,EAEA6b,IACAzJ,QACAuJ,IAOAvJ,EAAAzG,OAA8BgQ,GAAAvJ,IAE9BwJ,EAAAC,EAAAzJ,IAAAsJ,GAEAE,EAAAvZ,KAAA0Z,OAAA/b,IAAA0b,CAGA,IAAA/N,GAAAyE,KAAA8H,IACA,OAAAvM,GACAtL,KAAA2Z,eAAA,YAA4C9B,KAAAvM,GAAeiO,GAE3DA,EASA,QAAAK,IAAAxZ,GACA,MAAA6Q,GAAAjR,KAAAkS,SAAA,UAAA9R,GAAA,IAAAyZ,GAKA,QAAAC,IAAAC,EAAAC,GACA,MAAAtS,OAAAC,QAAAoS,IACA,IAAAA,EAAA3R,QAAA4R,GAEAD,IAAAC,EASA,QAAAC,IACAC,EACA3R,EACA4R,EACAC,EACAC,GAEA,GAAAC,GAAAjH,GAAAkH,SAAAhS,IAAA4R,CACA,OAAAE,IAAAD,IAAA/G,GAAAkH,SAAAhS,GACAuR,GAAAO,EAAAD,GACGE,EACHR,GAAAQ,EAAAJ,GACGE,EACHtI,GAAAsI,KAAA7R,MADG,GAUH,QAAAiS,IACAvO,EACAD,EACA9F,EACAuU,EACAC,GAEA,GAAAxU,EACA,GAAAU,EAAAV,GAKK,CACLwB,MAAAC,QAAAzB,KACAA,EAAAuD,EAAAvD,GAEA,IAAAoQ,EA4BA,QAAA/N,KAAArC,IA3BA,SAAAqC,GACA,GACA,UAAAA,GACA,UAAAA,GACAoS,GAAApS,GAEA+N,EAAArK,MACS,CACT,GAAA/K,GAAA+K,EAAAkK,OAAAlK,EAAAkK,MAAAjV,IACAoV,GAAAmE,GAAApH,GAAAuH,YAAA5O,EAAA9K,EAAAqH,GACA0D,EAAA4O,WAAA5O,EAAA4O,aACA5O,EAAAkK,QAAAlK,EAAAkK,UAEA,GAAA2E,GAAA9K,GAAAzH,GACAwS,EAAAjJ,GAAAvJ,EACA,MAAAuS,IAAAxE,IAAAyE,IAAAzE,MACAA,EAAA/N,GAAArC,EAAAqC,GAEAmS,GAAA,EACAzO,EAAAmJ,KAAAnJ,EAAAmJ,QACA,UAAA7M,GAAA,SAAAyS,GACA9U,EAAAqC,GAAAyS,KAMAzS,QAGA,MAAA0D,GAQA,QAAAgP,IACAzY,EACA0Y,GAEA,GAAA1S,GAAAxI,KAAAmb,eAAAnb,KAAAmb,iBACAC,EAAA5S,EAAAhG,EAGA,OAAA4Y,KAAAF,EACAE,GAGAA,EAAA5S,EAAAhG,GAAAxC,KAAAkS,SAAAlN,gBAAAxC,GAAAjF,KACAyC,KAAAqb,aACA,KACArb,MAEAsb,GAAAF,EAAA,aAAA5Y,GAAA,GACA4Y,GAOA,QAAAG,IACAH,EACA5Y,EACA+F,GAGA,MADA+S,IAAAF,EAAA,WAAA5Y,GAAA+F,EAAA,IAAAA,EAAA,QACA6S,EAGA,QAAAE,IACAF,EACA7S,EACAiT,GAEA,GAAA9T,MAAAC,QAAAyT,GACA,OAAA/d,GAAA,EAAmBA,EAAA+d,EAAAjb,OAAiB9C,IACpC+d,EAAA/d,IAAA,gBAAA+d,GAAA/d,IACAoe,GAAAL,EAAA/d,GAAAkL,EAAA,IAAAlL,EAAAme,OAIAC,IAAAL,EAAA7S,EAAAiT,GAIA,QAAAC,IAAA7E,EAAArO,EAAAiT,GACA5E,EAAAnK,UAAA,EACAmK,EAAArO,MACAqO,EAAA4E,SAKA,QAAAE,IAAAzP,EAAA/F,GACA,GAAAA,EACA,GAAAW,EAAAX,GAKK,CACL,GAAAkP,GAAAnJ,EAAAmJ,GAAAnJ,EAAAmJ,GAAA9L,KAA4C2C,EAAAmJ,MAC5C,QAAA7M,KAAArC,GAAA,CACA,GAAAL,GAAAuP,EAAA7M,GACAoT,EAAAzV,EAAAqC,EACA6M,GAAA7M,GAAA1C,KAAA1G,OAAA0G,EAAA8V,WAIA,MAAA1P,GAKA,QAAA2P,IACA5G,EACAtL,EAEAmS,EACAC,GAEApS,MAAgB2O,SAAAwD,EAChB,QAAAxe,GAAA,EAAiBA,EAAA2X,EAAA7U,OAAgB9C,IAAA,CACjC,GAAAwa,GAAA7C,EAAA3X,EACAqK,OAAAC,QAAAkQ,GACA+D,GAAA/D,EAAAnO,EAAAmS,GACKhE,IAELA,EAAAiB,QACAjB,EAAApP,GAAAqQ,OAAA,GAEApP,EAAAmO,EAAAtP,KAAAsP,EAAApP,IAMA,MAHAqT,KACA,EAAAxD,KAAAwD,GAEApS,EAKA,QAAAqS,IAAAC,EAAAC,GACA,OAAA5e,GAAA,EAAiBA,EAAA4e,EAAA9b,OAAmB9C,GAAA,GACpC,GAAAkL,GAAA0T,EAAA5e,EACA,iBAAAkL,QACAyT,EAAAC,EAAA5e,IAAA4e,EAAA5e,EAAA,IASA,MAAA2e,GAMA,QAAAE,IAAAhW,EAAAiW,GACA,sBAAAjW,GAAAiW,EAAAjW,IAKA,QAAAkW,IAAA9Q,GACAA,EAAA+Q,GAAAd,GACAjQ,EAAAgR,GAAA1U,EACA0D,EAAAiR,GAAAxc,EACAuL,EAAAkR,GAAAzD,GACAzN,EAAAmR,GAAArD,GACA9N,EAAAoR,GAAA9S,EACA0B,EAAAqR,GAAAnS,EACAc,EAAAsR,GAAA3B,GACA3P,EAAAuR,GAAAjD,GACAtO,EAAAwR,GAAA7C,GACA3O,EAAAyR,GAAAvC,GACAlP,EAAA0R,GAAArR,EACAL,EAAA2R,GAAAC,GACA5R,EAAA6R,GAAAvB,GACAtQ,EAAA8R,GAAA1B,GACApQ,EAAA+R,GAAAtB,GACAzQ,EAAAgS,GAAApB,GAKA,QAAAqB,IACAtR,EACA8D,EACA7D,EACA1G,EACA4F,GAEA,GAKAoS,GALAC,EAAAzd,KAEAiD,EAAAmI,EAAAnI,OAIAqF,GAAA9C,EAAA,SACAgY,EAAA1f,OAAAmK,OAAAzC,GAEAgY,EAAAE,UAAAlY,IAKAgY,EAAAhY,EAEAA,IAAAkY,UAEA,IAAAC,GAAAlX,EAAAxD,EAAAgC,WACA2Y,GAAAD,CAEA3d,MAAAiM,OACAjM,KAAA+P,QACA/P,KAAAkM,WACAlM,KAAAwF,SACAxF,KAAA6d,UAAA5R,EAAAmJ,IAAAoD,GACAxY,KAAA8d,WAAAvG,GAAAtU,EAAAiN,OAAA1K,GACAxF,KAAA4X,MAAA,WAOA,MANA6F,GAAA/D,QACA1B,GACA/L,EAAA8R,YACAN,EAAA/D,OAAA/B,GAAAzL,EAAA1G,IAGAiY,EAAA/D,QAGA5b,OAAAC,eAAAiC,KAAA,eACA/B,YAAA,EACAC,IAAA,WACA,MAAA8Z,IAAA/L,EAAA8R,YAAA/d,KAAA4X,YAKA+F,IAEA3d,KAAAkS,SAAAjP,EAEAjD,KAAA0Z,OAAA1Z,KAAA4X,QACA5X,KAAAyZ,aAAAzB,GAAA/L,EAAA8R,YAAA/d,KAAA0Z,SAGAzW,EAAAkC,SACAnF,KAAAge,GAAA,SAAAlV,EAAAa,EAAAlM,EAAAC,GACA,GAAAoO,GAAA7K,GAAAuc,EAAA1U,EAAAa,EAAAlM,EAAAC,EAAAkgB,EAKA,OAJA9R,KAAApE,MAAAC,QAAAmE,KACAA,EAAAe,UAAA5J,EAAAkC,SACA2G,EAAAa,UAAAnH,GAEAsG,GAGA9L,KAAAge,GAAA,SAAAlV,EAAAa,EAAAlM,EAAAC,GAAqC,MAAAuD,IAAAuc,EAAA1U,EAAAa,EAAAlM,EAAAC,EAAAkgB,IAMrC,QAAAK,IACA7S,EACAqG,EACAxF,EACAuR,EACAtR,GAEA,GAAAjJ,GAAAmI,EAAAnI,QACA8M,KACAyB,EAAAvO,EAAA8M,KACA,IAAAvJ,EAAAgL,GACA,OAAAjJ,KAAAiJ,GACAzB,EAAAxH,GAAAgJ,GAAAhJ,EAAAiJ,EAAAC,GAAA+G,QAGAhS,GAAAyF,EAAAkK,QAA4B+H,GAAAnO,EAAA9D,EAAAkK,OAC5B3P,EAAAyF,EAAA8D,QAA4BmO,GAAAnO,EAAA9D,EAAA8D,MAG5B,IAAAoO,GAAA,GAAAZ,IACAtR,EACA8D,EACA7D,EACAsR,EACApS,GAGAU,EAAA7I,EAAA8B,OAAAxH,KAAA,KAAA4gB,EAAAH,GAAAG,EAEA,IAAArS,YAAAF,IACA,MAAAwS,IAAAtS,EAAAG,EAAAkS,EAAA3Y,OAAAvC,EAAAkb,EACG,IAAAzW,MAAAC,QAAAmE,GAAA,CAGH,OAFAuS,GAAA5H,GAAA3K,OACApC,EAAA,GAAAhC,OAAA2W,EAAAle,QACA9C,EAAA,EAAmBA,EAAAghB,EAAAle,OAAmB9C,IACtCqM,EAAArM,GAAA+gB,GAAAC,EAAAhhB,GAAA4O,EAAAkS,EAAA3Y,OAAAvC,EAAAkb,EAEA,OAAAzU,IAIA,QAAA0U,IAAAtS,EAAAG,EAAAuR,EAAAva,EAAAkb,GAIA,GAAAG,GAAAzS,EAAAC,EASA,OARAwS,GAAA3R,UAAA6Q,EACAc,EAAA1R,UAAA3J,EAIAgJ,EAAA4L,QACAyG,EAAArS,OAAAqS,EAAArS,UAAmC4L,KAAA5L,EAAA4L,MAEnCyG,EAGA,QAAAJ,IAAA3U,EAAAuF,GACA,OAAAvG,KAAAuG,GACAvF,EAAAyG,GAAAzH,IAAAuG,EAAAvG,GA+EA,QAAAgW,IACAnT,EACAa,EACA5G,EACA6G,EACAF,GAEA,IAAA3F,EAAA+E,GAAA,CAIA,GAAAoT,GAAAnZ,EAAA6M,SAAApB,KASA,IANAlK,EAAAwE,KACAA,EAAAoT,EAAAlV,OAAA8B,IAKA,kBAAAA,GAAA,CAQA,GAAAmB,EACA,IAAAlG,EAAA+E,EAAAqT,OACAlS,EAAAnB,MAEA7E,MADA6E,EAAAsT,GAAAnS,EAAAiS,KAKA,MAAAG,IACApS,EACAN,EACA5G,EACA6G,EACAF,EAKAC,SAIA2S,GAAAxT,GAGA5E,EAAAyF,EAAA4S,QACAC,GAAA1T,EAAAnI,QAAAgJ,EAIA,IAAAwF,GAAAyE,GAAAjK,EAAAb,EAAAY,EAGA,IAAAvF,EAAA2E,EAAAnI,QAAAiC,YACA,MAAA+Y,IAAA7S,EAAAqG,EAAAxF,EAAA5G,EAAA6G,EAKA,IAAA2R,GAAA5R,EAAAmJ,EAKA,IAFAnJ,EAAAmJ,GAAAnJ,EAAA8S,SAEAtY,EAAA2E,EAAAnI,QAAA+b,UAAA,CAKA,GAAAnH,GAAA5L,EAAA4L,IACA5L,MACA4L,IACA5L,EAAA4L,QAKAoH,GAAAhT,EAGA,IAAAtO,GAAAyN,EAAAnI,QAAAtF,MAAAqO,CAQA,OAPA,IAAAJ,IACA,iBAAAR,EAAA,KAAAzN,EAAA,IAAAA,EAAA,IACAsO,MAAA1F,iBAAAlB,GACK+F,OAAAqG,YAAAoM,YAAA7R,MAAAE,YACLK,KAMA,QAAA2S,IACApT,EACAtG,GAEA,GAAAvC,IACAkc,cAAA,EACAC,aAAAtT,EACAtG,UAGA6Z,EAAAvT,EAAAG,KAAAoT,cAKA,OAJA7Y,GAAA6Y,KACApc,EAAA8B,OAAAsa,EAAAta,OACA9B,EAAA+B,gBAAAqa,EAAAra,iBAEA,GAAA8G,GAAAQ,iBAAAlB,KAAAnI,GAGA,QAAAgc,IAAAhT,GAEA,OADA2D,GAAA3D,EAAA7G,OAAA6G,EAAA7G,SACA/H,EAAA,EAAiBA,EAAAiiB,GAAAnf,OAAyB9C,IAAA,CAC1C,GAAAkL,GAAA+W,GAAAjiB,GACAwI,EAAA+J,EAAArH,GACAgX,EAAAC,GAAAjX,EACA1C,KAAA0Z,GAAA1Z,KAAA4Z,UACA7P,EAAArH,GAAA1C,EAAA6Z,GAAAH,EAAA1Z,GAAA0Z,IAKA,QAAAG,IAAAC,EAAAC,GACA,GAAA3J,GAAA,SAAAnN,EAAAa,GAEAgW,EAAA7W,EAAAa,GACAiW,EAAA9W,EAAAa,GAGA,OADAsM,GAAAwJ,SAAA,EACAxJ,EAKA,QAAA6I,IAAA7b,EAAAgJ,GACA,GAAAyF,GAAAzO,EAAA4b,OAAA5b,EAAA4b,MAAAnN,MAAA,QACA+D,EAAAxS,EAAA4b,OAAA5b,EAAA4b,MAAApJ,OAAA,SACGxJ,EAAAkK,QAAAlK,EAAAkK,WAA+BzE,GAAAzF,EAAA4S,MAAA3Y,KAClC,IAAAkP,GAAAnJ,EAAAmJ,KAAAnJ,EAAAmJ,OACAvP,EAAAuP,EAAAK,GACAoK,EAAA5T,EAAA4S,MAAAgB,QACArZ,GAAAX,IAEA6B,MAAAC,QAAA9B,IACA,IAAAA,EAAAuC,QAAAyX,GACAha,IAAAga,KAEAzK,EAAAK,IAAAoK,GAAA1gB,OAAA0G,IAGAuP,EAAAK,GAAAoK,EAWA,QAAA5e,IACAoE,EACA2G,EACAC,EACAC,EACA4T,EACAC,GAUA,OARArY,MAAAC,QAAAsE,IAAAtF,EAAAsF,MACA6T,EAAA5T,EACAA,EAAAD,EACAA,MAAA1F,IAEAE,EAAAsZ,KACAD,EAAAE,IAEAC,GAAA5a,EAAA2G,EAAAC,EAAAC,EAAA4T,GAGA,QAAAG,IACA5a,EACA2G,EACAC,EACAC,EACA4T,GAEA,GAAAtZ,EAAAyF,IAAAzF,EAAA,EAAAiH,QAMA,MAAAyP,KAMA,IAHA1W,EAAAyF,IAAAzF,EAAAyF,EAAAiU,MACAlU,EAAAC,EAAAiU,KAEAlU,EAEA,MAAAkR,KAeAxV,OAAAC,QAAAuE,IACA,kBAAAA,GAAA,KAEAD,QACAA,EAAA8R,aAAwBjZ,QAAAoH,EAAA,IACxBA,EAAA/L,OAAA,GAEA2f,IAAAE,GACA9T,EAAAuK,GAAAvK,GACG4T,IAAAK,KACHjU,EAAAsK,GAAAtK,GAEA,IAAAJ,GAAAU,CACA,oBAAAR,GAAA,CACA,GAAAZ,EACAoB,GAAAnH,EAAAC,QAAAD,EAAAC,OAAAkH,IAAA6G,GAAA+M,gBAAApU,GAGAF,EAFAuH,GAAAgN,cAAArU,GAEA,GAAAJ,IACAyH,GAAAiN,qBAAAtU,GAAAC,EAAAC,MACA3F,UAAAlB,GAEK4G,KAAAsU,MAAA/Z,EAAA4E,EAAA6F,EAAA5L,EAAA6M,SAAA,aAAAlG,IAOL,GAAAJ,IACAI,EAAAC,EAAAC,MACA3F,UAAAlB,GAPAkZ,GAAAnT,EAAAa,EAAA5G,EAAA6G,EAAAF,OAYAF,GAAAyS,GAAAvS,EAAAC,EAAA5G,EAAA6G,EAEA,OAAAxE,OAAAC,QAAAmE,GACAA,EACGtF,EAAAsF,IACHtF,EAAAgG,IAAoBgU,GAAA1U,EAAAU,GACpBhG,EAAAyF,IAAsBwU,GAAAxU,GACtBH,GAEAoR,KAIA,QAAAsD,IAAA1U,EAAAU,EAAAkU,GAOA,GANA5U,EAAAU,KACA,kBAAAV,EAAAE,MAEAQ,MAAAjG,GACAma,GAAA,GAEAla,EAAAsF,EAAAI,UACA,OAAA7O,GAAA,EAAAC,EAAAwO,EAAAI,SAAA/L,OAA8C9C,EAAAC,EAAOD,IAAA,CACrD,GAAAoT,GAAA3E,EAAAI,SAAA7O,EACAmJ,GAAAiK,EAAAzE,OACA3F,EAAAoK,EAAAjE,KAAA/F,EAAAia,IAAA,QAAAjQ,EAAAzE,MACAwU,GAAA/P,EAAAjE,EAAAkU,IASA,QAAAD,IAAAxU,GACArF,EAAAqF,EAAA0U,QACArM,GAAArI,EAAA0U,OAEA/Z,EAAAqF,EAAA2U,QACAtM,GAAArI,EAAA2U,OAMA,QAAAC,IAAAtR,GACAA,EAAAuR,OAAA,KACAvR,EAAA4L,aAAA,IACA,IAAAlY,GAAAsM,EAAA2C,SACA6O,EAAAxR,EAAAjK,OAAArC,EAAAmc,aACAjB,EAAA4C,KAAA1b,OACAkK,GAAAmK,OAAA/B,GAAA1U,EAAA+d,gBAAA7C,GACA5O,EAAAkK,aAAAjB,GAKAjJ,EAAAyO,GAAA,SAAAlV,EAAAa,EAAAlM,EAAAC,GAAiC,MAAAuD,IAAAsO,EAAAzG,EAAAa,EAAAlM,EAAAC,GAAA,IAGjC6R,EAAAoK,eAAA,SAAA7Q,EAAAa,EAAAlM,EAAAC,GAA6C,MAAAuD,IAAAsO,EAAAzG,EAAAa,EAAAlM,EAAAC,GAAA,GAI7C,IAAAujB,GAAAF,KAAA9U,IAWA8B,GAAAwB,EAAA,SAAA0R,KAAA9K,OAAAqC,GAAA,SACAzK,EAAAwB,EAAA,aAAAtM,EAAAie,kBAAA1I,GAAA,SAgFA,QAAA2I,IAAAC,EAAAC,GAOA,OALAD,EAAAhjB,YACA6Q,IAAA,WAAAmS,EAAApI,OAAAsI,gBAEAF,IAAAtc,SAEA8B,EAAAwa,GACAC,EAAA/X,OAAA8X,GACAA,EAGA,QAAAzC,IACA4C,EACAtV,EACA5G,EACA6G,EACAF,GAEA,GAAA4K,GAAAsG,IAGA,OAFAtG,GAAArK,aAAAgV,EACA3K,EAAA9J,WAAoBb,OAAA5G,UAAA6G,WAAAF,OACpB4K,EAGA,QAAA8H,IACA6C,EACA/C,GAEA,GAAA/X,EAAA8a,EAAA5N,QAAAnN,EAAA+a,EAAAC,WACA,MAAAD,GAAAC,SAGA,IAAAhb,EAAA+a,EAAAE,UACA,MAAAF,GAAAE,QAGA,IAAAC,GAAAC,EAMA,IALAD,GAAAlb,EAAA+a,EAAAK,UAAA,IAAAL,EAAAK,OAAAxZ,QAAAsZ,IAEAH,EAAAK,OAAAvhB,KAAAqhB,GAGAjb,EAAA8a,EAAAM,UAAArb,EAAA+a,EAAAO,aACA,MAAAP,GAAAO,WAGA,IAAAJ,IAAAlb,EAAA+a,EAAAK,QAAA,CACA,GAAAA,GAAAL,EAAAK,QAAAF,GACAK,GAAA,EACAC,EAAA,KACAC,EAAA,IAEK,GAAAC,IAAA,4BAA2C,MAAA3gB,GAAAqgB,EAAAF,IAEhD,IAAAS,GAAA,SAAAC,GACA,OAAA/kB,GAAA,EAAAC,EAAAskB,EAAAzhB,OAAwC9C,EAAAC,EAAOD,IAC/CukB,EAAAvkB,GAAAglB,cAGAD,KACAR,EAAAzhB,OAAA,EACA,OAAA6hB,IACAM,aAAAN,GACAA,EAAA,MAEA,OAAAC,IACAK,aAAAL,GACAA,EAAA,QAKA5N,EAAA5J,EAAA,SAAAf,GAEA6X,EAAAE,SAAAN,GAAAzX,EAAA8U,GAGAuD,EAGAH,EAAAzhB,OAAA,EAFAgiB,GAAA,KAMAI,EAAA9X,EAAA,SAAA+X,GAKAhc,EAAA+a,EAAAC,aACAD,EAAA5N,OAAA,EACAwO,GAAA,MAIAzY,EAAA6X,EAAAlN,EAAAkO,EA+CA,OA7CA3b,GAAA8C,KACAnC,EAAAmC,GAEArD,EAAAkb,EAAAE,WACA/X,EAAAlC,KAAA6M,EAAAkO,GAEOhb,EAAAmC,EAAA+Y,aACP/Y,EAAA+Y,UAAAjb,KAAA6M,EAAAkO,GAEA/b,EAAAkD,EAAAiK,SACA4N,EAAAC,UAAAL,GAAAzX,EAAAiK,MAAA6K,IAGAhY,EAAAkD,EAAAmY,WACAN,EAAAO,YAAAX,GAAAzX,EAAAmY,QAAArD,GACA,IAAA9U,EAAAgZ,MACAnB,EAAAM,SAAA,EAEAG,EAAAW,WAAA,WACAX,EAAA,KACA3b,EAAAkb,EAAAE,WAAApb,EAAAkb,EAAA5N,SACA4N,EAAAM,SAAA,EACAM,GAAA,KAEazY,EAAAgZ,OAAA,MAIblc,EAAAkD,EAAAkZ,WACAX,EAAAU,WAAA,WACAV,EAAA,KACA5b,EAAAkb,EAAAE,WACAc,EAGA,OAGW7Y,EAAAkZ,YAKXb,GAAA,EAEAR,EAAAM,QACAN,EAAAO,YACAP,EAAAE,UAMA,QAAAoB,IAAAjM,GACA,MAAAA,GAAAlK,WAAAkK,EAAArK,aAKA,QAAAuW,IAAA5W,GACA,GAAAxE,MAAAC,QAAAuE,GACA,OAAA7O,GAAA,EAAmBA,EAAA6O,EAAA/L,OAAqB9C,IAAA,CACxC,GAAAI,GAAAyO,EAAA7O,EACA,IAAAmJ,EAAA/I,KAAA+I,EAAA/I,EAAA6O,mBAAAuW,GAAAplB,IACA,MAAAA,IAUA,QAAAslB,IAAAxT,GACAA,EAAAyT,QAAAllB,OAAAmK,OAAA,MACAsH,EAAA0T,eAAA,CAEA,IAAApF,GAAAtO,EAAA2C,SAAAgP,gBACArD,IACAqF,GAAA3T,EAAAsO,GAMA,QAAAlY,IAAA8P,EAAAhN,GACA6C,GAAA4W,IAAAzM,EAAAhN,GAGA,QAAA0a,IAAA1N,EAAAhN,GACA6C,GAAA8X,KAAA3N,EAAAhN,GAGA,QAAA8M,IAAAE,EAAAhN,GACA,GAAA4a,GAAA/X,EACA,gBAAAgY,KAEA,OADA7a,EAAAO,MAAA,KAAAD,YAEAsa,EAAAD,KAAA3N,EAAA6N,IAKA,QAAAJ,IACA3T,EACAsO,EACA0F,GAEAjY,GAAAiE,EACA4F,GAAA0I,EAAA0F,MAA+C5d,GAAAwd,GAAA5N,GAAAhG,GAC/CjE,OAAA/E,GAqGA,QAAAid,IAAAjU,GACA,GAAAkU,GAAAC,EAEA,OADAA,IAAAnU,EACA,WACAmU,GAAAD,GAIA,QAAAE,IAAApU,GACA,GAAAtM,GAAAsM,EAAA2C,SAGA1M,EAAAvC,EAAAuC,MACA,IAAAA,IAAAvC,EAAA+b,SAAA,CACA,KAAAxZ,EAAA0M,SAAA8M,UAAAxZ,EAAAqN,SACArN,IAAAqN,OAEArN,GAAAoe,UAAAvjB,KAAAkP,GAGAA,EAAAsD,QAAArN,EACA+J,EAAAsU,MAAAre,IAAAqe,MAAAtU,EAEAA,EAAAqU,aACArU,EAAAuU,SAEAvU,EAAAwU,SAAA,KACAxU,EAAAyU,UAAA,KACAzU,EAAA0U,iBAAA,EACA1U,EAAA2U,YAAA,EACA3U,EAAA4U,cAAA,EACA5U,EAAA6U,mBAAA,EAsFA,QAAAC,IACA9U,EACA+U,EACAC,GAEAhV,EAAAiV,IAAAF,EACA/U,EAAA2C,SAAAnN,SACAwK,EAAA2C,SAAAnN,OAAAmY,IAmBAuH,GAAAlV,EAAA,cAEA,IAAAmV,EA2CA,OAvBAA,GAAA,WACAnV,EAAAoV,QAAApV,EAAAqV,UAAAL,IAOA,GAAAM,IAAAtV,EAAAmV,EAAA/iB,GACAmjB,OAAA,WACAvV,EAAA2U,aAAA3U,EAAA4U,cACAM,GAAAlV,EAAA,mBAGG,GACHgV,GAAA,EAIA,MAAAhV,EAAAjK,SACAiK,EAAA2U,YAAA,EACAO,GAAAlV,EAAA,YAEAA,EAGA,QAAAwV,IACAxV,EACAkC,EACAoM,EACAkD,EACAiE,GAYA,GAAAC,GAAAlE,EAAA9U,KAAA8R,YACAmH,EAAA3V,EAAAkK,aACA0L,KACAF,MAAA5M,SACA6M,IAAA1M,KAAA0M,EAAA7M,SACA4M,GAAA1V,EAAAkK,aAAAnB,OAAA2M,EAAA3M,MAMA8M,KACAJ,GACAzV,EAAA2C,SAAA8O,iBACAmE,EAkBA,IAfA5V,EAAA2C,SAAAkN,aAAA2B,EACAxR,EAAAjK,OAAAyb,EAEAxR,EAAAuR,SACAvR,EAAAuR,OAAAtb,OAAAub,GAEAxR,EAAA2C,SAAA8O,gBAAAgE,EAKAzV,EAAA8V,OAAAtE,EAAA9U,KAAAkK,OAAAqC,GACAjJ,EAAA+V,WAAAzH,GAAArF,GAGA/G,GAAAlC,EAAA2C,SAAAnC,MAAA,CACA/C,GAAA,EAGA,QAFA+C,GAAAR,EAAA4C,OACAoT,EAAAhW,EAAA2C,SAAAsT,cACAnoB,EAAA,EAAmBA,EAAAkoB,EAAAplB,OAAqB9C,IAAA,CACxC,GAAAkL,GAAAgd,EAAAloB,GACAmU,EAAAjC,EAAA2C,SAAAnC,KACAA,GAAAxH,GAAAgJ,GAAAhJ,EAAAiJ,EAAAC,EAAAlC,GAEAvC,GAAA,GAEAuC,EAAA2C,SAAAT,YAIAoM,KAAArF,EACA,IAAA+K,GAAAhU,EAAA2C,SAAAgP,gBACA3R,GAAA2C,SAAAgP,iBAAArD,EACAqF,GAAA3T,EAAAsO,EAAA0F,GAGA6B,IACA7V,EAAAmK,OAAA/B,GAAAqN,EAAAjE,EAAA1b,SACAkK,EAAA8S,gBAQA,QAAAoD,IAAAlW,GACA,KAAAA,QAAAsD,UACA,GAAAtD,EAAAyU,UAAuB,QAEvB,UAGA,QAAA0B,IAAAnW,EAAAoW,GACA,GAAAA,GAEA,GADApW,EAAA0U,iBAAA,EACAwB,GAAAlW,GACA,WAEG,IAAAA,EAAA0U,gBACH,MAEA,IAAA1U,EAAAyU,WAAA,OAAAzU,EAAAyU,UAAA,CACAzU,EAAAyU,WAAA,CACA,QAAA3mB,GAAA,EAAmBA,EAAAkS,EAAAqU,UAAAzjB,OAAyB9C,IAC5CqoB,GAAAnW,EAAAqU,UAAAvmB,GAEAonB,IAAAlV,EAAA,cAIA,QAAAqW,IAAArW,EAAAoW,GACA,KAAAA,IACApW,EAAA0U,iBAAA,EACAwB,GAAAlW,KAIAA,EAAAyU,WAAA,CACAzU,EAAAyU,WAAA,CACA,QAAA3mB,GAAA,EAAmBA,EAAAkS,EAAAqU,UAAAzjB,OAAyB9C,IAC5CuoB,GAAArW,EAAAqU,UAAAvmB,GAEAonB,IAAAlV,EAAA,gBAIA,QAAAkV,IAAAlV,EAAAnK,GAEAiG,GACA,IAAAwa,GAAAtW,EAAA2C,SAAA9M,GACAuN,EAAAvN,EAAA,OACA,IAAAygB,EACA,OAAAxoB,GAAA,EAAAsD,EAAAklB,EAAA1lB,OAAwC9C,EAAAsD,EAAOtD,IAC/C4V,GAAA4S,EAAAxoB,GAAAkS,EAAA,KAAAA,EAAAoD,EAGApD,GAAA0T,eACA1T,EAAAuW,MAAA,QAAA1gB,GAEAqG,IAkBA,QAAAsa,MACAvjB,GAAAwjB,GAAA7lB,OAAA8lB,GAAA9lB,OAAA,EACA2U,MAIAoR,GAAAC,IAAA,EAqCA,QAAAC,MACAC,GAAAC,KACAH,IAAA,CACA,IAAAI,GAAAnmB,CAcA,KAJA4lB,GAAAQ,KAAA,SAAA1d,EAAAa,GAA8B,MAAAb,GAAA1I,GAAAuJ,EAAAvJ,KAI9BoC,GAAA,EAAiBA,GAAAwjB,GAAA7lB,OAAsBqC,KACvC+jB,EAAAP,GAAAxjB,IACA+jB,EAAAzB,QACAyB,EAAAzB,SAEA1kB,EAAAmmB,EAAAnmB,GACA0U,GAAA1U,GAAA,KACAmmB,EAAAE,KAmBA,IAAAC,GAAAT,GAAA9Z,QACAwa,EAAAX,GAAA7Z,OAEA4Z,MAGAa,GAAAF,GACAG,GAAAF,GAIAG,IAAAzT,GAAAyT,UACAA,GAAAC,KAAA,SAIA,QAAAF,IAAAb,GAEA,IADA,GAAA3oB,GAAA2oB,EAAA7lB,OACA9C,KAAA,CACA,GAAAkpB,GAAAP,EAAA3oB,GACAkS,EAAAgX,EAAAhX,EACAA,GAAAwU,WAAAwC,GAAAhX,EAAA2U,aAAA3U,EAAA4U,cACAM,GAAAlV,EAAA,YASA,QAAAyX,IAAAzX,GAGAA,EAAAyU,WAAA,EACAiC,GAAA5lB,KAAAkP,GAGA,QAAAqX,IAAAZ,GACA,OAAA3oB,GAAA,EAAiBA,EAAA2oB,EAAA7lB,OAAkB9C,IACnC2oB,EAAA3oB,GAAA2mB,WAAA,EACA0B,GAAAM,EAAA3oB,IAAA,GASA,QAAA4pB,IAAAV,GACA,GAAAnmB,GAAAmmB,EAAAnmB,EACA,UAAA0U,GAAA1U,GAAA,CAEA,GADA0U,GAAA1U,IAAA,EACA+lB,GAEK,CAIL,IADA,GAAA9oB,GAAA2oB,GAAA7lB,OAAA,EACA9C,EAAAmF,IAAAwjB,GAAA3oB,GAAA+C,GAAAmmB,EAAAnmB,IACA/C,GAEA2oB,IAAA3d,OAAAhL,EAAA,IAAAkpB,OARAP,IAAA3lB,KAAAkmB,EAWAL,MACAA,IAAA,EAMAlS,GAAAoS,MAgOA,QAAAtN,IAAAxN,EAAA4b,EAAA3e,GACA4e,GAAAjpB,IAAA,WACA,MAAA8B,MAAAknB,GAAA3e,IAEA4e,GAAA9Y,IAAA,SAAApH,GACAjH,KAAAknB,GAAA3e,GAAAtB,GAEAnJ,OAAAC,eAAAuN,EAAA/C,EAAA4e,IAGA,QAAAC,IAAA7X,GACAA,EAAA8X,YACA,IAAAC,GAAA/X,EAAA2C,QACAoV,GAAAvX,OAAmBwX,GAAAhY,EAAA+X,EAAAvX,OACnBuX,EAAAE,SAAqBC,GAAAlY,EAAA+X,EAAAE,SACrBF,EAAArb,KACAyb,GAAAnY,GAEAjC,EAAAiC,EAAAoY,UAAyB,GAEzBL,EAAAM,UAAsBC,GAAAtY,EAAA+X,EAAAM,UACtBN,EAAAQ,OAAAR,EAAAQ,QAAAC,IACAC,GAAAzY,EAAA+X,EAAAQ,OAIA,QAAAP,IAAAhY,EAAA0Y,GACA,GAAAxW,GAAAlC,EAAA2C,SAAAT,cACA1B,EAAAR,EAAA4C,UAGA7H,EAAAiF,EAAA2C,SAAAsT,aACA0C,GAAA3Y,EAAAsD,OAEAqV,IACAlb,GAAA,EAqCA,QAAAzE,KAAA0f,IAnCA,SAAA1f,GACA+B,EAAAjK,KAAAkI,EACA,IAAArC,GAAAqL,GAAAhJ,EAAA0f,EAAAxW,EAAAlC,EAuBAxB,GAAAgC,EAAAxH,EAAArC,GAKAqC,IAAAgH,IACAuJ,GAAAvJ,EAAA,SAAAhH,IAIAA,EACAyE,IAAA,GAGA,QAAA0a,IAAAnY,GACA,GAAAtD,GAAAsD,EAAA2C,SAAAjG,IACAA,GAAAsD,EAAAoY,MAAA,kBAAA1b,GACAkc,GAAAlc,EAAAsD,GACAtD,MACApF,EAAAoF,KACAA,KAYA,KAJA,GAAA3B,GAAAxM,OAAAwM,KAAA2B,GACA8D,EAAAR,EAAA2C,SAAAnC,MAEA1S,GADAkS,EAAA2C,SAAAsV,QACAld,EAAAnK,QACA9C,KAAA,CACA,GAAAkL,GAAA+B,EAAAjN,EASA0S,IAAAzH,EAAAyH,EAAAxH,IAMKoC,EAAApC,IACLuQ,GAAAvJ,EAAA,QAAAhH,GAIA+E,EAAArB,GAAA,GAGA,QAAAkc,IAAAlc,EAAAsD,GAEAlE,GACA,KACA,MAAAY,GAAA1O,KAAAgS,KACG,MAAArF,GAEH,MADAuI,IAAAvI,EAAAqF,EAAA,aAEG,QACH9D,KAMA,QAAAoc,IAAAtY,EAAAqY,GAEA,GAAAQ,GAAA7Y,EAAA8Y,kBAAAvqB,OAAAmK,OAAA,MAEAqgB,EAAA3a,IAEA,QAAApF,KAAAqf,GAAA,CACA,GAAAW,GAAAX,EAAArf,GACA3K,EAAA,kBAAA2qB,OAAArqB,GAQAoqB,KAEAF,EAAA7f,GAAA,GAAAsc,IACAtV,EACA3R,GAAA+D,EACAA,EACA6mB,KAOAjgB,IAAAgH,IACAkZ,GAAAlZ,EAAAhH,EAAAggB,IAWA,QAAAE,IACAnd,EACA/C,EACAggB,GAEA,GAAAG,IAAA/a,IACA,mBAAA4a,IACApB,GAAAjpB,IAAAwqB,EACAC,GAAApgB,GACAqgB,GAAAL,GACApB,GAAA9Y,IAAA1M,IAEAwlB,GAAAjpB,IAAAqqB,EAAArqB,IACAwqB,IAAA,IAAAH,EAAA7f,MACAigB,GAAApgB,GACAqgB,GAAAL,EAAArqB,KACAyD,EACAwlB,GAAA9Y,IAAAka,EAAAla,KAAA1M,GAWA7D,OAAAC,eAAAuN,EAAA/C,EAAA4e,IAGA,QAAAwB,IAAApgB,GACA,kBACA,GAAAge,GAAAvmB,KAAAqoB,mBAAAroB,KAAAqoB,kBAAA9f,EACA,IAAAge,EAOA,MANAA,GAAAsC,OACAtC,EAAAuC,WAEAtd,GAAAF,QACAib,EAAAhY,SAEAgY,EAAArgB,OAKA,QAAA0iB,IAAAngB,GACA,kBACA,MAAAA,GAAAlL,KAAAyC,YAIA,QAAAynB,IAAAlY,EAAAiY,GACAjY,EAAA2C,SAAAnC,KACA,QAAAxH,KAAAif,GAsBAjY,EAAAhH,GAAA,kBAAAif,GAAAjf,GAAA5G,EAAAQ,GAAAqlB,EAAAjf,GAAAgH,GAIA,QAAAyY,IAAAzY,EAAAuY,GACA,OAAAvf,KAAAuf,GAAA,CACA,GAAA5U,GAAA4U,EAAAvf,EACA,IAAAb,MAAAC,QAAAuL,GACA,OAAA7V,GAAA,EAAqBA,EAAA6V,EAAA/S,OAAoB9C,IACzC0rB,GAAAxZ,EAAAhH,EAAA2K,EAAA7V,QAGA0rB,IAAAxZ,EAAAhH,EAAA2K,IAKA,QAAA6V,IACAxZ,EACAyZ,EACA9V,EACAjQ,GASA,MAPA4D,GAAAqM,KACAjQ,EAAAiQ,EACAA,aAEA,gBAAAA,KACAA,EAAA3D,EAAA2D,IAEA3D,EAAA0Z,OAAAD,EAAA9V,EAAAjQ,GAqHA,QAAAimB,IAAA3Z,EAAAtM,GACA,GAAAqkB,GAAA/X,EAAA2C,SAAApU,OAAAmK,OAAAsH,EAAA4Z,YAAAlmB,SAEA8d,EAAA9d,EAAAmc,YACAkI,GAAA9hB,OAAAvC,EAAAuC,OACA8hB,EAAAlI,aAAA2B,CAEA,IAAAqI,GAAArI,EAAAzU,gBACAgb,GAAA7V,UAAA2X,EAAA3X,UACA6V,EAAApG,iBAAAkI,EAAAvL,UACAyJ,EAAAtG,gBAAAoI,EAAAld,SACAob,EAAA+B,cAAAD,EAAApd,IAEA/I,EAAA8B,SACAuiB,EAAAviB,OAAA9B,EAAA8B,OACAuiB,EAAAtiB,gBAAA/B,EAAA+B,iBAIA,QAAA4Z,IAAAxT,GACA,GAAAnI,GAAAmI,EAAAnI,OACA,IAAAmI,EAAAke,MAAA,CACA,GAAAC,GAAA3K,GAAAxT,EAAAke,MAEA,IAAAC,IADAne,EAAAme,aACA,CAGAne,EAAAme,cAEA,IAAAC,GAAAC,GAAAre,EAEAoe,IACAlgB,EAAA8B,EAAAse,cAAAF,GAEAvmB,EAAAmI,EAAAnI,QAAAuN,EAAA+Y,EAAAne,EAAAse,eACAzmB,EAAAtF,OACAsF,EAAA0mB,WAAA1mB,EAAAtF,MAAAyN,IAIA,MAAAnI,GAGA,QAAAwmB,IAAAre,GACA,GAAAwe,GACAC,EAAAze,EAAAnI,QACA6mB,EAAA1e,EAAA2e,aACA,QAAAxhB,KAAAshB,GACAA,EAAAthB,KAAAuhB,EAAAvhB,KACAqhB,IAAsBA,MACtBA,EAAArhB,GAAAshB,EAAAthB,GAGA,OAAAqhB,GAGA,QAAAI,IAAA/mB,GAMAjD,KAAAiqB,MAAAhnB,GAWA,QAAAinB,IAAAF,GACAA,EAAAG,IAAA,SAAAC,GACA,GAAAC,GAAArqB,KAAAsqB,oBAAAtqB,KAAAsqB,qBACA,IAAAD,EAAAjiB,QAAAgiB,IAAA,EACA,MAAApqB,KAIA,IAAAmT,GAAAhK,EAAAJ,UAAA,EAQA,OAPAoK,GAAAoX,QAAAvqB,MACA,kBAAAoqB,GAAAI,QACAJ,EAAAI,QAAAxhB,MAAAohB,EAAAjX,GACK,kBAAAiX,IACLA,EAAAphB,MAAA,KAAAmK,GAEAkX,EAAAhqB,KAAA+pB,GACApqB,MAMA,QAAAyqB,IAAAT,GACAA,EAAAU,MAAA,SAAAA,GAEA,MADA1qB,MAAAiD,QAAAuN,EAAAxQ,KAAAiD,QAAAynB,GACA1qB,MAMA,QAAA2qB,IAAAX,GAMAA,EAAAvL,IAAA,CACA,IAAAA,GAAA,CAKAuL,GAAA1gB,OAAA,SAAAogB,GACAA,OACA,IAAAkB,GAAA5qB,KACA6qB,EAAAD,EAAAnM,IACAqM,EAAApB,EAAAqB,QAAArB,EAAAqB,SACA,IAAAD,EAAAD,GACA,MAAAC,GAAAD,EAGA,IAAAltB,GAAA+rB,EAAA/rB,MAAAitB,EAAA3nB,QAAAtF,KAKAqtB,EAAA,SAAA/nB,GACAjD,KAAAiqB,MAAAhnB,GA6CA,OA3CA+nB,GAAAzsB,UAAAT,OAAAmK,OAAA2iB,EAAArsB,WACAysB,EAAAzsB,UAAA4qB,YAAA6B,EACAA,EAAAvM,QACAuM,EAAA/nB,QAAAuN,EACAoa,EAAA3nB,QACAymB,GAEAsB,EAAA,MAAAJ,EAKAI,EAAA/nB,QAAA8M,OACAkb,GAAAD,GAEAA,EAAA/nB,QAAA2kB,UACAsD,GAAAF,GAIAA,EAAA1hB,OAAAshB,EAAAthB,OACA0hB,EAAAN,MAAAE,EAAAF,MACAM,EAAAb,IAAAS,EAAAT,IAIAgB,GAAA3T,QAAA,SAAAtW,GACA8pB,EAAA9pB,GAAA0pB,EAAA1pB,KAGAvD,IACAqtB,EAAA/nB,QAAA0mB,WAAAhsB,GAAAqtB,GAMAA,EAAAzB,aAAAqB,EAAA3nB,QACA+nB,EAAAtB,gBACAsB,EAAAjB,cAAAzgB,KAAiC0hB,EAAA/nB,SAGjC6nB,EAAAD,GAAAG,EACAA,GAIA,QAAAC,IAAAG,GACA,GAAArb,GAAAqb,EAAAnoB,QAAA8M,KACA,QAAAxH,KAAAwH,GACA+I,GAAAsS,EAAA7sB,UAAA,SAAAgK,GAIA,QAAA2iB,IAAAE,GACA,GAAAxD,GAAAwD,EAAAnoB,QAAA2kB,QACA,QAAArf,KAAAqf,GACAa,GAAA2C,EAAA7sB,UAAAgK,EAAAqf,EAAArf,IAMA,QAAA8iB,IAAArB,GAIAmB,GAAA3T,QAAA,SAAAtW,GACA8oB,EAAA9oB,GAAA,SACAd,EACAkrB,GAEA,MAAAA,IAOA,cAAApqB,GAAA2F,EAAAykB,KACAA,EAAA3tB,KAAA2tB,EAAA3tB,MAAAyC,EACAkrB,EAAAtrB,KAAAiD,QAAA6N,MAAAxH,OAAAgiB,IAEA,cAAApqB,GAAA,kBAAAoqB,KACAA,GAAwBnpB,KAAAmpB,EAAAhqB,OAAAgqB,IAExBtrB,KAAAiD,QAAA/B,EAAA,KAAAd,GAAAkrB,EACAA,GAdAtrB,KAAAiD,QAAA/B,EAAA,KAAAd,MAwBA,QAAAmrB,IAAAjE,GACA,MAAAA,OAAAlc,KAAAnI,QAAAtF,MAAA2pB,EAAAtb,KAGA,QAAAwf,IAAAC,EAAA9tB,GACA,MAAA+J,OAAAC,QAAA8jB,GACAA,EAAArjB,QAAAzK,IAAA,EACG,gBAAA8tB,GACHA,EAAAvjB,MAAA,KAAAE,QAAAzK,IAAA,IACGoJ,EAAA0kB,IACHA,EAAA/nB,KAAA/F,GAMA,QAAA+tB,IAAAC,EAAAvnB,GACA,GAAAsE,GAAAijB,EAAAjjB,MACA4B,EAAAqhB,EAAArhB,KACAwW,EAAA6K,EAAA7K,MACA,QAAAvY,KAAAG,GAAA,CACA,GAAAkjB,GAAAljB,EAAAH,EACA,IAAAqjB,EAAA,CACA,GAAAjuB,GAAA4tB,GAAAK,EAAAtf,iBACA3O,KAAAyG,EAAAzG,IACAkuB,GAAAnjB,EAAAH,EAAA+B,EAAAwW,KAMA,QAAA+K,IACAnjB,EACAH,EACA+B,EACAwhB,GAEA,GAAAC,GAAArjB,EAAAH,IACAwjB,GAAAD,GAAAC,EAAA/f,MAAA8f,EAAA9f,KACA+f,EAAAC,kBAAAC,WAEAvjB,EAAAH,GAAA,KACAhH,EAAA+I,EAAA/B,GAuNA,QAAA2jB,IAAApgB,GAIA,IAHA,GAAAG,GAAAH,EAAAG,KACArK,EAAAkK,EACAqgB,EAAArgB,EACAtF,EAAA2lB,EAAAH,qBACAG,IAAAH,kBAAAlL,SACAqL,EAAAlgB,OACAA,EAAAmgB,GAAAD,EAAAlgB,QAGA,MAAAzF,EAAA5E,IAAA4D,SACA5D,KAAAqK,OACAA,EAAAmgB,GAAAngB,EAAArK,EAAAqK,MAGA,OAAAogB,IAAApgB,EAAAqgB,YAAArgB,EAAA2U,OAGA,QAAAwL,IAAA3b,EAAAjL,GACA,OACA8mB,YAAAntB,GAAAsR,EAAA6b,YAAA9mB,EAAA8mB,aACA1L,MAAApa,EAAAiK,EAAAmQ,QACAnQ,EAAAmQ,MAAApb,EAAAob,OACApb,EAAAob,OAIA,QAAAyL,IACAC,EACAC,GAEA,MAAA/lB,GAAA8lB,IAAA9lB,EAAA+lB,GACAptB,GAAAmtB,EAAAE,GAAAD,IAGA,GAGA,QAAAptB,IAAA2J,EAAAa,GACA,MAAAb,GAAAa,EAAAb,EAAA,IAAAa,EAAAb,EAAAa,GAAA,GAGA,QAAA6iB,IAAAtmB,GACA,MAAAwB,OAAAC,QAAAzB,GACAumB,GAAAvmB,GAEAU,EAAAV,GACAwmB,GAAAxmB,GAEA,gBAAAA,GACAA,EAGA,GAGA,QAAAumB,IAAAvmB,GAGA,OADAymB,GADAjjB,EAAA,GAEArM,EAAA,EAAAC,EAAA4I,EAAA/F,OAAmC9C,EAAAC,EAAOD,IAC1CmJ,EAAAmmB,EAAAH,GAAAtmB,EAAA7I,MAAA,KAAAsvB,IACAjjB,IAAgBA,GAAA,KAChBA,GAAAijB,EAGA,OAAAjjB,GAGA,QAAAgjB,IAAAxmB,GACA,GAAAwD,GAAA,EACA,QAAAnB,KAAArC,GACAA,EAAAqC,KACAmB,IAAgBA,GAAA,KAChBA,GAAAnB,EAGA,OAAAmB,GAqCA,QAAA0W,IAAApU,GACA,MAAA4gB,IAAA5gB,GACA,MAIA,SAAAA,EACA,WADA,GAMA,QAAA6gB,IAAA7gB,GAEA,IAAAwH,GACA,QAEA,IAAA6M,GAAArU,GACA,QAIA,IAFAA,IAAApI,cAEA,MAAAkpB,GAAA9gB,GACA,MAAA8gB,IAAA9gB,EAEA,IAAAsY,GAAAtjB,SAAAC,cAAA+K,EACA,OAAAA,GAAA5D,QAAA,QAEA0kB,GAAA9gB,GACAsY,EAAA6E,cAAA4D,OAAAC,oBACA1I,EAAA6E,cAAA4D,OAAAE,YAGAH,GAAA9gB,GAAA,qBAAAtI,KAAA4gB,EAAAvkB,YAWA,QAAAmtB,IAAA5I,GACA,mBAAAA,GAAA,CACA,GAAA6I,GAAAnsB,SAAAQ,cAAA8iB,EACA,OAAA6I,IAIAnsB,SAAAC,cAAA,OAIA,MAAAqjB,GAMA,QAAA8I,IAAAC,EAAAvhB,GACA,GAAAO,GAAArL,SAAAC,cAAAosB,EACA,kBAAAA,EACAhhB,GAGAP,EAAAG,MAAAH,EAAAG,KAAAkK,WAAA5P,KAAAuF,EAAAG,KAAAkK,MAAAmX,UACAjhB,EAAArJ,aAAA,uBAEAqJ,GAGA,QAAAkhB,IAAAC,EAAAH,GACA,MAAArsB,UAAAusB,gBAAAE,GAAAD,GAAAH,GAGA,QAAAxqB,IAAAuJ,GACA,MAAApL,UAAA6B,eAAAuJ,GAGA,QAAAshB,IAAAthB,GACA,MAAApL,UAAA0sB,cAAAthB,GAGA,QAAArJ,IAAAnB,EAAA+rB,EAAAC,GACAhsB,EAAAmB,aAAA4qB,EAAAC,GAGA,QAAA/rB,IAAA+U,EAAAnG,GACAmG,EAAA/U,YAAA4O,GAGA,QAAArP,IAAAwV,EAAAnG,GACAmG,EAAAxV,YAAAqP,GAGA,QAAA7O,IAAAgV,GACA,MAAAA,GAAAhV,WAGA,QAAAisB,IAAAjX,GACA,MAAAA,GAAAiX,YAGA,QAAAR,IAAAzW,GACA,MAAAA,GAAAyW,QAGA,QAAAS,IAAAlX,EAAAxK,GACAwK,EAAAmX,YAAA3hB,EAGA,QAAA4hB,IAAApX,EAAAlS,GACAkS,EAAA5T,aAAA0B,EAAA,IAmCA,QAAAupB,IAAAniB,EAAAoiB,GACA,GAAA3lB,GAAAuD,EAAAG,KAAAkiB,GACA,IAAA3nB,EAAA+B,GAAA,CAEA,GAAAgH,GAAAzD,EAAAzG,QACA8oB,EAAAriB,EAAAkgB,mBAAAlgB,EAAAO,IACA3L,EAAA6O,EAAAuU,KACAoK,GACAxmB,MAAAC,QAAAjH,EAAA6H,IACAhH,EAAAb,EAAA6H,GAAA4lB,GACKztB,EAAA6H,KAAA4lB,IACLztB,EAAA6H,OAAAhC,IAGAuF,EAAAG,KAAAmiB,SACA1mB,MAAAC,QAAAjH,EAAA6H,IAEO7H,EAAA6H,GAAAH,QAAA+lB,GAAA,GAEPztB,EAAA6H,GAAAlI,KAAA8tB,GAHAztB,EAAA6H,IAAA4lB,GAMAztB,EAAA6H,GAAA4lB,GAqBA,QAAAE,IAAAvlB,EAAAa,GACA,MACAb,GAAAP,MAAAoB,EAAApB,MAEAO,EAAAkD,MAAArC,EAAAqC,KACAlD,EAAA4D,YAAA/C,EAAA+C,WACAlG,EAAAsC,EAAAmD,QAAAzF,EAAAmD,EAAAsC,OACAqiB,GAAAxlB,EAAAa,IAEAlD,EAAAqC,EAAA+Z,qBACA/Z,EAAAyD,eAAA5C,EAAA4C,cACAlG,EAAAsD,EAAA4C,aAAAoH,QAMA,QAAA2a,IAAAxlB,EAAAa,GACA,aAAAb,EAAAkD,IAA0B,QAC1B,IAAA3O,GACAkxB,EAAA/nB,EAAAnJ,EAAAyL,EAAAmD,OAAAzF,EAAAnJ,IAAA8Y,QAAA9Y,EAAA6D,KACAstB,EAAAhoB,EAAAnJ,EAAAsM,EAAAsC,OAAAzF,EAAAnJ,IAAA8Y,QAAA9Y,EAAA6D,IACA,OAAAqtB,KAAAC,GAAAC,GAAAF,IAAAE,GAAAD,GAGA,QAAAE,IAAAxiB,EAAAyiB,EAAAC,GACA,GAAAvxB,GAAAkL,EACAlJ,IACA,KAAAhC,EAAAsxB,EAAoBtxB,GAAAuxB,IAAavxB,EACjCkL,EAAA2D,EAAA7O,GAAAkL,IACA/B,EAAA+B,KAAqBlJ,EAAAkJ,GAAAlL,EAErB,OAAAgC,GA6tBA,QAAAwvB,IAAAC,EAAAhjB,IACAgjB,EAAA7iB,KAAAqE,YAAAxE,EAAAG,KAAAqE,aACAqU,GAAAmK,EAAAhjB,GAIA,QAAA6Y,IAAAmK,EAAAhjB,GACA,GAQAvD,GAAAwmB,EAAAC,EARAC,EAAAH,IAAAI,GACAC,EAAArjB,IAAAojB,GACAE,EAAAC,GAAAP,EAAA7iB,KAAAqE,WAAAwe,EAAAzpB,SACAiqB,EAAAD,GAAAvjB,EAAAG,KAAAqE,WAAAxE,EAAAzG,SAEAkqB,KACAC,IAGA,KAAAjnB,IAAA+mB,GACAP,EAAAK,EAAA7mB,GACAymB,EAAAM,EAAA/mB,GACAwmB,GAQAC,EAAAS,SAAAV,EAAA7oB,MACA8oB,EAAAU,OAAAX,EAAAY,IACAC,GAAAZ,EAAA,SAAAljB,EAAAgjB,GACAE,EAAAnkB,KAAAmkB,EAAAnkB,IAAAglB,kBACAL,EAAAnvB,KAAA2uB,KAVAY,GAAAZ,EAAA,OAAAljB,EAAAgjB,GACAE,EAAAnkB,KAAAmkB,EAAAnkB,IAAAilB,UACAP,EAAAlvB,KAAA2uB,GAaA,IAAAO,EAAApvB,OAAA,CACA,GAAA4vB,GAAA,WACA,OAAA1yB,GAAA,EAAqBA,EAAAkyB,EAAApvB,OAA2B9C,IAChDuyB,GAAAL,EAAAlyB,GAAA,WAAAyO,EAAAgjB,GAGAG,GACApZ,GAAA/J,EAAA,SAAAikB,GAEAA,IAYA,GARAP,EAAArvB,QACA0V,GAAA/J,EAAA,uBACA,OAAAzO,GAAA,EAAqBA,EAAAmyB,EAAArvB,OAA8B9C,IACnDuyB,GAAAJ,EAAAnyB,GAAA,mBAAAyO,EAAAgjB,MAKAG,EACA,IAAA1mB,IAAA6mB,GACAE,EAAA/mB,IAEAqnB,GAAAR,EAAA7mB,GAAA,SAAAumB,IAAAK,GAQA,QAAAE,IACAhf,EACAd,GAEA,GAAA7F,GAAA5L,OAAAmK,OAAA,KACA,KAAAoI,EAEA,MAAA3G,EAEA,IAAArM,GAAA2xB,CACA,KAAA3xB,EAAA,EAAaA,EAAAgT,EAAAlQ,OAAiB9C,IAC9B2xB,EAAA3e,EAAAhT,GACA2xB,EAAAgB,YAEAhB,EAAAgB,UAAAC,IAEAvmB,EAAAwmB,GAAAlB,MACAA,EAAAnkB,IAAAoG,EAAA1B,EAAA2C,SAAA,aAAA8c,EAAArxB,MAAA,EAGA,OAAA+L,GAGA,QAAAwmB,IAAAlB,GACA,MAAAA,GAAAmB,SAAAnB,EAAA,SAAAlxB,OAAAwM,KAAA0kB,EAAAgB,eAA4ExwB,KAAA,KAG5E,QAAAowB,IAAAZ,EAAA5pB,EAAA0G,EAAAgjB,EAAAK,GACA,GAAA1mB,GAAAumB,EAAAnkB,KAAAmkB,EAAAnkB,IAAAzF,EACA,IAAAqD,EACA,IACAA,EAAAqD,EAAAO,IAAA2iB,EAAAljB,EAAAgjB,EAAAK,GACK,MAAAjlB,GACLuI,GAAAvI,EAAA4B,EAAAzG,QAAA,aAAA2pB,EAAA,SAAA5pB,EAAA,UAYA,QAAAgrB,IAAAtB,EAAAhjB,GACA,GAAAwb,GAAAxb,EAAAQ,gBACA,MAAA9F,EAAA8gB,KAAA,IAAAA,EAAAlc,KAAAnI,QAAAotB,cAGAhqB,EAAAyoB,EAAA7iB,KAAAkK,QAAA9P,EAAAyF,EAAAG,KAAAkK,QAAA,CAGA,GAAA5N,GAAAqK,EACAvG,EAAAP,EAAAO,IACAikB,EAAAxB,EAAA7iB,KAAAkK,UACAA,EAAArK,EAAAG,KAAAkK,SAEA3P,GAAA2P,EAAA1I,UACA0I,EAAArK,EAAAG,KAAAkK,MAAA7M,KAAwC6M,GAGxC,KAAA5N,IAAA4N,GACAvD,EAAAuD,EAAA5N,GACA+nB,EAAA/nB,KACAqK,GACA2d,GAAAlkB,EAAA9D,EAAAqK,IAMA4d,IAAAC,KAAAta,EAAAjQ,QAAAoqB,EAAApqB,OACAqqB,GAAAlkB,EAAA,QAAA8J,EAAAjQ,MAEA,KAAAqC,IAAA+nB,GACAjqB,EAAA8P,EAAA5N,MACAmoB,GAAAnoB,GACA8D,EAAAskB,kBAAAC,GAAAC,GAAAtoB,IACOuoB,GAAAvoB,IACP8D,EAAA0kB,gBAAAxoB,KAMA,QAAAgoB,IAAAjM,EAAA/b,EAAArC,GACAoe,EAAA+I,QAAAjlB,QAAA,QACA4oB,GAAA1M,EAAA/b,EAAArC,GACG+qB,GAAA1oB,GAGH2oB,GAAAhrB,GACAoe,EAAAyM,gBAAAxoB,IAIArC,EAAA,oBAAAqC,GAAA,UAAA+b,EAAA+I,QACA,OACA9kB,EACA+b,EAAAthB,aAAAuF,EAAArC,IAEG4qB,GAAAvoB,GACH+b,EAAAthB,aAAAuF,EAAA4oB,GAAA5oB,EAAArC,IACGwqB,GAAAnoB,GACH2oB,GAAAhrB,GACAoe,EAAAqM,kBAAAC,GAAAC,GAAAtoB,IAEA+b,EAAA8M,eAAAR,GAAAroB,EAAArC,GAGA8qB,GAAA1M,EAAA/b,EAAArC,GAIA,QAAA8qB,IAAA1M,EAAA/b,EAAArC,GACA,GAAAgrB,GAAAhrB,GACAoe,EAAAyM,gBAAAxoB,OACG,CAKH,GACAioB,KAAAa,IACA,aAAA/M,EAAA+I,SACA,gBAAA9kB,GAAA,KAAArC,IAAAoe,EAAAgN,OACA,CACA,GAAAC,GAAA,SAAArnB,GACAA,EAAAsnB,2BACAlN,EAAAmN,oBAAA,QAAAF,GAEAjN,GAAAoN,iBAAA,QAAAH,GAEAjN,EAAAgN,QAAA,EAEAhN,EAAAthB,aAAAuF,EAAArC,IAWA,QAAAyrB,IAAA7C,EAAAhjB,GACA,GAAAwY,GAAAxY,EAAAO,IACAJ,EAAAH,EAAAG,KACA2lB,EAAA9C,EAAA7iB,IACA,MACA5F,EAAA4F,EAAAqgB,cACAjmB,EAAA4F,EAAA2U,SACAva,EAAAurB,IACAvrB,EAAAurB,EAAAtF,cACAjmB,EAAAurB,EAAAhR,SALA,CAYA,GAAAiR,GAAA3F,GAAApgB,GAGAgmB,EAAAxN,EAAAyN,kBACAvrB,GAAAsrB,KACAD,EAAA1yB,GAAA0yB,EAAArF,GAAAsF,KAIAD,IAAAvN,EAAA0N,aACA1N,EAAAthB,aAAA,QAAA6uB,GACAvN,EAAA0N,WAAAH,IA4BA,QAAAI,IAAA7c,GAEA,GAAA5O,EAAA4O,EAAA8c,KAAA,CAEA,GAAAzc,GAAA+a,GAAA,gBACApb,GAAAK,MAAAtW,OAAAiW,EAAA8c,IAAA9c,EAAAK,cACAL,GAAA8c,IAKA1rB,EAAA4O,EAAA+c,OACA/c,EAAAgd,UAAAjzB,OAAAiW,EAAA+c,IAAA/c,EAAAgd,kBACAhd,GAAA+c,KAMA,QAAAE,IAAA5c,EAAAvC,EAAAH,GACA,GAAAsQ,GAAAiP,EACA,gBAAAhP,KAEA,OADApQ,EAAAlK,MAAA,KAAAD,YAEAwpB,GAAA9c,EAAA6N,EAAAvQ,EAAAsQ,IAUA,QAAAmP,IACA70B,EACAuV,EACAH,EACA4C,GAQA,GAAA8c,GAAA,CACA,GAAAC,GAAArM,GACAsM,EAAAzf,CACAA,GAAAyf,EAAAC,SAAA,SAAA1oB,GACA,GAIAA,EAAAoB,SAAApB,EAAA2oB,eAEA3oB,EAAA4oB,WAAAJ,GAIAxoB,EAAA4oB,WAAA,GAIA5oB,EAAAoB,OAAAynB,gBAAA/xB,SAEA,MAAA2xB,GAAA3pB,MAAAhJ,KAAA+I,YAIAupB,GAAAZ,iBACA/zB,EACAuV,EACA8f,IACSjgB,UAAA4C,WACT5C,GAIA,QAAAwf,IACA50B,EACAuV,EACAH,EACAsQ,IAEAA,GAAAiP,IAAAb,oBACA9zB,EACAuV,EAAA0f,UAAA1f,EACAH,GAIA,QAAAkgB,IAAAnE,EAAAhjB,GACA,IAAAzF,EAAAyoB,EAAA7iB,KAAAmJ,MAAA/O,EAAAyF,EAAAG,KAAAmJ,IAAA,CAGA,GAAAA,GAAAtJ,EAAAG,KAAAmJ,OACAC,EAAAyZ,EAAA7iB,KAAAmJ,MACAkd,IAAAxmB,EAAAO,IACA4lB,GAAA7c,GACAD,GAAAC,EAAAC,EAAAmd,GAAAD,GAAAF,GAAAvmB,EAAAzG,SACAitB,OAAA/rB,IAYA,QAAA2sB,IAAApE,EAAAhjB,GACA,IAAAzF,EAAAyoB,EAAA7iB,KAAA4O,YAAAxU,EAAAyF,EAAAG,KAAA4O,UAAA,CAGA,GAAAtS,GAAAqK,EACAvG,EAAAP,EAAAO,IACA8mB,EAAArE,EAAA7iB,KAAA4O,aACA9K,EAAAjE,EAAAG,KAAA4O,YAEArU,GAAAuJ,EAAAtC,UACAsC,EAAAjE,EAAAG,KAAA4O,SAAAvR,KAA2CyG,GAG3C,KAAAxH,IAAA4qB,GACA5qB,IAAAwH,KACA1D,EAAA9D,GAAA,GAIA,KAAAA,IAAAwH,GAAA,CAKA,GAJA6C,EAAA7C,EAAAxH,GAIA,gBAAAA,GAAA,cAAAA,EAAA,CAEA,GADAuD,EAAAI,WAA2BJ,EAAAI,SAAA/L,OAAA,GAC3ByS,IAAAugB,EAAA5qB,GAAkC,QAGlC,KAAA8D,EAAAvJ,WAAA3C,QACAkM,EAAAxK,YAAAwK,EAAAvJ,WAAA,IAIA,aAAAyF,GAAA,aAAA8D,EAAAghB,QAAA,CAGAhhB,EAAA+mB,OAAAxgB,CAEA,IAAAygB,GAAAhtB,EAAAuM,GAAA,GAAAzL,OAAAyL,EACA0gB,IAAAjnB,EAAAgnB,KACAhnB,EAAAnG,MAAAmtB,OAEK,kBAAA9qB,GAAAqkB,GAAAvgB,EAAAghB,UAAAhnB,EAAAgG,EAAAknB,WAAA,CAELC,OAAAxyB,SAAAC,cAAA,OACAuyB,GAAAD,UAAA,QAAA3gB,EAAA,QAEA,KADA,GAAA6gB,GAAAD,GAAArwB,WACAkJ,EAAAlJ,YACAkJ,EAAAxK,YAAAwK,EAAAlJ,WAEA,MAAAswB,EAAAtwB,YACAkJ,EAAAjL,YAAAqyB,EAAAtwB,gBAEK,IAKLyP,IAAAugB,EAAA5qB,GAIA,IACA8D,EAAA9D,GAAAqK,EACO,MAAA1I,OAQP,QAAAopB,IAAAjnB,EAAAqnB,GACA,OAAArnB,EAAAsnB,YACA,WAAAtnB,EAAAghB,SACAuG,GAAAvnB,EAAAqnB,IACAG,GAAAxnB,EAAAqnB,IAIA,QAAAE,IAAAvnB,EAAAqnB,GAGA,GAAAI,IAAA,CAGA,KAAOA,EAAA9yB,SAAA+yB,gBAAA1nB,EAA+C,MAAAnC,IACtD,MAAA4pB,IAAAznB,EAAAnG,QAAAwtB,EAGA,QAAAG,IAAAxnB,EAAAoC,GACA,GAAAvI,GAAAmG,EAAAnG,MACA8pB,EAAA3jB,EAAA2nB,WACA,IAAAxtB,EAAAwpB,GAAA,CACA,GAAAA,EAAAiE,OACA,MAAArsB,GAAA1B,KAAA0B,EAAA6G,EAEA,IAAAuhB,EAAAkE,KACA,MAAAhuB,GAAAguB,SAAAzlB,EAAAylB,OAGA,MAAAhuB,KAAAuI,EAwBA,QAAA0lB,IAAAloB,GACA,GAAA0U,GAAAyT,GAAAnoB,EAAA0U,MAGA,OAAA1U,GAAAooB,YACA/qB,EAAA2C,EAAAooB,YAAA1T,GACAA,EAIA,QAAAyT,IAAAE,GACA,MAAA5sB,OAAAC,QAAA2sB,GACA7qB,EAAA6qB,GAEA,gBAAAA,GACAC,GAAAD,GAEAA,EAOA,QAAAE,IAAA1oB,EAAA2oB,GACA,GACAC,GADAhrB,IAGA,IAAA+qB,EAEA,IADA,GAAAtI,GAAArgB,EACAqgB,EAAAH,oBACAG,IAAAH,kBAAAlL,SAEAqL,EAAAlgB,OACAyoB,EAAAP,GAAAhI,EAAAlgB,QAEA3C,EAAAI,EAAAgrB,IAKAA,EAAAP,GAAAroB,EAAAG,QACA3C,EAAAI,EAAAgrB,EAIA,KADA,GAAA9yB,GAAAkK,EACAlK,IAAA4D,QACA5D,EAAAqK,OAAAyoB,EAAAP,GAAAvyB,EAAAqK,QACA3C,EAAAI,EAAAgrB,EAGA,OAAAhrB,GA8CA,QAAAirB,IAAA7F,EAAAhjB,GACA,GAAAG,GAAAH,EAAAG,KACA2lB,EAAA9C,EAAA7iB,IAEA,MAAA5F,EAAA4F,EAAAooB,cAAAhuB,EAAA4F,EAAA0U,QACAta,EAAAurB,EAAAyC,cAAAhuB,EAAAurB,EAAAjR,QADA,CAMA,GAAA/N,GAAAjV,EACA2mB,EAAAxY,EAAAO,IACAuoB,EAAAhD,EAAAyC,YACAQ,EAAAjD,EAAAkD,iBAAAlD,EAAAjR,UAGAoU,EAAAH,GAAAC,EAEAlU,EAAAyT,GAAAtoB,EAAAG,KAAA0U,UAKA7U,GAAAG,KAAA6oB,gBAAAtuB,EAAAma,EAAAlT,QACAnE,KAAeqX,GACfA,CAEA,IAAAqU,GAAAR,GAAA1oB,GAAA,EAEA,KAAAnO,IAAAo3B,GACA1uB,EAAA2uB,EAAAr3B,KACAs3B,GAAA3Q,EAAA3mB,EAAA,GAGA,KAAAA,IAAAq3B,IACApiB,EAAAoiB,EAAAr3B,MACAo3B,EAAAp3B,IAEAs3B,GAAA3Q,EAAA3mB,EAAA,MAAAiV,EAAA,GAAAA,IAkBA,QAAAsiB,IAAA5Q,EAAAuN,GAEA,GAAAA,QAAAqC,QAKA,GAAA5P,EAAA6Q,UACAtD,EAAAzpB,QAAA,QACAypB,EAAA3pB,MAAAktB,IAAA5d,QAAA,SAAA/Z,GAAoD,MAAA6mB,GAAA6Q,UAAAxvB,IAAAlI,KAEpD6mB,EAAA6Q,UAAAxvB,IAAAksB,OAEG,CACH,GAAAjf,GAAA,KAAA0R,EAAA+Q,aAAA,iBACAziB,GAAAxK,QAAA,IAAAypB,EAAA,QACAvN,EAAAthB,aAAA,SAAA4P,EAAAif,GAAAqC,SASA,QAAAoB,IAAAhR,EAAAuN,GAEA,GAAAA,QAAAqC,QAKA,GAAA5P,EAAA6Q,UACAtD,EAAAzpB,QAAA,QACAypB,EAAA3pB,MAAAktB,IAAA5d,QAAA,SAAA/Z,GAAoD,MAAA6mB,GAAA6Q,UAAA5zB,OAAA9D,KAEpD6mB,EAAA6Q,UAAA5zB,OAAAswB,GAEAvN,EAAA6Q,UAAAh1B,QACAmkB,EAAAyM,gBAAA,aAEG,CAGH,IAFA,GAAAne,GAAA,KAAA0R,EAAA+Q,aAAA,kBACAE,EAAA,IAAA1D,EAAA,IACAjf,EAAAxK,QAAAmtB,IAAA,GACA3iB,IAAA4iB,QAAAD,EAAA,IAEA3iB,KAAAshB,OACAthB,EACA0R,EAAAthB,aAAA,QAAA4P,GAEA0R,EAAAyM,gBAAA,UAOA,QAAA0E,IAAAllB,GACA,GAAAA,EAAA,CAIA,mBAAAA,GAAA,CACA,GAAA7G,KAKA,QAJA,IAAA6G,EAAAjO,KACAgH,EAAAI,EAAAgsB,GAAAnlB,EAAA5S,MAAA,MAEA2L,EAAAI,EAAA6G,GACA7G,EACG,sBAAA6G,GACHmlB,GAAAnlB,OADG,IAgDH,QAAAolB,IAAAltB,GACAmtB,GAAA,WACAA,GAAAntB,KAIA,QAAAotB,IAAAvR,EAAAuN,GACA,GAAAiE,GAAAxR,EAAAyN,qBAAAzN,EAAAyN,sBACA+D,GAAA1tB,QAAAypB,GAAA,IACAiE,EAAAz1B,KAAAwxB,GACAqD,GAAA5Q,EAAAuN,IAIA,QAAAkE,IAAAzR,EAAAuN,GACAvN,EAAAyN,oBACAxwB,EAAA+iB,EAAAyN,mBAAAF,GAEAyD,GAAAhR,EAAAuN,GAGA,QAAAmE,IACA1R,EACA2R,EACAhiB,GAEA,GAAAka,GAAA+H,GAAA5R,EAAA2R,GACA/0B,EAAAitB,EAAAjtB,KACA0hB,EAAAuL,EAAAvL,QACAuT,EAAAhI,EAAAgI,SACA,KAAAj1B,EAAc,MAAA+S,IACd,IAAAwB,GAAAvU,IAAAk1B,GAAAC,GAAAC,GACAC,EAAA,EACAC,EAAA,WACAlS,EAAAmN,oBAAAhc,EAAAghB,GACAxiB,KAEAwiB,EAAA,SAAAvsB,GACAA,EAAAoB,SAAAgZ,KACAiS,GAAAJ,GACAK,IAIA7T,YAAA,WACA4T,EAAAJ,GACAK,KAEG5T,EAAA,GACH0B,EAAAoN,iBAAAjc,EAAAghB,GAKA,QAAAP,IAAA5R,EAAA2R,GACA,GASA/0B,GATAX,EAAAwsB,OAAA2J,iBAAApS,GAEAqS,GAAAp2B,EAAAq2B,GAAA,cAAA1uB,MAAA,MACA2uB,GAAAt2B,EAAAq2B,GAAA,iBAAA1uB,MAAA,MACA4uB,EAAAC,GAAAJ,EAAAE,GACAG,GAAAz2B,EAAA02B,GAAA,cAAA/uB,MAAA,MACAgvB,GAAA32B,EAAA02B,GAAA,iBAAA/uB,MAAA,MACAivB,EAAAJ,GAAAC,EAAAE,GAGAtU,EAAA,EACAuT,EAAA,CA8BA,OA5BAF,KAAAG,GACAU,EAAA,IACA51B,EAAAk1B,GACAxT,EAAAkU,EACAX,EAAAU,EAAA12B,QAEG81B,IAAAmB,GACHD,EAAA,IACAj2B,EAAAk2B,GACAxU,EAAAuU,EACAhB,EAAAe,EAAA/2B,SAGAyiB,EAAAxb,KAAAuH,IAAAmoB,EAAAK,GACAj2B,EAAA0hB,EAAA,EACAkU,EAAAK,EACAf,GACAgB,GACA,KACAjB,EAAAj1B,EACAA,IAAAk1B,GACAS,EAAA12B,OACA+2B,EAAA/2B,OACA,IAMAe,OACA0hB,UACAuT,YACAkB,aANAn2B,IAAAk1B,IACAkB,GAAA5zB,KAAAnD,EAAAq2B,GAAA,cASA,QAAAG,IAAAQ,EAAAC,GAEA,KAAAD,EAAAp3B,OAAAq3B,EAAAr3B,QACAo3B,IAAAp4B,OAAAo4B,EAGA,OAAAnwB,MAAAuH,IAAA3F,MAAA,KAAAwuB,EAAAn4B,IAAA,SAAA3B,EAAAL,GACA,MAAAo6B,IAAA/5B,GAAA+5B,GAAAF,EAAAl6B,OAQA,QAAAo6B,IAAA/4B,GACA,WAAAg5B,OAAAh5B,EAAAyN,MAAA,MAAAqpB,QAAA,UAKA,QAAAmC,IAAA7rB,EAAA8rB,GACA,GAAAtT,GAAAxY,EAAAO,GAGA7F,GAAA8d,EAAAuT,YACAvT,EAAAuT,SAAAC,WAAA,EACAxT,EAAAuT,WAGA,IAAA5rB,GAAAwpB,GAAA3pB,EAAAG,KAAA8rB,WACA,KAAA1xB,EAAA4F,KAKAzF,EAAA8d,EAAA0T,WAAA,IAAA1T,EAAA2T,SAAA,CA4BA,IAxBA,GAAA31B,GAAA2J,EAAA3J,IACApB,EAAA+K,EAAA/K,KACAg3B,EAAAjsB,EAAAisB,WACAC,EAAAlsB,EAAAksB,aACAC,EAAAnsB,EAAAmsB,iBACAC,EAAApsB,EAAAosB,YACAC,EAAArsB,EAAAqsB,cACAC,EAAAtsB,EAAAssB,kBACAC,EAAAvsB,EAAAusB,YACAb,EAAA1rB,EAAA0rB,MACAc,EAAAxsB,EAAAwsB,WACAC,EAAAzsB,EAAAysB,eACAC,EAAA1sB,EAAA0sB,aACAC,EAAA3sB,EAAA2sB,OACAC,EAAA5sB,EAAA4sB,YACAC,EAAA7sB,EAAA6sB,gBACAC,EAAA9sB,EAAA8sB,SAMA1zB,EAAAqe,GACAsV,EAAAtV,GAAApe,OACA0zB,KAAAxzB,QACAH,EAAA2zB,EAAA3zB,QACA2zB,IAAAxzB,MAGA,IAAAyzB,IAAA5zB,EAAA6e,aAAApY,EAAAotB,YAEA,KAAAD,GAAAL,GAAA,KAAAA,EAAA,CAIA,GAAAO,GAAAF,GAAAZ,EACAA,EACAH,EACAkB,EAAAH,GAAAV,EACAA,EACAH,EACAiB,EAAAJ,GAAAX,EACAA,EACAH,EAEAmB,EAAAL,EACAN,GAAAH,EACAA,EACAe,EAAAN,GACA,kBAAAL,KACAjB,EACA6B,EAAAP,EACAJ,GAAAJ,EACAA,EACAgB,EAAAR,EACAH,GAAAJ,EACAA,EAEAgB,EAAA9xB,EACAhB,EAAAmyB,GACAA,EAAApB,MACAoB,GAOAY,GAAA,IAAAr3B,IAAA+uB,GACAuI,EAAAC,GAAAN,GAEAtlB,EAAAqQ,EAAA0T,SAAAvtB,EAAA,WACAkvB,IACA5D,GAAAzR,EAAA+U,GACAtD,GAAAzR,EAAA8U,IAEAnlB,EAAA6jB,WACA6B,GACA5D,GAAAzR,EAAA6U,GAEAM,KAAAnV,IAEAkV,KAAAlV,GAEAA,EAAA0T,SAAA,MAGAlsB,GAAAG,KAAA6tB,MAEAjkB,GAAA/J,EAAA,oBACA,GAAAtG,GAAA8e,EAAA1iB,WACAm4B,EAAAv0B,KAAAw0B,UAAAx0B,EAAAw0B,SAAAluB,EAAAvD,IACAwxB,IACAA,EAAA/tB,MAAAF,EAAAE,KACA+tB,EAAA1tB,IAAAwrB,UAEAkC,EAAA1tB,IAAAwrB,WAEA0B,KAAAjV,EAAArQ,KAKAqlB,KAAAhV,GACAqV,IACA9D,GAAAvR,EAAA6U,GACAtD,GAAAvR,EAAA8U,GACAzD,GAAA,WACAI,GAAAzR,EAAA6U,GACAllB,EAAA6jB,YACAjC,GAAAvR,EAAA+U,GACAO,IACAK,GAAAP,GACA/W,WAAA1O,EAAAylB,GAEA1D,GAAA1R,EAAApjB,EAAA+S,QAOAnI,EAAAG,KAAA6tB,OACAlC,OACA2B,KAAAjV,EAAArQ,IAGA0lB,GAAAC,GACA3lB,MAIA,QAAAimB,IAAApuB,EAAAquB,GAsEA,QAAAC,KAEAnmB,EAAA6jB,aAIAhsB,EAAAG,KAAA6tB,MAAAxV,EAAA1iB,cACA0iB,EAAA1iB,WAAAo4B,WAAA1V,EAAA1iB,WAAAo4B,cAA6DluB,EAAA,KAAAA,GAE7DuuB,KAAA/V,GACAqV,IACA9D,GAAAvR,EAAAgW,GACAzE,GAAAvR,EAAAiW,GACA5E,GAAA,WACAI,GAAAzR,EAAAgW,GACArmB,EAAA6jB,YACAjC,GAAAvR,EAAAkW,GACAZ,IACAK,GAAAQ,GACA9X,WAAA1O,EAAAwmB,GAEAzE,GAAA1R,EAAApjB,EAAA+S,QAMAimB,KAAA5V,EAAArQ,GACA0lB,GAAAC,GACA3lB,KAlGA,GAAAqQ,GAAAxY,EAAAO,GAGA7F,GAAA8d,EAAA0T,YACA1T,EAAA0T,SAAAF,WAAA,EACAxT,EAAA0T,WAGA,IAAA/rB,GAAAwpB,GAAA3pB,EAAAG,KAAA8rB,WACA,IAAA1xB,EAAA4F,IAAA,IAAAqY,EAAA2T,SACA,MAAAkC,IAIA,KAAA3zB,EAAA8d,EAAAuT,UAAA,CAIA,GAAAv1B,GAAA2J,EAAA3J,IACApB,EAAA+K,EAAA/K,KACAo5B,EAAAruB,EAAAquB,WACAE,EAAAvuB,EAAAuuB,aACAD,EAAAtuB,EAAAsuB,iBACAF,EAAApuB,EAAAouB,YACAH,EAAAjuB,EAAAiuB,MACAQ,EAAAzuB,EAAAyuB,WACAC,EAAA1uB,EAAA0uB,eACAC,EAAA3uB,EAAA2uB,WACA7B,EAAA9sB,EAAA8sB,SAEAY,GAAA,IAAAr3B,IAAA+uB,GACAuI,EAAAC,GAAAK,GAEAO,EAAA7yB,EACAhB,EAAAmyB,GACAA,EAAAmB,MACAnB,GAOA9kB,EAAAqQ,EAAAuT,SAAAptB,EAAA,WACA6Z,EAAA1iB,YAAA0iB,EAAA1iB,WAAAo4B,WACA1V,EAAA1iB,WAAAo4B,SAAAluB,EAAAvD,KAAA,MAEAoxB,IACA5D,GAAAzR,EAAAkW,GACAzE,GAAAzR,EAAAiW,IAEAtmB,EAAA6jB,WACA6B,GACA5D,GAAAzR,EAAAgW,GAEAK,KAAArW,KAEA6V,IACAO,KAAApW,IAEAA,EAAAuT,SAAA,MAGA+C,GACAA,EAAAR,GAEAA,KAsDA,QAAAH,IAAAhzB,GACA,sBAAAA,KAAAY,MAAAZ,GASA,QAAA4yB,IAAApxB,GACA,GAAApC,EAAAoC,GACA,QAEA,IAAAoyB,GAAApyB,EAAAuM,GACA,OAAAxO,GAAAq0B,GAEAhB,GACAnyB,MAAAC,QAAAkzB,GACAA,EAAA,GACAA,IAGApyB,EAAAQ,SAAAR,EAAAtI,QAAA,EAIA,QAAA26B,IAAAC,EAAAjvB,IACA,IAAAA,EAAAG,KAAA6tB,MACAnC,GAAA7rB,GAuGA,QAAAkvB,IAAA1W,EAAA2W,EAAA1rB,GACA2rB,GAAA5W,EAAA2W,EAAA1rB,IAEAihB,IAAAC,KACA9N,WAAA,WACAuY,GAAA5W,EAAA2W,EAAA1rB,IACK,GAIL,QAAA2rB,IAAA5W,EAAA2W,EAAA1rB,GACA,GAAArJ,GAAA+0B,EAAA/0B,MACAi1B,EAAA7W,EAAAgJ,QACA,KAAA6N,GAAAzzB,MAAAC,QAAAzB,GAAA,CASA,OADAinB,GAAAiO,EACA/9B,EAAA,EAAAC,EAAAgnB,EAAArhB,QAAA9C,OAAwC9C,EAAAC,EAAOD,IAE/C,GADA+9B,EAAA9W,EAAArhB,QAAA5F,GACA89B,EACAhO,EAAA3iB,EAAAtE,EAAAm1B,GAAAD,KAAA,EACAA,EAAAjO,eACAiO,EAAAjO,gBAGA,IAAAvjB,EAAAyxB,GAAAD,GAAAl1B,GAIA,YAHAoe,EAAAgX,gBAAAj+B,IACAinB,EAAAgX,cAAAj+B,GAMA89B,KACA7W,EAAAgX,eAAA,IAIA,QAAAC,IAAAr1B,EAAAjD,GACA,MAAAA,GAAAgH,MAAA,SAAApM,GAAqC,OAAA+L,EAAA/L,EAAAqI,KAGrC,QAAAm1B,IAAAD,GACA,gBAAAA,GACAA,EAAAhI,OACAgI,EAAAl1B,MAGA,QAAAs1B,IAAAtxB,GACAA,EAAAoB,OAAAqoB,WAAA,EAGA,QAAA8H,IAAAvxB,GAEAA,EAAAoB,OAAAqoB,YACAzpB,EAAAoB,OAAAqoB,WAAA,EACA+H,GAAAxxB,EAAAoB,OAAA,UAGA,QAAAowB,IAAApX,EAAApjB,GACA,GAAAgJ,GAAAlJ,SAAA26B,YAAA,aACAzxB,GAAA0xB,UAAA16B,GAAA,MACAojB,EAAAuX,cAAA3xB,GAMA,QAAA4xB,IAAAhwB,GACA,OAAAA,EAAAkgB,mBAAAlgB,EAAAG,MAAAH,EAAAG,KAAA8rB,WAEAjsB,EADAgwB,GAAAhwB,EAAAkgB,kBAAAlL,QAsFA,QAAAib,IAAAjwB,GACA,GAAAkwB,GAAAlwB,KAAAQ,gBACA,OAAA0vB,MAAA5wB,KAAAnI,QAAA+b,SACA+c,GAAAjZ,GAAAkZ,EAAA9vB,WAEAJ,EAIA,QAAAmwB,IAAA7a,GACA,GAAAnV,MACAhJ,EAAAme,EAAAlP,QAEA,QAAA3J,KAAAtF,GAAAwO,UACAxF,EAAA1D,GAAA6Y,EAAA7Y,EAIA,IAAAsV,GAAA5a,EAAAie,gBACA,QAAAxI,KAAAmF,GACA5R,EAAA+D,GAAA0I,IAAAmF,EAAAnF,EAEA,OAAAzM,GAGA,QAAAiwB,IAAAl2B,EAAAm2B,GACA,oBAAAz4B,KAAAy4B,EAAAnwB,KACA,MAAAhG,GAAA,cACA+J,MAAAosB,EAAA7vB,iBAAAmF,YAKA,QAAA2qB,IAAAtwB,GACA,KAAAA,IAAAtG,QACA,GAAAsG,EAAAG,KAAA8rB,WACA,SAKA,QAAAsE,IAAA5rB,EAAA6rB,GACA,MAAAA,GAAA/zB,MAAAkI,EAAAlI,KAAA+zB,EAAAtwB,MAAAyE,EAAAzE,IA2QA,QAAAuwB,IAAA9+B,GAEAA,EAAA4O,IAAAmwB,SACA/+B,EAAA4O,IAAAmwB,UAGA/+B,EAAA4O,IAAA2rB,UACAv6B,EAAA4O,IAAA2rB,WAIA,QAAAyE,IAAAh/B,GACAA,EAAAwO,KAAAywB,OAAAj/B,EAAA4O,IAAAswB,wBAGA,QAAAC,IAAAn/B,GACA,GAAAo/B,GAAAp/B,EAAAwO,KAAA6wB,IACAJ,EAAAj/B,EAAAwO,KAAAywB,OACAK,EAAAF,EAAAG,KAAAN,EAAAM,KACAC,EAAAJ,EAAAK,IAAAR,EAAAQ,GACA,IAAAH,GAAAE,EAAA,CACAx/B,EAAAwO,KAAAkxB,OAAA,CACA,IAAAz+B,GAAAjB,EAAA4O,IAAAsU,KACAjiB,GAAA0+B,UAAA1+B,EAAA2+B,gBAAA,aAAAN,EAAA,MAAAE,EAAA,MACAv+B,EAAA4+B,mBAAA;;;;;AAlrQA,GAAA9kB,IAAA1a,OAAAy/B,WA6CAz2B,GAAAhJ,OAAAS,UAAAwB,SAgFA4a,IALA7S,EAAA,qBAKAA,EAAA,+BAiBAtJ,GAAAV,OAAAS,UAAAC,eAmBAg/B,GAAA,SACAxtB,GAAAxH,EAAA,SAAAT,GACA,MAAAA,GAAAytB,QAAAgI,GAAA,SAAAzC,EAAAt9B,GAAkD,MAAAA,KAAAggC,cAAA,OAMlDnsB,GAAA9I,EAAA,SAAAT,GACA,MAAAA,GAAA21B,OAAA,GAAAD,cAAA11B,EAAAoE,MAAA,KAMAwxB,GAAA,aACA7rB,GAAAtJ,EAAA,SAAAT,GACA,MAAAA,GAAAytB,QAAAmI,GAAA,OAAA/5B,gBA8BAzB,GAAAy7B,SAAAr/B,UAAA4D,KACA+G,EACAP,EAkDAk1B,GAAA,SAAA/0B,EAAAa,EAAAlM,GAA6B,UAO7Boc,GAAA,SAAAkhB,GAA6B,MAAAA,IAkE7B+C,GAAA,uBAEA3S,IACA,YACA,YACA,UAGA4S,IACA,eACA,UACA,cACA,UACA,eACA,UACA,gBACA,YACA,YACA,cACA,gBACA,kBAOA1qB,IAKA2qB,sBAAAlgC,OAAAmK,OAAA,MAKAg2B,QAAA,EAKAC,eAAiB,EAKjBpX,UAAY,EAKZqX,aAAA,EAKA7qB,aAAA,KAKA8qB,YAAA,KAKAC,mBAMA9jB,SAAAzc,OAAAmK,OAAA,MAMAoY,cAAAwd,GAMAS,eAAAT,GAMAhR,iBAAAgR,GAKAzd,gBAAAze,EAKA2e,qBAAAzG,GAMAe,YAAAijB,GAMAU,OAAA,EAKAC,gBAAAT,IAUAU,GAAA,8JAyBAxzB,GAAA,GAAAyzB,QAAA,KAAAD,GAAA,kBAkBAE,GAAA,gBAGAnrB,GAAA,mBAAAuZ,QACAtZ,GAAA,mBAAAmrB,gCAAAC,SACAC,GAAArrB,IAAAmrB,cAAAC,SAAAj7B,cACAm7B,GAAAvrB,IAAAuZ,OAAAtpB,UAAAE,UAAAC,cACA4sB,GAAAuO,IAAA,eAAAr7B,KAAAq7B,IACA1N,GAAA0N,OAAA32B,QAAA,cACAqoB,GAAAsO,OAAA32B,QAAA,WAEA42B,IADAD,OAAA32B,QAAA,WACA22B,IAAA,uBAAAr7B,KAAAq7B,KAAA,QAAAD,IAGAG,IAFAF,IAAA,cAAAr7B,KAAAq7B,IACAA,IAAA,YAAAr7B,KAAAq7B,IACAA,OAAA1sB,MAAA,mBAGA0V,MAAqBD,MAErBkL,IAAA,CACA,IAAAxf,GACA,IACA,GAAA8T,MACAxpB,QAAAC,eAAAupB,GAAA,WACAppB,IAAA,WAEA80B,IAAA,KAGAjG,OAAA2E,iBAAA,oBAAApK,IACG,MAAApd,IAKH,GAAAg1B,IA2BAC,GA1BAxxB,GAAA,WAWA,WAVApH,KAAA24B,KAOAA,IALA1rB,KAAAC,QAAA,KAAAtN,IAGAA,EAAA,oBAAAA,EAAA,QAAAi5B,IAAAC,UAKAH,IAIApY,GAAAtT,IAAAuZ,OAAAuS,6BAOArwB,GACA,mBAAA+J,SAAA7N,EAAA6N,SACA,mBAAA9J,UAAA/D,EAAA+D,QAAAC,QAMAgwB,IAFA,mBAAAI,MAAAp0B,EAAAo0B,KAEAA,IAGA,WACA,QAAAA,KACAv/B,KAAAqO,IAAAvQ,OAAAmK,OAAA,MAYA,MAVAs3B,GAAAhhC,UAAAuW,IAAA,SAAAvM,GACA,WAAAvI,KAAAqO,IAAA9F,IAEAg3B,EAAAhhC,UAAAoH,IAAA,SAAA4C,GACAvI,KAAAqO,IAAA9F,IAAA,GAEAg3B,EAAAhhC,UAAAkW,MAAA,WACAzU,KAAAqO,IAAAvQ,OAAAmK,OAAA,OAGAs3B,IAMA,IAAAC,IAAA79B,EA8FA89B,GAAA,EAMAj0B,GAAA,WACAxL,KAAAI,GAAAq/B,KACAz/B,KAAA0/B,QAGAl0B,IAAAjN,UAAAohC,OAAA,SAAAC,GACA5/B,KAAA0/B,KAAAr/B,KAAAu/B,IAGAp0B,GAAAjN,UAAAshC,UAAA,SAAAD,GACAr+B,EAAAvB,KAAA0/B,KAAAE,IAGAp0B,GAAAjN,UAAAgQ,OAAA,WACA/C,GAAAF,QACAE,GAAAF,OAAAw0B,OAAA9/B,OAIAwL,GAAAjN,UAAAmQ,OAAA,WASA,OAPAgxB,GAAA1/B,KAAA0/B,KAAAvzB,QAOA9O,EAAA,EAAAC,EAAAoiC,EAAAv/B,OAAkC9C,EAAAC,EAAOD,IACzCqiC,EAAAriC,GAAAiE,UAOAkK,GAAAF,OAAA,IACA,IAAAC,OAcAK,GAAA,SACAI,EACAC,EACAC,EACAE,EACAC,EACAhH,EACAiH,EACAC,GAEAvM,KAAAgM,MACAhM,KAAAiM,OACAjM,KAAAkM,WACAlM,KAAAoM,OACApM,KAAAqM,MACArM,KAAAwM,OAAAjG,GACAvG,KAAAqF,UACArF,KAAA2M,cAAApG,GACAvG,KAAA4M,cAAArG,GACAvG,KAAA6M,cAAAtG,GACAvG,KAAAuI,IAAA0D,KAAA1D,IACAvI,KAAAsM,mBACAtM,KAAAgsB,sBAAAzlB,GACAvG,KAAAwF,WAAAe,GACAvG,KAAA+/B,KAAA,EACA//B,KAAAyM,UAAA,EACAzM,KAAAk5B,cAAA,EACAl5B,KAAA0M,WAAA,EACA1M,KAAA+M,UAAA,EACA/M,KAAAwb,QAAA,EACAxb,KAAAuM,eACAvM,KAAA8M,cAAAvG,GACAvG,KAAA6iB,oBAAA,GAGAmd,IAA0BvvB,OAASzS,cAAA,GAInCgiC,IAAAvvB,MAAAvS,IAAA,WACA,MAAA8B,MAAAgsB,mBAGAluB,OAAAmiC,iBAAAr0B,GAAArN,UAAAyhC,GAEA,IAAA9iB,IAAA,SAAA9Q,OACA,KAAAA,MAAA,GAEA,IAAAwK,GAAA,GAAAhL,GAGA,OAFAgL,GAAAxK,OACAwK,EAAAlK,WAAA,EACAkK,GA0CAspB,GAAAx4B,MAAAnJ,UACA4hC,GAAAriC,OAAAmK,OAAAi4B,KAGA,OACA,MACA,QACA,UACA,SACA,OACA,WAMA1oB,QAAA,SAAA4oB,GAEA,GAAAzN,GAAAuN,GAAAE,EACAv1B,GAAAs1B,GAAAC,EAAA,WAEA,IADA,GAAAjtB,MAAAX,EAAAzJ,UAAA5I,OACAqS,KAAAW,EAAAX,GAAAzJ,UAAAyJ,EAEA,IAEAsd,GAFAxY,EAAAqb,EAAA3pB,MAAAhJ,KAAAmT,GACA3F,EAAAxN,KAAAyN,MAEA,QAAA2yB,GACA,WACA,cACAtQ,EAAA3c,CACA,MACA,cACA2c,EAAA3c,EAAAhH,MAAA,GAMA,MAHA2jB,IAAmBtiB,EAAA6yB,aAAAvQ,GAEnBtiB,EAAAU,IAAAQ,SACA4I,KAMA,IAAAgpB,IAAAxiC,OAAAyiC,oBAAAJ,IAMAlzB,IAAA,EAYAS,GAAA,SAAAxH,GACAlG,KAAAkG,QACAlG,KAAAkO,IAAA,GAAA1C,IACAxL,KAAA8N,QAAA,EACAjD,EAAA3E,EAAA,SAAAlG,MACA0H,MAAAC,QAAAzB,IACAy4B,GACAzxB,EAAAhH,EAAAi6B,IAEA9yB,EAAAnH,EAAAi6B,GAAAG,IAEAtgC,KAAAqgC,aAAAn6B,IAEAlG,KAAAwgC,KAAAt6B,GASAwH,IAAAnP,UAAAiiC,KAAA,SAAAn/B,GAEA,OADAiJ,GAAAxM,OAAAwM,KAAAjJ,GACAhE,EAAA,EAAiBA,EAAAiN,EAAAnK,OAAiB9C,IAClC0Q,EAAA1M,EAAAiJ,EAAAjN,KAOAqQ,GAAAnP,UAAA8hC,aAAA,SAAAI,GACA,OAAApjC,GAAA,EAAAC,EAAAmjC,EAAAtgC,OAAmC9C,EAAAC,EAAOD,IAC1CiQ,EAAAmzB,EAAApjC,IAgNA,IAAAuT,IAAAyC,GAAA2qB,qBA4FAptB,IAAA3E,KAAA,SACAoD,EACAC,EACAC,GAEA,MAAAA,GAcAH,EAAAC,EAAAC,EAAAC,GAbAD,GAAA,kBAAAA,GAQAD,EAEAD,EAAAC,EAAAC,IAmCAyuB,GAAAvmB,QAAA,SAAApS,GACAwL,GAAAxL,GAAAsK,IAyBAyb,GAAA3T,QAAA,SAAAtW,GACA0P,GAAA1P,EAAA,KAAA2O,IASAe,GAAAkX,MAAA,SACAzY,EACAC,EACAC,EACAhH,GAMA,GAHA8G,IAAA0Y,KAAkC1Y,MAAA9I,IAClC+I,IAAAyY,KAAiCzY,MAAA/I,KAEjC+I,EAAkB,MAAAxR,QAAAmK,OAAAoH,GAAA,KAIlB,KAAAA,EAAmB,MAAAC,EACnB,IAAAjG,KACAC,GAAAD,EAAAgG,EACA,QAAAqJ,KAAApJ,GAAA,CACA,GAAA9J,GAAA6D,EAAAqP,GACAjI,EAAAnB,EAAAoJ,EACAlT,KAAAkC,MAAAC,QAAAnC,KACAA,OAEA6D,EAAAqP,GAAAlT,EACAA,EAAArG,OAAAsR,GACA/I,MAAAC,QAAA8I,SAEA,MAAApH,IAMAuH,GAAAb,MACAa,GAAA4W,QACA5W,GAAAV,OACAU,GAAAgX,SAAA,SACAvY,EACAC,EACAC,EACAhH,GAKA,IAAA8G,EAAmB,MAAAC,EACnB,IAAAjG,GAAAvL,OAAAmK,OAAA,KAGA,OAFAqB,GAAAD,EAAAgG,GACAC,GAAiBhG,EAAAD,EAAAiG,GACjBjG,GAEAuH,GAAAuG,QAAA/H,CAKA,IAyhBA+E,IAzhBAtD,GAAA,SAAAxB,EAAAC,GACA,WAAA/I,KAAA+I,EACAD,EACAC,GA6fAoxB,IAAA,EAEA3sB,MACAF,IAAA,CA+BA,uBAAAO,UAAAjJ,EAAAiJ,SAAA,CACA,GAAA3V,IAAA2V,QAAAC,SACAF,IAAA,WACA1V,GAAA+I,KAAAoM,IAMAorB,IAAgBrc,WAAAhhB,IAEhB++B,IAAA,MACC,IAAAlQ,IAAA,mBAAAmQ,oBACDx1B,EAAAw1B,mBAEA,yCAAAA,iBAAA5gC,WAoBAoU,OAJC,KAAA/N,GAAA+E,EAAA/E,GAID,WACAA,EAAAwN,KAIA,WACA+O,WAAA/O,GAAA,QAzBA,CAIA,GAAAgtB,IAAA,EACAC,GAAA,GAAAF,kBAAA/sB,IACAktB,GAAA9/B,SAAA6B,eAAAsE,OAAAy5B,IACAC,IAAAvzB,QAAAwzB,IACAC,eAAA,IAEA5sB,GAAA,WACAysB,OAAA,KACAE,GAAA70B,KAAA9E,OAAAy5B,KAEAF,IAAA,EA4CA,GAwFAlsB,IAAA,GAAA2qB,IA4DAzpB,GAAAlN,EAAA,SAAA7K,GACA,GAAAgY,GAAA,MAAAhY,EAAA+/B,OAAA,EACA//B,GAAAgY,EAAAhY,EAAAwO,MAAA,GAAAxO,CACA,IAAAqjC,GAAA,MAAArjC,EAAA+/B,OAAA,EACA//B,GAAAqjC,EAAArjC,EAAAwO,MAAA,GAAAxO,CACA,IAAAoV,GAAA,MAAApV,EAAA+/B,OAAA,EAEA,OADA//B,GAAAoV,EAAApV,EAAAwO,MAAA,GAAAxO,GAEAA,OACA8M,KAAAu2B,EACAjuB,UACA4C,YAk2BAyG,IAAAmB,GAAAhf,UA0EA,IAupBA+M,IAvpBAkU,IACAyhB,KAAA,SAAAn1B,EAAAyY,GACA,GACAzY,EAAAkgB,oBACAlgB,EAAAkgB,kBAAA7H,cACArY,EAAAG,KAAAi1B,UACA,CAEA,GAAAC,GAAAr1B,CACA0T,IAAA4hB,SAAAD,SACK,EACLr1B,EAAAkgB,kBAAA9M,GACApT,EACA4X,KAEA2d,OAAA9c,EAAAzY,EAAAO,QAAA9F,GAAAge,KAIA6c,SAAA,SAAAtS,EAAAhjB,GACA,GAAA7I,GAAA6I,EAAAQ,gBAEAyY,IADAjZ,EAAAkgB,kBAAA8C,EAAA9C,kBAGA/oB,EAAAwO,UACAxO,EAAA4a,UACA/R,EACA7I,EAAAiJ,WAIAo1B,OAAA,SAAAx1B,GACA,GAAAzG,GAAAyG,EAAAzG,QACA2mB,EAAAlgB,EAAAkgB,iBACAA,GAAA9H,aACA8H,EAAA9H,YAAA,EACAO,GAAAuH,EAAA,YAEAlgB,EAAAG,KAAAi1B,YACA77B,EAAA6e,WAMA8C,GAAAgF,GAEAtG,GAAAsG,GAAA,KAKAuV,QAAA,SAAAz1B,GACA,GAAAkgB,GAAAlgB,EAAAkgB,iBACAA,GAAA7H,eACArY,EAAAG,KAAAi1B,UAGAtb,GAAAoG,GAAA,GAFAA,EAAAC,cAQA3M,GAAAxhB,OAAAwM,KAAAkV,IAqKAW,GAAA,EACAH,GAAA,EA+KA2B,GAAA,KA+XA+B,GAAA,KA+UAsC,MACAC,MACAnR,MAEAoR,IAAA,EACAC,IAAA,EACA3jB,GAAA,EAmBA6jB,GAAA,EAGAC,GAAAnc,KAAAq3B,GAQA,IAAAhuB,KAAAgd,GAAA,CACA,GAAA2N,IAAApR,OAAAoR,WAEAA,KACA,kBAAAA,IAAAqD,KACAlb,KAAAtlB,SAAA26B,YAAA,SAAA7I,YAMAxM,GAAA,WAA0B,MAAA6X,IAAAqD,QAoI1B,GAAAC,IAAA,EAOA5c,GAAA,SACAtV,EACAyZ,EACA/U,EACAhR,EACAy+B,GAEA1hC,KAAAuP,KACAmyB,IACAnyB,EAAAwU,SAAA/jB,MAEAuP,EAAA8X,UAAAhnB,KAAAL,MAEAiD,GACAjD,KAAA2hC,OAAA1+B,EAAA0+B,KACA3hC,KAAA4hC,OAAA3+B,EAAA2+B,KACA5hC,KAAA6hC,OAAA5+B,EAAA4+B,KACA7hC,KAAA+hB,OAAA9e,EAAA8e,KACA/hB,KAAA8kB,OAAA7hB,EAAA6hB,QAEA9kB,KAAA2hC,KAAA3hC,KAAA4hC,KAAA5hC,KAAA6hC,KAAA7hC,KAAA+hB,MAAA,EAEA/hB,KAAAiU,KACAjU,KAAAI,KAAAqhC,GACAzhC,KAAA8hC,QAAA,EACA9hC,KAAA6oB,MAAA7oB,KAAA6hC,KACA7hC,KAAA+hC,QACA/hC,KAAAgiC,WACAhiC,KAAAiiC,OAAA,GAAA9C,IACAn/B,KAAAkiC,UAAA,GAAA/C,IACAn/B,KAAAmiC,WAEA,GAEA,kBAAAnZ,GACAhpB,KAAApC,OAAAorB,GAEAhpB,KAAApC,OAAAmN,EAAAie,GACAhpB,KAAApC,SACAoC,KAAApC,OAAA+D,IASA3B,KAAAkG,MAAAlG,KAAA6hC,SACAt7B,GACAvG,KAAA9B,MAMA2mB,IAAAtmB,UAAAL,IAAA,WACAmN,EAAArL,KACA,IAAAkG,GACAqJ,EAAAvP,KAAAuP,EACA,KACArJ,EAAAlG,KAAApC,OAAAL,KAAAgS,KACG,MAAArF,GACH,IAAAlK,KAAA4hC,KAGA,KAAA13B,EAFAuI,IAAAvI,EAAAqF,EAAA,uBAAAvP,KAAA,gBAIG,QAGHA,KAAA2hC,MACArtB,GAAApO,GAEAuF,IACAzL,KAAAoiC,cAEA,MAAAl8B,IAMA2e,GAAAtmB,UAAAuhC,OAAA,SAAA5xB,GACA,GAAA9N,GAAA8N,EAAA9N,EACAJ,MAAAkiC,UAAAptB,IAAA1U,KACAJ,KAAAkiC,UAAAv8B,IAAAvF,GACAJ,KAAAgiC,QAAA3hC,KAAA6N,GACAlO,KAAAiiC,OAAAntB,IAAA1U,IACA8N,EAAAyxB,OAAA3/B,QAQA6kB,GAAAtmB,UAAA6jC,YAAA,WAEA,IADA,GAAA/kC,GAAA2C,KAAA+hC,KAAA5hC,OACA9C,KAAA,CACA,GAAA6Q,GAAAlO,KAAA+hC,KAAA1kC,EACA2C,MAAAkiC,UAAAptB,IAAA5G,EAAA9N,KACA8N,EAAA2xB,UAAA7/B,MAGA,GAAAqiC,GAAAriC,KAAAiiC,MACAjiC,MAAAiiC,OAAAjiC,KAAAkiC,UACAliC,KAAAkiC,UAAAG,EACAriC,KAAAkiC,UAAAztB,QACA4tB,EAAAriC,KAAA+hC,KACA/hC,KAAA+hC,KAAA/hC,KAAAgiC,QACAhiC,KAAAgiC,QAAAK,EACAriC,KAAAgiC,QAAA7hC,OAAA,GAOA0kB,GAAAtmB,UAAA+C,OAAA,WAEAtB,KAAA6hC,KACA7hC,KAAA6oB,OAAA,EACG7oB,KAAA+hB,KACH/hB,KAAAymB,MAEAQ,GAAAjnB,OAQA6kB,GAAAtmB,UAAAkoB,IAAA,WACA,GAAAzmB,KAAA8hC,OAAA,CACA,GAAA57B,GAAAlG,KAAA9B,KACA,IACAgI,IAAAlG,KAAAkG,OAIAU,EAAAV,IACAlG,KAAA2hC,KACA,CAEA,GAAAlS,GAAAzvB,KAAAkG,KAEA,IADAlG,KAAAkG,QACAlG,KAAA4hC,KACA,IACA5hC,KAAAiU,GAAA1W,KAAAyC,KAAAuP,GAAArJ,EAAAupB,GACS,MAAAvlB,GACTuI,GAAAvI,EAAAlK,KAAAuP,GAAA,yBAAAvP,KAAA,oBAGAA,MAAAiU,GAAA1W,KAAAyC,KAAAuP,GAAArJ,EAAAupB,MAUA5K,GAAAtmB,UAAAuqB,SAAA,WACA9oB,KAAAkG,MAAAlG,KAAA9B,MACA8B,KAAA6oB,OAAA,GAMAhE,GAAAtmB,UAAAgQ,OAAA,WAEA,IADA,GAAAlR,GAAA2C,KAAA+hC,KAAA5hC,OACA9C,KACA2C,KAAA+hC,KAAA1kC,GAAAkR,UAOAsW,GAAAtmB,UAAA+jC,SAAA,WACA,GAAAtiC,KAAA8hC,OAAA,CAIA9hC,KAAAuP,GAAA6U,mBACA7iB,EAAAvB,KAAAuP,GAAA8X,UAAArnB,KAGA,KADA,GAAA3C,GAAA2C,KAAA+hC,KAAA5hC,OACA9C,KACA2C,KAAA+hC,KAAA1kC,GAAAwiC,UAAA7/B,KAEAA,MAAA8hC,QAAA,GAMA,IAAA3a,KACAlpB,YAAA,EACAD,cAAA,EACAE,IAAAyD,EACA0M,IAAA1M,GAsIA6mB,IAA8BqZ,MAAA,GA6M9BU,GAAA,GAEA,SAAAvY,GACAA,EAAAzrB,UAAA0rB,MAAA,SAAAhnB,GACA,GAAAsM,GAAAvP,IAEAuP,GAAAizB,KAAAD,KAWAhzB,EAAA1B,QAAA,EAEA5K,KAAAkc,aAIA+J,GAAA3Z,EAAAtM,GAEAsM,EAAA2C,SAAA1B,EACAoO,GAAArP,EAAA4Z,aACAlmB,MACAsM,GAOAA,EAAA8L,aAAA9L,EAGAA,EAAAkzB,MAAAlzB,EACAoU,GAAApU,GACAwT,GAAAxT,GACAsR,GAAAtR,GACAkV,GAAAlV,EAAA,gBACA8H,GAAA9H,GACA6X,GAAA7X,GACA2H,GAAA3H,GACAkV,GAAAlV,EAAA,WASAA,EAAA2C,SAAAoS,IACA/U,EAAA8xB,OAAA9xB,EAAA2C,SAAAoS,MAsEA0F,IAnLA,SAAAA,GAIA,GAAA0Y,KACAA,GAAAxkC,IAAA,WAA6B,MAAA8B,MAAA2nB,MAC7B,IAAAgb,KACAA,GAAAzkC,IAAA,WAA8B,MAAA8B,MAAAmS,QAa9BrU,OAAAC,eAAAisB,EAAAzrB,UAAA,QAAAmkC,GACA5kC,OAAAC,eAAAisB,EAAAzrB,UAAA,SAAAokC,GAEA3Y,EAAAzrB,UAAAqkC,KAAAv0B,EACA2b,EAAAzrB,UAAAskC,QAAAj0B,EAEAob,EAAAzrB,UAAA0qB,OAAA,SACAD,EACA/U,EACAhR,GAEA,GAAAsM,GAAAvP,IACA,IAAA6G,EAAAoN,GACA,MAAA8U,IAAAxZ,EAAAyZ,EAAA/U,EAAAhR,EAEAA,SACAA,EAAA2+B,MAAA,CACA,IAAArb,GAAA,GAAA1B,IAAAtV,EAAAyZ,EAAA/U,EAAAhR,EACA,IAAAA,EAAA6/B,UACA,IACA7uB,EAAA1W,KAAAgS,EAAAgX,EAAArgB,OACO,MAAAyN,GACPlB,GAAAkB,EAAApE,EAAA,mCAAAgX,EAAA,gBAGA,kBACAA,EAAA+b,cAsIAtY,IAvwCA,SAAAA,GACA,GAAA+Y,GAAA,QACA/Y,GAAAzrB,UAAA2jB,IAAA,SAAAzM,EAAAhN,GACA,GAAA8G,GAAAvP,IACA,IAAA0H,MAAAC,QAAA8N,GACA,OAAApY,GAAA,EAAAC,EAAAmY,EAAAtV,OAAuC9C,EAAAC,EAAOD,IAC9CkS,EAAA2S,IAAAzM,EAAApY,GAAAoL,QAGA8G,EAAAyT,QAAAvN,KAAAlG,EAAAyT,QAAAvN,QAAApV,KAAAoI,GAGAs6B,EAAAr/B,KAAA+R,KACAlG,EAAA0T,eAAA,EAGA,OAAA1T,IAGAya,EAAAzrB,UAAAykC,MAAA,SAAAvtB,EAAAhN,GAEA,QAAA2M,KACA7F,EAAA6T,KAAA3N,EAAAL,GACA3M,EAAAO,MAAAuG,EAAAxG,WAHA,GAAAwG,GAAAvP,IAOA,OAFAoV,GAAA3M,KACA8G,EAAA2S,IAAAzM,EAAAL,GACA7F,GAGAya,EAAAzrB,UAAA6kB,KAAA,SAAA3N,EAAAhN,GACA,GAAA8G,GAAAvP,IAEA,KAAA+I,UAAA5I,OAEA,MADAoP,GAAAyT,QAAAllB,OAAAmK,OAAA,MACAsH,CAGA,IAAA7H,MAAAC,QAAA8N,GAAA,CACA,OAAAwtB,GAAA,EAAA3lC,EAAAmY,EAAAtV,OAAyC8iC,EAAA3lC,EAAS2lC,IAClD1zB,EAAA6T,KAAA3N,EAAAwtB,GAAAx6B,EAEA,OAAA8G,GAGA,GAAA2zB,GAAA3zB,EAAAyT,QAAAvN,EACA,KAAAytB,EACA,MAAA3zB,EAEA,KAAA9G,EAEA,MADA8G,GAAAyT,QAAAvN,GAAA,KACAlG,CAKA,KAFA,GAAA0E,GACA5W,EAAA6lC,EAAA/iC,OACA9C,KAEA,IADA4W,EAAAivB,EAAA7lC,MACAoL,GAAAwL,EAAAxL,OAAA,CACAy6B,EAAA76B,OAAAhL,EAAA,EACA,OAGA,MAAAkS,IAGAya,EAAAzrB,UAAAunB,MAAA,SAAArQ,GACA,GAAAlG,GAAAvP,KAaAkjC,EAAA3zB,EAAAyT,QAAAvN,EACA,IAAAytB,EAAA,CACAA,IAAA/iC,OAAA,EAAAgJ,EAAA+5B,IAGA,QAFA/vB,GAAAhK,EAAAJ,UAAA,GACA4J,EAAA,sBAAA8C,EAAA,IACApY,EAAA,EAAAC,EAAA4lC,EAAA/iC,OAAqC9C,EAAAC,EAAOD,IAC5C4V,GAAAiwB,EAAA7lC,GAAAkS,EAAA4D,EAAA5D,EAAAoD,GAGA,MAAApD,KA+qCAya,IApoCA,SAAAA,GACAA,EAAAzrB,UAAAomB,QAAA,SAAA7Y,EAAAyY,GACA,GAAAhV,GAAAvP,KACAmjC,EAAA5zB,EAAAiV,IACA4e,EAAA7zB,EAAAuR,OACAuiB,EAAA7f,GAAAjU,EACAA,GAAAuR,OAAAhV,EAQAyD,EAAAiV,IALA4e,EAKA7zB,EAAA+zB,UAAAF,EAAAt3B,GAHAyD,EAAA+zB,UAAA/zB,EAAAiV,IAAA1Y,EAAAyY,GAAA,GAKA8e,IAEAF,IACAA,EAAAI,QAAA,MAEAh0B,EAAAiV,MACAjV,EAAAiV,IAAA+e,QAAAh0B,GAGAA,EAAAjK,QAAAiK,EAAAsD,SAAAtD,EAAAjK,SAAAiK,EAAAsD,QAAAiO,SACAvR,EAAAsD,QAAA2R,IAAAjV,EAAAiV,MAMAwF,EAAAzrB,UAAA8jB,aAAA,WACA,GAAA9S,GAAAvP,IACAuP,GAAAwU,UACAxU,EAAAwU,SAAAziB,UAIA0oB,EAAAzrB,UAAA0tB,SAAA,WACA,GAAA1c,GAAAvP,IACA,KAAAuP,EAAA6U,kBAAA,CAGAK,GAAAlV,EAAA,iBACAA,EAAA6U,mBAAA,CAEA,IAAA5e,GAAA+J,EAAAsD,SACArN,KAAA4e,mBAAA7U,EAAA2C,SAAA8M,UACAzd,EAAAiE,EAAAoe,UAAArU,GAGAA,EAAAwU,UACAxU,EAAAwU,SAAAue,UAGA,KADA,GAAAjlC,GAAAkS,EAAA8X,UAAAlnB,OACA9C,KACAkS,EAAA8X,UAAAhqB,GAAAilC,UAIA/yB,GAAAoY,MAAAla,QACA8B,EAAAoY,MAAAla,OAAAK,UAGAyB,EAAA4U,cAAA,EAEA5U,EAAA+zB,UAAA/zB,EAAAuR,OAAA,MAEA2D,GAAAlV,EAAA,aAEAA,EAAA6T,OAEA7T,EAAAiV,MACAjV,EAAAiV,IAAA+e,QAAA,MAGAh0B,EAAAjK,SACAiK,EAAAjK,OAAAE,OAAA,SAujCAwkB,IAviDA,SAAAA,GAEA5N,GAAA4N,EAAAzrB,WAEAyrB,EAAAzrB,UAAAilC,UAAA,SAAA/6B,GACA,MAAAuL,IAAAvL,EAAAzI,OAGAgqB,EAAAzrB,UAAAqmB,QAAA,WACA,GAAArV,GAAAvP,KACAmuB,EAAA5e,EAAA2C,SACAnN,EAAAopB,EAAAppB,OACAqa,EAAA+O,EAAA/O,YAEAA,KACA7P,EAAAkK,aAAAzB,GACAoH,EAAAnT,KAAA8R,YACAxO,EAAAmK,OACAnK,EAAAkK,eAMAlK,EAAAjK,OAAA8Z,CAEA,IAAAtT,EACA,KAIA6V,GAAApS,EACAzD,EAAA/G,EAAAxH,KAAAgS,EAAA8L,aAAA9L,EAAAoK,gBACK,MAAAzP,GACLuI,GAAAvI,EAAAqF,EAAA,UAYAzD,EAAAyD,EAAAuR,OAEK,QACLa,GAAA,KAmBA,MAhBAja,OAAAC,QAAAmE,IAAA,IAAAA,EAAA3L,SACA2L,IAAA,IAGAA,YAAAF,MAQAE,EAAAoR,MAGApR,EAAAtG,OAAA4Z,EACAtT,IAo+CAke,GA8MA,IAAAyZ,KAAAt8B,OAAAu3B,OAAAh3B,OAEAg8B,IACA/lC,KAAA,aACAqhB,UAAA,EAEAjP,OACA4zB,QAAAF,GACAG,QAAAH,GACA90B,KAAAxH,OAAAuwB,SAGAmM,QAAA,WACA7jC,KAAA0I,MAAA5K,OAAAmK,OAAA,MACAjI,KAAAsK,SAGAw5B,UAAA,WACA,OAAAv7B,KAAAvI,MAAA0I,MACAmjB,GAAA7rB,KAAA0I,MAAAH,EAAAvI,KAAAsK,OAIAy5B,QAAA,WACA,GAAAtmB,GAAAzd,IAEAA,MAAAipB,OAAA,mBAAAhiB,GACAykB,GAAAjO,EAAA,SAAA9f,GAA0C,MAAA6tB,IAAAvkB,EAAAtJ,OAE1CqC,KAAAipB,OAAA,mBAAAhiB,GACAykB,GAAAjO,EAAA,SAAA9f,GAA0C,OAAA6tB,GAAAvkB,EAAAtJ,QAI1CoH,OAAA,WACA,GAAA8S,GAAA7X,KAAA0Z,OAAA5U,QACAgH,EAAAgX,GAAAjL,GACAvL,EAAAR,KAAAQ,gBACA,IAAAA,EAAA,CAEA,GAAA3O,GAAA4tB,GAAAjf,GACA6hB,EAAAnuB,KACA2jC,EAAAxV,EAAAwV,QACAC,EAAAzV,EAAAyV,OACA,IAEAD,KAAAhmC,IAAA6tB,GAAAmY,EAAAhmC,KAEAimC,GAAAjmC,GAAA6tB,GAAAoY,EAAAjmC,GAEA,MAAAmO,EAGA,IAAAk4B,GAAAhkC,KACA0I,EAAAs7B,EAAAt7B,MACA4B,EAAA05B,EAAA15B,KACA/B,EAAA,MAAAuD,EAAAvD,IAGA+D,EAAAlB,KAAAqT,KAAAnS,EAAAN,IAAA,KAAAM,EAAA,QACAR,EAAAvD,GACAG,GAAAH,IACAuD,EAAAkgB,kBAAAtjB,EAAAH,GAAAyjB,kBAEAzqB,EAAA+I,EAAA/B,GACA+B,EAAAjK,KAAAkI,KAEAG,EAAAH,GAAAuD,EACAxB,EAAAjK,KAAAkI,GAEAvI,KAAA2O,KAAArE,EAAAnK,OAAA8jC,SAAAjkC,KAAA2O,MACAkd,GAAAnjB,EAAA4B,EAAA,GAAAA,EAAAtK,KAAA8gB,SAIAhV,EAAAG,KAAAi1B,WAAA,EAEA,MAAAp1B,IAAA+L,KAAA,KAIAqsB,IACAR,eAKA,SAAA1Z,GAEA,GAAAma,KACAA,GAAAjmC,IAAA,WAA+B,MAAAmV,KAQ/BvV,OAAAC,eAAAisB,EAAA,SAAAma,GAKAna,EAAAoa,MACA5E,QACAl2B,SACAkH,eACA6zB,eAAAt2B,GAGAic,EAAA3b,MACA2b,EAAAsa,OAAA11B,EACAob,EAAAhW,YAGAgW,EAAAua,WAAA,SAAAljC,GAEA,MADAiM,GAAAjM,GACAA,GAGA2oB,EAAA/mB,QAAAnF,OAAAmK,OAAA,MACAkjB,GAAA3T,QAAA,SAAAtW,GACA8oB,EAAA/mB,QAAA/B,EAAA,KAAApD,OAAAmK,OAAA,QAKA+hB,EAAA/mB,QAAA6N,MAAAkZ,EAEA1gB,EAAA0gB,EAAA/mB,QAAA0mB,WAAAua,IAEAha,GAAAF,GACAS,GAAAT,GACAW,GAAAX,GACAqB,GAAArB,IAGAA,IAEAlsB,OAAAC,eAAAisB,GAAAzrB,UAAA,aACAL,IAAAyP,KAGA7P,OAAAC,eAAAisB,GAAAzrB,UAAA,eACAL,IAAA,WAEA,MAAA8B,MAAAsF,QAAAtF,KAAAsF,OAAAC,cAKAzH,OAAAC,eAAAisB,GAAA,2BACA9jB,MAAAqX,KAGAyM,GAAAwa,QAAA,QAMA,IAi4CAlS,IA8FAkB,GAiNAiR,GAhrDAnG,GAAAx2B,EAAA,eAGA48B,GAAA58B,EAAA,yCACA8S,GAAA,SAAA5O,EAAA9K,EAAAyjC,GACA,MACA,UAAAA,GAAAD,GAAA14B,IAAA,WAAA9K,GACA,aAAAyjC,GAAA,WAAA34B,GACA,YAAA24B,GAAA,UAAA34B,GACA,UAAA24B,GAAA,UAAA34B,GAIA8kB,GAAAhpB,EAAA,wCAEA88B,GAAA98B,EAAA,sCAEAqpB,GAAA,SAAA5oB,EAAArC,GACA,MAAAgrB,IAAAhrB,IAAA,UAAAA,EACA,QAEA,oBAAAqC,GAAAq8B,GAAA1+B,GACAA,EACA,QAGA+qB,GAAAnpB,EACA,wYAQA8oB,GAAA,+BAEAF,GAAA,SAAA/yB,GACA,YAAAA,EAAA+/B,OAAA,cAAA//B,EAAAwO,MAAA,MAGA0kB,GAAA,SAAAlzB,GACA,MAAA+yB,IAAA/yB,KAAAwO,MAAA,EAAAxO,EAAAwC,QAAA,IAGA+wB,GAAA,SAAAjqB,GACA,aAAAA,IAAA,IAAAA,GAsFAwmB,IACAgG,IAAA,6BACAoR,KAAA,sCAGAC,GAAAh9B,EACA,snBAeA8kB,GAAA9kB,EACA,kNAGA,GAGAuY,GAAA,SAAArU,GACA,MAAA84B,IAAA94B,IAAA4gB,GAAA5gB,IAcA8gB,GAAAhvB,OAAAmK,OAAA,MA0BAwmB,GAAA3mB,EAAA,6CAgFAi9B,GAAAjnC,OAAAy/B,QACAt8B,cAAAmsB,GACAG,mBACA1qB,kBACA6qB,iBACA3qB,gBACAlB,eACAT,eACAQ,cACAisB,eACAR,WACAS,kBACAE,mBAKAG,IACAlmB,OAAA,SAAA8yB,EAAAjvB,GACAmiB,GAAAniB,IAEAxK,OAAA,SAAAwtB,EAAAhjB,GACAgjB,EAAA7iB,KAAAkiB,MAAAriB,EAAAG,KAAAkiB,MACAF,GAAAa,GAAA,GACAb,GAAAniB,KAGAy1B,QAAA,SAAAz1B,GACAmiB,GAAAniB,GAAA,KA2CAojB,GAAA,GAAAtjB,IAAA,UAEAgE,IAAA,iDAuvBAU,IACArI,OAAA4mB,GACAvtB,OAAAutB,GACA0S,QAAA,SAAAz1B,GACA+iB,GAAA/iB,EAAAojB,MAuEAe,GAAAnyB,OAAAmK,OAAA,MAwCA+8B,IACA7W,GACA7d,IAoGA6F,IACAlO,OAAAmoB,GACA9uB,OAAA8uB,IAoCA6U,IACAh9B,OAAA0pB,GACArwB,OAAAqwB,IAaAO,GAAA,MACAC,GAAA,MAwCAM,GAAAiO,MAAAzB,IAAAvH,OAAAuH,GAAA,SAwEAiG,IACAj9B,OAAAgrB,GACA3xB,OAAA2xB,IAgHApY,IACA5S,OAAAirB,GACA5xB,OAAA4xB,IAKAqB,GAAA/rB,EAAA,SAAA9F,GACA,GAAAgH,MACAy7B,EAAA,gBACAC,EAAA,OAOA,OANA1iC,GAAAwF,MAAAi9B,GAAA3tB,QAAA,SAAA5Y,GACA,GAAAA,EAAA,CACA,GAAAyjC,GAAAzjC,EAAAsJ,MAAAk9B,EACA/C,GAAAliC,OAAA,IAAAuJ,EAAA24B,EAAA,GAAAnO,QAAAmO,EAAA,GAAAnO,WAGAxqB,IA4DA27B,GAAA,MACAC,GAAA,iBACArQ,GAAA,SAAA3Q,EAAA3mB,EAAAsJ,GAEA,GAAAo+B,GAAA3hC,KAAA/F,GACA2mB,EAAA3D,MAAA4kB,YAAA5nC,EAAAsJ,OACG,IAAAq+B,GAAA5hC,KAAAuD,GACHqd,EAAA3D,MAAA4kB,YAAAzzB,GAAAnU,GAAAsJ,EAAAuuB,QAAA8P,GAAA,qBACG,CACH,GAAAE,GAAAC,GAAA9nC,EACA,IAAA+J,MAAAC,QAAAV,GAIA,OAAA5J,GAAA,EAAAmV,EAAAvL,EAAA9G,OAAuC9C,EAAAmV,EAASnV,IAChDinB,EAAA3D,MAAA6kB,GAAAv+B,EAAA5J,OAGAinB,GAAA3D,MAAA6kB,GAAAv+B,IAKAy+B,IAAA,qBAGAD,GAAAj9B,EAAA,SAAAkJ,GAGA,GAFA+yB,OAAAzjC,SAAAC,cAAA,OAAA0f,MAEA,YADAjP,EAAA1B,GAAA0B,KACAA,IAAA+yB,IACA,MAAA/yB,EAGA,QADAi0B,GAAAj0B,EAAAgsB,OAAA,GAAAD,cAAA/rB,EAAAvF,MAAA,GACA9O,EAAA,EAAiBA,EAAAqoC,GAAAvlC,OAAwB9C,IAAA,CACzC,GAAAM,GAAA+nC,GAAAroC,GAAAsoC,CACA,IAAAhoC,IAAA8mC,IACA,MAAA9mC,MAgDAgjB,IACA1Y,OAAA0sB,GACArzB,OAAAqzB,IAKAS,GAAA,MAiFAM,GAAAltB,EAAA,SAAA7K,GACA,OACAu6B,WAAAv6B,EAAA,SACAw6B,aAAAx6B,EAAA,YACAy6B,iBAAAz6B,EAAA,gBACA28B,WAAA38B,EAAA,SACA68B,aAAA78B,EAAA,YACA48B,iBAAA58B,EAAA,mBAIAioC,GAAApyB,KAAA6d,GACA+E,GAAA,aACAgB,GAAA,YAGAR,GAAA,aACAP,GAAA,gBACAY,GAAA,YACAX,GAAA,cACAsP,UAEAr/B,KAAAwmB,OAAA8Y,qBACAt/B,KAAAwmB,OAAA+Y,wBAEAlP,GAAA,mBACAP,GAAA,2BAEA9vB,KAAAwmB,OAAAgZ,oBACAx/B,KAAAwmB,OAAAiZ,uBAEA/O,GAAA,kBACAX,GAAA,sBAKA,IAAAV,IAAApiB,GACAuZ,OAAAkZ,sBACAlZ,OAAAkZ,sBAAA9jC,KAAA4qB,QACApK,WACA,SAAAla,GAA8C,MAAAA,MAsD9C6uB,GAAA,yBA0XAS,GAAAvkB,IACAvL,OAAA6yB,GACAoL,SAAApL,GACAv5B,OAAA,SAAAuK,EAAAquB,IAEA,IAAAruB,EAAAG,KAAA6tB,KACAI,GAAApuB,EAAAquB,GAEAA,SAKAgM,IACAhwB,GACA8uB,GACAC,GACArqB,GACA8F,GACAoX,IAOAh7B,GAAAopC,GAAAhnC,OAAA6lC,IAEAoB,GAj7DA,SAAAC,GAgBA,QAAAC,GAAAj6B,GACA,UAAAT,IAAAm5B,EAAA1X,QAAAhhB,GAAAzI,wBAA2D2C,GAAA8F,GAG3D,QAAAk6B,GAAAC,EAAA3oB,GACA,QAAAvI,KACA,KAAAA,EAAAuI,WACA4oB,EAAAD,GAIA,MADAlxB,GAAAuI,YACAvI,EAGA,QAAAmxB,GAAAniB,GACA,GAAA9e,GAAAu/B,EAAAnjC,WAAA0iB,EAEA9d,GAAAhB,IACAu/B,EAAAljC,YAAA2D,EAAA8e,GAsBA,QAAAoiB,GACA56B,EACA66B,EACAC,EACAC,EACAC,EACAC,EACAvkC,GAYA,GAVAgE,EAAAsF,EAAAO,MAAA7F,EAAAugC,KAMAj7B,EAAAi7B,EAAAvkC,GAAAqJ,EAAAC,IAGAA,EAAAotB,cAAA4N,GACAvoB,EAAAzS,EAAA66B,EAAAC,EAAAC,GAAA,CAIA,GAAA56B,GAAAH,EAAAG,KACAC,EAAAJ,EAAAI,SACAF,EAAAF,EAAAE,GACAxF,GAAAwF,IAeAF,EAAAO,IAAAP,EAAAU,GACAu4B,EAAAxX,gBAAAzhB,EAAAU,GAAAR,GACA+4B,EAAA9jC,cAAA+K,EAAAF,GACAk7B,EAAAl7B,GAIAm7B,EAAAn7B,EAAAI,EAAAy6B,GACAngC,EAAAyF,IACAi7B,EAAAp7B,EAAA66B,GAEArF,EAAAsF,EAAA96B,EAAAO,IAAAw6B,IAMKpgC,EAAAqF,EAAAY,YACLZ,EAAAO,IAAA04B,EAAArX,cAAA5hB,EAAAM,MACAk1B,EAAAsF,EAAA96B,EAAAO,IAAAw6B,KAEA/6B,EAAAO,IAAA04B,EAAAliC,eAAAiJ,EAAAM,MACAk1B,EAAAsF,EAAA96B,EAAAO,IAAAw6B,KAIA,QAAAtoB,GAAAzS,EAAA66B,EAAAC,EAAAC,GACA,GAAAxpC,GAAAyO,EAAAG,IACA,IAAAzF,EAAAnJ,GAAA,CACA,GAAA8pC,GAAA3gC,EAAAsF,EAAAkgB,oBAAA3uB,EAAA6jC,SAQA,IAPA16B,EAAAnJ,IAAA+H,OAAAoB,EAAAnJ,IAAA4jC,OACA5jC,EAAAyO,GAAA,GAMAtF,EAAAsF,EAAAkgB,mBAMA,MALAob,GAAAt7B,EAAA66B,GACArF,EAAAsF,EAAA96B,EAAAO,IAAAw6B,GACApgC,EAAA0gC,IACAE,EAAAv7B,EAAA66B,EAAAC,EAAAC,IAEA,GAKA,QAAAO,GAAAt7B,EAAA66B,GACAngC,EAAAsF,EAAAG,KAAAq7B,iBACAX,EAAAtmC,KAAA2I,MAAA29B,EAAA76B,EAAAG,KAAAq7B,eACAx7B,EAAAG,KAAAq7B,cAAA,MAEAx7B,EAAAO,IAAAP,EAAAkgB,kBAAAxH,IACA+iB,EAAAz7B,IACAo7B,EAAAp7B,EAAA66B,GACAK,EAAAl7B,KAIAmiB,GAAAniB,GAEA66B,EAAAtmC,KAAAyL,IAIA,QAAAu7B,GAAAv7B,EAAA66B,EAAAC,EAAAC,GAOA,IANA,GAAAxpC,GAKAmqC,EAAA17B,EACA07B,EAAAxb,mBAEA,GADAwb,IAAAxb,kBAAAlL,OACAta,EAAAnJ,EAAAmqC,EAAAv7B,OAAAzF,EAAAnJ,IAAA06B,YAAA,CACA,IAAA16B,EAAA,EAAmBA,EAAA6lC,EAAAgD,SAAA/lC,SAAyB9C,EAC5C6lC,EAAAgD,SAAA7oC,GAAA6xB,GAAAsY,EAEAb,GAAAtmC,KAAAmnC,EACA,OAKAlG,EAAAsF,EAAA96B,EAAAO,IAAAw6B,GAGA,QAAAvF,GAAA97B,EAAA6G,EAAAo7B,GACAjhC,EAAAhB,KACAgB,EAAAihC,GACA1C,EAAAnjC,WAAA6lC,KAAAjiC,GACAu/B,EAAAhiC,aAAAyC,EAAA6G,EAAAo7B,GAGA1C,EAAA3jC,YAAAoE,EAAA6G,IAKA,QAAA46B,GAAAn7B,EAAAI,EAAAy6B,GACA,GAAAj/B,MAAAC,QAAAuE,GAIA,OAAA7O,GAAA,EAAqBA,EAAA6O,EAAA/L,SAAqB9C,EAC1CqpC,EAAAx6B,EAAA7O,GAAAspC,EAAA76B,EAAAO,IAAA,QAAAH,EAAA7O,OAEKsJ,GAAAmF,EAAAM,OACL24B,EAAA3jC,YAAA0K,EAAAO,IAAA04B,EAAAliC,eAAAsE,OAAA2E,EAAAM,QAIA,QAAAm7B,GAAAz7B,GACA,KAAAA,EAAAkgB,mBACAlgB,IAAAkgB,kBAAAlL,MAEA,OAAAta,GAAAsF,EAAAE,KAGA,QAAAk7B,GAAAp7B,EAAA66B,GACA,OAAA1D,GAAA,EAAqBA,EAAAC,EAAAj7B,OAAA9H,SAAyB8iC,EAC9CC,EAAAj7B,OAAAg7B,GAAA/T,GAAApjB,EAEAzO,GAAAyO,EAAAG,KAAA7G,KACAoB,EAAAnJ,KACAmJ,EAAAnJ,EAAA4K,SAA4B5K,EAAA4K,OAAAinB,GAAApjB,GAC5BtF,EAAAnJ,EAAAikC,SAA4BqF,EAAAtmC,KAAAyL,IAO5B,QAAAk7B,GAAAl7B,GACA,GAAAzO,EACA,IAAAmJ,EAAAnJ,EAAAyO,EAAAe,WACAk4B,EAAA/W,cAAAliB,EAAAO,IAAAhP,OAGA,KADA,GAAAqqC,GAAA57B,EACA47B,GACAlhC,EAAAnJ,EAAAqqC,EAAAriC,UAAAmB,EAAAnJ,IAAA6U,SAAA/M,WACA4/B,EAAA/W,cAAAliB,EAAAO,IAAAhP,GAEAqqC,IAAAliC,MAIAgB,GAAAnJ,EAAAqmB,KACArmB,IAAAyO,EAAAzG,SACAhI,IAAAyO,EAAAa,WACAnG,EAAAnJ,IAAA6U,SAAA/M,WAEA4/B,EAAA/W,cAAAliB,EAAAO,IAAAhP,GAIA,QAAAsqC,GAAAf,EAAAC,EAAAxoB,EAAAupB,EAAAhZ,EAAA+X,GACA,KAAUiB,GAAAhZ,IAAoBgZ,EAC9BlB,EAAAroB,EAAAupB,GAAAjB,EAAAC,EAAAC,GAAA,EAAAxoB,EAAAupB,GAIA,QAAAC,GAAA/7B,GACA,GAAAzO,GAAAsD,EACAsL,EAAAH,EAAAG,IACA,IAAAzF,EAAAyF,GAEA,IADAzF,EAAAnJ,EAAA4O,EAAA7G,OAAAoB,EAAAnJ,IAAAkkC,UAAyDlkC,EAAAyO,GACzDzO,EAAA,EAAiBA,EAAA6lC,EAAA3B,QAAAphC,SAAwB9C,EAAO6lC,EAAA3B,QAAAlkC,GAAAyO,EAEhD,IAAAtF,EAAAnJ,EAAAyO,EAAAI,UACA,IAAAvL,EAAA,EAAiBA,EAAAmL,EAAAI,SAAA/L,SAA2BQ,EAC5CknC,EAAA/7B,EAAAI,SAAAvL,IAKA,QAAAmnC,GAAAlB,EAAAvoB,EAAAupB,EAAAhZ,GACA,KAAUgZ,GAAAhZ,IAAoBgZ,EAAA,CAC9B,GAAAG,GAAA1pB,EAAAupB,EACAphC,GAAAuhC,KACAvhC,EAAAuhC,EAAA/7B,MACAg8B,EAAAD,GACAF,EAAAE,IAEAtB,EAAAsB,EAAA17B,OAMA,QAAA27B,GAAAl8B,EAAAquB,GACA,GAAA3zB,EAAA2zB,IAAA3zB,EAAAsF,EAAAG,MAAA,CACA,GAAA5O,GACAwgB,EAAAqlB,EAAA3hC,OAAApB,OAAA,CAaA,KAZAqG,EAAA2zB,GAGAA,EAAAtc,aAGAsc,EAAAoM,EAAAz6B,EAAAO,IAAAwR,GAGArX,EAAAnJ,EAAAyO,EAAAkgB,oBAAAxlB,EAAAnJ,IAAAyjB,SAAAta,EAAAnJ,EAAA4O,OACA+7B,EAAA3qC,EAAA88B,GAEA98B,EAAA,EAAiBA,EAAA6lC,EAAA3hC,OAAApB,SAAuB9C,EACxC6lC,EAAA3hC,OAAAlE,GAAAyO,EAAAquB,EAEA3zB,GAAAnJ,EAAAyO,EAAAG,KAAA7G,OAAAoB,EAAAnJ,IAAAkE,QACAlE,EAAAyO,EAAAquB,GAEAA,QAGAsM,GAAA36B,EAAAO,KAIA,QAAA47B,GAAArB,EAAAsB,EAAAC,EAAAxB,EAAAyB,GAoBA,IAnBA,GAQAC,GAAAC,EAAAC,EAAA1B,EARA2B,EAAA,EACAC,EAAA,EACAC,EAAAR,EAAA/nC,OAAA,EACAwoC,EAAAT,EAAA,GACAU,EAAAV,EAAAQ,GACAG,EAAAV,EAAAhoC,OAAA,EACA2oC,EAAAX,EAAA,GACAY,EAAAZ,EAAAU,GAMAG,GAAAZ,EAMAI,GAAAE,GAAAD,GAAAI,GACAxiC,EAAAsiC,GACAA,EAAAT,IAAAM,GACOniC,EAAAuiC,GACPA,EAAAV,IAAAQ,GACOra,GAAAsa,EAAAG,IACPG,EAAAN,EAAAG,EAAAnC,EAAAwB,EAAAM,GACAE,EAAAT,IAAAM,GACAM,EAAAX,IAAAM,IACOpa,GAAAua,EAAAG,IACPE,EAAAL,EAAAG,EAAApC,EAAAwB,EAAAU,GACAD,EAAAV,IAAAQ,GACAK,EAAAZ,IAAAU,IACOxa,GAAAsa,EAAAI,IACPE,EAAAN,EAAAI,EAAApC,EAAAwB,EAAAU,GACAG,GAAAjE,EAAAhiC,aAAA6jC,EAAA+B,EAAAt8B,IAAA04B,EAAAlX,YAAA+a,EAAAv8B,MACAs8B,EAAAT,IAAAM,GACAO,EAAAZ,IAAAU,IACOxa,GAAAua,EAAAE,IACPG,EAAAL,EAAAE,EAAAnC,EAAAwB,EAAAM,GACAO,GAAAjE,EAAAhiC,aAAA6jC,EAAAgC,EAAAv8B,IAAAs8B,EAAAt8B,KACAu8B,EAAAV,IAAAQ,GACAI,EAAAX,IAAAM,KAEApiC,EAAAgiC,KAAmCA,EAAA3Z,GAAAwZ,EAAAM,EAAAE,IACnCJ,EAAA9hC,EAAAsiC,EAAAvgC,KACA8/B,EAAAS,EAAAvgC,KACA2gC,EAAAJ,EAAAZ,EAAAM,EAAAE,GACAriC,EAAAiiC,GACA5B,EAAAoC,EAAAnC,EAAAC,EAAA+B,EAAAt8B,KAAA,EAAA87B,EAAAM,IAEAF,EAAAL,EAAAI,GACAja,GAAAka,EAAAO,IACAG,EAAAV,EAAAO,EAAAnC,EAAAwB,EAAAM,GACAP,EAAAI,OAAA/hC,GACAyiC,GAAAjE,EAAAhiC,aAAA6jC,EAAA2B,EAAAl8B,IAAAs8B,EAAAt8B,MAGAq6B,EAAAoC,EAAAnC,EAAAC,EAAA+B,EAAAt8B,KAAA,EAAA87B,EAAAM,IAGAK,EAAAX,IAAAM,GAGAD,GAAAE,GACA7B,EAAAxgC,EAAA8hC,EAAAU,EAAA,SAAAV,EAAAU,EAAA,GAAAx8B,IACAs7B,EAAAf,EAAAC,EAAAsB,EAAAM,EAAAI,EAAAlC,IACK8B,EAAAI,GACLf,EAAAlB,EAAAsB,EAAAM,EAAAE,GAsBA,QAAAQ,GAAAtyB,EAAAsxB,EAAA9+B,EAAAotB,GACA,OAAAn5B,GAAA+L,EAAuB/L,EAAAm5B,EAASn5B,IAAA,CAChC,GAAAI,GAAAyqC,EAAA7qC,EACA,IAAAmJ,EAAA/I,IAAA4wB,GAAAzX,EAAAnZ,GAA2C,MAAAJ,IAI3C,QAAA4rC,GACAna,EACAhjB,EACA66B,EACAI,EACAvkC,EACA4lC,GAEA,GAAAtZ,IAAAhjB,EAAA,CAIAtF,EAAAsF,EAAAO,MAAA7F,EAAAugC,KAEAj7B,EAAAi7B,EAAAvkC,GAAAqJ,EAAAC,GAGA,IAAAO,GAAAP,EAAAO,IAAAyiB,EAAAziB,GAEA,IAAA5F,EAAAqoB,EAAAjM,oBAMA,YALArc,EAAAsF,EAAAS,aAAAkV,UACA0nB,EAAAra,EAAAziB,IAAAP,EAAA66B,GAEA76B,EAAA+W,oBAAA,EASA,IAAApc,EAAAqF,EAAAW,WACAhG,EAAAqoB,EAAAriB,WACAX,EAAAvD,MAAAumB,EAAAvmB,MACA9B,EAAAqF,EAAAiB,WAAAtG,EAAAqF,EAAA0P,SAGA,YADA1P,EAAAkgB,kBAAA8C,EAAA9C,kBAIA,IAAA3uB,GACA4O,EAAAH,EAAAG,IACAzF,GAAAyF,IAAAzF,EAAAnJ,EAAA4O,EAAA7G,OAAAoB,EAAAnJ,IAAA+jC,WACA/jC,EAAAyxB,EAAAhjB,EAGA,IAAAo8B,GAAApZ,EAAA5iB,SACA67B,EAAAj8B,EAAAI,QACA,IAAA1F,EAAAyF,IAAAs7B,EAAAz7B,GAAA,CACA,IAAAzO,EAAA,EAAiBA,EAAA6lC,EAAA5hC,OAAAnB,SAAuB9C,EAAO6lC,EAAA5hC,OAAAjE,GAAAyxB,EAAAhjB,EAC/CtF,GAAAnJ,EAAA4O,EAAA7G,OAAAoB,EAAAnJ,IAAAiE,SAAwDjE,EAAAyxB,EAAAhjB,GAExDzF,EAAAyF,EAAAM,MACA5F,EAAA0hC,IAAA1hC,EAAAuhC,GACAG,IAAAH,GAA2BE,EAAA57B,EAAA67B,EAAAH,EAAApB,EAAAyB,GACpB5hC,EAAAuhC,IAIPvhC,EAAAsoB,EAAA1iB,OAAmC24B,EAAAjX,eAAAzhB,EAAA,IACnCs7B,EAAAt7B,EAAA,KAAA07B,EAAA,EAAAA,EAAA5nC,OAAA,EAAAwmC,IACOngC,EAAA0hC,GACPJ,EAAAz7B,EAAA67B,EAAA,EAAAA,EAAA/nC,OAAA,GACOqG,EAAAsoB,EAAA1iB,OACP24B,EAAAjX,eAAAzhB,EAAA,IAEKyiB,EAAA1iB,OAAAN,EAAAM,MACL24B,EAAAjX,eAAAzhB,EAAAP,EAAAM,MAEA5F,EAAAyF,IACAzF,EAAAnJ,EAAA4O,EAAA7G,OAAAoB,EAAAnJ,IAAA+rC,YAA2D/rC,EAAAyxB,EAAAhjB,IAI3D,QAAAu9B,GAAAv9B,EAAAka,EAAAsjB,GAGA,GAAA7iC,EAAA6iC,IAAA9iC,EAAAsF,EAAAtG,QACAsG,EAAAtG,OAAAyG,KAAAq7B,cAAAthB,MAEA,QAAA3oB,GAAA,EAAqBA,EAAA2oB,EAAA7lB,SAAkB9C,EACvC2oB,EAAA3oB,GAAA4O,KAAA7G,KAAAk8B,OAAAtb,EAAA3oB,IAaA,QAAA8rC,GAAA98B,EAAAP,EAAA66B,EAAA4C,GACA,GAAAlsC,GACA2O,EAAAF,EAAAE,IACAC,EAAAH,EAAAG,KACAC,EAAAJ,EAAAI,QAIA,IAHAq9B,KAAAt9B,KAAAsU,IACAzU,EAAAO,MAEA5F,EAAAqF,EAAAY,YAAAlG,EAAAsF,EAAAS,cAEA,MADAT,GAAA+W,oBAAA,GACA,CAQA,IAAArc,EAAAyF,KACAzF,EAAAnJ,EAAA4O,EAAA7G,OAAAoB,EAAAnJ,IAAA4jC,OAAsD5jC,EAAAyO,GAAA,GACtDtF,EAAAnJ,EAAAyO,EAAAkgB,oBAGA,MADAob,GAAAt7B,EAAA66B,IACA,CAGA,IAAAngC,EAAAwF,GAAA,CACA,GAAAxF,EAAA0F,GAEA,GAAAG,EAAAm9B,gBAIA,GAAAhjC,EAAAnJ,EAAA4O,IAAAzF,EAAAnJ,IAAAwd,WAAArU,EAAAnJ,IAAAk2B,YACA,GAAAl2B,IAAAgP,EAAAknB,UAWA,aAEW,CAIX,OAFAkW,IAAA,EACAtd,EAAA9f,EAAAlJ,WACA8/B,EAAA,EAA6BA,EAAA/2B,EAAA/L,OAAuB8iC,IAAA,CACpD,IAAA9W,IAAAgd,EAAAhd,EAAAjgB,EAAA+2B,GAAA0D,EAAA4C,GAAA,CACAE,GAAA,CACA,OAEAtd,IAAA0B,YAIA,IAAA4b,GAAAtd,EAUA,aAxCA8a,GAAAn7B,EAAAI,EAAAy6B,EA6CA,IAAAngC,EAAAyF,GAAA,CACA,GAAAy9B,IAAA,CACA,QAAAnhC,KAAA0D,GACA,IAAA09B,EAAAphC,GAAA,CACAmhC,GAAA,EACAxC,EAAAp7B,EAAA66B,EACA,QAGA+C,GAAAz9B,EAAA,OAEAqI,GAAArI,EAAA,YAGKI,GAAAJ,OAAAH,EAAAM,OACLC,EAAAJ,KAAAH,EAAAM,KAEA,UAxlBA,GAAA/O,GAAAsD,EACAuiC,KAEAnmC,EAAAspC,EAAAtpC,QACAgoC,EAAAsB,EAAAtB,OAEA,KAAA1nC,EAAA,EAAaA,EAAAuS,GAAAzP,SAAkB9C,EAE/B,IADA6lC,EAAAtzB,GAAAvS,OACAsD,EAAA,EAAeA,EAAA5D,EAAAoD,SAAoBQ,EACnC6F,EAAAzJ,EAAA4D,GAAAiP,GAAAvS,MACA6lC,EAAAtzB,GAAAvS,IAAAgD,KAAAtD,EAAA4D,GAAAiP,GAAAvS,IA2CA,IAocAssC,GAAA7hC,EAAA,0CA6GA,iBAAAgnB,EAAAhjB,EAAAyY,EAAA6jB,GACA,GAAA/hC,EAAAyF,GAEA,YADAtF,EAAAsoB,IAA4B+Y,EAAA/Y,GAI5B,IAAA8a,IAAA,EACAjD,IAEA,IAAAtgC,EAAAyoB,GAEA8a,GAAA,EACAlD,EAAA56B,EAAA66B,OACK,CACL,GAAAkD,GAAArjC,EAAAsoB,EAAAmJ,SACA,KAAA4R,GAAAxb,GAAAS,EAAAhjB,GAEAm9B,EAAAna,EAAAhjB,EAAA66B,EAAA,UAAAyB,OACO,CACP,GAAAyB,EAAA,CAQA,GAJA,IAAA/a,EAAAmJ,UAAAnJ,EAAAgb,aAAAhM,MACAhP,EAAAiC,gBAAA+M,IACAvZ,GAAA,GAEA9d,EAAA8d,IACA4kB,EAAAra,EAAAhjB,EAAA66B,GAEA,MADA0C,GAAAv9B,EAAA66B,GAAA,GACA7X,CAaAA,GAAAwX,EAAAxX,GAIA,GAAAib,GAAAjb,EAAAziB,IACAu6B,EAAA7B,EAAAnjC,WAAAmoC,EAcA,IAXArD,EACA56B,EACA66B,EAIAoD,EAAAlS,SAAA,KAAA+O,EACA7B,EAAAlX,YAAAkc,IAIAvjC,EAAAsF,EAAAtG,QAGA,IAFA,GAAAkiC,GAAA57B,EAAAtG,OACAwkC,EAAAzC,EAAAz7B,GACA47B,GAAA,CACA,OAAArqC,GAAA,EAA2BA,EAAA6lC,EAAA3B,QAAAphC,SAAwB9C,EACnD6lC,EAAA3B,QAAAlkC,GAAAqqC,EAGA,IADAA,EAAAr7B,IAAAP,EAAAO,IACA29B,EAAA,CACA,OAAA/G,GAAA,EAA+BA,EAAAC,EAAAj7B,OAAA9H,SAAyB8iC,EACxDC,EAAAj7B,OAAAg7B,GAAA/T,GAAAwY,EAKA,IAAApG,GAAAoG,EAAAz7B,KAAA7G,KAAAk8B,MACA,IAAAA,EAAArrB,OAEA,OAAAg0B,GAAA,EAAiCA,EAAA3I,EAAAtsB,IAAA7U,OAAyB8pC,IAC1D3I,EAAAtsB,IAAAi1B,SAIAhc,IAAAyZ,EAEAA,KAAAliC,OAKAgB,EAAAogC,GACAkB,EAAAlB,GAAA9X,GAAA,KACStoB,EAAAsoB,EAAA9iB,MACT67B,EAAA/Y,IAMA,MADAua,GAAAv9B,EAAA66B,EAAAiD,GACA99B,EAAAO,OAquCiC04B,WAAAhoC,YAQjCs0B,KAEArwB,SAAA0wB,iBAAA,6BACA,GAAApN,GAAAtjB,SAAA+yB,aACAzP,MAAA4lB,QACAxO,GAAApX,EAAA,UAKA,IAAA6lB,KACAra,SAAA,SAAAxL,EAAA2W,EAAAnvB,EAAAgjB,GACA,WAAAhjB,EAAAE,KAEA8iB,EAAAziB,MAAAyiB,EAAAziB,IAAA+9B,UACAv0B,GAAA/J,EAAA,uBACAq+B,GAAAta,iBAAAvL,EAAA2W,EAAAnvB,KAGAkvB,GAAA1W,EAAA2W,EAAAnvB,EAAAzG,SAEAif,EAAA8lB,aAAA/qC,IAAA9B,KAAA+mB,EAAArhB,QAAAo4B,MACK,aAAAvvB,EAAAE,KAAAyiB,GAAAnK,EAAApjB,SACLojB,EAAA0P,YAAAiH,EAAAjL,UACAiL,EAAAjL,UAAA6R,OACAvd,EAAAoN,iBAAA,mBAAA8J,IACAlX,EAAAoN,iBAAA,iBAAA+J,IAKAnX,EAAAoN,iBAAA,SAAA+J,IAEApK,KACA/M,EAAA4lB,QAAA,MAMAra,iBAAA,SAAAvL,EAAA2W,EAAAnvB,GACA,cAAAA,EAAAE,IAAA,CACAgvB,GAAA1W,EAAA2W,EAAAnvB,EAAAzG,QAKA,IAAAglC,GAAA/lB,EAAA8lB,UACAE,EAAAhmB,EAAA8lB,aAAA/qC,IAAA9B,KAAA+mB,EAAArhB,QAAAo4B,GACA,IAAAiP,EAAAC,KAAA,SAAA1sC,EAAAR,GAA2C,OAAAuM,EAAA/L,EAAAwsC,EAAAhtC,MAAyC,EAGpFinB,EAAAgJ,SACA2N,EAAA/0B,MAAAqkC,KAAA,SAAAjkC,GAA6C,MAAAi1B,IAAAj1B,EAAAgkC,KAC7CrP,EAAA/0B,QAAA+0B,EAAAxL,UAAA8L,GAAAN,EAAA/0B,MAAAokC,KAEA5O,GAAApX,EAAA,cAsFAwV,IACA33B,KAAA,SAAAmiB,EAAA6J,EAAAriB,GACA,GAAA5F,GAAAioB,EAAAjoB,KAEA4F,GAAAgwB,GAAAhwB,EACA,IAAA0+B,GAAA1+B,EAAAG,MAAAH,EAAAG,KAAA8rB,WACA0S,EAAAnmB,EAAAomB,mBACA,SAAApmB,EAAA3D,MAAAgqB,QAAA,GAAArmB,EAAA3D,MAAAgqB,OACAzkC,IAAAskC,GACA1+B,EAAAG,KAAA6tB,MAAA,EACAnC,GAAA7rB,EAAA,WACAwY,EAAA3D,MAAAgqB,QAAAF,KAGAnmB,EAAA3D,MAAAgqB,QAAAzkC,EAAAukC,EAAA,QAIAnpC,OAAA,SAAAgjB,EAAA6J,EAAAriB,GACA,GAAA5F,GAAAioB,EAAAjoB,OAIAA,IAHAioB,EAAAsB,WAIA3jB,EAAAgwB,GAAAhwB,GACAA,EAAAG,MAAAH,EAAAG,KAAA8rB,YAEAjsB,EAAAG,KAAA6tB,MAAA,EACA5zB,EACAyxB,GAAA7rB,EAAA,WACAwY,EAAA3D,MAAAgqB,QAAArmB,EAAAomB,qBAGAxQ,GAAApuB,EAAA,WACAwY,EAAA3D,MAAAgqB,QAAA,UAIArmB,EAAA3D,MAAAgqB,QAAAzkC,EAAAoe,EAAAomB,mBAAA,SAIAE,OAAA,SACAtmB,EACA2W,EACAnvB,EACAgjB,EACAK,GAEAA,IACA7K,EAAA3D,MAAAgqB,QAAArmB,EAAAomB,sBAKAG,IACAhsB,MAAAsrB,GACArQ,SAKAgR,IACAntC,KAAAwJ,OACAyxB,OAAAv0B,QACA/B,IAAA+B,QACA0mC,KAAA5jC,OACAjG,KAAAiG,OACA+wB,WAAA/wB,OACAmzB,WAAAnzB,OACAgxB,aAAAhxB,OACAqzB,aAAArzB,OACAixB,iBAAAjxB,OACAozB,iBAAApzB,OACAkxB,YAAAlxB,OACAoxB,kBAAApxB,OACAmxB,cAAAnxB,OACA4xB,UAAArB,OAAAvwB,OAAArJ,SAkDAktC,GAAA,SAAAvtC,GAAkC,MAAAA,GAAAuO,KAAA6W,GAAAplB,IAElCwtC,GAAA,SAAAvtC,GAAqC,eAAAA,EAAAC,MAErCutC,IACAvtC,KAAA,aACAoS,MAAA+6B,GACA9rB,UAAA,EAEAja,OAAA,SAAAiB,GACA,GAAAyX,GAAAzd,KAEAkM,EAAAlM,KAAA0Z,OAAA5U,OACA,IAAAoH,IAKAA,IAAA9H,OAAA4mC,IAEA9+B,EAAA/L,QAAA,CAaA,GAAA4qC,GAAA/qC,KAAA+qC,KAYA5O,EAAAjwB,EAAA,EAIA,IAAAkwB,GAAAp8B,KAAAsF,QACA,MAAA62B,EAKA,IAAA1rB,GAAAsrB,GAAAI,EAEA,KAAA1rB,EACA,MAAA0rB,EAGA,IAAAn8B,KAAAmrC,SACA,MAAAjP,IAAAl2B,EAAAm2B,EAMA,IAAA/7B,GAAA,gBAAAJ,KAAA,QACAyQ,GAAAlI,IAAA,MAAAkI,EAAAlI,IACAkI,EAAA/D,UACAtM,EAAA,UACAA,EAAAqQ,EAAAzE,IACArF,EAAA8J,EAAAlI,KACA,IAAApB,OAAAsJ,EAAAlI,KAAAH,QAAAhI,GAAAqQ,EAAAlI,IAAAnI,EAAAqQ,EAAAlI,IACAkI,EAAAlI,GAEA,IAAA0D,IAAAwE,EAAAxE,OAAAwE,EAAAxE,UAA8C8rB,WAAAkE,GAAAj8B,MAC9CorC,EAAAprC,KAAA8gB,OACAwb,EAAAP,GAAAqP,EAQA,IAJA36B,EAAAxE,KAAAqE,YAAAG,EAAAxE,KAAAqE,WAAAi6B,KAAAU,MACAx6B,EAAAxE,KAAA6tB,MAAA,GAIAwC,GACAA,EAAArwB,OACAowB,GAAA5rB,EAAA6rB,KACAzZ,GAAAyZ,MAEAA,EAAAtQ,oBAAAsQ,EAAAtQ,kBAAAlL,OAAApU,WACA,CAGA,GAAAklB,GAAA0K,EAAArwB,KAAA8rB,WAAAzuB,KAAwD2C,EAExD,eAAA8+B,EAOA,MALA/qC,MAAAmrC,UAAA,EACAt1B,GAAA+b,EAAA,wBACAnU,EAAA0tB,UAAA,EACA1tB,EAAA4E,iBAEA6Z,GAAAl2B,EAAAm2B,EACO,eAAA4O,EAAA,CACP,GAAAloB,GAAApS,GACA,MAAA26B,EAEA,IAAAC,GACAjR,EAAA,WAAwCiR,IACxCx1B,IAAA5J,EAAA,aAAAmuB,GACAvkB,GAAA5J,EAAA,iBAAAmuB,GACAvkB,GAAA+b,EAAA,sBAAAsI,GAAgEmR,EAAAnR,KAIhE,MAAAiC,MAMApsB,GAAAzG,GACA0C,IAAA7E,OACAmkC,UAAAnkC,QACC2jC,UAED/6B,IAAAg7B,IAEA,IAAAQ,KACAx7B,SAEAy7B,YAAA,WACA,GAAA/tB,GAAAzd,KAEAsB,EAAAtB,KAAA2kB,OACA3kB,MAAA2kB,QAAA,SAAA7Y,EAAAyY,GACA,GAAA8e,GAAA7f,GAAA/F,EAEAA,GAAA6lB,UACA7lB,EAAAqD,OACArD,EAAAguB,MACA,GACA,GAEAhuB,EAAAqD,OAAArD,EAAAguB,KACApI,IACA/hC,EAAA/D,KAAAkgB,EAAA3R,EAAAyY,KAIAxf,OAAA,SAAAiB,GAQA,OAPAgG,GAAAhM,KAAAgM,KAAAhM,KAAAsF,OAAA2G,KAAAD,KAAA,OACA3M,EAAAvB,OAAAmK,OAAA,MACAyjC,EAAA1rC,KAAA0rC,aAAA1rC,KAAAkM,SACAy/B,EAAA3rC,KAAA0Z,OAAA5U,YACAoH,EAAAlM,KAAAkM,YACA0/B,EAAA3P,GAAAj8B,MAEA3C,EAAA,EAAmBA,EAAAsuC,EAAAxrC,OAAwB9C,IAAA,CAC3C,GAAAI,GAAAkuC,EAAAtuC,EACA,IAAAI,EAAAuO,IACA,SAAAvO,EAAA8K,KAAA,IAAApB,OAAA1J,EAAA8K,KAAAH,QAAA,WACA8D,EAAA7L,KAAA5C,GACA4B,EAAA5B,EAAA8K,KAAA9K,GACWA,EAAAwO,OAAAxO,EAAAwO,UAAuB8rB,WAAA6T,QASlC,GAAAF,EAAA,CAGA,OAFAD,MACAI,KACA5I,EAAA,EAAuBA,EAAAyI,EAAAvrC,OAA2B8iC,IAAA,CAClD,GAAA6I,GAAAJ,EAAAzI,EACA6I,GAAA7/B,KAAA8rB,WAAA6T,EACAE,EAAA7/B,KAAA6wB,IAAAgP,EAAAz/B,IAAAswB,wBACAt9B,EAAAysC,EAAAvjC,KACAkjC,EAAAprC,KAAAyrC,GAEAD,EAAAxrC,KAAAyrC,GAGA9rC,KAAAyrC,KAAAzlC,EAAAgG,EAAA,KAAAy/B,GACAzrC,KAAA6rC,UAGA,MAAA7lC,GAAAgG,EAAA,KAAAE,IAGA6/B,QAAA,WACA,GAAA7/B,GAAAlM,KAAA0rC,aACAJ,EAAAtrC,KAAAsrC,YAAAtrC,KAAArC,MAAA,YACAuO,GAAA/L,QAAAH,KAAAgsC,QAAA9/B,EAAA,GAAAG,IAAAi/B,KAMAp/B,EAAAsL,QAAA+kB,IACArwB,EAAAsL,QAAAilB,IACAvwB,EAAAsL,QAAAolB,IAKA58B,KAAAisC,QAAAjrC,SAAAkrC,KAAAC,aAEAjgC,EAAAsL,QAAA,SAAA/Z,GACA,GAAAA,EAAAwO,KAAAkxB,MAAA,CACA,GAAA7Y,GAAA7mB,EAAA4O,IACA3N,EAAA4lB,EAAA3D,KACAkV,IAAAvR,EAAAgnB,GACA5sC,EAAA0+B,UAAA1+B,EAAA2+B,gBAAA3+B,EAAA4+B,mBAAA,GACAhZ,EAAAoN,iBAAA2E,GAAA/R,EAAAkY,QAAA,QAAAvoB,GAAA/J,GACAA,KAAAoB,SAAAgZ,GAGApa,IAAA,aAAAxG,KAAAwG,EAAAkiC,gBACA9nB,EAAAmN,oBAAA4E,GAAApiB,GACAqQ,EAAAkY,QAAA,KACAzG,GAAAzR,EAAAgnB,WAOA9jB,SACAwkB,QAAA,SAAA1nB,EAAAgnB,GAEA,IAAA1F,GACA,QAGA,IAAA5lC,KAAAqsC,SACA,MAAArsC,MAAAqsC,QAOA,IAAA/tB,GAAAgG,EAAAgoB,WACAhoB,GAAAyN,oBACAzN,EAAAyN,mBAAAva,QAAA,SAAAqa,GAAsDyD,GAAAhX,EAAAuT,KAEtDqD,GAAA5W,EAAAgtB,GACAhtB,EAAAqC,MAAAgqB,QAAA,OACA3qC,KAAAwkB,IAAApjB,YAAAkd,EACA,IAAA3L,GAAAujB,GAAA5X,EAEA,OADAte,MAAAwkB,IAAA3iB,YAAAyc,GACAte,KAAAqsC,SAAA15B,EAAA0kB,gBAiCAkV,IACArB,cACAK,mBAMAvhB,IAAA3W,OAAAuH,eACAoP,GAAA3W,OAAAgN,iBACA2J,GAAA3W,OAAAirB,kBACAtU,GAAA3W,OAAA+M,mBACA4J,GAAA3W,OAAAwZ,oBAGAvjB,EAAA0gB,GAAA/mB,QAAAqN,WAAAu6B,IACAvhC,EAAA0gB,GAAA/mB,QAAA0mB,WAAA4iB,IAGAviB,GAAAzrB,UAAA+kC,UAAA9vB,GAAA4yB,GAAAzkC,EAGAqoB,GAAAzrB,UAAA8iC,OAAA,SACA/c,EACAC,GAGA,MADAD,MAAA9Q,GAAA0Z,GAAA5I,OAAA/d,GACA8d,GAAArkB,KAAAskB,EAAAC,IAKA/Q,IACAmP,WAAA,WACAtP,GAAAyT,UACAA,IACAA,GAAAC,KAAA,OAAAiD,KAsBG,GAKY/jB,EAAA,YL4dc1I,KAAK0I,EAAqBjJ,EAAoB,GAAIA,EAAoB,IAAIoJ,eAIjG,SAAUhJ,EAAQD,GM5tRxB,GAAAqvC,EAGAA,GAAA,WACA,MAAAxsC,QAGA,KAEAwsC,KAAA5O,SAAA,qBAAA6O,MAAA,QACC,MAAAviC,GAED,gBAAA6iB,UACAyf,EAAAzf,QAOA3vB,EAAAD,QAAAqvC,GNmuRM,SAAUpvC,EAAQ6I,EAAqBjJ,GAE7C,YOzvRA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAA2vC,GAAA3vC,EAAA,IAAA4vC,EAAA5vC,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACEF,EAAA,EACAC,EAAA,GATF,EAWAE,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,SPkwR1B,SAAU3vC,EAAQ6I,EAAqBjJ,GAE7C,YACqB,IAAIgwC,GAAuDhwC,EAAoB,IAC3EiwC,EAAwDjwC,EAAoB,IAC5EkwC,EAA4DlwC,EAAoB,IAChFmwC,EAA6DnwC,EAAoB,IACjFowC,EAAmEpwC,EAAoB,IACvFqwC,EAAiErwC,EAAoB,IACrFswC,EAAuEtwC,EAAoB,IAC3FuwC,EAA+DvwC,EAAoB,GQrwR5GiJ,GAAA,GACAtI,KAAA,MACAgsB,YACA6jB,aAAAR,EAAA,EACAS,cAAAR,EAAA,EACAS,cAAAR,EAAA,EACAS,eAAAR,EAAA,EACAS,qBAAAR,EAAA,EACAS,mBAAAR,EAAA,EACAS,yBAAAR,EAAA,EACAS,qBAAAR,EAAA,GAEA3lB,UACAomB,KADA,WAEA,MAAAhuC,MAAAiuC,OAAAC,MAAAF,SR8yRM,SAAU5wC,EAAQ6I,EAAqBjJ,GAE7C,YSz0RAiJ,GAAA,GACA2hB,UACAumB,YADA,WAEA,MAAAnuC,MAAAiuC,OAAAC,MAAAE,YAGA5mB,SACA6mB,QADA,WAEAruC,KAAAiuC,OAAAK,SAAA,YAEAC,WAJA,WAKAvuC,KAAAiuC,OAAAK,SAAA,kBTu2RM,SAAUlxC,EAAQ6I,EAAqBjJ,GAE7C,YUz3RAiJ,GAAA,GACAuhB,SACAgnB,QADA,SACA7wC,GACAqC,KAAAiuC,OAAAQ,OAAA,UAAA9wC,KAGAiqB,UACAomB,KADA,WAEA,MAAAhuC,MAAAiuC,OAAAC,MAAAF,SVk5RM,SAAU5wC,EAAQ6I,EAAqBjJ,GAE7C,YWr2RAiJ,GAAA,GACA2hB,UACA8mB,SADA,WAEA,OAAA1uC,KAAAiuC,OAAAC,MAAAE,YAGA5mB,SACAmnB,YADA,SACAC,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,iCAEAtuC,KAAAiuC,OAAAK,SAAA,mCAGAO,wBARA,SAQAD,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,4CAEAtuC,KAAAiuC,OAAAK,SAAA,8CAGAQ,wBAfA,SAeAF,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,4CAEAtuC,KAAAiuC,OAAAK,SAAA,8CAGAS,sBAtBA,SAsBAH,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,0CAEAtuC,KAAAiuC,OAAAK,SAAA,4CAGAU,kBA7BA,SA6BAJ,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,yCAEAtuC,KAAAiuC,OAAAK,SAAA,2CAGAW,oBApCA,SAoCAL,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,2CAEAtuC,KAAAiuC,OAAAK,SAAA,6CAGAY,cA3CA,SA2CAN,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,oCAEAtuC,KAAAiuC,OAAAK,SAAA,sCAGAa,aAlDA,SAkDAP,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,mCAEAtuC,KAAAiuC,OAAAK,SAAA,qCAGAc,aAzDA,SAyDAR,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,mCAEAtuC,KAAAiuC,OAAAK,SAAA,qCAGAe,aAhEA,SAgEAT,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,mCAEAtuC,KAAAiuC,OAAAK,SAAA,qCAGAgB,iBAvEA,SAuEAV,GACAA,EACA5uC,KAAAiuC,OAAAK,SAAA,uCAEAtuC,KAAAiuC,OAAAK,SAAA,4CXs7RM,SAAUlxC,EAAQ6I,EAAqBjJ,GAE7C,YY/jSAiJ,GAAA,GACAuhB,SACA+nB,MAAA,WACAvvC,KAAAiuC,OAAAK,SAAA,gCAGA1mB,UACA8mB,SADA,WAEA,OAAA1uC,KAAAiuC,OAAAC,MAAAE,WAEAoB,OAJA,WAKA,MAAAxvC,MAAAiuC,OAAAC,MAAAsB,WZylSM,SAAUpyC,EAAQ6I,EAAqBjJ,GAE7C,YACqB,IAAIyyC,GAA8CzyC,EAAoB,Ea1mS3FiJ,GAAA,GACA0jB,YACA+lB,eAAAD,EAAA,GAEAjoB,SACA+nB,MAAA,WACAvvC,KAAAiuC,OAAAK,SAAA,sCAGA1mB,UACA8mB,SADA,WAEA,OAAA1uC,KAAAiuC,OAAAC,MAAAE,WAEAuB,aAJA,WAKA,MAAA3vC,MAAAiuC,OAAAC,MAAAyB,iBbioSM,SAAUvyC,EAAQ6I,EAAqBjJ,GAE7C,YcnpSAiJ,GAAA,GACAtI,KAAA,WACAoS,OACA,SAEA9D,KAAA,WACA,OACA2jC,MAAA,IAGAhoB,UACAioB,SAAA,WACA,MAAA7vC,MAAA6e,MAAA3S,UAAAlM,KAAA6e,MAAA3S,SAAA/L,SAGAqnB,SACAsoB,OAAA,WACA9vC,KAAA6vC,WACA7vC,KAAA4vC,MAAA5vC,KAAA4vC,UduqSM,SAAUxyC,EAAQ6I,EAAqBjJ,GAE7C,YACqB,IAAIyyC,GAA8CzyC,EAAoB,Ee1rS3FiJ,GAAA,GACA0jB,YACA+lB,eAAAD,EAAA,GAEAjoB,SACA+nB,MAAA,WACAvvC,KAAAiuC,OAAAK,SAAA,mCAGA1mB,UACA8mB,SADA,WAEA,OAAA1uC,KAAAiuC,OAAAC,MAAAE,WAEA2B,UAJA,WAKA,MAAA/vC,MAAAiuC,OAAAC,MAAA6B,cfitSM,SAAU3yC,EAAQ6I,EAAqBjJ,GAE7C,YACqB,IAAIyyC,GAA8CzyC,EAAoB,EgBluS3FiJ,GAAA,GACA0jB,YACA+lB,eAAAD,EAAA,GAEAjoB,SACA+nB,MAAA,WACAvvC,KAAAiuC,OAAAK,SAAA,0CAGA1mB,UACA8mB,SADA,WAEA,OAAA1uC,KAAAiuC,OAAAC,MAAAE,WAEA4B,iBAJA,WAKA,MAAAhwC,MAAAiuC,OAAAC,MAAA8B,qBhByvSM,SAAU5yC,EAAQ6I,EAAqBjJ,GAE7C,YiB3wSAiJ,GAAA,GACA2hB,UACA8mB,SADA,WAEA,OAAA1uC,KAAAiuC,OAAAC,MAAAE,WAEA6B,WAJA,WAKA,MAAAjwC,MAAAiuC,OAAAC,MAAA+B,aAGAzoB,SACA+nB,MAAA,WACAvvC,KAAAiuC,OAAAK,SAAA,sCjBgySM,SAAUlxC,EAAQ6I,EAAqBjJ,GAE7C,YkB1zSAc,QAAAC,eAAAkI,EAAA,cAAAC,OAAA,OAAAgqC,GAAAlzC,EAAA,GAAAmzC,EAAAnzC,EAAA,IAAAozC,EAAApzC,EAAAmB,EAAAgyC,GAAAE,EAAArzC,EAAA,IAAAszC,GAAAtzC,EAAAmB,EAAAkyC,GAAArzC,EAAA,KAAAuzC,GAAAvzC,EAAAmB,EAAAmyC,GAAAtzC,EAAA,KAAAwzC,EAAAxzC,EAAA,GAOAgtB,WAAIG,IAAIsmB,KAER,GAAIzmB,YACA1F,GAAI,OACJosB,UACA3rC,OAAQ,SAAAiB,GAAA,MAAKA,GAAE2qC,SlB40Sb,SAAUvzC,EAAQD,EAASH,ImBx1SjC,SAAAmJ,GAoBA,QAAAyqC,GAAAxwC,EAAAywC,GACA7wC,KAAA8wC,IAAA1wC,EACAJ,KAAA+wC,SAAAF,EAtBA,GAAAG,OAAA,KAAA7qC,MACA,mBAAA8qC,aACAlkB,OACA/jB,EAAA40B,SAAAr/B,UAAAyK,KAIA7L,GAAAwlB,WAAA,WACA,UAAAiuB,GAAA5nC,EAAAzL,KAAAolB,WAAAquB,EAAAjoC,WAAAuZ,eAEAnlB,EAAA+zC,YAAA,WACA,UAAAN,GAAA5nC,EAAAzL,KAAA2zC,YAAAF,EAAAjoC,WAAAooC,gBAEAh0C,EAAAmlB,aACAnlB,EAAAg0C,cAAA,SAAAvuB,GACAA,GACAA,EAAAwuB,SAQAR,EAAAryC,UAAA8yC,MAAAT,EAAAryC,UAAA4vB,IAAA,aACAyiB,EAAAryC,UAAA6yC,MAAA,WACApxC,KAAA+wC,SAAAxzC,KAAAyzC,EAAAhxC,KAAA8wC,MAIA3zC,EAAAm0C,OAAA,SAAA1yC,EAAA2yC,GACAjvB,aAAA1jB,EAAA4yC,gBACA5yC,EAAA6yC,aAAAF,GAGAp0C,EAAAu0C,SAAA,SAAA9yC,GACA0jB,aAAA1jB,EAAA4yC,gBACA5yC,EAAA6yC,cAAA,GAGAt0C,EAAAw0C,aAAAx0C,EAAA2kC,OAAA,SAAAljC,GACA0jB,aAAA1jB,EAAA4yC,eAEA,IAAAD,GAAA3yC,EAAA6yC,YACAF,IAAA,IACA3yC,EAAA4yC,eAAA7uB,WAAA,WACA/jB,EAAAgzC,YACAhzC,EAAAgzC,cACKL,KAKLv0C,EAAQ,IAIRG,EAAAiJ,aAAA,mBAAA6qC,YAAA7qC,kBACA,KAAAD,KAAAC,cACApG,WAAAoG,aACAjJ,EAAA00C,eAAA,mBAAAZ,YAAAY,oBACA,KAAA1rC,KAAA0rC,gBACA7xC,WAAA6xC,iBnB41S6Bt0C,KAAKJ,EAASH,EAAoB,KAIzD,SAAUI,EAAQD,EAASH,IoB95SjC,SAAAmJ,EAAA2rC,IAAA,SAAA3rC,EAAAI,GACA,YAYA,SAAAH,GAAAyZ,GAEA,kBAAAA,KACAA,EAAA,GAAA+d,UAAA,GAAA/d,GAIA,QADA1M,GAAA,GAAAzL,OAAAqB,UAAA5I,OAAA,GACA9C,EAAA,EAAqBA,EAAA8V,EAAAhT,OAAiB9C,IACtC8V,EAAA9V,GAAA0L,UAAA1L,EAAA,EAGA,IAAA00C,IAAkBlyB,WAAA1M,OAGlB,OAFA6+B,GAAAC,GAAAF,EACAG,EAAAD,GACAA,IAGA,QAAAJ,GAAAM,SACAH,GAAAG,GAGA,QAAA1rB,GAAAsrB,GACA,GAAAlyB,GAAAkyB,EAAAlyB,SACA1M,EAAA4+B,EAAA5+B,IACA,QAAAA,EAAAhT,QACA,OACA0f,GACA,MACA,QACAA,EAAA1M,EAAA,GACA,MACA,QACA0M,EAAA1M,EAAA,GAAAA,EAAA,GACA,MACA,QACA0M,EAAA1M,EAAA,GAAAA,EAAA,GAAAA,EAAA,GACA,MACA,SACA0M,EAAA7W,MAAAzC,EAAA4M,IAKA,QAAAi/B,GAAAD,GAGA,GAAAE,EAGA1vB,WAAAyvB,EAAA,EAAAD,OACS,CACT,GAAAJ,GAAAC,EAAAG,EACA,IAAAJ,EAAA,CACAM,GAAA,CACA,KACA5rB,EAAAsrB,GACiB,QACjBF,EAAAM,GACAE,GAAA,KApEA,IAAAlsC,EAAAC,aAAA,CAIA,GAIA8rC,GAJAD,EAAA,EACAD,KACAK,GAAA,EACAC,EAAAnsC,EAAAnF,SAoJAuxC,EAAAz0C,OAAA00C,gBAAA10C,OAAA00C,eAAArsC,EACAosC,QAAA5vB,WAAA4vB,EAAApsC,EAGU,wBAAApG,SAAAxC,KAAA4I,EAAA2rC,SArFV,WACAI,EAAA,SAAAC,GACAL,EAAA99B,SAAA,WAA0Co+B,EAAAD,SAI1C,WAGA,GAAAhsC,EAAAssC,cAAAtsC,EAAAusC,cAAA,CACA,GAAAC,IAAA,EACAC,EAAAzsC,EAAA0sC,SAMA,OALA1sC,GAAA0sC,UAAA,WACAF,GAAA,GAEAxsC,EAAAssC,YAAA,QACAtsC,EAAA0sC,UAAAD,EACAD,MAIA,WAKA,GAAAG,GAAA,gBAAA1rC,KAAA2rC,SAAA,IACAC,EAAA,SAAAv9B,GACAA,EAAAnW,SAAA6G,GACA,gBAAAsP,GAAAxJ,MACA,IAAAwJ,EAAAxJ,KAAA7D,QAAA0qC,IACAV,GAAA38B,EAAAxJ,KAAAE,MAAA2mC,EAAA3yC,SAIAgG,GAAAurB,iBACAvrB,EAAAurB,iBAAA,UAAAshB,GAAA,GAEA7sC,EAAA8sC,YAAA,YAAAD,GAGAd,EAAA,SAAAC,GACAhsC,EAAAssC,YAAAK,EAAAX,EAAA,SAmDKhsC,EAAA+sC,eA/CL,WACA,GAAAC,GAAA,GAAAD,eACAC,GAAAC,MAAAP,UAAA,SAAAp9B,GAEA28B,EADA38B,EAAAxJ,OAIAimC,EAAA,SAAAC,GACAgB,EAAAE,MAAAZ,YAAAN,OA2CKG,GAAA,sBAAAA,GAAArxC,cAAA,UAvCL,WACA,GAAAqyC,GAAAhB,EAAAiB,eACArB,GAAA,SAAAC,GAGA,GAAAqB,GAAAlB,EAAArxC,cAAA,SACAuyC,GAAAC,mBAAA,WACArB,EAAAD,GACAqB,EAAAC,mBAAA,KACAH,EAAAzxC,YAAA2xC,GACAA,EAAA,MAEAF,EAAAlyC,YAAAoyC,OAIA,WACAtB,EAAA,SAAAC,GACAxvB,WAAAyvB,EAAA,EAAAD,OA8BAI,EAAAnsC,eACAmsC,EAAAV,mBACC,mBAAAZ,UAAA,KAAA9qC,EAAAnG,KAAAmG,EAAA8qC,QpBk6S4B1zC,KAAKJ,EAASH,EAAoB,GAAIA,EAAoB,MAIjF,SAAUI,EAAQD,GqBplTxB,QAAAu2C,KACA,SAAApwC,OAAA,mCAEA,QAAAqwC,KACA,SAAArwC,OAAA,qCAsBA,QAAAswC,GAAAC,GACA,GAAAC,IAAAnxB,WAEA,MAAAA,YAAAkxB,EAAA,EAGA,KAAAC,IAAAJ,IAAAI,IAAAnxB,WAEA,MADAmxB,GAAAnxB,WACAA,WAAAkxB,EAAA,EAEA,KAEA,MAAAC,GAAAD,EAAA,GACK,MAAA3pC,GACL,IAEA,MAAA4pC,GAAAv2C,KAAA,KAAAs2C,EAAA,GACS,MAAA3pC,GAET,MAAA4pC,GAAAv2C,KAAAyC,KAAA6zC,EAAA,KAMA,QAAAE,GAAAC,GACA,GAAAC,IAAA3xB,aAEA,MAAAA,cAAA0xB,EAGA,KAAAC,IAAAN,IAAAM,IAAA3xB,aAEA,MADA2xB,GAAA3xB,aACAA,aAAA0xB,EAEA,KAEA,MAAAC,GAAAD,GACK,MAAA9pC,GACL,IAEA,MAAA+pC,GAAA12C,KAAA,KAAAy2C,GACS,MAAA9pC,GAGT,MAAA+pC,GAAA12C,KAAAyC,KAAAg0C,KAYA,QAAAE,KACAC,GAAAC,IAGAD,GAAA,EACAC,EAAAj0C,OACA6lB,EAAAouB,EAAAj1C,OAAA6mB,GAEAquB,GAAA,EAEAruB,EAAA7lB,QACAm0C,KAIA,QAAAA,KACA,IAAAH,EAAA,CAGA,GAAAvxB,GAAAgxB,EAAAM,EACAC,IAAA,CAGA,KADA,GAAA3hC,GAAAwT,EAAA7lB,OACAqS,GAAA,CAGA,IAFA4hC,EAAApuB,EACAA,OACAquB,EAAA7hC,GACA4hC,GACAA,EAAAC,GAAA5tB,KAGA4tB,IAAA,EACA7hC,EAAAwT,EAAA7lB,OAEAi0C,EAAA,KACAD,GAAA,EACAJ,EAAAnxB,IAiBA,QAAA2xB,GAAAV,EAAAW,GACAx0C,KAAA6zC,MACA7zC,KAAAw0C,QAYA,QAAA7yC,MAhKA,GAOAmyC,GACAG,EARAnC,EAAA10C,EAAAD,YAgBA,WACA,IAEA22C,EADA,kBAAAnxB,YACAA,WAEA+wB,EAEK,MAAAxpC,GACL4pC,EAAAJ,EAEA,IAEAO,EADA,kBAAA3xB,cACAA,aAEAqxB,EAEK,MAAAzpC,GACL+pC,EAAAN,KAuDA,IAEAS,GAFApuB,KACAmuB,GAAA,EAEAE,GAAA,CAyCAvC,GAAA99B,SAAA,SAAA6/B,GACA,GAAA1gC,GAAA,GAAAzL,OAAAqB,UAAA5I,OAAA,EACA,IAAA4I,UAAA5I,OAAA,EACA,OAAA9C,GAAA,EAAuBA,EAAA0L,UAAA5I,OAAsB9C,IAC7C8V,EAAA9V,EAAA,GAAA0L,UAAA1L,EAGA2oB,GAAA3lB,KAAA,GAAAk0C,GAAAV,EAAA1gC,IACA,IAAA6S,EAAA7lB,QAAAg0C,GACAP,EAAAU,IASAC,EAAAh2C,UAAAkoB,IAAA,WACAzmB,KAAA6zC,IAAA7qC,MAAA,KAAAhJ,KAAAw0C,QAEA1C,EAAA2C,MAAA,UACA3C,EAAA4C,SAAA,EACA5C,EAAA1S,OACA0S,EAAA6C,QACA7C,EAAAtN,QAAA,GACAsN,EAAA8C,YAIA9C,EAAA18B,GAAAzT,EACAmwC,EAAA+C,YAAAlzC,EACAmwC,EAAArnC,KAAA9I,EACAmwC,EAAAgD,IAAAnzC,EACAmwC,EAAAiD,eAAApzC,EACAmwC,EAAAkD,mBAAArzC,EACAmwC,EAAA/qB,KAAAplB,EACAmwC,EAAAmD,gBAAAtzC,EACAmwC,EAAAoD,oBAAAvzC,EAEAmwC,EAAAj0B,UAAA,SAAAlgB,GAAqC,UAErCm0C,EAAA7W,QAAA,SAAAt9B,GACA,SAAA2F,OAAA,qCAGAwuC,EAAAqD,IAAA,WAA2B,WAC3BrD,EAAAsD,MAAA,SAAApmB,GACA,SAAA1rB,OAAA,mCAEAwuC,EAAAuD,MAAA,WAA4B,WrBsmTtB,SAAUj4C,EAAQD,EAASH;CsB5xTjC,SAAAs4C,EAAA/zB,GAEAnkB,EAAAD,QAAAokB,EAA2BvkB,EAAQ,KAOlC,mBAAAi0C,YAAA,SAAAsE,GACD,gBAAAx4C,GAKA,QAAAC,GAAAC,GAGA,GAAAC,EAAAD,GACA,MAAAC,GAAAD,GAAAE,OAGA,IAAAC,GAAAF,EAAAD,IACAI,EAAAJ,EACAK,GAAA,EACAH,WAUA,OANAJ,GAAAE,GAAAM,KAAAH,EAAAD,QAAAC,IAAAD,QAAAH,GAGAI,EAAAE,GAAA,EAGAF,EAAAD,QAvBA,GAAAD,KA4DA,OAhCAF,GAAAQ,EAAAT,EAGAC,EAAAS,EAAAP,EAGAF,EAAAU,EAAA,SAAAP,EAAAQ,EAAAC,GACAZ,EAAAa,EAAAV,EAAAQ,IACAG,OAAAC,eAAAZ,EAAAQ,GACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,KAMAZ,EAAAmB,EAAA,SAAAf,GACA,GAAAQ,GAAAR,KAAAgB,WACA,WAAmC,MAAAhB,GAAA,SACnC,WAAyC,MAAAA,GAEzC,OADAJ,GAAAU,EAAAE,EAAA,IAAAA,GACAA,GAIAZ,EAAAa,EAAA,SAAAQ,EAAAC,GAA8D,MAAAR,QAAAS,UAAAC,eAAAjB,KAAAc,EAAAC,IAG9DtB,EAAAyB,EAAA,IAGAzB,IAAA0B,EAAA,MAKA,SAAAtB,EAAAD,GAOAC,EAAAD,QAAA,SACAmH,EACAC,EACAE,EACAC,EACAC,GAEA,GAAAC,GACAC,EAAAP,QAGApD,QAAAoD,GAAAQ,OACA,YAAA5D,GAAA,aAAAA,IACA0D,EAAAN,EACAO,EAAAP,EAAAQ,QAIA,IAAA7B,GAAA,kBAAA4B,GACAA,EAAA5B,QACA4B,CAGAN,KACAtB,EAAA8B,OAAAR,EAAAQ,OACA9B,EAAA+B,gBAAAT,EAAAS,iBAIAN,IACAzB,EAAAkC,SAAAT,EAGA,IAAAU,EA4BA,IA3BAT,GACAS,EAAA,SAAAC,GAEAA,EACAA,GACArF,KAAAsF,QAAAtF,KAAAsF,OAAAC,YACAvF,KAAAwF,QAAAxF,KAAAwF,OAAAF,QAAAtF,KAAAwF,OAAAF,OAAAC,WAEAF,GAAA,mBAAAI,uBACAJ,EAAAI,qBAGAhB,GACAA,EAAAlH,KAAAyC,KAAAqF,GAGAA,KAAAK,uBACAL,EAAAK,sBAAAC,IAAAhB,IAKA1B,EAAA2C,aAAAR,GACGX,IACHW,EAAAX,GAGAW,EAAA,CACA,GAAAF,GAAAjC,EAAAiC,WACAW,EAAAX,EACAjC,EAAA8B,OACA9B,EAAA6C,YACAZ,GAOAjC,EAAA8B,OAAA,SAAAiB,EAAAX,GAEA,MADAD,GAAA7H,KAAA8H,GACAQ,EAAAG,EAAAX,IAPApC,EAAA6C,aAAAD,KACA1G,OAAA0G,EAAAT,IACAA,GAUA,OACAR,WACAzH,QAAA0H,EACA5B,aAOA,SAAA7F,EAAAD,EAAAH,GAEA,YAGAG,GAAAiB,YAAA,CAEA,IAAAo3C,GAAAx4C,EAAA,KAEAy4C,EAEA,SAAAp0C,GAAsC,MAAAA,MAAAjD,WAAAiD,GAAuCyD,QAAAzD,IAF7Em0C,EAIAr4C,GAAA2H,QAAA,SAAAzD,EAAAkH,EAAArC,GAYA,MAXAqC,KAAAlH,IACA,EAAAo0C,EAAA3wC,SAAAzD,EAAAkH,GACArC,QACAjI,YAAA,EACAD,cAAA,EACA8M,UAAA,IAGAzJ,EAAAkH,GAAArC,EAGA7E,IAKA,SAAAjE,EAAA6I,EAAAjJ,GAEA,YACAA,GAAAU,EAAAuI,EAAA,eAA2F,MAAAyvC,IAC3F,IAAAriC,IACAsiC,wBAAA,KACAC,gBAAA,MACAC,yBAAA,KACAC,wBAAA,KACAC,wBAAA,KACAC,qBAAA,IACAC,mBAAA,aACAC,wBAAA,EACAC,yBAAA,KACAC,qBAAA,KACAC,kBAAA,KACAC,gBAAA,KACAC,kBAAA,KACAC,sBAAA,KACAC,8BAAA,KACAC,qBAAA,KACAC,kBAAA,KACAC,mBAAA,KACAC,+BAAA,EACAC,+BAAA,EACAC,oBAAA,EACAC,wBAAA,EAGA/wC,GAAA,GAEA,IAAAyvC,GAAA,SAAAzyC,GACAoQ,EAAApQ,IAKA,SAAA7F,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAEA,GAAA0zC,GAAA1zC,EAAA,WACAyiC,EAAAziC,EAAA,IACAgc,EAAAhc,EAAA,GAAAgc,OACAi+B,EAAA,kBAAAj+B,IAEA5b,EAAAD,QAAA,SAAAQ,GACA,MAAA+yC,GAAA/yC,KAAA+yC,EAAA/yC,GACAs5C,GAAAj+B,EAAArb,KAAAs5C,EAAAj+B,EAAAymB,GAAA,UAAA9hC,MAGA+yC,SAKA,SAAAtzC,EAAAD,EAAAH,GAEAI,EAAAD,SAAkB2H,QAAA9H,EAAA,IAAAoB,YAAA,IAIlB,SAAAhB,EAAA6I,EAAAjJ,GAEA,YASA,SAAAk6C,GAAA71C,EAAA2J,GAIA,MAHAA,GAAA9C,MAAA,KAAAivC,OAAA,SAAAt5C,EAAAR,GACA,MAAAQ,GAAAR,IACKgE,GAOL,QAAA+G,GAAAosC,EAAAnzC,EAAAoH,GACA,IAAA+rC,EAAA,QAEA,KAAA/rC,GAAA,kBAAAA,GAAA,MAAA+rC,GAAApsC,QAAA/G,EAEA,QAAAhE,GAAA,EAAmBA,EAAAm3C,EAAAr0C,OAAkB9C,IACrC,GAAAoL,EAAA+rC,EAAAn3C,GAAAgE,GACA,MAAAhE,EAIA,UA4BA,QAAA+5C,GAAA9yB,OACA,KAAAA,EAAA/iB,OACA+iB,EAAA/iB,SAEA+iB,EAAA1iB,WAAAC,YAAAyiB,GAQA,QAAA+yB,GAAAnxC,GACA,MAAAA,GAGAA,EAAAsvB,QAAA,sCAAuC,QAHvCtvB,EAtEAD,EAAA,EAAAixC,EACAjxC,EAAA,EAAAmC,EACApL,EAAAU,EAAAuI,EAAA,eAA2F,MAAAqxC,KAC3FrxC,EAAA,EAAAmxC,EACAnxC,EAAA,EAAAoxC,CAgCA,IAAAC,IACAC,QAAA,WACA,yBAAAxqB,gBAAAtpB,UAAAE,UAAA0O,MAAA,aAEAmlC,WAAA,WACA,yBAAAzqB,gBAAAtpB,UAAAE,UAAA0O,MAAA,gBAEAolC,IAAA,WACA,yBAAA1qB,gBAAAtpB,UAAAE,UAAA0O,MAAA,sBAEAqlC,MAAA,WACA,yBAAA3qB,gBAAAtpB,UAAAE,UAAA0O,MAAA,gBAEAslC,QAAA,WACA,yBAAA5qB,gBAAAtpB,UAAAE,UAAA0O,MAAA,cAEAulC,IAAA,WACA,MAAAN,GAAAC,WAAAD,EAAAE,cAAAF,EAAAG,OAAAH,EAAAI,SAAAJ,EAAAK,aAyBA,SAAAv6C,EAAAD,GAGA,GAAAgJ,GAAA/I,EAAAD,QAAA,mBAAA4vB,gBAAA3lB,WACA2lB,OAAA,mBAAAkkB,YAAA7pC,WAAA6pC,KAEArT,SAAA,gBACA,iBAAAia,WAAA1xC,IAKA,SAAA/I,EAAAD,GAEA,GAAA26C,GAAA16C,EAAAD,SAA6BqnC,QAAA,QAC7B,iBAAAuT,WAAAD,IAKA,SAAA16C,EAAAD,EAAAH,GAEA,GAAAg7C,GAAAh7C,EAAA,IACAi7C,EAAAj7C,EAAA,IACAk7C,EAAAl7C,EAAA,IACAm7C,EAAAr6C,OAAAC,cAEAZ,GAAAi7C,EAAAp7C,EAAA,IAAAc,OAAAC,eAAA,SAAAs6C,EAAAC,EAAAC,GAIA,GAHAP,EAAAK,GACAC,EAAAJ,EAAAI,GAAA,GACAN,EAAAO,GACAN,EAAA,IACA,MAAAE,GAAAE,EAAAC,EAAAC,GACG,MAAAruC,IACH,UAAAquC,IAAA,OAAAA,GAAA,KAAAC,WAAA,2BAEA,OADA,SAAAD,KAAAF,EAAAC,GAAAC,EAAAryC,OACAmyC,IAMA,SAAAj7C,EAAAD,EAAAH,GAGAI,EAAAD,SAAAH,EAAA,eACA,MAA0E,IAA1Ec,OAAAC,kBAAiC,KAAQG,IAAA,WAAmB,YAAc4K,KAM1E,SAAA1L,EAAAD,GAEA,GAAAqB,MAAuBA,cACvBpB,GAAAD,QAAA,SAAAs7C,EAAAlwC,GACA,MAAA/J,GAAAjB,KAAAk7C,EAAAlwC,KAMA,SAAAnL,EAAA6I,EAAAjJ,GAEA,YACA,IAAA07C,GAAA17C,EAAA,GAGAiJ,GAAA,GACA+K,QAAA0nC,EAAA,GACA3oC,OACA4oC,KAAAxxC,OACAyxC,SAAAv0C,QACAwd,QAAAxd,QACAw0C,QAAAx0C,QACAy0C,KAAA3xC,OAEA4xC,aAAA5xC,OACA6xC,WAAAthB,OAAAvwB,SAEA8E,KAAA,WACA,OACAgtC,SAAA,EACAC,WAAA,IAIAtxB,UAIAuxB,YAAA,WAEA,OADA3zC,GAAAxF,KAAA6S,QACAxV,EAAA,EAA2BA,EAAA,EAAOA,IAClCmI,MAAA4zC,MAAAC,WACA7zC,IAAAqN,QAGA,OAAArN,IAOA8zC,WAAA,WACA,GAAAt5C,KAAAm5C,YAEA,MAAAn5C,MAAAm5C,YAAAI,SAOAC,cAAA,WACA,GAAAx5C,KAAAm5C,YAEA,MAAAn5C,MAAAm5C,YAAAM,YAOAC,SAAA,WACA,OAAA15C,KAAA24C,MACA,eACA,MAAA34C,MAAA24C,IACA,iBACA,MACA,gBACA,cAAA34C,KAAA25C,YAAA,kBAIAnyB,SAIAoyB,MAAA,WACA,GAAAC,GAAA75C,SAEAuG,KAAAvG,KAAAo5C,MAAAU,aAEA95C,KAAAwjC,UAAA,WACA,MAAAqW,GAAAr1B,IAAAhjB,cAAAq4C,EAAAT,MAAAU,aAAAF,WAGAG,OAAA,SAAA/+B,GACAhb,KAAAk5C,WAAA,EACAl5C,KAAA8lB,MAAA,OAAA9K,GACAhb,KAAAg6C,sBAEAC,QAAA,SAAAj/B,GACAhb,KAAAk5C,WAAA,EACAl5C,KAAA8lB,MAAA,QAAA9K,IASAg/B,mBAAA,WACA,OAAAzzC,KAAAvG,KAAA8jB,MAAA9jB,KAAAo5C,MAAAU,aAAA,CAEA,GAAAx1B,GAAAtkB,KAAAwkB,IAAAhjB,cAAAxB,KAAAo5C,MAAAU,aAEA54C,EAAA,KACAg5C,EAAA,KACAjB,GAAA,CAmBA,OAlBA30B,GAAA61B,kBACAj5C,EAAA,YACAg5C,EAAA51B,EAAA81B,kBACAnB,GAAA,GAEAj5C,KAAAi5C,UAEAj5C,KAAAm5C,cAEAn5C,KAAAm5C,YAAAj4C,OACAlB,KAAAm5C,YAAAI,QAAAr4C,GAGAlB,KAAAm5C,YAAAe,UACAl6C,KAAAm5C,YAAAM,WAAAS,IAIAl6C,KAAAi5C,aAOA,SAAA77C,EAAA6I,EAAAjJ,GAEA,YACA,IAAAq9C,GAAAr9C,EAAA,EAGAiJ,GAAA,GACA8J,OACAuqC,SAAAnzC,QAEA8E,KAAA,WACA,OACA0tC,YAAA35C,KAAAs6C,UAAAD,EAAA,EAAAzE,oBAOA,SAAAx4C,EAAAD,EAAAH,GAEAI,EAAAD,SAAkB2H,QAAA9H,EAAA,IAAAoB,YAAA,IAIlB,SAAAhB,EAAAD,EAAAH,GAEA,GAAAm7C,GAAAn7C,EAAA,GACAu9C,EAAAv9C,EAAA,GACAI,GAAAD,QAAAH,EAAA,aAAAqB,EAAAkK,EAAArC,GACA,MAAAiyC,GAAAC,EAAA/5C,EAAAkK,EAAAgyC,EAAA,EAAAr0C,KACC,SAAA7H,EAAAkK,EAAArC,GAED,MADA7H,GAAAkK,GAAArC,EACA7H,IAMA,SAAAjB,EAAAD,EAAAH,GAEA,GAAA4J,GAAA5J,EAAA,GACAI,GAAAD,QAAA,SAAAs7C,GACA,IAAA7xC,EAAA6xC,GAAA,KAAAD,WAAAC,EAAA,qBACA,OAAAA,KAMA,SAAAr7C,EAAAD,EAAAH,GAGA,GAAAw9C,GAAAx9C,EAAA,IACAy9C,EAAAz9C,EAAA,GACAI,GAAAD,QAAA,SAAAs7C,GACA,MAAA+B,GAAAC,EAAAhC,MAMA,SAAAr7C,EAAAD,EAAAH,GAEA,GAAAmJ,GAAAnJ,EAAA,GACA86C,EAAA96C,EAAA,GACA4L,EAAA5L,EAAA,IACA09C,EAAA19C,EAAA,IACA8X,EAAA9X,EAAA,IAGA29C,EAAA,SAAAz5C,EAAAvD,EAAA2B,GACA,GASAiJ,GAAAqyC,EAAAC,EATAC,EAAA55C,EAAAy5C,EAAAI,EACAC,EAAA95C,EAAAy5C,EAAAM,EACAC,EAAAh6C,EAAAy5C,EAAAQ,EACAC,EAAAl6C,EAAAy5C,EAAArC,EACA+C,EAAAn6C,EAAAy5C,EAAAW,EACAC,EAAAr6C,EAAAy5C,EAAAa,EACAr+C,EAAA69C,EAAAlD,IAAAn6C,KAAAm6C,EAAAn6C,OACA89C,EAAAt+C,EAAA,UACAmO,EAAA0vC,EAAA70C,EAAA+0C,EAAA/0C,EAAAxI,IAAAwI,EAAAxI,QAAkF,SAElFq9C,KAAA17C,EAAA3B,EACA,KAAA4K,IAAAjJ,IAEAs7C,GAAAE,GAAAxvC,OAAA/E,KAAA+E,EAAA/C,KACAuM,EAAA3X,EAAAoL,KAEAsyC,EAAAD,EAAAtvC,EAAA/C,GAAAjJ,EAAAiJ,GAEApL,EAAAoL,GAAAyyC,GAAA,kBAAA1vC,GAAA/C,GAAAjJ,EAAAiJ,GAEA8yC,GAAAT,EAAAhyC,EAAAiyC,EAAA10C,GAEAo1C,GAAAjwC,EAAA/C,IAAAsyC,EAAA,SAAAa,GACA,GAAAX,GAAA,SAAAjyC,EAAAa,EAAAlM,GACA,GAAAuC,eAAA07C,GAAA,CACA,OAAA3yC,UAAA5I,QACA,iBAAAu7C,EACA,kBAAAA,GAAA5yC,EACA,kBAAA4yC,GAAA5yC,EAAAa,GACW,UAAA+xC,GAAA5yC,EAAAa,EAAAlM,GACF,MAAAi+C,GAAA1yC,MAAAhJ,KAAA+I,WAGT,OADAgyC,GAAA,UAAAW,EAAA,UACAX,GAEKF,GAAAO,GAAA,kBAAAP,GAAAjyC,EAAAg1B,SAAArgC,KAAAs9C,KAELO,KACAj+C,EAAAw+C,UAAAx+C,EAAAw+C,aAA+CpzC,GAAAsyC,EAE/C35C,EAAAy5C,EAAAiB,GAAAH,MAAAlzC,IAAAmyC,EAAAe,EAAAlzC,EAAAsyC,KAKAF,GAAAI,EAAA,EACAJ,EAAAM,EAAA,EACAN,EAAAQ,EAAA,EACAR,EAAArC,EAAA,EACAqC,EAAAW,EAAA,GACAX,EAAAa,EAAA,GACAb,EAAAkB,EAAA,GACAlB,EAAAiB,EAAA,IACAx+C,EAAAD,QAAAw9C,GAKA,SAAAv9C,EAAAD,GAEAC,EAAAD,QAAA,SAAAs7C,GACA,sBAAAA,GAAA,OAAAA,EAAA,kBAAAA,KAMA,SAAAr7C,EAAAD,GAEAC,EAAAD,QAAA,SAAA2+C,GACA,IACA,QAAAA,IACG,MAAA5xC,GACH,YAOA,SAAA9M,EAAAD,GAEAC,EAAAD,QAAA,SAAA4+C,EAAA71C,GACA,OACAjI,aAAA,EAAA89C,GACA/9C,eAAA,EAAA+9C,GACAjxC,WAAA,EAAAixC,GACA71C,WAOA,SAAA9I,EAAAD,GAEAC,EAAAD,YAKA,SAAAC,EAAAD,GAEAC,EAAAD,QAAAo4C,GAIA,SAAAn4C,EAAAD,EAAAH,GAGA,GAAAg/C,GAAAh/C,EAAA,IACAi/C,EAAAj/C,EAAA,GAEAI,GAAAD,QAAAW,OAAAwM,MAAA,SAAA+tC,GACA,MAAA2D,GAAA3D,EAAA4D,KAMA,SAAA7+C,EAAAD,GAEA,GAAAiD,GAAA,EACA87C,EAAA90C,KAAA2rC,QACA31C,GAAAD,QAAA,SAAAoL,GACA,gBAAApJ,WAAAoH,KAAAgC,EAAA,GAAAA,EAAA,QAAAnI,EAAA87C,GAAAn8C,SAAA,OAMA,SAAA3C,EAAAD,GAEAA,EAAAi7C,KAAc+D,sBAKd,SAAA/+C,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAGA,GAAA4J,GAAA5J,EAAA,GAGAI,GAAAD,QAAA,SAAAs7C,EAAA0C,GACA,IAAAv0C,EAAA6xC,GAAA,MAAAA,EACA,IAAAhwC,GAAAxB,CACA,IAAAk0C,GAAA,mBAAA1yC,EAAAgwC,EAAA14C,YAAA6G,EAAAK,EAAAwB,EAAAlL,KAAAk7C,IAAA,MAAAxxC,EACA,uBAAAwB,EAAAgwC,EAAA2D,WAAAx1C,EAAAK,EAAAwB,EAAAlL,KAAAk7C,IAAA,MAAAxxC,EACA,KAAAk0C,GAAA,mBAAA1yC,EAAAgwC,EAAA14C,YAAA6G,EAAAK,EAAAwB,EAAAlL,KAAAk7C,IAAA,MAAAxxC,EACA,MAAAuxC,WAAA,6CAMA,SAAAp7C,EAAAD,GAEA,GAAA4C,MAAiBA,QAEjB3C,GAAAD,QAAA,SAAAs7C,GACA,MAAA14C,GAAAxC,KAAAk7C,GAAAtsC,MAAA,QAMA,SAAA/O,EAAAD,GAGAC,EAAAD,QAAA,SAAAs7C,GACA,OAAAlyC,IAAAkyC,EAAA,KAAAD,WAAA,yBAAAC,EACA,OAAAA,KAMA,SAAAr7C,EAAAD,GAGA,GAAAk/C,GAAAj1C,KAAAi1C,KACAh1C,EAAAD,KAAAC,KACAjK,GAAAD,QAAA,SAAAs7C,GACA,MAAA5wC,OAAA4wC,MAAA,GAAAA,EAAA,EAAApxC,EAAAg1C,GAAA5D,KAMA,SAAAr7C,EAAAD,EAAAH,GAEA,GAAAs/C,GAAAt/C,EAAA,YACAyiC,EAAAziC,EAAA,GACAI,GAAAD,QAAA,SAAAoL,GACA,MAAA+zC,GAAA/zC,KAAA+zC,EAAA/zC,GAAAk3B,EAAAl3B,MAMA,SAAAnL,EAAAD,EAAAH,GAEA,GAAAmJ,GAAAnJ,EAAA,GAEA0zC,EAAAvqC,EADA,wBACAA,EADA,yBAEA/I,GAAAD,QAAA,SAAAoL,GACA,MAAAmoC,GAAAnoC,KAAAmoC,EAAAnoC,SAMA,SAAAnL,EAAAD,GAGAC,EAAAD,QAAA,gGAEA+K,MAAA,MAKA,SAAA9K,EAAAD,GAEAA,EAAAi7C,EAAAt6C,OAAAy+C,uBAKA,SAAAn/C,EAAAD,EAAAH,GAGA,GAAAy9C,GAAAz9C,EAAA,GACAI,GAAAD,QAAA,SAAAs7C,GACA,MAAA36C,QAAA28C,EAAAhC,MAMA,SAAAr7C,EAAAD,EAAAH,GAEA,YAEA,IAAAw/C,GAAAx/C,EAAA,OAGAA,GAAA,IAAAmK,OAAA,kBAAAs1C,GACAz8C,KAAAyc,GAAAtV,OAAAs1C,GACAz8C,KAAA2c,GAAA,GAEC,WACD,GAEA+/B,GAFArE,EAAAr4C,KAAAyc,GACAja,EAAAxC,KAAA2c,EAEA,OAAAna,IAAA61C,EAAAl4C,QAAiC+F,UAAAK,GAAA4S,MAAA,IACjCujC,EAAAF,EAAAnE,EAAA71C,GACAxC,KAAA2c,IAAA+/B,EAAAv8C,QACU+F,MAAAw2C,EAAAvjC,MAAA,OAMV,SAAA/b,EAAAD,GAEAC,EAAAD,SAAA,GAKA,SAAAC,EAAAD,EAAAH,GAEA,GAAA6N,GAAA7N,EAAA,GAAAo7C,EACAtjC,EAAA9X,EAAA,IACA2/C,EAAA3/C,EAAA,iBAEAI,GAAAD,QAAA,SAAAs7C,EAAAzsC,EAAA4wC,GACAnE,IAAA3jC,EAAA2jC,EAAAmE,EAAAnE,IAAAl6C,UAAAo+C,IAAA9xC,EAAA4tC,EAAAkE,GAAoE3+C,cAAA,EAAAkI,MAAA8F,MAMpE,SAAA5O,EAAAD,EAAAH,GAEAG,EAAAi7C,EAAAp7C,EAAA,IAKA,SAAAI,EAAAD,EAAAH,GAEA,GAAAmJ,GAAAnJ,EAAA,GACA86C,EAAA96C,EAAA,GACA6/C,EAAA7/C,EAAA,IACA8/C,EAAA9/C,EAAA,IACAe,EAAAf,EAAA,GAAAo7C,CACAh7C,GAAAD,QAAA,SAAAQ,GACA,GAAAo/C,GAAAjF,EAAA9+B,SAAA8+B,EAAA9+B,OAAA6jC,KAA0D12C,EAAA6S,WAC1D,MAAArb,EAAA+/B,OAAA,IAAA//B,IAAAo/C,IAAAh/C,EAAAg/C,EAAAp/C,GAAkFuI,MAAA42C,EAAA1E,EAAAz6C,OAMlF,SAAAP,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAGA,GAAAggD,GAAAhgD,EAAA,GACAI,GAAAD,QAAA,SAAAsL,EAAAw0C,EAAA98C,GAEA,GADA68C,EAAAv0C,OACAlC,KAAA02C,EAAA,MAAAx0C,EACA,QAAAtI,GACA,uBAAA2I,GACA,MAAAL,GAAAlL,KAAA0/C,EAAAn0C,GAEA,wBAAAA,EAAAa,GACA,MAAAlB,GAAAlL,KAAA0/C,EAAAn0C,EAAAa,GAEA,wBAAAb,EAAAa,EAAAlM,GACA,MAAAgL,GAAAlL,KAAA0/C,EAAAn0C,EAAAa,EAAAlM,IAGA,kBACA,MAAAgL,GAAAO,MAAAi0C,EAAAl0C,cAOA,SAAA3L,EAAAD,EAAAH,GAEAI,EAAAD,SAAAH,EAAA,MAAAA,EAAA,eACA,MAAsG,IAAtGc,OAAAC,eAAAf,EAAA,gBAAqEkB,IAAA,WAAmB,YAAc4K,KAMtG,SAAA1L,EAAAD,EAAAH,GAEA,GAAA4J,GAAA5J,EAAA,IACAgE,EAAAhE,EAAA,GAAAgE,SAEAkf,EAAAtZ,EAAA5F,IAAA4F,EAAA5F,EAAAC,cACA7D,GAAAD,QAAA,SAAAs7C,GACA,MAAAv4B,GAAAlf,EAAAC,cAAAw3C,QAMA,SAAAr7C,EAAAD,EAAAH,GAEA,GAAA8X,GAAA9X,EAAA,IACAkgD,EAAAlgD,EAAA,IACAmgD,EAAAngD,EAAA,QACAogD,EAAApgD,EAAA,eAEAI,GAAAD,QAAA,SAAAkB,EAAAg/C,GACA,GAGA90C,GAHA8vC,EAAA6E,EAAA7+C,GACAhB,EAAA,EACAia,IAEA,KAAA/O,IAAA8vC,GAAA9vC,GAAA60C,GAAAtoC,EAAAujC,EAAA9vC,IAAA+O,EAAAjX,KAAAkI,EAEA,MAAA80C,EAAAl9C,OAAA9C,GAAAyX,EAAAujC,EAAA9vC,EAAA80C,EAAAhgD,SACA8/C,EAAA7lC,EAAA/O,IAAA+O,EAAAjX,KAAAkI,GAEA,OAAA+O,KAMA,SAAAla,EAAAD,EAAAH,GAGA,GAAAsgD,GAAAtgD,EAAA,GAEAI,GAAAD,QAAAW,OAAA,KAAAq+C,qBAAA,GAAAr+C,OAAA,SAAA26C,GACA,gBAAA6E,EAAA7E,KAAAvwC,MAAA,IAAApK,OAAA26C,KAMA,SAAAr7C,EAAAD,EAAAH,GAGA,GAAAugD,GAAAvgD,EAAA,IACAwgD,EAAAp2C,KAAAo2C,GACApgD,GAAAD,QAAA,SAAAs7C,GACA,MAAAA,GAAA,EAAA+E,EAAAD,EAAA9E,GAAA,sBAMA,SAAAr7C,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,IAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAEA,YAeA,SAAAygD,GAAAp8C,GAAsC,MAAAA,MAAAjD,WAAAiD,GAAuCyD,QAAAzD,GAZ7ElE,EAAAiB,YAAA,CAEA,IAAAs/C,GAAA1gD,EAAA,IAEA2gD,EAAAF,EAAAC,GAEAE,EAAA5gD,EAAA,GAEA6gD,EAAAJ,EAAAG,GAEAE,EAAA,kBAAAD,GAAA/4C,SAAA,gBAAA64C,GAAA74C,QAAA,SAAAzD,GAAiH,aAAAA,IAAqB,SAAAA,GAAmB,MAAAA,IAAA,kBAAAw8C,GAAA/4C,SAAAzD,EAAA8nB,cAAA00B,EAAA/4C,SAAAzD,IAAAw8C,EAAA/4C,QAAAvG,UAAA,eAAA8C,GAIzJlE,GAAA2H,QAAA,kBAAA+4C,GAAA/4C,SAAA,WAAAg5C,EAAAH,EAAA74C,SAAA,SAAAzD,GACA,gBAAAA,EAAA,YAAAy8C,EAAAz8C,IACC,SAAAA,GACD,MAAAA,IAAA,kBAAAw8C,GAAA/4C,SAAAzD,EAAA8nB,cAAA00B,EAAA/4C,SAAAzD,IAAAw8C,EAAA/4C,QAAAvG,UAAA,kBAAA8C,EAAA,YAAAy8C,EAAAz8C,KAKA,SAAAjE,EAAAD,EAAAH,GAEA,YAEA,IAAA6/C,GAAA7/C,EAAA,IACA29C,EAAA39C,EAAA,IACA+gD,EAAA/gD,EAAA,IACA09C,EAAA19C,EAAA,IACAghD,EAAAhhD,EAAA,IACAihD,EAAAjhD,EAAA,IACAkhD,EAAAlhD,EAAA,IACAw1C,EAAAx1C,EAAA,IACAmhD,EAAAnhD,EAAA,eACAohD,OAAA9zC,MAAA,WAAAA,QAKA+zC,EAAA,WAA8B,MAAAr+C,MAE9B5C,GAAAD,QAAA,SAAAmhD,EAAAC,EAAAC,EAAAtlC,EAAAulC,EAAAC,EAAAC,GACAV,EAAAO,EAAAD,EAAArlC,EACA,IAeAsO,GAAAjf,EAAAq2C,EAfAC,EAAA,SAAAC,GACA,IAAAV,GAAAU,IAAAC,GAAA,MAAAA,GAAAD,EACA,QAAAA,GACA,IAVA,OAWA,IAVA,SAUA,kBAA6C,UAAAN,GAAAx+C,KAAA8+C,IACxC,kBAA4B,UAAAN,GAAAx+C,KAAA8+C,KAEjCnC,EAAA4B,EAAA,YACAS,EAdA,UAcAP,EACAQ,GAAA,EACAF,EAAAT,EAAA//C,UACA2gD,EAAAH,EAAAZ,IAAAY,EAnBA,eAmBAN,GAAAM,EAAAN,GACAU,EAAAD,GAAAL,EAAAJ,GACAW,EAAAX,EAAAO,EAAAH,EAAA,WAAAM,MAAA54C,GACA84C,EAAA,SAAAd,EAAAQ,EAAAO,SAAAJ,GAwBA,IArBAG,IACAT,EAAApM,EAAA6M,EAAA9hD,KAAA,GAAA+gD,QACAxgD,OAAAS,WAAAqgD,EAAA1lC,OAEAglC,EAAAU,EAAAjC,GAAA,GAEAE,GAAA,kBAAA+B,GAAAT,IAAAzD,EAAAkE,EAAAT,EAAAE,IAIAW,GAAAE,GAjCA,WAiCAA,EAAAvhD,OACAshD,GAAA,EACAE,EAAA,WAAkC,MAAAD,GAAA3hD,KAAAyC,QAGlC68C,IAAA8B,IAAAP,IAAAa,GAAAF,EAAAZ,IACAzD,EAAAqE,EAAAZ,EAAAgB,GAGAnB,EAAAO,GAAAY,EACAnB,EAAArB,GAAA0B,EACAI,EAMA,GALAj3B,GACAvL,OAAA+iC,EAAAG,EAAAN,EA9CA,UA+CAv0C,KAAAo0C,EAAAS,EAAAN,EAhDA,QAiDAS,QAAAF,GAEAT,EAAA,IAAAp2C,IAAAif,GACAjf,IAAAw2C,IAAAhB,EAAAgB,EAAAx2C,EAAAif,EAAAjf,QACKoyC,KAAArC,EAAAqC,EAAAI,GAAAqD,GAAAa,GAAAV,EAAA/2B,EAEL,OAAAA,KAMA,SAAApqB,EAAAD,EAAAH,GAEAI,EAAAD,QAAAH,EAAA,KAKA,SAAAI,EAAAD,EAAAH,GAGA,GAAAg7C,GAAAh7C,EAAA,IACAuiD,EAAAviD,EAAA,IACAi/C,EAAAj/C,EAAA,IACAogD,EAAApgD,EAAA,gBACAwiD,EAAA,aAIAC,EAAA,WAEA,GAIAC,GAJAC,EAAA3iD,EAAA,cACAK,EAAA4+C,EAAA97C,MAcA,KAVAw/C,EAAAh/B,MAAAgqB,QAAA,OACA3tC,EAAA,IAAAoE,YAAAu+C,GACAA,EAAAxyC,IAAA,cAGAuyC,EAAAC,EAAAC,cAAA5+C,SACA0+C,EAAA9P,OACA8P,EAAAG,MAAAC,uCACAJ,EAAAtO,QACAqO,EAAAC,EAAA3E,EACA19C,WAAAoiD,GAAA,UAAAxD,EAAA5+C,GACA,OAAAoiD,KAGAriD,GAAAD,QAAAW,OAAAmK,QAAA,SAAAowC,EAAA0H,GACA,GAAAzoC,EAQA,OAPA,QAAA+gC,GACAmH,EAAA,UAAAxH,EAAAK,GACA/gC,EAAA,GAAAkoC,GACAA,EAAA,eAEAloC,EAAA8lC,GAAA/E,GACG/gC,EAAAmoC,QACHl5C,KAAAw5C,EAAAzoC,EAAAioC,EAAAjoC,EAAAyoC,KAMA,SAAA3iD,EAAAD,EAAAH,GAEAA,EAAA,GAYA,QAXAmJ,GAAAnJ,EAAA,GACA09C,EAAA19C,EAAA,IACAghD,EAAAhhD,EAAA,IACAgjD,EAAAhjD,EAAA,kBAEAijD,EAAA,wbAIA/3C,MAAA,KAEA7K,EAAA,EAAeA,EAAA4iD,EAAA9/C,OAAyB9C,IAAA,CACxC,GAAAkhD,GAAA0B,EAAA5iD,GACA6iD,EAAA/5C,EAAAo4C,GACAQ,EAAAmB,KAAA3hD,SACAwgD,OAAAiB,IAAAtF,EAAAqE,EAAAiB,EAAAzB,GACAP,EAAAO,GAAAP,EAAAt2C,QAMA,SAAAtK,EAAAD,EAAAH,GAGA,GAAAg/C,GAAAh/C,EAAA,IACAmjD,EAAAnjD,EAAA,IAAAmC,OAAA,qBAEAhC,GAAAi7C,EAAAt6C,OAAAyiC,qBAAA,SAAA8X,GACA,MAAA2D,GAAA3D,EAAA8H,KAMA,SAAA/iD,EAAAD,EAAAH,GAEAI,EAAAD,SAAkB2H,QAAA9H,EAAA,IAAAoB,YAAA,IAIlB,SAAAhB,EAAAD,EAAAH,GAEA,GAAAojD,GAAApjD,EAAA,IACAmhD,EAAAnhD,EAAA,eACAghD,EAAAhhD,EAAA,GACAI,GAAAD,QAAAH,EAAA,GAAAqjD,kBAAA,SAAA5H,GACA,OAAAlyC,IAAAkyC,EAAA,MAAAA,GAAA0F,IACA1F,EAAA,eACAuF,EAAAoC,EAAA3H,MAMA,SAAAr7C,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACA,IAAAsjD,GAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACAE,EAAAxjD,EAAA,GACAyjD,EAAAzjD,EAAAmB,EAAAqiD,GACAE,EAAA1jD,EAAA,GAKAiJ,GAAA,GACA+K,QAAA0vC,EAAA,GACA/2B,WAAA42B,OAA+FE,EAAA33C,EAAAnL,KAAA8iD,EAAA33C,GAC/FiH,OACA+xB,QACA5gC,KAAAmD,QACAS,SAAA,GAEA2vC,MAAAttC,OACAw5C,UACAz/C,KAAAmD,QACAS,SAAA,GAEA5D,KAAAiG,OACAy5C,QAAAv8C,QACAs0C,KAAAxxC,OACAuyC,SAAAvyC,OACA05C,WACA3/C,KAAAmD,QACAS,SAAA,GAEAi0B,UACA73B,KAAAw2B,OACA5yB,QAAA,MAGAmH,KAAA,WACA,OACA60C,SAAA9gD,KAAA8hC,SAIAha,OACAga,OAAA,SAAA57B,GACAlG,KAAA8gD,SAAA56C,GAEA46C,SAAA,SAAA56C,GACAA,EACAlG,KAAA+gD,eAEA/gD,KAAAghD,OACA1+B,aAAAtiB,KAAAghD,SAKAp5B,UAIAkxB,KAAA,WACA,OAAA94C,KAAAkB,MACA,cACA,mBACA,kBACA,oBACA,kBACA,aACA,iBACA,oBACA,SACA,eAIAsmB,SAIA4pB,MAAA,WACApxC,KAAA8gD,UAAA,EACA9gD,KAAA8lB,MAAA,SACA9lB,KAAA8lB,MAAA,qBAMAi7B,aAAA,WACA,GAAAlH,GAAA75C,IAEAA,MAAA6gD,YACA7gD,KAAAghD,MAAAr+B,WAAA,WACAk3B,EAAAiH,UACAjH,EAAAzI,SAEiBpxC,KAAA+4B,aAIjBgL,QAAA,WACA/jC,KAAA+gD,kBAMA,SAAA3jD,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACA,IAAAq9C,GAAAr9C,EAAA,GACAikD,EAAAjkD,EAAA,EAIAiJ,GAAA,GACA8J,OACA7O,MACAA,KAAAiG,OACArC,QAAA,WAEAo1C,QAAA/yC,OACA4xB,SAAArB,OACA1R,OACA9kB,KAAAmD,QACAS,YAAAyB,IAEA26C,UACAhgD,KAAAiG,OACArC,QAAA,SACAq8C,UAAA,SAAAj7C,GACA,6FAAAkC,QAAAlC,IAAA,IAGAk7C,UAAAj6C,QAEA8E,KAAA,WACA,OACA60C,UAAA,EACAO,UAAA,KACAC,aAAA,KACAC,aAAAvhD,KAAAohD,WAAA/G,EAAA,EAAA1E,0BAIA/tB,UACA45B,cAAA,WACA,OAAAxhD,KAAAkhD,UACA,mBACA,aACA,kBACA,MAAAlhD,MAAAqhD,SAEA,uBACA,gBACA,qBACA,MAAArhD,MAAAshD,eAGAvpB,WAAA,WACA,OAAA/3B,KAAAkhD,UACA,mBACA,aACA,kBACA,OACAvpB,MAAA,aACAuC,MAAA,UAEA,uBACA,gBACA,qBACA,OACAvC,MAAA,WACAuC,MAAA,cAKA1S,SACAi6B,YAAA,WAGA,aAFAl7C,KAAAvG,KAAAgmB,MAAAhmB,KAAAgmB,MAAAq0B,EAAA,EAAAtD,sBAIA/2C,KAAAqhD,UAAAK,kBAAA,GAAA1hD,KAAAshD,aAAAI,kBAAA,IAEAtQ,MAAA,WACA,GAAAyI,GAAA75C,IAEAsiB,cAAAtiB,KAAAghD,OACAhhD,KAAA8gD,UAAA,EAGAn+B,WAAA,WACAk3B,EAAA5tB,WACAnuB,OAAAmjD,EAAA,GAAApH,EAAAr1B,MACa,MAEbm9B,WAAA,WACA,GAAAC,GAAA5hD,IAEA,IAAAA,KAAAyhD,cAKA,WAHA9+B,YAAA,WACA,MAAAi/B,GAAAD,cACiB,IAGjB3hD,MAAAwhD,cAAAK,sBAAA,aAAA7hD,KAAAwkB,KACAxkB,KAAA8gD,UAAA,EAEA9gD,KAAA8hD,aACA9hD,KAAAghD,MAAAr+B,WAAA,WACA,MAAAi/B,GAAAxQ,SACiBpxC,KAAA+hD,eAGjBC,eAAA,WAIA,GAHAhiD,KAAAqhD,UAAArgD,SAAAQ,cAAA,mBACAxB,KAAAshD,aAAAtgD,SAAAQ,cAAA,uBAEAxB,KAAAqhD,YAAArhD,KAAAshD,aAAA,CAEAthD,KAAAqhD,YACArhD,KAAAqhD,UAAArgD,SAAAC,cAAA,OACAjB,KAAAqhD,UAAAY,UAAA,kBAGAjiD,KAAAshD,eACAthD,KAAAshD,aAAAtgD,SAAAC,cAAA,OACAjB,KAAAshD,aAAAW,UAAA,oBAGA,IAAAb,GAAApgD,SAAAQ,cAAAxB,KAAAuhD,eAAAvgD,SAAAkrC,IAEAkV,GAAAhgD,YAAApB,KAAAqhD,WACAD,EAAAhgD,YAAApB,KAAAshD,cAEAthD,KAAAuhD,eACAvhD,KAAAqhD,UAAAlsB,UAAAxvB,IAAA,wBACA3F,KAAAshD,aAAAnsB,UAAAxvB,IAAA,4BAIA6lC,YAAA,WACAxrC,KAAAgiD,kBAEAje,QAAA,WACA/jC,KAAA2hD,gBAMA,SAAAvkD,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YAmJA,SAAAklD,GAAAzwC,GAGA,YAFA,mBAAAsb,gBAAA/C,IAAA+C,OAAA/C,IAAAm4B,EAAAr5C,GACAQ,OAAA84C,EAAAt5C,KAEAwb,GAAAtjB,SAAAC,cAAA,OACAwQ,cAvJA3T,OAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAm8C,KACArlD,GAAAU,EAAA2kD,EAAA,0BAA8E,MAAAtJ,KAC9E/7C,EAAAU,EAAA2kD,EAAA,sBAA0E,MAAAC,KAC1EtlD,EAAAU,EAAA2kD,EAAA,sBAA0E,MAAAE,KAC1EvlD,EAAAU,EAAA2kD,EAAA,wBAA4E,MAAAG,KAC5ExlD,EAAAU,EAAA2kD,EAAA,oBAAwE,MAAAI,KACxEzlD,EAAAU,EAAA2kD,EAAA,sBAA0E,MAAAK,KAC1E1lD,EAAAU,EAAA2kD,EAAA,mBAAuE,MAAAM,KACvE3lD,EAAAU,EAAA2kD,EAAA,kBAAsE,MAAAvJ,KACtE97C,EAAAU,EAAA2kD,EAAA,mBAAuE,MAAAO,KACvE5lD,EAAAU,EAAA2kD,EAAA,qBAAyE,MAAAxgC,MACzE7kB,EAAAU,EAAA2kD,EAAA,qBAAyE,MAAAQ,MACzE7lD,EAAAU,EAAA2kD,EAAA,mBAAuE,MAAAS,MACvE9lD,EAAAU,EAAA2kD,EAAA,0BAA8E,MAAAU,MAC9E/lD,EAAAU,EAAA2kD,EAAA,wBAA4E,MAAAW,MAC5EhmD,EAAAU,EAAA2kD,EAAA,mBAAuE,MAAAY,MACvEjmD,EAAAU,EAAA2kD,EAAA,mBAAuE,MAAAa,MACvElmD,EAAAU,EAAA2kD,EAAA,oBAAwE,MAAAc,MACxEnmD,EAAAU,EAAA2kD,EAAA,sBAA0E,MAAAe,MAC1EpmD,EAAAU,EAAA2kD,EAAA,oBAAwE,MAAAgB,MACxErmD,EAAAU,EAAA2kD,EAAA,mBAAuE,MAAAiB,MACvEtmD,EAAAU,EAAA2kD,EAAA,kBAAsE,MAAAkB,MACtEvmD,EAAAU,EAAA2kD,EAAA,iBAAqE,MAAAr2C,MACrEhP,EAAAU,EAAA2kD,EAAA,sBAA0E,MAAAmB,MAC1ExmD,EAAAU,EAAA2kD,EAAA,wBAA4E,MAAAoB,MAC5EzmD,EAAAU,EAAA2kD,EAAA,mBAAuE,MAAAqB,MACvE1mD,EAAAU,EAAA2kD,EAAA,qBAAyE,MAAAsB,MACzE3mD,EAAAU,EAAA2kD,EAAA,oBAAwE,MAAAuB,KAGxE,IAAAC,GAAA7mD,EAAA,IACA8mD,EAAA9mD,EAAAmB,EAAA0lD,GAOAE,GAJA/mD,EAAA,IAIAA,EAAA,KACAgnD,EAAAhnD,EAAAmB,EAAA4lD,GAIA55B,EAAA,SAAAC,GACA,mBAAA2C,gBAAA/C,KACA+C,OAAA/C,IAAAG,IAAAC,IAIA65B,EAAA,SAAAj6B,EAAAvH,GACAuH,EAAAvH,YAAA9kB,KAAA8kB,IAGAyhC,EAAA,SAAAl6B,EAAA1rB,EAAAmkB,GACAuH,EAAAzrB,UAAAD,GAAAmkB,GAOA0hC,GACA35B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAg6B,EAAAl7C,IAIAqhB,GAAAg6B,EAEA,IAAApL,GAAA,EAEAqL,EAAApnD,EAAA,IACAqnD,EAAArnD,EAAAmB,EAAAimD,GAGAE,EAAAtnD,EAAA,KACAunD,EAAAvnD,EAAAmB,EAAAmmD,GAQAE,GACAh6B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAq6B,EAAAv7C,GACAm7C,EAAAj6B,EAAAu6B,EAAAz7C,IAIAqhB,GAAAq6B,EAEA,IAAAlC,GAAA,EAEAmC,EAAAznD,EAAA,KACA0nD,EAAA1nD,EAAAmB,EAAAsmD,GAOAE,GACAn6B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA06B,EAAA57C,IAIAqhB,GAAAw6B,EAEA,IAAApC,GAAA,EAEAqC,EAAA5nD,EAAA,KACA6nD,EAAA7nD,EAAAmB,EAAAymD,GAOAE,GACAt6B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA66B,EAAA/7C,IAIAqhB,GAAA26B,EAEA,IAAAtC,GAAA,EAEAuC,EAAA/nD,EAAA,IACAmlD,EAAAnlD,EAAAmB,EAAA4mD,GAGAC,EAAAhoD,EAAA,KACAolD,EAAAplD,EAAAmB,EAAA6mD,GAkBAC,GACAC,MAAA,SAAAtvC,GACA,GAAAskC,OAAA,EACA,iBAAAtkC,KAAAskC,EAAAtkC,EACA,IAAAuvC,IACAC,WAAA,EACAlL,UAGA,OAAAgI,GADA4B,IAAAqB,EAAAvvC,KAGAyvC,QAAA,SAAAzvC,GACA,GAAAuvC,KAEA,OAAAjD,GADA4B,IAAAqB,EAAAvvC,KAGA0vC,OAAA,SAAA1vC,GACA,GAAAuvC,IACAI,UAAA,EACAC,YAAA,OAGA,OAAAtD,GADA4B,IAAAqB,EAAAvvC,MAKA6vC,GACAj7B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAo4B,EAAAt5C,GACAo7C,EAAAl6B,EAAA,UAAAi7B,IAIA96B,GAAAs7B,EAEA,IAAAhD,GAAA,EAEAiD,EAAA1oD,EAAA,IACA2oD,EAAA3oD,EAAAmB,EAAAunD,GAGAE,EAAA5oD,EAAA,IACA6oD,EAAA7oD,EAAAmB,EAAAynD,GAQAE,GACAt7B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA27B,EAAA78C,GACAm7C,EAAAj6B,EAAA67B,EAAA/8C,IAIAqhB,GAAA27B,EAEA,IAAApD,GAAA,EAEAqD,EAAA/oD,EAAA,IACAgpD,EAAAhpD,EAAAmB,EAAA4nD,GAOAE,GACAz7B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAg8B,EAAAl9C,IAIAqhB,GAAA87B,EAEA,IAAAtD,GAAA,EAEAuD,EAAAlpD,EAAA,GACAmpD,EAAAnpD,EAAAmB,EAAA+nD,GAOAE,GACA57B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAm8B,EAAAr9C,IAIAqhB,GAAAi8B,EAEA,IAAAtN,GAAA,EAEAuN,EAAArpD,EAAA,IACAspD,EAAAtpD,EAAAmB,EAAAkoD,GAOAE,GACA/7B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAs8B,EAAAx9C,IAIAqhB,GAAAo8B,EAEA,IAAA3D,GAAA,EAEA4D,EAAAxpD,EAAA,KACAypD,GAAAzpD,EAAAmB,EAAAqoD,GASAE,IACA9W,KAAA,SAAAh6B,GACA,GAAAuvC,IACAwB,cAAA,GAEAl1C,EAAAqyC,IAAAqB,EAAAvvC,EAIA,aAFA,mBAAAmX,gBAAA/C,IAAA+C,OAAA/C,IAAAm4B,EAAAr5C,GACAQ,OAAAm9C,GAAA39C,KAEAwb,GAAAtjB,SAAAC,cAAA,OACAwQ,gBAKAm1C,IACAp8B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAy8B,GAAA39C,GACAo7C,EAAAl6B,EAAA,WAAA08B,KAIAv8B,GAAAy8B,GAEA,IAAA/kC,IAAA,GAEAglC,GAAA7pD,EAAA,KACA8pD,GAAA9pD,EAAAmB,EAAA0oD,IAOAE,IACAv8B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA88B,GAAAh+C,IAIAqhB,GAAA48B,GAEA,IAAAlE,IAAA,GAEAmE,GAAAhqD,EAAA,IACAiqD,GAAAjqD,EAAAmB,EAAA6oD,IASAE,IACAtX,KAAA,SAAAh6B,GACA,GAAA9W,OAAA,GACA0G,MAAA,EACA,iBAAAoQ,KAAA9W,EAAA8W,EAEA,IAAAuvC,IACAwB,cAAA,EACA7nD,UAEA8W,GAAApQ,SACAA,EAAAoQ,EAAApQ,aACAoQ,GAAApQ,OAEA,IAAAiM,GAAAqyC,IAAAqB,EAAAvvC,EAIA,aAFA,mBAAAmX,gBAAA/C,IAAA+C,OAAA/C,IAAAm4B,EAAAr5C,GACAQ,OAAA29C,GAAAn+C,KAEAtD,SACA8e,GAAAtjB,SAAAC,cAAA,OACAwQ,gBAKA01C,IACA38B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAi9B,GAAAn+C,GACAo7C,EAAAl6B,EAAA,SAAAk9B,KAIA/8B,GAAAg9B,GAEA,IAAArE,IAAA,GAEAsE,GAAApqD,EAAA,KACAqqD,GAAArqD,EAAAmB,EAAAipD,IAOAE,IACA98B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAq9B,GAAAv+C,IAIAqhB,GAAAm9B,GAEA,IAAAvE,IAAA,GAEAwE,GAAAvqD,EAAA,IACAwqD,GAAAxqD,EAAAmB,EAAAopD,IAOAE,IACAj9B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAw9B,GAAA1+C,IAIAqhB,GAAAs9B,GAEA,IAAAzE,IAAA,GAEA0E,GAAA1qD,EAAA,KACA2qD,GAAA3qD,EAAAmB,EAAAupD,IAOAE,IACAp9B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA29B,GAAA7+C,IAIAqhB,GAAAy9B,GAEA,IAAA3E,IAAA,GAEA4E,GAAA7qD,EAAA,KACA8qD,GAAA9qD,EAAAmB,EAAA0pD,IAGAE,GAAA/qD,EAAA,KACAgrD,GAAAhrD,EAAAmB,EAAA4pD,IAQAE,IACAz9B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA89B,GAAAh/C,GACAm7C,EAAAj6B,EAAAg+B,GAAAl/C,IAIAqhB,GAAA89B,GAEA,IAAA/E,IAAA,GAEAgF,GAAAlrD,EAAA,IACAmrD,GAAAnrD,EAAAmB,EAAA+pD,IAOAE,IACA59B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAm+B,GAAAr/C,IAIAqhB,GAAAi+B,GAEA,IAAAjF,IAAA,GAEAkF,GAAArrD,EAAA,KACAsrD,GAAAtrD,EAAAmB,EAAAkqD,IASAE,IACA3Y,KAAA,SAAAh6B,GACA,GAAAskC,OAAA,EACA,iBAAAtkC,KAAAskC,EAAAtkC,EAEA,IAAAuvC,IACAjkD,KAAA,aACAggD,SAAA,kBACAhH,WAEAzoC,EAAAqyC,IAAAqB,EAAAvvC,EAIA,aAFA,mBAAAmX,gBAAA/C,IAAA+C,OAAA/C,IAAAm4B,EAAAr5C,GACAQ,OAAAg/C,GAAAx/C,KAEAwb,GAAAtjB,SAAAC,cAAA,OACAwQ,gBAKA+2C,IACAh+B,QAAA,SAAAR,GACAk6B,EAAAl6B,EAAA,YAAAu+B,KAIAp+B,GAAAq+B,GAEA,IAAApF,IAAA,GAEAqF,GAAAzrD,EAAA,KACA0rD,GAAA1rD,EAAAmB,EAAAsqD,IAOAE,IACAn+B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA0+B,GAAA5/C,IAIAqhB,GAAAw+B,GAEA,IAAAtF,IAAA,GAEAuF,GAAA5rD,EAAA,KACA6rD,GAAA7rD,EAAAmB,EAAAyqD,IAGAE,GAAA9rD,EAAA,IACA+rD,GAAA/rD,EAAAmB,EAAA2qD,IAQAE,IACAx+B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA6+B,GAAA//C,GACAm7C,EAAAj6B,EAAA++B,GAAAjgD,IAIAqhB,GAAA6+B,GAEA,IAAA1F,IAAA,GAEA2F,GAAAjsD,EAAA,KACAksD,GAAAlsD,EAAAmB,EAAA8qD,IAGAE,GAAAnsD,EAAA,KACAosD,GAAApsD,EAAAmB,EAAAgrD,IAQAE,IACA7+B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAk/B,GAAApgD,GACAm7C,EAAAj6B,EAAAo/B,GAAAtgD,IAIAqhB,GAAAk/B,GAEA,IAAA9F,IAAA,GAEA+F,GAAAtsD,EAAA,IACAusD,GAAAvsD,EAAAmB,EAAAmrD,IAGAE,GAAAxsD,EAAA,KACAysD,GAAAzsD,EAAAmB,EAAAqrD,IAQAE,IACAl/B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAu/B,GAAAzgD,GACAm7C,EAAAj6B,EAAAy/B,GAAA3gD,IAIAqhB,GAAAu/B,GAEA,IAAA19C,IAAA,GAEA29C,GAAA3sD,EAAA,KACA4sD,GAAA5sD,EAAAmB,EAAAwrD,IAOAE,IACAr/B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA4/B,GAAA9gD,IAIAqhB,GAAA0/B,GAEA,IAAArG,IAAA,GAEAsG,GAAA9sD,EAAA,KACA+sD,GAAA/sD,EAAAmB,EAAA2rD,IAOAE,IACAx/B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAA+/B,GAAAjhD,IAIAqhB,GAAA6/B,GAEA,IAAAvG,IAAA,GAEAwG,GAAAjtD,EAAA,KACAktD,GAAAltD,EAAAmB,EAAA8rD,IASAE,IACAva,KAAA,SAAAh6B,GACA,GAAAskC,OAAA,EACA,iBAAAtkC,KAAAskC,EAAAtkC,EAEA,IAAAuvC,IAA4BjL,WAC5BzoC,EAAAqyC,IAAAqB,EAAAvvC,EAIA,aAFA,mBAAAmX,gBAAA/C,IAAA+C,OAAA/C,IAAAm4B,EAAAr5C,GACAQ,OAAA4gD,GAAAphD,KAEAwb,GAAAtjB,SAAAC,cAAA,OACAwQ,gBAKA24C,IACA5/B,QAAA,SAAAR,GACAk6B,EAAAl6B,EAAA,SAAAmgC,KAIAhgC,GAAAigC,GAEA,IAAA1G,IAAA,GAEA2G,GAAArtD,EAAA,KACAstD,GAAAttD,EAAAmB,EAAAksD,IAOAE,IACA//B,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAsgC,GAAAxhD,IAIAqhB,GAAAogC,GAEA,IAAA5G,IAAA,GAEA6G,GAAAxtD,EAAA,KACAytD,GAAAztD,EAAAmB,EAAAqsD,IAOAE,IACAlgC,QAAA,SAAAR,GACAi6B,EAAAj6B,EAAAygC,GAAA3hD,IAIAqhB,GAAAugC,GAEA,IAAA9G,IAAA,GAgCAvwC,GAAArW,EAAA,GAYAyzC,IACAjmB,QAAA,SAAAR,GACA,GAAA/mB,GAAA8F,UAAA5I,OAAA,OAAAoG,KAAAwC,UAAA,GAAAA,UAAA,KAGAjL,QAAAuV,GAAA,GAAAywC,IAAAzwC,GAAA,EAAApQ,GAEA,QAAA0nD,KAAAtI,GACAr4B,EAAAG,IAAAk4B,EAAAsI,KAKAxgC,GAAAsmB,GAEAxqC,GAAA,YAIA,SAAA7I,EAAAD,EAAAH,GAEAA,EAAA,IACAI,EAAAD,QAAAH,EAAA,GAAAc,OAAA8sD,QAKA,SAAAxtD,EAAAD,EAAAH,GAGA,GAAA29C,GAAA39C,EAAA,GAEA29C,KAAAQ,EAAAR,EAAAI,EAAA,UAA0C6P,OAAA5tD,EAAA,OAK1C,SAAAI,EAAAD,GAEAC,EAAAD,QAAA,SAAAs7C,GACA,qBAAAA,GAAA,KAAAD,WAAAC,EAAA,sBACA,OAAAA,KAMA,SAAAr7C,EAAAD,EAAAH,GAEA,YAGA,IAAA6tD,GAAA7tD,EAAA,IACA8tD,EAAA9tD,EAAA,IACA+tD,EAAA/tD,EAAA,IACAyM,EAAAzM,EAAA,IACAw9C,EAAAx9C,EAAA,IACAguD,EAAAltD,OAAA8sD,MAGAxtD,GAAAD,SAAA6tD,GAAAhuD,EAAA,eACA,GAAAiuD,MACA3P,KAEAH,EAAAniC,SACAkyC,EAAA,sBAGA,OAFAD,GAAA9P,GAAA,EACA+P,EAAAhjD,MAAA,IAAAsP,QAAA,SAAA2zC,GAAoC7P,EAAA6P,OACjB,GAAnBH,KAAmBC,GAAA9P,IAAAr9C,OAAAwM,KAAA0gD,KAAsC1P,IAAA97C,KAAA,KAAA0rD,IACxD,SAAA5/C,EAAAhM,GAMD,IALA,GAAA8rD,GAAA3hD,EAAA6B,GACA+/C,EAAAtiD,UAAA5I,OACAqC,EAAA,EACA8oD,EAAAR,EAAA1S,EACAmT,EAAAR,EAAA3S,EACAiT,EAAA7oD,GAMA,IALA,GAIA+F,GAJA4yC,EAAAX,EAAAzxC,UAAAvG,MACA8H,EAAAghD,EAAAT,EAAA1P,GAAAh8C,OAAAmsD,EAAAnQ,IAAA0P,EAAA1P,GACAh7C,EAAAmK,EAAAnK,OACAQ,EAAA,EAEAR,EAAAQ,GAAA4qD,EAAAhuD,KAAA49C,EAAA5yC,EAAA+B,EAAA3J,QAAAyqD,EAAA7iD,GAAA4yC,EAAA5yC,GACG,OAAA6iD,IACFJ,GAKD,SAAA5tD,EAAAD,EAAAH,GAIA,GAAAkgD,GAAAlgD,EAAA,IACAwuD,EAAAxuD,EAAA,IACAyuD,EAAAzuD,EAAA,GACAI,GAAAD,QAAA,SAAAuuD,GACA,gBAAAC,EAAArnC,EAAAsnC,GACA,GAGA1lD,GAHAmyC,EAAA6E,EAAAyO,GACAxrD,EAAAqrD,EAAAnT,EAAAl4C,QACAqC,EAAAipD,EAAAG,EAAAzrD,EAIA,IAAAurD,GAAApnC,MAAA,KAAAnkB,EAAAqC,GAGA,IAFA0D,EAAAmyC,EAAA71C,OAEA0D,EAAA,aAEK,MAAY/F,EAAAqC,EAAeA,IAAA,IAAAkpD,GAAAlpD,IAAA61C,KAChCA,EAAA71C,KAAA8hB,EAAA,MAAAonC,IAAAlpD,GAAA,CACK,QAAAkpD,IAAA,KAOL,SAAAtuD,EAAAD,EAAAH,GAEA,GAAAugD,GAAAvgD,EAAA,IACA2R,EAAAvH,KAAAuH,IACA6uC,EAAAp2C,KAAAo2C,GACApgD,GAAAD,QAAA,SAAAqF,EAAArC,GAEA,MADAqC,GAAA+6C,EAAA/6C,GACAA,EAAA,EAAAmM,EAAAnM,EAAArC,EAAA,GAAAq9C,EAAAh7C,EAAArC,KAMA,SAAA/C,EAAAD,KAMA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA2lD,GAAA7uD,EAAA,IACA8uD,EAAA9uD,EAAAmB,EAAA0tD,GACAE,EAAA/uD,EAAA,IACAgvD,EAAAhvD,EAAAmB,EAAA4tD,GACAE,EAAAjvD,EAAA,GACAkvD,EAAAlvD,EAAAmB,EAAA8tD,GACAE,EAAAnvD,EAAA,GACAovD,EAAApvD,EAAA,IACAqvD,EAAArvD,EAAA,IACAsvD,EAAAtvD,EAAAmB,EAAAkuD,EAoEApmD,GAAA,SACAtI,KAAA,gBACAgsB,WAAAuiC,OAA+FI,EAAAxjD,EAAAnL,KAAA2uD,EAAAxjD,GAC/FkI,QAAAo7C,EAAA,GACA/7B,cAAA,EACAtgB,OACA7J,OAAAwxB,OAAAvwB,QACA8E,MACA/K,KAAAwG,MACA5C,QAAA,WACA,WAGA69C,OACAzhD,KAAAiG,OACArC,QAAA,SAEAynD,UAAAloD,QACAmoD,cAAAnoD,QACAooD,YAAApoD,SAEA4H,KAAA,WACA,OACAkhB,SAAA,KACAu/B,QAAA,KACA5L,UAAA,EACA6L,SAAA3sD,KAAAkG,MACA0mD,4BAAA,EACAC,UAAA,EACAC,iBAAA,EACAhT,YAAA,UAIAlyB,UAKAmlC,UAAA,WACA,GAAAA,KAIA,IAHAA,EAAA1sD,KAAAL,KAAA8jB,MAAA8+B,MAAAp+B,IAAAhjB,cAAA,UACAurD,EAAA1sD,KAAAL,KAAA8jB,MAAA4+B,cAEAn8C,KAAAvG,KAAA8jB,MAAA4+B,SAAA,CACA,GAAAx2C,GAAAlM,KAAA8jB,MAAA4+B,SAAAsK,iBAAA,KACAC,GAAA,EACAC,GAAA,EACAC,MAAA5mD,EAEA,KACA,OAAA6mD,GAAA1P,EAAAsO,IAAA9/C,KAAqI+gD,GAAAG,EAAA1P,EAAAxkC,QAAAC,MAAgE8zC,GAAA,GACrM,GAAAx8C,GAAA28C,EAAAlnD,KAEA6mD,GAAA1sD,KAAAoQ,IAEiB,MAAAiC,GACjBw6C,GAAA,EACAC,EAAAz6C,EACiB,QACjB,KACAu6C,GAAAvP,EAAA2P,QACA3P,EAAA2P,SAEqB,QACrB,GAAAH,EACA,KAAAC,KAMA,MAAAJ,IAOAO,eAAA,WACA,QAAAttD,KAAAyZ,aAAA3U,SAOAyoD,aAAA,WACA,QAAAvtD,KAAA0Z,OAAA8zC,OAOAC,cAAA,WACA,QAAAztD,KAAA0Z,OAAAg0C,SAGA5lC,OAKAg5B,SAAA,SAAAhf,GACA,GAAA+X,GAAA75C,IAEA8hC,GACA9hC,KAAA2tD,kCAEA3tD,KAAAwjC,UAAA,WACA,MAAAqW,GAAA+T,WAAA,QAGAjrC,WAAA,WACAk3B,EAAA8T,kCACiB,OAWjBhB,SAAA,SAAAzmD,GACAlG,KAAA8lB,MAAA,QAAA5f,EAEA,IAAA2nD,GAAA7tD,KAAAq7B,SAAAr7B,KAAAmtB,SACA0gC,QAAA3nD,GACAlG,KAAAg7B,YAAA,UAGAh7B,KAAA6sD,UAAA7sD,KAAAysD,cAAAvmD,IACAlG,KAAA8gD,WAAA56C,IAUAA,MAAA,SAAAktB,GACApzB,KAAA2sD,SAAAv5B,GACApzB,KAAAi5C,SAAAj5C,KAAA8jB,MAAA8+B,MAAA5I,sBAOA/tC,KAAA,SAAA/F,GAEAlG,KAAAusD,WACAvsD,KAAA8tD,kBAAA5nD,KAIAshB,SAIAomC,WAAA,SAAAxyB,OACA70B,KAAA60B,IAEAp7B,KAAA0sD,QAAAtxB,IAQAJ,YAAA,SAAAI,GACA,GAAAwmB,GAAA5hD,KAEA+tD,IAAAhlD,UAAA5I,OAAA,OAAAoG,KAAAwC,UAAA,KAAAA,UAAA,OAEAxC,KAAA60B,IAEAp7B,KAAAmtB,SAAAiO,EACAp7B,KAAA8lB,MAAA,SAAA9lB,KAAAmtB,UACA,OAAAntB,KAAAmtB,WACAntB,KAAA2sD,SAAA3sD,KAAAwsD,cAAA,GAAAxsD,KAAAq7B,SAAAr7B,KAAAmtB,WAEA4gC,GAAA/tD,KAAAwjC,UAAA,WACAoe,EAAAd,UAAA,MAQAgN,kBAAA,SAAA7qD,GACA,GAAA+qD,GAAAhuD,IAEAA,MAAAwjC,UAAA,WACAvgC,EAAA9C,QAEA6tD,EAAAvB,aAAA,KAAAuB,EAAArB,UAAAqB,EAAAtB,UAAAzpD,EAAA,KACA+qD,EAAAJ,WAAA3qD,EAAA,IAGA+qD,EAAAJ,WAAA,SAUAK,aAAA,WACA,OAAAjuD,KAAA0sD,SACA1sD,KAAAg7B,YAAAh7B,KAAA0sD,UASAwB,WAAA,WACA,UAAAluD,KAAA0sD,QAEA,YADA1sD,KAAA8gD,UAAA,EAGA9gD,MAAAg7B,YAAAh7B,KAAA0sD,UAOAyB,eAAA,SAAA14C,GACAzV,KAAA+sD,UAAA3kD,QAAAqN,EAAAnK,QAAA,IAAAtL,KAAA8gD,UAAA,IASAzlB,SAAA,SAAAD,GACA,GAAAgzB,GAAArlD,UAAA5I,OAAA,OAAAoG,KAAAwC,UAAA,IAAAA,UAAA,EAEA,IAAAqyB,EAAA,CAEA,GAAAl1B,GAAA,qBAAAk1B,EAAA,YAAA0wB,IAAA1wB,IAAAt9B,OAAAquD,EAAA,GAAA/wB,EAAAp7B,KAAA2iD,OAAAvnB,EAEAizB,EAAA,gBAAAruD,MAAA2sD,SAAA7uD,OAAAquD,EAAA,GAAAnsD,KAAA2sD,UAAA3sD,KAAA2sD,SACA2B,EAAA,GAAA5vB,QAAA,IAAA2vB,EAAA,SAEA,OAAAD,GAAAloD,EAAAsvB,QAAA84B,EAAA,aAAApoD,IAQAynD,+BAAA,WACA,GAAAY,GAAAvuD,IAEAA,MAAAwjC,UAAA,WAKA,OAAAj9B,KAAAgoD,EAAAzqC,MAAA4+B,SAAA,CAEA,GAAA8L,GAAAD,EAAAzqC,MAAA4+B,SAAA/lB,uBAEA4xB,GAAA3B,2BAAA4B,EAAAtxB,KAAA,GAAAsxB,EAAAC,SAAA1hC,OAAA2hC,aAAA1tD,SAAAuyC,gBAAAob,kBASAC,UAAA,SAAAC,GACA,GAAAC,GAAA,SAAAD,EAAA,IACA,IAAA7uD,KAAA8gD,SAAA,CACA,GAAAt+C,GAAAxC,KAAAiM,KAAA7D,QAAApI,KAAA0sD,SAAAoC,CACAtsD,KAAAxC,KAAAiM,KAAA9L,OAAA,EAAAH,KAAAiM,KAAA9L,OAAAqC,EACAA,IAAA,IAAAA,EAEAxC,KAAA4tD,WAAA5tD,KAAAiM,KAAAzJ,GAEA,IAAA1C,GAAAE,KAAA8jB,MAAA4+B,SAAAlhD,cAAA,qBACAutD,EAAAjvD,EAAAktD,iBAAA,oCAAAxqD,EAEA,KAAAusD,EAAA,MAEA,IAAAC,GAAAlvD,EAAAmvD,UACAC,EAAApvD,EAAAmvD,UAAAnvD,EAAA6uD,aAAAI,EAAAJ,YAEAI,GAAAI,UAAAH,EACAlvD,EAAAmvD,UAAAF,EAAAI,UACiBJ,EAAAI,WAAAD,IACjBpvD,EAAAmvD,UAAAF,EAAAI,UAAArvD,EAAA6uD,aAAAI,EAAAJ,kBAGA3uD,MAAA8gD,UAAA,GASAsO,QAAA,SAAA35C,GACAzV,KAAAq7B,SAAAr7B,KAAAmtB,YAAAntB,KAAA2sD,UACA3sD,KAAAwkB,IAAAhjB,cAAA,SAAA6tD,SAEArvD,KAAAysD,cACAzsD,KAAA8gD,UAAA,EACA9gD,KAAAusD,WACAvsD,KAAA8tD,kBAAA9tD,KAAAiM,OAGAjM,KAAA6sD,UAAA,EACA7sD,KAAA8lB,MAAA,QAAArQ,IAOAskC,OAAA,SAAAtkC,GACAzV,KAAA6sD,UAAA,EACA7sD,KAAA8lB,MAAA,OAAArQ,KAGAouB,QAAA,WACA,mBAAA9W,UACA/rB,SAAA0wB,iBAAA,QAAA1xB,KAAAmuD,gBACAphC,OAAA2E,iBAAA,SAAA1xB,KAAA2tD,kCAGA2B,cAAA,WACA,mBAAAviC,UACA/rB,SAAAywB,oBAAA,QAAAzxB,KAAAmuD,gBACAphC,OAAA0E,oBAAA,SAAAzxB,KAAA2tD,oCAOA,SAAAvwD,EAAAD,EAAAH,GAEAI,EAAAD,SAAkB2H,QAAA9H,EAAA,IAAAoB,YAAA,IAIlB,SAAAhB,EAAAD,EAAAH,GAEAA,EAAA,IACAA,EAAA,IACAI,EAAAD,QAAAH,EAAA,IAAAo7C,EAAA,aAKA,SAAAh7C,EAAAD,EAAAH,GAEA,GAAAugD,GAAAvgD,EAAA,IACAy9C,EAAAz9C,EAAA,GAGAI,GAAAD,QAAA,SAAAoyD,GACA,gBAAAtS,EAAAngB,GACA,GAGAh0B,GAAAa,EAHAjL,EAAAyI,OAAAszC,EAAAwC,IACA5/C,EAAAkgD,EAAAzgB,GACAx/B,EAAAoB,EAAAyB,MAEA,OAAA9C,GAAA,GAAAA,GAAAC,EAAAiyD,EAAA,OAAAhpD,IACAuC,EAAApK,EAAAkM,WAAAvN,GACAyL,EAAA,OAAAA,EAAA,OAAAzL,EAAA,IAAAC,IAAAqM,EAAAjL,EAAAkM,WAAAvN,EAAA,WAAAsM,EAAA,MACA4lD,EAAA7wD,EAAAg/B,OAAArgC,GAAAyL,EACAymD,EAAA7wD,EAAAyN,MAAA9O,IAAA,GAAAsM,EAAA,OAAAb,EAAA,qBAOA,SAAA1L,EAAAD,EAAAH,GAEA,YAEA,IAAAiL,GAAAjL,EAAA,IACAwyD,EAAAxyD,EAAA,IACAkhD,EAAAlhD,EAAA,IACA4hD,IAGA5hD,GAAA,IAAA4hD,EAAA5hD,EAAA,0BAA4F,MAAAgD,QAE5F5C,EAAAD,QAAA,SAAAqhD,EAAAD,EAAArlC,GACAslC,EAAAjgD,UAAA0J,EAAA22C,GAAqD1lC,KAAAs2C,EAAA,EAAAt2C,KACrDglC,EAAAM,EAAAD,EAAA,eAMA,SAAAnhD,EAAAD,EAAAH,GAEA,GAAAm7C,GAAAn7C,EAAA,GACAg7C,EAAAh7C,EAAA,IACA6tD,EAAA7tD,EAAA,GAEAI,GAAAD,QAAAH,EAAA,IAAAc,OAAAmiC,iBAAA,SAAAoY,EAAA0H,GACA/H,EAAAK,EAKA,KAJA,GAGAC,GAHAhuC,EAAAugD,EAAA9K,GACA5/C,EAAAmK,EAAAnK,OACA9C,EAAA,EAEA8C,EAAA9C,GAAA86C,EAAAC,EAAAC,EAAAC,EAAAhuC,EAAAjN,KAAA0iD,EAAAzH,GACA,OAAAD,KAMA,SAAAj7C,EAAAD,EAAAH,GAEA,GAAAgE,GAAAhE,EAAA,GAAAgE,QACA5D,GAAAD,QAAA6D,KAAAuyC,iBAKA,SAAAn2C,EAAAD,EAAAH,GAGA,GAAA8X,GAAA9X,EAAA,IACAyM,EAAAzM,EAAA,IACAogD,EAAApgD,EAAA,gBACAyyD,EAAA3xD,OAAAS,SAEAnB,GAAAD,QAAAW,OAAA00C,gBAAA,SAAA6F,GAEA,MADAA,GAAA5uC,EAAA4uC,GACAvjC,EAAAujC,EAAA+E,GAAA/E,EAAA+E,GACA,kBAAA/E,GAAAlvB,aAAAkvB,eAAAlvB,YACAkvB,EAAAlvB,YAAA5qB,UACG85C,YAAAv6C,QAAA2xD,EAAA,OAMH,SAAAryD,EAAAD,EAAAH,GAEA,YAEA,IAAA0yD,GAAA1yD,EAAA,IACA2yD,EAAA3yD,EAAA,IACAghD,EAAAhhD,EAAA,IACAkgD,EAAAlgD,EAAA,GAMAI,GAAAD,QAAAH,EAAA,IAAA0K,MAAA,iBAAA+0C,EAAAqC,GACA9+C,KAAAyc,GAAAygC,EAAAT,GACAz8C,KAAA2c,GAAA,EACA3c,KAAA8c,GAAAgiC,GAEC,WACD,GAAAzG,GAAAr4C,KAAAyc,GACAqiC,EAAA9+C,KAAA8c,GACAta,EAAAxC,KAAA2c,IACA,QAAA07B,GAAA71C,GAAA61C,EAAAl4C,QACAH,KAAAyc,OAAAlW,GACAopD,EAAA,IAEA,QAAA7Q,EAAA6Q,EAAA,EAAAntD,GACA,UAAAs8C,EAAA6Q,EAAA,EAAAtX,EAAA71C,IACAmtD,EAAA,GAAAntD,EAAA61C,EAAA71C,MACC,UAGDw7C,EAAA4R,UAAA5R,EAAAt2C,MAEAgoD,EAAA,QACAA,EAAA,UACAA,EAAA,YAKA,SAAAtyD,EAAAD,GAEAC,EAAAD,QAAA,cAKA,SAAAC,EAAAD,GAEAC,EAAAD,QAAA,SAAAgc,EAAAjT,GACA,OAAUA,QAAAiT,YAMV,SAAA/b,EAAAD,EAAAH,GAEAA,EAAA,IACAA,EAAA,IACAA,EAAA,IACAA,EAAA,IACAI,EAAAD,QAAAH,EAAA,GAAAgc,QAKA,SAAA5b,EAAAD,EAAAH,GAEA,YAGA,IAAAmJ,GAAAnJ,EAAA,GACA8X,EAAA9X,EAAA,IACA6yD,EAAA7yD,EAAA,IACA29C,EAAA39C,EAAA,IACA+gD,EAAA/gD,EAAA,IACA8yD,EAAA9yD,EAAA,IAAA+yD,IACAC,EAAAhzD,EAAA,IACAs/C,EAAAt/C,EAAA,IACAkhD,EAAAlhD,EAAA,IACAyiC,EAAAziC,EAAA,IACAizD,EAAAjzD,EAAA,GACA8/C,EAAA9/C,EAAA,IACAkzD,EAAAlzD,EAAA,IACAmzD,EAAAnzD,EAAA,IACA2K,EAAA3K,EAAA,IACAg7C,EAAAh7C,EAAA,IACA4J,EAAA5J,EAAA,IACAkgD,EAAAlgD,EAAA,IACAk7C,EAAAl7C,EAAA,IACAu9C,EAAAv9C,EAAA,IACAozD,EAAApzD,EAAA,IACAqzD,EAAArzD,EAAA,IACAszD,EAAAtzD,EAAA,IACAuzD,EAAAvzD,EAAA,GACAg/C,EAAAh/C,EAAA,IACAwzD,EAAAF,EAAAlY,EACAD,EAAAoY,EAAAnY,EACAqY,EAAAJ,EAAAjY,EACA2E,EAAA52C,EAAA6S,OACA03C,EAAAvqD,EAAAvG,KACA+wD,EAAAD,KAAA7wD,UAEA+wD,EAAAX,EAAA,WACAY,EAAAZ,EAAA,eACA1E,KAAepP,qBACf2U,EAAAxU,EAAA,mBACAyU,EAAAzU,EAAA,WACA0U,EAAA1U,EAAA,cACAmT,EAAA3xD,OAAA,UACAmzD,EAAA,kBAAAlU,GACAmU,EAAA/qD,EAAA+qD,QAEA9iD,GAAA8iD,MAAA,YAAAA,EAAA,UAAAC,UAGAC,EAAAvB,GAAAG,EAAA,WACA,MAEG,IAFHI,EAAAjY,KAAsB,KACtBj6C,IAAA,WAAsB,MAAAi6C,GAAAn4C,KAAA,KAAuBkG,MAAA,IAAW4C,MACrDA,IACF,SAAA2vC,EAAAlwC,EAAA8oD,GACD,GAAAC,GAAAd,EAAAf,EAAAlnD,EACA+oD,UAAA7B,GAAAlnD,GACA4vC,EAAAM,EAAAlwC,EAAA8oD,GACAC,GAAA7Y,IAAAgX,GAAAtX,EAAAsX,EAAAlnD,EAAA+oD,IACCnZ,EAEDoZ,EAAA,SAAAvlD,GACA,GAAAwlD,GAAAT,EAAA/kD,GAAAokD,EAAArT,EAAA,UAEA,OADAyU,GAAA10C,GAAA9Q,EACAwlD,GAGAC,EAAAR,GAAA,gBAAAlU,GAAA9jC,SAAA,SAAAw/B,GACA,sBAAAA,IACC,SAAAA,GACD,MAAAA,aAAAsE,IAGA2U,EAAA,SAAAjZ,EAAAlwC,EAAA8oD,GAKA,MAJA5Y,KAAAgX,GAAAiC,EAAAV,EAAAzoD,EAAA8oD,GACArZ,EAAAS,GACAlwC,EAAA2vC,EAAA3vC,GAAA,GACAyvC,EAAAqZ,GACAv8C,EAAAi8C,EAAAxoD,IACA8oD,EAAApzD,YAIA6W,EAAA2jC,EAAAmY,IAAAnY,EAAAmY,GAAAroD,KAAAkwC,EAAAmY,GAAAroD,IAAA,GACA8oD,EAAAjB,EAAAiB,GAAsBpzD,WAAAs8C,EAAA,UAJtBzlC,EAAA2jC,EAAAmY,IAAAzY,EAAAM,EAAAmY,EAAArW,EAAA,OACA9B,EAAAmY,GAAAroD,IAAA,GAIK6oD,EAAA3Y,EAAAlwC,EAAA8oD,IACFlZ,EAAAM,EAAAlwC,EAAA8oD,IAEHM,EAAA,SAAAlZ,EAAAH,GACAN,EAAAS,EAKA,KAJA,GAGAlwC,GAHA+B,EAAA6lD,EAAA7X,EAAA4E,EAAA5E,IACAj7C,EAAA,EACAC,EAAAgN,EAAAnK,OAEA7C,EAAAD,GAAAq0D,EAAAjZ,EAAAlwC,EAAA+B,EAAAjN,KAAAi7C,EAAA/vC,GACA,OAAAkwC,IAEAmZ,EAAA,SAAAnZ,EAAAH,GACA,WAAA/xC,KAAA+xC,EAAA8X,EAAA3X,GAAAkZ,EAAAvB,EAAA3X,GAAAH,IAEAuZ,EAAA,SAAAtpD,GACA,GAAAupD,GAAAvG,EAAAhuD,KAAAyC,KAAAuI,EAAA2vC,EAAA3vC,GAAA,GACA,SAAAvI,OAAAyvD,GAAA36C,EAAAi8C,EAAAxoD,KAAAuM,EAAAk8C,EAAAzoD,QACAupD,IAAAh9C,EAAA9U,KAAAuI,KAAAuM,EAAAi8C,EAAAxoD,IAAAuM,EAAA9U,KAAA4wD,IAAA5wD,KAAA4wD,GAAAroD,KAAAupD,IAEAC,EAAA,SAAAtZ,EAAAlwC,GAGA,GAFAkwC,EAAAyE,EAAAzE,GACAlwC,EAAA2vC,EAAA3vC,GAAA,GACAkwC,IAAAgX,IAAA36C,EAAAi8C,EAAAxoD,IAAAuM,EAAAk8C,EAAAzoD,GAAA,CACA,GAAA8oD,GAAAb,EAAA/X,EAAAlwC,EAEA,QADA8oD,IAAAv8C,EAAAi8C,EAAAxoD,IAAAuM,EAAA2jC,EAAAmY,IAAAnY,EAAAmY,GAAAroD,KAAA8oD,EAAApzD,YAAA,GACAozD,IAEAW,EAAA,SAAAvZ,GAKA,IAJA,GAGAlwC,GAHA80C,EAAAoT,EAAAvT,EAAAzE,IACAnhC,KACAja,EAAA,EAEAggD,EAAAl9C,OAAA9C,GACAyX,EAAAi8C,EAAAxoD,EAAA80C,EAAAhgD,OAAAkL,GAAAqoD,GAAAroD,GAAAunD,GAAAx4C,EAAAjX,KAAAkI,EACG,OAAA+O,IAEH26C,EAAA,SAAAxZ,GAMA,IALA,GAIAlwC,GAJA2pD,EAAAzZ,IAAAgX,EACApS,EAAAoT,EAAAyB,EAAAlB,EAAA9T,EAAAzE,IACAnhC,KACAja,EAAA,EAEAggD,EAAAl9C,OAAA9C,IACAyX,EAAAi8C,EAAAxoD,EAAA80C,EAAAhgD,OAAA60D,IAAAp9C,EAAA26C,EAAAlnD,IAAA+O,EAAAjX,KAAA0wD,EAAAxoD,GACG,OAAA+O,GAIH25C,KACAlU,EAAA,WACA,GAAA/8C,eAAA+8C,GAAA,KAAAvE,WAAA,+BACA,IAAAxsC,GAAAyzB,EAAA12B,UAAA5I,OAAA,EAAA4I,UAAA,OAAAxC,IACAq8B,EAAA,SAAA18B,GACAlG,OAAAyvD,GAAA7sB,EAAArlC,KAAAyzD,EAAA9qD,GACA4O,EAAA9U,KAAA4wD,IAAA97C,EAAA9U,KAAA4wD,GAAA5kD,KAAAhM,KAAA4wD,GAAA5kD,IAAA,GACAolD,EAAApxD,KAAAgM,EAAAuuC,EAAA,EAAAr0C,IAGA,OADA2pD,IAAAzhD,GAAAgjD,EAAA3B,EAAAzjD,GAAgEhO,cAAA,EAAAqQ,IAAAu0B,IAChE2uB,EAAAvlD,IAEA+xC,EAAAhB,EAAA,gCACA,MAAA/8C,MAAA8c,KAGAwzC,EAAAlY,EAAA2Z,EACAxB,EAAAnY,EAAAsZ,EACA10D,EAAA,IAAAo7C,EAAAiY,EAAAjY,EAAA4Z,EACAh1D,EAAA,IAAAo7C,EAAAyZ,EACA70D,EAAA,IAAAo7C,EAAA6Z,EAEApC,IAAA7yD,EAAA,KACA+gD,EAAA0R,EAAA,uBAAAoC,GAAA,GAGA/U,EAAA1E,EAAA,SAAAz6C,GACA,MAAA4zD,GAAAtB,EAAAtyD,MAIAg9C,IAAAM,EAAAN,EAAAa,EAAAb,EAAAI,GAAAkW,GAA0Dj4C,OAAA+jC,GAE1D,QAAAoV,IAAA,iHAGAjqD,MAAA,KAAAvH,GAAA,EAAoBwxD,GAAAhyD,OAAAQ,IAAuBsvD,EAAAkC,GAAAxxD,MAE3C,QAAAyxD,IAAApW,EAAAiU,EAAAvf,OAAAya,GAAA,EAAoDiH,GAAAjyD,OAAAgrD,IAA6B+E,EAAAkC,GAAAjH,MAEjFxQ,KAAAQ,EAAAR,EAAAI,GAAAkW,EAAA,UAEAoB,IAAA,SAAA9pD,GACA,MAAAuM,GAAAg8C,EAAAvoD,GAAA,IACAuoD,EAAAvoD,GACAuoD,EAAAvoD,GAAAw0C,EAAAx0C,IAGA+pD,OAAA,SAAAd,GACA,IAAAC,EAAAD,GAAA,KAAAhZ,WAAAgZ,EAAA,oBACA,QAAAjpD,KAAAuoD,GAAA,GAAAA,EAAAvoD,KAAAipD,EAAA,MAAAjpD,IAEAgqD,UAAA,WAA0BnkD,GAAA,GAC1BokD,UAAA,WAA0BpkD,GAAA,KAG1BusC,IAAAQ,EAAAR,EAAAI,GAAAkW,EAAA,UAEAhpD,OAAA2pD,EAEA7zD,eAAA2zD,EAEAzxB,iBAAA0xB,EAEAxjD,yBAAA4jD,EAEAxxB,oBAAAyxB,EAEAzV,sBAAA0V,IAIAvB,GAAA/V,IAAAQ,EAAAR,EAAAI,IAAAkW,GAAAjB,EAAA,WACA,GAAA7U,GAAA4B,GAIA,iBAAA4T,GAAAxV,KAA2D,MAA3DwV,GAAoD7nD,EAAAqyC,KAAe,MAAAwV,EAAA7yD,OAAAq9C,OAClE,QACDt7C,UAAA,SAAA44C,GAIA,IAHA,GAEAga,GAAAC,EAFAv/C,GAAAslC,GACAp7C,EAAA,EAEA0L,UAAA5I,OAAA9C,GAAA8V,EAAA9S,KAAA0I,UAAA1L,KAEA,IADAq1D,EAAAD,EAAAt/C,EAAA,IACAvM,EAAA6rD,QAAAlsD,KAAAkyC,KAAAgZ,EAAAhZ,GAMA,MALA9wC,GAAA8qD,OAAA,SAAAlqD,EAAArC,GAEA,GADA,kBAAAwsD,KAAAxsD,EAAAwsD,EAAAn1D,KAAAyC,KAAAuI,EAAArC,KACAurD,EAAAvrD,GAAA,MAAAA,KAEAiN,EAAA,GAAAs/C,EACA9B,EAAA3nD,MAAA0nD,EAAAv9C,MAKA4pC,EAAA,UAAA8T,IAAA7zD,EAAA,IAAA+/C,EAAA,UAAA8T,EAAA9T,EAAA,UAAAX,SAEA8B,EAAAnB,EAAA,UAEAmB,EAAA92C,KAAA,WAEA82C,EAAA/3C,EAAAvG,KAAA,YAKA,SAAAxC,EAAAD,EAAAH,GAEA,GAAA8yD,GAAA9yD,EAAA,YACA4J,EAAA5J,EAAA,IACA8X,EAAA9X,EAAA,IACA21D,EAAA31D,EAAA,GAAAo7C,EACAh4C,EAAA,EACAwN,EAAA9P,OAAA8P,cAAA,WACA,UAEAglD,GAAA51D,EAAA,eACA,MAAA4Q,GAAA9P,OAAA+0D,yBAEAC,EAAA,SAAAra,GACAka,EAAAla,EAAAqX,GAAqB5pD,OACrB7I,EAAA,OAAA+C,EACA2yD,SAGAC,EAAA,SAAAva,EAAAxwC,GAEA,IAAArB,EAAA6xC,GAAA,sBAAAA,MAAA,gBAAAA,GAAA,SAAAA,CACA,KAAA3jC,EAAA2jC,EAAAqX,GAAA,CAEA,IAAAliD,EAAA6qC,GAAA,SAEA,KAAAxwC,EAAA,SAEA6qD,GAAAra,GAEG,MAAAA,GAAAqX,GAAAzyD,GAEH41D,EAAA,SAAAxa,EAAAxwC,GACA,IAAA6M,EAAA2jC,EAAAqX,GAAA,CAEA,IAAAliD,EAAA6qC,GAAA,QAEA,KAAAxwC,EAAA,QAEA6qD,GAAAra,GAEG,MAAAA,GAAAqX,GAAAiD,GAGHG,EAAA,SAAAza,GAEA,MADAma,IAAAO,EAAAC,MAAAxlD,EAAA6qC,KAAA3jC,EAAA2jC,EAAAqX,IAAAgD,EAAAra,GACAA,GAEA0a,EAAA/1D,EAAAD,SACA4yD,IAAAD,EACAsD,MAAA,EACAJ,UACAC,UACAC,aAMA,SAAA91D,EAAAD,EAAAH,GAGA,GAAA6tD,GAAA7tD,EAAA,IACA8tD,EAAA9tD,EAAA,IACA+tD,EAAA/tD,EAAA,GACAI,GAAAD,QAAA,SAAAs7C,GACA,GAAAnhC,GAAAuzC,EAAApS,GACA6S,EAAAR,EAAA1S,CACA,IAAAkT,EAKA,IAJA,GAGA/iD,GAHA8qD,EAAA/H,EAAA7S,GACA8S,EAAAR,EAAA3S,EACA/6C,EAAA,EAEAg2D,EAAAlzD,OAAA9C,GAAAkuD,EAAAhuD,KAAAk7C,EAAAlwC,EAAA8qD,EAAAh2D,OAAAia,EAAAjX,KAAAkI,EACG,OAAA+O,KAMH,SAAAla,EAAAD,EAAAH,GAGA,GAAAsgD,GAAAtgD,EAAA,GACAI,GAAAD,QAAAuK,MAAAC,SAAA,SAAAgoB,GACA,eAAA2tB,EAAA3tB,KAMA,SAAAvyB,EAAAD,EAAAH,GAGA,GAAAkgD,GAAAlgD,EAAA,IACAyzD,EAAAzzD,EAAA,IAAAo7C,EACAr4C,KAAiBA,SAEjBuzD,EAAA,gBAAAvmC,iBAAAjvB,OAAAyiC,oBACAziC,OAAAyiC,oBAAAxT,WAEAwmC,EAAA,SAAA9a,GACA,IACA,MAAAgY,GAAAhY,GACG,MAAAvuC,GACH,MAAAopD,GAAAnnD,SAIA/O,GAAAD,QAAAi7C,EAAA,SAAAK,GACA,MAAA6a,IAAA,mBAAAvzD,EAAAxC,KAAAk7C,GAAA8a,EAAA9a,GAAAgY,EAAAvT,EAAAzE,MAMA,SAAAr7C,EAAAD,EAAAH,GAEA,GAAA+tD,GAAA/tD,EAAA,IACAu9C,EAAAv9C,EAAA,IACAkgD,EAAAlgD,EAAA,IACAk7C,EAAAl7C,EAAA,IACA8X,EAAA9X,EAAA,IACAi7C,EAAAj7C,EAAA,IACAwzD,EAAA1yD,OAAAqQ,wBAEAhR,GAAAi7C,EAAAp7C,EAAA,IAAAwzD,EAAA,SAAAnY,EAAAC,GAGA,GAFAD,EAAA6E,EAAA7E,GACAC,EAAAJ,EAAAI,GAAA,GACAL,EAAA,IACA,MAAAuY,GAAAnY,EAAAC,GACG,MAAApuC,IACH,GAAA4K,EAAAujC,EAAAC,GAAA,MAAAiC,IAAAwQ,EAAA3S,EAAA76C,KAAA86C,EAAAC,GAAAD,EAAAC,MAMA,SAAAl7C,EAAAD,KAMA,SAAAC,EAAAD,EAAAH,GAEAA,EAAA,sBAKA,SAAAI,EAAAD,EAAAH,GAEAA,EAAA,mBAKA,SAAAI,EAAAD,EAAAH,GAEAA,EAAA,IACAA,EAAA,IACAI,EAAAD,QAAAH,EAAA,KAKA,SAAAI,EAAAD,EAAAH,GAEA,GAAAg7C,GAAAh7C,EAAA,IACAkB,EAAAlB,EAAA,GACAI,GAAAD,QAAAH,EAAA,GAAAw2D,YAAA,SAAA/a,GACA,GAAAgb,GAAAv1D,EAAAu6C,EACA,sBAAAgb,GAAA,KAAAjb,WAAAC,EAAA,oBACA,OAAAT,GAAAyb,EAAAl2D,KAAAk7C,MAMA,SAAAr7C,EAAAD,EAAAH,GAGA,GAAAsgD,GAAAtgD,EAAA,IACA2/C,EAAA3/C,EAAA,kBAEA02D,EAA+C,aAA/CpW,EAAA,WAA2B,MAAAv0C,eAG3B4qD,EAAA,SAAAlb,EAAAlwC,GACA,IACA,MAAAkwC,GAAAlwC,GACG,MAAA2B,KAGH9M,GAAAD,QAAA,SAAAs7C,GACA,GAAAJ,GAAA+S,EAAA9P,CACA,YAAA/0C,KAAAkyC,EAAA,mBAAAA,EAAA,OAEA,iBAAA2S,EAAAuI,EAAAtb,EAAAv6C,OAAA26C,GAAAkE,IAAAyO,EAEAsI,EAAApW,EAAAjF,GAEA,WAAAiD,EAAAgC,EAAAjF,KAAA,kBAAAA,GAAAub,OAAA,YAAAtY,IAMA,SAAAl+C,EAAAD,EAAAH,GAEAI,EAAAD,SAAkB2H,QAAA9H,EAAA,KAAAoB,YAAA,IAIlB,SAAAhB,EAAAD,EAAAH,GAEAA,EAAA,IACA,IAAA62D,GAAA72D,EAAA,GAAAc,MACAV,GAAAD,QAAA,SAAAs7C,EAAAlwC,EAAAurD,GACA,MAAAD,GAAA91D,eAAA06C,EAAAlwC,EAAAurD,KAMA,SAAA12D,EAAAD,EAAAH,GAEA,GAAA29C,GAAA39C,EAAA,GAEA29C,KAAAQ,EAAAR,EAAAI,GAAA/9C,EAAA,cAAqEe,eAAAf,EAAA,GAAAo7C,KAKrE,SAAAh7C,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAo6C,GAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACAyT,EAAA/2D,EAAA,GACAg3D,EAAAh3D,EAAAmB,EAAA41D,GACAE,EAAAj3D,EAAA,GACAk3D,EAAAl3D,EAAA,GA8DAiJ,GAAA,SACAtI,KAAA,SACAgsB,WAAA42B,OAA+FyT,EAAAlrD,EAAAnL,KAAAq2D,EAAAlrD,GAC/FkI,QAAAkjD,EAAA,GACA7jC,cAAA,EACAtgB,OACA7J,OAAAwxB,OAAAvwB,QACAjG,MACAA,KAAAiG,OACArC,QAAA,QAEAqvD,eAAA9vD,QACA+vD,YACAlzD,KAAAmD,QACAS,QAAA,WACA,MAAAmvD,GAAA,EAAAjd,0BAIA/qC,KAAA,WACA,OACA0gD,SAAA3sD,KAAAkG,MACAqzC,QAAAv5C,KAAAkB,KACAmzD,gBAAAr0D,KAAA+4C,cAAAkb,EAAA,EAAA9d,yBACAme,mBAAA,EACAxa,YAAA,aAAA95C,KAAAkB,KAAA,qBAIA0mB,UACA2sC,YAAA,WACA,OAAAv0D,KAAAw0D,aAAAx0D,KAAA24C,MACA8b,cAAAz0D,KAAA44C,SACA8b,aAAA10D,KAAA6hB,QACA8yC,eAAA30D,KAAA40D,cAGAC,aAAA,WACA,OAAA70D,KAAAs5C,WAAAt5C,KAAA24C,MAAiDmc,aAAA90D,KAAA64C,WAEjDkc,aAAA,WACA,MAAA/0D,MAAAm0D,gBAAAn0D,KAAA6hB,SAAA7hB,KAAAs5C,YAOAkb,aAAA,WACA,MAAAx0D,MAAA84C,MAAA94C,KAAA+0D,aACA,kCACa/0D,KAAA84C,MAAA94C,KAAA+0D,aACb,kBACa/0D,KAAA84C,KACb,qBADa,IASbkc,eAAA,WACA,OAAAh1D,KAAAs5C,YACA,iBACA,aACA,iBACA,oBACA,eACA,mBACA,kBACA,gBAQAsb,WAAA,WACA,QAAA50D,KAAAw5C,eAOAyb,oBAAA,WACA,MAAAj1D,MAAAs0D,kBAAA,iBAMAY,YAAA,WACA,sBAAAl1D,MAAA2sD,SACA3sD,KAAA2sD,SAAAxsD,OACa,gBAAAH,MAAA2sD,SACb3sD,KAAA2sD,SAAA5sD,WAAAI,OAEA,IAGA2nB,OAMA5hB,MAAA,SAAAktB,GACApzB,KAAA2sD,SAAAv5B,GAQAu5B,SAAA,SAAAzmD,GACAlG,KAAA8lB,MAAA,QAAA5f,IACAlG,KAAAi5C,SAAAj5C,KAAAg6C,uBAGAxyB,SAKA2tC,yBAAA,WACA,GAAAtb,GAAA75C,IAEAA,MAAAs0D,mBAAAt0D,KAAAs0D,kBACAt0D,KAAAu5C,QAAAv5C,KAAAs0D,kBAAA,kBAEAt0D,KAAAwjC,UAAA,WACAqW,EAAA/1B,MAAA8+B,MAAAhJ,WASAwb,QAAA,SAAA3/C,GACA,GAAAmsC,GAAA5hD,IAEAA,MAAAwjC,UAAA,WACAoe,EAAA+K,SAAAl3C,EAAAnK,OAAApF,YAQA,SAAA9I,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAmvD,GAAAr4D,EAAA,EAUAiJ,GAAA,SACAtI,KAAA,QACAoS,OACA7O,KAAAiG,OACAmuD,KAAAnuD,OACA2xC,KAAA3xC,OACAwxC,KAAAxxC,OACAouD,WAAApuD,OACAquD,YAAAruD,OACAsuD,KAAApxD,SAEAujB,UAMA8tC,QAAA,WACA,MAAA11D,MAAAy1D,KAQA,QAAAz1D,KAAA21D,QAAA31D,KAAA21D,QAAA,IAAA31D,KAAA84C,KAAA,MAAA94C,KAAA41D,oBAAA51D,KAAA84C,MAPA,QAAA94C,KAAA21D,QACA31D,KAAA21D,QAAA,IAAA31D,KAAA84C,KAEA,MAAA94C,KAAA84C,MAMA6c,QAAA,WACA,MAAA31D,MAAAs1D,MAAAD,EAAA,EAAAzf,iBAEA2D,QAAA,WACA,GAAAv5C,KAAAkB,KAAA,CAEA,GAAA20D,GAAA71D,KAAAkB,KAAAgH,MAAA,IACA,IAAA2tD,EAAA11D,OAEA,kBAAA01D,EAAA,KAEAC,cAAA,WACA,MAAA91D,MAAAu1D,YAAAv1D,KAAA+1D,kBAEAA,iBAAA,WACA,GAAAC,GAAA,QAAAh2D,KAAA21D,QAAA,mBACAM,EAAA,QAAAj2D,KAAA21D,QAAA,mBACAO,EAAA,QAAAl2D,KAAA21D,QAAA,kBACA,QAAA31D,KAAA24C,MACA,eACA,MACA,iBACA,MAAAsd,EACA,gBACA,MAAAC,EACA,SACA,MAAAF,MAIAxuC,SAIAouC,oBAAA,SAAA1vD,GACA,OAAAA,GACA,YACA,aACA,mBACA,mBACA,oBACA,oBACA,aACA,4BACA,oBACA,0BACA,gBACA,gBACA,qBACA,mBACA,oBACA,kBACA,oBACA,kBACA,WACA,WACA,eACA,iBACA,iBACA,kBACA,eACA,gBACA,SACA,MAAAA,QAQA,SAAA9I,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,QACAsO,YAAA,OACA1L,OAAAu1C,EAAA5c,QAAA4c,EAAAxd,QACG36B,EAAA,KACH4C,OAAAu1C,EAAAR,QAAAQ,EAAAT,QAAAS,EAAAL,cAAAK,EAAAX,kBAECxwD,qBAID,SAAA5H,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,UACA1L,MAAAu1C,EAAA5B,cACG,aAAA4B,EAAAj1D,KAAA8c,EAAA,QAAAm4C,EAAAp5C,IACHoR,IAAA,QACA7B,YAAA,QACA1L,MAAAu1C,EAAAtB,aACA1+C,OACAjV,KAAAi1D,EAAA5c,QACAR,aAAAod,EAAA9B,gBACArb,UAAAmd,EAAAnd,WAEAn+B,UACA3U,MAAAiwD,EAAAxJ,UAEAv3C,IACAwtC,MAAAuT,EAAAf,QACAiB,KAAAF,EAAApc,OACAH,MAAAuc,EAAAlc,UAEG,QAAAkc,EAAA9wC,QAAA,IAAArH,EAAA,WAAAm4C,EAAAp5C,IACHoR,IAAA,WACA7B,YAAA,WACA1L,MAAAu1C,EAAAtB,aACA1+C,OACA6iC,UAAAmd,EAAAnd,WAEAn+B,UACA3U,MAAAiwD,EAAAxJ,UAEAv3C,IACAwtC,MAAAuT,EAAAf,QACAiB,KAAAF,EAAApc,OACAH,MAAAuc,EAAAlc,UAEG,WAAAkc,EAAA9wC,QAAA,IAAA8wC,EAAAn5C,GAAA,KAAAm5C,EAAA,KAAAn4C,EAAA,UACHsO,YAAA,UACAnW,OACA2iC,KAAAqd,EAAArd,KACAwc,KAAAa,EAAA7b,SACA3B,KAAAwd,EAAAzc,YAEGyc,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAAt0C,UAAAs0C,EAAAhC,iBAAAgC,EAAA7c,WAiBA6c,EAAAl5C,KAjBAe,EAAA,UACHsO,YAAA,WACA1L,OACA01C,eAAAH,EAAAhC,gBAEAh+C,OACA2iC,KAAAqd,EAAAhC,eAAAgC,EAAAlB,oBAAAkB,EAAAnB,eACAM,KAAAa,EAAA7b,SACA3B,KAAAwd,EAAAzc,SACAx4C,KAAAi1D,EAAAhC,eAAA,aAAAgC,EAAA7c,WACAmc,KAAA,IAEA12C,UACAw3C,MAAA,SAAAv7C,GACAm7C,EAAAhB,yBAAAn6C,OAGGm7C,EAAAn5C,GAAA,KAAAm5C,EAAAnd,WAAAmd,EAAA/B,YAAA,WAAA+B,EAAAj1D,KAAA8c,EAAA,SACHsO,YAAA,eACA1L,OACA41C,gBAAAL,EAAAjd,aAEGid,EAAAn5C,GAAA,aAAAm5C,EAAA55C,GAAA45C,EAAAjB,aAAA,MAAAiB,EAAA55C,GAAA45C,EAAAnd,WAAA,YAAAmd,EAAAl5C,MAAA,IACFjY,qBAID,SAAA5H,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,uBACA1L,OACA6zC,cAAA0B,EAAAvd,YAEG56B,EAAA,UAAAm4C,EAAAp5C,IACHoR,IAAA,QACAhY,OACAwiC,KAAAwd,EAAAxd,KACA92B,QAAAs0C,EAAAt0C,QACAg3B,QAAAsd,EAAAtd,QACAC,KAAAqd,EAAArd,KACA2d,YAAAN,EAAA7b,SACAtB,UAAAmd,EAAAnd,UACAD,aAAA,OAEA3jC,IACAwkC,MAAAuc,EAAA/G,QACAiH,KAAAF,EAAApc,QAEAh7B,UACA23C,MAAA,SAAA17C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,SAAA37C,EAAAzS,KAAqF,WACrFyS,GAAA47C,iBACAT,EAAArV,UAAA,GAEA+V,SAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,QAAA37C,EAAAzS,KAAoF,WACpF4tD,GAAAjI,WAAAlzC,IACO,SAAAA,GACP,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAAuF,WACvFyS,GAAA47C,iBACAT,EAAAlI,aAAAjzC,IACO,SAAAA,GACP,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,QAAA37C,EAAAzS,KAAoF,WACpFyS,GAAA47C,iBACAT,EAAAvH,UAAA,OACO,SAAA5zC,GACP,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,UAAA37C,EAAAzS,KAAsF,WACtFyS,GAAA47C,iBACAT,EAAAvH,UAAA,WAGA/vC,OACA3Y,MAAAiwD,EAAA,SACAt2C,SAAA,SAAAi3C,GACAX,EAAAxJ,SAAAmK,GAEA30B,WAAA,aAEG,UAAAg0B,EAAA9wC,QAAA,IAAA8wC,EAAAn5C,GAAA,KAAAgB,EAAA,cACH7H,OACAxY,KAAA,UAEGqgB,EAAA,OACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAArV,WAAAqV,EAAAlqD,KAAA9L,OAAA,GAAAg2D,EAAA5I,cAAA4I,EAAA1I,eACAtrB,WAAA,mEAEAhU,IAAA,WACA7B,YAAA,gBACA1L,OACAm2C,iBAAAZ,EAAAvJ,8BAEG5uC,EAAA,OACHsO,YAAA,qBACG6pC,EAAA,cAAAn4C,EAAA,OACHsO,YAAA,kBACG6pC,EAAA15C,GAAA,cAAA05C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA35C,GAAA25C,EAAA,cAAA/6B,EAAA54B,GACH,MAAAwb,GAAA,KACAzV,IAAA/F,EACA8pB,YAAA,gBACA1L,OACAo2C,aAAA57B,IAAA+6B,EAAAzJ,SAEAt3C,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAAn7B,YAAAI,OAGK+6B,EAAA,eAAAA,EAAA15C,GAAA,gBACL2e,SACA54B,UACKwb,EAAA,QACLnD,UACA0Y,UAAA4iC,EAAA55C,GAAA45C,EAAA96B,SAAAD,GAAA,QAEK,KACF+6B,EAAAn5C,GAAA,SAAAm5C,EAAAlqD,KAAA9L,QAAAg2D,EAAA5I,aAAAvvC,EAAA,OACHsO,YAAA,8BACG6pC,EAAA15C,GAAA,aAAA05C,EAAAl5C,MAAA,YACFjY,qBAID,SAAA5H,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA+wD,GAAAj6D,EAAA,GACAk6D,EAAAl6D,EAAAmB,EAAA84D,EAyBAhxD,GAAA,SACAtI,KAAA,YACAoS,OACA7J,OAAAiB,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAquD,aAAAhwD,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACA5H,KAAAiG,OACAunC,SAAArqC,QACA+yD,SAAA/yD,QACA1G,KAAAwJ,OACAwxC,KAAAxxC,OACAkwD,WACAn2D,MAAAiG,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAhE,SAAA,GAEAwyD,YACAp2D,MAAAiG,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAhE,SAAA,IAGAmH,KAAA,WACA,OACA0gD,SAAA3sD,KAAAkG,QAIA4hB,OAIA5hB,MAAA,SAAAktB,GACApzB,KAAA2sD,SAAAv5B,GAMAu5B,SAAA,SAAAzmD,GACAlG,KAAA8lB,MAAA,QAAA5f,OAOA,SAAA9I,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,SACAmQ,IAAA,QACA7B,YAAA,sBACA1L,OAAAu1C,EAAAxd,MACA4e,cAAApB,EAAAznB,WAEAv4B,OACAu4B,SAAAynB,EAAAznB,SACA8oB,UAAArB,EAAAznB,UAAA,GAEAt5B,IACAyhD,QAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,MAAA4tD,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAA0I,WAC1IyS,GAAA47C,iBACAT,EAAAryC,MAAA2zC,MAAAlB,YAGGv4C,EAAA,SACH1N,aACA3S,KAAA,QACAwyB,QAAA,UACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEAhsB,OACAjV,KAAA,WACAwtC,SAAAynB,EAAAznB,SACA0oB,SAAAjB,EAAAiB,SACAz5D,KAAAw4D,EAAAx4D,KACA+5D,aAAAvB,EAAAkB,UACAM,cAAAxB,EAAAmB,YAEAz8C,UACA3U,MAAAiwD,EAAAgB,YACAS,QAAAlwD,MAAAC,QAAAwuD,EAAAxJ,UAAAwJ,EAAAx5C,GAAAw5C,EAAAxJ,SAAAwJ,EAAAgB,cAAA,EAAAhB,EAAAz5C,GAAAy5C,EAAAxJ,SAAAwJ,EAAAkB,YAEAjiD,IACAgd,OAAA,SAAApX,GACA,GAAA68C,GAAA1B,EAAAxJ,SACAmL,EAAA98C,EAAA1P,OACAysD,EAAAD,EAAAF,QAAAzB,EAAA,UAAAA,EAAA,UACA,IAAAzuD,MAAAC,QAAAkwD,GAAA,CACA,GAAAf,GAAAX,EAAAgB,YACAa,EAAA7B,EAAAx5C,GAAAk7C,EAAAf,EACAgB,GAAAF,QACAI,EAAA,IAAA7B,EAAAxJ,SAAAkL,EAAA14D,QAAA23D,KAEAkB,GAAA,IAAA7B,EAAAxJ,SAAAkL,EAAA1rD,MAAA,EAAA6rD,GAAA74D,OAAA04D,EAAA1rD,MAAA6rD,EAAA,SAGA7B,GAAAxJ,SAAAoL,MAIG5B,EAAAn5C,GAAA,KAAAgB,EAAA,QACHsO,YAAA,QACA1L,MAAAu1C,EAAAj1D,OACGi1D,EAAAn5C,GAAA,KAAAgB,EAAA,QACHsO,YAAA,kBACG6pC,EAAA15C,GAAA,kBACFzX,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA+wD,GAAAj6D,EAAA,GACAk6D,EAAAl6D,EAAAmB,EAAA84D,EAuBAhxD,GAAA,SACAtI,KAAA,kBACAoS,OACA7J,OAAAiB,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAquD,aAAAhwD,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACA4lC,SAAArqC,QACA1G,KAAAwJ,OACAwxC,KAAAxxC,OACAjG,MACAA,KAAAiG,OACArC,QAAA,eAGAmH,KAAA,WACA,OACA0gD,SAAA3sD,KAAAkG,QAIA0hB,UACAgwC,QAAA,WACA,MAAAlwD,OAAAC,QAAA3H,KAAA2sD,UACA3sD,KAAA2sD,SAAAvkD,QAAApI,KAAAm3D,cAAA,EAEAn3D,KAAA2sD,WAAA3sD,KAAAm3D,cAGArvC,OAIA5hB,MAAA,SAAAktB,GACApzB,KAAA2sD,SAAAv5B,GAMAu5B,SAAA,SAAAzmD,GACAlG,KAAA8lB,MAAA,QAAA5f,OAOA,SAAA9I,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,YACGtO,EAAA,SACHmQ,IAAA,QACA7B,YAAA,6BACA1L,OAAAu1C,EAAAyB,QAAAzB,EAAAj1D,KAAA,KAAAi1D,EAAAxd,MACA4e,cAAApB,EAAAznB,WAEAv4B,OACAu4B,SAAAynB,EAAAznB,SACA8oB,UAAArB,EAAAznB,UAAA,GAEAt5B,IACAyhD,QAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,MAAA4tD,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAA0I,WAC1IyS,GAAA47C,iBACAT,EAAAryC,MAAA2zC,MAAAlB,YAGGJ,EAAA15C,GAAA,WAAA05C,EAAAn5C,GAAA,KAAAgB,EAAA,SACH1N,aACA3S,KAAA,QACAwyB,QAAA,UACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEAhsB,OACAjV,KAAA,WACAwtC,SAAAynB,EAAAznB,SACA/wC,KAAAw4D,EAAAx4D,MAEAkd,UACA3U,MAAAiwD,EAAAgB,YACAS,QAAAlwD,MAAAC,QAAAwuD,EAAAxJ,UAAAwJ,EAAAx5C,GAAAw5C,EAAAxJ,SAAAwJ,EAAAgB,cAAA,EAAAhB,EAAA,UAEA/gD,IACAgd,OAAA,SAAApX,GACA,GAAA68C,GAAA1B,EAAAxJ,SACAmL,EAAA98C,EAAA1P,OACAysD,IAAAD,EAAAF,OACA,IAAAlwD,MAAAC,QAAAkwD,GAAA,CACA,GAAAf,GAAAX,EAAAgB,YACAa,EAAA7B,EAAAx5C,GAAAk7C,EAAAf,EACAgB,GAAAF,QACAI,EAAA,IAAA7B,EAAAxJ,SAAAkL,EAAA14D,QAAA23D,KAEAkB,GAAA,IAAA7B,EAAAxJ,SAAAkL,EAAA1rD,MAAA,EAAA6rD,GAAA74D,OAAA04D,EAAA1rD,MAAA6rD,EAAA,SAGA7B,GAAAxJ,SAAAoL,OAIG,MACF/yD,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,IAe1DD,EAAA,SACAtI,KAAA,YACAoS,OACA6/B,MACA1uC,KAAAmD,QACAS,SAAA,GAEAmzD,WACA/2D,KAAAiG,OACArC,QAAA,SAGAmH,KAAA,WACA,OACAisD,OAAAl4D,KAAA4vC,OAIA9nB,OACA8nB,KAAA,SAAA1pC,GACAlG,KAAAk4D,OAAAhyD,IAGAshB,SAIAsoB,OAAA,WACA9vC,KAAAk4D,QAAAl4D,KAAAk4D,OACAl4D,KAAA8lB,MAAA,cAAA9lB,KAAAk4D,QACAl4D,KAAA8lB,MAAA9lB,KAAAk4D,OAAA,oBAOA,SAAA96D,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,aACGtO,EAAA,OACHsO,YAAA,mBACAlX,IACAmhD,MAAAJ,EAAArmB,UAEGqmB,EAAA15C,GAAA,gBACHmzB,KAAAumB,EAAA+B,UACG,GAAA/B,EAAAn5C,GAAA,KAAAgB,EAAA,cACH7H,OACAxY,KAAAw4D,EAAA8B,aAEGj6C,EAAA,OACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAA,OACAh0B,WAAA,WAEA7V,YAAA,qBACG6pC,EAAA15C,GAAA,sBACFzX,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAqBAiyD,GArBA7X,EAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACA8X,EAAAp7D,EAAA,IACAq7D,EAAAr7D,EAAA,GACAs7D,EAAAt7D,EAAA,GACAu7D,EAAAv7D,EAAA,IACAw7D,EAAAx7D,EAAAmB,EAAAo6D,GACAE,EAAAz7D,EAAA,IACA07D,EAAA17D,EAAAmB,EAAAs6D,GACAE,EAAA37D,EAAA,IACA47D,EAAA57D,EAAAmB,EAAAw6D,GACAE,EAAA77D,EAAA,IACA87D,EAAA97D,EAAAmB,EAAA06D,GACAE,EAAA/7D,EAAA,IACAg8D,EAAAh8D,EAAAmB,EAAA46D,GACAE,EAAAj8D,EAAA,GACAk8D,EAAAl8D,EAAAmB,EAAA86D,GACAE,EAAAn8D,EAAA,KACAo8D,EAAAp8D,EAAAmB,EAAAg7D,EA4JAlzD,GAAA,SACAtI,KAAA,cACAgsB,YAAAwuC,KAAiC5X,IAAA4X,EAAAiB,EAAAtwD,EAAAnL,KAAAy7D,EAAAtwD,GAAAy3C,IAAA4X,EAAAS,EAAA9vD,EAAAnL,KAAAi7D,EAAA9vD,GAAAy3C,IAAA4X,EAAAW,EAAAhwD,EAAAnL,KAAAm7D,EAAAhwD,GAAAy3C,IAAA4X,EAAAa,EAAAlwD,EAAAnL,KAAAq7D,EAAAlwD,GAAAy3C,IAAA4X,EAAAe,EAAApwD,EAAAnL,KAAAu7D,EAAApwD,GAAAy3C,IAAA4X,EAAAK,EAAA1vD,EAAAnL,KAAA66D,EAAA1vD,GAAAy3C,IAAA4X,EAAAO,EAAA5vD,EAAAnL,KAAA+6D,EAAA5vD,GAAAqvD,GACjCnnD,QAAAonD,EAAA,GACA/nC,cAAA,EACAtgB,OACA7J,MAAAiE,KACAkvD,UACAn4D,KAAAwG,MACA5C,QAAA,WACA,MAAA4C,OAAAC,QAAA2wD,EAAA,EAAAhiB,iBACAgiB,EAAA,EAAAhiB,iBAEA,kCAIAgjB,YACAp4D,KAAAwG,MACA5C,QAAA,WACA,MAAA4C,OAAAC,QAAA2wD,EAAA,EAAA/hB,mBACA+hB,EAAA,EAAA/hB,mBAEA,iHAIAgjB,gBACAr4D,KAAAw2B,OACA5yB,QAAA,WACA,sBAAAwzD,GAAA,EAAA9hB,sBACA8hB,EAAA,EAAA9hB,sBAEA,IAIAgjB,OAAAn1D,QACAo1D,QAAAtvD,KACAuvD,QAAAvvD,KACAwvD,YAAAxvD,KACA+xB,YAAA/0B,OACAyyD,UACA14D,KAAAmD,QACAS,SAAA,GAEA4pC,UACAxtC,KAAAmD,QACAS,SAAA,GAEA+0D,kBAAAnyD,MACAoyD,wBACA54D,KAAAwG,MACA5C,QAAA,WACA,MAAAwzD,GAAA,EAAA7hB,gCAGAsjB,gBAAAryD,MACAsyD,eACA94D,KAAA08B,SACA94B,QAAA,SAAAm1D,GACA,qBAAA3B,GAAA,EAAAliB,qBACA,MAAAkiB,GAAA,EAAAliB,qBAAA6jB,EAEA,IAAAC,GAAAD,EAAAE,cAAA,KAAAF,EAAAG,WAAA,OAAAH,EAAAI,SAEA,OADA,IAAAlwD,MAAA+vD,GACAI,uBAIAC,YACAr5D,KAAA08B,SACA94B,QAAA,SAAAm1D,GACA,wBAAA3B,GAAA,EAAAjiB,kBACAiiB,EAAA,EAAAjiB,kBAAA4jB,GAEA,GAAA9vD,WAAAqwD,MAAAP,MAIAQ,cACAv5D,KAAAmD,QACAS,QAAA,WACA,MAAAwzD,GAAA,EAAAzhB,gCAGAqK,SAAA/5C,OACA+9B,OAAAx9B,MACAgzD,YACAx5D,KAAAiG,OACArC,QAAA,SAGAmH,KAAA,WACA,GAAA0tD,GAAA35D,KAAAkG,OAAAlG,KAAA25D,aAAA,GAAAxvD,KAEA,QACAwwD,aAAA36D,KAAAkG,MACA00D,iBACAC,MAAAlB,EAAAS,WACAU,KAAAnB,EAAAQ,eAEArgB,YAAA,QACAihB,eAAA,IAIAnzC,UAKAozC,YAAA,WAMA,OALAC,GAAAj7D,KAAA05D,QAAA15D,KAAA05D,QAAAS,cAAA/yD,KAAAuH,KAAA,GAAAxE,OAAAgwD,cAAAn6D,KAAA46D,gBAAAE,MAAA,EAEAI,EAAAl7D,KAAAy5D,QAAAz5D,KAAAy5D,QAAAU,cAAA,KAEAgB,KACA99D,EAAA69D,EAAsC79D,GAAA49D,EAAiB59D,IACvD89D,EAAA96D,KAAAhD,EAGA,OAAA89D,GAAAC,WAEAC,aAAA,WACA,QAAAr7D,KAAAy5D,SACA,GAAAtvD,MAAAnK,KAAA46D,gBAAAE,KAAA96D,KAAA46D,gBAAAC,QACA,GAAA1wD,MAAAnK,KAAAy5D,QAAAU,cAAAn6D,KAAAy5D,QAAAW,aAGAkB,YAAA,WACA,QAAAt7D,KAAA05D,SACA,GAAAvvD,MAAAnK,KAAA46D,gBAAAE,KAAA96D,KAAA46D,gBAAAC,QACA,GAAA1wD,MAAAnK,KAAA05D,QAAAS,cAAAn6D,KAAA05D,QAAAU,aAGA9iB,SAAA,WACA,MAAAt3C,MAAAy6D,cAAApC,EAAA,EAAAzgB,QAGA9vB,OAKA6yC,aAAA,SAAAz0D,GACA,GAAAq1D,GAAAr1D,GAAA,GAAAiE,KACAnK,MAAA46D,iBACAC,MAAAU,EAAAnB,WACAU,KAAAS,EAAApB,eAEAn6D,KAAA8lB,MAAA,QAAA5f,GACAlG,KAAA8jB,MAAA4+B,WACA1iD,KAAA8jB,MAAA4+B,SAAA5B,UAAA,IAUA56C,MAAA,SAAAktB,GACApzB,KAAA26D,aAAAvnC,GAEApzB,KAAAi5C,SAAAj5C,KAAA8jB,MAAA8+B,MAAA5I,sBAEA2f,YAAA,SAAAzzD,GACAA,IACAlG,KAAA46D,iBACAC,MAAA30D,EAAAk0D,WACAU,KAAA50D,EAAAi0D,iBASAqB,wBAAA,SAAAt1D,GACAlG,KAAA8lB,MAAA,eAAA5f,IAEAu1D,uBAAA,SAAAv1D,GACAlG,KAAA8lB,MAAA,cAAA5f,KAGAshB,SAIAk0C,mBAAA,SAAAzB,GACAj6D,KAAA26D,aAAAV,GAOA0B,SAAA,SAAAz1D,GACA,GAAA+zD,GAAAj6D,KAAAu6D,WAAAr0D,EACA+zD,KAAApyD,MAAAoyD,GACAj6D,KAAA26D,aAAAV,GAGAj6D,KAAA26D,aAAA,KACA36D,KAAA8jB,MAAA8+B,MAAA+J,SAAA3sD,KAAA26D,eAQAiB,YAAA,SAAA11D,GACA,MAAAA,KAAA2B,MAAA3B,GACAlG,KAAAg6D,cAAA9zD,GAEA,MASA21D,eAAA,WACA77D,KAAA0uC,WAEA1uC,KAAA46D,gBAAAC,MAAA,EACA76D,KAAA46D,gBAAAC,OAAA,GAEA76D,KAAA46D,gBAAAC,MAAA,GACA76D,KAAA46D,gBAAAE,MAAA,KASAgB,eAAA,WACA97D,KAAA0uC,WAEA1uC,KAAA46D,gBAAAC,MAAA,GACA76D,KAAA46D,gBAAAC,OAAA,GAEA76D,KAAA46D,gBAAAC,MAAA,EACA76D,KAAA46D,gBAAAE,MAAA,KAQAiB,eAAA,SAAA71D,GACA,GAAA+zD,GAAA,GAAA9vD,MAAAjE,EACA,IAAAA,IAAA2B,MAAAoyD,GAAA,CACA,GAAAa,GAAAb,EAAAE,cACAU,EAAAZ,EAAAG,WAAA,EACA4B,EAAA/B,EAAAI,SACA,OAAAS,GAAA,KAAAD,EAAA,WAAAA,EAAA,KAAAmB,EAAA,WAAAA,EAEA,UAOAC,qBAAA,SAAAxmD,GACA,GAAAwkD,GAAAxkD,EAAAnK,OAAApF,KACAlG,MAAA26D,aAAAV,EAAA,GAAA9vD,MAAA8vD,EAAAzkC,QAAA,oBAOA,SAAAp4B,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAg2D,GAAAl/D,EAAA,IACAm/D,EAAAn/D,EAAAmB,EAAA+9D,GACAE,EAAAp/D,EAAA,GACAq/D,EAAAr/D,EAAAmB,EAAAi+D,EAmCAn2D,GAAA,SACAtI,KAAA,YACAoS,OACA7J,OACAhF,MAAAiG,OAAAuwB,OAAArzB,QAAAvG,OAAA4J,MAAA20D,EAAAvzD,EAAA80B,UACA94B,QAAA,MAEA4pC,SAAArqC,QACAi4D,UAAAj4D,QACAm1D,OAAAn1D,QACA68C,UACAhgD,KAAAiG,OACAg6C,UAAA,SAAAj7C,GACA,sDAAAkC,QAAAlC,IAAA,IAGAq2D,aACAr7D,KAAAmD,QACAS,SAAA,IAGAmH,KAAA,WACA,OACAkhB,SAAAntB,KAAAkG,MACA46C,UAAA,EACA0b,aAAA,IAIA50C,UACA2sC,YAAA,WACA,OAAAv0D,KAAAkhD,UACAqW,cAAAv3D,KAAA0uC,SACA+tB,eAAAz8D,KAAAs8D,UACAI,YAAA18D,KAAAw5D,OACAmD,YAAA38D,KAAA8gD,UAAA9gD,KAAAw5D,OACAoD,kBAAA58D,KAAA68D,iBAGAA,cAAA,WACA,MAAA78D,MAAAu8D,cAAAv8D,KAAAw5D,SAAAx5D,KAAAs8D,YAGAx0C,OAIA5hB,MAAA,SAAAktB,GACApzB,KAAAmtB,SAAAiG,GAOA0tB,SAAA,SAAA56C,GACAlG,KAAA8lB,MAAA,gBAAA5f,KAGAshB,SAOAs1C,WAAA,SAAA52D,GACAlG,KAAAmtB,WAAAjnB,IACAlG,KAAA8lB,MAAA,SAAA5f,GACAlG,KAAAmtB,SAAAjnB,GAEAlG,KAAA8lB,MAAA,QAAA5f,GACAlG,KAAA8gD,UAAA,GAOAic,cAAA,SAAAz4C,GACA,GAAAA,IAAAtkB,KAAA8jB,MAAAk5C,aAAA,QACA,IAAA14C,IAAAtkB,KAAA8jB,MAAA4X,QAAA,QAEA,QAAAn1B,KAAAvG,KAAA8jB,MAAAk5C,aAAA,CACA,GAAA9wD,GAAAlM,KAAA8jB,MAAAk5C,aAAAhQ,iBAAA,KACAC,GAAA,EACAC,GAAA,EACAC,MAAA5mD,EAEA,KACA,OAAA6mD,GAAA1P,EAAAye,IAAAjwD,KAAqI+gD,GAAAG,EAAA1P,EAAAxkC,QAAAC,MAAgE8zC,GAAA,GAGrM,GAAA3oC,IAFA8oC,EAAAlnD,MAGA,UAGiB,MAAAwM,GACjBw6C,GAAA,EACAC,EAAAz6C,EACiB,QACjB,KACAu6C,GAAAvP,EAAA2P,QACA3P,EAAA2P,SAEqB,QACrB,GAAAH,EACA,KAAAC,KAMA,OAAA5mD,KAAAvG,KAAA8jB,MAAA4X,QAAA,CACA,GAAAuhC,GAAAj9D,KAAA8jB,MAAA4X,QAAAsxB,iBAAA,KACAkQ,GAAA,EACAC,GAAA,EACAC,MAAA72D,EAEA,KACA,OAAA82D,GAAA1f,EAAAwe,IAAAc,KAAwIC,GAAAG,EAAA1f,EAAAzkC,QAAAC,MAAmE+jD,GAAA,GAG3M,GAAA54C,IAFA+4C,EAAAn3D,MAGA,UAGiB,MAAAwM,GACjByqD,GAAA,EACAC,EAAA1qD,EACiB,QACjB,KACAwqD,GAAAvf,EAAA0P,QACA1P,EAAA0P,SAEqB,QACrB,GAAA8P,EACA,KAAAC,KAMA,UAOAjP,eAAA,SAAA14C,GACAzV,KAAAw5D,QAEAx5D,KAAA+8D,cAAAtnD,EAAAnK,UAAAtL,KAAA8gD,UAAA,IAOAhR,OAAA,WACA,GAAA+J,GAAA75C,IAEAA,MAAA0uC,UAAA1uC,KAAAs8D,YAEAt8D,KAAA8gD,SAOA9gD,KAAA8gD,UAAA9gD,KAAA8gD,SAJA9gD,KAAAwjC,UAAA,WACAqW,EAAAiH,UAAAjH,EAAAiH,cAOAjd,QAAA,WACA,mBAAA9W,SACA/rB,SAAA0wB,iBAAA,QAAA1xB,KAAAmuD,iBAGAmB,cAAA,WACA,mBAAAviC,SACA/rB,SAAAywB,oBAAA,QAAAzxB,KAAAmuD,mBAOA,SAAA/wD,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,WACA1L,MAAAu1C,EAAA5B,cACG4B,EAAAqD,OASArD,EAAAl5C,KATAe,EAAA,OACHmQ,IAAA,UACA7B,YAAA,mBACAnW,OACAmnD,KAAA,UAEAloD,IACAmhD,MAAAJ,EAAArmB,UAEGqmB,EAAA15C,GAAA,eAAA05C,EAAAn5C,GAAA,KAAAgB,EAAA,cACH7H,OACAxY,KAAA,UAEGw4D,EAAA,cAAAn4C,EAAA,OACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEA7V,YAAA,eACG6pC,EAAAl5C,OAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,cACH7H,OACAxY,KAAA,UAEGqgB,EAAA,OACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,OAAAiwD,EAAAznB,WAAAynB,EAAArV,UAAAqV,EAAAmG,YAAAnG,EAAAqD,OACAr3B,WAAA,qDAEAhU,IAAA,eACA7B,YAAA,kBACGtO,EAAA,OACHsO,YAAA,qBACG6pC,EAAA15C,GAAA,wBACFzX,qBAID,SAAA5H,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA+wD,GAAAj6D,EAAA,GACAk6D,EAAAl6D,EAAAmB,EAAA84D,EAoBAhxD,GAAA,SACAtI,KAAA,gBACAoS,OACA7J,OACAhF,MAAAiG,OAAAuwB,OAAArzB,QAAAvG,OAAA4J,MAAAwvD,EAAApuD,EAAA80B,UACA94B,QAAA,MAEAy4D,UAAAl5D,QACAqqC,SAAArqC,QACAm5D,OAAAn5D,QACAo5D,YAAAp5D,QACAq5D,QAAAr5D,SAEAujB,UACA+1C,cAAA,WACA,OACApG,cAAAv3D,KAAA6S,QAAA67B,UAAA1uC,KAAA0uC,SACAkvB,iBAAA59D,KAAAy9D,YACAd,YAAA,OAAA38D,KAAAkG,OAAAlG,KAAAkG,QAAAlG,KAAA6S,QAAAsa,WAGA0wC,YAAA,WACA,OACAC,iBAAA99D,KAAA09D,QACAnG,cAAAv3D,KAAA0uC,SACAkvB,iBAAA59D,KAAAy9D,YACAd,YAAA,OAAA38D,KAAAkG,OAAAlG,KAAAkG,QAAAlG,KAAA6S,QAAAsa,SACA4wC,WAAA/9D,KAAA09D,UAOAM,YAAA,WACA,QAAAh+D,KAAA6S,QAAA67B,UAAA1uC,KAAAu9D,WAAAv9D,KAAA0uC,UAAA1uC,KAAAw9D,UAGAh2C,SAIAs1C,WAAA,WACA98D,KAAAg+D,cAEAh+D,KAAA6S,QAAAiqD,WAAA98D,KAAAkG,OACAlG,KAAA8lB,MAAA,YAGA+d,QAAA,WACA,IAAA7jC,KAAA6S,QAAAumC,MAAAojB,YAEA,KADAx8D,MAAAisB,WACA,GAAA3oB,OAAA,mDAOA,SAAAlG,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAD,GAAA,UAAAn4C,EAAA,MACAsO,YAAA,qBACG6pC,EAAAqH,QAAArH,EAAAuH,QAMA1/C,EAAA,OACH4C,MAAAu1C,EAAA0H,YACAzoD,IACAmhD,MAAAJ,EAAA2G,cAEG3G,EAAA15C,GAAA,eAXAuB,EAAA,KACHsO,YAAA,gBACA1L,MAAAu1C,EAAAwH,cACAvoD,IACAmhD,MAAAJ,EAAA2G,cAEG3G,EAAA15C,GAAA,gBAMFzX,qBAID,SAAA5H,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAo6C,GAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACA2d,EAAAjhE,EAAA,KACAkhE,EAAAlhE,EAAAmB,EAAA8/D,EA6CAh4D,GAAA,SACAtI,KAAA,SACAgsB,WAAA42B,OAA+F2d,EAAAp1D,EAAAnL,KAAAugE,EAAAp1D,GAC/FiH,OACA7O,KAAAiG,OACAswD,MAAAtwD,OACAg3D,SAAAh3D,OACA+yC,SAAA/yC,OAAAO,OACA02D,QAAA/5D,QACAg6D,eAAAh6D,QACA68C,SAAA/5C,OACAyxC,SAAAv0C,QACAi6D,WAAAj6D,QACAk6D,QACAr9D,KAAAmD,QACAS,SAAA,GAEA0wD,YAAAruD,QAEA8E,KAAA,WACA,OACAstC,QAAAv5C,KAAAkB,KACAu4C,WAAAz5C,KAAAk6C,QACAskB,eAAA,KACAnlB,UAAA,IAIAzxB,UACA2sC,YAAA,WACA,OAAAv0D,KAAAy+D,aACAhK,cAAAz0D,KAAA44C,SACA8lB,uBAAA1+D,KAAAq+D,eACAM,gBAAA3+D,KAAAs+D,cAWAG,YAAA,WACA,OAAAl4D,KAAAvG,KAAAkhD,SAAA,CAEA,GAAAA,GAAAlhD,KAAAkhD,SAAAh5C,MAAA,IACA,MAAAg5C,EAAA/gD,OAAA,IAEA,GAAAy+D,GAAA5+D,KAAAo+D,QAAA,2BAEA,OAAAp+D,MAAAkhD,SAAA0d,EAAA1d,EAAA,aAOA2d,iBAAA,WACA,MAAA7+D,MAAAy5C,YACA/xC,MAAAC,QAAA3H,KAAAy5C,YACAz5C,KAAAy5C,WAAAr1C,OAAA,SAAA8B,GACA,GAAAA,EACA,MAAAA,KAEqB1G,KAAA,UAKrBQ,KAAAy5C,aAIA3xB,OAIA5mB,KAAA,SAAAgF,GACAlG,KAAAu5C,QAAArzC,GAOAg0C,QAAA,SAAAh0C,GACAlG,KAAAy5C,WAAAvzC,IAGAshB,SAOAs3C,UAAA,WACA,GAAA9+D,KAAAo+D,QAAA,kBAEA,IAAAW,GAAA,CAMA,OALA/+D,MAAA0Z,OAAA5U,UACAi6D,EAAA/+D,KAAA0Z,OAAA5U,QAAAqyC,OAAA,SAAA95C,EAAAuZ,GACA,MAAAA,GAAA5K,IAAA3O,EAAA,EAAAA,GACiB,IAEjB0hE,EAAA,GAAA/+D,KAAAu+D,SAAAv+D,KAAAs+D,WACA,iBADA,KAKAv6B,QAAA,WACA,GAAA/jC,KAAAs+D,WAAA,CAEAt+D,KAAAwkB,IAAAwoC,iBAAA,uCACA7sD,OAAA,IACAH,KAAAw+D,eAAA,iBAQA,SAAAphE,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,IAE1DD,EAAA,SACAtI,KAAA,aACAoS,OACAmqC,SACAh5C,MAAAiG,SAEAjG,MACAA,MAAAiG,UAGApC,OAAA,SAAAiB,GACA,GAAA6zC,GAAA75C,IAEA,OAAAgG,GAAA,OAAyBmQ,OAASyK,MAAA,eAA0B5gB,KAAA0Z,OAAA5U,QAAAzF,IAAA,SAAAiH,GAE5D,MAAAA,GAAA0F,IAGA6tC,EAAAK,QACAl0C,EAAA,WAAqCmQ,OAAS+jC,QAAAL,EAAAK,QAAAh5C,KAAA24C,EAAA34C,QAA+CoF,IAE7FN,EAAA,WAAiCmQ,OAASjV,KAAA24C,EAAA34C,QAAuBoF,IALjEA,QAYA,SAAAlJ,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,QACA1L,OAAAu1C,EAAA5B,YAAA4B,EAAA2I,eACG3I,EAAA,WAAAn4C,EAAA,OACHsO,YAAA,cACA1L,OAAAu1C,EAAAX,YAAAW,EAAAqI,kBACGrI,EAAA,MAAAn4C,EAAA,SACHsO,YAAA,QACAnW,OACAk8C,IAAA8D,EAAAgI,YAEGhI,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAA45C,EAAAsB,OAAA,gBAAAtB,EAAAl5C,QAAAk5C,EAAA,MAAAn4C,EAAA,SACHsO,YAAA,QACA1L,MAAAu1C,EAAAX,YACAr/C,OACAk8C,IAAA8D,EAAAgI,YAEGhI,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAA45C,EAAAsB,OAAA,gBAAAtB,EAAAl5C,MAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA,WAAAn4C,EAAA,gBACH7H,OACA+jC,QAAAic,EAAA1c,WAAA0c,EAAA0I,iBAAA,GACA39D,KAAAi1D,EAAA5c,WAEG4c,EAAA15C,GAAA,gBAAA05C,EAAA15C,GAAA,YAAA05C,EAAAn5C,GAAA,KAAAm5C,EAAA1c,aAAA0c,EAAAmI,WAAAtgD,EAAA,KACHsO,YAAA,OACA1L,MAAAu1C,EAAA5c,QACA1+B,UACA0Y,UAAA4iC,EAAA55C,GAAA45C,EAAA0I,qBAEG1I,EAAAl5C,MAAA,IACFjY,qBAID,SAAA5H,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA+wD,GAAAj6D,EAAA,GACAk6D,EAAAl6D,EAAAmB,EAAA84D,GACA+H,EAAAhiE,EAAA,GACAiiE,EAAAjiE,EAAAmB,EAAA6gE,GACAE,EAAAliE,EAAA,GACAmiE,EAAAniE,EAAAmB,EAAA+gE,GACAhL,EAAAl3D,EAAA,GA4CAiJ,GAAA,SACAtI,KAAA,UACAgsB,WAAAs1C,OAA+FE,EAAAr2D,EAAAnL,KAAAwhE,EAAAr2D,GAC/FkI,QAAAkjD,EAAA,GACA7jC,cAAA,EACAtgB,OACA7J,OACAhF,MAAAiG,OAAAuwB,OAAArzB,QAAAvG,OAAA4J,MAAAwvD,EAAApuD,EAAA80B,UACA94B,QAAA,MAEAo3B,YAAA/0B,OACAmmB,SAAAjpB,QACA+6D,YAAAj4D,OAAAuwB,SAEAzrB,KAAA,WACA,OACAkhB,SAAAntB,KAAAkG,MACAm5D,WAAA,EACAvlB,YAAA,WAIAlyB,UACA03C,YAAA,WACA,OAAAt/D,KAAA24C,KAAA34C,KAAAs5C,YACAimB,eAAAv/D,KAAA44C,SACA8b,aAAA10D,KAAA6hB,QACA29C,cAAAx/D,KAAAstB,SACAwnC,aAAA90D,KAAA64C,QACA4mB,WAAA,OAAAz/D,KAAAmtB,aAIArF,OAMA5hB,MAAA,SAAAktB,GACApzB,KAAAmtB,SAAAiG,GACApzB,KAAAi5C,SAAAj5C,KAAAg6C,sBAQA7sB,SAAA,SAAAjnB,GACAlG,KAAA8lB,MAAA,QAAA5f,IACAlG,KAAAi5C,SAAAj5C,KAAAg6C,yBAOA,SAAA58C,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,UACA1L,OACA6zC,cAAA0B,EAAAvd,SAAA8mB,iBAAAvJ,EAAArd,QAEG96B,EAAA,QACHsO,YAAA,SACA1L,MAAAu1C,EAAAmJ,cACGthD,EAAA,SAAAm4C,EAAAp5C,IACHzM,aACA3S,KAAA,QACAwyB,QAAA,UACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEAhU,IAAA,SACAhY,OACAmX,SAAA6oC,EAAA7oC,SACAqrB,KAAAwd,EAAAiJ,YAEAhqD,IACAihD,KAAA,SAAAr7C,GACAm7C,EAAArwC,MAAA,OAAA9K,IAAAm7C,EAAAnc,sBAEAJ,MAAA,SAAA5+B,GACAm7C,EAAArwC,MAAA,QAAA9K,IAEAoX,OAAA,SAAApX,GACA,GAAA2kD,GAAAj4D,MAAAnJ,UAAA6F,OAAA7G,KAAAyd,EAAA1P,OAAArI,QAAA,SAAApF,GACA,MAAAA,GAAAsvB,WACS9tB,IAAA,SAAAxB,GAET,MADA,UAAAA,KAAAu1B,OAAAv1B,EAAAqI,OAGAiwD,GAAAhpC,SAAAnS,EAAA1P,OAAAgiB,SAAAqyC,IAAA,MAGG,SAAAxJ,EAAA9wC,QAAA,IAAA8wC,EAAA,YAAAn4C,EAAA,UACH7H,OACAgX,SAAA,GACAuhB,SAAA,GACAkxB,OAAA,IAEA/kD,UACA3U,MAAA,QAEGiwD,EAAAn5C,GAAA,qBAAAm5C,EAAA55C,GAAA45C,EAAAj6B,aAAA,oBAAAi6B,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA15C,GAAA,iBAAA05C,EAAAn5C,GAAA,KAAAm5C,EAAA,KAAAn4C,EAAA,UACHsO,YAAA,UACAnW,OACA2iC,KAAAqd,EAAArd,KACAwc,KAAAa,EAAA7b,SACA3B,KAAAwd,EAAAzc,YAEGyc,EAAAl5C,MAAA,IACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAo6C,GAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACAuf,EAAA7iE,EAAA,KACA8iE,EAAA9iE,EAAAmB,EAAA0hE,EAmCA55D,GAAA,SACAtI,KAAA,mBACAgsB,WAAA42B,OAA+Fuf,EAAAh3D,EAAAnL,KAAAmiE,EAAAh3D,GAC/FiH,OACA7J,MAAAiE,KACAkvD,SAAA3xD,MACA4xD,WAAA5xD,MACA6xD,eAAA7hC,OACAwN,OAAAx9B,MACAgzD,WAAAvzD,OACAsyD,QAAAtvD,KACAuvD,QAAAvvD,KACAilD,QAAAtxD,OACA4wC,SAAArqC,QACAw1D,kBAAAnyD,MACAoyD,uBAAApyD,MACAqyD,gBAAAryD,OAEAkgB,UACAm4C,gBAAA,WAGA,IAFA,GAAAA,MACAv9D,EAAAxC,KAAAu5D,eACAwG,EAAA5/D,OAAAH,KAAAq5D,SAAAl5D,QAAA,CACA,GAAA6/D,GAAAhgE,KAAAq5D,SAAA72D,EAAAxC,KAAAq5D,SAAAl5D,OACA4/D,GAAA1/D,KAAA2/D,GACAx9D,IAEA,MAAAu9D,IAEAE,UAAA,WACA,MAAAjgE,MAAAklC,QAAAllC,KAAAklC,OAAA/kC,QAOA+/D,kBAAA,WACA,IAAAlgE,KAAAklC,OAAA,QAIA,QAFAi7B,MAEA9iE,EAAA,EAA2BA,EAAA2C,KAAAklC,OAAA/kC,OAAwB9C,IAAA,CACnD,GAAAoY,GAAAzV,KAAAklC,OAAA7nC,EAEAoY,GAAAjX,eAAA,UACAiX,GAA6BwkD,KAAAxkD,IAE7BA,EAAAjX,eAAA,UACAiX,EAAAvU,KAAA,cAEAuU,EAAAwkD,KAAAG,aAAAp6D,KAAAovD,QAAAyL,OAAAplD,EAAAwkD,KAAAE,gBAAAn6D,KAAAovD,QAAA0L,MACAqF,EAAA9/D,KAAAoV,GAIA,MAAA0qD,KAGA34C,SAIAk0C,mBAAA,SAAAzB,GACAj6D,KAAA8lB,MAAA,QAAAm0C,IAOAmG,YAAA,SAAAC,EAAAxF,EAAAC,GAUA,OATAwF,GAAA,GAAAn2D,MAAA2wD,EAAAD,GAEA0F,KAEAC,EAAA,GAAAr2D,MAAA2wD,EAAAD,EAAAwF,GAAAI,SAEAjqC,EAAAgqC,GAAAxgE,KAAAu5D,eAAAiH,EAAAxgE,KAAAu5D,eAAA,EAAAv5D,KAAAu5D,eAAAiH,EAEAE,EAAA,EACArjE,EAAA,EAA2BA,EAAAm5B,EAASn5B,IACpCkjE,EAAAh2C,QAAA,GAAApgB,MAAAm2D,EAAAnG,cAAAmG,EAAAlG,WAAAiG,EAAAK,IACAA,GAGAH,GAAAlgE,KAAA,GAAA8J,MAAA2wD,EAAAD,EAAAwF,GAGA,KADA,GAAAM,GAAA,EACAJ,EAAApgE,OAAA,GACAogE,EAAAlgE,KAAA,GAAA8J,MAAA2wD,EAAAD,EAAAwF,EAAAM,IACAA,GAGA,OAAAJ,IAOAK,iBAAA,SAAA/F,EAAAC,GAMA,IALA,GAAA8F,MACAC,EAAA,GAAA12D,MAAA2wD,EAAAD,EAAA,KAAAR,UAEAyG,EAAA,EAEAA,GAAAD,EAAA,IACA,GAAAE,GAAA/gE,KAAAogE,YAAAU,EAAAjG,EAAAC,GACAkG,GAAA,CAEAD,GAAAvpD,QAAA,SAAAwkD,GACAA,EAAA5B,aAAAS,IACAmG,GAAA,KAIAA,GACAJ,EAAAvgE,KAAA0gE,GAGAD,GAAA,EAGA,MAAAF,IAEAK,iBAAA,SAAAC,EAAA1+D,GACA,IAAAxC,KAAAkgE,kBAAA//D,OAAA,QAEA,IAAAghE,MAEAP,IACAA,GAAA5gE,KAAA4gE,iBAAA5gE,KAAAovD,QAAAyL,MAAA76D,KAAAovD,QAAA0L,KAEA,QAAAp9D,GAAA,EAA2BA,EAAAkjE,EAAAp+D,GAAArC,OAAoCzC,IAC/D,OAAAwM,GAAA,EAA+BA,EAAAlK,KAAAkgE,kBAAA//D,OAAmC+J,IAAA,CAClE,GAAAg2D,GAAAlgE,KAAAkgE,kBAAAh2D,GAAA+vD,KAAA7vD,SACA81D,KAAAU,EAAAp+D,GAAA9E,GAAA0M,WACA+2D,EAAA9gE,KAAAL,KAAAkgE,kBAAAh2D,IAKA,MAAAi3D,OAOA,SAAA/jE,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,IAsC1DD,EAAA,SACAtI,KAAA,sBACAoS,OACAqxD,aAAAj3D,KACA+2D,MACAhgE,KAAAwG,MACA0vD,UAAA,GAEAyD,OACA35D,KAAAw2B,OACA0/B,UAAA,GAEAqC,QAAAtvD,KACAuvD,QAAAvvD,KACAukC,SAAArqC,QACAw1D,kBAAAnyD,MACAoyD,uBAAApyD,MACAqyD,gBAAAryD,MACAw9B,OAAAx9B,MACAgzD,WAAAvzD,QAEAqgB,SAKA65C,eAAA,SAAArF,GACA,GAAAsF,KAYA,IAVAthE,KAAAy5D,SACA6H,EAAAjhE,KAAA27D,GAAAh8D,KAAAy5D,SAGAz5D,KAAA05D,SACA4H,EAAAjhE,KAAA27D,GAAAh8D,KAAA05D,SAGA4H,EAAAjhE,KAAA27D,EAAA5B,aAAAp6D,KAAA66D,OAEA76D,KAAA+5D,gBACA,OAAA18D,GAAA,EAA+BA,EAAA2C,KAAA+5D,gBAAA55D,OAAiC9C,IAAA,CAChE,GAAAkkE,GAAAvhE,KAAA+5D,gBAAA18D,EACA,IAAA2+D,EAAA3B,YAAAkH,EAAAlH,WAAA2B,EAAA7B,gBAAAoH,EAAApH,eAAA6B,EAAA5B,aAAAmH,EAAAnH,WACA,QAEAkH,GAAAjhE,MAAA,GAKA,GAAAL,KAAA65D,kBACA,OAAAl9C,GAAA,EAAgCA,EAAA3c,KAAA65D,kBAAA15D,OAAoCwc,IAAA,CACpE,GAAA6kD,GAAAxhE,KAAA65D,kBAAAl9C,EACA2kD,GAAAjhE,KAAA27D,EAAA3B,YAAAmH,EAAAnH,WAAA2B,EAAA7B,gBAAAqH,EAAArH,eAAA6B,EAAA5B,aAAAoH,EAAApH,YAIA,GAAAp6D,KAAA85D,uBACA,OAAA2H,GAAA,EAAiCA,EAAAzhE,KAAA85D,uBAAA35D,OAA0CshE,IAAA,CAC3E,GAAAjB,GAAAxgE,KAAA85D,uBAAA2H,EACAH,GAAAjhE,KAAA27D,EAAAyE,WAAAD,GAIA,MAAAc,GAAAl5D,SAAA,MAOAs5D,eAAA,SAAA1F,GACAh8D,KAAA0uC,UAEA1uC,KAAAqhE,eAAArF,IACAh8D,KAAA8lB,MAAA,SAAAk2C,IAGA2F,gBAAA,SAAA3F,GACA,IAAAh8D,KAAAklC,OAAA/kC,OAAA,QAIA,QAFAyhE,MAEAvkE,EAAA,EAA2BA,EAAA2C,KAAAklC,OAAA/kC,OAAwB9C,IACnD2C,KAAAklC,OAAA7nC,GAAA48D,KAAAwG,WAAAzE,EAAAyE,UACAmB,EAAAvhE,KAAAL,KAAAklC,OAAA7nC,GAIA,SAAAukE,EAAAzhE,QAIAyhE,GAOAC,YAAA,SAAA7F,GACA,QAAA8F,GAAAC,EAAAC,GAEA,SAAAD,IAAAC,KAIAD,EAAA1H,YAAA2H,EAAA3H,WAAA0H,EAAA5H,gBAAA6H,EAAA7H,eAAA4H,EAAA3H,aAAA4H,EAAA5H,YAGA,OACA6H,cAAAH,EAAA9F,EAAAh8D,KAAAohE,cACAc,WAAAJ,EAAA9F,EAAA,GAAA7xD,OACAg4D,gBAAAniE,KAAAqhE,eAAArF,KAAAh8D,KAAA0uC,SACA0zB,mBAAApiE,KAAAqhE,eAAArF,IAAAh8D,KAAA0uC,cAQA,SAAAtxC,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,mBACG6pC,EAAA35C,GAAA25C,EAAA,cAAA6F,EAAAx5D,GACH,OAAA2zD,EAAAkL,eAAArF,KAAA7F,EAAAznB,SAAA1wB,EAAA,KACAzV,IAAA/F,EACA8pB,YAAA,kBACA1L,OAAAu1C,EAAA0L,YAAA7F,IACAqG,YAAAlM,EAAAwL,gBAAA3F,IACO7F,EAAAuE,YACPvkD,OACAmnD,KAAA,SACAgF,KAAA,IACA5zB,SAAAynB,EAAAznB,UAEAt5B,IACAmhD,MAAA,SAAAv7C,GACAA,EAAA47C,iBACAT,EAAAuL,eAAA1F,IAEAnF,SAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAAyF,WACzFyS,GAAA47C,iBACAT,EAAAuL,eAAA1F,IACS,SAAAhhD,GACT,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAAyF,WACzFyS,GAAA47C,iBACAT,EAAAuL,eAAA1F,QAGK7F,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAAy/C,EAAA3B,WAAA,oBAAAlE,EAAAwL,gBAAA3F,GAAAh+C,EAAA,OACLsO,YAAA,UACK6pC,EAAA35C,GAAA25C,EAAAwL,gBAAA3F,GAAA,SAAAvmD,EAAAjT,GACL,MAAAwb,GAAA,OACAzV,IAAA/F,EACA8pB,YAAA,QACA1L,MAAAnL,EAAAvU,UAEKi1D,EAAAl5C,OAAAe,EAAA,OACLzV,IAAA/F,EACA8pB,YAAA,kBACA1L,MAAAu1C,EAAA0L,YAAA7F,KACK7F,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAAy/C,EAAA3B,WAAA,oBACF,IACFr1D,qBAID,SAAA5H,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,WACAsO,YAAA,qBACGtO,EAAA,UACHsO,YAAA,qBACG6pC,EAAA35C,GAAA25C,EAAA,yBAAA6F,EAAAx5D,GACH,MAAAwb,GAAA,OACAzV,IAAA/F,EACA8pB,YAAA,oBACK6pC,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAAy/C,GAAA,mBACF7F,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,kBACA1L,OACA2hD,aAAApM,EAAA8J,YAEG9J,EAAA35C,GAAA25C,EAAAyK,iBAAAzK,EAAA/G,QAAAyL,MAAA1E,EAAA/G,QAAA0L,MAAA,SAAAoG,EAAA1+D,GACH,MAAAwb,GAAA,0BACAzV,IAAA/F,EACA2T,OACAqsD,gBAAArM,EAAAjwD,MACAg7D,OACArG,MAAA1E,EAAA/G,QAAAyL,MACA4H,WAAAtM,EAAAsD,QACAiJ,WAAAvM,EAAAuD,QACAhrB,SAAAynB,EAAAznB,SACAi0B,qBAAAxM,EAAA0D,kBACA+I,4BAAAzM,EAAA2D,uBACA+I,mBAAA1M,EAAA4D,gBACA70B,OAAAixB,EAAA8K,iBAAAC,EAAA1+D,GACAk4D,WAAAvE,EAAAuE,YAEAtlD,IACAi6C,OAAA8G,EAAAuF,4BAIC12D,qBAID,SAAA5H,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,qBACA1L,OAAAu1C,EAAAxd,MACA8b,cAAA0B,EAAAvd,cAEGud,EAAA7e,UAAA6e,EAAAqD,OAAAx7C,EAAA,cACHmQ,IAAA,WACAhY,OACA+qC,SAAAiV,EAAAjV,SACAxS,SAAAynB,EAAAznB,SACA8qB,OAAArD,EAAAqD,UAEGrD,EAAAqD,OA6BArD,EAAAl5C,KA7BAe,EAAA,UAAAm4C,EAAAp5C,IACHoR,IAAA,QACAhY,OACA0B,KAAA,UACAkhC,aAAA,MACA7yC,MAAAiwD,EAAAyF,YAAAzF,EAAAwE,cACAz+B,YAAAi6B,EAAAj6B,YACAyc,KAAAwd,EAAAxd,KACAG,KAAAqd,EAAArd,KACA2d,YAAAN,EAAA7b,SACAzB,QAAAsd,EAAAtd,QACAh3B,QAAAs0C,EAAAt0C,QACA6sB,SAAAynB,EAAAznB,SACAkrB,SAAAzD,EAAAyD,UAEAxkD,IACAwkC,MAAA,SAAA5+B,GACAm7C,EAAArwC,MAAA,QAAA9K,IAEAq7C,KAAA,SAAAr7C,GACAm7C,EAAArwC,MAAA,OAAA9K,IAAAm7C,EAAAnc,uBAGAj7B,UACAqT,OAAA,SAAApX,GACAm7C,EAAAwF,SAAA3gD,EAAA1P,OAAApF,SAGA2R,KAAA,WACG,UAAAs+C,EAAA9wC,QAAA,IAAA8wC,EAAAn5C,GAAA,KAAAgB,EAAA,mBACH7H,OACAu4B,SAAAynB,EAAAznB,SACA8uB,OAAA,MAEGx/C,EAAA,UACHsO,YAAA,0BACG/lB,KAAA4vD,EAAAz8C,OAAAg0C,QAAAyI,EAAAz8C,OAAAg0C,OAAAvtD,QAAAg2D,EAAA15C,GAAA,WAAAuB,EAAA,OACHsO,YAAA,iCACG6pC,EAAAkF,cAAAlF,EAAAznB,SA6BAynB,EAAAl5C,KA7BAe,EAAA,KACHsO,YAAA,sBACAnW,OACAmnD,KAAA,SACAgF,KAAA,IACA5zB,SAAAynB,EAAAznB,UAEAt5B,IACAmhD,MAAA,SAAAv7C,GACAA,EAAA47C,iBACAT,EAAA0F,eAAA7gD,IAEA67C,SAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAAuF,WACvFyS,GAAA47C,iBACAT,EAAA0F,eAAA7gD,IACO,SAAAA,GACP,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAAuF,WACvFyS,GAAA47C,iBACAT,EAAA0F,eAAA7gD,QAGGgD,EAAA,UACH7H,OACA2iC,KAAA,eACAwc,KAAAa,EAAA7b,SACAmb,KAAA,GACAv0D,KAAA,8BAEG,GAAAi1D,EAAAn5C,GAAA,KAAAgB,EAAA,KACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,OAAAiwD,EAAAmF,cAAAnF,EAAAznB,SACAvM,WAAA,8BAEA7V,YAAA,kBACAnW,OACAmnD,KAAA,SACAgF,KAAA,IACA5zB,SAAAynB,EAAAznB,UAEAt5B,IACAmhD,MAAA,SAAAv7C,GACAA,EAAA47C,iBACAT,EAAA2F,eAAA9gD,IAEA67C,SAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAAuF,WACvFyS,GAAA47C,iBACAT,EAAA2F,eAAA9gD,IACO,SAAAA,GACP,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAAuF,WACvFyS,GAAA47C,iBACAT,EAAA2F,eAAA9gD,QAGGgD,EAAA,UACH7H,OACA2iC,KAAA,gBACAwc,KAAAa,EAAA7b,SACAmb,KAAA,GACAv0D,KAAA,8BAEG,GAAAi1D,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,oBACGtO,EAAA,WAAAA,EAAA,YACH7H,OACAu4B,SAAAynB,EAAAznB,UAEA7vB,OACA3Y,MAAAiwD,EAAAyE,gBAAA,MACA/6C,SAAA,SAAAi3C,GACAX,EAAAvzB,KAAAuzB,EAAAyE,gBAAA,QAAA9D,IAEA30B,WAAA,0BAEGg0B,EAAA35C,GAAA25C,EAAA,oBAAA0E,EAAAr4D,GACH,MAAAwb,GAAA,UACAzV,IAAAsyD,EACAhgD,UACA3U,MAAA1D,KAEK2zD,EAAAn5C,GAAA,yCAAAm5C,EAAA55C,GAAAs+C,GAAA,2CACF1E,EAAAn5C,GAAA,KAAAgB,EAAA,YACH7H,OACAu4B,SAAAynB,EAAAznB,UAEA7vB,OACA3Y,MAAAiwD,EAAAyE,gBAAA,KACA/6C,SAAA,SAAAi3C,GACAX,EAAAvzB,KAAAuzB,EAAAyE,gBAAA,OAAA9D,IAEA30B,WAAA,yBAEGg0B,EAAA35C,GAAA25C,EAAA,qBAAA2E,GACH,MAAA98C,GAAA,UACAzV,IAAAuyD,EACAjgD,UACA3U,MAAA40D,KAEK3E,EAAAn5C,GAAA,yCAAAm5C,EAAA55C,GAAAu+C,GAAA,4CACF,aAAA3E,EAAAn5C,GAAA,KAAAgB,EAAA,sBACH7H,OACA2sD,YAAA3M,EAAAkD,SACA0J,cAAA5M,EAAAmD,WACA0J,oBAAA7M,EAAAoD,eACAkJ,WAAAtM,EAAAsD,QACAiJ,WAAAvM,EAAAuD,QACAtK,QAAA+G,EAAAyE,gBACAlsB,SAAAynB,EAAAznB,SACAi0B,qBAAAxM,EAAA0D,kBACA+I,4BAAAzM,EAAA2D,uBACA+I,mBAAA1M,EAAA4D,gBACA70B,OAAAixB,EAAAjxB,OACAw1B,WAAAvE,EAAAuE,YAEAtlD,IACAg8B,MAAA,SAAAp2B,GACAm7C,EAAAryC,MAAA4+B,SAAA5B,UAAA,IAGAjiC,OACA3Y,MAAAiwD,EAAA,aACAt2C,SAAA,SAAAi3C,GACAX,EAAAwE,aAAA7D,GAEA30B,WAAA,kBAEGg0B,EAAAn5C,GAAA,SAAAzW,KAAA4vD,EAAAz8C,OAAA5U,SAAAqxD,EAAAz8C,OAAA5U,QAAA3E,OAAA6d,EAAA,UACHsO,YAAA,sBACG6pC,EAAA15C,GAAA,eAAA05C,EAAAl5C,MAAA,OAAAe,EAAA,UAAAm4C,EAAAp5C,IACHoR,IAAA,QACAhY,OACAjV,KAAA,OACA63C,aAAA,MACA7yC,MAAAiwD,EAAA4F,eAAA5F,EAAAjwD,OACAg2B,YAAAi6B,EAAAj6B,YACAyc,KAAAwd,EAAAxd,KACAG,KAAAqd,EAAArd,KACA2d,YAAAN,EAAA7b,SACAz4B,QAAAs0C,EAAAt0C,QACAlT,IAAAwnD,EAAA4F,eAAA5F,EAAAuD,SACAlc,IAAA2Y,EAAA4F,eAAA5F,EAAAsD,SACA/qB,SAAAynB,EAAAznB,SACAkrB,UAAA,GAEAxkD,IACAwkC,MAAA,SAAA5+B,GACAm7C,EAAArwC,MAAA,QAAA9K,IAEAq7C,KAAA,SAAAr7C,GACAm7C,EAAArwC,MAAA,OAAA9K,IAAAm7C,EAAAnc,uBAGAj7B,UACAqT,OAAA,SAAApX,GACAm7C,EAAA8F,qBAAAjhD,MAGG,UAAAm7C,EAAA9wC,QAAA,SACFrgB,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAo6C,GAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACAyT,EAAA/2D,EAAA,GACAg3D,EAAAh3D,EAAAmB,EAAA41D,GACAkP,EAAAjmE,EAAA,IACAkmE,EAAAlmE,EAAAmB,EAAA8kE,GACA3K,EAAAt7D,EAAA,GACAmmE,EAAAnmE,EAAA,GACAomE,EAAApmE,EAAA,GAyEAiJ,GAAA,SACAtI,KAAA,UACAgsB,WAAA42B,OAA+FyT,EAAAlrD,EAAAnL,KAAAq2D,EAAAlrD,GAC/FiI,QAAAmyD,EAAAp6D,EACAkI,QAAAoyD,EAAA,GACArzD,OACA0kC,MAAAttC,OACA+yC,QAAA/yC,OACA2xC,KAAA3xC,OACAy5C,QAAAv8C,QACAnD,MACAA,KAAAiG,OACArC,QAAA,cAEA6zC,KAAAxxC,OACAq+C,aACAtkD,KAAAiG,OACArC,QAAA,WACA,MAAAwzD,GAAA,EAAAziB,yBAAAyiB,EAAA,EAAAziB,yBAAA,OAGAwtB,YACAniE,KAAAiG,OACArC,QAAA,WACA,MAAAwzD,GAAA,EAAAxiB,wBAAAwiB,EAAA,EAAAxiB,wBAAA,WAGAyP,SAAAlhD,QACAi/D,YACApiE,KAAApD,OACAgH,QAAA,WACA,WAGAy+D,WACAriE,KAAA08B,SACA94B,QAAA,cAEA0+D,SACAtiE,KAAAiG,OACArC,QAAA,YAGAmH,KAAA,WAGA,OACAq5C,OAHAtlD,KAAAulD,SAAAvlD,KAAAsjE,WAAAp9D,OAAA,MAIA46C,UAAA,EACA1G,kBAAA,KAIAxyB,UAIA67C,WAAA,WACA,OAAAzjE,KAAAkB,MACA,cACA,mBACA,kBACA,oBACA,kBACA,aACA,iBACA,oBACA,SACA,cAGAwiE,WAAA,WACA,MAAA1jE,MAAA2jE,cAAAv7D,QAAA,eAGAof,SAKA69B,QAAA,WACA,GAAAxL,GAAA75C,IAEA,QAAAuG,KAAAvG,KAAA8jB,MAAA8+B,QACA5iD,KAAA8jB,MAAA8+B,MAAAzI,gBAKA,MAJAn6C,MAAAo6C,kBAAAp6C,KAAA8jB,MAAA8+B,MAAAxI,sBACAp6C,MAAAwjC,UAAA,WACA,MAAAqW,GAAA/1B,MAAA8+B,MAAAyM,UAMArvD,MAAAujE,UAAAvjE,KAAAslD,QACAtlD,KAAAoxC,SAOAA,MAAA,WACA,GAAAwQ,GAAA5hD,IAEAA,MAAA8gD,UAAA,EAEAn+B,WAAA,WACAi/B,EAAA31B,WACAnuB,OAAAqlE,EAAA,GAAAvhB,EAAAp9B,MACa,OAGbgnB,YAAA,WAEAxqC,SAAAkrC,KAAA9qC,YAAApB,KAAAwkB,MAEAuf,QAAA,WACA,GAAAiqB,GAAAhuD,IAEAA,MAAA8gD,UAAA,MAEA,KAAA9gD,KAAAsjE,WAAAlM,UACAp3D,KAAA4iC,KAAA5iC,KAAAsjE,WAAA,eAGAtjE,KAAAwjC,UAAA,WAEAwqB,EAAAzI,SACAyI,EAAAlqC,MAAA8+B,MAAAhJ,QACa,WAAAoU,EAAAwV,SAAAxV,EAAA0V,WACb1V,EAAAlqC,MAAA8/C,aAAAhqB,QAEAoU,EAAAlqC,MAAA+/C,cAAAjqB,aAQA,SAAAx8C,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA49D,GAAA9mE,EAAA,GACA+mE,EAAA/mE,EAAA,EAiCAiJ,GAAA,SACAtI,KAAA,SACAoS,OACA+xB,OAAAz9B,QACAoe,WAAA3kB,OAAA8/B,UACA9+B,QAAAqI,OACAw/C,aAAAtiD,QACA0L,MAAAjS,OACAonC,OAAApnC,OACAkmE,OACA9iE,MAAAiG,OAAAuwB,QACA5yB,QAAA,KAEAm/D,aAAA5/D,QACA4zD,WACA/2D,KAAAiG,OACArC,QAAA,YAEAsgD,WACAlkD,MAAAwG,MAAArD,SACAS,QAAA,WACA,0CAGAo/D,UACAhjE,KAAA08B,SACA94B,QAAA,cAEAq/D,QACAjjE,KAAAiG,OACArC,QAAA,WACA,MAAAi/D,GAAA,EAAAntB,mBAAAmtB,EAAA,EAAAntB,mBAAA,QAEAuK,UAAA,SAAAj7C,GACA,sBAAAkC,QAAAlC,IAAA,KAIA+F,KAAA,WACA,OACA60C,SAAA9gD,KAAA8hC,SAAA,EACAsiC,eAAA,KACAC,SAAA,gBAAArkE,MAAAgkE,MAAAhkE,KAAAgkE,MAAA,KAAAhkE,KAAAgkE,QAIAp8C,UACA+7C,cAAA,WACA,uBAAA3jE,MAAAolD,UAAAplD,KAAAolD,WAAA,oCAAAplD,KAAAolD,WAEAkf,MAAA,WACA,MAAAtkE,MAAA2jE,cAAAv7D,QAAA,UAGA0f,OACAga,OAAA,SAAA57B,GACAlG,KAAA8gD,SAAA56C,GAEA46C,SAAA,WACA9gD,KAAAukE,iBAGA/8C,SACA+8C,aAAA,WACA,sBAAAx3C,QAAA,CAEA,YAAA/sB,KAAAmkE,OAMA,YALAnkE,KAAA8gD,SACA9/C,SAAAuyC,gBAAApe,UAAAxvB,IAAA,cAEA3E,SAAAuyC,gBAAApe,UAAA5zB,OAAA,cAaA,IARAvB,KAAAokE,eAAApkE,KAAAokE,eAAApkE,KAAAokE,eAAApjE,SAAAuyC,gBAAA0b,UAEAjvD,KAAA8gD,SACA9/C,SAAAkrC,KAAA/W,UAAAxvB,IAAA,eAEA3E,SAAAkrC,KAAA/W,UAAA5zB,OAAA,eAGAvB,KAAA8gD,SAEA,YADA9/C,SAAAkrC,KAAAvrB,MAAAuc,IAAA,IAAAl9B,KAAAokE,eAAA,KAIApjE,UAAAuyC,gBAAA0b,UAAAjvD,KAAAokE,eACApjE,SAAAkrC,KAAAvrB,MAAAuc,IAAA,KACAl9B,KAAAokE,eAAA,OAOAI,OAAA,SAAApkC,GACApgC,KAAA2jE,cAAAv7D,QAAAg4B,GAAA,IAEApgC,KAAAkkE,SAAAl7D,MAAA,KAAAD,WACA/I,KAAAoxC,UAQAA,MAAA,WACA,GAAAyI,GAAA75C,IAEAA,MAAA8lB,MAAA,SACA9lB,KAAA8lB,MAAA,oBAGA9lB,KAAA2mD,eACA3mD,KAAA8gD,UAAA,EACAn+B,WAAA,WACAk3B,EAAA5tB,WACAnuB,OAAAgmE,EAAA,GAAAjqB,EAAAr1B,MACiB,OAQjBigD,SAAA,SAAAhvD,GAEAzV,KAAA8gD,UAAA,KAAArrC,EAAAkhD,SAAA32D,KAAAwkE,OAAA,YAGA3gC,QAAA,WACA,mBAAA9W,SACA/rB,SAAA0wB,iBAAA,QAAA1xB,KAAAykE,WAGAj5B,YAAA,WAGAxrC,KAAA2mD,cAAA3lD,SAAAkrC,KAAA9qC,YAAApB,KAAAwkB,MAEAuf,QAAA,WACA/jC,KAAA2mD,aAAA3mD,KAAA8gD,UAAA,EAAoD9gD,KAAA8gD,UAAA9gD,KAAAukE,gBAEpDjV,cAAA,WACA,sBAAAviC,QAAA,CACA/rB,SAAAywB,oBAAA,QAAAzxB,KAAAykE,UAEAzjE,SAAAuyC,gBAAApe,UAAA5zB,OAAA,aACA,IAAA6iE,GAAApkE,KAAAokE,eAAApkE,KAAAokE,eAAApjE,SAAAuyC,gBAAA0b,SACAjuD,UAAAkrC,KAAA/W,UAAA5zB,OAAA,eACAP,SAAAuyC,gBAAA0b,UAAAmV,EACApjE,SAAAkrC,KAAAvrB,MAAAuc,IAAA,SAOA,SAAA9/B,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,cACA7H,OACAxY,KAAAw4D,EAAA8B,aAEG9B,EAAA,SAAAn4C,EAAA,OACHsO,YAAA,oBACGtO,EAAA,OACHsO,YAAA,mBACAlX,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAAqO,OAAA,eAGGrO,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,oBACA1L,OACA8jD,iBAAAvO,EAAA8N,cAEAtjD,OACAgkD,SAAAxO,EAAAkO,YAEGlO,EAAA,UAAAn4C,EAAAm4C,EAAA1zC,UAAA0zC,EAAA/4C,GAAA+4C,EAAAp5C,IACH/Q,IAAA,YACAoJ,IACAg8B,MAAA+kB,EAAA/kB,QAEG,YAAA+kB,EAAApmD,OAAA,GAAAomD,EAAAjxB,SAAAixB,EAAA,QAAAn4C,EAAA,OACHnD,UACA0Y,UAAA4iC,EAAA55C,GAAA45C,EAAAr3D,YAEGq3D,EAAA15C,GAAA,eAAA05C,EAAAn5C,GAAA,KAAAm5C,EAAA,MAAAn4C,EAAA,UACHsO,YAAA,uBACAnW,OACAjV,KAAA,UAEAkU,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAAqO,OAAA,SAGGrO,EAAAl5C,OAAAk5C,EAAAl5C,QACFjY,qBAID,SAAA5H,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,cACA7H,OACAxY,KAAAw4D,EAAA8B,aAEG9B,EAAA,SAAAn4C,EAAA,OACHsO,YAAA,yBACA1L,MAAAu1C,EAAAxd,OACG36B,EAAA,OACHsO,YAAA,mBACAlX,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAAqO,OAAA,eAGGrO,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,iCACG6pC,EAAA,MAAAn4C,EAAA,UACHsO,YAAA,oBACGtO,EAAA,KACHsO,YAAA,qBACG6pC,EAAAn5C,GAAAm5C,EAAA55C,GAAA45C,EAAA1hB,YAAA0hB,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,WACHsO,YAAA,kBACA1L,OACAgkD,gBAAAzO,EAAA1hB,MAAAowB,UAAA1O,EAAAvV,WAEG5iC,EAAA,OACHsO,YAAA,UACG6pC,EAAA,QAAAn4C,EAAA,OACHsO,YAAA,eACGtO,EAAA,UACH7H,OACA2iC,KAAAqd,EAAArd,KAAAqd,EAAArd,KAAAqd,EAAAsN,WACAnO,KAAAa,EAAA7b,SACAp5C,KAAAi1D,EAAAj1D,KACAu0D,MAAAU,EAAArd,KACAH,KAAA,eAEG,GAAAwd,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,kBACGtO,EAAA,KACHnD,UACA0Y,UAAA4iC,EAAA55C,GAAA45C,EAAAjc,YAEGic,EAAAn5C,GAAA,KAAAm5C,EAAA,SAAAn4C,EAAA,OACHsO,YAAA,UACGtO,EAAA,OACHsO,YAAA,YACGtO,EAAA,QAAAm4C,EAAAp5C,IACHzM,aACA3S,KAAA,QACAwyB,QAAA,UACAjqB,MAAAiwD,EAAA,OACAh0B,WAAA,WAEAhU,IAAA,QACA7B,YAAA,QACA1L,OACAkkD,YAAA3O,EAAA/b,mBAEAv/B,UACA3U,MAAAiwD,EAAA,QAEA/gD,IACAshD,MAAA,SAAA17C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAAuF,WACvF4tD,GAAA9Q,QAAArqC,IAEA4nC,MAAA,SAAA5nC,GACAA,EAAA1P,OAAAqoB,YACAwiC,EAAA7Q,OAAAtqC,EAAA1P,OAAApF,UAGG,QAAAiwD,EAAAmN,YAAA,MAAAnN,EAAAn5C,GAAA,KAAAgB,EAAA,KACHsO,YAAA,mBACG6pC,EAAAn5C,GAAAm5C,EAAA55C,GAAA45C,EAAA/b,wBAAA+b,EAAAl5C,WAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,UACHsO,YAAA,oBACG6pC,EAAA,WAAAn4C,EAAA,UACHmQ,IAAA,eACA7B,YAAA,SACAlX,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAAqO,OAAA,cAGGrO,EAAAn5C,GAAA,yBAAAm5C,EAAA55C,GAAA45C,EAAAkN,YAAA,wBAAAlN,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,UACHmQ,IAAA,gBACA7B,YAAA,SACA1L,MAAAu1C,EAAAj1D,KACAkU,IACAmhD,MAAAJ,EAAA9Q,WAEG8Q,EAAAn5C,GAAA,yBAAAm5C,EAAA55C,GAAA45C,EAAA3Q,aAAA,8BAAA2Q,EAAAl5C,QACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAG1D,IAAA6+D,GAAA/nE,EAAA,GAKAsrB,EAAA,mBAAAyE,QAEAE,EAAA3E,EAAAxqB,OAAAivB,OAAAE,WAkBAhnB,GAAA,SACAtI,KAAA,WACAoS,OACA+xB,OAAAz9B,QACAsiD,aAAAtiD,QACA+8C,WAAAtjD,OAAA8/B,SAAA3Q,GACA+3C,YACA9jE,KAAAmD,QACAS,SAAA,GAEAmzD,WACA/2D,KAAAiG,OACArC,QAAA,QAEAsgD,WACAlkD,KAAAmD,QACAS,SAAA,GAEAo/D,UACAhjE,KAAA08B,SACA94B,QAAA,eAGAmH,KAAA,WACA,OACA60C,SAAA9gD,KAAA8hC,SAAA,IAIAha,OACAga,OAAA,SAAA57B,GACAlG,KAAA8gD,SAAA56C,IAGAshB,SAIAg9C,OAAA,WACAxkE,KAAAolD,WAAAplD,KAAA8gD,UAEA9gD,KAAAoxC,SAMAA,MAAA,WACA,GAAAyI,GAAA75C,IAEAA,MAAAkkE,SAAAl7D,MAAA,KAAAD,WACA/I,KAAA8lB,MAAA,SACA9lB,KAAA8lB,MAAA,oBAGA9lB,KAAA2mD,eACA3mD,KAAA8gD,UAAA,EACAn+B,WAAA,WACAk3B,EAAA5tB,WACAnuB,OAAAinE,EAAA,GAAAlrB,EAAAr1B,MACiB,OAOjBigD,SAAA,SAAAhvD,GAEA,KAAAA,EAAAkhD,SAAA32D,KAAAwkE,WAGA3gC,QAAA,WACA,mBAAA9W,SACA/rB,SAAA0wB,iBAAA,QAAA1xB,KAAAykE,WAGAj5B,YAAA,WAGAxrC,KAAA2mD,eACA3mD,KAAAohD,WAGAphD,KAAAglE,YAAA,EACAhlE,KAAAohD,UAAAhgD,YAAApB,KAAAwkB,MAHAxjB,SAAAkrC,KAAA9qC,YAAApB,KAAAwkB,OAOAuf,QAAA,WACA/jC,KAAA2mD,eAAA3mD,KAAA8gD,UAAA,IAEAwO,cAAA,WACA,mBAAAviC,SACA/rB,SAAAywB,oBAAA,QAAAzxB,KAAAykE,aAOA,SAAArnE,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,cACA7H,OACAxY,KAAAw4D,EAAA8B,aAEG9B,EAAA,SAAAn4C,EAAA,OACHsO,YAAA,4BACA1L,OACAqkD,eAAA9O,EAAA6O,cAEGhnD,EAAA,OACHsO,YAAA,qBACAlX,IACAmhD,MAAAJ,EAAAqO,UAEGrO,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,mBACG6pC,EAAAl5C,QACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAg/D,GAAAloE,EAAA,GAsCAiJ,GAAA,SACAtI,KAAA,WACAqT,QAAAk0D,EAAA,GACAj5D,KAAA,WACA,OACAk5D,YAAAnlE,KAAA05C,UAAA15C,KAAA24C,MAAA,eAOA,SAAAv7C,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,cACA7H,OACAxY,KAAA,UAEGw4D,EAAA,SAAAn4C,EAAA,WACHsO,YAAA,UACA1L,OAAAu1C,EAAAj1D,KAAAi1D,EAAAxd,QACGwd,EAAA,MAAAn4C,EAAA,UACHsO,YAAA,mBACGtO,EAAA,KAAAm4C,EAAAn5C,GAAAm5C,EAAA55C,GAAA45C,EAAA1hB,UAAA0hB,EAAAn5C,GAAA,KAAAm5C,EAAA,SAAAn4C,EAAA,UACHsO,YAAA,SACAnW,OACAjV,KAAA,UAEAkU,IACAmhD,MAAAJ,EAAA/kB,SAEG+kB,EAAAl5C,OAAAk5C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,WACHsO,YAAA,iBACGtO,EAAA,OACHsO,YAAA,UACG6pC,EAAArd,MAAAqd,EAAAvV,QAAA5iC,EAAA,OACHsO,YAAA,eACGtO,EAAA,UACH4C,MAAAu1C,EAAAj1D,KACAiV,OACA2iC,KAAAqd,EAAArd,KACA2d,YAAAN,EAAA7b,SACAmb,KAAA,GACA9c,KAAAwd,EAAAgP,gBAEG,GAAAhP,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,kBACG6pC,EAAA15C,GAAA,qBAAA05C,EAAAl5C,QACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAg/D,GAAAloE,EAAA,GAgCAiJ,GAAA,SACAtI,KAAA,gBACAqT,QAAAk0D,EAAA,KAKA,SAAA9nE,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,cACA7H,OACAxY,KAAA,UAEGw4D,EAAA,SAAAn4C,EAAA,WACHsO,YAAA,eACA1L,MAAAu1C,EAAAj1D,OACGi1D,EAAA,SAAAn4C,EAAA,UACHsO,YAAA,SACAnW,OACAjV,KAAA,UAEAkU,IACAmhD,MAAAJ,EAAA/kB,SAEG+kB,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,UACG6pC,EAAArd,MAAAqd,EAAAvV,QAAA5iC,EAAA,OACHsO,YAAA,eACGtO,EAAA,UACH7H,OACA2iC,KAAAqd,EAAArd,KACA2d,YAAAN,EAAA7b,SACAmb,KAAA,GACA9c,KAAA,eAEG,GAAAwd,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,kBACG6pC,EAAA15C,GAAA,mBAAA05C,EAAAl5C,QACFjY,qBAID,SAAA5H,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAo6C,GAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACAyT,EAAA/2D,EAAA,GACAg3D,EAAAh3D,EAAAmB,EAAA41D,GACAqR,EAAApoE,EAAA,GA8EAiJ,GAAA,SACAtI,KAAA,cACAgsB,WAAA42B,OAA+FyT,EAAAlrD,EAAAnL,KAAAq2D,EAAAlrD,GAC/FkI,QAAAo0D,EAAA,GACAr1D,OACAs1D,OAAA3tC,OAAAvwB,QACAm+D,SACApkE,MAAAw2B,OAAAvwB,QACArC,QAAA,IAEAgnB,SACA5qB,MAAAw2B,OAAAvwB,QACArC,QAAA,GAEA6zC,KAAAxxC,OACAo+D,OAAAlhE,QACAw0C,QAAAx0C,QACAmhE,MAAAr+D,QAEAygB,UACA2sC,YAAA,WACA,OAAAv0D,KAAAwlE,MAAAxlE,KAAA24C,MACA8sB,YAAAzlE,KAAAulE,OACAzQ,aAAA90D,KAAA64C,WAQA6sB,UAAA,WACA,MAAAt+D,MAAAi1C,KAAAr8C,KAAAqlE,MAAArlE,KAAAslE,UAOAK,UAAA,WACA,GAAAA,GAAA3lE,KAAA8rB,QAAA9rB,KAAAslE,QAAAtlE,KAAAslE,QAAA,CACA,OAAAK,IAAA,EAAAA,EAAA,GAOAC,QAAA,WACA,MAAA5lE,MAAA8rB,QAAA,GAOA+5C,SAAA,WACA,MAAA7lE,MAAA8rB,SAAA,GAOAg6C,iBAAA,WACA,MAAA9lE,MAAA8rB,SAAA,GAOAi6C,QAAA,WACA,MAAA/lE,MAAA8rB,SAAA9rB,KAAA0lE,UAAA,GAOAM,gBAAA,WACA,MAAAhmE,MAAA8rB,QAAA9rB,KAAA0lE,UAAA,GAAA1lE,KAAA8rB,SAAA9rB,KAAA0lE,UAAA,GAOAO,QAAA,WACA,MAAAjmE,MAAA8rB,QAAA9rB,KAAA0lE,WAQAQ,aAAA,WACA,GAAArsB,GAAA75C,IAEA,KAAAA,KAAAulE,OAAA,CAwBA,OAtBAvoC,GAAA51B,KAAAuH,IAAA,EAAA3O,KAAA8rB,QAAA,GACAq6C,EAAA/+D,KAAAo2C,IAAAx9C,KAAA8rB,QAAA,EAAA9rB,KAAA0lE,WAEAU,KAmBA/oE,EAAA2/B,EAA8B3/B,GAAA8oE,EAAY9oE,KAjB1C,SAAAA,GACA+oE,EAAA/lE,MACA4zB,OAAA52B,EACAgpE,UAAAxsB,EAAA/tB,UAAAzuB,EACAk5D,MAAA,SAAA9gD,GACAokC,EAAA/tB,UAAAzuB,IACAw8C,EAAA/zB,MAAA,SAAAzoB,GACAw8C,EAAA/zB,MAAA,iBAAAzoB,GAGAw8C,EAAArW,UAAA,WACA,MAAA/tB,GAAAnK,OAAAsuC,eAOAv8C,EAEA,OAAA+oE,MAGAt+C,OAIA49C,UAAA,SAAAx/D,GACAlG,KAAA8rB,QAAA5lB,GAAAlG,KAAA+W,SAGAyQ,SAIA8+C,KAAA,WACAtmE,KAAA4lE,UACA5lE,KAAA8lB,MAAA,SAAA9lB,KAAA8rB,QAAA,GACA9rB,KAAA8lB,MAAA,iBAAA9lB,KAAA8rB,QAAA,KAOAy6C,MAAA,WACAvmE,KAAA8lB,MAAA,YACA9lB,KAAA8lB,MAAA,qBAOA/O,KAAA,WACA/W,KAAA8lB,MAAA,SAAA9lB,KAAA0lE,WACA1lE,KAAA8lB,MAAA,iBAAA9lB,KAAA0lE,YAOAxsD,KAAA,WACAlZ,KAAAimE,UACAjmE,KAAA8lB,MAAA,SAAA9lB,KAAA8rB,QAAA,GACA9rB,KAAA8lB,MAAA,iBAAA9lB,KAAA8rB,QAAA,QAOA,SAAA1uB,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,aACA1L,MAAAu1C,EAAA5B,cACGv2C,EAAA,KACHsO,YAAA,sBACAnW,OACAmnD,KAAA,SACAgF,KAAA,IACA5zB,UAAAynB,EAAAyP,SAEAxwD,IACAmhD,MAAA,SAAAv7C,GACAA,EAAA47C,iBACAT,EAAAmQ,KAAAtrD,OAGGgD,EAAA,UACH7H,OACA2iC,KAAA,eACA2d,YAAAN,EAAA7b,SACAmb,KAAA,OAEG,GAAAU,EAAAn5C,GAAA,KAAAgB,EAAA,KACHsO,YAAA,kBACAnW,OACAmnD,KAAA,SACAgF,KAAA,IACA5zB,UAAAynB,EAAA8P,SAEA7wD,IACAmhD,MAAA,SAAAv7C,GACAA,EAAA47C,iBACAT,EAAAj9C,KAAA8B,OAGGgD,EAAA,UACH7H,OACA2iC,KAAA,gBACA2d,YAAAN,EAAA7b,SACAmb,KAAA,OAEG,GAAAU,EAAAn5C,GAAA,KAAAm5C,EAAAoP,OAiDApP,EAAAl5C,KAjDAe,EAAA,MACHsO,YAAA,oBACG6pC,EAAA,SAAAn4C,EAAA,MAAAA,EAAA,KACHsO,YAAA,kBACAnW,OACAmnD,KAAA,SACAgF,KAAA,KAEAltD,IACAmhD,MAAA,SAAAv7C,GACAA,EAAA47C,iBACAT,EAAAoQ,MAAAvrD,OAGGm7C,EAAAn5C,GAAA,yCAAAm5C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA,iBAAAn4C,EAAA,MAAAA,EAAA,QACHsO,YAAA,wBACG6pC,EAAAn5C,GAAA,SAAAm5C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA35C,GAAA25C,EAAA,sBAAAnoB,GACH,MAAAhwB,GAAA,MACAzV,IAAAylC,EAAA/Z,SACKjW,EAAA,KACLsO,YAAA,kBACA1L,OACA4lD,aAAAx4B,EAAAq4B,WAEAlwD,OACAmnD,KAAA,SACAgF,KAAA,KAEAltD,IACAmhD,MAAA,SAAAv7C,GACAA,EAAA47C,iBACA5oB,EAAAuoB,MAAAv7C,OAGKm7C,EAAAn5C,GAAA,qBAAAm5C,EAAA55C,GAAAyxB,EAAA/Z,QAAA,wBACFkiC,EAAAn5C,GAAA,KAAAm5C,EAAA,gBAAAn4C,EAAA,MAAAA,EAAA,QACHsO,YAAA,wBACG6pC,EAAAn5C,GAAA,SAAAm5C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA,QAAAn4C,EAAA,MAAAA,EAAA,KACHsO,YAAA,kBACAnW,OACAmnD,KAAA,SACAgF,KAAA,KAEAltD,IACAmhD,MAAA,SAAAv7C,GACAA,EAAA47C,iBACAT,EAAAp/C,KAAAiE,OAGGm7C,EAAAn5C,GAAA,qBAAAm5C,EAAA55C,GAAA45C,EAAAuP,WAAA,sBAAAvP,EAAAl5C,MAAA,GAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA,OAAAn4C,EAAA,SACHsO,YAAA,SACG,GAAA6pC,EAAAmP,SAAAnP,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAA45C,EAAAwP,WAAA,MAAAxP,EAAA55C,GAAA45C,EAAAkP,OAAA,gBAAAlP,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAA45C,EAAAwP,WAAA,IAAAxP,EAAA55C,GAAAnV,KAAAo2C,IAAA2Y,EAAArqC,QAAAqqC,EAAAmP,QAAAnP,EAAAkP,QAAA,MAAAlP,EAAA55C,GAAA45C,EAAAkP,OAAA,mBAAAlP,EAAAl5C,QACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAo6C,GAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACAyT,EAAA/2D,EAAA,GACAg3D,EAAAh3D,EAAAmB,EAAA41D,GACAqR,EAAApoE,EAAA,GAiCAiJ,GAAA,SACAtI,KAAA,SACAgsB,WAAA42B,OAA+FyT,EAAAlrD,EAAAnL,KAAAq2D,EAAAlrD,GAC/FkI,QAAAo0D,EAAA,GACAr1D,OACA02D,aACAvlE,KAAAmD,QACAS,SAAA,GAEA8qC,MACA1uC,KAAAmD,QACAS,SAAA,GAEA4hE,mBACAxlE,KAAAmD,QACAS,SAAA,GAEA4oD,OAAAvmD,OACArI,QAAAqI,OACA8wD,WACA/2D,KAAAiG,OACArC,QAAA,SAGAmH,KAAA,WACA,OACAisD,OAAAl4D,KAAA4vC,OAIA9nB,OACA8nB,KAAA,SAAA1pC,GACAlG,KAAAk4D,OAAAhyD,IAGAshB,SAIAsoB,OAAA,WACA9vC,KAAAymE,cAEAzmE,KAAAk4D,QAAAl4D,KAAAk4D,OACAl4D,KAAA8lB,MAAA,cAAA9lB,KAAAk4D,QAEAl4D,KAAAk4D,OACAl4D,KAAA8lB,MAAA,QAEA9lB,KAAA8lB,MAAA,cAQA,SAAA1oB,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,UACGtO,EAAA,OACHsO,YAAA,gBACA1L,OACA+lD,iBAAAxQ,EAAAsQ,aAEArxD,IACAmhD,MAAAJ,EAAArmB,UAEGqmB,EAAA,OAAAn4C,EAAA,QACHnD,UACA0Y,UAAA4iC,EAAA55C,GAAA45C,EAAAzI,WAEGyI,EAAA15C,GAAA,UAAA05C,EAAAn5C,GAAA,KAAAm5C,EAAA,YAAAn4C,EAAA,UACHsO,YAAA,kBACAnW,OACAs/C,KAAA,GACA3c,KAAAqd,EAAA+B,OAAA,sBACAzB,YAAAN,EAAA7b,YAEG6b,EAAAl5C,MAAA,GAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,cACH7H,OACAxY,KAAAw4D,EAAA8B,aAEGj6C,EAAA,OACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAA,OACAh0B,WAAA,WAEA7V,YAAA,gBACA1L,OACAgmD,eAAAzQ,EAAAuQ,qBAEGvQ,EAAA,QAAAn4C,EAAA,OACHnD,UACA0Y,UAAA4iC,EAAA55C,GAAA45C,EAAAr3D,YAEGq3D,EAAA15C,GAAA,sBACFzX,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA+wD,GAAAj6D,EAAA,GACAk6D,EAAAl6D,EAAAmB,EAAA84D,EAuBAhxD,GAAA,SACAtI,KAAA,SACAoS,OACA7J,OAAAiB,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAquD,aAAAhwD,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACA5H,KAAAiG,OACAunC,SAAArqC,QACA+yD,SAAA/yD,QACA1G,KAAAwJ,OACAwxC,KAAAxxC,QAEA8E,KAAA,WACA,OACA0gD,SAAA3sD,KAAAkG,QAIA4hB,OAIA5hB,MAAA,SAAAktB,GACApzB,KAAA2sD,SAAAv5B,GAMAu5B,SAAA,SAAAzmD,GAGAA,IAAAlG,KAAAm3D,aACAn3D,KAAA8lB,MAAA,QAAA5f,OAQA,SAAA9I,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,SACAmQ,IAAA,QACA7B,YAAA,gBACA1L,OAAAu1C,EAAAxd,MACA4e,cAAApB,EAAAznB,WAEAv4B,OACAu4B,SAAAynB,EAAAznB,SACA8oB,UAAArB,EAAAznB,UAAA,GAEAt5B,IACAyhD,QAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,MAAA4tD,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAA0I,WAC1IyS,GAAA47C,iBACAT,EAAAryC,MAAA2zC,MAAAlB,YAGGv4C,EAAA,SACH1N,aACA3S,KAAA,QACAwyB,QAAA,UACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEAhsB,OACAjV,KAAA,QACAwtC,SAAAynB,EAAAznB,SACA0oB,SAAAjB,EAAAiB,SACAz5D,KAAAw4D,EAAAx4D,MAEAkd,UACA3U,MAAAiwD,EAAAgB,YACAS,QAAAzB,EAAAz5C,GAAAy5C,EAAAxJ,SAAAwJ,EAAAgB,cAEA/hD,IACAgd,OAAA,SAAApX,GACAm7C,EAAAxJ,SAAAwJ,EAAAgB,gBAGGhB,EAAAn5C,GAAA,KAAAgB,EAAA,QACHsO,YAAA,QACA1L,MAAAu1C,EAAAj1D,OACGi1D,EAAAn5C,GAAA,KAAAgB,EAAA,QACHsO,YAAA,kBACG6pC,EAAA15C,GAAA,kBACFzX,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA+wD,GAAAj6D,EAAA,GACAk6D,EAAAl6D,EAAAmB,EAAA84D,EAuBAhxD,GAAA,SACAtI,KAAA,eACAoS,OACA7J,OAAAiB,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAquD,aAAAhwD,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACA5H,MACAA,KAAAiG,OACArC,QAAA,cAEA4pC,SAAArqC,QACA1G,KAAAwJ,OACAwxC,KAAAxxC,QAEA8E,KAAA,WACA,OACA0gD,SAAA3sD,KAAAkG,QAIA4hB,OAIA5hB,MAAA,SAAAktB,GACApzB,KAAA2sD,SAAAv5B,GAMAu5B,SAAA,SAAAzmD,GAGAA,IAAAlG,KAAAm3D,aACAn3D,KAAA8lB,MAAA,QAAA5f,OAQA,SAAA9I,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,YACGtO,EAAA,SACHmQ,IAAA,QACA7B,YAAA,uBACA1L,OAAAu1C,EAAAxJ,WAAAwJ,EAAAgB,YAAAhB,EAAAj1D,KAAA,KAAAi1D,EAAAxd,MACAxiC,OACAu4B,SAAAynB,EAAAznB,SACA8oB,UAAArB,EAAAznB,UAAA,GAEAt5B,IACAyhD,QAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,MAAA4tD,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAA0I,WAC1IyS,GAAA47C,iBACAT,EAAAryC,MAAA2zC,MAAAlB,YAGGJ,EAAA15C,GAAA,WAAA05C,EAAAn5C,GAAA,KAAAgB,EAAA,SACH1N,aACA3S,KAAA,QACAwyB,QAAA,UACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEAhsB,OACAjV,KAAA,QACAwtC,SAAAynB,EAAAznB,SACA/wC,KAAAw4D,EAAAx4D,MAEAkd,UACA3U,MAAAiwD,EAAAgB,YACAS,QAAAzB,EAAAz5C,GAAAy5C,EAAAxJ,SAAAwJ,EAAAgB,cAEA/hD,IACAgd,OAAA,SAAApX,GACAm7C,EAAAxJ,SAAAwJ,EAAAgB,iBAGG,MACFnyD,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAmvD,GAAAr4D,EAAA,GACA6pE,EAAA7pE,EAAA,GAyBAiJ,GAAA,SACAtI,KAAA,YACAqT,QAAA61D,EAAA,GACA92D,OACA+2D,YACA5lE,KAAAiG,OACArC,QAAA,MAEAiiE,UACA7lE,KAAA08B,SACA94B,QAAA,cAEAg9C,YACA5gD,KAAAmD,QACAS,SAAA,IAGAmH,KAAA,WACA,OACA81C,YAAA/hD,KAAA+4B,UAAAs8B,EAAA,EAAAtf,0BAIAvuB,SAKAw/C,OAAA,WACAhnE,KAAA+mE,WACA/mE,KAAAoxC,YAOA,SAAAh0C,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,cACA7H,OACA8wD,qBAAA9Q,EAAAp+B,WAAAJ,MACAuvC,qBAAA/Q,EAAAp+B,WAAAmC,SAEGlc,EAAA,OACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEA7V,YAAA,WACA1L,OAAAu1C,EAAAj1D,KAAAi1D,EAAAjV,YACGljC,EAAA,KACHsO,YAAA,SACG6pC,EAAAn5C,GAAAm5C,EAAA55C,GAAA45C,EAAAjc,YAAAic,EAAAn5C,GAAA,KAAAm5C,EAAA,WAAAn4C,EAAA,OACHsO,YAAA,SACA1L,MAAAu1C,EAAAj1D,KACAkU,IACAmhD,MAAAJ,EAAA6Q,UAEGhpD,EAAA,UACHsO,YAAA,mBACG6pC,EAAAn5C,GAAAm5C,EAAA55C,GAAA45C,EAAA2Q,iBAAA3Q,EAAAl5C,UACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA+wD,GAAAj6D,EAAA,GACAk6D,EAAAl6D,EAAAmB,EAAA84D,EA6BAhxD,GAAA,SACAtI,KAAA,UACAoS,OACA7J,OAAAiB,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAquD,aAAAhwD,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACA4lC,SAAArqC,QACAnD,KAAAiG,OACAxJ,KAAAwJ,OACAwxC,KAAAxxC,OACAkwD,WACAn2D,MAAAiG,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAhE,SAAA,GAEAwyD,YACAp2D,MAAAiG,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAhE,SAAA,IAGAmH,KAAA,WACA,OACA0gD,SAAA3sD,KAAAkG,MACAihE,aAAA,IAIAr/C,OAIA5hB,MAAA,SAAAktB,GACApzB,KAAA2sD,SAAAv5B,GAMAu5B,SAAA,SAAAzmD,GACAlG,KAAA8lB,MAAA,QAAA5f,OAOA,SAAA9I,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,SACAmQ,IAAA,QACA7B,YAAA,SACA1L,OAAAu1C,EAAAxd,MACA4e,cAAApB,EAAAznB,WAEAv4B,OACAu4B,SAAAynB,EAAAznB,SACA8oB,UAAArB,EAAAznB,UAAA,GAEAt5B,IACAyhD,QAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,MAAA4tD,EAAAr5C,GAAA9B,EAAA27C,QAAA,WAAA37C,EAAAzS,KAA0I,WAC1IyS,GAAA47C,iBACAT,EAAAryC,MAAA2zC,MAAAlB,SAEA6Q,UAAA,SAAApsD,GACAm7C,EAAAgR,aAAA,GAEAE,QAAA,SAAArsD,GACAm7C,EAAAgR,aAAA,GAEAG,SAAA,SAAAtsD,GACAm7C,EAAAgR,aAAA,GAEA9Q,KAAA,SAAAr7C,GACAm7C,EAAAgR,aAAA,MAGGnpD,EAAA,SACH1N,aACA3S,KAAA,QACAwyB,QAAA,UACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEAhsB,OACAjV,KAAA,WACAwtC,SAAAynB,EAAAznB,SACA/wC,KAAAw4D,EAAAx4D,KACA+5D,aAAAvB,EAAAkB,UACAM,cAAAxB,EAAAmB,YAEAz8C,UACA3U,MAAAiwD,EAAAgB,YACAS,QAAAlwD,MAAAC,QAAAwuD,EAAAxJ,UAAAwJ,EAAAx5C,GAAAw5C,EAAAxJ,SAAAwJ,EAAAgB,cAAA,EAAAhB,EAAAz5C,GAAAy5C,EAAAxJ,SAAAwJ,EAAAkB,YAEAjiD,IACAmhD,MAAA,SAAAv7C,GACAA,EAAAusD,mBAEAn1C,OAAA,SAAApX,GACA,GAAA68C,GAAA1B,EAAAxJ,SACAmL,EAAA98C,EAAA1P,OACAysD,EAAAD,EAAAF,QAAAzB,EAAA,UAAAA,EAAA,UACA,IAAAzuD,MAAAC,QAAAkwD,GAAA,CACA,GAAAf,GAAAX,EAAAgB,YACAa,EAAA7B,EAAAx5C,GAAAk7C,EAAAf,EACAgB,GAAAF,QACAI,EAAA,IAAA7B,EAAAxJ,SAAAkL,EAAA14D,QAAA23D,KAEAkB,GAAA,IAAA7B,EAAAxJ,SAAAkL,EAAA1rD,MAAA,EAAA6rD,GAAA74D,OAAA04D,EAAA1rD,MAAA6rD,EAAA,SAGA7B,GAAAxJ,SAAAoL,MAIG5B,EAAAn5C,GAAA,KAAAgB,EAAA,QACHsO,YAAA,QACA1L,QACA4mD,aAAArR,EAAAgR,cAAAhR,EAAAznB,UACKynB,EAAAj1D,QACFi1D,EAAAn5C,GAAA,KAAAgB,EAAA,QACHsO,YAAA,kBACG6pC,EAAA15C,GAAA,kBACFzX,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAmBAiyD,GAnBAsP,EAAAzqE,EAAA,KACA0qE,EAAA1qE,EAAAmB,EAAAspE,GACAzI,EAAAhiE,EAAA,GACAiiE,EAAAjiE,EAAAmB,EAAA6gE,GACA3G,EAAAr7D,EAAA,GACA2qE,EAAA3qE,EAAA,IACA4qE,EAAA5qE,EAAAmB,EAAAwpE,GACAE,EAAA7qE,EAAA,GACA8qE,EAAA9qE,EAAAmB,EAAA0pE,GACAE,EAAA/qE,EAAA,IACAgrE,EAAAhrE,EAAAmB,EAAA4pE,GACAE,EAAAjrE,EAAA,KACAkrE,EAAAlrE,EAAAmB,EAAA8pE,GACAE,EAAAnrE,EAAA,IACAorE,EAAAprE,EAAAmB,EAAAgqE,GACAE,EAAArrE,EAAA,GA+LAiJ,GAAA,SACAtI,KAAA,SACAgsB,YAAAwuC,KAAiC8G,IAAA9G,EAAAyP,EAAA9+D,EAAAnL,KAAAiqE,EAAA9+D,GAAAm2D,IAAA9G,EAAA2P,EAAAh/D,EAAAnL,KAAAmqE,EAAAh/D,GAAAm2D,IAAA9G,EAAA6P,EAAAl/D,EAAAnL,KAAAqqE,EAAAl/D,GAAAm2D,IAAA9G,EAAA+P,EAAAp/D,EAAAnL,KAAAuqE,EAAAp/D,GAAAm2D,IAAA9G,EAAAiQ,EAAAt/D,EAAAnL,KAAAyqE,EAAAt/D,GAAAqvD,GACjCnnD,QAAAq3D,EAAA,GACAt4D,OACA9D,MACA/K,KAAAwG,MACA5C,QAAA,WACA,WAGAwjE,SACApnE,KAAAwG,MACA5C,QAAA,WACA,WAGAyjE,SAAAlkE,QACAmkE,QAAAnkE,QACAokE,SAAApkE,QACAi4D,UAAAj4D,QACAwd,QAAAxd,QACAqkE,SAAArkE,QACAskE,UAAAtkE,QACA8oB,SAAArvB,OACA8qE,UAAAvkE,QACAwkE,gBAAAjrC,SACAkrC,gBACA5nE,KAAA08B,SACA94B,QAAA,WACA,WAGAikE,aACA7nE,KAAAwG,MACA5C,QAAA,WACA,WAGAkkE,aACA9nE,KAAAmD,QACAS,SAAA,GAEAmkE,aAAA9hE,OAAAO,OACAwhE,sBACAhoE,KAAAiG,OACArC,QAAA,OAEAqkE,UAAA9kE,QACA+kE,aACAloE,KAAAw2B,OACA5yB,QAAA,GAEAwgE,SACApkE,MAAAw2B,OAAAvwB,QACArC,QAAA,IAEAukE,iBAAAhlE,QACAilE,eAAAniE,OACAoiE,eAAAllE,QACAmlE,UACAtoE,KAAA08B,SACA94B,QAAA,WACA,WAGA2kE,gBACAvoE,KAAAwG,MACA5C,QAAA,WACA,WAGA4kE,oBACAxoE,KAAA08B,SACA94B,QAAA,WACA,WAGA6kE,WACAzoE,KAAAiG,OACArC,QAAA,IAEA8kE,kBAAAvlE,QACAghE,OACAnkE,MAAAw2B,OAAAvwB,QACArC,QAAA,IAGAmH,KAAA,WACA,OACAirC,eAAAmhB,EAAA,EACAwR,cAAA1qE,OAAAuoE,IAAA1nE,KAAAsoE,UACAwB,kBAAA9pE,KAAAypE,eACAM,QAAA/pE,KAAAiM,KACA+9D,aAAAhqE,KAAA4pE,kBAAA5pE,KAAAqlE,MAAArlE,KAAAiM,KAAA9L,OACA8pE,kBAAA9qE,OAAAuoE,IAAA1nE,KAAA+oE,cACAmB,eAAAlqE,KAAAopE,YACAe,qBACAC,OAAA,EACAC,eAAA,EACAC,UAAA,IAIA1iD,UACA2iD,aAAA,WACA,OACAC,cAAAxqE,KAAAuoE,SACAkC,aAAAzqE,KAAAwoE,QACAkC,YAAA1qE,KAAAyoE,SACAkC,mBAAA3qE,KAAAgpE,YACAvM,gBAAAz8D,KAAAs8D,WAAAt8D,KAAA4oE,YAAA5oE,KAAA4qE,YAAAzqE,SAQAyqE,YAAA,WACA,IAAA5qE,KAAAmpE,UAAA,MAAAnpE,MAAA+pE,OAEA,IAAAX,GAAAppE,KAAAkqE,eACA5E,EAAAtlE,KAAAslE,OAEA,IAAAtlE,KAAA+pE,QAAA5pE,QAAAmlE,EACA,MAAAtlE,MAAA+pE,OAEA,IAAA3gE,IAAAggE,EAAA,GAAA9D,EACA9uC,EAAAyN,SAAA76B,EAAA,IAAA66B,SAAAqhC,EAAA,GACA,OAAAtlE,MAAA+pE,QAAA59D,MAAA/C,EAAAotB,IAQAq0C,aAAA,WACA,GAAAhxB,GAAA75C,KAEA8qE,EAAA9qE,KAAA4qE,YAAAxmE,OAAA,SAAA2mE,GACA,MAAAlxB,GAAAivB,eAAAiC,IAEA,YAAAD,EAAA3qE,SACA2qE,EAAAvgC,KAAA,SAAAygC,GACA,MAAAltE,QAAAu6D,EAAA,GAAAxe,EAAAowB,eAAAe,EAAAnxB,EAAAgvB,iBAAA,KASAoC,iBAAA,WACA,GAAArpB,GAAA5hD,IAKA,YAHAA,KAAA4qE,YAAAxmE,OAAA,SAAA2mE,GACA,MAAAnpB,GAAAknB,eAAAiC,KAEA5qE,QAOA+qE,sBAAA,WACA,MAAAlrE,MAAA6pE,WAAAt/B,KAAA,SAAA4gC,GACA,MAAAA,GAAAC,YAQAC,YAAA,WACA,GAAAC,GAAAtrE,KAAA6pE,WAAA1pE,MAIA,OAHAmrE,IAAAtrE,KAAA2oE,UAAA,IACA2C,GAAAtrE,KAAA0oE,SAAA,MAKA5gD,OAQA7b,KAAA,SAAA/F,GACA,GAAA8nD,GAAAhuD,KAGA6pE,EAAA7pE,KAAA6pE,UAEA7pE,MAAA6pE,cACA7pE,KAAA+pE,QAAA7jE,EAIAlG,KAAAwjC,UAAA,WACAwqB,EAAA6b,WAAA1pE,SAAA6tD,EAAA6b,gBAGA7pE,KAAAupE,gBACAvpE,KAAAwmB,KAAAxmB,KAAAmqE,mBAAA,GAEAnqE,KAAA4pE,oBACA5pE,KAAAgqE,aAAA9jE,EAAA/F,SASAklE,MAAA,SAAAkG,GACAvrE,KAAA4pE,oBAEA5pE,KAAAgqE,aAAAuB,IAQAxC,YAAA,SAAAyC,GACAxrE,KAAAiqE,kBAAA9qE,OAAAuoE,IAAA8D,KAEAlD,QAAA,SAAApiE,GACAlG,KAAA6pE,cAAA1qE,OAAAuoE,IAAAxhE,KAOA2jE,WAAA,SAAA4B,GACA,GAAAA,EAAAtrE,QAAAH,KAAAqqE,cACArqE,KAAA0rE,WACA1rE,KAAAqqE,eAAA,MACa,IAAAoB,EAAAtrE,QACbH,KAAAmqE,kBAAAxnB,MACA,OAAAtlD,GAAA,EAAmCA,EAAAouE,EAAAtrE,OAAwB9C,IAC3D,GAAAouE,EAAApuE,GAAAslD,QAAA3iD,KAAAmqE,kBAAAxnB,MAAA,CACA3iD,KAAAmqE,kBAAAsB,EAAApuE,EACA,SAYAosE,eAAA,SAAAkC,GACA3rE,KAAA8pE,kBAAA6B,GAEAvC,YAAA,SAAA36D,GACAzO,KAAAkqE,eAAAz7D,IAGA+Y,SAKAokD,OAAA,SAAAp3B,EAAAjsC,EAAAE,EAAA2hE,GAwBA,MArBA3hE,IAAA,kBAAAA,MACAtJ,OAAAuoE,IAAAlzB,IAAAhuB,KAAA,SAAA1d,EAAAa,GACA,MAAAlB,GAAAK,EAAAa,EAAAygE,QAGAjrE,OAAAuoE,IAAAlzB,IAAAhuB,KAAA,SAAA1d,EAAAa,GAEA,GAAAkiE,GAAA/tE,OAAAu6D,EAAA,GAAAvvD,EAAAP,GACAujE,EAAAhuE,OAAAu6D,EAAA,GAAA1uD,EAAApB,EAEA,OAAAsjE,IAAA,IAAAA,EACAC,GAAA,IAAAA,EACAD,IAAAC,EAAA,GAEAD,EAAA,gBAAAA,KAAApuC,cAAAouC,EACAC,EAAA,gBAAAA,KAAAruC,cAAAquC,EAEA1B,EAAAyB,EAAAC,EAAA,KAAAD,EAAAC,GAAA,MANA,EADA,KAoBAtlD,KAAA,SAAA2kD,GACA,GAAAY,GAAAhjE,UAAA5I,OAAA,OAAAoG,KAAAwC,UAAA,IAAAA,UAAA,EAEAoiE,MAAAC,WAEAW,IACA/rE,KAAAoqE,MAAAe,IAAAnrE,KAAAmqE,mBAAAnqE,KAAAoqE,MAAA,SAAApqE,KAAAkpE,qBAAAtlE,eAEA5D,KAAAqqE,eACArqE,KAAA8lB,MAAA,OAAAqlD,EAAAxoB,MAAA3iD,KAAAoqE,MAAA,cAEApqE,KAAAupE,iBACAvpE,KAAA+pE,QAAA/pE,KAAA4rE,OAAA5rE,KAAA+pE,QAAAoB,EAAAxoB,MAAAwoB,EAAAa,WAAAhsE,KAAAoqE,QAEApqE,KAAAmqE,kBAAAgB,IAOAc,aAAA,SAAAlB,GACA,MAAAjtE,QAAAu6D,EAAA,GAAAr4D,KAAAiqE,eAAAc,EAAA/qE,KAAA6oE,kBAAA,GAOAqD,iBAAA,SAAAnB,GACA,GAAAvoE,GAAA1E,OAAAu6D,EAAA,GAAAr4D,KAAAiqE,eAAAc,EAAA/qE,KAAA6oE,gBACArmE,IAAA,GACAxC,KAAAiqE,eAAA5hE,OAAA7F,EAAA,IASA2pE,SAAA,WACA,GAAA5d,GAAAvuD,KAEA6qE,EAAA7qE,KAAA6qE,YACA7qE,MAAA4qE,YAAApzD,QAAA,SAAA40D,GACA7d,EAAA2d,iBAAAE,GACAvB,GACAtc,EAAAua,eAAAsD,IACA7d,EAAA0b,eAAA5pE,KAAA+rE,KAKApsE,KAAA8lB,MAAA,QAAA9lB,KAAAiqE,gBACAjqE,KAAA8lB,MAAA,YAAA9lB,KAAAiqE,gBAGAjqE,KAAA8lB,MAAA,qBAAA9lB,KAAAiqE,iBAQAoC,SAAA,SAAAtB,GACA/qE,KAAAisE,aAAAlB,GAGA/qE,KAAAksE,iBAAAnB,GAFA/qE,KAAAiqE,eAAA5pE,KAAA0qE,GAKA/qE,KAAA8lB,MAAA,QAAA9lB,KAAAiqE,eAAAc,GAGA/qE,KAAA8lB,MAAA,qBAAA9lB,KAAAiqE,iBAQAqC,UAAA,SAAAvB,EAAAvoE,GACAxC,KAAA8lB,MAAA,QAAAilD,GAEA/qE,KAAAmtB,WAAA49C,IAGA/qE,KAAA8lB,MAAA,SAAAilD,EAAA/qE,KAAAmtB,UAGAntB,KAAA8lB,MAAA,kBAAAilD,KAOAwB,YAAA,SAAAv+B,GACAhuC,KAAAkqE,eAAAl8B,EAAA,EAAAA,EAAA,EACAhuC,KAAA8lB,MAAA,cAAA9lB,KAAAkqE,gBACAlqE,KAAA8lB,MAAA,qBAAA9lB,KAAAkqE,iBAOAsC,cAAA,SAAAnrE,GACArB,KAAAysE,mBAAAprE,IAGArB,KAAA0sE,eAAArrE,GACArB,KAAA8lB,MAAA,gBAAAzkB,KAEArB,KAAA2sE,cAAAtrE,GACArB,KAAA8lB,MAAA,eAAAzkB,IAIArB,KAAA8lB,MAAA,wBAAA9lB,KAAA8pE,oBAEA6C,cAAA,SAAAtrE,GACA,GAAAmB,GAAAxC,KAAA4sE,gBAAAvrE,EACArB,MAAA8pE,kBAAAzpE,KAAAmC,IAEAkqE,eAAA,SAAArrE,GACA,GAAAmB,GAAAxC,KAAA4sE,gBAAAvrE,GACAhE,EAAA2C,KAAA8pE,kBAAA1hE,QAAA5F,EACAxC,MAAA8pE,kBAAAzhE,OAAAhL,EAAA,IAEAovE,mBAAA,SAAAprE,GACA,GAAAmB,GAAAxC,KAAA4sE,gBAAAvrE,EAEA,OADArB,MAAA8pE,kBAAA1hE,QAAA5F,IAAA,GASAoqE,gBAAA,SAAApqE,GACA,GAAA+F,GAAAvI,KAAA2pE,SACA,OAAAphE,GAAApI,OAAAqC,EAAA+F,GAAA/F,GAEAqqE,4BAAA,WAEA,GADA7sE,KAAAypE,eAAAtpE,OAAA,IACAH,KAAA2pE,UAAAxpE,OACA,SAAAmD,OAAA,wGAQAwpE,oBAAA,WACA,GAAA9sE,KAAA0Z,OAAAqzD,OAAA5sE,OAAA,UAEA,IAAA6L,GAAAhM,KAAA0Z,OAAAqzD,OAAA,GAAA/gE,GACA,cAAAA,GAAA,OAAAA,GASAghE,kBAAA,WACA,gBAAAhtE,KAAA0Z,OAAA,gBAOAuzD,aAAA,SAAAnwC,GACA,GAAA98B,KAAA4qE,YAAAzqE,OAAA,CAEA,GAAAqC,GAAAxC,KAAA4qE,YAAAxiE,QAAApI,KAAAmtB,UAAA2P,CAGAt6B,KAAA,IAAAA,EAAAxC,KAAA4qE,YAAAzqE,OAAA,EAAAH,KAAA4qE,YAAAzqE,OAAA,EAAAqC,EAEAxC,KAAAssE,UAAAtsE,KAAA4qE,YAAApoE,MAOAo3C,MAAA,WACA55C,KAAA4oE,WAEA5oE,KAAAwkB,IAAAhjB,cAAA,SAAAo4C,SAOA8xB,SAAA,WACA,GAAAwB,GAAAltE,IAEA,IAAAA,KAAAipE,YAAA,CAEA,GAAAkE,GAAA,GACAC,EAAAptE,KAAAkpE,oBAEAxhE,OAAAC,QAAA3H,KAAAipE,cACAkE,EAAAntE,KAAAipE,YAAA,GACAjpE,KAAAipE,YAAA,KACAmE,EAAAptE,KAAAipE,YAAA,KAGAkE,EAAAntE,KAAAipE,YAGAjpE,KAAA6pE,WAAAryD,QAAA,SAAA2zD,GACAA,EAAAxoB,QAAAwqB,IACAD,EAAA9C,MAAA,SAAAgD,EAAAxpE,cACAspE,EAAA1mD,KAAA2kD,GAAA,SAMApnC,QAAA,WACA/jC,KAAA6sE,iCAMA,SAAAzvE,EAAAD,EAAAH,GAEA,YAGAG,GAAAiB,YAAA,CAEA,IAAAoL,GAAAxM,EAAA,KAEAqwE,EAEA,SAAAhsE,GAAsC,MAAAA,MAAAjD,WAAAiD,GAAuCyD,QAAAzD,IAF7EmI,EAIArM,GAAA2H,QAAA,SAAAqD,GACA,GAAAT,MAAAC,QAAAQ,GAAA,CACA,OAAA9K,GAAA,EAAAiwE,EAAA5lE,MAAAS,EAAAhI,QAA6C9C,EAAA8K,EAAAhI,OAAgB9C,IAC7DiwE,EAAAjwE,GAAA8K,EAAA9K,EAGA,OAAAiwE,GAEA,SAAAD,EAAAvoE,SAAAqD,KAMA,SAAA/K,EAAAD,EAAAH,GAEAI,EAAAD,SAAkB2H,QAAA9H,EAAA,KAAAoB,YAAA,IAIlB,SAAAhB,EAAAD,EAAAH,GAEAA,EAAA,IACAA,EAAA,KACAI,EAAAD,QAAAH,EAAA,GAAA0K,MAAAoH,MAKA,SAAA1R,EAAAD,EAAAH,GAEA,YAEA,IAAA4L,GAAA5L,EAAA,IACA29C,EAAA39C,EAAA,IACAyM,EAAAzM,EAAA,IACAO,EAAAP,EAAA,KACAuwE,EAAAvwE,EAAA,KACAwuD,EAAAxuD,EAAA,IACAwwE,EAAAxwE,EAAA,KACAywE,EAAAzwE,EAAA,GAEA29C,KAAAQ,EAAAR,EAAAI,GAAA/9C,EAAA,cAAA0wE,GAA2EhmE,MAAAoH,KAAA4+D,KAAoB,SAE/F5+D,KAAA,SAAA6+D,GACA,GAOAxtE,GAAAmX,EAAAq4C,EAAA12C,EAPAo/B,EAAA5uC,EAAAkkE,GACAjyB,EAAA,kBAAA17C,WAAA0H,MACA2jD,EAAAtiD,UAAA5I,OACAytE,EAAAviB,EAAA,EAAAtiD,UAAA,OAAAxC,GACAsnE,MAAAtnE,KAAAqnE,EACAprE,EAAA,EACAixD,EAAAga,EAAAp1B,EAIA,IAFAw1B,IAAAD,EAAAhlE,EAAAglE,EAAAviB,EAAA,EAAAtiD,UAAA,OAAAxC,GAAA,QAEAA,IAAAktD,GAAA/X,GAAAh0C,OAAA6lE,EAAA9Z,GAMA,IADAtzD,EAAAqrD,EAAAnT,EAAAl4C,QACAmX,EAAA,GAAAokC,GAAAv7C,GAAkCA,EAAAqC,EAAgBA,IAClDgrE,EAAAl2D,EAAA9U,EAAAqrE,EAAAD,EAAAv1B,EAAA71C,MAAA61C,EAAA71C,QANA,KAAAyW,EAAAw6C,EAAAl2D,KAAA86C,GAAA/gC,EAAA,GAAAokC,KAAuDiU,EAAA12C,EAAAC,QAAAC,KAAgC3W,IACvFgrE,EAAAl2D,EAAA9U,EAAAqrE,EAAAtwE,EAAA0b,EAAA20D,GAAAje,EAAAzpD,MAAA1D,IAAA,GAAAmtD,EAAAzpD,MASA,OADAoR,GAAAnX,OAAAqC,EACA8U,MAOA,SAAAla,EAAAD,EAAAH,GAGA,GAAAg7C,GAAAh7C,EAAA,GACAI,GAAAD,QAAA,SAAA8b,EAAAxQ,EAAAvC,EAAAo5C,GACA,IACA,MAAAA,GAAA72C,EAAAuvC,EAAA9xC,GAAA,GAAAA,EAAA,IAAAuC,EAAAvC,GAEG,MAAAgE,GACH,GAAAb,GAAA4P,EAAA,MAEA,WADA1S,KAAA8C,GAAA2uC,EAAA3uC,EAAA9L,KAAA0b,IACA/O,KAOA,SAAA9M,EAAAD,EAAAH,GAGA,GAAAghD,GAAAhhD,EAAA,IACAmhD,EAAAnhD,EAAA,eACA8wE,EAAApmE,MAAAnJ,SAEAnB,GAAAD,QAAA,SAAAs7C,GACA,WAAAlyC,KAAAkyC,IAAAuF,EAAAt2C,QAAA+wC,GAAAq1B,EAAA3vB,KAAA1F,KAMA,SAAAr7C,EAAAD,EAAAH,GAEA,YAEA,IAAA00D,GAAA10D,EAAA,GACAu9C,EAAAv9C,EAAA,GAEAI,GAAAD,QAAA,SAAAkB,EAAAmE,EAAA0D,GACA1D,IAAAnE,GAAAqzD,EAAAtZ,EAAA/5C,EAAAmE,EAAA+3C,EAAA,EAAAr0C,IACA7H,EAAAmE,GAAA0D,IAMA,SAAA9I,EAAAD,EAAAH,GAEA,GAAAmhD,GAAAnhD,EAAA,eACA+wE,GAAA,CAEA,KACA,GAAAC,IAAA,GAAA7vB,IACA6vB,GAAA,kBAAiCD,GAAA,GAEjCrmE,MAAAoH,KAAAk/D,EAAA,WAAiC,UAChC,MAAA9jE,IAED9M,EAAAD,QAAA,SAAA2+C,EAAAmyB,GACA,IAAAA,IAAAF,EAAA,QACA,IAAAG,IAAA,CACA,KACA,GAAA/lE,IAAA,GACAulE,EAAAvlE,EAAAg2C,IACAuvB,GAAAx0D,KAAA,WAA6B,OAASC,KAAA+0D,GAAA,IACtC/lE,EAAAg2C,GAAA,WAAiC,MAAAuvB,IACjC5xB,EAAA3zC,GACG,MAAA+B,IACH,MAAAgkE,KAMA,SAAA9wE,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAQAiyD,GARA7X,EAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACA6tB,EAAAnxE,EAAA,IACAoxE,EAAApxE,EAAAmB,EAAAgwE,GACAjP,EAAAliE,EAAA,GACAmiE,EAAAniE,EAAAmB,EAAA+gE,EAsCAj5D,GAAA,SACAtI,KAAA,mBACAgsB,YAAAwuC,KAAiC5X,IAAA4X,EAAAiW,EAAAtlE,EAAAnL,KAAAywE,EAAAtlE,GAAAy3C,IAAA4X,EAAAgH,EAAAr2D,EAAAnL,KAAAwhE,EAAAr2D,GAAAqvD,GACjCpoD,OACAo6D,kBAAArsE,OACAssE,MAAA/lE,QACAikE,QAAA5gE,OAEAuE,KAAA,WACA,OACAoiE,WAAAruE,KAAAmqE,oBAIAriD,OACAumD,WAAA,SAAAlD,GACAnrE,KAAAmqE,oBAAAgB,GAEAnrE,KAAA8lB,MAAA,OAAAqlD,IAEAhB,kBAAA,SAAAgB,GACAnrE,KAAAquE,WAAAlD,IAGA3jD,SACAhB,KAAA,WACAxmB,KAAA8lB,MAAA,OAAA9lB,KAAAquE,gBAOA,SAAAjxE,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,4BACGtO,EAAA,OACHsO,YAAA,qBACGtO,EAAA,YACH7H,OACAyiC,SAAA,IAEA/5B,OACA3Y,MAAAiwD,EAAA,WACAt2C,SAAA,SAAAi3C,GACAX,EAAAkY,WAAAvX,GAEA30B,WAAA,eAEGg0B,EAAA35C,GAAA25C,EAAA,iBAAAgV,EAAA3oE,GACH,MAAA2oE,GAAA,SAAAntD,EAAA,UACAzV,IAAA/F,EACAqY,UACA3U,MAAAilE,KAEKhV,EAAAn5C,GAAA,qBAAAm5C,EAAA55C,GAAA4uD,EAAA1T,OAAA,oBAAAtB,EAAAl5C,QACFk5C,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,YACGtO,EAAA,UACHsO,YAAA,oBACAlX,IACAmhD,MAAAJ,EAAA3vC,QAEGxI,EAAA,UACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAAgU,oBAAAhU,EAAAkY,WACAlsC,WAAA,qCAEAvhB,OACA0tD,WAAAnY,EAAAiU,OAEAj0D,OACA2iC,KAAA,WACAH,KAAA,WACA8c,KAAA,OAEG,YACFzwD,qBAID,SAAA5H,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA+wD,GAAAj6D,EAAA,GACAk6D,EAAAl6D,EAAAmB,EAAA84D,EAYAhxD,GAAA,SACAtI,KAAA,eACAoS,OACA0nD,MAAAtwD,OACAonE,WAAApnE,OAAAuwB,QACAirB,MAAAx7C,OACAgsD,MAAAhsD,OAAAuwB,OAAArzB,QAAAu5B,SAAA9/B,OAAA4J,MAAAwvD,EAAApuD,GACAk7D,OAAAtsC,OAAAvwB,QACAqnE,QAAAnqE,QACAoqE,SAAApqE,QACA+mE,SAAA/mE,QACAqqE,SACAxtE,KAAAmD,QACAS,SAAA,GAEAknE,WAAApuC,SACA+wC,SAAAtqE,SAEA4H,KAAA,WACA,OACA2iE,OAAA5uE,KAAAuuE,WAAAvuE,KAAAy3D,QAIA7vC,UACA2sC,YAAA,WACA,OACAsa,iBAAA7uE,KAAAwuE,UAAAxuE,KAAAyuE,SACAK,oBAAA9uE,KAAAyuE,YAIAjjC,YAAA,WACA,GAAAqO,GAAA75C,IAEA,KAAAA,KAAA6S,QAAAumC,MAAAkxB,SAEA,KADAtqE,MAAAisB,WACA,GAAA3oB,OAAA,2CAGA,KAAAtD,KAAA2uE,SAAA,EAIA3uE,KAAA6S,QAAAy1D,QAAA/9B,KAAA,SAAA4gC,GACA,MAAAA,GAAAyD,SAAA/0B,EAAA+0B,UAEA5uE,KAAA6S,QAAAy1D,QAAAjoE,KAAAL,QAEAsvD,cAAA,WACA,GAAA9sD,GAAAxC,KAAA6S,QAAAy1D,QAAAjpE,IAAA,SAAA8rE,GACA,MAAAA,GAAAyD,SACSxmE,QAAApI,KAAA4uE,OACTpsE,IAAA,GACAxC,KAAA6S,QAAAy1D,QAAAjgE,OAAA7F,EAAA,MAOA,SAAApF,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAD,GAAA,QAAAn4C,EAAA,MACA4C,MAAAu1C,EAAA5B,YACAp+C,OACA44D,aAAA5Y,EAAAsB,SAEGz5C,EAAA,QAAAm4C,EAAA15C,GAAA,iBAAA05C,EAAAl5C,MACFjY,qBAID,SAAA5H,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,UACA1L,OACA8zC,aAAAyB,EAAAt0C,WAEGs0C,EAAA6S,aAAA7S,EAAA+U,sBAAAltD,EAAA,uBACH7H,OACA64D,sBAAA7Y,EAAAgU,kBACA8E,SAAA9Y,EAAAiU,MACA9B,QAAAnS,EAAA0T,YAEAz0D,IACAoR,KAAA,SAAA2kD,GAAiC,MAAAhV,GAAA3vC,KAAA2kD,OAE9BhV,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,kBACGtO,EAAA,SACHsO,YAAA,QACA1L,MAAAu1C,EAAAoU,aACAp0D,OACAqhD,WAAArB,EAAAyS,WAAA,GAEAxzD,IACAyhD,SAAA,SAAA77C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,QAAA37C,EAAAzS,KAAoF,WACpFyS,GAAA47C,iBACAT,EAAA8W,cAAA,IACO,SAAAjyD,GACP,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,UAAA37C,EAAAzS,KAAsF,WACtFyS,GAAA47C,iBACAT,EAAA8W,aAAA,QAGG9W,EAAA0T,WAAA,OAAA7rD,EAAA,SAAAA,EAAA,MAAAm4C,EAAA,SAAAn4C,EAAA,MACH7H,OACA6tD,MAAA,UAEG7N,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA,UAAAn4C,EAAA,MACHsO,YAAA,kBACGtO,EAAA,cACH7H,OACAjQ,MAAAiwD,EAAA0U,aACAn8B,SAAAynB,EAAA8U,kBAEAlsD,UACAqT,OAAA,SAAApX,GACAm7C,EAAAgW,SAAAnxD,QAGG,GAAAm7C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA35C,GAAA25C,EAAA,oBAAAgV,EAAA3oE,GACH,MAAA2oE,GAAAuD,aAAAnoE,KAAA4kE,EAAAuD,QAAA1wD,EAAA,MACAzV,IAAA/F,EACAoe,OACAsuD,kBAAA/Y,EAAAgU,oBAAAgB,EACAgE,cAAAhE,EAAAC,UAEAzqD,OACAqjD,MAAAmH,EAAAnH,MAAA,MAEA5uD,IACAmhD,MAAA,SAAAv7C,GACAA,EAAAusD,kBACApR,EAAA3vC,KAAA2kD,OAGKntD,EAAA,OACLsO,YAAA,UACA1L,OACAwuD,aAAAjE,EAAAqD,QACAa,cAAAlE,EAAAsD,YAEKtY,EAAA18C,aAAA,OAAA08C,EAAA15C,GAAA,eACL0uD,SACA3oE,WACK2zD,EAAAn5C,GAAAm5C,EAAA55C,GAAA4uD,EAAA1T,SAAAtB,EAAAn5C,GAAA,KAAAgB,EAAA,UACL1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAAgU,oBAAAgB,EACAhpC,WAAA,iCAEAvhB,OACA0tD,WAAAnY,EAAAiU,OAEAj0D,OACA2iC,KAAA,WACA2d,YAAAN,EAAA7b,SACAmb,KAAA,GACA9c,KAAA,eAEK,KAAAwd,EAAAl5C,QACF,KAAAk5C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAAyU,YAAA,OAAA5sD,EAAA,SAAAm4C,EAAA35C,GAAA25C,EAAA,qBAAA4U,EAAAvoE,GACH,OAAAwb,EAAA,MACAzV,IAAA/F,EACAoe,OAAAu1C,EAAAqT,SAAAuB,EAAAvoE,IACAy/D,cAAA8I,IAAA5U,EAAAhpC,SACAmiD,aAAAnZ,EAAA8V,aAAAlB,KAEA31D,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAAmW,UAAAvB,IAEAwE,SAAA,SAAAv0D,GACAm7C,EAAArwC,MAAA,WAAAilD,OAGK5U,EAAA,SAAAn4C,EAAA,MACLsO,YAAA,iBACK6pC,EAAAuT,mBAAAqB,GAAA/sD,EAAA,KACL7H,OACAmnD,KAAA,UAEAloD,IACAmhD,MAAA,SAAAv7C,GACAA,EAAAusD,kBACApR,EAAAqW,cAAAzB,OAGK/sD,EAAA,UACL4C,OACA6zC,cAAA0B,EAAAsW,mBAAA1B,IAEA50D,OACA2iC,KAAA,gBACA2d,YAAAN,EAAA7b,SACAmb,KAAA,OAEK,GAAAU,EAAAl5C,OAAAk5C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA,UAAAn4C,EAAA,MACLsO,YAAA,kBACKtO,EAAA,cACL7H,OACAu4B,UAAAynB,EAAA2S,eAAAiC,GACA7kE,MAAAiwD,EAAA8V,aAAAlB,IAEAhsD,UACAqT,OAAA,SAAApX,GACAm7C,EAAAkW,SAAAtB,QAGK,GAAA5U,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAA18C,aAAA,QAAA08C,EAAA15C,GAAA,gBACLsuD,MACAvoE,UACK2zD,EAAA35C,GAAA25C,EAAA,oBAAAgV,GACL,MAAAntD,GAAA,eAAAm4C,EAAAp5C,IACAxU,IAAA4iE,EAAAxoB,MACAxsC,OACAw4D,SAAA,KAEO,eAAAxD,GAAA,IAAAA,EAAA,WAAAntD,EAAA,QACPnD,UACA0Y,UAAA4iC,EAAA55C,GAAA45C,EAAAjf,eAAA6zB,EAAAI,EAAAxoB,YAEOwT,EAAAn5C,GAAA,yCAAAm5C,EAAA55C,GAAA45C,EAAAjf,eAAA6zB,EAAAI,EAAAxoB,QAAA,8CACF,GAAAwT,EAAAn5C,GAAA,KAAAm5C,EAAAuS,UAAAvS,EAAAsW,mBAAA1B,GAAA/sD,EAAA,MACLsO,YAAA,WACKtO,EAAA,MACL7H,OACAq5D,QAAArZ,EAAAkV,eAEKrtD,EAAA,OACLsO,YAAA,qBACK6pC,EAAA15C,GAAA,eACLsuD,MACAvoE,WACK,OAAA2zD,EAAAl5C,SACF,GAAAe,EAAA,SAAAA,EAAA,MACHsO,YAAA,aACGtO,EAAA,MACH7H,OACAq5D,QAAArZ,EAAAkV,eAEGlV,EAAA15C,GAAA,iBAAA05C,EAAAn5C,GAAA,SAAAzW,KAAA4vD,EAAAz8C,OAAAqzD,OAAA/uD,EAAA,SAAAA,EAAA,MACHsO,YAAA,iBACG6pC,EAAA2W,sBAAA3W,EAAA15C,GAAA,UAAAuB,EAAA,MACH7H,OACAq5D,QAAArZ,EAAAkV,eAEGlV,EAAA15C,GAAA,oBAAA05C,EAAAl5C,SAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAAwS,WAAAxS,EAAA6W,qBAAA7W,EAAAgT,UAAAnrD,EAAA,OACHsO,YAAA,UACGtO,EAAA,OACHsO,YAAA,eACG6pC,EAAA15C,GAAA,mBAAA05C,EAAAn5C,GAAA,KAAAgB,EAAA,OACHsO,YAAA,gBACG6pC,EAAA,UAAAn4C,EAAA,OACHsO,YAAA,eACGtO,EAAA,gBACH7H,OACAsgD,YAAAN,EAAA7b,SACA+qB,MAAAlP,EAAA6T,aACAyF,WAAAtZ,EAAAmP,QACAC,OAAApP,EAAAkT,iBACA1wB,KAAAwd,EAAAmT,eACAx9C,QAAAqqC,EAAA+T,gBAEA90D,IACAgd,OAAA+jC,EAAAoW,gBAEG,GAAApW,EAAAl5C,SAAAk5C,EAAAl5C,MAAA,IACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAG1D,IAwDAiyD,GAxDAp6D,EAAAf,EAAA,GACA0yE,EAAA1yE,EAAAmB,EAAAJ,GAGAmoD,EAAAlpD,EAAA,GACAmpD,EAAAnpD,EAAAmB,EAAA+nD,GAGAypB,GACAhyE,KAAA,iBACAoS,OACA0S,WACAvhB,KAAApD,OACAs5D,UAAA,GAEAz5D,MACAuD,KAAAiG,OACArC,QAAA,WAEAkH,KACA9K,KAAAiG,OACArC,QAAA,OAEA2Q,OACAvU,KAAAiG,OACArC,QAAA,iBAGA0iB,SACAooD,QAAA,WACA5vE,KAAAqiB,gBAEAwtD,eAAA,WACA,MAAA7vE,MAAAyiB,WAAAziB,KAAAyiB,UAAA5U,SAGAg2B,QAAA,WACA7jC,KAAA6vE,kBACA7vE,KAAAyiB,UAAAP,IAAAliB,KAAAyV,MAAAzV,KAAA4vE,UAGAtgB,cAAA,WACAtvD,KAAA6vE,kBACA7vE,KAAAyiB,UAAAW,KAAApjB,KAAAyV,MAAAzV,KAAA4vE,UAGA7qE,OAAA,SAAAiB,GACA,GAAAhG,KAAA6vE,iBAAA,CACA,GAAAj4D,GAAA5X,KAAAyiB,UAAA/I,OAAA1Z,KAAArC,KACA,OAAAqI,GAAAhG,KAAAgM,OAAiC4L,KA+CjC3R,GAAA,SACAtI,KAAA,QACAgsB,YAAAwuC,KAAiCuX,IAAAvX,EAAAhS,EAAAr9C,EAAAnL,KAAAwoD,EAAAr9C,GAAA4mE,IAAAvX,EAAAwX,EAAAhyE,KAAAgyE,GAAAxX,GACjCpoD,OACA7J,OAAAiB,OAAAuwB,QACAkhB,SAAAv0C,QACAnD,KAAAiG,OACAwxC,KAAAxxC,OACA+5C,SAAA/5C,OACA2oE,UACA5uE,KAAAmD,QACAS,SAAA,IAGAmH,KAAA,WACA,OACA8jE,UAAA/vE,KAAAkG,OAAA,EACA8pE,YACAC,cAAA,EACAC,SAAA,IAIAtoD,UACAuoD,WAAA,WACA,OAAAnwE,KAAAkB,KAAAlB,KAAA24C,KAAA34C,KAAAkhD,UACAqe,eAAAv/D,KAAA44C,SACAw3B,8BAAA,sBAAApwE,KAAAkB,SAIA4mB,OAIA5hB,MAAA,SAAAktB,GACApzB,KAAAqwE,UAAAj9C,IAOA48C,SAAA,WACAhwE,KAAAgwE,SAAA7vE,SACAH,KAAAgwE,SAAAhwE,KAAA+vE,WAAAjvB,UAAA,KAIAt5B,SAIA6oD,UAAA,SAAAC,GACAtwE,KAAA+vE,YAAAO,IAEAtwE,KAAAgwE,SAAAhwE,KAAA+vE,WAAAQ,WAAAvwE,KAAA+vE,UAAAO,GACAtwE,KAAAgwE,SAAAM,GAAApqC,SAAAlmC,KAAA+vE,UAAAO,GACAtwE,KAAA+vE,UAAAO,EACAtwE,KAAA8lB,MAAA,SAAAwqD,KAOAE,SAAA,SAAAtqE,GACAlG,KAAA8lB,MAAA,QAAA5f,GACAlG,KAAAqwE,UAAAnqE,KAGA69B,QAAA,WACA/jC,KAAAgwE,SAAA7vE,SACAH,KAAAgwE,SAAAhwE,KAAA+vE,WAAAjvB,UAAA,MAOA,SAAA1jD,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,SACA1L,OACA2+C,eAAApJ,EAAAvd,YAEG56B,EAAA,OACHsO,YAAA,OACA1L,MAAAu1C,EAAAga,aACGnyD,EAAA,KAAAm4C,EAAA35C,GAAA25C,EAAA,kBAAAsa,EAAAjuE,GACH,MAAAwb,GAAA,MACA1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAuqE,EAAA,QACAtuC,WAAA,oBAEA55B,IAAA/F,EACAoe,OACA+7C,YAAAxG,EAAA4Z,YAAAvtE,EAAA+0D,cAAAkZ,EAAA/hC,YAEK1wB,EAAA,KACL5I,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAAqa,SAAAhuE,OAGKiuE,EAAA/2D,OAAA,QAAAsE,EAAA,oBACL7H,OACAsM,UAAAguD,EACA9yE,KAAA,SACAqO,IAAA,YAEKykE,EAAA,KAAAzyD,EAAA,UACL7H,OACA2iC,KAAA23B,EAAA33B,KACAwc,KAAAmb,EAAAn2B,SACA3B,KAAAwd,EAAAxd,QAEKwd,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,KAAAgB,EAAA,QAAAm4C,EAAAn5C,GAAAm5C,EAAA55C,GAAAk0D,EAAAhZ,YAAA,UACFtB,EAAAn5C,GAAA,KAAAgB,EAAA,WACHsO,YAAA,gBACG6pC,EAAA15C,GAAA,kBACFzX,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAwqE,GAAA1zE,EAAA,GAYAiJ,GAAA,SACAtI,KAAA,WACAqT,QAAA0/D,EAAA,GACA3gE,OACA0nD,MAAAtwD,OACA2xC,KAAA3xC,OACAunC,SAAArqC,QACAqqE,SACAxtE,KAAAmD,QACAS,SAAA,IAGAmH,KAAA,WACA,OACA60C,UAAA,EACA6vB,eAAA,OAIAnpD,SAIA0e,SAAA,SAAA0qC,EAAApuE,GACAxC,KAAA6S,QAAAi9D,SAGA9vE,KAAA2wE,eAAAnuE,EAAAouE,EAAA,0BAFA5wE,KAAA2wE,eAAA,KAIA3wE,KAAA8gD,UAAA,GAOAyvB,WAAA,SAAAK,EAAApuE,GACAxC,KAAA6S,QAAAi9D,SAGA9vE,KAAA2wE,eAAAnuE,EAAAouE,EAAA,0BAFA5wE,KAAA2wE,eAAA,KAIA3wE,KAAA8gD,UAAA,IAGAjd,QAAA,WACA,IAAA7jC,KAAA6S,QAAAumC,MAAA82B,QAEA,KADAlwE,MAAAisB,WACA,GAAA3oB,OAAA,sCAEAtD,MAAA6S,QAAAm9D,SAAA3vE,KAAAL,OAEAsvD,cAAA,WACA,GAAA9sD,GAAAxC,KAAA6S,QAAAm9D,SAAA5nE,QAAApI,KACAwC,IAAA,GACAxC,KAAA6S,QAAAm9D,SAAA3nE,OAAA7F,EAAA,MAOA,SAAApF,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,cACA7H,OACAxY,KAAAw4D,EAAAwa,kBAEG3yD,EAAA,OACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAArV,UAAAqV,EAAAuY,QACAvsC,WAAA,wBAEA7V,YAAA,aACG6pC,EAAA15C,GAAA,kBACFzX,qBAID,SAAA5H,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,IAyC1DD,EAAA,SACAtI,KAAA,OACAoS,OACA8gE,SAAAxsE,QACAs8C,SAAAt8C,QACAnD,KAAAiG,OACAwxC,KAAAxxC,OACA0xC,QAAAx0C,QACAqqC,SAAArqC,QACAysE,SAAAzsE,QACA0sE,SACA7vE,KAAAmD,QACAS,SAAA,IAGA0iB,SAKA4pB,MAAA,WACApxC,KAAA0uC,UAEA1uC,KAAA8lB,MAAA,aAOA,SAAA1oB,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAD,GAAA0a,UAAA1a,EAAAxV,SAAA3iC,EAAA,OACAsO,YAAA,oBACGtO,EAAA,QACHsO,YAAA,MACA1L,OAAAu1C,EAAAj1D,KAAAi1D,EAAAxd,MACAmc,aAAAqB,EAAAtd,YAEG76B,EAAA,QACH4C,OACAowD,eAAA7a,EAAA2a,YAEG3a,EAAA15C,GAAA,iBAAA05C,EAAAn5C,GAAA,KAAAgB,EAAA,KACHsO,YAAA,gBACA1L,OAAAu1C,EAAAxd,MACAmc,aAAAqB,EAAAtd,UAEA1iC,OACAmnD,KAAA,SACA9F,WAAArB,EAAA4a,SAAA,EACAriC,SAAAynB,EAAAznB,UAEAt5B,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAA/kB,SAEAslB,MAAA,SAAA17C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,gBAAA37C,EAAAzS,KAA6F,WAC7FyS,GAAA47C,iBACAT,EAAA/kB,cAGGpzB,EAAA,QACHsO,YAAA,MACA1L,OAAAu1C,EAAAj1D,KAAAi1D,EAAAxd,MACAmc,aAAAqB,EAAAtd,YAEG76B,EAAA,QACH4C,OACAowD,eAAA7a,EAAA2a,YAEG3a,EAAA15C,GAAA,eAAA05C,EAAAn5C,GAAA,KAAAm5C,EAAA,SAAAn4C,EAAA,KACHsO,YAAA,kBACAnW,OACAmnD,KAAA,SACA5uB,SAAAynB,EAAAznB,SACA8oB,WAAArB,EAAA4a,SAAA,GAEA37D,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAA/kB,SAEAslB,MAAA,SAAA17C,GACA,eAAAA,KAAAm7C,EAAAr5C,GAAA9B,EAAA27C,QAAA,gBAAA37C,EAAAzS,KAA6F,WAC7FyS,GAAA47C,iBACAT,EAAA/kB,YAGG+kB,EAAAl5C,QACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,IAQ1DD,EAAA,SACAtI,KAAA,WACAoS,OACA8gE,SAAAxsE,WAMA,SAAAjH,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,cAChD,QAD0Ew8C,EAAA1zB,MAAAzkB,IAAAo4C,GAC1E,OACA9pC,YAAA,OACA1L,OACAqwD,aAAA9a,EAAA0a,YAEG1a,EAAA15C,GAAA,gBACFzX,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAaAiyD,GAbAtM,EAAA7uD,EAAA,IACA8uD,EAAA9uD,EAAAmB,EAAA0tD,GACAmT,EAAAhiE,EAAA,GACAiiE,EAAAjiE,EAAAmB,EAAA6gE,GACA3G,EAAAr7D,EAAA,GACAk0E,EAAAl0E,EAAA,IACAm0E,EAAAn0E,EAAAmB,EAAA+yE,GACAE,EAAAp0E,EAAA,IACAq0E,EAAAr0E,EAAAmB,EAAAizE,GACAE,EAAAt0E,EAAA,GA6EAiJ,GAAA,SACAtI,KAAA,YACAgsB,YAAAwuC,KAAiC8G,IAAA9G,EAAAkZ,EAAAvoE,EAAAnL,KAAA0zE,EAAAvoE,GAAAm2D,IAAA9G,EAAAgZ,EAAAroE,EAAAnL,KAAAwzE,EAAAroE,GAAAqvD,GACjCnnD,QAAAsgE,EAAA,GACAjhD,cAAA,EACAtgB,OACA7J,OACAhF,KAAAwG,MACA5C,QAAA,WACA,WAGAmH,MACA/K,KAAAwG,MACA5C,QAAA,WACA,WAGA5D,KAAAiG,OACA0xC,SACA33C,KAAAmD,QACAS,SAAA,GAEA+rE,UACA3vE,KAAAmD,QACAS,SAAA,GAEAysE,SACArwE,MAAAw2B,OAAAvwB,QACAiwD,UAAA,GAEAzU,OACAzhD,KAAAiG,OACArC,QAAA,SAEAi0C,aAAA10C,QACAqqC,SAAArqC,QACAysE,SAAAzsE,QACAmtE,iBACAtwE,KAAAwG,MACA5C,QAAA,WACA,iBAGA2sE,cACAvwE,KAAAwG,MACA5C,QAAA,WACA,YAGA4sE,SAAArtE,QACAstE,mBACAzwE,KAAAwG,MACA5C,QAAA,WACA,cAGA8sE,cACA1wE,KAAA08B,SACA94B,QAAA,WACA,WAGA+sE,iBACA3wE,KAAAmD,QACAS,SAAA,IAGAmH,KAAA,WACA,OACA6lE,KAAA9xE,KAAAkG,UACA6rE,OAAA,GACAj4B,YAAA,QACAk4B,aAAA,IAIApqD,UACA2sC,YAAA,WACA,OACAE,cAAAz0D,KAAA44C,WAGAq5B,iBAAA,WACA,OACAC,aAAAlyE,KAAAk5C,UACAi5B,eAAAnyE,KAAAulD,WAGA2P,YAAA,WACA,MAAAl1D,MAAA+xE,OAAA79C,OAAA/zB,QAEAiyE,gBAAA,WACA,MAAApyE,MAAAstD,eAAA,wBAEA+kB,cAAA,WACA,MAAAryE,MAAAutD,aAAA,sBAEAD,eAAA,WACA,QAAAttD,KAAAyZ,aAAA3U,SAEAyoD,aAAA,WACA,QAAAvtD,KAAA0Z,OAAA8zC,OAOAjI,SAAA,WACA,aAAAvlD,KAAAuxE,SAAAvxE,KAAAsyE,WAAAtyE,KAAAuxE,SAEAe,WAAA,WACA,MAAAtyE,MAAA8xE,KAAA3xE,QAQAoyE,mBAAA,WACA,GAAAC,GAAAxyE,KAAA2xE,iBAEA,OAAAa,GAAAryE,OAAA,GAAAu+B,QAAA8zC,EAAAnzE,IAAA,SAAAX,GACA,MAAAA,KAAA82B,QAAA,2BAA6C,eAChCh2B,KAAA,iBAGbsoB,OAIA5hB,MAAA,SAAAktB,GACApzB,KAAA8xE,KAAA1+C,GAEA2+C,OAAA,SAAA7rE,GACAlG,KAAA8lB,MAAA,SAAA5f,EAAAguB,SAEAqxB,SAAA,WACAvlD,KAAAulD,UAAAvlD,KAAA+5C,WAGAvyB,SACAirD,OAAA,SAAAzmE,GACA,GAAA0mE,GAAA1mE,GAAAhM,KAAA+xE,OAAA79C,MAEA,IAAAw+C,EAAA,CACA,IAAA1yE,KAAA+4C,aAAA,CACA,GAAA45B,GAAA3yE,KAAAuyE,kBACA,IAAAI,GAAAD,EAAArgE,MAAAsgE,GAMA,WALAD,GAAAxqE,MAAAyqE,GAAAtzE,IAAA,SAAAuzE,GACA,MAAAA,GAAA1+C,SACyB9vB,OAAA,SAAAwuE,GACzB,WAAAA,EAAAzyE,SACyBd,IAAAW,KAAAyyE,WAOzBzyE,KAAA6xE,kBAAA,IAAA7xE,KAAA8xE,KAAA1pE,QAAAsqE,KACA1yE,KAAA4xE,aAAAc,KACA1yE,KAAA8xE,KAAAzxE,KAAAqyE,GACA1yE,KAAA8lB,MAAA,QAAA9lB,KAAA8xE,MACA9xE,KAAA8lB,MAAA,MAAA4sD,IAIA1yE,KAAA+xE,OAAA,IAEAc,qBAAA,SAAA7mE,GACA,2BAAAA,EAAA,YAAA8/C,IAAA9/C,IACAlO,OAAAu6D,EAAA,GAAArsD,EAAAhM,KAAA2iD,OAGA32C,GAEA8mE,aAAA,SAAA93D,GAEAhb,KAAA+4C,cAAA/4C,KAAAyyE,SAEAzyE,KAAA+5C,OAAA/+B,IAEA+3D,SAAA,SAAA33C,GACA,GAAAye,GAAA75C,IAEAo7B,KAEAp7B,KAAAyyE,OAAAr3C,GACAp7B,KAAAwjC,UAAA,WACAqW,EAAAk4B,OAAA,OAGAiB,UAAA,SAAAxwE,GACA,GAAAwJ,GAAAhM,KAAA8xE,KAAAzpE,OAAA7F,EAAA,KAGA,OAFAxC,MAAA8lB,MAAA,QAAA9lB,KAAA8xE,MACA9xE,KAAA8lB,MAAA,SAAA9Z,GACAA,GAEAinE,cAAA,WACAjzE,KAAAsyE,WAAA,GACAtyE,KAAAgzE,UAAAhzE,KAAAsyE,WAAA,IAGAzb,QAAA,SAAAphD,IACA,IAAAzV,KAAAyxE,aAAArpE,QAAAqN,EAAAkhD,UAAA32D,KAAA+xE,OAAA5xE,QACAH,KAAAizE,gBAGAjzE,KAAA+4C,eAAA/4C,KAAA0xE,UAEA1xE,KAAAwxE,gBAAAppE,QAAAqN,EAAAkhD,UAAA,IACAlhD,EAAAmhD,iBACA52D,KAAAyyE,cAQA,SAAAr1E,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,mBACA1L,MAAAu1C,EAAA5B,cACGv2C,EAAA,OACHsO,YAAA,qBACA1L,OAAAu1C,EAAA7c,WAAA6c,EAAAxd,KAAAwd,EAAA8b,kBACA97D,OACAu4B,SAAAynB,EAAAznB,UAEAt5B,IACAmhD,MAAA,SAAAv7C,GACAm7C,EAAA5Q,UAAA4Q,EAAAvc,MAAA5+B,OAGGm7C,EAAA35C,GAAA25C,EAAA,cAAAnqD,EAAAxJ,GACH,MAAAwb,GAAA,SACAzV,IAAA/F,EACA2T,OACAjV,KAAAi1D,EAAAj1D,KACAy3C,KAAAwd,EAAAxd,KACAE,QAAAsd,EAAAtd,QACAg4B,SAAA1a,EAAA0a,SACAE,SAAA,EACAriC,SAAAynB,EAAAznB,SACAoiC,SAAA3a,EAAA2a,SACAnwB,SAAA,IAEAvrC,IACAg8B,MAAA,SAAAp2B,GACAm7C,EAAA6c,UAAAxwE,OAGK2zD,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAA45C,EAAA0c,qBAAA7mE,IAAA,kBACFmqD,EAAAn5C,GAAA,KAAAm5C,EAAA,SAAAn4C,EAAA,iBAAAm4C,EAAAp5C,IACHoR,IAAA,eACAhY,OACAlK,KAAAkqD,EAAAlqD,KACA02C,MAAAwT,EAAAxT,MACA7J,KAAAqd,EAAArd,KACA2d,YAAAN,EAAA7b,SACAtB,UAAAmd,EAAAnd,UACAk6B,eAAA,EACAv6B,KAAAwd,EAAAxd,KACAjK,SAAAynB,EAAAznB,SACA7sB,QAAAs0C,EAAAt0C,QACAsxD,aAAA,IAEA/9D,IACAwkC,MAAAuc,EAAAlc,QACAoc,KAAAF,EAAA2c,aACAzjB,OAAA8G,EAAA4c,UAEAh0D,UACA83C,QAAA,SAAA77C,GACAm7C,EAAAU,QAAA77C,KAGA+C,YAAAo4C,EAAAh5C,KACA5U,IAAA4tD,EAAAic,gBACA3pE,GAAA,SAAAsH,GACA,OAAAomD,EAAA15C,GAAA,gBACA2e,OAAArrB,EAAAqrB,OACA54B,MAAAuN,EAAAvN,aAIAqc,OACA3Y,MAAAiwD,EAAA,OACAt2C,SAAA,SAAAi3C,GACAX,EAAA4b,OAAAjb,GAEA30B,WAAA,WAEG,iBAAAg0B,EAAA9wC,QAAA,IAAArH,EAAA,YACHnG,KAAAs+C,EAAAkc,gBACGlc,EAAA15C,GAAA,iBAAA05C,EAAAl5C,MAAA,GAAAk5C,EAAAn5C,GAAA,KAAAm5C,EAAAob,SAAApb,EAAAnd,UAAAh7B,EAAA,KACHsO,YAAA,iBACG6pC,EAAAnd,WAAAmd,EAAAjB,YAAA,GAAAiB,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAA45C,EAAAjB,aAAA,MAAAiB,EAAA55C,GAAA45C,EAAAnd,WAAA,eAAAmd,EAAA,SAAAA,EAAAn5C,GAAA,iBAAAm5C,EAAA55C,GAAA45C,EAAAmc,YAAA,MAAAnc,EAAA55C,GAAA45C,EAAAob,SAAA,eAAApb,EAAAl5C,MAAA,GAAAk5C,EAAAl5C,QACFjY,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAmBAiyD,GAnBA7X,EAAAtjD,EAAA,GACAujD,EAAAvjD,EAAAmB,EAAAmiD,GACA8X,EAAAp7D,EAAA,IACAq7D,EAAAr7D,EAAA,GACAs7D,EAAAt7D,EAAA,GACAu7D,EAAAv7D,EAAA,IACAw7D,EAAAx7D,EAAAmB,EAAAo6D,GACAE,EAAAz7D,EAAA,IACA07D,EAAA17D,EAAAmB,EAAAs6D,GACAE,EAAA37D,EAAA,IACA47D,EAAA57D,EAAAmB,EAAAw6D,GACAE,EAAA77D,EAAA,IACA87D,EAAA97D,EAAAmB,EAAA06D,GACAE,EAAA/7D,EAAA,IACAg8D,EAAAh8D,EAAAmB,EAAA46D,GACAE,EAAAj8D,EAAA,GACAk8D,EAAAl8D,EAAAmB,EAAA86D,GAsHAma,EAAA,KACAC,EAAA,KAIAC,EAAA,SAAAptE,GACA,OAAAA,EAAA,WAAAA,GAGAqtE,EAAA,SAAAtZ,EAAA1qD,GACA,GAAAikE,GAAAvZ,EAAAwZ,WACAC,EAAAzZ,EAAA0Z,aACAC,GAAA,CASA,OAlBA,OAUArkE,EAAAskE,aACAD,EAAAJ,EAAA,GACAA,EAAA,GACAA,GAAA,GACS,IAAAA,IACTA,EAAA,KAGAF,EAAAE,GAAA,IAAAF,EAAAI,IAlBA,OAkBAnkE,EAAAskE,WAAA,KAAAD,EAAAR,EAAAC,GAAA,KAGAS,EAAA,SAAA7Z,EAAA1qD,GACA,GAAA0qD,EAAA,CACA,GAAA8Z,GAAA9Z,EACA2Z,GAAA,CACA,IAzBA,OAyBArkE,EAAAskE,WAAA,CACA,GAAAG,GAAA/Z,EAAA/xD,MAAA,IACA6rE,GAAAC,EAAA,GACAJ,EAAAI,EAAA,KAAAZ,EAEA,GAAAa,GAAAF,EAAA7rE,MAAA,KACAsrE,EAAAvvC,SAAAgwC,EAAA,OACAP,EAAAzvC,SAAAgwC,EAAA,MACA,IAAApsE,MAAA2rE,MAAA,GAAAA,EAAA,IAjCA,OAiCAjkE,EAAAskE,aAAAL,EAAA,GAAAA,EAAA,KAAA3rE,MAAA6rE,MAAA,GAAAA,EAAA,GACA,WAEA,IAAAh2E,GAAA,IAiBA,OAhBA6R,GAAAorD,eAAA9yD,MAAA0H,EAAAorD,cACAj9D,EAAA,GAAAyM,MAAAoF,EAAAorD,eAEAj9D,EAAA,GAAAyM,MACAzM,EAAAw2E,gBAAA,GACAx2E,EAAAy2E,WAAA,IAEAz2E,EAAA02E,WAAAV,GA5CA,OA6CAnkE,EAAAskE,aACAD,GAAA,KAAAJ,EACAA,EAAA,EACaI,GAAA,KAAAJ,IACbA,GAAA,KAGA91E,EAAA22E,SAAAb,GACA91E,EAEA,YAGAuI,GAAA,SACAtI,KAAA,cACAgsB,YAAAwuC,KAAiC5X,IAAA4X,EAAAS,EAAA9vD,EAAAnL,KAAAi7D,EAAA9vD,GAAAy3C,IAAA4X,EAAAW,EAAAhwD,EAAAnL,KAAAm7D,EAAAhwD,GAAAy3C,IAAA4X,EAAAa,EAAAlwD,EAAAnL,KAAAq7D,EAAAlwD,GAAAy3C,IAAA4X,EAAAe,EAAApwD,EAAAnL,KAAAu7D,EAAApwD,GAAAy3C,IAAA4X,EAAAK,EAAA1vD,EAAAnL,KAAA66D,EAAA1vD,GAAAy3C,IAAA4X,EAAAO,EAAA5vD,EAAAnL,KAAA+6D,EAAA5vD,GAAAqvD,GACjCnnD,QAAAonD,EAAA,GACA/nC,cAAA,EACAtgB,OACA7J,MAAAiE,KACAqvD,OAAAn1D,QACAiwE,QAAAnqE,KACAoqE,QAAApqE,KACA+xB,YAAA/0B,OACAyyD,UACA14D,KAAAmD,QACAS,SAAA,GAEA4pC,UACAxtC,KAAAmD,QACAS,SAAA,GAEA+uE,YACA3yE,KAAAiG,OACArC,QAhFA,KAiFAq8C,UAAA,SAAAj7C,GACA,MAlFA,OAkFAA,GAjFA,OAiFAA,IAGAsuE,kBACAtzE,KAAAw2B,OACA5yB,QAAA,GAEAyuE,eACAryE,KAAA08B,SACA94B,QAAA,SAAAm1D,EAAA1qD,GACA,wBAAA+oD,GAAA,EAAA5hB,qBACA4hB,EAAA,EAAA5hB,qBAAAujB,GAEAsZ,EAAAtZ,EAAA1qD,KAIAukE,YACA5yE,KAAA08B,SACA94B,QAAA,SAAAm1D,EAAA1qD,GACA,wBAAA+oD,GAAA,EAAA3hB,kBACA2hB,EAAA,EAAA3hB,kBAAAsjB,GAEA6Z,EAAA7Z,EAAA1qD,KAIAkrD,cACAv5D,KAAAmD,QACAS,QAAA,WACA,MAAAwzD,GAAA,EAAAxhB,gCAGAoK,SAAA/5C,OACAstE,kBAAA/sE,OAEAuE,KAAA,WACA,OACA0uD,aAAA36D,KAAAkG,MACAwuE,cAAA,KACAC,gBAAA,KACAC,iBAAA,KACA96B,YAAA,QACA+6B,eAAA,IAIAjtD,UACA4rD,MAAA,WAGA,OAFAA,MACAsB,EAAA90E,KAAA+0E,eAAA,MACA13E,EAAA,EAA2BA,EAAAy3E,EAAmBz3E,IAAA,CAC9C,GAAA6I,GAAA7I,EACAo6D,EAAAvxD,CACAlG,MAAA+0E,iBACA7uE,EAAA7I,EAAA,EACAo6D,EAAAvxD,EACAlG,KAAA40E,mBAAAxB,EACA,KAAAltE,IACAA,EAAA,GAEqBlG,KAAA40E,mBAAAvB,GACrB,KAAAntE,IACAA,GAAA,KAIAstE,EAAAnzE,MACAo3D,MAAA6b,EAAA7b,GACAvxD,UAGA,MAAAstE,IAEAE,QAAA,WAEA,OADAA,MACAr2E,EAAA,EAA2BA,EAAA,GAAQA,GAAA2C,KAAAw0E,iBACnCd,EAAArzE,MACAo3D,MAAA6b,EAAAj2E,GACA6I,MAAA7I,GAGA,OAAAq2E,IAEAsB,UAAA,WACA,OAAA5B,EAAAC,IAEA/7B,SAAA,WACA,MAAAt3C,MAAAy6D,cAAApC,EAAA,EAAAzgB,OAEAm9B,eAAA,WACA,MA7KA,OA6KA/0E,KAAA6zE,aAGA/rD,OACA+rD,WAAA,SAAA3tE,GACA,OAAAlG,KAAA00E,gBACA10E,KAAA40E,iBAAA50E,KAAA00E,eAAA,GAAArB,EAAAD,IAQAzY,aAAA,SAAAz0D,GACAlG,KAAA8lB,MAAA,QAAA5f,IASAA,MAAA,SAAAktB,GACApzB,KAAAi1E,oBAAA7hD,GACApzB,KAAA26D,aAAAvnC,GAEApzB,KAAAi5C,SAAAj5C,KAAA8jB,MAAA8+B,MAAA5I,uBAGAxyB,SACA0tD,iBAAA,SAAAhvE,GACA,OAAAlG,KAAA00E,gBACAxuE,IAAAmtE,EACA,IAAArzE,KAAA00E,cACA10E,KAAA00E,cAAA,GAEA10E,KAAA00E,eAAA,GAEiBxuE,IAAAktE,IACjB,KAAApzE,KAAA00E,cACA10E,KAAA00E,cAAA,EAEA10E,KAAA00E,eAAA,KAIA10E,KAAAm1E,mBAAAn1E,KAAA00E,cAAA10E,KAAA20E,gBAAAzuE,IAEAkvE,cAAA,SAAAlvE,GACAlG,KAAAm1E,mBAAAlxC,SAAA/9B,EAAA,IAAAlG,KAAA20E,gBAAA30E,KAAA40E,mBAEAS,gBAAA,SAAAnvE,GACAlG,KAAAm1E,mBAAAn1E,KAAA00E,cAAAzwC,SAAA/9B,EAAA,IAAAlG,KAAA40E,mBAEAO,mBAAA,SAAA3B,EAAAE,EAAAsB,GACA,MAAAxB,GAAA,MAAAE,KAAA1zE,KAAA+0E,gBAAA,OAAAC,GAAAh1E,KAAA+0E,kBACA/0E,KAAA26D,eAAA9yD,MAAA7H,KAAA26D,cACA36D,KAAA26D,aAAA,GAAAxwD,MAAAnK,KAAA26D,eAEA36D,KAAA26D,aAAA,GAAAxwD,MACAnK,KAAA26D,aAAAuZ,gBAAA,GACAl0E,KAAA26D,aAAAwZ,WAAA,IAEAn0E,KAAA26D,aAAA0Z,SAAAb,GACAxzE,KAAA26D,aAAAyZ,WAAAV,KAGAuB,oBAAA,SAAA/uE,GACAA,GACAlG,KAAA00E,cAAAxuE,EAAAutE,WACAzzE,KAAA20E,gBAAAzuE,EAAAytE,aACA3zE,KAAA40E,iBAAA1uE,EAAAutE,YAAA,GAAAJ,EAAAD,IAEApzE,KAAA00E,cAAA,KACA10E,KAAA20E,gBAAA,KACA30E,KAAA40E,iBAAAxB,IAGAkC,eAAA,SAAAC,GACA,GAAA17B,GAAA75C,KAEA0uC,GAAA,CACA,IAAA1uC,KAAAs0E,QAAA,CACA,GAAAkB,GAAAx1E,KAAAs0E,QAAAb,UACA/kC,GAAA6mC,EAAAC,EAEA,GAAAx1E,KAAAu0E,UACA7lC,EAAA,CACA,GAAA+mC,GAAAz1E,KAAAu0E,QAAAd,UACA/kC,GAAA6mC,EAAAE,EAGA,GAAAz1E,KAAAy0E,oBACA/lC,EACA,UAAA1uC,KAAA20E,gBAAA,CACA,GAAAe,GAAA11E,KAAAy0E,kBAAArwE,OAAA,SAAA6vE,GACA,MAAAA,GAAAR,aAAA8B,GAAAtB,EAAAN,eAAA95B,EAAA86B,iBAEAjmC,GAAAgnC,EAAAv1E,OAAA,MACqB,CACrB,GAAAw1E,GAAA31E,KAAAy0E,kBAAArwE,OAAA,SAAA6vE,GACA,MAAAA,GAAAR,aAAA8B,GAEA7mC,GAAAinC,EAAAx1E,SAAAH,KAAA0zE,QAAAvzE,OAIA,MAAAuuC,IAEAknC,iBAAA,SAAAC,GACA,GAAAj0B,GAAA5hD,KAEA0uC,GAAA,CACA,WAAA1uC,KAAA00E,cAAA,CACA,GAAA10E,KAAAs1E,eAAAt1E,KAAA00E,eACAhmC,GAAA,MACiB,CACjB,GAAA1uC,KAAAs0E,QAAA,CACA,GAAAkB,GAAAx1E,KAAAs0E,QAAAb,WACAqC,EAAA91E,KAAAs0E,QAAAX,YACAjlC,GAAA1uC,KAAA00E,gBAAAc,GAAAK,EAAAC,EAEA,GAAA91E,KAAAu0E,UACA7lC,EAAA,CACA,GAAA+mC,GAAAz1E,KAAAu0E,QAAAd,WACAsC,EAAA/1E,KAAAu0E,QAAAZ,YACAjlC,GAAA1uC,KAAA00E,gBAAAe,GAAAI,EAAAE,GAIA,GAAA/1E,KAAAy0E,oBACA/lC,EAAA,CAIAA,EAHA1uC,KAAAy0E,kBAAArwE,OAAA,SAAA6vE,GACA,MAAAA,GAAAR,aAAA7xB,EAAA8yB,eAAAT,EAAAN,eAAAkC,IAEA11E,OAAA,GAIA,MAAAuuC,IAOAitB,SAAA,SAAAz1D,GACA,GAAA+zD,GAAAj6D,KAAA8zE,WAAA5tE,EAAAlG,KACAA,MAAAi1E,oBAAAhb,GACAA,IAAApyD,MAAAoyD,GACAj6D,KAAA26D,aAAAV,GAGAj6D,KAAA26D,aAAA,KACA36D,KAAA8jB,MAAA8+B,MAAA+J,SAAA3sD,KAAA26D,eAQAiB,YAAA,SAAA11D,GACA,MAAAA,KAAA2B,MAAA3B,GACAlG,KAAAuzE,cAAArtE,EAAAlG,MAEA,MAQAoxC,MAAA,WACApxC,KAAA8jB,MAAA4+B,WACA1iD,KAAA8jB,MAAA4+B,SAAA5B,UAAA,IAQAk1B,aAAA,SAAA9vE,GACA,GAAA+zD,GAAA,GAAA9vD,MAAAjE,EACA,IAAAA,IAAA2B,MAAAoyD,GAAA,CACA,GAAAuZ,GAAAvZ,EAAAwZ,WACAC,EAAAzZ,EAAA0Z,YACA,OAAAL,GAAAE,GAAA,IAAAF,EAAAI,GAAA,MAEA,UAOAzX,qBAAA,SAAAxmD,GACA,GAAAwkD,GAAAxkD,EAAAnK,OAAApF,KACA,IAAA+zD,EAAA,CACAj6D,KAAA26D,eAAA9yD,MAAA7H,KAAA26D,cACA36D,KAAA26D,aAAA,GAAAxwD,MAAAnK,KAAA26D,eAEA36D,KAAA26D,aAAA,GAAAxwD,MACAnK,KAAA26D,aAAAuZ,gBAAA,GACAl0E,KAAA26D,aAAAwZ,WAAA,GAEA,IAAAF,GAAAha,EAAA/xD,MAAA,IACAlI,MAAA26D,aAAA0Z,SAAApwC,SAAAgwC,EAAA,QACAj0E,KAAA26D,aAAAyZ,WAAAnwC,SAAAgwC,EAAA,YAEAj0E,MAAA26D,aAAA,OAIA52B,QAAA,WACA/jC,KAAAi1E,oBAAAj1E,KAAAkG,UAMA,SAAA9I,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,OACAsO,YAAA,qBACA1L,OAAAu1C,EAAAxd,MACA8b,cAAA0B,EAAAvd,cAEGud,EAAA7e,UAAA6e,EAAAqD,OAAAx7C,EAAA,cACHmQ,IAAA,WACAhY,OACA+qC,SAAAiV,EAAAjV,SACAxS,SAAAynB,EAAAznB,SACA8qB,OAAArD,EAAAqD,UAEGrD,EAAAqD,OA6BArD,EAAAl5C,KA7BAe,EAAA,UAAAm4C,EAAAp5C,IACHoR,IAAA,QACAhY,OACA0B,KAAA,UACAkhC,aAAA,MACA7yC,MAAAiwD,EAAAyF,YAAAzF,EAAAwE,cACAz+B,YAAAi6B,EAAAj6B,YACAyc,KAAAwd,EAAAxd,KACAG,KAAAqd,EAAArd,KACA2d,YAAAN,EAAA7b,SACAz4B,QAAAs0C,EAAAt0C,QACA6sB,SAAAynB,EAAAznB,SACAkrB,SAAAzD,EAAAyD,SACA/gB,QAAAsd,EAAAtd,SAEAzjC,IACAwkC,MAAA,SAAA5+B,GACAm7C,EAAArwC,MAAA,QAAA9K,IAEAq7C,KAAA,SAAAr7C,GACAm7C,EAAArwC,MAAA,OAAA9K,IAAAm7C,EAAAnc,uBAGAj7B,UACAqT,OAAA,SAAApX,GACAm7C,EAAAwF,SAAA3gD,EAAA1P,OAAApF,SAGA2R,KAAA,WACG,UAAAs+C,EAAA9wC,QAAA,IAAA8wC,EAAAn5C,GAAA,KAAAgB,EAAA,mBACH7H,OACAu4B,SAAAynB,EAAAznB,SACA8uB,OAAA,MAEGx/C,EAAA,OACHsO,YAAA,oBACGtO,EAAA,WAAAA,EAAA,YACH7H,OACAu4B,SAAAynB,EAAAznB,SACAxS,YAAA,MAEAnd,UACAqT,OAAA,SAAApX,GACAm7C,EAAAif,cAAAp6D,EAAA1P,OAAApF,SAGA2Y,OACA3Y,MAAAiwD,EAAA,cACAt2C,SAAA,SAAAi3C,GACAX,EAAAue,cAAA5d,GAEA30B,WAAA,kBAEGg0B,EAAA35C,GAAA25C,EAAA,eAAAof,GACH,MAAAv3D,GAAA,UACAzV,IAAAgtE,EAAArvE,MACAiQ,OACAu4B,SAAAynB,EAAAmf,eAAAC,EAAArvE,QAEA2U,UACA3U,MAAAqvE,EAAArvE,SAEKiwD,EAAAn5C,GAAA,iCAAAm5C,EAAA55C,GAAAg5D,EAAA9d,OAAA,mCACFtB,EAAAn5C,GAAA,KAAAgB,EAAA,YACH7H,OACAu4B,SAAAynB,EAAAznB,SACAxS,YAAA,MAEAnd,UACAqT,OAAA,SAAApX,GACAm7C,EAAAkf,gBAAAr6D,EAAA1P,OAAApF,SAGA2Y,OACA3Y,MAAAiwD,EAAA,gBACAt2C,SAAA,SAAAi3C,GACAX,EAAAwe,gBAAA7d,GAEA30B,WAAA,oBAEGg0B,EAAA35C,GAAA25C,EAAA,iBAAA0f,GACH,MAAA73D,GAAA,UACAzV,IAAAstE,EAAA3vE,MACAiQ,OACAu4B,SAAAynB,EAAAyf,iBAAAC,EAAA3vE,QAEA2U,UACA3U,MAAA2vE,EAAA3vE,SAEKiwD,EAAAn5C,GAAA,iCAAAm5C,EAAA55C,GAAAs5D,EAAApe,OAAA,mCACFtB,EAAAn5C,GAAA,KAAAm5C,EAAA4e,eAuBA5e,EAAAl5C,KAvBAe,EAAA,YACH7H,OACAu4B,SAAAynB,EAAAznB,UAEA3vB,UACAqT,OAAA,SAAApX,GACAm7C,EAAA+e,iBAAAl6D,EAAA1P,OAAApF,SAGA2Y,OACA3Y,MAAAiwD,EAAA,iBACAt2C,SAAA,SAAAi3C,GACAX,EAAAye,iBAAA9d,GAEA30B,WAAA,qBAEGg0B,EAAA35C,GAAA25C,EAAA,mBAAA8f,GACH,MAAAj4D,GAAA,UACAzV,IAAA0tE,EACAp7D,UACA3U,MAAA+vE,KAEK9f,EAAAn5C,GAAA,iCAAAm5C,EAAA55C,GAAA05D,GAAA,oCACF,OAAA9f,EAAAn5C,GAAA,SAAAzW,KAAA4vD,EAAAz8C,OAAA5U,SAAAqxD,EAAAz8C,OAAA5U,QAAA3E,OAAA6d,EAAA,UACHsO,YAAA,sBACG6pC,EAAA15C,GAAA,eAAA05C,EAAAl5C,QAAA,GAAAe,EAAA,UAAAm4C,EAAAp5C,IACHoR,IAAA,QACAhY,OACAjV,KAAA,OACA63C,aAAA,MACA7yC,MAAAiwD,EAAA6f,aAAA7f,EAAAjwD,OACAg2B,YAAAi6B,EAAAj6B,YACAyc,KAAAwd,EAAAxd,KACAG,KAAAqd,EAAArd,KACA2d,YAAAN,EAAA7b,SACAz4B,QAAAs0C,EAAAt0C,QACAlT,IAAAwnD,EAAA6f,aAAA7f,EAAAoe,SACA/2B,IAAA2Y,EAAA6f,aAAA7f,EAAAme,SACA5lC,SAAAynB,EAAAznB,SACAkrB,UAAA,GAEAxkD,IACAwkC,MAAA,SAAA5+B,GACAm7C,EAAArwC,MAAA,QAAA9K,IAEAq7C,KAAA,SAAAr7C,GACAm7C,EAAArwC,MAAA,OAAA9K,IAAAm7C,EAAAnc,uBAGAj7B,UACAqT,OAAA,SAAApX,GACAm7C,EAAA8F,qBAAAjhD,MAGG,UAAAm7C,EAAA9wC,QAAA,SACFrgB,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAmvD,GAAAr4D,EAAA,GACA6pE,EAAA7pE,EAAA,GAkBAiJ,GAAA,SACAtI,KAAA,SACAqT,QAAA61D,EAAA,GACA56D,KAAA,WACA,OACA81C,YAAA/hD,KAAA+4B,UAAAs8B,EAAA,EAAArf,yBAOA,SAAA54C,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,cACA7H,OACA8wD,qBAAA9Q,EAAAp+B,WAAAJ,MACAuvC,qBAAA/Q,EAAAp+B,WAAAmC,SAEGlc,EAAA,OACH1N,aACA3S,KAAA,OACAwyB,QAAA,SACAjqB,MAAAiwD,EAAA,SACAh0B,WAAA,aAEA7V,YAAA,QACA1L,OAAAu1C,EAAAj1D,KAAAi1D,EAAAjV,YACGljC,EAAA,OACHnD,UACA0Y,UAAA4iC,EAAA55C,GAAA45C,EAAAjc,iBAGCl1C,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAAmvD,GAAAr4D,EAAA,EAmBAiJ,GAAA,SACAtI,KAAA,WACAoS,OACA+xB,QACA5gC,KAAAmD,QACAS,SAAA,GAEA5D,KAAAiG,OACAswD,MAAAtwD,OACA+5C,UACAhgD,KAAAiG,OACArC,QAAA,SACAq8C,UAAA,SAAAj7C,GACA,kDAAAkC,QAAAlC,IAAA,IAGAgwE,OAAA7xE,QACAyrE,SAAAzrE,QACA8xE,OAAA9xE,QACA+xE,OAAA/xE,QACAgyE,WAAAhyE,QACAs0C,MACAz3C,KAAAiG,OACArC,QAAA,cAGA8iB,UACA2xB,QAAA,WACA,MAAAv5C,MAAAkB,MAAAm0D,EAAA,EAAApf,oBAEAqgC,YAAA,WACA,MAAAt2E,MAAA8vE,UAAAza,EAAA,EAAAnf,2BAOA,SAAA94C,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,cAChD,QAD0Ew8C,EAAA1zB,MAAAzkB,IAAAo4C,GAC1E,QACAx1C,OAAAu1C,EAAA5c,QAAA4c,EAAAjV,SAAAiV,EAAAxd,MACAgL,QAAAwS,EAAAr0B,OACAy0C,YAAApgB,EAAAggB,OACAK,cAAArgB,EAAAmgB,YACAG,YAAAtgB,EAAA+f,OACAQ,eAAAvgB,EAAAkgB,WACAM,YAAAxgB,EAAAigB,SAEAjgE,OACA44D,aAAA5Y,EAAAsB,SAEGtB,EAAA15C,GAAA,gBACFzX,qBAID,SAAA5H,EAAAD,EAAAH,GAEA,GAAA+vC,GAAA/vC,EAAA,GAEAA,EAAA,KAEAA,EAAA,KAEA,KAEA,KAEA,KAGAI,GAAAD,QAAA4vC,EAAA5vC,SAKA,SAAAC,EAAA6I,EAAAjJ,GAEA,YACAc,QAAAC,eAAAkI,EAAA,cAA0DC,OAAA,GAC1D,IAAA0wE,GAAA55E,EAAA,GAoCAiJ,GAAA,SACAtI,KAAA,UACAqT,QAAA4lE,EAAA,GACAvmD,cAAA,EACAtgB,OACA7J,OACAhF,KAAAwG,MACA5C,QAAA,WACA,WAGAwoB,SAAAjpB,QACAqqC,SAAArqC,QACAwyE,OAAA1vE,OACA2vE,SAAAzyE,QACAnD,MACAA,KAAAiG,OACArC,QAAA,cAEAiyE,QACA71E,KAAAmD,QACAS,SAAA,IAGAmH,KAAA,WACA,OACA0gD,SAAA3sD,KAAAkG,UACA8wE,eAAA,EACAl9B,YAAA,UAIAhyB,OAOA5hB,MAAA,SAAAktB,GACApzB,KAAA2sD,SAAAv5B,EACApzB,KAAA2sD,UAAA,IAAA3sD,KAAA2sD,SAAAxsD,SACAH,KAAA8jB,MAAA8+B,MAAA18C,MAAA,OAEAlG,KAAAi5C,UAAAj5C,KAAA82E,UAAA92E,KAAAg6C,uBAGAxyB,SAMAyvD,aAAA,SAAAxhE,GACA,IAAAzV,KAAA0uC,WAAA1uC,KAAA6hB,QAAA,CACA7hB,KAAA82E,UACA92E,KAAAk3E,qBAAA,EAEA,IAAAhxE,GAAAuP,EAAAnK,OAAA6rE,OAAA1hE,EAAA2hE,aAAAD,KACA,IAAAjxE,KAAA/F,OAAA,CACA,GAAAH,KAAAstB,SAYAttB,KAAA+2E,SACA/2E,KAAA2sD,iBAXA,IAAA3sD,KAAA82E,SAAA,CACA,OAAA5wE,EAAA/F,OAGA,QAFAH,MAAA2sD,gBAKA3sD,MAAA2sD,WAOA,QAAAtvD,GAAA,EAA+BA,EAAA6I,EAAA/F,OAAkB9C,IAAA,CACjD,GAAAg6E,GAAAnxE,EAAA7I,EACA2C,MAAAs3E,UAAAD,IACAr3E,KAAA2sD,SAAAtsD,KAAAg3E,IAIAr3E,KAAA8lB,MAAA,QAAA9lB,KAAA2sD,WACA3sD,KAAA82E,UAAA92E,KAAAg6C,uBAOAk9B,oBAAA,SAAAt9B,GACA55C,KAAA0uC,UAAA1uC,KAAA6hB,UACA7hB,KAAAg3E,cAAAp9B,IAQA09B,UAAA,SAAAD,GACA,IAAAr3E,KAAA62E,OAAA,QACA,IAAAU,GAAAv3E,KAAA62E,OAAA3uE,MAAA,IACA,QAAAqvE,EAAAp3E,OAAA,QAEA,QADAq3E,IAAA,EACAn6E,EAAA,EAA2BA,EAAAk6E,EAAAp3E,SAAAq3E,EAA4Bn6E,IAAA,CACvD,GAAA6D,GAAAq2E,EAAAl6E,GAAA62B,MACA,IAAAhzB,EACA,SAAAA,EAAAu2E,UAAA,MAEA,GAAAC,GAAAL,EAAA15E,KAAAg6E,YAAA,IACAD,IAAA,GAAAL,EAAA15E,KAAA85E,UAAAC,KAAAx2E,IACAs2E,GAAA,OAIAH,GAAAn2E,KAAAmR,MAAAnR,KACAs2E,GAAA,GAKA,MAAAA,OAOA,SAAAp6E,EAAAD,GAEAC,EAAAD,SAAgB4H,OAAA,WAAmB,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAC1E,OAAAp4C,GAAA,SACAsO,YAAA,mBACG6pC,EAAA2gB,SAAA94D,EAAA,OACHsO,YAAA,mBACA1L,OAAAu1C,EAAAj1D,MACAwzD,aAAAyB,EAAAt0C,QACA01C,cAAApB,EAAAznB,SACAsoB,aAAAb,EAAA6gB,gBAEA5hE,IACAwiE,SAAA,SAAA58D,GACAA,EAAA47C,iBACAT,EAAA+gB,qBAAA,IAEAW,UAAA,SAAA78D,GACAA,EAAA47C,iBACAT,EAAA+gB,qBAAA,IAEAY,UAAA,SAAA98D,GACAA,EAAA47C,iBACAT,EAAA+gB,qBAAA,IAEAa,KAAA,SAAA/8D,GACAA,EAAA47C,iBACAT,EAAA8gB,aAAAj8D,OAGGm7C,EAAA15C,GAAA,gBAzBA05C,EAAA15C,GAAA,YAyBA05C,EAAAn5C,GAAA,KAAAgB,EAAA,QAAAm4C,EAAAp5C,IACHoR,IAAA,QACAhY,OACAjV,KAAA,OACAosB,SAAA6oC,EAAA7oC,SACAupD,OAAA1gB,EAAA0gB,OACAnoC,SAAAynB,EAAAznB,UAEAt5B,IACAgd,OAAA+jC,EAAA8gB,eAEG,QAAA9gB,EAAA9wC,QAAA,SACFrgB,0BtBuyTK,SAAU5H,EAAQD,EAASH,GuB7urBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAAgD,WAAA8B,GAAA,OvBsvrB/D,SAAU1B,EAAQD,EAASH,GwB7vrBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAAkC,GAKrEG,EAAAkD,MAAcjD,EAAAC,EAAS,qtiNAAqtiN,MxBswrBtuiN,SAAUD,EAAQD,GyBvwrBxBC,EAAAD,QAAA,SAAA0G,EAAA/D,GAGA,OAFAS,MACA03E,KACA56E,EAAA,EAAiBA,EAAAyC,EAAAK,OAAiB9C,IAAA,CAClC,GAAAuB,GAAAkB,EAAAzC,GACA+C,EAAAxB,EAAA,GACA0D,EAAA1D,EAAA,GACA2D,EAAA3D,EAAA,GACAa,EAAAb,EAAA,GACAs5E,GACA93E,GAAAyD,EAAA,IAAAxG,EACAiF,MACAC,QACA9C,YAEAw4E,GAAA73E,GAGA63E,EAAA73E,GAAAQ,MAAAP,KAAA63E,GAFA33E,EAAAF,KAAA43E,EAAA73E,IAAmCA,KAAAQ,OAAAs3E,KAKnC,MAAA33E,KzBmxrBM,SAAUnD,EAAQD,EAASH,G0BzyrBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6C,WAAA8B,GAAA,O1BkzrB5D,SAAU1B,EAAQD,EAASH,G2BzzrBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+B,GAKlEG,EAAAkD,MAAcjD,EAAAC,EAAS,2MAA2M,M3Bk0rB5N,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,Y4Bz0rBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAAm7E,GAAAn7E,EAAA,GAAAo7E,EAAAp7E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACEsrC,EAAA,EACAC,EAAA,GATF,EAWAtrC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,S5Bk1rB1B,SAAU3vC,EAAQD,EAASH,G6Bz2rBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA0D,WAAA8B,GAAA,O7Bk3rBzE,SAAU1B,EAAQD,EAASH,G8Bz3rBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA4C,GAK/EG,EAAAkD,MAAcjD,EAAAC,EAAS,S9Bk4rBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,Y+Bz4rBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAAq7E,GAAAr7E,EAAA,GAAAs7E,EAAAt7E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACEwrC,EAAA,EACAC,EAAA,GATF,EAWAxrC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,S/Bk5rB1B,SAAU3vC,EAAQD,EAASH,GgCz6rBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,OhCk7rB5E,SAAU1B,EAAQD,EAASH,GiCz7rBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,SjCk8rBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,YkCz8rBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAiBsO,YAAA,sBAAgC6pC,EAAAv5C,GAAA,GAAAu5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAkCsO,YAAA,gBAA0BtO,EAAA,OAAYsO,YAAA,iBAA2B6pC,EAAAn5C,GAAA,KAAAgB,EAAA,OAAwBsO,YAAA,eAAyBtO,EAAA,OAAYsO,YAAA,gBAA0BtO,EAAA,KAAUsO,YAAA,YAAsB6pC,EAAA,YAAAn4C,EAAA,UAAiCsO,YAAA,mBAAAlX,IAAmCmhD,MAAAJ,EAAA5nB,cAAwB4nB,EAAAn5C,GAAA,gBAAAgB,EAAA,UAAsCsO,YAAA,oBAAAlX,IAAoCmhD,MAAAJ,EAAA9nB,WAAqB8nB,EAAAn5C,GAAA,wBAC/hBhY,GAAA,WAAoC,GAAAmxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAiBsO,YAAA,iBAA2BtO,EAAA,KAAUsO,YAAA,cAAAnW,OAAiCmsD,KAAA,OAAYnM,EAAAn5C,GAAA,4BACtMu7D,GAAiBxzE,SAAAC,kBACFiB,GAAA,KlC88rBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,YmCn9rBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAAw7E,GAAAx7E,EAAA,GAAAy7E,EAAAz7E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACE2rC,EAAA,EACAC,EAAA,GATF,EAWA3rC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,SnC49rB1B,SAAU3vC,EAAQD,EAASH,GoCn/rBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,OpC4/rB5E,SAAU1B,EAAQD,EAASH,GqCngsBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,SrC4gsBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,YsCnhsBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,SAAmBsO,YAAA,SAAmBtO,EAAA,KAAUsO,YAAA,eAAyB6pC,EAAAn5C,GAAA,2BAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAyDsO,YAAA,cAAwBtO,EAAA,MAAAA,EAAA,KAAmB4C,OAAO+7C,YAAA,WAAAxG,EAAAnoB,MAAqC54B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA3nB,QAAA,eAAgC2nB,EAAAn5C,GAAA,qBAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAA,EAAA,KAA2D4C,OAAO+7C,YAAA,UAAAxG,EAAAnoB,MAAoC54B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA3nB,QAAA,cAA+B2nB,EAAAn5C,GAAA,cAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAA,EAAA,KAAoD4C,OAAO+7C,YAAA,gBAAAxG,EAAAnoB,MAA0C54B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA3nB,QAAA,oBAAqC2nB,EAAAn5C,GAAA,oBAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAA,EAAA,KAA0D4C,OAAO+7C,YAAA,aAAAxG,EAAAnoB,MAAuC54B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA3nB,QAAA,iBAAkC2nB,EAAAn5C,GAAA,iBAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAA,EAAA,KAAuD4C,OAAO+7C,YAAA,oBAAAxG,EAAAnoB,MAA8C54B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA3nB,QAAA,wBAAyC2nB,EAAAn5C,GAAA,wBAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAA,EAAA,KAA8D4C,OAAO+7C,YAAA,cAAAxG,EAAAnoB,MAAwC54B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA3nB,QAAA,kBAAmC2nB,EAAAn5C,GAAA,uBACpsChY,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,KtCwhsBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,YuC7hsBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAA07E,GAAA17E,EAAA,GAAA27E,EAAA37E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACE6rC,EAAA,EACAC,EAAA,GATF,EAWA7rC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,SvCsisB1B,SAAU3vC,EAAQD,EAASH,GwC7jsBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,OxCsksB5E,SAAU1B,EAAQD,EAASH,GyC7ksBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,SzCslsBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,Y0C7lsBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAiBsO,YAAA,QAAkBtO,EAAA,OAAYsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAAxnB,YAAA3zB,EAAA1P,OAAAssD,aAAgDzB,EAAAn5C,GAAA,wCAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAuEsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAAtnB,wBAAA7zB,EAAA1P,OAAAssD,aAA4DzB,EAAAn5C,GAAA,sDAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAqFsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAArnB,wBAAA9zB,EAAA1P,OAAAssD,aAA4DzB,EAAAn5C,GAAA,sDAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAqFsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAApnB,sBAAA/zB,EAAA1P,OAAAssD,aAA0DzB,EAAAn5C,GAAA,8DAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAA6FsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAAnnB,kBAAAh0B,EAAA1P,OAAAssD,aAAsDzB,EAAAn5C,GAAA,uDAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAsFsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAAlnB,oBAAAj0B,EAAA1P,OAAAssD,aAAwDzB,EAAAn5C,GAAA,yDAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAwFsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAAjnB,eAAAl0B,EAAA1P,OAAAssD,aAAmDzB,EAAAn5C,GAAA,mDAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAkFsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAAhnB,cAAAn0B,EAAA1P,OAAAssD,aAAkDzB,EAAAn5C,GAAA,kDAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAiFsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA/mB,cAAAp0B,EAAA1P,OAAAssD,aAAkDzB,EAAAn5C,GAAA,kDAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAiFsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA9mB,cAAAr0B,EAAA1P,OAAAssD,aAAkDzB,EAAAn5C,GAAA,uDAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAsFsO,YAAA,UAAoBtO,EAAA,SAAcsO,YAAA,aAAuBtO,EAAA,SAAc7H,OAAOjV,KAAA,WAAAwtC,SAAAynB,EAAAznB,UAA0Ct5B,IAAKmhD,MAAA,SAAAv7C,GAAyB,MAAAm7C,GAAA7mB,kBAAAt0B,EAAA1P,OAAAssD,aAAsDzB,EAAAn5C,GAAA,8DAC/oGhY,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,K1CkmsBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,Y2CvmsBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAA47E,GAAA57E,EAAA,IAAA67E,EAAA77E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACE+rC,EAAA,EACAC,EAAA,GATF,EAWA/rC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,S3CgnsB1B,SAAU3vC,EAAQD,EAASH,G4CvosBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,O5CgpsB5E,SAAU1B,EAAQD,EAASH,G6CvpsBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,S7CgqsBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,Y8CvqsBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAiBsO,YAAA,QAAkBtO,EAAA,MAAWsO,YAAA,UAAoB6pC,EAAAn5C,GAAA,WAAAgB,EAAA,KAA4BsO,YAAA,iBAAAnW,OAAoCu4B,SAAAynB,EAAAznB,UAAwBt5B,IAAKmhD,MAAAJ,EAAA5mB,SAAmB4mB,EAAAn5C,GAAA,eAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAm4C,EAAAn5C,GAAA,KAAAm5C,EAAA35C,GAAA25C,EAAA,gBAAA2iB,EAAAC,GAAuG,MAAA/6D,GAAA,OAAAA,EAAA,KAAyBsO,YAAA,uBAAiC6pC,EAAAn5C,GAAA,QAAAm5C,EAAA55C,GAAAw8D,MAAA5iB,EAAAn5C,GAAA,KAAAm5C,EAAA35C,GAAAs8D,EAAA,iBAAAxtE,EAAA0tE,GAAuG,MAAAh7D,GAAA,OAAAA,EAAA,KAAyBsO,YAAA,qBAAA+H,aAA8C4kD,cAAA,SAAqB9iB,EAAAn5C,GAAA,UAAAm5C,EAAA55C,GAAAy8D,GAAA,KAAA7iB,EAAA55C,GAAAjR,EAAAwzC,MAAA,OAAAqX,EAAAn5C,GAAA,KAAAm5C,EAAA35C,GAAAlR,EAAA,iBAAA4tE,EAAAC,GAAwI,MAAAn7D,GAAA,OAAAA,EAAA,KAAyBsO,YAAA,gBAAA+H,aAAyC4kD,cAAA,SAAqB9iB,EAAAn5C,GAAA,SAAAm5C,EAAA55C,GAAA48D,GAAA,KAAAhjB,EAAA55C,GAAA28D,EAAAE,aAAA,KAAAjjB,EAAA55C,GAAA28D,EAAAp6B,MAAA,KAAAqX,EAAA55C,GAAA28D,EAAA5N,OAAA,sBAAyI,KAAMnV,EAAAn5C,GAAA,KAAAgB,EAAA,aAA2B,IACx/BhZ,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,K9C4qsBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,Y+CjrsBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAAq8E,GAAAr8E,EAAA,IAAAs8E,EAAAt8E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACEwsC,EAAA,EACAC,EAAA,GATF,EAWAxsC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,S/C0rsB1B,SAAU3vC,EAAQD,EAASH,GgDjtsBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,OhD0tsB5E,SAAU1B,EAAQD,EAASH,GiDjusBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,SjD0usBjB,SAAUD,EAAQD,EAASH,GkD5usBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,OlDqvsB5E,SAAU1B,EAAQD,EAASH,GmD5vsBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,SnDqwsBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,YoD5wsBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,MAAAA,EAAA,OAA0B5I,IAAImhD,MAAAJ,EAAArmB,UAAoBqmB,EAAA,SAAAn4C,EAAA,QAAAm4C,EAAAn5C,GAAA,IAAAm5C,EAAA55C,GAAA45C,EAAAvmB,KAAA,gBAAAumB,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,aAAAm5C,EAAA55C,GAAA45C,EAAAt3C,MAAAu6D,aAAA,YAAAjjB,EAAAn5C,GAAA,KAAAm5C,EAAA,SAAAn4C,EAAA,MAAsL1N,aAAa3S,KAAA,OAAAwyB,QAAA,SAAAjqB,MAAAiwD,EAAA,KAAAh0B,WAAA,SAAgE9N,aAAeklD,eAAA,MAAAC,cAAA,UAA4CrjB,EAAA35C,GAAA25C,EAAAt3C,MAAA,kBAAAA,GAA6C,MAAAb,GAAA,YAAsB7H,OAAO0I,aAAiB,GAAAs3C,EAAAl5C,QACpiBjY,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,KpDixsBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,YqDtxsBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAiBsO,YAAA,QAAkBtO,EAAA,MAAWsO,YAAA,UAAoB6pC,EAAAn5C,GAAA,iBAAAgB,EAAA,KAAkCsO,YAAA,iBAAAnW,OAAoCu4B,SAAAynB,EAAAznB,UAAwBt5B,IAAKmhD,MAAAJ,EAAA5mB,SAAmB4mB,EAAAn5C,GAAA,eAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAm4C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAAA,EAAA,MAAAA,EAAA,gBAA+F7H,OAAO0I,MAAAs3C,EAAAxmB,iBAA0B,QACjZ3qC,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,KrD2xsBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,YsDhysBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAAy8E,GAAAz8E,EAAA,IAAA08E,EAAA18E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACE4sC,EAAA,EACAC,EAAA,GATF,EAWA5sC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,StDyysB1B,SAAU3vC,EAAQD,EAASH,GuDh0sBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,OvDy0sB5E,SAAU1B,EAAQD,EAASH,GwDh1sBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,SxDy1sBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,YyDh2sBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAiBsO,YAAA,QAAkBtO,EAAA,MAAWsO,YAAA,UAAoB6pC,EAAAn5C,GAAA,cAAAgB,EAAA,KAA+BsO,YAAA,iBAAAnW,OAAoCu4B,SAAAynB,EAAAznB,UAAwBt5B,IAAKmhD,MAAAJ,EAAA5mB,SAAmB4mB,EAAAn5C,GAAA,eAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAm4C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAAA,EAAA,MAAAA,EAAA,gBAA+F7H,OAAO0I,MAAAs3C,EAAApmB,cAAuB,QAC3Y/qC,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,KzDq2sBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,Y0D12sBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAA28E,GAAA38E,EAAA,IAAA48E,EAAA58E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACE8sC,EAAA,EACAC,EAAA,GATF,EAWA9sC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,S1Dm3sB1B,SAAU3vC,EAAQD,EAASH,G2D14sBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,O3Dm5sB5E,SAAU1B,EAAQD,EAASH,G4D15sBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,S5Dm6sBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,Y6D16sBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAiBsO,YAAA,QAAkBtO,EAAA,MAAWsO,YAAA,UAAoB6pC,EAAAn5C,GAAA,qBAAAgB,EAAA,KAAsCsO,YAAA,iBAAAnW,OAAoCu4B,SAAAynB,EAAAznB,UAAwBt5B,IAAKmhD,MAAAJ,EAAA5mB,SAAmB4mB,EAAAn5C,GAAA,eAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAm4C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAAA,EAAA,MAAAA,EAAA,gBAA+F7H,OAAO0I,MAAAs3C,EAAAnmB,qBAA8B,QACzZhrC,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,K7D+6sBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,Y8Dp7sBA,SAAA0vC,GAAAnnC,GACEvI,EAAQ,IADV,GAAA68E,GAAA78E,EAAA,IAAA88E,EAAA98E,EAAA,IAGA6vC,EAAyB7vC,EAAQ,GASjC8vC,EAAAJ,EAKAK,EAAAF,EACEgtC,EAAA,EACAC,EAAA,GATF,EAWAhtC,EAPA,KAEA,KAUe7mC,GAAA,EAAA8mC,EAAiB,S9D67sB1B,SAAU3vC,EAAQD,EAASH,G+Dp9sBjC,GAAA8B,GAAc9B,EAAQ,GACtB,iBAAA8B,SAA4C1B,EAAAC,EAASyB,EAAA,MACrDA,EAAAk5E,SAAA56E,EAAAD,QAAA2B,EAAAk5E,OAEah7E,GAAQ,GAA6D,WAAA8B,GAAA,O/D69sB5E,SAAU1B,EAAQD,EAASH,GgEp+sBjCG,EAAAC,EAAAD,QAA2BH,EAAQ,IAA+C,GAKlFG,EAAAkD,MAAcjD,EAAAC,EAAS,ShE6+sBjB,SAAUD,EAAQ6I,EAAqBjJ,GAE7C,YiEp/sBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAiBsO,YAAA,QAAkBtO,EAAA,MAAWsO,YAAA,UAAoB6pC,EAAAn5C,GAAA,eAAAgB,EAAA,KAAgCsO,YAAA,iBAAAnW,OAAoCu4B,SAAAynB,EAAAznB,UAAwBt5B,IAAKmhD,MAAAJ,EAAA5mB,SAAmB4mB,EAAAn5C,GAAA,eAAAm5C,EAAAn5C,GAAA,KAAAgB,EAAA,MAAAm4C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAAA,EAAA,MAAAm4C,EAAAlmB,WAAA9vC,OAAA,EAAA6d,EAAA,OAAkHqW,aAAa+I,UAAA,aAAA4mC,MAAA,SAAA+V,OAAA,SAA2D5jE,OAAQhJ,IAAA,yBAAsBgpD,EAAAlmB,cAA2BkmB,EAAAl5C,YAClgBjY,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,KjEy/sBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,YkE9/sBA,IAAA+H,GAAA,WAA0B,GAAAoxD,GAAAn2D,KAAao2D,EAAAD,EAAAx8C,eAA0BqE,EAAAm4C,EAAA1zB,MAAAzkB,IAAAo4C,CAAwB,OAAAp4C,GAAA,OAAAA,EAAA,cAAAm4C,EAAAn5C,GAAA,KAAAgB,EAAA,OAAwDsO,YAAA,YAAsBtO,EAAA,OAAYsO,YAAA,cAAwBtO,EAAA,OAAYsO,YAAA,YAAsBtO,EAAA,OAAYsO,YAAA,gBAA0BtO,EAAA,mBAAAm4C,EAAAn5C,GAAA,KAAAgB,EAAA,OAA8CsO,YAAA,WAAqB,WAAA6pC,EAAAnoB,KAAAhwB,EAAA,eAAAm4C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,eAAAm5C,EAAAnoB,KAAAhwB,EAAA,gBAAAm4C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,qBAAAm5C,EAAAnoB,KAAAhwB,EAAA,sBAAAm4C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,kBAAAm5C,EAAAnoB,KAAAhwB,EAAA,oBAAAm4C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,yBAAAm5C,EAAAnoB,KAAAhwB,EAAA,0BAAAm4C,EAAAl5C,KAAAk5C,EAAAn5C,GAAA,mBAAAm5C,EAAAnoB,KAAAhwB,EAAA,sBAAAm4C,EAAAl5C,MAAA,cACtVjY,KACAuzE,GAAiBxzE,SAAAC,kBACFiB,GAAA,KlEmgtBT,SAAU7I,EAAQ6I,EAAqBjJ,GAE7C,YAKA,SAASg9E,GAAgBC,EAAUz7B,GAAe,KAAMy7B,YAAoBz7B,IAAgB,KAAM,IAAIhG,WAAU,qCAJ3F,GAAItI,GAAoClzC,EAAoB,GACxDk9E,EAAqCl9E,EAAoB,IAC9Em9E,EAAe,WAAc,QAASl6C,GAAiB30B,EAAQyE,GAAS,IAAK,GAAI1S,GAAI,EAAGA,EAAI0S,EAAM5P,OAAQ9C,IAAK,CAAE,GAAImyD,GAAaz/C,EAAM1S,EAAImyD,GAAWvxD,WAAauxD,EAAWvxD,aAAc,EAAOuxD,EAAWxxD,cAAe,EAAU,SAAWwxD,KAAYA,EAAW1kD,UAAW,GAAMhN,OAAOC,eAAeuN,EAAQkkD,EAAWjnD,IAAKinD,IAAiB,MAAO,UAAUhR,EAAa47B,EAAYC,GAAiJ,MAA9HD,IAAYn6C,EAAiBue,EAAYjgD,UAAW67E,GAAiBC,GAAap6C,EAAiBue,EAAa67B,GAAqB77B,KmExgtBhiBx0B,WAAIG,IAAImwD,InEihtBR,ImE/gtBMC,GnE+gtBW,WmE9gtBb,QAAAA,KAAcP,EAAAh6E,KAAAu6E,GACVv6E,KAAKw6E,GAAK,KnEqktBd,MAjDAL,GAAaI,IACThyE,IAAK,UACLrC,MAAO,SmEnhtBHb,GACJ,GAAIm1E,GAAK,GAAIC,WAAU,sBAEvBD,GAAGE,OAAS,WACRr1E,EAAQopC,OAAO,gBAAgB,IAGnC+rC,EAAG3nC,UAAY,SAAS8nC,GACpB,GAAIC,GAAOh7E,KAAK46D,MAAMmgB,EAAI1uE,KACN,WAAhB2uE,EAAA,KACAv1E,EAAQopC,OAAO,YAAamsC,EAAA,QACL,gBAAhBA,EAAA,KACPv1E,EAAQopC,OAAO,iBAAkBmsC,EAAA,MACV,aAAhBA,EAAA,KACPv1E,EAAQopC,OAAO,eAAgBmsC,EAAA,MACR,oBAAhBA,EAAA,KACPv1E,EAAQopC,OAAO,oBAAqBmsC,EAAA,MACb,cAAhBA,EAAA,KACPv1E,EAAQopC,OAAO,gBAAiBmsC,EAAA,MAEhClnE,QAAQ8rB,KAAK,yBAA2Bo7C,EAAA,OAIhDJ,EAAGK,QAAU,WACTx1E,EAAQopC,OAAO,gBAAgB,IAGnCzuC,KAAKw6E,GAAKA,KnEshtBVjyE,IAAK,OACLrC,MAAO,SmEphtBN40E,GACe,OAAZ96E,KAAKw6E,IACLx6E,KAAKw6E,GAAGO,KAAKD,MnEwhtBjBvyE,IAAK,aACLrC,MAAO,WmEphtBS,OAAZlG,KAAKw6E,KACLx6E,KAAKw6E,GAAGppC,QACRpxC,KAAKw6E,GAAK,UnE0htBXD,KmErhtBPS,EAAa,GAAIT,GAEf7pC,EAAQ,GAAI4pC,KAAKW,OACnBC,QAAQ,EACRhtC,OACIE,WAAW,EACXJ,KAAM,UACNwB,UACAG,gBACAI,aACAC,oBACAC,eAEJkrC,WACIC,aADO,SACMltC,EAAOE,GAChBF,EAAME,UAAYA,GAEtBI,QAJO,SAICN,EAAOvwC,GACXuwC,EAAMF,KAAOrwC,GAEjB09E,UAPO,SAOGntC,EAAOsB,GACbtB,EAAMsB,OAASA,GAEnB8rC,eAVO,SAUQptC,EAAOyB,GAClBzB,EAAMyB,aAAeA,GAEzB4rC,aAbO,SAaMrtC,EAAO6B,GAChB7B,EAAM6B,UAAYA,GAEtByrC,kBAhBO,SAgBWttC,EAAO8B,GACrB9B,EAAM8B,iBAAmBA,GAE7ByrC,cAnBO,SAmBOvtC,EAAO+B,GACjB/B,EAAM+B,WAAaA,IAG3ByrC,SACIrtC,QADK,SACGhpC,GACJ21E,EAAW3sC,QAAQhpC,IAEvBkpC,WAJK,SAIMlpC,GACP21E,EAAWzsC,cAEfotC,YAPK,SAOOt2E,EAASy1E,GACjBE,EAAWD,KAAKD,MAKbpqC,QnE4htBT,SAAUtzC,EAAQ6I,EAAqBjJ,GAE7C;;;;;AoEjotBA,QAAA4+E,GAAA5xD,GAuBA,QAAA6xD,KACA,GAAA54E,GAAAjD,KAAAkS,QAEAjP,GAAAytC,MACA1wC,KAAAiuC,OAAA,kBAAAhrC,GAAAytC,MACAztC,EAAAytC,QACAztC,EAAAytC,MACKztC,EAAAuC,QAAAvC,EAAAuC,OAAAyoC,SACLjuC,KAAAiuC,OAAAhrC,EAAAuC,OAAAyoC,QA5BA,GAFAvW,OAAA1N,EAAAwa,QAAAt8B,MAAA,UAEA,EACA8hB,EAAAU,OAAe5kB,aAAA+1E,QACZ,CAGH,GAAA5xD,GAAAD,EAAAzrB,UAAA0rB,KACAD,GAAAzrB,UAAA0rB,MAAA,SAAAhnB,OACA,KAAAA,UAEAA,EAAAg+B,KAAAh+B,EAAAg+B,MACA46C,GAAA18E,OAAA8D,EAAAg+B,MACA46C,EACA5xD,EAAA1sB,KAAAyC,KAAAiD,KAyBA,QAAA64E,GAAAprC,GACAqrC,IAEArrC,EAAAsrC,aAAAD,EAEAA,EAAAh1D,KAAA,YAAA2pB,GAEAqrC,EAAA3mE,GAAA,gCAAA6mE,GACAvrC,EAAAwrC,aAAAD,KAGAvrC,EAAAyrC,UAAA,SAAAC,EAAAluC,GACA6tC,EAAAh1D,KAAA,gBAAAq1D,EAAAluC,MAgBA,QAAAmuC,GAAAh7E,EAAAoH,GACA3K,OAAAwM,KAAAjJ,GAAAmW,QAAA,SAAAjP,GAA2C,MAAAE,GAAApH,EAAAkH,QAG3C,QAAA3B,GAAAvF,GACA,cAAAA,GAAA,gBAAAA,GAGA,QAAAkG,GAAAN,GACA,MAAAA,IAAA,kBAAAA,GAAAO,KAkIA,QAAAlG,GAAA0J,EAAAsxE,EAAAC,GASA,GAHAD,EAAAh7E,OAAAi7E,GAGAA,EAAAx/E,QACA,OAAAwL,KAAAg0E,GAAAx/E,QAAA,CACA,IAAAu/E,EAAAE,SAAAj0E,GAOA,MAEAjH,GACA0J,EAAA7L,OAAAoJ,GACA+zE,EAAAE,SAAAj0E,GACAg0E,EAAAx/E,QAAAwL,KA0RA,QAAAk0E,GAAAh0E,EAAAi3B,GAIA,MAHAA,GAAAt3B,QAAAK,GAAA,GACAi3B,EAAAr/B,KAAAoI,GAEA,WACA,GAAApL,GAAAqiC,EAAAt3B,QAAAK,EACApL,IAAA,GACAqiC,EAAAr3B,OAAAhL,EAAA,IAKA,QAAAq/E,GAAAhsC,EAAAisC,GACAjsC,EAAAksC,SAAA9+E,OAAAmK,OAAA,MACAyoC,EAAAmsC,WAAA/+E,OAAAmK,OAAA,MACAyoC,EAAAosC,gBAAAh/E,OAAAmK,OAAA,MACAyoC,EAAAqsC,qBAAAj/E,OAAAmK,OAAA,KACA,IAAAimC,GAAAwC,EAAAxC,KAEA8uC,GAAAtsC,EAAAxC,KAAAwC,EAAAusC,SAAA3nC,MAAA,GAEA4nC,EAAAxsC,EAAAxC,EAAAyuC,GAGA,QAAAO,GAAAxsC,EAAAxC,EAAAyuC,GACA,GAAAQ,GAAAzsC,EAAAylB,GAGAzlB,GAAA0sC,UACA,IAAAC,GAAA3sC,EAAAosC,gBACAl1D,IACAy0D,GAAAgB,EAAA,SAAA50E,EAAAF,GAEAqf,EAAArf,GAAA,WAAiC,MAAAE,GAAAioC,IACjC5yC,OAAAC,eAAA2yC,EAAA0sC,QAAA70E,GACArK,IAAA,WAAwB,MAAAwyC,GAAAylB,IAAA5tD,IACxBtK,YAAA,KAOA,IAAAggC,GAAAjU,EAAA3W,OAAA4qB,MACAjU,GAAA3W,OAAA4qB,QAAA,EACAyS,EAAAylB,IAAA,GAAAnsC,IACA/d,MACAqxE,QAAApvC,GAEAtmB,aAEAoC,EAAA3W,OAAA4qB,SAGAyS,EAAAwqC,QACAqC,EAAA7sC,GAGAysC,IACAR,GAGAjsC,EAAA8sC,YAAA,WACAL,EAAAx1D,MAAA21D,QAAA,OAGAtzD,EAAAhW,SAAA,WAA8B,MAAAmpE,GAAAlxD,cAI9B,QAAA+wD,GAAAtsC,EAAA+sC,EAAAzyE,EAAA5N,EAAAu/E,GACA,GAAAz0D,IAAAld,EAAA7K,OACAqtB,EAAAkjB,EAAAusC,SAAAS,aAAA1yE,EAQA,IALA5N,EAAAugF,aACAjtC,EAAAqsC,qBAAAvvD,GAAApwB,IAIA8qB,IAAAy0D,EAAA,CACA,GAAAiB,GAAAC,EAAAJ,EAAAzyE,EAAAmB,MAAA,OACA2xE,EAAA9yE,IAAA7K,OAAA,EACAuwC,GAAA8sC,YAAA,WACAxzD,EAAA3b,IAAAuvE,EAAAE,EAAA1gF,EAAA8wC,SAIA,GAAA6vC,GAAA3gF,EAAAiI,QAAA24E,EAAAttC,EAAAljB,EAAAxiB,EAEA5N,GAAA6gF,gBAAA,SAAA7B,EAAA7zE,GAEA21E,EAAAxtC,EADAljB,EAAAjlB,EACA6zE,EAAA2B,KAGA3gF,EAAA+gF,cAAA,SAAAnX,EAAAz+D,GACA,GAAArH,GAAA8lE,EAAA1xB,KAAA/sC,EAAAilB,EAAAjlB,EACA2K,EAAA8zD,EAAA9zD,SAAA8zD,CACAoX,GAAA1tC,EAAAxvC,EAAAgS,EAAA6qE,KAGA3gF,EAAAihF,cAAA,SAAAzgF,EAAA2K,GAEA+1E,EAAA5tC,EADAljB,EAAAjlB,EACA3K,EAAAmgF,KAGA3gF,EAAAmhF,aAAA,SAAA9tE,EAAAlI,GACAy0E,EAAAtsC,EAAA+sC,EAAAzyE,EAAA7L,OAAAoJ,GAAAkI,EAAAksE,KAQA,QAAAqB,GAAAttC,EAAAljB,EAAAxiB,GACA,GAAAwzE,GAAA,KAAAhxD,EAEAuwD,GACAzvC,SAAAkwC,EAAA9tC,EAAApC,SAAA,SAAAmwC,EAAAC,EAAA36E,GACA,GAAAoP,GAAAwrE,EAAAF,EAAAC,EAAA36E,GACA66E,EAAAzrE,EAAAyrE,QACA37E,EAAAkQ,EAAAlQ,QACA/B,EAAAiS,EAAAjS,IAUA,OARA+B,MAAAqyC,OACAp0C,EAAAssB,EAAAtsB,GAOAwvC,EAAApC,SAAAptC,EAAA09E,IAGAnwC,OAAA+vC,EAAA9tC,EAAAjC,OAAA,SAAAgwC,EAAAC,EAAA36E,GACA,GAAAoP,GAAAwrE,EAAAF,EAAAC,EAAA36E,GACA66E,EAAAzrE,EAAAyrE,QACA37E,EAAAkQ,EAAAlQ,QACA/B,EAAAiS,EAAAjS,IAEA+B,MAAAqyC,OACAp0C,EAAAssB,EAAAtsB,GAOAwvC,EAAAjC,OAAAvtC,EAAA09E,EAAA37E,IAiBA,OAXAnF,QAAAmiC,iBAAA89C,GACAX,SACAl/E,IAAAsgF,EACA,WAAuB,MAAA9tC,GAAA0sC,SACvB,WAAuB,MAAAyB,GAAAnuC,EAAAljB,KAEvB0gB,OACAhwC,IAAA,WAAwB,MAAA2/E,GAAAntC,EAAAxC,MAAAljC,OAIxB+yE,EAGA,QAAAc,GAAAnuC,EAAAljB,GACA,GAAAsxD,MAEAC,EAAAvxD,EAAArtB,MAiBA,OAhBArC,QAAAwM,KAAAomC,EAAA0sC,SAAA5lE,QAAA,SAAAtW,GAEA,GAAAA,EAAAiL,MAAA,EAAA4yE,KAAAvxD,EAAA,CAGA,GAAAwxD,GAAA99E,EAAAiL,MAAA4yE,EAKAjhF,QAAAC,eAAA+gF,EAAAE,GACA9gF,IAAA,WAAwB,MAAAwyC,GAAA0sC,QAAAl8E,IACxBjD,YAAA,OAIA6gF,EAGA,QAAAZ,GAAAxtC,EAAAxvC,EAAAgS,EAAA6qE,IACArtC,EAAAmsC,WAAA37E,KAAAwvC,EAAAmsC,WAAA37E,QACAb,KAAA,SAAAu+E,GACA1rE,EAAA3V,KAAAmzC,EAAAqtC,EAAA7vC,MAAA0wC,KAIA,QAAAR,GAAA1tC,EAAAxvC,EAAAgS,EAAA6qE,IACArtC,EAAAksC,SAAA17E,KAAAwvC,EAAAksC,SAAA17E,QACAb,KAAA,SAAAu+E,EAAA3qE,GACA,GAAAvK,GAAAwJ,EAAA3V,KAAAmzC,GACApC,SAAAyvC,EAAAzvC,SACAG,OAAAsvC,EAAAtvC,OACA2uC,QAAAW,EAAAX,QACAlvC,MAAA6vC,EAAA7vC,MACA+wC,YAAAvuC,EAAA0sC,QACAK,UAAA/sC,EAAAxC,OACK0wC,EAAA3qE,EAIL,OAHA1M,GAAAmC,KACAA,EAAA0K,QAAAC,QAAA3K,IAEAgnC,EAAAsrC,aACAtyE,EAAAjC,MAAA,SAAAiL,GAEA,KADAg+B,GAAAsrC,aAAAj1D,KAAA,aAAArU,GACAA,IAGAhJ,IAKA,QAAA40E,GAAA5tC,EAAAxvC,EAAAg+E,EAAAnB,GACArtC,EAAAosC,gBAAA57E,KAMAwvC,EAAAosC,gBAAA57E,GAAA,SAAAwvC,GACA,MAAAwuC,GACAnB,EAAA7vC,MACA6vC,EAAAX,QACA1sC,EAAAxC,MACAwC,EAAA0sC,WAKA,QAAAG,GAAA7sC,GACAA,EAAAylB,IAAAltC,OAAA,WAAgC,MAAAjpB,MAAA2nB,MAAA21D,SAA4B,cAItD37C,MAAA,EAAA5f,MAAA,IAGN,QAAA87D,GAAA3vC,EAAAljC,GACA,MAAAA,GAAA7K,OACA6K,EAAAmsC,OAAA,SAAAjJ,EAAA3lC,GAAyC,MAAA2lC,GAAA3lC,IAAqB2lC,GAC9DA,EAGA,QAAAywC,GAAAz9E,EAAA09E,EAAA37E,GAWA,MAVA2D,GAAA1F,aACA+B,EAAA27E,EACAA,EAAA19E,EACAA,WAOUA,OAAA09E,UAAA37E,WAGV,QAAAunB,GAAA20D,GACAn1D,GAAAm1D,IAAAn1D,IAQAA,EAAAm1D,EACAvD,EAAA5xD,IAuJA,QAAAo1D,GAAA//E,GACA,MAAAqI,OAAAC,QAAAtI,GACAA,MAAA,SAAAkJ,GAA8B,OAAUA,MAAAtB,IAAAsB,KACxCzK,OAAAwM,KAAAjL,OAAA,SAAAkJ,GAA2C,OAAUA,MAAAtB,IAAA5H,EAAAkJ,MAQrD,QAAA82E,GAAA52E,GACA,gBAAA+kB,EAAAnuB,GAOA,MANA,gBAAAmuB,IACAnuB,EAAAmuB,EACAA,EAAA,IACK,MAAAA,EAAAkQ,OAAAlQ,EAAArtB,OAAA,KACLqtB,GAAA,KAEA/kB,EAAA+kB,EAAAnuB,IAWA,QAAAigF,GAAA5uC,EAAA6uC,EAAA/xD,GAKA,MAJAkjB,GAAAqsC,qBAAAvvD,GA16BA,GAAAuuD,GACA,mBAAAhvD,SACAA,OAAAuS,6BA+CAkgD,EAAA,SAAAC,EAAAC,GACA1/E,KAAA0/E,UAEA1/E,KAAAi9D,UAAAn/D,OAAAmK,OAAA,MAEAjI,KAAA2/E,WAAAF,CACA,IAAAG,GAAAH,EAAAvxC,KAGAluC,MAAAkuC,OAAA,kBAAA0xC,eAGA5/C,GAA0B29C,YAAc3/E,cAAA,GAExCgiC,GAAA29C,WAAAz/E,IAAA,WACA,QAAA8B,KAAA2/E,WAAAhC,YAGA6B,EAAAjhF,UAAAshF,SAAA,SAAAt3E,EAAAnL,GACA4C,KAAAi9D,UAAA10D,GAAAnL,GAGAoiF,EAAAjhF,UAAAsD,YAAA,SAAA0G,SACAvI,MAAAi9D,UAAA10D,IAGAi3E,EAAAjhF,UAAAi+E,SAAA,SAAAj0E,GACA,MAAAvI,MAAAi9D,UAAA10D,IAGAi3E,EAAAjhF,UAAA+C,OAAA,SAAAm+E,GACAz/E,KAAA2/E,WAAAhC,WAAA8B,EAAA9B,WACA8B,EAAA/D,UACA17E,KAAA2/E,WAAAjE,QAAA+D,EAAA/D,SAEA+D,EAAAtE,YACAn7E,KAAA2/E,WAAAxE,UAAAsE,EAAAtE,WAEAsE,EAAArC,UACAp9E,KAAA2/E,WAAAvC,QAAAqC,EAAArC,UAIAoC,EAAAjhF,UAAAggF,aAAA,SAAA91E,GACA4zE,EAAAr8E,KAAAi9D,UAAAx0D,IAGA+2E,EAAAjhF,UAAA8/E,cAAA,SAAA51E,GACAzI,KAAA2/E,WAAAvC,SACAf,EAAAr8E,KAAA2/E,WAAAvC,QAAA30E,IAIA+2E,EAAAjhF,UAAA4/E,cAAA,SAAA11E,GACAzI,KAAA2/E,WAAAjE,SACAW,EAAAr8E,KAAA2/E,WAAAjE,QAAAjzE,IAIA+2E,EAAAjhF,UAAA0/E,gBAAA,SAAAx1E,GACAzI,KAAA2/E,WAAAxE,WACAkB,EAAAr8E,KAAA2/E,WAAAxE,UAAA1yE,IAIA3K,OAAAmiC,iBAAAu/C,EAAAjhF,UAAAyhC,EAEA,IAAA8/C,GAAA,SAAAC,GAEA//E,KAAAggF,YAAAD,GAAA,GAGAD,GAAAvhF,UAAAL,IAAA,SAAA8M,GACA,MAAAA,GAAAmsC,OAAA,SAAA/5C,EAAAmL,GACA,MAAAnL,GAAAo/E,SAAAj0E,IACGvI,KAAAs1C,OAGHwqC,EAAAvhF,UAAAm/E,aAAA,SAAA1yE,GACA,GAAA5N,GAAA4C,KAAAs1C,IACA,OAAAtqC,GAAAmsC,OAAA,SAAA3pB,EAAAjlB,GAEA,MADAnL,KAAAo/E,SAAAj0E,GACAilB,GAAApwB,EAAAugF,WAAAp1E,EAAA,SACG,KAGHu3E,EAAAvhF,UAAA+C,OAAA,SAAAy+E,GACAz+E,KAAAtB,KAAAs1C,KAAAyqC,IAGAD,EAAAvhF,UAAAyhF,SAAA,SAAAh1E,EAAAy0E,EAAAC,GACA,GAAAjiE,GAAAzd,SACA,KAAA0/E,OAAA,EAMA,IAAAnD,GAAA,GAAAiD,GAAAC,EAAAC,EACA,QAAA10E,EAAA7K,OACAH,KAAAs1C,KAAAinC,MACG,CACHv8E,KAAA9B,IAAA8M,EAAAmB,MAAA,OACA0zE,SAAA70E,IAAA7K,OAAA,GAAAo8E,GAIAkD,EAAA1iF,SACAs/E,EAAAoD,EAAA1iF,QAAA,SAAAkjF,EAAA13E,GACAkV,EAAAuiE,SAAAh1E,EAAA7L,OAAAoJ,GAAA03E,EAAAP,MAKAI,EAAAvhF,UAAA2hF,WAAA,SAAAl1E,GACA,GAAAxF,GAAAxF,KAAA9B,IAAA8M,EAAAmB,MAAA,OACA5D,EAAAyC,IAAA7K,OAAA,EACAqF,GAAAg3E,SAAAj0E,GAAAm3E,SAEAl6E,EAAA3D,YAAA0G,GAgCA,IAyCAyhB,GAEAixD,EAAA,SAAAh4E,GACA,GAAAwa,GAAAzd,SACA,KAAAiD,WAKA+mB,GAAA,mBAAA+C,gBAAA/C,KACAQ,EAAAuC,OAAA/C,IASA,IAAAm2D,GAAAl9E,EAAAk9E,YAAgC,KAAAA,SAChC,IAAAjF,GAAAj4E,EAAAi4E,WAA8B,KAAAA,OAAA,GAG9Bl7E,KAAAogF,aAAA,EACApgF,KAAA48E,SAAA9+E,OAAAmK,OAAA,MACAjI,KAAAqgF,sBACArgF,KAAA68E,WAAA/+E,OAAAmK,OAAA,MACAjI,KAAA88E,gBAAAh/E,OAAAmK,OAAA,MACAjI,KAAAi9E,SAAA,GAAA6C,GAAA78E,GACAjD,KAAA+8E,qBAAAj/E,OAAAmK,OAAA,MACAjI,KAAAsgF,gBACAtgF,KAAAugF,WAAA,GAAAv2D,EAGA,IAAA0mB,GAAA1wC,KACAmuB,EAAAnuB,KACAsuC,EAAAngB,EAAAmgB,SACAG,EAAAtgB,EAAAsgB,MACAzuC,MAAAsuC,SAAA,SAAAptC,EAAA09E,GACA,MAAAtwC,GAAA/wC,KAAAmzC,EAAAxvC,EAAA09E,IAEA5+E,KAAAyuC,OAAA,SAAAvtC,EAAA09E,EAAA37E,GACA,MAAAwrC,GAAAlxC,KAAAmzC,EAAAxvC,EAAA09E,EAAA37E,IAIAjD,KAAAk7E,QAEA,IAAAhtC,GAAAluC,KAAAi9E,SAAA3nC,KAAApH,KAKA8uC,GAAAh9E,KAAAkuC,KAAAluC,KAAAi9E,SAAA3nC,MAIA4nC,EAAAl9E,KAAAkuC,GAGAiyC,EAAA3oE,QAAA,SAAA4S,GAAqC,MAAAA,GAAA3M,UAErClX,KAAAtD,EAAA6jB,SAAA7jB,EAAA6jB,SAAAkD,EAAA3W,OAAAyT,WAEAg1D,EAAA97E,OAIAwgF,GAA4BtyC,OAASlwC,cAAA,GAErCwiF,GAAAtyC,MAAAhwC,IAAA,WACA,MAAA8B,MAAAm2D,IAAAxuC,MAAA21D,SAGAkD,EAAAtyC,MAAA7/B,IAAA,SAAA/H,KAMA20E,EAAA18E,UAAAkwC,OAAA,SAAAgwC,EAAAC,EAAA36E,GACA,GAAA0Z,GAAAzd,KAGAmuB,EAAAwwD,EAAAF,EAAAC,EAAA36E,GACA7C,EAAAitB,EAAAjtB,KACA09E,EAAAzwD,EAAAywD,QAGAxC,GAFAjuD,EAAAlrB,SAEkB/B,OAAA09E,YAClB6B,EAAAzgF,KAAA68E,WAAA37E,EACAu/E,KAMAzgF,KAAAw9E,YAAA,WACAiD,EAAAjpE,QAAA,SAAAtE,GACAA,EAAA0rE,OAGA5+E,KAAAsgF,aAAA9oE,QAAA,SAAAooB,GAA4C,MAAAA,GAAAw8C,EAAA3+D,EAAAywB,WAa5C+sC,EAAA18E,UAAA+vC,SAAA,SAAAmwC,EAAAC,GACA,GAAAjhE,GAAAzd,KAGAmuB,EAAAwwD,EAAAF,EAAAC,GACAx9E,EAAAitB,EAAAjtB,KACA09E,EAAAzwD,EAAAywD,QAEA5X,GAAgB9lE,OAAA09E,WAChB6B,EAAAzgF,KAAA48E,SAAA17E,EACA,IAAAu/E,EAAA,CAOA,IACAzgF,KAAAqgF,mBACAj8E,OAAA,SAAAw7B,GAA8B,MAAAA,GAAA9a,SAC9BtN,QAAA,SAAAooB,GAA+B,MAAAA,GAAA9a,OAAAkiD,EAAAvpD,EAAAywB,SAC5B,MAAAhkC,IAWH,OAJAu2E,EAAAtgF,OAAA,EACAiU,QAAAssE,IAAAD,EAAAphF,IAAA,SAAA6T,GAAgD,MAAAA,GAAA0rE,MAChD6B,EAAA,GAAA7B,IAEAp3E,KAAA,SAAAkC,GACA,IACA+T,EAAA4iE,mBACAj8E,OAAA,SAAAw7B,GAAgC,MAAAA,GAAA+gD,QAChCnpE,QAAA,SAAAooB,GAAiC,MAAAA,GAAA+gD,MAAA3Z,EAAAvpD,EAAAywB,SAC5B,MAAAhkC,IAML,MAAAR,OAIAuxE,EAAA18E,UAAA49E,UAAA,SAAA1zE,GACA,MAAAg0E,GAAAh0E,EAAAzI,KAAAsgF,eAGArF,EAAA18E,UAAAqiF,gBAAA,SAAAn4E,GAEA,MAAAg0E,GADA,kBAAAh0E,IAAyCqc,OAAArc,GAAaA,EACtDzI,KAAAqgF,qBAGApF,EAAA18E,UAAAupB,MAAA,SAAAlqB,EAAAqW,EAAAhR,GACA,GAAAwa,GAAAzd,IAKA,OAAAA,MAAAugF,WAAAt3D,OAAA,WAA6C,MAAArrB,GAAA6f,EAAAywB,MAAAzwB,EAAA2/D,UAA+CnpE,EAAAhR,IAG5Fg4E,EAAA18E,UAAA29E,aAAA,SAAAhuC,GACA,GAAAzwB,GAAAzd,IAEAA,MAAAw9E,YAAA,WACA//D,EAAA04C,IAAAxuC,MAAA21D,QAAApvC,KAIA+sC,EAAA18E,UAAAsiF,eAAA,SAAA71E,EAAAy0E,EAAAx8E,OACA,KAAAA,UAEA,gBAAA+H,KAAiCA,OAOjChL,KAAAi9E,SAAA+C,SAAAh1E,EAAAy0E,GACAzC,EAAAh9E,UAAAkuC,MAAAljC,EAAAhL,KAAAi9E,SAAA/+E,IAAA8M,GAAA/H,EAAA69E,eAEA5D,EAAAl9E,UAAAkuC,QAGA+sC,EAAA18E,UAAAwiF,iBAAA,SAAA/1E,GACA,GAAAyS,GAAAzd,IAEA,iBAAAgL,KAAiCA,OAMjChL,KAAAi9E,SAAAiD,WAAAl1E,GACAhL,KAAAw9E,YAAA,WACA,GAAAI,GAAAC,EAAApgE,EAAAywB,MAAAljC,EAAAmB,MAAA,MACA6d,GAAAsa,OAAAs5C,EAAA5yE,IAAA7K,OAAA,MAEAu8E,EAAA18E,OAGAi7E,EAAA18E,UAAAyiF,UAAA,SAAAC,GACAjhF,KAAAi9E,SAAA37E,OAAA2/E,GACAvE,EAAA18E,MAAA,IAGAi7E,EAAA18E,UAAAi/E,YAAA,SAAA/0E,GACA,GAAAy4E,GAAAlhF,KAAAogF,WACApgF,MAAAogF,aAAA,EACA33E,IACAzI,KAAAogF,YAAAc,GAGApjF,OAAAmiC,iBAAAg7C,EAAA18E,UAAAiiF,EAmSA,IAAAW,GAAA9B,EAAA,SAAA7xD,EAAA4zD,GACA,GAAA13E,KAuBA,OAtBA01E,GAAAgC,GAAA5pE,QAAA,SAAA2W,GACA,GAAA5lB,GAAA4lB,EAAA5lB,IACAtB,EAAAknB,EAAAlnB,GAEAyC,GAAAnB,GAAA,WACA,GAAA2lC,GAAAluC,KAAAiuC,OAAAC,MACAkvC,EAAAp9E,KAAAiuC,OAAAmvC,OACA,IAAA5vD,EAAA,CACA,GAAApwB,GAAAkiF,EAAAt/E,KAAAiuC,OAAA,WAAAzgB,EACA,KAAApwB,EACA,MAEA8wC,GAAA9wC,EAAAiI,QAAA6oC,MACAkvC,EAAAhgF,EAAAiI,QAAA+3E,QAEA,wBAAAn2E,GACAA,EAAA1J,KAAAyC,KAAAkuC,EAAAkvC,GACAlvC,EAAAjnC,IAGAyC,EAAAnB,GAAA84E,MAAA,IAEA33E,IASA43E,EAAAjC,EAAA,SAAA7xD,EAAA2tD,GACA,GAAAzxE,KAuBA,OAtBA01E,GAAAjE,GAAA3jE,QAAA,SAAA2W,GACA,GAAA5lB,GAAA4lB,EAAA5lB,IACAtB,EAAAknB,EAAAlnB,GAEAyC,GAAAnB,GAAA,WAEA,IADA,GAAA4K,MAAAX,EAAAzJ,UAAA5I,OACAqS,KAAAW,EAAAX,GAAAzJ,UAAAyJ,EAGA,IAAAi8B,GAAAzuC,KAAAiuC,OAAAQ,MACA,IAAAjhB,EAAA,CACA,GAAApwB,GAAAkiF,EAAAt/E,KAAAiuC,OAAA,eAAAzgB,EACA,KAAApwB,EACA,MAEAqxC,GAAArxC,EAAAiI,QAAAopC,OAEA,wBAAAxnC,GACAA,EAAA+B,MAAAhJ,MAAAyuC,GAAAtvC,OAAAgU,IACAs7B,EAAAzlC,MAAAhJ,KAAAiuC,QAAAhnC,GAAA9H,OAAAgU,OAGAzJ,IASA63E,EAAAlC,EAAA,SAAA7xD,EAAA4vD,GACA,GAAA1zE,KAoBA,OAnBA01E,GAAAhC,GAAA5lE,QAAA,SAAA2W,GACA,GAAA5lB,GAAA4lB,EAAA5lB,IACAtB,EAAAknB,EAAAlnB,GAGAA,GAAAumB,EAAAvmB,EACAyC,EAAAnB,GAAA,WACA,IAAAilB,GAAA8xD,EAAAt/E,KAAAiuC,OAAA,aAAAzgB,GAOA,MAAAxtB,MAAAiuC,OAAAmvC,QAAAn2E,IAGAyC,EAAAnB,GAAA84E,MAAA,IAEA33E,IASA83E,EAAAnC,EAAA,SAAA7xD,EAAAkuD,GACA,GAAAhyE,KAuBA,OAtBA01E,GAAA1D,GAAAlkE,QAAA,SAAA2W,GACA,GAAA5lB,GAAA4lB,EAAA5lB,IACAtB,EAAAknB,EAAAlnB,GAEAyC,GAAAnB,GAAA,WAEA,IADA,GAAA4K,MAAAX,EAAAzJ,UAAA5I,OACAqS,KAAAW,EAAAX,GAAAzJ,UAAAyJ,EAGA,IAAA87B,GAAAtuC,KAAAiuC,OAAAK,QACA,IAAA9gB,EAAA,CACA,GAAApwB,GAAAkiF,EAAAt/E,KAAAiuC,OAAA,aAAAzgB,EACA,KAAApwB,EACA,MAEAkxC,GAAAlxC,EAAAiI,QAAAipC,SAEA,wBAAArnC,GACAA,EAAA+B,MAAAhJ,MAAAsuC,GAAAnvC,OAAAgU,IACAm7B,EAAAtlC,MAAAhJ,KAAAiuC,QAAAhnC,GAAA9H,OAAAgU,OAGAzJ,IAQA+3E,EAAA,SAAAj0D,GAAoD,OACpD2zD,WAAAh/E,KAAA,KAAAqrB,GACA+zD,aAAAp/E,KAAA,KAAAqrB,GACA8zD,eAAAn/E,KAAA,KAAAqrB,GACAg0D,aAAAr/E,KAAA,KAAAqrB,KAgDAk0D,GACAzG,QACAzwD,UACAga,QAAA,QACA28C,WACAG,eACAC,aACAC,aACAC,0BAGAx7E,GAAA","file":"build.js","sourcesContent":["/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"/dist/\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 16);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports) {\n\n/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\nmodule.exports = function(useSourceMap) {\n\tvar list = [];\n\n\t// return the list of modules as css string\n\tlist.toString = function toString() {\n\t\treturn this.map(function (item) {\n\t\t\tvar content = cssWithMappingToString(item, useSourceMap);\n\t\t\tif(item[2]) {\n\t\t\t\treturn \"@media \" + item[2] + \"{\" + content + \"}\";\n\t\t\t} else {\n\t\t\t\treturn content;\n\t\t\t}\n\t\t}).join(\"\");\n\t};\n\n\t// import a list of modules into the list\n\tlist.i = function(modules, mediaQuery) {\n\t\tif(typeof modules === \"string\")\n\t\t\tmodules = [[null, modules, \"\"]];\n\t\tvar alreadyImportedModules = {};\n\t\tfor(var i = 0; i < this.length; i++) {\n\t\t\tvar id = this[i][0];\n\t\t\tif(typeof id === \"number\")\n\t\t\t\talreadyImportedModules[id] = true;\n\t\t}\n\t\tfor(i = 0; i < modules.length; i++) {\n\t\t\tvar item = modules[i];\n\t\t\t// skip already imported module\n\t\t\t// this implementation is not 100% perfect for weird media query combinations\n\t\t\t// when a module is imported multiple times with different media queries.\n\t\t\t// I hope this will never occur (Hey this way we have smaller bundles)\n\t\t\tif(typeof item[0] !== \"number\" || !alreadyImportedModules[item[0]]) {\n\t\t\t\tif(mediaQuery && !item[2]) {\n\t\t\t\t\titem[2] = mediaQuery;\n\t\t\t\t} else if(mediaQuery) {\n\t\t\t\t\titem[2] = \"(\" + item[2] + \") and (\" + mediaQuery + \")\";\n\t\t\t\t}\n\t\t\t\tlist.push(item);\n\t\t\t}\n\t\t}\n\t};\n\treturn list;\n};\n\nfunction cssWithMappingToString(item, useSourceMap) {\n\tvar content = item[1] || '';\n\tvar cssMapping = item[3];\n\tif (!cssMapping) {\n\t\treturn content;\n\t}\n\n\tif (useSourceMap && typeof btoa === 'function') {\n\t\tvar sourceMapping = toComment(cssMapping);\n\t\tvar sourceURLs = cssMapping.sources.map(function (source) {\n\t\t\treturn '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'\n\t\t});\n\n\t\treturn [content].concat(sourceURLs).concat([sourceMapping]).join('\\n');\n\t}\n\n\treturn [content].join('\\n');\n}\n\n// Adapted from convert-source-map (MIT)\nfunction toComment(sourceMap) {\n\t// eslint-disable-next-line no-undef\n\tvar base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\n\tvar data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;\n\n\treturn '/*# ' + data + ' */';\n}\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n Modified by Evan You @yyx990803\n*/\n\nvar hasDocument = typeof document !== 'undefined'\n\nif (typeof DEBUG !== 'undefined' && DEBUG) {\n if (!hasDocument) {\n throw new Error(\n 'vue-style-loader cannot be used in a non-browser environment. ' +\n \"Use { target: 'node' } in your Webpack config to indicate a server-rendering environment.\"\n ) }\n}\n\nvar listToStyles = __webpack_require__(23)\n\n/*\ntype StyleObject = {\n id: number;\n parts: Array<StyleObjectPart>\n}\n\ntype StyleObjectPart = {\n css: string;\n media: string;\n sourceMap: ?string\n}\n*/\n\nvar stylesInDom = {/*\n [id: number]: {\n id: number,\n refs: number,\n parts: Array<(obj?: StyleObjectPart) => void>\n }\n*/}\n\nvar head = hasDocument && (document.head || document.getElementsByTagName('head')[0])\nvar singletonElement = null\nvar singletonCounter = 0\nvar isProduction = false\nvar noop = function () {}\nvar options = null\nvar ssrIdKey = 'data-vue-ssr-id'\n\n// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>\n// tags it will allow on a page\nvar isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase())\n\nmodule.exports = function (parentId, list, _isProduction, _options) {\n isProduction = _isProduction\n\n options = _options || {}\n\n var styles = listToStyles(parentId, list)\n addStylesToDom(styles)\n\n return function update (newList) {\n var mayRemove = []\n for (var i = 0; i < styles.length; i++) {\n var item = styles[i]\n var domStyle = stylesInDom[item.id]\n domStyle.refs--\n mayRemove.push(domStyle)\n }\n if (newList) {\n styles = listToStyles(parentId, newList)\n addStylesToDom(styles)\n } else {\n styles = []\n }\n for (var i = 0; i < mayRemove.length; i++) {\n var domStyle = mayRemove[i]\n if (domStyle.refs === 0) {\n for (var j = 0; j < domStyle.parts.length; j++) {\n domStyle.parts[j]()\n }\n delete stylesInDom[domStyle.id]\n }\n }\n }\n}\n\nfunction addStylesToDom (styles /* Array<StyleObject> */) {\n for (var i = 0; i < styles.length; i++) {\n var item = styles[i]\n var domStyle = stylesInDom[item.id]\n if (domStyle) {\n domStyle.refs++\n for (var j = 0; j < domStyle.parts.length; j++) {\n domStyle.parts[j](item.parts[j])\n }\n for (; j < item.parts.length; j++) {\n domStyle.parts.push(addStyle(item.parts[j]))\n }\n if (domStyle.parts.length > item.parts.length) {\n domStyle.parts.length = item.parts.length\n }\n } else {\n var parts = []\n for (var j = 0; j < item.parts.length; j++) {\n parts.push(addStyle(item.parts[j]))\n }\n stylesInDom[item.id] = { id: item.id, refs: 1, parts: parts }\n }\n }\n}\n\nfunction createStyleElement () {\n var styleElement = document.createElement('style')\n styleElement.type = 'text/css'\n head.appendChild(styleElement)\n return styleElement\n}\n\nfunction addStyle (obj /* StyleObjectPart */) {\n var update, remove\n var styleElement = document.querySelector('style[' + ssrIdKey + '~=\"' + obj.id + '\"]')\n\n if (styleElement) {\n if (isProduction) {\n // has SSR styles and in production mode.\n // simply do nothing.\n return noop\n } else {\n // has SSR styles but in dev mode.\n // for some reason Chrome can't handle source map in server-rendered\n // style tags - source maps in <style> only works if the style tag is\n // created and inserted dynamically. So we remove the server rendered\n // styles and inject new ones.\n styleElement.parentNode.removeChild(styleElement)\n }\n }\n\n if (isOldIE) {\n // use singleton mode for IE9.\n var styleIndex = singletonCounter++\n styleElement = singletonElement || (singletonElement = createStyleElement())\n update = applyToSingletonTag.bind(null, styleElement, styleIndex, false)\n remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true)\n } else {\n // use multi-style-tag mode in all other cases\n styleElement = createStyleElement()\n update = applyToTag.bind(null, styleElement)\n remove = function () {\n styleElement.parentNode.removeChild(styleElement)\n }\n }\n\n update(obj)\n\n return function updateStyle (newObj /* StyleObjectPart */) {\n if (newObj) {\n if (newObj.css === obj.css &&\n newObj.media === obj.media &&\n newObj.sourceMap === obj.sourceMap) {\n return\n }\n update(obj = newObj)\n } else {\n remove()\n }\n }\n}\n\nvar replaceText = (function () {\n var textStore = []\n\n return function (index, replacement) {\n textStore[index] = replacement\n return textStore.filter(Boolean).join('\\n')\n }\n})()\n\nfunction applyToSingletonTag (styleElement, index, remove, obj) {\n var css = remove ? '' : obj.css\n\n if (styleElement.styleSheet) {\n styleElement.styleSheet.cssText = replaceText(index, css)\n } else {\n var cssNode = document.createTextNode(css)\n var childNodes = styleElement.childNodes\n if (childNodes[index]) styleElement.removeChild(childNodes[index])\n if (childNodes.length) {\n styleElement.insertBefore(cssNode, childNodes[index])\n } else {\n styleElement.appendChild(cssNode)\n }\n }\n}\n\nfunction applyToTag (styleElement, obj) {\n var css = obj.css\n var media = obj.media\n var sourceMap = obj.sourceMap\n\n if (media) {\n styleElement.setAttribute('media', media)\n }\n if (options.ssrId) {\n styleElement.setAttribute(ssrIdKey, obj.id)\n }\n\n if (sourceMap) {\n // https://developer.chrome.com/devtools/docs/javascript-debugging\n // this makes source maps inside style tags work properly in Chrome\n css += '\\n/*# sourceURL=' + sourceMap.sources[0] + ' */'\n // http://stackoverflow.com/a/26603875\n css += '\\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + ' */'\n }\n\n if (styleElement.styleSheet) {\n styleElement.styleSheet.cssText = css\n } else {\n while (styleElement.firstChild) {\n styleElement.removeChild(styleElement.firstChild)\n }\n styleElement.appendChild(document.createTextNode(css))\n }\n}\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports) {\n\n/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file.\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nmodule.exports = function normalizeComponent (\n rawScriptExports,\n compiledTemplate,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier /* server only */\n) {\n var esModule\n var scriptExports = rawScriptExports = rawScriptExports || {}\n\n // ES6 modules interop\n var type = typeof rawScriptExports.default\n if (type === 'object' || type === 'function') {\n esModule = rawScriptExports\n scriptExports = rawScriptExports.default\n }\n\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (compiledTemplate) {\n options.render = compiledTemplate.render\n options.staticRenderFns = compiledTemplate.staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = injectStyles\n }\n\n if (hook) {\n var functional = options.functional\n var existing = functional\n ? options.render\n : options.beforeCreate\n\n if (!functional) {\n // inject component registration as beforeCreate hook\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n } else {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return existing(h, context)\n }\n }\n }\n\n return {\n esModule: esModule,\n exports: scriptExports,\n options: options\n }\n}\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* WEBPACK VAR INJECTION */(function(global, setImmediate) {/*!\n * Vue.js v2.6.10\n * (c) 2014-2019 Evan You\n * Released under the MIT License.\n */\n/* */\n\nvar emptyObject = Object.freeze({});\n\n// These helpers produce better VM code in JS engines due to their\n// explicitness and function inlining.\nfunction isUndef (v) {\n return v === undefined || v === null\n}\n\nfunction isDef (v) {\n return v !== undefined && v !== null\n}\n\nfunction isTrue (v) {\n return v === true\n}\n\nfunction isFalse (v) {\n return v === false\n}\n\n/**\n * Check if value is primitive.\n */\nfunction isPrimitive (value) {\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n // $flow-disable-line\n typeof value === 'symbol' ||\n typeof value === 'boolean'\n )\n}\n\n/**\n * Quick object check - this is primarily used to tell\n * Objects from primitive values when we know the value\n * is a JSON-compliant type.\n */\nfunction isObject (obj) {\n return obj !== null && typeof obj === 'object'\n}\n\n/**\n * Get the raw type string of a value, e.g., [object Object].\n */\nvar _toString = Object.prototype.toString;\n\nfunction toRawType (value) {\n return _toString.call(value).slice(8, -1)\n}\n\n/**\n * Strict object type check. Only returns true\n * for plain JavaScript objects.\n */\nfunction isPlainObject (obj) {\n return _toString.call(obj) === '[object Object]'\n}\n\nfunction isRegExp (v) {\n return _toString.call(v) === '[object RegExp]'\n}\n\n/**\n * Check if val is a valid array index.\n */\nfunction isValidArrayIndex (val) {\n var n = parseFloat(String(val));\n return n >= 0 && Math.floor(n) === n && isFinite(val)\n}\n\nfunction isPromise (val) {\n return (\n isDef(val) &&\n typeof val.then === 'function' &&\n typeof val.catch === 'function'\n )\n}\n\n/**\n * Convert a value to a string that is actually rendered.\n */\nfunction toString (val) {\n return val == null\n ? ''\n : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)\n ? JSON.stringify(val, null, 2)\n : String(val)\n}\n\n/**\n * Convert an input value to a number for persistence.\n * If the conversion fails, return original string.\n */\nfunction toNumber (val) {\n var n = parseFloat(val);\n return isNaN(n) ? val : n\n}\n\n/**\n * Make a map and return a function for checking if a key\n * is in that map.\n */\nfunction makeMap (\n str,\n expectsLowerCase\n) {\n var map = Object.create(null);\n var list = str.split(',');\n for (var i = 0; i < list.length; i++) {\n map[list[i]] = true;\n }\n return expectsLowerCase\n ? function (val) { return map[val.toLowerCase()]; }\n : function (val) { return map[val]; }\n}\n\n/**\n * Check if a tag is a built-in tag.\n */\nvar isBuiltInTag = makeMap('slot,component', true);\n\n/**\n * Check if an attribute is a reserved attribute.\n */\nvar isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');\n\n/**\n * Remove an item from an array.\n */\nfunction remove (arr, item) {\n if (arr.length) {\n var index = arr.indexOf(item);\n if (index > -1) {\n return arr.splice(index, 1)\n }\n }\n}\n\n/**\n * Check whether an object has the property.\n */\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction hasOwn (obj, key) {\n return hasOwnProperty.call(obj, key)\n}\n\n/**\n * Create a cached version of a pure function.\n */\nfunction cached (fn) {\n var cache = Object.create(null);\n return (function cachedFn (str) {\n var hit = cache[str];\n return hit || (cache[str] = fn(str))\n })\n}\n\n/**\n * Camelize a hyphen-delimited string.\n */\nvar camelizeRE = /-(\\w)/g;\nvar camelize = cached(function (str) {\n return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })\n});\n\n/**\n * Capitalize a string.\n */\nvar capitalize = cached(function (str) {\n return str.charAt(0).toUpperCase() + str.slice(1)\n});\n\n/**\n * Hyphenate a camelCase string.\n */\nvar hyphenateRE = /\\B([A-Z])/g;\nvar hyphenate = cached(function (str) {\n return str.replace(hyphenateRE, '-$1').toLowerCase()\n});\n\n/**\n * Simple bind polyfill for environments that do not support it,\n * e.g., PhantomJS 1.x. Technically, we don't need this anymore\n * since native bind is now performant enough in most browsers.\n * But removing it would mean breaking code that was able to run in\n * PhantomJS 1.x, so this must be kept for backward compatibility.\n */\n\n/* istanbul ignore next */\nfunction polyfillBind (fn, ctx) {\n function boundFn (a) {\n var l = arguments.length;\n return l\n ? l > 1\n ? fn.apply(ctx, arguments)\n : fn.call(ctx, a)\n : fn.call(ctx)\n }\n\n boundFn._length = fn.length;\n return boundFn\n}\n\nfunction nativeBind (fn, ctx) {\n return fn.bind(ctx)\n}\n\nvar bind = Function.prototype.bind\n ? nativeBind\n : polyfillBind;\n\n/**\n * Convert an Array-like object to a real Array.\n */\nfunction toArray (list, start) {\n start = start || 0;\n var i = list.length - start;\n var ret = new Array(i);\n while (i--) {\n ret[i] = list[i + start];\n }\n return ret\n}\n\n/**\n * Mix properties into target object.\n */\nfunction extend (to, _from) {\n for (var key in _from) {\n to[key] = _from[key];\n }\n return to\n}\n\n/**\n * Merge an Array of Objects into a single Object.\n */\nfunction toObject (arr) {\n var res = {};\n for (var i = 0; i < arr.length; i++) {\n if (arr[i]) {\n extend(res, arr[i]);\n }\n }\n return res\n}\n\n/* eslint-disable no-unused-vars */\n\n/**\n * Perform no operation.\n * Stubbing args to make Flow happy without leaving useless transpiled code\n * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).\n */\nfunction noop (a, b, c) {}\n\n/**\n * Always return false.\n */\nvar no = function (a, b, c) { return false; };\n\n/* eslint-enable no-unused-vars */\n\n/**\n * Return the same value.\n */\nvar identity = function (_) { return _; };\n\n/**\n * Check if two values are loosely equal - that is,\n * if they are plain objects, do they have the same shape?\n */\nfunction looseEqual (a, b) {\n if (a === b) { return true }\n var isObjectA = isObject(a);\n var isObjectB = isObject(b);\n if (isObjectA && isObjectB) {\n try {\n var isArrayA = Array.isArray(a);\n var isArrayB = Array.isArray(b);\n if (isArrayA && isArrayB) {\n return a.length === b.length && a.every(function (e, i) {\n return looseEqual(e, b[i])\n })\n } else if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n } else if (!isArrayA && !isArrayB) {\n var keysA = Object.keys(a);\n var keysB = Object.keys(b);\n return keysA.length === keysB.length && keysA.every(function (key) {\n return looseEqual(a[key], b[key])\n })\n } else {\n /* istanbul ignore next */\n return false\n }\n } catch (e) {\n /* istanbul ignore next */\n return false\n }\n } else if (!isObjectA && !isObjectB) {\n return String(a) === String(b)\n } else {\n return false\n }\n}\n\n/**\n * Return the first index at which a loosely equal value can be\n * found in the array (if value is a plain object, the array must\n * contain an object of the same shape), or -1 if it is not present.\n */\nfunction looseIndexOf (arr, val) {\n for (var i = 0; i < arr.length; i++) {\n if (looseEqual(arr[i], val)) { return i }\n }\n return -1\n}\n\n/**\n * Ensure a function is called only once.\n */\nfunction once (fn) {\n var called = false;\n return function () {\n if (!called) {\n called = true;\n fn.apply(this, arguments);\n }\n }\n}\n\nvar SSR_ATTR = 'data-server-rendered';\n\nvar ASSET_TYPES = [\n 'component',\n 'directive',\n 'filter'\n];\n\nvar LIFECYCLE_HOOKS = [\n 'beforeCreate',\n 'created',\n 'beforeMount',\n 'mounted',\n 'beforeUpdate',\n 'updated',\n 'beforeDestroy',\n 'destroyed',\n 'activated',\n 'deactivated',\n 'errorCaptured',\n 'serverPrefetch'\n];\n\n/* */\n\n\n\nvar config = ({\n /**\n * Option merge strategies (used in core/util/options)\n */\n // $flow-disable-line\n optionMergeStrategies: Object.create(null),\n\n /**\n * Whether to suppress warnings.\n */\n silent: false,\n\n /**\n * Show production mode tip message on boot?\n */\n productionTip: \"production\" !== 'production',\n\n /**\n * Whether to enable devtools\n */\n devtools: \"production\" !== 'production',\n\n /**\n * Whether to record perf\n */\n performance: false,\n\n /**\n * Error handler for watcher errors\n */\n errorHandler: null,\n\n /**\n * Warn handler for watcher warns\n */\n warnHandler: null,\n\n /**\n * Ignore certain custom elements\n */\n ignoredElements: [],\n\n /**\n * Custom user key aliases for v-on\n */\n // $flow-disable-line\n keyCodes: Object.create(null),\n\n /**\n * Check if a tag is reserved so that it cannot be registered as a\n * component. This is platform-dependent and may be overwritten.\n */\n isReservedTag: no,\n\n /**\n * Check if an attribute is reserved so that it cannot be used as a component\n * prop. This is platform-dependent and may be overwritten.\n */\n isReservedAttr: no,\n\n /**\n * Check if a tag is an unknown element.\n * Platform-dependent.\n */\n isUnknownElement: no,\n\n /**\n * Get the namespace of an element\n */\n getTagNamespace: noop,\n\n /**\n * Parse the real tag name for the specific platform.\n */\n parsePlatformTagName: identity,\n\n /**\n * Check if an attribute must be bound using property, e.g. value\n * Platform-dependent.\n */\n mustUseProp: no,\n\n /**\n * Perform updates asynchronously. Intended to be used by Vue Test Utils\n * This will significantly reduce performance if set to false.\n */\n async: true,\n\n /**\n * Exposed for legacy reasons\n */\n _lifecycleHooks: LIFECYCLE_HOOKS\n});\n\n/* */\n\n/**\n * unicode letters used for parsing html tags, component names and property paths.\n * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname\n * skipping \\u10000-\\uEFFFF due to it freezing up PhantomJS\n */\nvar unicodeRegExp = /a-zA-Z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD/;\n\n/**\n * Check if a string starts with $ or _\n */\nfunction isReserved (str) {\n var c = (str + '').charCodeAt(0);\n return c === 0x24 || c === 0x5F\n}\n\n/**\n * Define a property.\n */\nfunction def (obj, key, val, enumerable) {\n Object.defineProperty(obj, key, {\n value: val,\n enumerable: !!enumerable,\n writable: true,\n configurable: true\n });\n}\n\n/**\n * Parse simple path.\n */\nvar bailRE = new RegExp((\"[^\" + (unicodeRegExp.source) + \".$_\\\\d]\"));\nfunction parsePath (path) {\n if (bailRE.test(path)) {\n return\n }\n var segments = path.split('.');\n return function (obj) {\n for (var i = 0; i < segments.length; i++) {\n if (!obj) { return }\n obj = obj[segments[i]];\n }\n return obj\n }\n}\n\n/* */\n\n// can we use __proto__?\nvar hasProto = '__proto__' in {};\n\n// Browser environment sniffing\nvar inBrowser = typeof window !== 'undefined';\nvar inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;\nvar weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();\nvar UA = inBrowser && window.navigator.userAgent.toLowerCase();\nvar isIE = UA && /msie|trident/.test(UA);\nvar isIE9 = UA && UA.indexOf('msie 9.0') > 0;\nvar isEdge = UA && UA.indexOf('edge/') > 0;\nvar isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android');\nvar isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios');\nvar isChrome = UA && /chrome\\/\\d+/.test(UA) && !isEdge;\nvar isPhantomJS = UA && /phantomjs/.test(UA);\nvar isFF = UA && UA.match(/firefox\\/(\\d+)/);\n\n// Firefox has a \"watch\" function on Object.prototype...\nvar nativeWatch = ({}).watch;\n\nvar supportsPassive = false;\nif (inBrowser) {\n try {\n var opts = {};\n Object.defineProperty(opts, 'passive', ({\n get: function get () {\n /* istanbul ignore next */\n supportsPassive = true;\n }\n })); // https://github.com/facebook/flow/issues/285\n window.addEventListener('test-passive', null, opts);\n } catch (e) {}\n}\n\n// this needs to be lazy-evaled because vue may be required before\n// vue-server-renderer can set VUE_ENV\nvar _isServer;\nvar isServerRendering = function () {\n if (_isServer === undefined) {\n /* istanbul ignore if */\n if (!inBrowser && !inWeex && typeof global !== 'undefined') {\n // detect presence of vue-server-renderer and avoid\n // Webpack shimming the process\n _isServer = global['process'] && global['process'].env.VUE_ENV === 'server';\n } else {\n _isServer = false;\n }\n }\n return _isServer\n};\n\n// detect devtools\nvar devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;\n\n/* istanbul ignore next */\nfunction isNative (Ctor) {\n return typeof Ctor === 'function' && /native code/.test(Ctor.toString())\n}\n\nvar hasSymbol =\n typeof Symbol !== 'undefined' && isNative(Symbol) &&\n typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys);\n\nvar _Set;\n/* istanbul ignore if */ // $flow-disable-line\nif (typeof Set !== 'undefined' && isNative(Set)) {\n // use native Set when available.\n _Set = Set;\n} else {\n // a non-standard Set polyfill that only works with primitive keys.\n _Set = /*@__PURE__*/(function () {\n function Set () {\n this.set = Object.create(null);\n }\n Set.prototype.has = function has (key) {\n return this.set[key] === true\n };\n Set.prototype.add = function add (key) {\n this.set[key] = true;\n };\n Set.prototype.clear = function clear () {\n this.set = Object.create(null);\n };\n\n return Set;\n }());\n}\n\n/* */\n\nvar warn = noop;\nvar tip = noop;\nvar generateComponentTrace = (noop); // work around flow check\nvar formatComponentName = (noop);\n\nif (false) {\n var hasConsole = typeof console !== 'undefined';\n var classifyRE = /(?:^|[-_])(\\w)/g;\n var classify = function (str) { return str\n .replace(classifyRE, function (c) { return c.toUpperCase(); })\n .replace(/[-_]/g, ''); };\n\n warn = function (msg, vm) {\n var trace = vm ? generateComponentTrace(vm) : '';\n\n if (config.warnHandler) {\n config.warnHandler.call(null, msg, vm, trace);\n } else if (hasConsole && (!config.silent)) {\n console.error((\"[Vue warn]: \" + msg + trace));\n }\n };\n\n tip = function (msg, vm) {\n if (hasConsole && (!config.silent)) {\n console.warn(\"[Vue tip]: \" + msg + (\n vm ? generateComponentTrace(vm) : ''\n ));\n }\n };\n\n formatComponentName = function (vm, includeFile) {\n if (vm.$root === vm) {\n return '<Root>'\n }\n var options = typeof vm === 'function' && vm.cid != null\n ? vm.options\n : vm._isVue\n ? vm.$options || vm.constructor.options\n : vm;\n var name = options.name || options._componentTag;\n var file = options.__file;\n if (!name && file) {\n var match = file.match(/([^/\\\\]+)\\.vue$/);\n name = match && match[1];\n }\n\n return (\n (name ? (\"<\" + (classify(name)) + \">\") : \"<Anonymous>\") +\n (file && includeFile !== false ? (\" at \" + file) : '')\n )\n };\n\n var repeat = function (str, n) {\n var res = '';\n while (n) {\n if (n % 2 === 1) { res += str; }\n if (n > 1) { str += str; }\n n >>= 1;\n }\n return res\n };\n\n generateComponentTrace = function (vm) {\n if (vm._isVue && vm.$parent) {\n var tree = [];\n var currentRecursiveSequence = 0;\n while (vm) {\n if (tree.length > 0) {\n var last = tree[tree.length - 1];\n if (last.constructor === vm.constructor) {\n currentRecursiveSequence++;\n vm = vm.$parent;\n continue\n } else if (currentRecursiveSequence > 0) {\n tree[tree.length - 1] = [last, currentRecursiveSequence];\n currentRecursiveSequence = 0;\n }\n }\n tree.push(vm);\n vm = vm.$parent;\n }\n return '\\n\\nfound in\\n\\n' + tree\n .map(function (vm, i) { return (\"\" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm)\n ? ((formatComponentName(vm[0])) + \"... (\" + (vm[1]) + \" recursive calls)\")\n : formatComponentName(vm))); })\n .join('\\n')\n } else {\n return (\"\\n\\n(found in \" + (formatComponentName(vm)) + \")\")\n }\n };\n}\n\n/* */\n\nvar uid = 0;\n\n/**\n * A dep is an observable that can have multiple\n * directives subscribing to it.\n */\nvar Dep = function Dep () {\n this.id = uid++;\n this.subs = [];\n};\n\nDep.prototype.addSub = function addSub (sub) {\n this.subs.push(sub);\n};\n\nDep.prototype.removeSub = function removeSub (sub) {\n remove(this.subs, sub);\n};\n\nDep.prototype.depend = function depend () {\n if (Dep.target) {\n Dep.target.addDep(this);\n }\n};\n\nDep.prototype.notify = function notify () {\n // stabilize the subscriber list first\n var subs = this.subs.slice();\n if (false) {\n // subs aren't sorted in scheduler if not running async\n // we need to sort them now to make sure they fire in correct\n // order\n subs.sort(function (a, b) { return a.id - b.id; });\n }\n for (var i = 0, l = subs.length; i < l; i++) {\n subs[i].update();\n }\n};\n\n// The current target watcher being evaluated.\n// This is globally unique because only one watcher\n// can be evaluated at a time.\nDep.target = null;\nvar targetStack = [];\n\nfunction pushTarget (target) {\n targetStack.push(target);\n Dep.target = target;\n}\n\nfunction popTarget () {\n targetStack.pop();\n Dep.target = targetStack[targetStack.length - 1];\n}\n\n/* */\n\nvar VNode = function VNode (\n tag,\n data,\n children,\n text,\n elm,\n context,\n componentOptions,\n asyncFactory\n) {\n this.tag = tag;\n this.data = data;\n this.children = children;\n this.text = text;\n this.elm = elm;\n this.ns = undefined;\n this.context = context;\n this.fnContext = undefined;\n this.fnOptions = undefined;\n this.fnScopeId = undefined;\n this.key = data && data.key;\n this.componentOptions = componentOptions;\n this.componentInstance = undefined;\n this.parent = undefined;\n this.raw = false;\n this.isStatic = false;\n this.isRootInsert = true;\n this.isComment = false;\n this.isCloned = false;\n this.isOnce = false;\n this.asyncFactory = asyncFactory;\n this.asyncMeta = undefined;\n this.isAsyncPlaceholder = false;\n};\n\nvar prototypeAccessors = { child: { configurable: true } };\n\n// DEPRECATED: alias for componentInstance for backwards compat.\n/* istanbul ignore next */\nprototypeAccessors.child.get = function () {\n return this.componentInstance\n};\n\nObject.defineProperties( VNode.prototype, prototypeAccessors );\n\nvar createEmptyVNode = function (text) {\n if ( text === void 0 ) text = '';\n\n var node = new VNode();\n node.text = text;\n node.isComment = true;\n return node\n};\n\nfunction createTextVNode (val) {\n return new VNode(undefined, undefined, undefined, String(val))\n}\n\n// optimized shallow clone\n// used for static nodes and slot nodes because they may be reused across\n// multiple renders, cloning them avoids errors when DOM manipulations rely\n// on their elm reference.\nfunction cloneVNode (vnode) {\n var cloned = new VNode(\n vnode.tag,\n vnode.data,\n // #7975\n // clone children array to avoid mutating original in case of cloning\n // a child.\n vnode.children && vnode.children.slice(),\n vnode.text,\n vnode.elm,\n vnode.context,\n vnode.componentOptions,\n vnode.asyncFactory\n );\n cloned.ns = vnode.ns;\n cloned.isStatic = vnode.isStatic;\n cloned.key = vnode.key;\n cloned.isComment = vnode.isComment;\n cloned.fnContext = vnode.fnContext;\n cloned.fnOptions = vnode.fnOptions;\n cloned.fnScopeId = vnode.fnScopeId;\n cloned.asyncMeta = vnode.asyncMeta;\n cloned.isCloned = true;\n return cloned\n}\n\n/*\n * not type checking this file because flow doesn't play well with\n * dynamically accessing methods on Array prototype\n */\n\nvar arrayProto = Array.prototype;\nvar arrayMethods = Object.create(arrayProto);\n\nvar methodsToPatch = [\n 'push',\n 'pop',\n 'shift',\n 'unshift',\n 'splice',\n 'sort',\n 'reverse'\n];\n\n/**\n * Intercept mutating methods and emit events\n */\nmethodsToPatch.forEach(function (method) {\n // cache original method\n var original = arrayProto[method];\n def(arrayMethods, method, function mutator () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n var result = original.apply(this, args);\n var ob = this.__ob__;\n var inserted;\n switch (method) {\n case 'push':\n case 'unshift':\n inserted = args;\n break\n case 'splice':\n inserted = args.slice(2);\n break\n }\n if (inserted) { ob.observeArray(inserted); }\n // notify change\n ob.dep.notify();\n return result\n });\n});\n\n/* */\n\nvar arrayKeys = Object.getOwnPropertyNames(arrayMethods);\n\n/**\n * In some cases we may want to disable observation inside a component's\n * update computation.\n */\nvar shouldObserve = true;\n\nfunction toggleObserving (value) {\n shouldObserve = value;\n}\n\n/**\n * Observer class that is attached to each observed\n * object. Once attached, the observer converts the target\n * object's property keys into getter/setters that\n * collect dependencies and dispatch updates.\n */\nvar Observer = function Observer (value) {\n this.value = value;\n this.dep = new Dep();\n this.vmCount = 0;\n def(value, '__ob__', this);\n if (Array.isArray(value)) {\n if (hasProto) {\n protoAugment(value, arrayMethods);\n } else {\n copyAugment(value, arrayMethods, arrayKeys);\n }\n this.observeArray(value);\n } else {\n this.walk(value);\n }\n};\n\n/**\n * Walk through all properties and convert them into\n * getter/setters. This method should only be called when\n * value type is Object.\n */\nObserver.prototype.walk = function walk (obj) {\n var keys = Object.keys(obj);\n for (var i = 0; i < keys.length; i++) {\n defineReactive$$1(obj, keys[i]);\n }\n};\n\n/**\n * Observe a list of Array items.\n */\nObserver.prototype.observeArray = function observeArray (items) {\n for (var i = 0, l = items.length; i < l; i++) {\n observe(items[i]);\n }\n};\n\n// helpers\n\n/**\n * Augment a target Object or Array by intercepting\n * the prototype chain using __proto__\n */\nfunction protoAugment (target, src) {\n /* eslint-disable no-proto */\n target.__proto__ = src;\n /* eslint-enable no-proto */\n}\n\n/**\n * Augment a target Object or Array by defining\n * hidden properties.\n */\n/* istanbul ignore next */\nfunction copyAugment (target, src, keys) {\n for (var i = 0, l = keys.length; i < l; i++) {\n var key = keys[i];\n def(target, key, src[key]);\n }\n}\n\n/**\n * Attempt to create an observer instance for a value,\n * returns the new observer if successfully observed,\n * or the existing observer if the value already has one.\n */\nfunction observe (value, asRootData) {\n if (!isObject(value) || value instanceof VNode) {\n return\n }\n var ob;\n if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {\n ob = value.__ob__;\n } else if (\n shouldObserve &&\n !isServerRendering() &&\n (Array.isArray(value) || isPlainObject(value)) &&\n Object.isExtensible(value) &&\n !value._isVue\n ) {\n ob = new Observer(value);\n }\n if (asRootData && ob) {\n ob.vmCount++;\n }\n return ob\n}\n\n/**\n * Define a reactive property on an Object.\n */\nfunction defineReactive$$1 (\n obj,\n key,\n val,\n customSetter,\n shallow\n) {\n var dep = new Dep();\n\n var property = Object.getOwnPropertyDescriptor(obj, key);\n if (property && property.configurable === false) {\n return\n }\n\n // cater for pre-defined getter/setters\n var getter = property && property.get;\n var setter = property && property.set;\n if ((!getter || setter) && arguments.length === 2) {\n val = obj[key];\n }\n\n var childOb = !shallow && observe(val);\n Object.defineProperty(obj, key, {\n enumerable: true,\n configurable: true,\n get: function reactiveGetter () {\n var value = getter ? getter.call(obj) : val;\n if (Dep.target) {\n dep.depend();\n if (childOb) {\n childOb.dep.depend();\n if (Array.isArray(value)) {\n dependArray(value);\n }\n }\n }\n return value\n },\n set: function reactiveSetter (newVal) {\n var value = getter ? getter.call(obj) : val;\n /* eslint-disable no-self-compare */\n if (newVal === value || (newVal !== newVal && value !== value)) {\n return\n }\n /* eslint-enable no-self-compare */\n if (false) {\n customSetter();\n }\n // #7981: for accessor properties without setter\n if (getter && !setter) { return }\n if (setter) {\n setter.call(obj, newVal);\n } else {\n val = newVal;\n }\n childOb = !shallow && observe(newVal);\n dep.notify();\n }\n });\n}\n\n/**\n * Set a property on an object. Adds the new property and\n * triggers change notification if the property doesn't\n * already exist.\n */\nfunction set (target, key, val) {\n if (false\n ) {\n warn((\"Cannot set reactive property on undefined, null, or primitive value: \" + ((target))));\n }\n if (Array.isArray(target) && isValidArrayIndex(key)) {\n target.length = Math.max(target.length, key);\n target.splice(key, 1, val);\n return val\n }\n if (key in target && !(key in Object.prototype)) {\n target[key] = val;\n return val\n }\n var ob = (target).__ob__;\n if (target._isVue || (ob && ob.vmCount)) {\n \"production\" !== 'production' && warn(\n 'Avoid adding reactive properties to a Vue instance or its root $data ' +\n 'at runtime - declare it upfront in the data option.'\n );\n return val\n }\n if (!ob) {\n target[key] = val;\n return val\n }\n defineReactive$$1(ob.value, key, val);\n ob.dep.notify();\n return val\n}\n\n/**\n * Delete a property and trigger change if necessary.\n */\nfunction del (target, key) {\n if (false\n ) {\n warn((\"Cannot delete reactive property on undefined, null, or primitive value: \" + ((target))));\n }\n if (Array.isArray(target) && isValidArrayIndex(key)) {\n target.splice(key, 1);\n return\n }\n var ob = (target).__ob__;\n if (target._isVue || (ob && ob.vmCount)) {\n \"production\" !== 'production' && warn(\n 'Avoid deleting properties on a Vue instance or its root $data ' +\n '- just set it to null.'\n );\n return\n }\n if (!hasOwn(target, key)) {\n return\n }\n delete target[key];\n if (!ob) {\n return\n }\n ob.dep.notify();\n}\n\n/**\n * Collect dependencies on array elements when the array is touched, since\n * we cannot intercept array element access like property getters.\n */\nfunction dependArray (value) {\n for (var e = (void 0), i = 0, l = value.length; i < l; i++) {\n e = value[i];\n e && e.__ob__ && e.__ob__.dep.depend();\n if (Array.isArray(e)) {\n dependArray(e);\n }\n }\n}\n\n/* */\n\n/**\n * Option overwriting strategies are functions that handle\n * how to merge a parent option value and a child option\n * value into the final value.\n */\nvar strats = config.optionMergeStrategies;\n\n/**\n * Options with restrictions\n */\nif (false) {\n strats.el = strats.propsData = function (parent, child, vm, key) {\n if (!vm) {\n warn(\n \"option \\\"\" + key + \"\\\" can only be used during instance \" +\n 'creation with the `new` keyword.'\n );\n }\n return defaultStrat(parent, child)\n };\n}\n\n/**\n * Helper that recursively merges two data objects together.\n */\nfunction mergeData (to, from) {\n if (!from) { return to }\n var key, toVal, fromVal;\n\n var keys = hasSymbol\n ? Reflect.ownKeys(from)\n : Object.keys(from);\n\n for (var i = 0; i < keys.length; i++) {\n key = keys[i];\n // in case the object is already observed...\n if (key === '__ob__') { continue }\n toVal = to[key];\n fromVal = from[key];\n if (!hasOwn(to, key)) {\n set(to, key, fromVal);\n } else if (\n toVal !== fromVal &&\n isPlainObject(toVal) &&\n isPlainObject(fromVal)\n ) {\n mergeData(toVal, fromVal);\n }\n }\n return to\n}\n\n/**\n * Data\n */\nfunction mergeDataOrFn (\n parentVal,\n childVal,\n vm\n) {\n if (!vm) {\n // in a Vue.extend merge, both should be functions\n if (!childVal) {\n return parentVal\n }\n if (!parentVal) {\n return childVal\n }\n // when parentVal & childVal are both present,\n // we need to return a function that returns the\n // merged result of both functions... no need to\n // check if parentVal is a function here because\n // it has to be a function to pass previous merges.\n return function mergedDataFn () {\n return mergeData(\n typeof childVal === 'function' ? childVal.call(this, this) : childVal,\n typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal\n )\n }\n } else {\n return function mergedInstanceDataFn () {\n // instance merge\n var instanceData = typeof childVal === 'function'\n ? childVal.call(vm, vm)\n : childVal;\n var defaultData = typeof parentVal === 'function'\n ? parentVal.call(vm, vm)\n : parentVal;\n if (instanceData) {\n return mergeData(instanceData, defaultData)\n } else {\n return defaultData\n }\n }\n }\n}\n\nstrats.data = function (\n parentVal,\n childVal,\n vm\n) {\n if (!vm) {\n if (childVal && typeof childVal !== 'function') {\n \"production\" !== 'production' && warn(\n 'The \"data\" option should be a function ' +\n 'that returns a per-instance value in component ' +\n 'definitions.',\n vm\n );\n\n return parentVal\n }\n return mergeDataOrFn(parentVal, childVal)\n }\n\n return mergeDataOrFn(parentVal, childVal, vm)\n};\n\n/**\n * Hooks and props are merged as arrays.\n */\nfunction mergeHook (\n parentVal,\n childVal\n) {\n var res = childVal\n ? parentVal\n ? parentVal.concat(childVal)\n : Array.isArray(childVal)\n ? childVal\n : [childVal]\n : parentVal;\n return res\n ? dedupeHooks(res)\n : res\n}\n\nfunction dedupeHooks (hooks) {\n var res = [];\n for (var i = 0; i < hooks.length; i++) {\n if (res.indexOf(hooks[i]) === -1) {\n res.push(hooks[i]);\n }\n }\n return res\n}\n\nLIFECYCLE_HOOKS.forEach(function (hook) {\n strats[hook] = mergeHook;\n});\n\n/**\n * Assets\n *\n * When a vm is present (instance creation), we need to do\n * a three-way merge between constructor options, instance\n * options and parent options.\n */\nfunction mergeAssets (\n parentVal,\n childVal,\n vm,\n key\n) {\n var res = Object.create(parentVal || null);\n if (childVal) {\n \"production\" !== 'production' && assertObjectType(key, childVal, vm);\n return extend(res, childVal)\n } else {\n return res\n }\n}\n\nASSET_TYPES.forEach(function (type) {\n strats[type + 's'] = mergeAssets;\n});\n\n/**\n * Watchers.\n *\n * Watchers hashes should not overwrite one\n * another, so we merge them as arrays.\n */\nstrats.watch = function (\n parentVal,\n childVal,\n vm,\n key\n) {\n // work around Firefox's Object.prototype.watch...\n if (parentVal === nativeWatch) { parentVal = undefined; }\n if (childVal === nativeWatch) { childVal = undefined; }\n /* istanbul ignore if */\n if (!childVal) { return Object.create(parentVal || null) }\n if (false) {\n assertObjectType(key, childVal, vm);\n }\n if (!parentVal) { return childVal }\n var ret = {};\n extend(ret, parentVal);\n for (var key$1 in childVal) {\n var parent = ret[key$1];\n var child = childVal[key$1];\n if (parent && !Array.isArray(parent)) {\n parent = [parent];\n }\n ret[key$1] = parent\n ? parent.concat(child)\n : Array.isArray(child) ? child : [child];\n }\n return ret\n};\n\n/**\n * Other object hashes.\n */\nstrats.props =\nstrats.methods =\nstrats.inject =\nstrats.computed = function (\n parentVal,\n childVal,\n vm,\n key\n) {\n if (childVal && \"production\" !== 'production') {\n assertObjectType(key, childVal, vm);\n }\n if (!parentVal) { return childVal }\n var ret = Object.create(null);\n extend(ret, parentVal);\n if (childVal) { extend(ret, childVal); }\n return ret\n};\nstrats.provide = mergeDataOrFn;\n\n/**\n * Default strategy.\n */\nvar defaultStrat = function (parentVal, childVal) {\n return childVal === undefined\n ? parentVal\n : childVal\n};\n\n/**\n * Validate component names\n */\nfunction checkComponents (options) {\n for (var key in options.components) {\n validateComponentName(key);\n }\n}\n\nfunction validateComponentName (name) {\n if (!new RegExp((\"^[a-zA-Z][\\\\-\\\\.0-9_\" + (unicodeRegExp.source) + \"]*$\")).test(name)) {\n warn(\n 'Invalid component name: \"' + name + '\". Component names ' +\n 'should conform to valid custom element name in html5 specification.'\n );\n }\n if (isBuiltInTag(name) || config.isReservedTag(name)) {\n warn(\n 'Do not use built-in or reserved HTML elements as component ' +\n 'id: ' + name\n );\n }\n}\n\n/**\n * Ensure all props option syntax are normalized into the\n * Object-based format.\n */\nfunction normalizeProps (options, vm) {\n var props = options.props;\n if (!props) { return }\n var res = {};\n var i, val, name;\n if (Array.isArray(props)) {\n i = props.length;\n while (i--) {\n val = props[i];\n if (typeof val === 'string') {\n name = camelize(val);\n res[name] = { type: null };\n } else if (false) {\n warn('props must be strings when using array syntax.');\n }\n }\n } else if (isPlainObject(props)) {\n for (var key in props) {\n val = props[key];\n name = camelize(key);\n res[name] = isPlainObject(val)\n ? val\n : { type: val };\n }\n } else if (false) {\n warn(\n \"Invalid value for option \\\"props\\\": expected an Array or an Object, \" +\n \"but got \" + (toRawType(props)) + \".\",\n vm\n );\n }\n options.props = res;\n}\n\n/**\n * Normalize all injections into Object-based format\n */\nfunction normalizeInject (options, vm) {\n var inject = options.inject;\n if (!inject) { return }\n var normalized = options.inject = {};\n if (Array.isArray(inject)) {\n for (var i = 0; i < inject.length; i++) {\n normalized[inject[i]] = { from: inject[i] };\n }\n } else if (isPlainObject(inject)) {\n for (var key in inject) {\n var val = inject[key];\n normalized[key] = isPlainObject(val)\n ? extend({ from: key }, val)\n : { from: val };\n }\n } else if (false) {\n warn(\n \"Invalid value for option \\\"inject\\\": expected an Array or an Object, \" +\n \"but got \" + (toRawType(inject)) + \".\",\n vm\n );\n }\n}\n\n/**\n * Normalize raw function directives into object format.\n */\nfunction normalizeDirectives (options) {\n var dirs = options.directives;\n if (dirs) {\n for (var key in dirs) {\n var def$$1 = dirs[key];\n if (typeof def$$1 === 'function') {\n dirs[key] = { bind: def$$1, update: def$$1 };\n }\n }\n }\n}\n\nfunction assertObjectType (name, value, vm) {\n if (!isPlainObject(value)) {\n warn(\n \"Invalid value for option \\\"\" + name + \"\\\": expected an Object, \" +\n \"but got \" + (toRawType(value)) + \".\",\n vm\n );\n }\n}\n\n/**\n * Merge two option objects into a new one.\n * Core utility used in both instantiation and inheritance.\n */\nfunction mergeOptions (\n parent,\n child,\n vm\n) {\n if (false) {\n checkComponents(child);\n }\n\n if (typeof child === 'function') {\n child = child.options;\n }\n\n normalizeProps(child, vm);\n normalizeInject(child, vm);\n normalizeDirectives(child);\n\n // Apply extends and mixins on the child options,\n // but only if it is a raw options object that isn't\n // the result of another mergeOptions call.\n // Only merged options has the _base property.\n if (!child._base) {\n if (child.extends) {\n parent = mergeOptions(parent, child.extends, vm);\n }\n if (child.mixins) {\n for (var i = 0, l = child.mixins.length; i < l; i++) {\n parent = mergeOptions(parent, child.mixins[i], vm);\n }\n }\n }\n\n var options = {};\n var key;\n for (key in parent) {\n mergeField(key);\n }\n for (key in child) {\n if (!hasOwn(parent, key)) {\n mergeField(key);\n }\n }\n function mergeField (key) {\n var strat = strats[key] || defaultStrat;\n options[key] = strat(parent[key], child[key], vm, key);\n }\n return options\n}\n\n/**\n * Resolve an asset.\n * This function is used because child instances need access\n * to assets defined in its ancestor chain.\n */\nfunction resolveAsset (\n options,\n type,\n id,\n warnMissing\n) {\n /* istanbul ignore if */\n if (typeof id !== 'string') {\n return\n }\n var assets = options[type];\n // check local registration variations first\n if (hasOwn(assets, id)) { return assets[id] }\n var camelizedId = camelize(id);\n if (hasOwn(assets, camelizedId)) { return assets[camelizedId] }\n var PascalCaseId = capitalize(camelizedId);\n if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] }\n // fallback to prototype chain\n var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];\n if (false) {\n warn(\n 'Failed to resolve ' + type.slice(0, -1) + ': ' + id,\n options\n );\n }\n return res\n}\n\n/* */\n\n\n\nfunction validateProp (\n key,\n propOptions,\n propsData,\n vm\n) {\n var prop = propOptions[key];\n var absent = !hasOwn(propsData, key);\n var value = propsData[key];\n // boolean casting\n var booleanIndex = getTypeIndex(Boolean, prop.type);\n if (booleanIndex > -1) {\n if (absent && !hasOwn(prop, 'default')) {\n value = false;\n } else if (value === '' || value === hyphenate(key)) {\n // only cast empty string / same name to boolean if\n // boolean has higher priority\n var stringIndex = getTypeIndex(String, prop.type);\n if (stringIndex < 0 || booleanIndex < stringIndex) {\n value = true;\n }\n }\n }\n // check default value\n if (value === undefined) {\n value = getPropDefaultValue(vm, prop, key);\n // since the default value is a fresh copy,\n // make sure to observe it.\n var prevShouldObserve = shouldObserve;\n toggleObserving(true);\n observe(value);\n toggleObserving(prevShouldObserve);\n }\n if (\n false\n ) {\n assertProp(prop, key, value, vm, absent);\n }\n return value\n}\n\n/**\n * Get the default value of a prop.\n */\nfunction getPropDefaultValue (vm, prop, key) {\n // no default, return undefined\n if (!hasOwn(prop, 'default')) {\n return undefined\n }\n var def = prop.default;\n // warn against non-factory defaults for Object & Array\n if (false) {\n warn(\n 'Invalid default value for prop \"' + key + '\": ' +\n 'Props with type Object/Array must use a factory function ' +\n 'to return the default value.',\n vm\n );\n }\n // the raw prop value was also undefined from previous render,\n // return previous default value to avoid unnecessary watcher trigger\n if (vm && vm.$options.propsData &&\n vm.$options.propsData[key] === undefined &&\n vm._props[key] !== undefined\n ) {\n return vm._props[key]\n }\n // call factory function for non-Function types\n // a value is Function if its prototype is function even across different execution context\n return typeof def === 'function' && getType(prop.type) !== 'Function'\n ? def.call(vm)\n : def\n}\n\n/**\n * Assert whether a prop is valid.\n */\nfunction assertProp (\n prop,\n name,\n value,\n vm,\n absent\n) {\n if (prop.required && absent) {\n warn(\n 'Missing required prop: \"' + name + '\"',\n vm\n );\n return\n }\n if (value == null && !prop.required) {\n return\n }\n var type = prop.type;\n var valid = !type || type === true;\n var expectedTypes = [];\n if (type) {\n if (!Array.isArray(type)) {\n type = [type];\n }\n for (var i = 0; i < type.length && !valid; i++) {\n var assertedType = assertType(value, type[i]);\n expectedTypes.push(assertedType.expectedType || '');\n valid = assertedType.valid;\n }\n }\n\n if (!valid) {\n warn(\n getInvalidTypeMessage(name, value, expectedTypes),\n vm\n );\n return\n }\n var validator = prop.validator;\n if (validator) {\n if (!validator(value)) {\n warn(\n 'Invalid prop: custom validator check failed for prop \"' + name + '\".',\n vm\n );\n }\n }\n}\n\nvar simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/;\n\nfunction assertType (value, type) {\n var valid;\n var expectedType = getType(type);\n if (simpleCheckRE.test(expectedType)) {\n var t = typeof value;\n valid = t === expectedType.toLowerCase();\n // for primitive wrapper objects\n if (!valid && t === 'object') {\n valid = value instanceof type;\n }\n } else if (expectedType === 'Object') {\n valid = isPlainObject(value);\n } else if (expectedType === 'Array') {\n valid = Array.isArray(value);\n } else {\n valid = value instanceof type;\n }\n return {\n valid: valid,\n expectedType: expectedType\n }\n}\n\n/**\n * Use function string name to check built-in types,\n * because a simple equality check will fail when running\n * across different vms / iframes.\n */\nfunction getType (fn) {\n var match = fn && fn.toString().match(/^\\s*function (\\w+)/);\n return match ? match[1] : ''\n}\n\nfunction isSameType (a, b) {\n return getType(a) === getType(b)\n}\n\nfunction getTypeIndex (type, expectedTypes) {\n if (!Array.isArray(expectedTypes)) {\n return isSameType(expectedTypes, type) ? 0 : -1\n }\n for (var i = 0, len = expectedTypes.length; i < len; i++) {\n if (isSameType(expectedTypes[i], type)) {\n return i\n }\n }\n return -1\n}\n\nfunction getInvalidTypeMessage (name, value, expectedTypes) {\n var message = \"Invalid prop: type check failed for prop \\\"\" + name + \"\\\".\" +\n \" Expected \" + (expectedTypes.map(capitalize).join(', '));\n var expectedType = expectedTypes[0];\n var receivedType = toRawType(value);\n var expectedValue = styleValue(value, expectedType);\n var receivedValue = styleValue(value, receivedType);\n // check if we need to specify expected value\n if (expectedTypes.length === 1 &&\n isExplicable(expectedType) &&\n !isBoolean(expectedType, receivedType)) {\n message += \" with value \" + expectedValue;\n }\n message += \", got \" + receivedType + \" \";\n // check if we need to specify received value\n if (isExplicable(receivedType)) {\n message += \"with value \" + receivedValue + \".\";\n }\n return message\n}\n\nfunction styleValue (value, type) {\n if (type === 'String') {\n return (\"\\\"\" + value + \"\\\"\")\n } else if (type === 'Number') {\n return (\"\" + (Number(value)))\n } else {\n return (\"\" + value)\n }\n}\n\nfunction isExplicable (value) {\n var explicitTypes = ['string', 'number', 'boolean'];\n return explicitTypes.some(function (elem) { return value.toLowerCase() === elem; })\n}\n\nfunction isBoolean () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; })\n}\n\n/* */\n\nfunction handleError (err, vm, info) {\n // Deactivate deps tracking while processing error handler to avoid possible infinite rendering.\n // See: https://github.com/vuejs/vuex/issues/1505\n pushTarget();\n try {\n if (vm) {\n var cur = vm;\n while ((cur = cur.$parent)) {\n var hooks = cur.$options.errorCaptured;\n if (hooks) {\n for (var i = 0; i < hooks.length; i++) {\n try {\n var capture = hooks[i].call(cur, err, vm, info) === false;\n if (capture) { return }\n } catch (e) {\n globalHandleError(e, cur, 'errorCaptured hook');\n }\n }\n }\n }\n }\n globalHandleError(err, vm, info);\n } finally {\n popTarget();\n }\n}\n\nfunction invokeWithErrorHandling (\n handler,\n context,\n args,\n vm,\n info\n) {\n var res;\n try {\n res = args ? handler.apply(context, args) : handler.call(context);\n if (res && !res._isVue && isPromise(res) && !res._handled) {\n res.catch(function (e) { return handleError(e, vm, info + \" (Promise/async)\"); });\n // issue #9511\n // avoid catch triggering multiple times when nested calls\n res._handled = true;\n }\n } catch (e) {\n handleError(e, vm, info);\n }\n return res\n}\n\nfunction globalHandleError (err, vm, info) {\n if (config.errorHandler) {\n try {\n return config.errorHandler.call(null, err, vm, info)\n } catch (e) {\n // if the user intentionally throws the original error in the handler,\n // do not log it twice\n if (e !== err) {\n logError(e, null, 'config.errorHandler');\n }\n }\n }\n logError(err, vm, info);\n}\n\nfunction logError (err, vm, info) {\n if (false) {\n warn((\"Error in \" + info + \": \\\"\" + (err.toString()) + \"\\\"\"), vm);\n }\n /* istanbul ignore else */\n if ((inBrowser || inWeex) && typeof console !== 'undefined') {\n console.error(err);\n } else {\n throw err\n }\n}\n\n/* */\n\nvar isUsingMicroTask = false;\n\nvar callbacks = [];\nvar pending = false;\n\nfunction flushCallbacks () {\n pending = false;\n var copies = callbacks.slice(0);\n callbacks.length = 0;\n for (var i = 0; i < copies.length; i++) {\n copies[i]();\n }\n}\n\n// Here we have async deferring wrappers using microtasks.\n// In 2.5 we used (macro) tasks (in combination with microtasks).\n// However, it has subtle problems when state is changed right before repaint\n// (e.g. #6813, out-in transitions).\n// Also, using (macro) tasks in event handler would cause some weird behaviors\n// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).\n// So we now use microtasks everywhere, again.\n// A major drawback of this tradeoff is that there are some scenarios\n// where microtasks have too high a priority and fire in between supposedly\n// sequential events (e.g. #4521, #6690, which have workarounds)\n// or even between bubbling of the same event (#6566).\nvar timerFunc;\n\n// The nextTick behavior leverages the microtask queue, which can be accessed\n// via either native Promise.then or MutationObserver.\n// MutationObserver has wider support, however it is seriously bugged in\n// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It\n// completely stops working after triggering a few times... so, if native\n// Promise is available, we will use it:\n/* istanbul ignore next, $flow-disable-line */\nif (typeof Promise !== 'undefined' && isNative(Promise)) {\n var p = Promise.resolve();\n timerFunc = function () {\n p.then(flushCallbacks);\n // In problematic UIWebViews, Promise.then doesn't completely break, but\n // it can get stuck in a weird state where callbacks are pushed into the\n // microtask queue but the queue isn't being flushed, until the browser\n // needs to do some other work, e.g. handle a timer. Therefore we can\n // \"force\" the microtask queue to be flushed by adding an empty timer.\n if (isIOS) { setTimeout(noop); }\n };\n isUsingMicroTask = true;\n} else if (!isIE && typeof MutationObserver !== 'undefined' && (\n isNative(MutationObserver) ||\n // PhantomJS and iOS 7.x\n MutationObserver.toString() === '[object MutationObserverConstructor]'\n)) {\n // Use MutationObserver where native Promise is not available,\n // e.g. PhantomJS, iOS7, Android 4.4\n // (#6466 MutationObserver is unreliable in IE11)\n var counter = 1;\n var observer = new MutationObserver(flushCallbacks);\n var textNode = document.createTextNode(String(counter));\n observer.observe(textNode, {\n characterData: true\n });\n timerFunc = function () {\n counter = (counter + 1) % 2;\n textNode.data = String(counter);\n };\n isUsingMicroTask = true;\n} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {\n // Fallback to setImmediate.\n // Techinically it leverages the (macro) task queue,\n // but it is still a better choice than setTimeout.\n timerFunc = function () {\n setImmediate(flushCallbacks);\n };\n} else {\n // Fallback to setTimeout.\n timerFunc = function () {\n setTimeout(flushCallbacks, 0);\n };\n}\n\nfunction nextTick (cb, ctx) {\n var _resolve;\n callbacks.push(function () {\n if (cb) {\n try {\n cb.call(ctx);\n } catch (e) {\n handleError(e, ctx, 'nextTick');\n }\n } else if (_resolve) {\n _resolve(ctx);\n }\n });\n if (!pending) {\n pending = true;\n timerFunc();\n }\n // $flow-disable-line\n if (!cb && typeof Promise !== 'undefined') {\n return new Promise(function (resolve) {\n _resolve = resolve;\n })\n }\n}\n\n/* */\n\n/* not type checking this file because flow doesn't play well with Proxy */\n\nvar initProxy;\n\nif (false) {\n var allowedGlobals = makeMap(\n 'Infinity,undefined,NaN,isFinite,isNaN,' +\n 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +\n 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +\n 'require' // for Webpack/Browserify\n );\n\n var warnNonPresent = function (target, key) {\n warn(\n \"Property or method \\\"\" + key + \"\\\" is not defined on the instance but \" +\n 'referenced during render. Make sure that this property is reactive, ' +\n 'either in the data option, or for class-based components, by ' +\n 'initializing the property. ' +\n 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',\n target\n );\n };\n\n var warnReservedPrefix = function (target, key) {\n warn(\n \"Property \\\"\" + key + \"\\\" must be accessed with \\\"$data.\" + key + \"\\\" because \" +\n 'properties starting with \"$\" or \"_\" are not proxied in the Vue instance to ' +\n 'prevent conflicts with Vue internals' +\n 'See: https://vuejs.org/v2/api/#data',\n target\n );\n };\n\n var hasProxy =\n typeof Proxy !== 'undefined' && isNative(Proxy);\n\n if (hasProxy) {\n var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact');\n config.keyCodes = new Proxy(config.keyCodes, {\n set: function set (target, key, value) {\n if (isBuiltInModifier(key)) {\n warn((\"Avoid overwriting built-in modifier in config.keyCodes: .\" + key));\n return false\n } else {\n target[key] = value;\n return true\n }\n }\n });\n }\n\n var hasHandler = {\n has: function has (target, key) {\n var has = key in target;\n var isAllowed = allowedGlobals(key) ||\n (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data));\n if (!has && !isAllowed) {\n if (key in target.$data) { warnReservedPrefix(target, key); }\n else { warnNonPresent(target, key); }\n }\n return has || !isAllowed\n }\n };\n\n var getHandler = {\n get: function get (target, key) {\n if (typeof key === 'string' && !(key in target)) {\n if (key in target.$data) { warnReservedPrefix(target, key); }\n else { warnNonPresent(target, key); }\n }\n return target[key]\n }\n };\n\n initProxy = function initProxy (vm) {\n if (hasProxy) {\n // determine which proxy handler to use\n var options = vm.$options;\n var handlers = options.render && options.render._withStripped\n ? getHandler\n : hasHandler;\n vm._renderProxy = new Proxy(vm, handlers);\n } else {\n vm._renderProxy = vm;\n }\n };\n}\n\n/* */\n\nvar seenObjects = new _Set();\n\n/**\n * Recursively traverse an object to evoke all converted\n * getters, so that every nested property inside the object\n * is collected as a \"deep\" dependency.\n */\nfunction traverse (val) {\n _traverse(val, seenObjects);\n seenObjects.clear();\n}\n\nfunction _traverse (val, seen) {\n var i, keys;\n var isA = Array.isArray(val);\n if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {\n return\n }\n if (val.__ob__) {\n var depId = val.__ob__.dep.id;\n if (seen.has(depId)) {\n return\n }\n seen.add(depId);\n }\n if (isA) {\n i = val.length;\n while (i--) { _traverse(val[i], seen); }\n } else {\n keys = Object.keys(val);\n i = keys.length;\n while (i--) { _traverse(val[keys[i]], seen); }\n }\n}\n\nvar mark;\nvar measure;\n\nif (false) {\n var perf = inBrowser && window.performance;\n /* istanbul ignore if */\n if (\n perf &&\n perf.mark &&\n perf.measure &&\n perf.clearMarks &&\n perf.clearMeasures\n ) {\n mark = function (tag) { return perf.mark(tag); };\n measure = function (name, startTag, endTag) {\n perf.measure(name, startTag, endTag);\n perf.clearMarks(startTag);\n perf.clearMarks(endTag);\n // perf.clearMeasures(name)\n };\n }\n}\n\n/* */\n\nvar normalizeEvent = cached(function (name) {\n var passive = name.charAt(0) === '&';\n name = passive ? name.slice(1) : name;\n var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first\n name = once$$1 ? name.slice(1) : name;\n var capture = name.charAt(0) === '!';\n name = capture ? name.slice(1) : name;\n return {\n name: name,\n once: once$$1,\n capture: capture,\n passive: passive\n }\n});\n\nfunction createFnInvoker (fns, vm) {\n function invoker () {\n var arguments$1 = arguments;\n\n var fns = invoker.fns;\n if (Array.isArray(fns)) {\n var cloned = fns.slice();\n for (var i = 0; i < cloned.length; i++) {\n invokeWithErrorHandling(cloned[i], null, arguments$1, vm, \"v-on handler\");\n }\n } else {\n // return handler return value for single handlers\n return invokeWithErrorHandling(fns, null, arguments, vm, \"v-on handler\")\n }\n }\n invoker.fns = fns;\n return invoker\n}\n\nfunction updateListeners (\n on,\n oldOn,\n add,\n remove$$1,\n createOnceHandler,\n vm\n) {\n var name, def$$1, cur, old, event;\n for (name in on) {\n def$$1 = cur = on[name];\n old = oldOn[name];\n event = normalizeEvent(name);\n if (isUndef(cur)) {\n \"production\" !== 'production' && warn(\n \"Invalid handler for event \\\"\" + (event.name) + \"\\\": got \" + String(cur),\n vm\n );\n } else if (isUndef(old)) {\n if (isUndef(cur.fns)) {\n cur = on[name] = createFnInvoker(cur, vm);\n }\n if (isTrue(event.once)) {\n cur = on[name] = createOnceHandler(event.name, cur, event.capture);\n }\n add(event.name, cur, event.capture, event.passive, event.params);\n } else if (cur !== old) {\n old.fns = cur;\n on[name] = old;\n }\n }\n for (name in oldOn) {\n if (isUndef(on[name])) {\n event = normalizeEvent(name);\n remove$$1(event.name, oldOn[name], event.capture);\n }\n }\n}\n\n/* */\n\nfunction mergeVNodeHook (def, hookKey, hook) {\n if (def instanceof VNode) {\n def = def.data.hook || (def.data.hook = {});\n }\n var invoker;\n var oldHook = def[hookKey];\n\n function wrappedHook () {\n hook.apply(this, arguments);\n // important: remove merged hook to ensure it's called only once\n // and prevent memory leak\n remove(invoker.fns, wrappedHook);\n }\n\n if (isUndef(oldHook)) {\n // no existing hook\n invoker = createFnInvoker([wrappedHook]);\n } else {\n /* istanbul ignore if */\n if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {\n // already a merged invoker\n invoker = oldHook;\n invoker.fns.push(wrappedHook);\n } else {\n // existing plain hook\n invoker = createFnInvoker([oldHook, wrappedHook]);\n }\n }\n\n invoker.merged = true;\n def[hookKey] = invoker;\n}\n\n/* */\n\nfunction extractPropsFromVNodeData (\n data,\n Ctor,\n tag\n) {\n // we are only extracting raw values here.\n // validation and default values are handled in the child\n // component itself.\n var propOptions = Ctor.options.props;\n if (isUndef(propOptions)) {\n return\n }\n var res = {};\n var attrs = data.attrs;\n var props = data.props;\n if (isDef(attrs) || isDef(props)) {\n for (var key in propOptions) {\n var altKey = hyphenate(key);\n if (false) {\n var keyInLowerCase = key.toLowerCase();\n if (\n key !== keyInLowerCase &&\n attrs && hasOwn(attrs, keyInLowerCase)\n ) {\n tip(\n \"Prop \\\"\" + keyInLowerCase + \"\\\" is passed to component \" +\n (formatComponentName(tag || Ctor)) + \", but the declared prop name is\" +\n \" \\\"\" + key + \"\\\". \" +\n \"Note that HTML attributes are case-insensitive and camelCased \" +\n \"props need to use their kebab-case equivalents when using in-DOM \" +\n \"templates. You should probably use \\\"\" + altKey + \"\\\" instead of \\\"\" + key + \"\\\".\"\n );\n }\n }\n checkProp(res, props, key, altKey, true) ||\n checkProp(res, attrs, key, altKey, false);\n }\n }\n return res\n}\n\nfunction checkProp (\n res,\n hash,\n key,\n altKey,\n preserve\n) {\n if (isDef(hash)) {\n if (hasOwn(hash, key)) {\n res[key] = hash[key];\n if (!preserve) {\n delete hash[key];\n }\n return true\n } else if (hasOwn(hash, altKey)) {\n res[key] = hash[altKey];\n if (!preserve) {\n delete hash[altKey];\n }\n return true\n }\n }\n return false\n}\n\n/* */\n\n// The template compiler attempts to minimize the need for normalization by\n// statically analyzing the template at compile time.\n//\n// For plain HTML markup, normalization can be completely skipped because the\n// generated render function is guaranteed to return Array<VNode>. There are\n// two cases where extra normalization is needed:\n\n// 1. When the children contains components - because a functional component\n// may return an Array instead of a single root. In this case, just a simple\n// normalization is needed - if any child is an Array, we flatten the whole\n// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep\n// because functional components already normalize their own children.\nfunction simpleNormalizeChildren (children) {\n for (var i = 0; i < children.length; i++) {\n if (Array.isArray(children[i])) {\n return Array.prototype.concat.apply([], children)\n }\n }\n return children\n}\n\n// 2. When the children contains constructs that always generated nested Arrays,\n// e.g. <template>, <slot>, v-for, or when the children is provided by user\n// with hand-written render functions / JSX. In such cases a full normalization\n// is needed to cater to all possible types of children values.\nfunction normalizeChildren (children) {\n return isPrimitive(children)\n ? [createTextVNode(children)]\n : Array.isArray(children)\n ? normalizeArrayChildren(children)\n : undefined\n}\n\nfunction isTextNode (node) {\n return isDef(node) && isDef(node.text) && isFalse(node.isComment)\n}\n\nfunction normalizeArrayChildren (children, nestedIndex) {\n var res = [];\n var i, c, lastIndex, last;\n for (i = 0; i < children.length; i++) {\n c = children[i];\n if (isUndef(c) || typeof c === 'boolean') { continue }\n lastIndex = res.length - 1;\n last = res[lastIndex];\n // nested\n if (Array.isArray(c)) {\n if (c.length > 0) {\n c = normalizeArrayChildren(c, ((nestedIndex || '') + \"_\" + i));\n // merge adjacent text nodes\n if (isTextNode(c[0]) && isTextNode(last)) {\n res[lastIndex] = createTextVNode(last.text + (c[0]).text);\n c.shift();\n }\n res.push.apply(res, c);\n }\n } else if (isPrimitive(c)) {\n if (isTextNode(last)) {\n // merge adjacent text nodes\n // this is necessary for SSR hydration because text nodes are\n // essentially merged when rendered to HTML strings\n res[lastIndex] = createTextVNode(last.text + c);\n } else if (c !== '') {\n // convert primitive to vnode\n res.push(createTextVNode(c));\n }\n } else {\n if (isTextNode(c) && isTextNode(last)) {\n // merge adjacent text nodes\n res[lastIndex] = createTextVNode(last.text + c.text);\n } else {\n // default key for nested array children (likely generated by v-for)\n if (isTrue(children._isVList) &&\n isDef(c.tag) &&\n isUndef(c.key) &&\n isDef(nestedIndex)) {\n c.key = \"__vlist\" + nestedIndex + \"_\" + i + \"__\";\n }\n res.push(c);\n }\n }\n }\n return res\n}\n\n/* */\n\nfunction initProvide (vm) {\n var provide = vm.$options.provide;\n if (provide) {\n vm._provided = typeof provide === 'function'\n ? provide.call(vm)\n : provide;\n }\n}\n\nfunction initInjections (vm) {\n var result = resolveInject(vm.$options.inject, vm);\n if (result) {\n toggleObserving(false);\n Object.keys(result).forEach(function (key) {\n /* istanbul ignore else */\n if (false) {\n defineReactive$$1(vm, key, result[key], function () {\n warn(\n \"Avoid mutating an injected value directly since the changes will be \" +\n \"overwritten whenever the provided component re-renders. \" +\n \"injection being mutated: \\\"\" + key + \"\\\"\",\n vm\n );\n });\n } else {\n defineReactive$$1(vm, key, result[key]);\n }\n });\n toggleObserving(true);\n }\n}\n\nfunction resolveInject (inject, vm) {\n if (inject) {\n // inject is :any because flow is not smart enough to figure out cached\n var result = Object.create(null);\n var keys = hasSymbol\n ? Reflect.ownKeys(inject)\n : Object.keys(inject);\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n // #6574 in case the inject object is observed...\n if (key === '__ob__') { continue }\n var provideKey = inject[key].from;\n var source = vm;\n while (source) {\n if (source._provided && hasOwn(source._provided, provideKey)) {\n result[key] = source._provided[provideKey];\n break\n }\n source = source.$parent;\n }\n if (!source) {\n if ('default' in inject[key]) {\n var provideDefault = inject[key].default;\n result[key] = typeof provideDefault === 'function'\n ? provideDefault.call(vm)\n : provideDefault;\n } else if (false) {\n warn((\"Injection \\\"\" + key + \"\\\" not found\"), vm);\n }\n }\n }\n return result\n }\n}\n\n/* */\n\n\n\n/**\n * Runtime helper for resolving raw children VNodes into a slot object.\n */\nfunction resolveSlots (\n children,\n context\n) {\n if (!children || !children.length) {\n return {}\n }\n var slots = {};\n for (var i = 0, l = children.length; i < l; i++) {\n var child = children[i];\n var data = child.data;\n // remove slot attribute if the node is resolved as a Vue slot node\n if (data && data.attrs && data.attrs.slot) {\n delete data.attrs.slot;\n }\n // named slots should only be respected if the vnode was rendered in the\n // same context.\n if ((child.context === context || child.fnContext === context) &&\n data && data.slot != null\n ) {\n var name = data.slot;\n var slot = (slots[name] || (slots[name] = []));\n if (child.tag === 'template') {\n slot.push.apply(slot, child.children || []);\n } else {\n slot.push(child);\n }\n } else {\n (slots.default || (slots.default = [])).push(child);\n }\n }\n // ignore slots that contains only whitespace\n for (var name$1 in slots) {\n if (slots[name$1].every(isWhitespace)) {\n delete slots[name$1];\n }\n }\n return slots\n}\n\nfunction isWhitespace (node) {\n return (node.isComment && !node.asyncFactory) || node.text === ' '\n}\n\n/* */\n\nfunction normalizeScopedSlots (\n slots,\n normalSlots,\n prevSlots\n) {\n var res;\n var hasNormalSlots = Object.keys(normalSlots).length > 0;\n var isStable = slots ? !!slots.$stable : !hasNormalSlots;\n var key = slots && slots.$key;\n if (!slots) {\n res = {};\n } else if (slots._normalized) {\n // fast path 1: child component re-render only, parent did not change\n return slots._normalized\n } else if (\n isStable &&\n prevSlots &&\n prevSlots !== emptyObject &&\n key === prevSlots.$key &&\n !hasNormalSlots &&\n !prevSlots.$hasNormal\n ) {\n // fast path 2: stable scoped slots w/ no normal slots to proxy,\n // only need to normalize once\n return prevSlots\n } else {\n res = {};\n for (var key$1 in slots) {\n if (slots[key$1] && key$1[0] !== '$') {\n res[key$1] = normalizeScopedSlot(normalSlots, key$1, slots[key$1]);\n }\n }\n }\n // expose normal slots on scopedSlots\n for (var key$2 in normalSlots) {\n if (!(key$2 in res)) {\n res[key$2] = proxyNormalSlot(normalSlots, key$2);\n }\n }\n // avoriaz seems to mock a non-extensible $scopedSlots object\n // and when that is passed down this would cause an error\n if (slots && Object.isExtensible(slots)) {\n (slots)._normalized = res;\n }\n def(res, '$stable', isStable);\n def(res, '$key', key);\n def(res, '$hasNormal', hasNormalSlots);\n return res\n}\n\nfunction normalizeScopedSlot(normalSlots, key, fn) {\n var normalized = function () {\n var res = arguments.length ? fn.apply(null, arguments) : fn({});\n res = res && typeof res === 'object' && !Array.isArray(res)\n ? [res] // single vnode\n : normalizeChildren(res);\n return res && (\n res.length === 0 ||\n (res.length === 1 && res[0].isComment) // #9658\n ) ? undefined\n : res\n };\n // this is a slot using the new v-slot syntax without scope. although it is\n // compiled as a scoped slot, render fn users would expect it to be present\n // on this.$slots because the usage is semantically a normal slot.\n if (fn.proxy) {\n Object.defineProperty(normalSlots, key, {\n get: normalized,\n enumerable: true,\n configurable: true\n });\n }\n return normalized\n}\n\nfunction proxyNormalSlot(slots, key) {\n return function () { return slots[key]; }\n}\n\n/* */\n\n/**\n * Runtime helper for rendering v-for lists.\n */\nfunction renderList (\n val,\n render\n) {\n var ret, i, l, keys, key;\n if (Array.isArray(val) || typeof val === 'string') {\n ret = new Array(val.length);\n for (i = 0, l = val.length; i < l; i++) {\n ret[i] = render(val[i], i);\n }\n } else if (typeof val === 'number') {\n ret = new Array(val);\n for (i = 0; i < val; i++) {\n ret[i] = render(i + 1, i);\n }\n } else if (isObject(val)) {\n if (hasSymbol && val[Symbol.iterator]) {\n ret = [];\n var iterator = val[Symbol.iterator]();\n var result = iterator.next();\n while (!result.done) {\n ret.push(render(result.value, ret.length));\n result = iterator.next();\n }\n } else {\n keys = Object.keys(val);\n ret = new Array(keys.length);\n for (i = 0, l = keys.length; i < l; i++) {\n key = keys[i];\n ret[i] = render(val[key], key, i);\n }\n }\n }\n if (!isDef(ret)) {\n ret = [];\n }\n (ret)._isVList = true;\n return ret\n}\n\n/* */\n\n/**\n * Runtime helper for rendering <slot>\n */\nfunction renderSlot (\n name,\n fallback,\n props,\n bindObject\n) {\n var scopedSlotFn = this.$scopedSlots[name];\n var nodes;\n if (scopedSlotFn) { // scoped slot\n props = props || {};\n if (bindObject) {\n if (false) {\n warn(\n 'slot v-bind without argument expects an Object',\n this\n );\n }\n props = extend(extend({}, bindObject), props);\n }\n nodes = scopedSlotFn(props) || fallback;\n } else {\n nodes = this.$slots[name] || fallback;\n }\n\n var target = props && props.slot;\n if (target) {\n return this.$createElement('template', { slot: target }, nodes)\n } else {\n return nodes\n }\n}\n\n/* */\n\n/**\n * Runtime helper for resolving filters\n */\nfunction resolveFilter (id) {\n return resolveAsset(this.$options, 'filters', id, true) || identity\n}\n\n/* */\n\nfunction isKeyNotMatch (expect, actual) {\n if (Array.isArray(expect)) {\n return expect.indexOf(actual) === -1\n } else {\n return expect !== actual\n }\n}\n\n/**\n * Runtime helper for checking keyCodes from config.\n * exposed as Vue.prototype._k\n * passing in eventKeyName as last argument separately for backwards compat\n */\nfunction checkKeyCodes (\n eventKeyCode,\n key,\n builtInKeyCode,\n eventKeyName,\n builtInKeyName\n) {\n var mappedKeyCode = config.keyCodes[key] || builtInKeyCode;\n if (builtInKeyName && eventKeyName && !config.keyCodes[key]) {\n return isKeyNotMatch(builtInKeyName, eventKeyName)\n } else if (mappedKeyCode) {\n return isKeyNotMatch(mappedKeyCode, eventKeyCode)\n } else if (eventKeyName) {\n return hyphenate(eventKeyName) !== key\n }\n}\n\n/* */\n\n/**\n * Runtime helper for merging v-bind=\"object\" into a VNode's data.\n */\nfunction bindObjectProps (\n data,\n tag,\n value,\n asProp,\n isSync\n) {\n if (value) {\n if (!isObject(value)) {\n \"production\" !== 'production' && warn(\n 'v-bind without argument expects an Object or Array value',\n this\n );\n } else {\n if (Array.isArray(value)) {\n value = toObject(value);\n }\n var hash;\n var loop = function ( key ) {\n if (\n key === 'class' ||\n key === 'style' ||\n isReservedAttribute(key)\n ) {\n hash = data;\n } else {\n var type = data.attrs && data.attrs.type;\n hash = asProp || config.mustUseProp(tag, type, key)\n ? data.domProps || (data.domProps = {})\n : data.attrs || (data.attrs = {});\n }\n var camelizedKey = camelize(key);\n var hyphenatedKey = hyphenate(key);\n if (!(camelizedKey in hash) && !(hyphenatedKey in hash)) {\n hash[key] = value[key];\n\n if (isSync) {\n var on = data.on || (data.on = {});\n on[(\"update:\" + key)] = function ($event) {\n value[key] = $event;\n };\n }\n }\n };\n\n for (var key in value) loop( key );\n }\n }\n return data\n}\n\n/* */\n\n/**\n * Runtime helper for rendering static trees.\n */\nfunction renderStatic (\n index,\n isInFor\n) {\n var cached = this._staticTrees || (this._staticTrees = []);\n var tree = cached[index];\n // if has already-rendered static tree and not inside v-for,\n // we can reuse the same tree.\n if (tree && !isInFor) {\n return tree\n }\n // otherwise, render a fresh tree.\n tree = cached[index] = this.$options.staticRenderFns[index].call(\n this._renderProxy,\n null,\n this // for render fns generated for functional component templates\n );\n markStatic(tree, (\"__static__\" + index), false);\n return tree\n}\n\n/**\n * Runtime helper for v-once.\n * Effectively it means marking the node as static with a unique key.\n */\nfunction markOnce (\n tree,\n index,\n key\n) {\n markStatic(tree, (\"__once__\" + index + (key ? (\"_\" + key) : \"\")), true);\n return tree\n}\n\nfunction markStatic (\n tree,\n key,\n isOnce\n) {\n if (Array.isArray(tree)) {\n for (var i = 0; i < tree.length; i++) {\n if (tree[i] && typeof tree[i] !== 'string') {\n markStaticNode(tree[i], (key + \"_\" + i), isOnce);\n }\n }\n } else {\n markStaticNode(tree, key, isOnce);\n }\n}\n\nfunction markStaticNode (node, key, isOnce) {\n node.isStatic = true;\n node.key = key;\n node.isOnce = isOnce;\n}\n\n/* */\n\nfunction bindObjectListeners (data, value) {\n if (value) {\n if (!isPlainObject(value)) {\n \"production\" !== 'production' && warn(\n 'v-on without argument expects an Object value',\n this\n );\n } else {\n var on = data.on = data.on ? extend({}, data.on) : {};\n for (var key in value) {\n var existing = on[key];\n var ours = value[key];\n on[key] = existing ? [].concat(existing, ours) : ours;\n }\n }\n }\n return data\n}\n\n/* */\n\nfunction resolveScopedSlots (\n fns, // see flow/vnode\n res,\n // the following are added in 2.6\n hasDynamicKeys,\n contentHashKey\n) {\n res = res || { $stable: !hasDynamicKeys };\n for (var i = 0; i < fns.length; i++) {\n var slot = fns[i];\n if (Array.isArray(slot)) {\n resolveScopedSlots(slot, res, hasDynamicKeys);\n } else if (slot) {\n // marker for reverse proxying v-slot without scope on this.$slots\n if (slot.proxy) {\n slot.fn.proxy = true;\n }\n res[slot.key] = slot.fn;\n }\n }\n if (contentHashKey) {\n (res).$key = contentHashKey;\n }\n return res\n}\n\n/* */\n\nfunction bindDynamicKeys (baseObj, values) {\n for (var i = 0; i < values.length; i += 2) {\n var key = values[i];\n if (typeof key === 'string' && key) {\n baseObj[values[i]] = values[i + 1];\n } else if (false) {\n // null is a speical value for explicitly removing a binding\n warn(\n (\"Invalid value for dynamic directive argument (expected string or null): \" + key),\n this\n );\n }\n }\n return baseObj\n}\n\n// helper to dynamically append modifier runtime markers to event names.\n// ensure only append when value is already string, otherwise it will be cast\n// to string and cause the type check to miss.\nfunction prependModifier (value, symbol) {\n return typeof value === 'string' ? symbol + value : value\n}\n\n/* */\n\nfunction installRenderHelpers (target) {\n target._o = markOnce;\n target._n = toNumber;\n target._s = toString;\n target._l = renderList;\n target._t = renderSlot;\n target._q = looseEqual;\n target._i = looseIndexOf;\n target._m = renderStatic;\n target._f = resolveFilter;\n target._k = checkKeyCodes;\n target._b = bindObjectProps;\n target._v = createTextVNode;\n target._e = createEmptyVNode;\n target._u = resolveScopedSlots;\n target._g = bindObjectListeners;\n target._d = bindDynamicKeys;\n target._p = prependModifier;\n}\n\n/* */\n\nfunction FunctionalRenderContext (\n data,\n props,\n children,\n parent,\n Ctor\n) {\n var this$1 = this;\n\n var options = Ctor.options;\n // ensure the createElement function in functional components\n // gets a unique context - this is necessary for correct named slot check\n var contextVm;\n if (hasOwn(parent, '_uid')) {\n contextVm = Object.create(parent);\n // $flow-disable-line\n contextVm._original = parent;\n } else {\n // the context vm passed in is a functional context as well.\n // in this case we want to make sure we are able to get a hold to the\n // real context instance.\n contextVm = parent;\n // $flow-disable-line\n parent = parent._original;\n }\n var isCompiled = isTrue(options._compiled);\n var needNormalization = !isCompiled;\n\n this.data = data;\n this.props = props;\n this.children = children;\n this.parent = parent;\n this.listeners = data.on || emptyObject;\n this.injections = resolveInject(options.inject, parent);\n this.slots = function () {\n if (!this$1.$slots) {\n normalizeScopedSlots(\n data.scopedSlots,\n this$1.$slots = resolveSlots(children, parent)\n );\n }\n return this$1.$slots\n };\n\n Object.defineProperty(this, 'scopedSlots', ({\n enumerable: true,\n get: function get () {\n return normalizeScopedSlots(data.scopedSlots, this.slots())\n }\n }));\n\n // support for compiled functional template\n if (isCompiled) {\n // exposing $options for renderStatic()\n this.$options = options;\n // pre-resolve slots for renderSlot()\n this.$slots = this.slots();\n this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots);\n }\n\n if (options._scopeId) {\n this._c = function (a, b, c, d) {\n var vnode = createElement(contextVm, a, b, c, d, needNormalization);\n if (vnode && !Array.isArray(vnode)) {\n vnode.fnScopeId = options._scopeId;\n vnode.fnContext = parent;\n }\n return vnode\n };\n } else {\n this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); };\n }\n}\n\ninstallRenderHelpers(FunctionalRenderContext.prototype);\n\nfunction createFunctionalComponent (\n Ctor,\n propsData,\n data,\n contextVm,\n children\n) {\n var options = Ctor.options;\n var props = {};\n var propOptions = options.props;\n if (isDef(propOptions)) {\n for (var key in propOptions) {\n props[key] = validateProp(key, propOptions, propsData || emptyObject);\n }\n } else {\n if (isDef(data.attrs)) { mergeProps(props, data.attrs); }\n if (isDef(data.props)) { mergeProps(props, data.props); }\n }\n\n var renderContext = new FunctionalRenderContext(\n data,\n props,\n children,\n contextVm,\n Ctor\n );\n\n var vnode = options.render.call(null, renderContext._c, renderContext);\n\n if (vnode instanceof VNode) {\n return cloneAndMarkFunctionalResult(vnode, data, renderContext.parent, options, renderContext)\n } else if (Array.isArray(vnode)) {\n var vnodes = normalizeChildren(vnode) || [];\n var res = new Array(vnodes.length);\n for (var i = 0; i < vnodes.length; i++) {\n res[i] = cloneAndMarkFunctionalResult(vnodes[i], data, renderContext.parent, options, renderContext);\n }\n return res\n }\n}\n\nfunction cloneAndMarkFunctionalResult (vnode, data, contextVm, options, renderContext) {\n // #7817 clone node before setting fnContext, otherwise if the node is reused\n // (e.g. it was from a cached normal slot) the fnContext causes named slots\n // that should not be matched to match.\n var clone = cloneVNode(vnode);\n clone.fnContext = contextVm;\n clone.fnOptions = options;\n if (false) {\n (clone.devtoolsMeta = clone.devtoolsMeta || {}).renderContext = renderContext;\n }\n if (data.slot) {\n (clone.data || (clone.data = {})).slot = data.slot;\n }\n return clone\n}\n\nfunction mergeProps (to, from) {\n for (var key in from) {\n to[camelize(key)] = from[key];\n }\n}\n\n/* */\n\n/* */\n\n/* */\n\n/* */\n\n// inline hooks to be invoked on component VNodes during patch\nvar componentVNodeHooks = {\n init: function init (vnode, hydrating) {\n if (\n vnode.componentInstance &&\n !vnode.componentInstance._isDestroyed &&\n vnode.data.keepAlive\n ) {\n // kept-alive components, treat as a patch\n var mountedNode = vnode; // work around flow\n componentVNodeHooks.prepatch(mountedNode, mountedNode);\n } else {\n var child = vnode.componentInstance = createComponentInstanceForVnode(\n vnode,\n activeInstance\n );\n child.$mount(hydrating ? vnode.elm : undefined, hydrating);\n }\n },\n\n prepatch: function prepatch (oldVnode, vnode) {\n var options = vnode.componentOptions;\n var child = vnode.componentInstance = oldVnode.componentInstance;\n updateChildComponent(\n child,\n options.propsData, // updated props\n options.listeners, // updated listeners\n vnode, // new parent vnode\n options.children // new children\n );\n },\n\n insert: function insert (vnode) {\n var context = vnode.context;\n var componentInstance = vnode.componentInstance;\n if (!componentInstance._isMounted) {\n componentInstance._isMounted = true;\n callHook(componentInstance, 'mounted');\n }\n if (vnode.data.keepAlive) {\n if (context._isMounted) {\n // vue-router#1212\n // During updates, a kept-alive component's child components may\n // change, so directly walking the tree here may call activated hooks\n // on incorrect children. Instead we push them into a queue which will\n // be processed after the whole patch process ended.\n queueActivatedComponent(componentInstance);\n } else {\n activateChildComponent(componentInstance, true /* direct */);\n }\n }\n },\n\n destroy: function destroy (vnode) {\n var componentInstance = vnode.componentInstance;\n if (!componentInstance._isDestroyed) {\n if (!vnode.data.keepAlive) {\n componentInstance.$destroy();\n } else {\n deactivateChildComponent(componentInstance, true /* direct */);\n }\n }\n }\n};\n\nvar hooksToMerge = Object.keys(componentVNodeHooks);\n\nfunction createComponent (\n Ctor,\n data,\n context,\n children,\n tag\n) {\n if (isUndef(Ctor)) {\n return\n }\n\n var baseCtor = context.$options._base;\n\n // plain options object: turn it into a constructor\n if (isObject(Ctor)) {\n Ctor = baseCtor.extend(Ctor);\n }\n\n // if at this stage it's not a constructor or an async component factory,\n // reject.\n if (typeof Ctor !== 'function') {\n if (false) {\n warn((\"Invalid Component definition: \" + (String(Ctor))), context);\n }\n return\n }\n\n // async component\n var asyncFactory;\n if (isUndef(Ctor.cid)) {\n asyncFactory = Ctor;\n Ctor = resolveAsyncComponent(asyncFactory, baseCtor);\n if (Ctor === undefined) {\n // return a placeholder node for async component, which is rendered\n // as a comment node but preserves all the raw information for the node.\n // the information will be used for async server-rendering and hydration.\n return createAsyncPlaceholder(\n asyncFactory,\n data,\n context,\n children,\n tag\n )\n }\n }\n\n data = data || {};\n\n // resolve constructor options in case global mixins are applied after\n // component constructor creation\n resolveConstructorOptions(Ctor);\n\n // transform component v-model data into props & events\n if (isDef(data.model)) {\n transformModel(Ctor.options, data);\n }\n\n // extract props\n var propsData = extractPropsFromVNodeData(data, Ctor, tag);\n\n // functional component\n if (isTrue(Ctor.options.functional)) {\n return createFunctionalComponent(Ctor, propsData, data, context, children)\n }\n\n // extract listeners, since these needs to be treated as\n // child component listeners instead of DOM listeners\n var listeners = data.on;\n // replace with listeners with .native modifier\n // so it gets processed during parent component patch.\n data.on = data.nativeOn;\n\n if (isTrue(Ctor.options.abstract)) {\n // abstract components do not keep anything\n // other than props & listeners & slot\n\n // work around flow\n var slot = data.slot;\n data = {};\n if (slot) {\n data.slot = slot;\n }\n }\n\n // install component management hooks onto the placeholder node\n installComponentHooks(data);\n\n // return a placeholder vnode\n var name = Ctor.options.name || tag;\n var vnode = new VNode(\n (\"vue-component-\" + (Ctor.cid) + (name ? (\"-\" + name) : '')),\n data, undefined, undefined, undefined, context,\n { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children },\n asyncFactory\n );\n\n return vnode\n}\n\nfunction createComponentInstanceForVnode (\n vnode, // we know it's MountedComponentVNode but flow doesn't\n parent // activeInstance in lifecycle state\n) {\n var options = {\n _isComponent: true,\n _parentVnode: vnode,\n parent: parent\n };\n // check inline-template render functions\n var inlineTemplate = vnode.data.inlineTemplate;\n if (isDef(inlineTemplate)) {\n options.render = inlineTemplate.render;\n options.staticRenderFns = inlineTemplate.staticRenderFns;\n }\n return new vnode.componentOptions.Ctor(options)\n}\n\nfunction installComponentHooks (data) {\n var hooks = data.hook || (data.hook = {});\n for (var i = 0; i < hooksToMerge.length; i++) {\n var key = hooksToMerge[i];\n var existing = hooks[key];\n var toMerge = componentVNodeHooks[key];\n if (existing !== toMerge && !(existing && existing._merged)) {\n hooks[key] = existing ? mergeHook$1(toMerge, existing) : toMerge;\n }\n }\n}\n\nfunction mergeHook$1 (f1, f2) {\n var merged = function (a, b) {\n // flow complains about extra args which is why we use any\n f1(a, b);\n f2(a, b);\n };\n merged._merged = true;\n return merged\n}\n\n// transform component v-model info (value and callback) into\n// prop and event handler respectively.\nfunction transformModel (options, data) {\n var prop = (options.model && options.model.prop) || 'value';\n var event = (options.model && options.model.event) || 'input'\n ;(data.attrs || (data.attrs = {}))[prop] = data.model.value;\n var on = data.on || (data.on = {});\n var existing = on[event];\n var callback = data.model.callback;\n if (isDef(existing)) {\n if (\n Array.isArray(existing)\n ? existing.indexOf(callback) === -1\n : existing !== callback\n ) {\n on[event] = [callback].concat(existing);\n }\n } else {\n on[event] = callback;\n }\n}\n\n/* */\n\nvar SIMPLE_NORMALIZE = 1;\nvar ALWAYS_NORMALIZE = 2;\n\n// wrapper function for providing a more flexible interface\n// without getting yelled at by flow\nfunction createElement (\n context,\n tag,\n data,\n children,\n normalizationType,\n alwaysNormalize\n) {\n if (Array.isArray(data) || isPrimitive(data)) {\n normalizationType = children;\n children = data;\n data = undefined;\n }\n if (isTrue(alwaysNormalize)) {\n normalizationType = ALWAYS_NORMALIZE;\n }\n return _createElement(context, tag, data, children, normalizationType)\n}\n\nfunction _createElement (\n context,\n tag,\n data,\n children,\n normalizationType\n) {\n if (isDef(data) && isDef((data).__ob__)) {\n \"production\" !== 'production' && warn(\n \"Avoid using observed data object as vnode data: \" + (JSON.stringify(data)) + \"\\n\" +\n 'Always create fresh vnode data objects in each render!',\n context\n );\n return createEmptyVNode()\n }\n // object syntax in v-bind\n if (isDef(data) && isDef(data.is)) {\n tag = data.is;\n }\n if (!tag) {\n // in case of component :is set to falsy value\n return createEmptyVNode()\n }\n // warn against non-primitive key\n if (false\n ) {\n {\n warn(\n 'Avoid using non-primitive value as key, ' +\n 'use string/number value instead.',\n context\n );\n }\n }\n // support single function children as default scoped slot\n if (Array.isArray(children) &&\n typeof children[0] === 'function'\n ) {\n data = data || {};\n data.scopedSlots = { default: children[0] };\n children.length = 0;\n }\n if (normalizationType === ALWAYS_NORMALIZE) {\n children = normalizeChildren(children);\n } else if (normalizationType === SIMPLE_NORMALIZE) {\n children = simpleNormalizeChildren(children);\n }\n var vnode, ns;\n if (typeof tag === 'string') {\n var Ctor;\n ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag);\n if (config.isReservedTag(tag)) {\n // platform built-in elements\n vnode = new VNode(\n config.parsePlatformTagName(tag), data, children,\n undefined, undefined, context\n );\n } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {\n // component\n vnode = createComponent(Ctor, data, context, children, tag);\n } else {\n // unknown or unlisted namespaced elements\n // check at runtime because it may get assigned a namespace when its\n // parent normalizes children\n vnode = new VNode(\n tag, data, children,\n undefined, undefined, context\n );\n }\n } else {\n // direct component options / constructor\n vnode = createComponent(tag, data, context, children);\n }\n if (Array.isArray(vnode)) {\n return vnode\n } else if (isDef(vnode)) {\n if (isDef(ns)) { applyNS(vnode, ns); }\n if (isDef(data)) { registerDeepBindings(data); }\n return vnode\n } else {\n return createEmptyVNode()\n }\n}\n\nfunction applyNS (vnode, ns, force) {\n vnode.ns = ns;\n if (vnode.tag === 'foreignObject') {\n // use default namespace inside foreignObject\n ns = undefined;\n force = true;\n }\n if (isDef(vnode.children)) {\n for (var i = 0, l = vnode.children.length; i < l; i++) {\n var child = vnode.children[i];\n if (isDef(child.tag) && (\n isUndef(child.ns) || (isTrue(force) && child.tag !== 'svg'))) {\n applyNS(child, ns, force);\n }\n }\n }\n}\n\n// ref #5318\n// necessary to ensure parent re-render when deep bindings like :style and\n// :class are used on slot nodes\nfunction registerDeepBindings (data) {\n if (isObject(data.style)) {\n traverse(data.style);\n }\n if (isObject(data.class)) {\n traverse(data.class);\n }\n}\n\n/* */\n\nfunction initRender (vm) {\n vm._vnode = null; // the root of the child tree\n vm._staticTrees = null; // v-once cached trees\n var options = vm.$options;\n var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree\n var renderContext = parentVnode && parentVnode.context;\n vm.$slots = resolveSlots(options._renderChildren, renderContext);\n vm.$scopedSlots = emptyObject;\n // bind the createElement fn to this instance\n // so that we get proper render context inside it.\n // args order: tag, data, children, normalizationType, alwaysNormalize\n // internal version is used by render functions compiled from templates\n vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };\n // normalization is always applied for the public version, used in\n // user-written render functions.\n vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };\n\n // $attrs & $listeners are exposed for easier HOC creation.\n // they need to be reactive so that HOCs using them are always updated\n var parentData = parentVnode && parentVnode.data;\n\n /* istanbul ignore else */\n if (false) {\n defineReactive$$1(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () {\n !isUpdatingChildComponent && warn(\"$attrs is readonly.\", vm);\n }, true);\n defineReactive$$1(vm, '$listeners', options._parentListeners || emptyObject, function () {\n !isUpdatingChildComponent && warn(\"$listeners is readonly.\", vm);\n }, true);\n } else {\n defineReactive$$1(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true);\n defineReactive$$1(vm, '$listeners', options._parentListeners || emptyObject, null, true);\n }\n}\n\nvar currentRenderingInstance = null;\n\nfunction renderMixin (Vue) {\n // install runtime convenience helpers\n installRenderHelpers(Vue.prototype);\n\n Vue.prototype.$nextTick = function (fn) {\n return nextTick(fn, this)\n };\n\n Vue.prototype._render = function () {\n var vm = this;\n var ref = vm.$options;\n var render = ref.render;\n var _parentVnode = ref._parentVnode;\n\n if (_parentVnode) {\n vm.$scopedSlots = normalizeScopedSlots(\n _parentVnode.data.scopedSlots,\n vm.$slots,\n vm.$scopedSlots\n );\n }\n\n // set parent vnode. this allows render functions to have access\n // to the data on the placeholder node.\n vm.$vnode = _parentVnode;\n // render self\n var vnode;\n try {\n // There's no need to maintain a stack becaues all render fns are called\n // separately from one another. Nested component's render fns are called\n // when parent component is patched.\n currentRenderingInstance = vm;\n vnode = render.call(vm._renderProxy, vm.$createElement);\n } catch (e) {\n handleError(e, vm, \"render\");\n // return error render result,\n // or previous vnode to prevent render error causing blank component\n /* istanbul ignore else */\n if (false) {\n try {\n vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e);\n } catch (e) {\n handleError(e, vm, \"renderError\");\n vnode = vm._vnode;\n }\n } else {\n vnode = vm._vnode;\n }\n } finally {\n currentRenderingInstance = null;\n }\n // if the returned array contains only a single node, allow it\n if (Array.isArray(vnode) && vnode.length === 1) {\n vnode = vnode[0];\n }\n // return empty vnode in case the render function errored out\n if (!(vnode instanceof VNode)) {\n if (false) {\n warn(\n 'Multiple root nodes returned from render function. Render function ' +\n 'should return a single root node.',\n vm\n );\n }\n vnode = createEmptyVNode();\n }\n // set parent\n vnode.parent = _parentVnode;\n return vnode\n };\n}\n\n/* */\n\nfunction ensureCtor (comp, base) {\n if (\n comp.__esModule ||\n (hasSymbol && comp[Symbol.toStringTag] === 'Module')\n ) {\n comp = comp.default;\n }\n return isObject(comp)\n ? base.extend(comp)\n : comp\n}\n\nfunction createAsyncPlaceholder (\n factory,\n data,\n context,\n children,\n tag\n) {\n var node = createEmptyVNode();\n node.asyncFactory = factory;\n node.asyncMeta = { data: data, context: context, children: children, tag: tag };\n return node\n}\n\nfunction resolveAsyncComponent (\n factory,\n baseCtor\n) {\n if (isTrue(factory.error) && isDef(factory.errorComp)) {\n return factory.errorComp\n }\n\n if (isDef(factory.resolved)) {\n return factory.resolved\n }\n\n var owner = currentRenderingInstance;\n if (owner && isDef(factory.owners) && factory.owners.indexOf(owner) === -1) {\n // already pending\n factory.owners.push(owner);\n }\n\n if (isTrue(factory.loading) && isDef(factory.loadingComp)) {\n return factory.loadingComp\n }\n\n if (owner && !isDef(factory.owners)) {\n var owners = factory.owners = [owner];\n var sync = true;\n var timerLoading = null;\n var timerTimeout = null\n\n ;(owner).$on('hook:destroyed', function () { return remove(owners, owner); });\n\n var forceRender = function (renderCompleted) {\n for (var i = 0, l = owners.length; i < l; i++) {\n (owners[i]).$forceUpdate();\n }\n\n if (renderCompleted) {\n owners.length = 0;\n if (timerLoading !== null) {\n clearTimeout(timerLoading);\n timerLoading = null;\n }\n if (timerTimeout !== null) {\n clearTimeout(timerTimeout);\n timerTimeout = null;\n }\n }\n };\n\n var resolve = once(function (res) {\n // cache resolved\n factory.resolved = ensureCtor(res, baseCtor);\n // invoke callbacks only if this is not a synchronous resolve\n // (async resolves are shimmed as synchronous during SSR)\n if (!sync) {\n forceRender(true);\n } else {\n owners.length = 0;\n }\n });\n\n var reject = once(function (reason) {\n \"production\" !== 'production' && warn(\n \"Failed to resolve async component: \" + (String(factory)) +\n (reason ? (\"\\nReason: \" + reason) : '')\n );\n if (isDef(factory.errorComp)) {\n factory.error = true;\n forceRender(true);\n }\n });\n\n var res = factory(resolve, reject);\n\n if (isObject(res)) {\n if (isPromise(res)) {\n // () => Promise\n if (isUndef(factory.resolved)) {\n res.then(resolve, reject);\n }\n } else if (isPromise(res.component)) {\n res.component.then(resolve, reject);\n\n if (isDef(res.error)) {\n factory.errorComp = ensureCtor(res.error, baseCtor);\n }\n\n if (isDef(res.loading)) {\n factory.loadingComp = ensureCtor(res.loading, baseCtor);\n if (res.delay === 0) {\n factory.loading = true;\n } else {\n timerLoading = setTimeout(function () {\n timerLoading = null;\n if (isUndef(factory.resolved) && isUndef(factory.error)) {\n factory.loading = true;\n forceRender(false);\n }\n }, res.delay || 200);\n }\n }\n\n if (isDef(res.timeout)) {\n timerTimeout = setTimeout(function () {\n timerTimeout = null;\n if (isUndef(factory.resolved)) {\n reject(\n false\n ? (\"timeout (\" + (res.timeout) + \"ms)\")\n : null\n );\n }\n }, res.timeout);\n }\n }\n }\n\n sync = false;\n // return in case resolved synchronously\n return factory.loading\n ? factory.loadingComp\n : factory.resolved\n }\n}\n\n/* */\n\nfunction isAsyncPlaceholder (node) {\n return node.isComment && node.asyncFactory\n}\n\n/* */\n\nfunction getFirstComponentChild (children) {\n if (Array.isArray(children)) {\n for (var i = 0; i < children.length; i++) {\n var c = children[i];\n if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {\n return c\n }\n }\n }\n}\n\n/* */\n\n/* */\n\nfunction initEvents (vm) {\n vm._events = Object.create(null);\n vm._hasHookEvent = false;\n // init parent attached events\n var listeners = vm.$options._parentListeners;\n if (listeners) {\n updateComponentListeners(vm, listeners);\n }\n}\n\nvar target;\n\nfunction add (event, fn) {\n target.$on(event, fn);\n}\n\nfunction remove$1 (event, fn) {\n target.$off(event, fn);\n}\n\nfunction createOnceHandler (event, fn) {\n var _target = target;\n return function onceHandler () {\n var res = fn.apply(null, arguments);\n if (res !== null) {\n _target.$off(event, onceHandler);\n }\n }\n}\n\nfunction updateComponentListeners (\n vm,\n listeners,\n oldListeners\n) {\n target = vm;\n updateListeners(listeners, oldListeners || {}, add, remove$1, createOnceHandler, vm);\n target = undefined;\n}\n\nfunction eventsMixin (Vue) {\n var hookRE = /^hook:/;\n Vue.prototype.$on = function (event, fn) {\n var vm = this;\n if (Array.isArray(event)) {\n for (var i = 0, l = event.length; i < l; i++) {\n vm.$on(event[i], fn);\n }\n } else {\n (vm._events[event] || (vm._events[event] = [])).push(fn);\n // optimize hook:event cost by using a boolean flag marked at registration\n // instead of a hash lookup\n if (hookRE.test(event)) {\n vm._hasHookEvent = true;\n }\n }\n return vm\n };\n\n Vue.prototype.$once = function (event, fn) {\n var vm = this;\n function on () {\n vm.$off(event, on);\n fn.apply(vm, arguments);\n }\n on.fn = fn;\n vm.$on(event, on);\n return vm\n };\n\n Vue.prototype.$off = function (event, fn) {\n var vm = this;\n // all\n if (!arguments.length) {\n vm._events = Object.create(null);\n return vm\n }\n // array of events\n if (Array.isArray(event)) {\n for (var i$1 = 0, l = event.length; i$1 < l; i$1++) {\n vm.$off(event[i$1], fn);\n }\n return vm\n }\n // specific event\n var cbs = vm._events[event];\n if (!cbs) {\n return vm\n }\n if (!fn) {\n vm._events[event] = null;\n return vm\n }\n // specific handler\n var cb;\n var i = cbs.length;\n while (i--) {\n cb = cbs[i];\n if (cb === fn || cb.fn === fn) {\n cbs.splice(i, 1);\n break\n }\n }\n return vm\n };\n\n Vue.prototype.$emit = function (event) {\n var vm = this;\n if (false) {\n var lowerCaseEvent = event.toLowerCase();\n if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {\n tip(\n \"Event \\\"\" + lowerCaseEvent + \"\\\" is emitted in component \" +\n (formatComponentName(vm)) + \" but the handler is registered for \\\"\" + event + \"\\\". \" +\n \"Note that HTML attributes are case-insensitive and you cannot use \" +\n \"v-on to listen to camelCase events when using in-DOM templates. \" +\n \"You should probably use \\\"\" + (hyphenate(event)) + \"\\\" instead of \\\"\" + event + \"\\\".\"\n );\n }\n }\n var cbs = vm._events[event];\n if (cbs) {\n cbs = cbs.length > 1 ? toArray(cbs) : cbs;\n var args = toArray(arguments, 1);\n var info = \"event handler for \\\"\" + event + \"\\\"\";\n for (var i = 0, l = cbs.length; i < l; i++) {\n invokeWithErrorHandling(cbs[i], vm, args, vm, info);\n }\n }\n return vm\n };\n}\n\n/* */\n\nvar activeInstance = null;\nvar isUpdatingChildComponent = false;\n\nfunction setActiveInstance(vm) {\n var prevActiveInstance = activeInstance;\n activeInstance = vm;\n return function () {\n activeInstance = prevActiveInstance;\n }\n}\n\nfunction initLifecycle (vm) {\n var options = vm.$options;\n\n // locate first non-abstract parent\n var parent = options.parent;\n if (parent && !options.abstract) {\n while (parent.$options.abstract && parent.$parent) {\n parent = parent.$parent;\n }\n parent.$children.push(vm);\n }\n\n vm.$parent = parent;\n vm.$root = parent ? parent.$root : vm;\n\n vm.$children = [];\n vm.$refs = {};\n\n vm._watcher = null;\n vm._inactive = null;\n vm._directInactive = false;\n vm._isMounted = false;\n vm._isDestroyed = false;\n vm._isBeingDestroyed = false;\n}\n\nfunction lifecycleMixin (Vue) {\n Vue.prototype._update = function (vnode, hydrating) {\n var vm = this;\n var prevEl = vm.$el;\n var prevVnode = vm._vnode;\n var restoreActiveInstance = setActiveInstance(vm);\n vm._vnode = vnode;\n // Vue.prototype.__patch__ is injected in entry points\n // based on the rendering backend used.\n if (!prevVnode) {\n // initial render\n vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);\n } else {\n // updates\n vm.$el = vm.__patch__(prevVnode, vnode);\n }\n restoreActiveInstance();\n // update __vue__ reference\n if (prevEl) {\n prevEl.__vue__ = null;\n }\n if (vm.$el) {\n vm.$el.__vue__ = vm;\n }\n // if parent is an HOC, update its $el as well\n if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {\n vm.$parent.$el = vm.$el;\n }\n // updated hook is called by the scheduler to ensure that children are\n // updated in a parent's updated hook.\n };\n\n Vue.prototype.$forceUpdate = function () {\n var vm = this;\n if (vm._watcher) {\n vm._watcher.update();\n }\n };\n\n Vue.prototype.$destroy = function () {\n var vm = this;\n if (vm._isBeingDestroyed) {\n return\n }\n callHook(vm, 'beforeDestroy');\n vm._isBeingDestroyed = true;\n // remove self from parent\n var parent = vm.$parent;\n if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {\n remove(parent.$children, vm);\n }\n // teardown watchers\n if (vm._watcher) {\n vm._watcher.teardown();\n }\n var i = vm._watchers.length;\n while (i--) {\n vm._watchers[i].teardown();\n }\n // remove reference from data ob\n // frozen object may not have observer.\n if (vm._data.__ob__) {\n vm._data.__ob__.vmCount--;\n }\n // call the last hook...\n vm._isDestroyed = true;\n // invoke destroy hooks on current rendered tree\n vm.__patch__(vm._vnode, null);\n // fire destroyed hook\n callHook(vm, 'destroyed');\n // turn off all instance listeners.\n vm.$off();\n // remove __vue__ reference\n if (vm.$el) {\n vm.$el.__vue__ = null;\n }\n // release circular reference (#6759)\n if (vm.$vnode) {\n vm.$vnode.parent = null;\n }\n };\n}\n\nfunction mountComponent (\n vm,\n el,\n hydrating\n) {\n vm.$el = el;\n if (!vm.$options.render) {\n vm.$options.render = createEmptyVNode;\n if (false) {\n /* istanbul ignore if */\n if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||\n vm.$options.el || el) {\n warn(\n 'You are using the runtime-only build of Vue where the template ' +\n 'compiler is not available. Either pre-compile the templates into ' +\n 'render functions, or use the compiler-included build.',\n vm\n );\n } else {\n warn(\n 'Failed to mount component: template or render function not defined.',\n vm\n );\n }\n }\n }\n callHook(vm, 'beforeMount');\n\n var updateComponent;\n /* istanbul ignore if */\n if (false) {\n updateComponent = function () {\n var name = vm._name;\n var id = vm._uid;\n var startTag = \"vue-perf-start:\" + id;\n var endTag = \"vue-perf-end:\" + id;\n\n mark(startTag);\n var vnode = vm._render();\n mark(endTag);\n measure((\"vue \" + name + \" render\"), startTag, endTag);\n\n mark(startTag);\n vm._update(vnode, hydrating);\n mark(endTag);\n measure((\"vue \" + name + \" patch\"), startTag, endTag);\n };\n } else {\n updateComponent = function () {\n vm._update(vm._render(), hydrating);\n };\n }\n\n // we set this to vm._watcher inside the watcher's constructor\n // since the watcher's initial patch may call $forceUpdate (e.g. inside child\n // component's mounted hook), which relies on vm._watcher being already defined\n new Watcher(vm, updateComponent, noop, {\n before: function before () {\n if (vm._isMounted && !vm._isDestroyed) {\n callHook(vm, 'beforeUpdate');\n }\n }\n }, true /* isRenderWatcher */);\n hydrating = false;\n\n // manually mounted instance, call mounted on self\n // mounted is called for render-created child components in its inserted hook\n if (vm.$vnode == null) {\n vm._isMounted = true;\n callHook(vm, 'mounted');\n }\n return vm\n}\n\nfunction updateChildComponent (\n vm,\n propsData,\n listeners,\n parentVnode,\n renderChildren\n) {\n if (false) {\n isUpdatingChildComponent = true;\n }\n\n // determine whether component has slot children\n // we need to do this before overwriting $options._renderChildren.\n\n // check if there are dynamic scopedSlots (hand-written or compiled but with\n // dynamic slot names). Static scoped slots compiled from template has the\n // \"$stable\" marker.\n var newScopedSlots = parentVnode.data.scopedSlots;\n var oldScopedSlots = vm.$scopedSlots;\n var hasDynamicScopedSlot = !!(\n (newScopedSlots && !newScopedSlots.$stable) ||\n (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||\n (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key)\n );\n\n // Any static slot children from the parent may have changed during parent's\n // update. Dynamic scoped slots may also have changed. In such cases, a forced\n // update is necessary to ensure correctness.\n var needsForceUpdate = !!(\n renderChildren || // has new static slots\n vm.$options._renderChildren || // has old static slots\n hasDynamicScopedSlot\n );\n\n vm.$options._parentVnode = parentVnode;\n vm.$vnode = parentVnode; // update vm's placeholder node without re-render\n\n if (vm._vnode) { // update child tree's parent\n vm._vnode.parent = parentVnode;\n }\n vm.$options._renderChildren = renderChildren;\n\n // update $attrs and $listeners hash\n // these are also reactive so they may trigger child update if the child\n // used them during render\n vm.$attrs = parentVnode.data.attrs || emptyObject;\n vm.$listeners = listeners || emptyObject;\n\n // update props\n if (propsData && vm.$options.props) {\n toggleObserving(false);\n var props = vm._props;\n var propKeys = vm.$options._propKeys || [];\n for (var i = 0; i < propKeys.length; i++) {\n var key = propKeys[i];\n var propOptions = vm.$options.props; // wtf flow?\n props[key] = validateProp(key, propOptions, propsData, vm);\n }\n toggleObserving(true);\n // keep a copy of raw propsData\n vm.$options.propsData = propsData;\n }\n\n // update listeners\n listeners = listeners || emptyObject;\n var oldListeners = vm.$options._parentListeners;\n vm.$options._parentListeners = listeners;\n updateComponentListeners(vm, listeners, oldListeners);\n\n // resolve slots + force update if has children\n if (needsForceUpdate) {\n vm.$slots = resolveSlots(renderChildren, parentVnode.context);\n vm.$forceUpdate();\n }\n\n if (false) {\n isUpdatingChildComponent = false;\n }\n}\n\nfunction isInInactiveTree (vm) {\n while (vm && (vm = vm.$parent)) {\n if (vm._inactive) { return true }\n }\n return false\n}\n\nfunction activateChildComponent (vm, direct) {\n if (direct) {\n vm._directInactive = false;\n if (isInInactiveTree(vm)) {\n return\n }\n } else if (vm._directInactive) {\n return\n }\n if (vm._inactive || vm._inactive === null) {\n vm._inactive = false;\n for (var i = 0; i < vm.$children.length; i++) {\n activateChildComponent(vm.$children[i]);\n }\n callHook(vm, 'activated');\n }\n}\n\nfunction deactivateChildComponent (vm, direct) {\n if (direct) {\n vm._directInactive = true;\n if (isInInactiveTree(vm)) {\n return\n }\n }\n if (!vm._inactive) {\n vm._inactive = true;\n for (var i = 0; i < vm.$children.length; i++) {\n deactivateChildComponent(vm.$children[i]);\n }\n callHook(vm, 'deactivated');\n }\n}\n\nfunction callHook (vm, hook) {\n // #7573 disable dep collection when invoking lifecycle hooks\n pushTarget();\n var handlers = vm.$options[hook];\n var info = hook + \" hook\";\n if (handlers) {\n for (var i = 0, j = handlers.length; i < j; i++) {\n invokeWithErrorHandling(handlers[i], vm, null, vm, info);\n }\n }\n if (vm._hasHookEvent) {\n vm.$emit('hook:' + hook);\n }\n popTarget();\n}\n\n/* */\n\nvar MAX_UPDATE_COUNT = 100;\n\nvar queue = [];\nvar activatedChildren = [];\nvar has = {};\nvar circular = {};\nvar waiting = false;\nvar flushing = false;\nvar index = 0;\n\n/**\n * Reset the scheduler's state.\n */\nfunction resetSchedulerState () {\n index = queue.length = activatedChildren.length = 0;\n has = {};\n if (false) {\n circular = {};\n }\n waiting = flushing = false;\n}\n\n// Async edge case #6566 requires saving the timestamp when event listeners are\n// attached. However, calling performance.now() has a perf overhead especially\n// if the page has thousands of event listeners. Instead, we take a timestamp\n// every time the scheduler flushes and use that for all event listeners\n// attached during that flush.\nvar currentFlushTimestamp = 0;\n\n// Async edge case fix requires storing an event listener's attach timestamp.\nvar getNow = Date.now;\n\n// Determine what event timestamp the browser is using. Annoyingly, the\n// timestamp can either be hi-res (relative to page load) or low-res\n// (relative to UNIX epoch), so in order to compare time we have to use the\n// same timestamp type when saving the flush timestamp.\n// All IE versions use low-res event timestamps, and have problematic clock\n// implementations (#9632)\nif (inBrowser && !isIE) {\n var performance = window.performance;\n if (\n performance &&\n typeof performance.now === 'function' &&\n getNow() > document.createEvent('Event').timeStamp\n ) {\n // if the event timestamp, although evaluated AFTER the Date.now(), is\n // smaller than it, it means the event is using a hi-res timestamp,\n // and we need to use the hi-res version for event listener timestamps as\n // well.\n getNow = function () { return performance.now(); };\n }\n}\n\n/**\n * Flush both queues and run the watchers.\n */\nfunction flushSchedulerQueue () {\n currentFlushTimestamp = getNow();\n flushing = true;\n var watcher, id;\n\n // Sort queue before flush.\n // This ensures that:\n // 1. Components are updated from parent to child. (because parent is always\n // created before the child)\n // 2. A component's user watchers are run before its render watcher (because\n // user watchers are created before the render watcher)\n // 3. If a component is destroyed during a parent component's watcher run,\n // its watchers can be skipped.\n queue.sort(function (a, b) { return a.id - b.id; });\n\n // do not cache length because more watchers might be pushed\n // as we run existing watchers\n for (index = 0; index < queue.length; index++) {\n watcher = queue[index];\n if (watcher.before) {\n watcher.before();\n }\n id = watcher.id;\n has[id] = null;\n watcher.run();\n // in dev build, check and stop circular updates.\n if (false) {\n circular[id] = (circular[id] || 0) + 1;\n if (circular[id] > MAX_UPDATE_COUNT) {\n warn(\n 'You may have an infinite update loop ' + (\n watcher.user\n ? (\"in watcher with expression \\\"\" + (watcher.expression) + \"\\\"\")\n : \"in a component render function.\"\n ),\n watcher.vm\n );\n break\n }\n }\n }\n\n // keep copies of post queues before resetting state\n var activatedQueue = activatedChildren.slice();\n var updatedQueue = queue.slice();\n\n resetSchedulerState();\n\n // call component updated and activated hooks\n callActivatedHooks(activatedQueue);\n callUpdatedHooks(updatedQueue);\n\n // devtool hook\n /* istanbul ignore if */\n if (devtools && config.devtools) {\n devtools.emit('flush');\n }\n}\n\nfunction callUpdatedHooks (queue) {\n var i = queue.length;\n while (i--) {\n var watcher = queue[i];\n var vm = watcher.vm;\n if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {\n callHook(vm, 'updated');\n }\n }\n}\n\n/**\n * Queue a kept-alive component that was activated during patch.\n * The queue will be processed after the entire tree has been patched.\n */\nfunction queueActivatedComponent (vm) {\n // setting _inactive to false here so that a render function can\n // rely on checking whether it's in an inactive tree (e.g. router-view)\n vm._inactive = false;\n activatedChildren.push(vm);\n}\n\nfunction callActivatedHooks (queue) {\n for (var i = 0; i < queue.length; i++) {\n queue[i]._inactive = true;\n activateChildComponent(queue[i], true /* true */);\n }\n}\n\n/**\n * Push a watcher into the watcher queue.\n * Jobs with duplicate IDs will be skipped unless it's\n * pushed when the queue is being flushed.\n */\nfunction queueWatcher (watcher) {\n var id = watcher.id;\n if (has[id] == null) {\n has[id] = true;\n if (!flushing) {\n queue.push(watcher);\n } else {\n // if already flushing, splice the watcher based on its id\n // if already past its id, it will be run next immediately.\n var i = queue.length - 1;\n while (i > index && queue[i].id > watcher.id) {\n i--;\n }\n queue.splice(i + 1, 0, watcher);\n }\n // queue the flush\n if (!waiting) {\n waiting = true;\n\n if (false) {\n flushSchedulerQueue();\n return\n }\n nextTick(flushSchedulerQueue);\n }\n }\n}\n\n/* */\n\n\n\nvar uid$2 = 0;\n\n/**\n * A watcher parses an expression, collects dependencies,\n * and fires callback when the expression value changes.\n * This is used for both the $watch() api and directives.\n */\nvar Watcher = function Watcher (\n vm,\n expOrFn,\n cb,\n options,\n isRenderWatcher\n) {\n this.vm = vm;\n if (isRenderWatcher) {\n vm._watcher = this;\n }\n vm._watchers.push(this);\n // options\n if (options) {\n this.deep = !!options.deep;\n this.user = !!options.user;\n this.lazy = !!options.lazy;\n this.sync = !!options.sync;\n this.before = options.before;\n } else {\n this.deep = this.user = this.lazy = this.sync = false;\n }\n this.cb = cb;\n this.id = ++uid$2; // uid for batching\n this.active = true;\n this.dirty = this.lazy; // for lazy watchers\n this.deps = [];\n this.newDeps = [];\n this.depIds = new _Set();\n this.newDepIds = new _Set();\n this.expression = false\n ? expOrFn.toString()\n : '';\n // parse expression for getter\n if (typeof expOrFn === 'function') {\n this.getter = expOrFn;\n } else {\n this.getter = parsePath(expOrFn);\n if (!this.getter) {\n this.getter = noop;\n \"production\" !== 'production' && warn(\n \"Failed watching path: \\\"\" + expOrFn + \"\\\" \" +\n 'Watcher only accepts simple dot-delimited paths. ' +\n 'For full control, use a function instead.',\n vm\n );\n }\n }\n this.value = this.lazy\n ? undefined\n : this.get();\n};\n\n/**\n * Evaluate the getter, and re-collect dependencies.\n */\nWatcher.prototype.get = function get () {\n pushTarget(this);\n var value;\n var vm = this.vm;\n try {\n value = this.getter.call(vm, vm);\n } catch (e) {\n if (this.user) {\n handleError(e, vm, (\"getter for watcher \\\"\" + (this.expression) + \"\\\"\"));\n } else {\n throw e\n }\n } finally {\n // \"touch\" every property so they are all tracked as\n // dependencies for deep watching\n if (this.deep) {\n traverse(value);\n }\n popTarget();\n this.cleanupDeps();\n }\n return value\n};\n\n/**\n * Add a dependency to this directive.\n */\nWatcher.prototype.addDep = function addDep (dep) {\n var id = dep.id;\n if (!this.newDepIds.has(id)) {\n this.newDepIds.add(id);\n this.newDeps.push(dep);\n if (!this.depIds.has(id)) {\n dep.addSub(this);\n }\n }\n};\n\n/**\n * Clean up for dependency collection.\n */\nWatcher.prototype.cleanupDeps = function cleanupDeps () {\n var i = this.deps.length;\n while (i--) {\n var dep = this.deps[i];\n if (!this.newDepIds.has(dep.id)) {\n dep.removeSub(this);\n }\n }\n var tmp = this.depIds;\n this.depIds = this.newDepIds;\n this.newDepIds = tmp;\n this.newDepIds.clear();\n tmp = this.deps;\n this.deps = this.newDeps;\n this.newDeps = tmp;\n this.newDeps.length = 0;\n};\n\n/**\n * Subscriber interface.\n * Will be called when a dependency changes.\n */\nWatcher.prototype.update = function update () {\n /* istanbul ignore else */\n if (this.lazy) {\n this.dirty = true;\n } else if (this.sync) {\n this.run();\n } else {\n queueWatcher(this);\n }\n};\n\n/**\n * Scheduler job interface.\n * Will be called by the scheduler.\n */\nWatcher.prototype.run = function run () {\n if (this.active) {\n var value = this.get();\n if (\n value !== this.value ||\n // Deep watchers and watchers on Object/Arrays should fire even\n // when the value is the same, because the value may\n // have mutated.\n isObject(value) ||\n this.deep\n ) {\n // set new value\n var oldValue = this.value;\n this.value = value;\n if (this.user) {\n try {\n this.cb.call(this.vm, value, oldValue);\n } catch (e) {\n handleError(e, this.vm, (\"callback for watcher \\\"\" + (this.expression) + \"\\\"\"));\n }\n } else {\n this.cb.call(this.vm, value, oldValue);\n }\n }\n }\n};\n\n/**\n * Evaluate the value of the watcher.\n * This only gets called for lazy watchers.\n */\nWatcher.prototype.evaluate = function evaluate () {\n this.value = this.get();\n this.dirty = false;\n};\n\n/**\n * Depend on all deps collected by this watcher.\n */\nWatcher.prototype.depend = function depend () {\n var i = this.deps.length;\n while (i--) {\n this.deps[i].depend();\n }\n};\n\n/**\n * Remove self from all dependencies' subscriber list.\n */\nWatcher.prototype.teardown = function teardown () {\n if (this.active) {\n // remove self from vm's watcher list\n // this is a somewhat expensive operation so we skip it\n // if the vm is being destroyed.\n if (!this.vm._isBeingDestroyed) {\n remove(this.vm._watchers, this);\n }\n var i = this.deps.length;\n while (i--) {\n this.deps[i].removeSub(this);\n }\n this.active = false;\n }\n};\n\n/* */\n\nvar sharedPropertyDefinition = {\n enumerable: true,\n configurable: true,\n get: noop,\n set: noop\n};\n\nfunction proxy (target, sourceKey, key) {\n sharedPropertyDefinition.get = function proxyGetter () {\n return this[sourceKey][key]\n };\n sharedPropertyDefinition.set = function proxySetter (val) {\n this[sourceKey][key] = val;\n };\n Object.defineProperty(target, key, sharedPropertyDefinition);\n}\n\nfunction initState (vm) {\n vm._watchers = [];\n var opts = vm.$options;\n if (opts.props) { initProps(vm, opts.props); }\n if (opts.methods) { initMethods(vm, opts.methods); }\n if (opts.data) {\n initData(vm);\n } else {\n observe(vm._data = {}, true /* asRootData */);\n }\n if (opts.computed) { initComputed(vm, opts.computed); }\n if (opts.watch && opts.watch !== nativeWatch) {\n initWatch(vm, opts.watch);\n }\n}\n\nfunction initProps (vm, propsOptions) {\n var propsData = vm.$options.propsData || {};\n var props = vm._props = {};\n // cache prop keys so that future props updates can iterate using Array\n // instead of dynamic object key enumeration.\n var keys = vm.$options._propKeys = [];\n var isRoot = !vm.$parent;\n // root instance props should be converted\n if (!isRoot) {\n toggleObserving(false);\n }\n var loop = function ( key ) {\n keys.push(key);\n var value = validateProp(key, propsOptions, propsData, vm);\n /* istanbul ignore else */\n if (false) {\n var hyphenatedKey = hyphenate(key);\n if (isReservedAttribute(hyphenatedKey) ||\n config.isReservedAttr(hyphenatedKey)) {\n warn(\n (\"\\\"\" + hyphenatedKey + \"\\\" is a reserved attribute and cannot be used as component prop.\"),\n vm\n );\n }\n defineReactive$$1(props, key, value, function () {\n if (!isRoot && !isUpdatingChildComponent) {\n warn(\n \"Avoid mutating a prop directly since the value will be \" +\n \"overwritten whenever the parent component re-renders. \" +\n \"Instead, use a data or computed property based on the prop's \" +\n \"value. Prop being mutated: \\\"\" + key + \"\\\"\",\n vm\n );\n }\n });\n } else {\n defineReactive$$1(props, key, value);\n }\n // static props are already proxied on the component's prototype\n // during Vue.extend(). We only need to proxy props defined at\n // instantiation here.\n if (!(key in vm)) {\n proxy(vm, \"_props\", key);\n }\n };\n\n for (var key in propsOptions) loop( key );\n toggleObserving(true);\n}\n\nfunction initData (vm) {\n var data = vm.$options.data;\n data = vm._data = typeof data === 'function'\n ? getData(data, vm)\n : data || {};\n if (!isPlainObject(data)) {\n data = {};\n \"production\" !== 'production' && warn(\n 'data functions should return an object:\\n' +\n 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',\n vm\n );\n }\n // proxy data on instance\n var keys = Object.keys(data);\n var props = vm.$options.props;\n var methods = vm.$options.methods;\n var i = keys.length;\n while (i--) {\n var key = keys[i];\n if (false) {\n if (methods && hasOwn(methods, key)) {\n warn(\n (\"Method \\\"\" + key + \"\\\" has already been defined as a data property.\"),\n vm\n );\n }\n }\n if (props && hasOwn(props, key)) {\n \"production\" !== 'production' && warn(\n \"The data property \\\"\" + key + \"\\\" is already declared as a prop. \" +\n \"Use prop default value instead.\",\n vm\n );\n } else if (!isReserved(key)) {\n proxy(vm, \"_data\", key);\n }\n }\n // observe data\n observe(data, true /* asRootData */);\n}\n\nfunction getData (data, vm) {\n // #7573 disable dep collection when invoking data getters\n pushTarget();\n try {\n return data.call(vm, vm)\n } catch (e) {\n handleError(e, vm, \"data()\");\n return {}\n } finally {\n popTarget();\n }\n}\n\nvar computedWatcherOptions = { lazy: true };\n\nfunction initComputed (vm, computed) {\n // $flow-disable-line\n var watchers = vm._computedWatchers = Object.create(null);\n // computed properties are just getters during SSR\n var isSSR = isServerRendering();\n\n for (var key in computed) {\n var userDef = computed[key];\n var getter = typeof userDef === 'function' ? userDef : userDef.get;\n if (false) {\n warn(\n (\"Getter is missing for computed property \\\"\" + key + \"\\\".\"),\n vm\n );\n }\n\n if (!isSSR) {\n // create internal watcher for the computed property.\n watchers[key] = new Watcher(\n vm,\n getter || noop,\n noop,\n computedWatcherOptions\n );\n }\n\n // component-defined computed properties are already defined on the\n // component prototype. We only need to define computed properties defined\n // at instantiation here.\n if (!(key in vm)) {\n defineComputed(vm, key, userDef);\n } else if (false) {\n if (key in vm.$data) {\n warn((\"The computed property \\\"\" + key + \"\\\" is already defined in data.\"), vm);\n } else if (vm.$options.props && key in vm.$options.props) {\n warn((\"The computed property \\\"\" + key + \"\\\" is already defined as a prop.\"), vm);\n }\n }\n }\n}\n\nfunction defineComputed (\n target,\n key,\n userDef\n) {\n var shouldCache = !isServerRendering();\n if (typeof userDef === 'function') {\n sharedPropertyDefinition.get = shouldCache\n ? createComputedGetter(key)\n : createGetterInvoker(userDef);\n sharedPropertyDefinition.set = noop;\n } else {\n sharedPropertyDefinition.get = userDef.get\n ? shouldCache && userDef.cache !== false\n ? createComputedGetter(key)\n : createGetterInvoker(userDef.get)\n : noop;\n sharedPropertyDefinition.set = userDef.set || noop;\n }\n if (false) {\n sharedPropertyDefinition.set = function () {\n warn(\n (\"Computed property \\\"\" + key + \"\\\" was assigned to but it has no setter.\"),\n this\n );\n };\n }\n Object.defineProperty(target, key, sharedPropertyDefinition);\n}\n\nfunction createComputedGetter (key) {\n return function computedGetter () {\n var watcher = this._computedWatchers && this._computedWatchers[key];\n if (watcher) {\n if (watcher.dirty) {\n watcher.evaluate();\n }\n if (Dep.target) {\n watcher.depend();\n }\n return watcher.value\n }\n }\n}\n\nfunction createGetterInvoker(fn) {\n return function computedGetter () {\n return fn.call(this, this)\n }\n}\n\nfunction initMethods (vm, methods) {\n var props = vm.$options.props;\n for (var key in methods) {\n if (false) {\n if (typeof methods[key] !== 'function') {\n warn(\n \"Method \\\"\" + key + \"\\\" has type \\\"\" + (typeof methods[key]) + \"\\\" in the component definition. \" +\n \"Did you reference the function correctly?\",\n vm\n );\n }\n if (props && hasOwn(props, key)) {\n warn(\n (\"Method \\\"\" + key + \"\\\" has already been defined as a prop.\"),\n vm\n );\n }\n if ((key in vm) && isReserved(key)) {\n warn(\n \"Method \\\"\" + key + \"\\\" conflicts with an existing Vue instance method. \" +\n \"Avoid defining component methods that start with _ or $.\"\n );\n }\n }\n vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);\n }\n}\n\nfunction initWatch (vm, watch) {\n for (var key in watch) {\n var handler = watch[key];\n if (Array.isArray(handler)) {\n for (var i = 0; i < handler.length; i++) {\n createWatcher(vm, key, handler[i]);\n }\n } else {\n createWatcher(vm, key, handler);\n }\n }\n}\n\nfunction createWatcher (\n vm,\n expOrFn,\n handler,\n options\n) {\n if (isPlainObject(handler)) {\n options = handler;\n handler = handler.handler;\n }\n if (typeof handler === 'string') {\n handler = vm[handler];\n }\n return vm.$watch(expOrFn, handler, options)\n}\n\nfunction stateMixin (Vue) {\n // flow somehow has problems with directly declared definition object\n // when using Object.defineProperty, so we have to procedurally build up\n // the object here.\n var dataDef = {};\n dataDef.get = function () { return this._data };\n var propsDef = {};\n propsDef.get = function () { return this._props };\n if (false) {\n dataDef.set = function () {\n warn(\n 'Avoid replacing instance root $data. ' +\n 'Use nested data properties instead.',\n this\n );\n };\n propsDef.set = function () {\n warn(\"$props is readonly.\", this);\n };\n }\n Object.defineProperty(Vue.prototype, '$data', dataDef);\n Object.defineProperty(Vue.prototype, '$props', propsDef);\n\n Vue.prototype.$set = set;\n Vue.prototype.$delete = del;\n\n Vue.prototype.$watch = function (\n expOrFn,\n cb,\n options\n ) {\n var vm = this;\n if (isPlainObject(cb)) {\n return createWatcher(vm, expOrFn, cb, options)\n }\n options = options || {};\n options.user = true;\n var watcher = new Watcher(vm, expOrFn, cb, options);\n if (options.immediate) {\n try {\n cb.call(vm, watcher.value);\n } catch (error) {\n handleError(error, vm, (\"callback for immediate watcher \\\"\" + (watcher.expression) + \"\\\"\"));\n }\n }\n return function unwatchFn () {\n watcher.teardown();\n }\n };\n}\n\n/* */\n\nvar uid$3 = 0;\n\nfunction initMixin (Vue) {\n Vue.prototype._init = function (options) {\n var vm = this;\n // a uid\n vm._uid = uid$3++;\n\n var startTag, endTag;\n /* istanbul ignore if */\n if (false) {\n startTag = \"vue-perf-start:\" + (vm._uid);\n endTag = \"vue-perf-end:\" + (vm._uid);\n mark(startTag);\n }\n\n // a flag to avoid this being observed\n vm._isVue = true;\n // merge options\n if (options && options._isComponent) {\n // optimize internal component instantiation\n // since dynamic options merging is pretty slow, and none of the\n // internal component options needs special treatment.\n initInternalComponent(vm, options);\n } else {\n vm.$options = mergeOptions(\n resolveConstructorOptions(vm.constructor),\n options || {},\n vm\n );\n }\n /* istanbul ignore else */\n if (false) {\n initProxy(vm);\n } else {\n vm._renderProxy = vm;\n }\n // expose real self\n vm._self = vm;\n initLifecycle(vm);\n initEvents(vm);\n initRender(vm);\n callHook(vm, 'beforeCreate');\n initInjections(vm); // resolve injections before data/props\n initState(vm);\n initProvide(vm); // resolve provide after data/props\n callHook(vm, 'created');\n\n /* istanbul ignore if */\n if (false) {\n vm._name = formatComponentName(vm, false);\n mark(endTag);\n measure((\"vue \" + (vm._name) + \" init\"), startTag, endTag);\n }\n\n if (vm.$options.el) {\n vm.$mount(vm.$options.el);\n }\n };\n}\n\nfunction initInternalComponent (vm, options) {\n var opts = vm.$options = Object.create(vm.constructor.options);\n // doing this because it's faster than dynamic enumeration.\n var parentVnode = options._parentVnode;\n opts.parent = options.parent;\n opts._parentVnode = parentVnode;\n\n var vnodeComponentOptions = parentVnode.componentOptions;\n opts.propsData = vnodeComponentOptions.propsData;\n opts._parentListeners = vnodeComponentOptions.listeners;\n opts._renderChildren = vnodeComponentOptions.children;\n opts._componentTag = vnodeComponentOptions.tag;\n\n if (options.render) {\n opts.render = options.render;\n opts.staticRenderFns = options.staticRenderFns;\n }\n}\n\nfunction resolveConstructorOptions (Ctor) {\n var options = Ctor.options;\n if (Ctor.super) {\n var superOptions = resolveConstructorOptions(Ctor.super);\n var cachedSuperOptions = Ctor.superOptions;\n if (superOptions !== cachedSuperOptions) {\n // super option changed,\n // need to resolve new options.\n Ctor.superOptions = superOptions;\n // check if there are any late-modified/attached options (#4976)\n var modifiedOptions = resolveModifiedOptions(Ctor);\n // update base extend options\n if (modifiedOptions) {\n extend(Ctor.extendOptions, modifiedOptions);\n }\n options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions);\n if (options.name) {\n options.components[options.name] = Ctor;\n }\n }\n }\n return options\n}\n\nfunction resolveModifiedOptions (Ctor) {\n var modified;\n var latest = Ctor.options;\n var sealed = Ctor.sealedOptions;\n for (var key in latest) {\n if (latest[key] !== sealed[key]) {\n if (!modified) { modified = {}; }\n modified[key] = latest[key];\n }\n }\n return modified\n}\n\nfunction Vue (options) {\n if (false\n ) {\n warn('Vue is a constructor and should be called with the `new` keyword');\n }\n this._init(options);\n}\n\ninitMixin(Vue);\nstateMixin(Vue);\neventsMixin(Vue);\nlifecycleMixin(Vue);\nrenderMixin(Vue);\n\n/* */\n\nfunction initUse (Vue) {\n Vue.use = function (plugin) {\n var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));\n if (installedPlugins.indexOf(plugin) > -1) {\n return this\n }\n\n // additional parameters\n var args = toArray(arguments, 1);\n args.unshift(this);\n if (typeof plugin.install === 'function') {\n plugin.install.apply(plugin, args);\n } else if (typeof plugin === 'function') {\n plugin.apply(null, args);\n }\n installedPlugins.push(plugin);\n return this\n };\n}\n\n/* */\n\nfunction initMixin$1 (Vue) {\n Vue.mixin = function (mixin) {\n this.options = mergeOptions(this.options, mixin);\n return this\n };\n}\n\n/* */\n\nfunction initExtend (Vue) {\n /**\n * Each instance constructor, including Vue, has a unique\n * cid. This enables us to create wrapped \"child\n * constructors\" for prototypal inheritance and cache them.\n */\n Vue.cid = 0;\n var cid = 1;\n\n /**\n * Class inheritance\n */\n Vue.extend = function (extendOptions) {\n extendOptions = extendOptions || {};\n var Super = this;\n var SuperId = Super.cid;\n var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});\n if (cachedCtors[SuperId]) {\n return cachedCtors[SuperId]\n }\n\n var name = extendOptions.name || Super.options.name;\n if (false) {\n validateComponentName(name);\n }\n\n var Sub = function VueComponent (options) {\n this._init(options);\n };\n Sub.prototype = Object.create(Super.prototype);\n Sub.prototype.constructor = Sub;\n Sub.cid = cid++;\n Sub.options = mergeOptions(\n Super.options,\n extendOptions\n );\n Sub['super'] = Super;\n\n // For props and computed properties, we define the proxy getters on\n // the Vue instances at extension time, on the extended prototype. This\n // avoids Object.defineProperty calls for each instance created.\n if (Sub.options.props) {\n initProps$1(Sub);\n }\n if (Sub.options.computed) {\n initComputed$1(Sub);\n }\n\n // allow further extension/mixin/plugin usage\n Sub.extend = Super.extend;\n Sub.mixin = Super.mixin;\n Sub.use = Super.use;\n\n // create asset registers, so extended classes\n // can have their private assets too.\n ASSET_TYPES.forEach(function (type) {\n Sub[type] = Super[type];\n });\n // enable recursive self-lookup\n if (name) {\n Sub.options.components[name] = Sub;\n }\n\n // keep a reference to the super options at extension time.\n // later at instantiation we can check if Super's options have\n // been updated.\n Sub.superOptions = Super.options;\n Sub.extendOptions = extendOptions;\n Sub.sealedOptions = extend({}, Sub.options);\n\n // cache constructor\n cachedCtors[SuperId] = Sub;\n return Sub\n };\n}\n\nfunction initProps$1 (Comp) {\n var props = Comp.options.props;\n for (var key in props) {\n proxy(Comp.prototype, \"_props\", key);\n }\n}\n\nfunction initComputed$1 (Comp) {\n var computed = Comp.options.computed;\n for (var key in computed) {\n defineComputed(Comp.prototype, key, computed[key]);\n }\n}\n\n/* */\n\nfunction initAssetRegisters (Vue) {\n /**\n * Create asset registration methods.\n */\n ASSET_TYPES.forEach(function (type) {\n Vue[type] = function (\n id,\n definition\n ) {\n if (!definition) {\n return this.options[type + 's'][id]\n } else {\n /* istanbul ignore if */\n if (false) {\n validateComponentName(id);\n }\n if (type === 'component' && isPlainObject(definition)) {\n definition.name = definition.name || id;\n definition = this.options._base.extend(definition);\n }\n if (type === 'directive' && typeof definition === 'function') {\n definition = { bind: definition, update: definition };\n }\n this.options[type + 's'][id] = definition;\n return definition\n }\n };\n });\n}\n\n/* */\n\n\n\nfunction getComponentName (opts) {\n return opts && (opts.Ctor.options.name || opts.tag)\n}\n\nfunction matches (pattern, name) {\n if (Array.isArray(pattern)) {\n return pattern.indexOf(name) > -1\n } else if (typeof pattern === 'string') {\n return pattern.split(',').indexOf(name) > -1\n } else if (isRegExp(pattern)) {\n return pattern.test(name)\n }\n /* istanbul ignore next */\n return false\n}\n\nfunction pruneCache (keepAliveInstance, filter) {\n var cache = keepAliveInstance.cache;\n var keys = keepAliveInstance.keys;\n var _vnode = keepAliveInstance._vnode;\n for (var key in cache) {\n var cachedNode = cache[key];\n if (cachedNode) {\n var name = getComponentName(cachedNode.componentOptions);\n if (name && !filter(name)) {\n pruneCacheEntry(cache, key, keys, _vnode);\n }\n }\n }\n}\n\nfunction pruneCacheEntry (\n cache,\n key,\n keys,\n current\n) {\n var cached$$1 = cache[key];\n if (cached$$1 && (!current || cached$$1.tag !== current.tag)) {\n cached$$1.componentInstance.$destroy();\n }\n cache[key] = null;\n remove(keys, key);\n}\n\nvar patternTypes = [String, RegExp, Array];\n\nvar KeepAlive = {\n name: 'keep-alive',\n abstract: true,\n\n props: {\n include: patternTypes,\n exclude: patternTypes,\n max: [String, Number]\n },\n\n created: function created () {\n this.cache = Object.create(null);\n this.keys = [];\n },\n\n destroyed: function destroyed () {\n for (var key in this.cache) {\n pruneCacheEntry(this.cache, key, this.keys);\n }\n },\n\n mounted: function mounted () {\n var this$1 = this;\n\n this.$watch('include', function (val) {\n pruneCache(this$1, function (name) { return matches(val, name); });\n });\n this.$watch('exclude', function (val) {\n pruneCache(this$1, function (name) { return !matches(val, name); });\n });\n },\n\n render: function render () {\n var slot = this.$slots.default;\n var vnode = getFirstComponentChild(slot);\n var componentOptions = vnode && vnode.componentOptions;\n if (componentOptions) {\n // check pattern\n var name = getComponentName(componentOptions);\n var ref = this;\n var include = ref.include;\n var exclude = ref.exclude;\n if (\n // not included\n (include && (!name || !matches(include, name))) ||\n // excluded\n (exclude && name && matches(exclude, name))\n ) {\n return vnode\n }\n\n var ref$1 = this;\n var cache = ref$1.cache;\n var keys = ref$1.keys;\n var key = vnode.key == null\n // same constructor may get registered as different local components\n // so cid alone is not enough (#3269)\n ? componentOptions.Ctor.cid + (componentOptions.tag ? (\"::\" + (componentOptions.tag)) : '')\n : vnode.key;\n if (cache[key]) {\n vnode.componentInstance = cache[key].componentInstance;\n // make current key freshest\n remove(keys, key);\n keys.push(key);\n } else {\n cache[key] = vnode;\n keys.push(key);\n // prune oldest entry\n if (this.max && keys.length > parseInt(this.max)) {\n pruneCacheEntry(cache, keys[0], keys, this._vnode);\n }\n }\n\n vnode.data.keepAlive = true;\n }\n return vnode || (slot && slot[0])\n }\n};\n\nvar builtInComponents = {\n KeepAlive: KeepAlive\n};\n\n/* */\n\nfunction initGlobalAPI (Vue) {\n // config\n var configDef = {};\n configDef.get = function () { return config; };\n if (false) {\n configDef.set = function () {\n warn(\n 'Do not replace the Vue.config object, set individual fields instead.'\n );\n };\n }\n Object.defineProperty(Vue, 'config', configDef);\n\n // exposed util methods.\n // NOTE: these are not considered part of the public API - avoid relying on\n // them unless you are aware of the risk.\n Vue.util = {\n warn: warn,\n extend: extend,\n mergeOptions: mergeOptions,\n defineReactive: defineReactive$$1\n };\n\n Vue.set = set;\n Vue.delete = del;\n Vue.nextTick = nextTick;\n\n // 2.6 explicit observable API\n Vue.observable = function (obj) {\n observe(obj);\n return obj\n };\n\n Vue.options = Object.create(null);\n ASSET_TYPES.forEach(function (type) {\n Vue.options[type + 's'] = Object.create(null);\n });\n\n // this is used to identify the \"base\" constructor to extend all plain-object\n // components with in Weex's multi-instance scenarios.\n Vue.options._base = Vue;\n\n extend(Vue.options.components, builtInComponents);\n\n initUse(Vue);\n initMixin$1(Vue);\n initExtend(Vue);\n initAssetRegisters(Vue);\n}\n\ninitGlobalAPI(Vue);\n\nObject.defineProperty(Vue.prototype, '$isServer', {\n get: isServerRendering\n});\n\nObject.defineProperty(Vue.prototype, '$ssrContext', {\n get: function get () {\n /* istanbul ignore next */\n return this.$vnode && this.$vnode.ssrContext\n }\n});\n\n// expose FunctionalRenderContext for ssr runtime helper installation\nObject.defineProperty(Vue, 'FunctionalRenderContext', {\n value: FunctionalRenderContext\n});\n\nVue.version = '2.6.10';\n\n/* */\n\n// these are reserved for web because they are directly compiled away\n// during template compilation\nvar isReservedAttr = makeMap('style,class');\n\n// attributes that should be using props for binding\nvar acceptValue = makeMap('input,textarea,option,select,progress');\nvar mustUseProp = function (tag, type, attr) {\n return (\n (attr === 'value' && acceptValue(tag)) && type !== 'button' ||\n (attr === 'selected' && tag === 'option') ||\n (attr === 'checked' && tag === 'input') ||\n (attr === 'muted' && tag === 'video')\n )\n};\n\nvar isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck');\n\nvar isValidContentEditableValue = makeMap('events,caret,typing,plaintext-only');\n\nvar convertEnumeratedValue = function (key, value) {\n return isFalsyAttrValue(value) || value === 'false'\n ? 'false'\n // allow arbitrary string value for contenteditable\n : key === 'contenteditable' && isValidContentEditableValue(value)\n ? value\n : 'true'\n};\n\nvar isBooleanAttr = makeMap(\n 'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +\n 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' +\n 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' +\n 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' +\n 'required,reversed,scoped,seamless,selected,sortable,translate,' +\n 'truespeed,typemustmatch,visible'\n);\n\nvar xlinkNS = 'http://www.w3.org/1999/xlink';\n\nvar isXlink = function (name) {\n return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'\n};\n\nvar getXlinkProp = function (name) {\n return isXlink(name) ? name.slice(6, name.length) : ''\n};\n\nvar isFalsyAttrValue = function (val) {\n return val == null || val === false\n};\n\n/* */\n\nfunction genClassForVnode (vnode) {\n var data = vnode.data;\n var parentNode = vnode;\n var childNode = vnode;\n while (isDef(childNode.componentInstance)) {\n childNode = childNode.componentInstance._vnode;\n if (childNode && childNode.data) {\n data = mergeClassData(childNode.data, data);\n }\n }\n while (isDef(parentNode = parentNode.parent)) {\n if (parentNode && parentNode.data) {\n data = mergeClassData(data, parentNode.data);\n }\n }\n return renderClass(data.staticClass, data.class)\n}\n\nfunction mergeClassData (child, parent) {\n return {\n staticClass: concat(child.staticClass, parent.staticClass),\n class: isDef(child.class)\n ? [child.class, parent.class]\n : parent.class\n }\n}\n\nfunction renderClass (\n staticClass,\n dynamicClass\n) {\n if (isDef(staticClass) || isDef(dynamicClass)) {\n return concat(staticClass, stringifyClass(dynamicClass))\n }\n /* istanbul ignore next */\n return ''\n}\n\nfunction concat (a, b) {\n return a ? b ? (a + ' ' + b) : a : (b || '')\n}\n\nfunction stringifyClass (value) {\n if (Array.isArray(value)) {\n return stringifyArray(value)\n }\n if (isObject(value)) {\n return stringifyObject(value)\n }\n if (typeof value === 'string') {\n return value\n }\n /* istanbul ignore next */\n return ''\n}\n\nfunction stringifyArray (value) {\n var res = '';\n var stringified;\n for (var i = 0, l = value.length; i < l; i++) {\n if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {\n if (res) { res += ' '; }\n res += stringified;\n }\n }\n return res\n}\n\nfunction stringifyObject (value) {\n var res = '';\n for (var key in value) {\n if (value[key]) {\n if (res) { res += ' '; }\n res += key;\n }\n }\n return res\n}\n\n/* */\n\nvar namespaceMap = {\n svg: 'http://www.w3.org/2000/svg',\n math: 'http://www.w3.org/1998/Math/MathML'\n};\n\nvar isHTMLTag = makeMap(\n 'html,body,base,head,link,meta,style,title,' +\n 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +\n 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +\n 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +\n 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +\n 'embed,object,param,source,canvas,script,noscript,del,ins,' +\n 'caption,col,colgroup,table,thead,tbody,td,th,tr,' +\n 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +\n 'output,progress,select,textarea,' +\n 'details,dialog,menu,menuitem,summary,' +\n 'content,element,shadow,template,blockquote,iframe,tfoot'\n);\n\n// this map is intentionally selective, only covering SVG elements that may\n// contain child elements.\nvar isSVG = makeMap(\n 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +\n 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +\n 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',\n true\n);\n\nvar isReservedTag = function (tag) {\n return isHTMLTag(tag) || isSVG(tag)\n};\n\nfunction getTagNamespace (tag) {\n if (isSVG(tag)) {\n return 'svg'\n }\n // basic support for MathML\n // note it doesn't support other MathML elements being component roots\n if (tag === 'math') {\n return 'math'\n }\n}\n\nvar unknownElementCache = Object.create(null);\nfunction isUnknownElement (tag) {\n /* istanbul ignore if */\n if (!inBrowser) {\n return true\n }\n if (isReservedTag(tag)) {\n return false\n }\n tag = tag.toLowerCase();\n /* istanbul ignore if */\n if (unknownElementCache[tag] != null) {\n return unknownElementCache[tag]\n }\n var el = document.createElement(tag);\n if (tag.indexOf('-') > -1) {\n // http://stackoverflow.com/a/28210364/1070244\n return (unknownElementCache[tag] = (\n el.constructor === window.HTMLUnknownElement ||\n el.constructor === window.HTMLElement\n ))\n } else {\n return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString()))\n }\n}\n\nvar isTextInputType = makeMap('text,number,password,search,email,tel,url');\n\n/* */\n\n/**\n * Query an element selector if it's not an element already.\n */\nfunction query (el) {\n if (typeof el === 'string') {\n var selected = document.querySelector(el);\n if (!selected) {\n \"production\" !== 'production' && warn(\n 'Cannot find element: ' + el\n );\n return document.createElement('div')\n }\n return selected\n } else {\n return el\n }\n}\n\n/* */\n\nfunction createElement$1 (tagName, vnode) {\n var elm = document.createElement(tagName);\n if (tagName !== 'select') {\n return elm\n }\n // false or null will remove the attribute but undefined will not\n if (vnode.data && vnode.data.attrs && vnode.data.attrs.multiple !== undefined) {\n elm.setAttribute('multiple', 'multiple');\n }\n return elm\n}\n\nfunction createElementNS (namespace, tagName) {\n return document.createElementNS(namespaceMap[namespace], tagName)\n}\n\nfunction createTextNode (text) {\n return document.createTextNode(text)\n}\n\nfunction createComment (text) {\n return document.createComment(text)\n}\n\nfunction insertBefore (parentNode, newNode, referenceNode) {\n parentNode.insertBefore(newNode, referenceNode);\n}\n\nfunction removeChild (node, child) {\n node.removeChild(child);\n}\n\nfunction appendChild (node, child) {\n node.appendChild(child);\n}\n\nfunction parentNode (node) {\n return node.parentNode\n}\n\nfunction nextSibling (node) {\n return node.nextSibling\n}\n\nfunction tagName (node) {\n return node.tagName\n}\n\nfunction setTextContent (node, text) {\n node.textContent = text;\n}\n\nfunction setStyleScope (node, scopeId) {\n node.setAttribute(scopeId, '');\n}\n\nvar nodeOps = /*#__PURE__*/Object.freeze({\n createElement: createElement$1,\n createElementNS: createElementNS,\n createTextNode: createTextNode,\n createComment: createComment,\n insertBefore: insertBefore,\n removeChild: removeChild,\n appendChild: appendChild,\n parentNode: parentNode,\n nextSibling: nextSibling,\n tagName: tagName,\n setTextContent: setTextContent,\n setStyleScope: setStyleScope\n});\n\n/* */\n\nvar ref = {\n create: function create (_, vnode) {\n registerRef(vnode);\n },\n update: function update (oldVnode, vnode) {\n if (oldVnode.data.ref !== vnode.data.ref) {\n registerRef(oldVnode, true);\n registerRef(vnode);\n }\n },\n destroy: function destroy (vnode) {\n registerRef(vnode, true);\n }\n};\n\nfunction registerRef (vnode, isRemoval) {\n var key = vnode.data.ref;\n if (!isDef(key)) { return }\n\n var vm = vnode.context;\n var ref = vnode.componentInstance || vnode.elm;\n var refs = vm.$refs;\n if (isRemoval) {\n if (Array.isArray(refs[key])) {\n remove(refs[key], ref);\n } else if (refs[key] === ref) {\n refs[key] = undefined;\n }\n } else {\n if (vnode.data.refInFor) {\n if (!Array.isArray(refs[key])) {\n refs[key] = [ref];\n } else if (refs[key].indexOf(ref) < 0) {\n // $flow-disable-line\n refs[key].push(ref);\n }\n } else {\n refs[key] = ref;\n }\n }\n}\n\n/**\n * Virtual DOM patching algorithm based on Snabbdom by\n * Simon Friis Vindum (@paldepind)\n * Licensed under the MIT License\n * https://github.com/paldepind/snabbdom/blob/master/LICENSE\n *\n * modified by Evan You (@yyx990803)\n *\n * Not type-checking this because this file is perf-critical and the cost\n * of making flow understand it is not worth it.\n */\n\nvar emptyNode = new VNode('', {}, []);\n\nvar hooks = ['create', 'activate', 'update', 'remove', 'destroy'];\n\nfunction sameVnode (a, b) {\n return (\n a.key === b.key && (\n (\n a.tag === b.tag &&\n a.isComment === b.isComment &&\n isDef(a.data) === isDef(b.data) &&\n sameInputType(a, b)\n ) || (\n isTrue(a.isAsyncPlaceholder) &&\n a.asyncFactory === b.asyncFactory &&\n isUndef(b.asyncFactory.error)\n )\n )\n )\n}\n\nfunction sameInputType (a, b) {\n if (a.tag !== 'input') { return true }\n var i;\n var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type;\n var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type;\n return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)\n}\n\nfunction createKeyToOldIdx (children, beginIdx, endIdx) {\n var i, key;\n var map = {};\n for (i = beginIdx; i <= endIdx; ++i) {\n key = children[i].key;\n if (isDef(key)) { map[key] = i; }\n }\n return map\n}\n\nfunction createPatchFunction (backend) {\n var i, j;\n var cbs = {};\n\n var modules = backend.modules;\n var nodeOps = backend.nodeOps;\n\n for (i = 0; i < hooks.length; ++i) {\n cbs[hooks[i]] = [];\n for (j = 0; j < modules.length; ++j) {\n if (isDef(modules[j][hooks[i]])) {\n cbs[hooks[i]].push(modules[j][hooks[i]]);\n }\n }\n }\n\n function emptyNodeAt (elm) {\n return new VNode(nodeOps.tagName(elm).toLowerCase(), {}, [], undefined, elm)\n }\n\n function createRmCb (childElm, listeners) {\n function remove$$1 () {\n if (--remove$$1.listeners === 0) {\n removeNode(childElm);\n }\n }\n remove$$1.listeners = listeners;\n return remove$$1\n }\n\n function removeNode (el) {\n var parent = nodeOps.parentNode(el);\n // element may have already been removed due to v-html / v-text\n if (isDef(parent)) {\n nodeOps.removeChild(parent, el);\n }\n }\n\n function isUnknownElement$$1 (vnode, inVPre) {\n return (\n !inVPre &&\n !vnode.ns &&\n !(\n config.ignoredElements.length &&\n config.ignoredElements.some(function (ignore) {\n return isRegExp(ignore)\n ? ignore.test(vnode.tag)\n : ignore === vnode.tag\n })\n ) &&\n config.isUnknownElement(vnode.tag)\n )\n }\n\n var creatingElmInVPre = 0;\n\n function createElm (\n vnode,\n insertedVnodeQueue,\n parentElm,\n refElm,\n nested,\n ownerArray,\n index\n ) {\n if (isDef(vnode.elm) && isDef(ownerArray)) {\n // This vnode was used in a previous render!\n // now it's used as a new node, overwriting its elm would cause\n // potential patch errors down the road when it's used as an insertion\n // reference node. Instead, we clone the node on-demand before creating\n // associated DOM element for it.\n vnode = ownerArray[index] = cloneVNode(vnode);\n }\n\n vnode.isRootInsert = !nested; // for transition enter check\n if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {\n return\n }\n\n var data = vnode.data;\n var children = vnode.children;\n var tag = vnode.tag;\n if (isDef(tag)) {\n if (false) {\n if (data && data.pre) {\n creatingElmInVPre++;\n }\n if (isUnknownElement$$1(vnode, creatingElmInVPre)) {\n warn(\n 'Unknown custom element: <' + tag + '> - did you ' +\n 'register the component correctly? For recursive components, ' +\n 'make sure to provide the \"name\" option.',\n vnode.context\n );\n }\n }\n\n vnode.elm = vnode.ns\n ? nodeOps.createElementNS(vnode.ns, tag)\n : nodeOps.createElement(tag, vnode);\n setScope(vnode);\n\n /* istanbul ignore if */\n {\n createChildren(vnode, children, insertedVnodeQueue);\n if (isDef(data)) {\n invokeCreateHooks(vnode, insertedVnodeQueue);\n }\n insert(parentElm, vnode.elm, refElm);\n }\n\n if (false) {\n creatingElmInVPre--;\n }\n } else if (isTrue(vnode.isComment)) {\n vnode.elm = nodeOps.createComment(vnode.text);\n insert(parentElm, vnode.elm, refElm);\n } else {\n vnode.elm = nodeOps.createTextNode(vnode.text);\n insert(parentElm, vnode.elm, refElm);\n }\n }\n\n function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {\n var i = vnode.data;\n if (isDef(i)) {\n var isReactivated = isDef(vnode.componentInstance) && i.keepAlive;\n if (isDef(i = i.hook) && isDef(i = i.init)) {\n i(vnode, false /* hydrating */);\n }\n // after calling the init hook, if the vnode is a child component\n // it should've created a child instance and mounted it. the child\n // component also has set the placeholder vnode's elm.\n // in that case we can just return the element and be done.\n if (isDef(vnode.componentInstance)) {\n initComponent(vnode, insertedVnodeQueue);\n insert(parentElm, vnode.elm, refElm);\n if (isTrue(isReactivated)) {\n reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);\n }\n return true\n }\n }\n }\n\n function initComponent (vnode, insertedVnodeQueue) {\n if (isDef(vnode.data.pendingInsert)) {\n insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert);\n vnode.data.pendingInsert = null;\n }\n vnode.elm = vnode.componentInstance.$el;\n if (isPatchable(vnode)) {\n invokeCreateHooks(vnode, insertedVnodeQueue);\n setScope(vnode);\n } else {\n // empty component root.\n // skip all element-related modules except for ref (#3455)\n registerRef(vnode);\n // make sure to invoke the insert hook\n insertedVnodeQueue.push(vnode);\n }\n }\n\n function reactivateComponent (vnode, insertedVnodeQueue, parentElm, refElm) {\n var i;\n // hack for #4339: a reactivated component with inner transition\n // does not trigger because the inner node's created hooks are not called\n // again. It's not ideal to involve module-specific logic in here but\n // there doesn't seem to be a better way to do it.\n var innerNode = vnode;\n while (innerNode.componentInstance) {\n innerNode = innerNode.componentInstance._vnode;\n if (isDef(i = innerNode.data) && isDef(i = i.transition)) {\n for (i = 0; i < cbs.activate.length; ++i) {\n cbs.activate[i](emptyNode, innerNode);\n }\n insertedVnodeQueue.push(innerNode);\n break\n }\n }\n // unlike a newly created component,\n // a reactivated keep-alive component doesn't insert itself\n insert(parentElm, vnode.elm, refElm);\n }\n\n function insert (parent, elm, ref$$1) {\n if (isDef(parent)) {\n if (isDef(ref$$1)) {\n if (nodeOps.parentNode(ref$$1) === parent) {\n nodeOps.insertBefore(parent, elm, ref$$1);\n }\n } else {\n nodeOps.appendChild(parent, elm);\n }\n }\n }\n\n function createChildren (vnode, children, insertedVnodeQueue) {\n if (Array.isArray(children)) {\n if (false) {\n checkDuplicateKeys(children);\n }\n for (var i = 0; i < children.length; ++i) {\n createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i);\n }\n } else if (isPrimitive(vnode.text)) {\n nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)));\n }\n }\n\n function isPatchable (vnode) {\n while (vnode.componentInstance) {\n vnode = vnode.componentInstance._vnode;\n }\n return isDef(vnode.tag)\n }\n\n function invokeCreateHooks (vnode, insertedVnodeQueue) {\n for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {\n cbs.create[i$1](emptyNode, vnode);\n }\n i = vnode.data.hook; // Reuse variable\n if (isDef(i)) {\n if (isDef(i.create)) { i.create(emptyNode, vnode); }\n if (isDef(i.insert)) { insertedVnodeQueue.push(vnode); }\n }\n }\n\n // set scope id attribute for scoped CSS.\n // this is implemented as a special case to avoid the overhead\n // of going through the normal attribute patching process.\n function setScope (vnode) {\n var i;\n if (isDef(i = vnode.fnScopeId)) {\n nodeOps.setStyleScope(vnode.elm, i);\n } else {\n var ancestor = vnode;\n while (ancestor) {\n if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) {\n nodeOps.setStyleScope(vnode.elm, i);\n }\n ancestor = ancestor.parent;\n }\n }\n // for slot content they should also get the scopeId from the host instance.\n if (isDef(i = activeInstance) &&\n i !== vnode.context &&\n i !== vnode.fnContext &&\n isDef(i = i.$options._scopeId)\n ) {\n nodeOps.setStyleScope(vnode.elm, i);\n }\n }\n\n function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) {\n for (; startIdx <= endIdx; ++startIdx) {\n createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm, false, vnodes, startIdx);\n }\n }\n\n function invokeDestroyHook (vnode) {\n var i, j;\n var data = vnode.data;\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.destroy)) { i(vnode); }\n for (i = 0; i < cbs.destroy.length; ++i) { cbs.destroy[i](vnode); }\n }\n if (isDef(i = vnode.children)) {\n for (j = 0; j < vnode.children.length; ++j) {\n invokeDestroyHook(vnode.children[j]);\n }\n }\n }\n\n function removeVnodes (parentElm, vnodes, startIdx, endIdx) {\n for (; startIdx <= endIdx; ++startIdx) {\n var ch = vnodes[startIdx];\n if (isDef(ch)) {\n if (isDef(ch.tag)) {\n removeAndInvokeRemoveHook(ch);\n invokeDestroyHook(ch);\n } else { // Text node\n removeNode(ch.elm);\n }\n }\n }\n }\n\n function removeAndInvokeRemoveHook (vnode, rm) {\n if (isDef(rm) || isDef(vnode.data)) {\n var i;\n var listeners = cbs.remove.length + 1;\n if (isDef(rm)) {\n // we have a recursively passed down rm callback\n // increase the listeners count\n rm.listeners += listeners;\n } else {\n // directly removing\n rm = createRmCb(vnode.elm, listeners);\n }\n // recursively invoke hooks on child component root node\n if (isDef(i = vnode.componentInstance) && isDef(i = i._vnode) && isDef(i.data)) {\n removeAndInvokeRemoveHook(i, rm);\n }\n for (i = 0; i < cbs.remove.length; ++i) {\n cbs.remove[i](vnode, rm);\n }\n if (isDef(i = vnode.data.hook) && isDef(i = i.remove)) {\n i(vnode, rm);\n } else {\n rm();\n }\n } else {\n removeNode(vnode.elm);\n }\n }\n\n function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {\n var oldStartIdx = 0;\n var newStartIdx = 0;\n var oldEndIdx = oldCh.length - 1;\n var oldStartVnode = oldCh[0];\n var oldEndVnode = oldCh[oldEndIdx];\n var newEndIdx = newCh.length - 1;\n var newStartVnode = newCh[0];\n var newEndVnode = newCh[newEndIdx];\n var oldKeyToIdx, idxInOld, vnodeToMove, refElm;\n\n // removeOnly is a special flag used only by <transition-group>\n // to ensure removed elements stay in correct relative positions\n // during leaving transitions\n var canMove = !removeOnly;\n\n if (false) {\n checkDuplicateKeys(newCh);\n }\n\n while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {\n if (isUndef(oldStartVnode)) {\n oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left\n } else if (isUndef(oldEndVnode)) {\n oldEndVnode = oldCh[--oldEndIdx];\n } else if (sameVnode(oldStartVnode, newStartVnode)) {\n patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n oldStartVnode = oldCh[++oldStartIdx];\n newStartVnode = newCh[++newStartIdx];\n } else if (sameVnode(oldEndVnode, newEndVnode)) {\n patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);\n oldEndVnode = oldCh[--oldEndIdx];\n newEndVnode = newCh[--newEndIdx];\n } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right\n patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);\n canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));\n oldStartVnode = oldCh[++oldStartIdx];\n newEndVnode = newCh[--newEndIdx];\n } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left\n patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);\n oldEndVnode = oldCh[--oldEndIdx];\n newStartVnode = newCh[++newStartIdx];\n } else {\n if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }\n idxInOld = isDef(newStartVnode.key)\n ? oldKeyToIdx[newStartVnode.key]\n : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);\n if (isUndef(idxInOld)) { // New element\n createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);\n } else {\n vnodeToMove = oldCh[idxInOld];\n if (sameVnode(vnodeToMove, newStartVnode)) {\n patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n oldCh[idxInOld] = undefined;\n canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);\n } else {\n // same key but different element. treat as new element\n createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);\n }\n }\n newStartVnode = newCh[++newStartIdx];\n }\n }\n if (oldStartIdx > oldEndIdx) {\n refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;\n addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);\n } else if (newStartIdx > newEndIdx) {\n removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);\n }\n }\n\n function checkDuplicateKeys (children) {\n var seenKeys = {};\n for (var i = 0; i < children.length; i++) {\n var vnode = children[i];\n var key = vnode.key;\n if (isDef(key)) {\n if (seenKeys[key]) {\n warn(\n (\"Duplicate keys detected: '\" + key + \"'. This may cause an update error.\"),\n vnode.context\n );\n } else {\n seenKeys[key] = true;\n }\n }\n }\n }\n\n function findIdxInOld (node, oldCh, start, end) {\n for (var i = start; i < end; i++) {\n var c = oldCh[i];\n if (isDef(c) && sameVnode(node, c)) { return i }\n }\n }\n\n function patchVnode (\n oldVnode,\n vnode,\n insertedVnodeQueue,\n ownerArray,\n index,\n removeOnly\n ) {\n if (oldVnode === vnode) {\n return\n }\n\n if (isDef(vnode.elm) && isDef(ownerArray)) {\n // clone reused vnode\n vnode = ownerArray[index] = cloneVNode(vnode);\n }\n\n var elm = vnode.elm = oldVnode.elm;\n\n if (isTrue(oldVnode.isAsyncPlaceholder)) {\n if (isDef(vnode.asyncFactory.resolved)) {\n hydrate(oldVnode.elm, vnode, insertedVnodeQueue);\n } else {\n vnode.isAsyncPlaceholder = true;\n }\n return\n }\n\n // reuse element for static trees.\n // note we only do this if the vnode is cloned -\n // if the new node is not cloned it means the render functions have been\n // reset by the hot-reload-api and we need to do a proper re-render.\n if (isTrue(vnode.isStatic) &&\n isTrue(oldVnode.isStatic) &&\n vnode.key === oldVnode.key &&\n (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))\n ) {\n vnode.componentInstance = oldVnode.componentInstance;\n return\n }\n\n var i;\n var data = vnode.data;\n if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {\n i(oldVnode, vnode);\n }\n\n var oldCh = oldVnode.children;\n var ch = vnode.children;\n if (isDef(data) && isPatchable(vnode)) {\n for (i = 0; i < cbs.update.length; ++i) { cbs.update[i](oldVnode, vnode); }\n if (isDef(i = data.hook) && isDef(i = i.update)) { i(oldVnode, vnode); }\n }\n if (isUndef(vnode.text)) {\n if (isDef(oldCh) && isDef(ch)) {\n if (oldCh !== ch) { updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly); }\n } else if (isDef(ch)) {\n if (false) {\n checkDuplicateKeys(ch);\n }\n if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }\n addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);\n } else if (isDef(oldCh)) {\n removeVnodes(elm, oldCh, 0, oldCh.length - 1);\n } else if (isDef(oldVnode.text)) {\n nodeOps.setTextContent(elm, '');\n }\n } else if (oldVnode.text !== vnode.text) {\n nodeOps.setTextContent(elm, vnode.text);\n }\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.postpatch)) { i(oldVnode, vnode); }\n }\n }\n\n function invokeInsertHook (vnode, queue, initial) {\n // delay insert hooks for component root nodes, invoke them after the\n // element is really inserted\n if (isTrue(initial) && isDef(vnode.parent)) {\n vnode.parent.data.pendingInsert = queue;\n } else {\n for (var i = 0; i < queue.length; ++i) {\n queue[i].data.hook.insert(queue[i]);\n }\n }\n }\n\n var hydrationBailed = false;\n // list of modules that can skip create hook during hydration because they\n // are already rendered on the client or has no need for initialization\n // Note: style is excluded because it relies on initial clone for future\n // deep updates (#7063).\n var isRenderedModule = makeMap('attrs,class,staticClass,staticStyle,key');\n\n // Note: this is a browser-only function so we can assume elms are DOM nodes.\n function hydrate (elm, vnode, insertedVnodeQueue, inVPre) {\n var i;\n var tag = vnode.tag;\n var data = vnode.data;\n var children = vnode.children;\n inVPre = inVPre || (data && data.pre);\n vnode.elm = elm;\n\n if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {\n vnode.isAsyncPlaceholder = true;\n return true\n }\n // assert node match\n if (false) {\n if (!assertNodeMatch(elm, vnode, inVPre)) {\n return false\n }\n }\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.init)) { i(vnode, true /* hydrating */); }\n if (isDef(i = vnode.componentInstance)) {\n // child component. it should have hydrated its own tree.\n initComponent(vnode, insertedVnodeQueue);\n return true\n }\n }\n if (isDef(tag)) {\n if (isDef(children)) {\n // empty element, allow client to pick up and populate children\n if (!elm.hasChildNodes()) {\n createChildren(vnode, children, insertedVnodeQueue);\n } else {\n // v-html and domProps: innerHTML\n if (isDef(i = data) && isDef(i = i.domProps) && isDef(i = i.innerHTML)) {\n if (i !== elm.innerHTML) {\n /* istanbul ignore if */\n if (false\n ) {\n hydrationBailed = true;\n console.warn('Parent: ', elm);\n console.warn('server innerHTML: ', i);\n console.warn('client innerHTML: ', elm.innerHTML);\n }\n return false\n }\n } else {\n // iterate and compare children lists\n var childrenMatch = true;\n var childNode = elm.firstChild;\n for (var i$1 = 0; i$1 < children.length; i$1++) {\n if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue, inVPre)) {\n childrenMatch = false;\n break\n }\n childNode = childNode.nextSibling;\n }\n // if childNode is not null, it means the actual childNodes list is\n // longer than the virtual children list.\n if (!childrenMatch || childNode) {\n /* istanbul ignore if */\n if (false\n ) {\n hydrationBailed = true;\n console.warn('Parent: ', elm);\n console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children);\n }\n return false\n }\n }\n }\n }\n if (isDef(data)) {\n var fullInvoke = false;\n for (var key in data) {\n if (!isRenderedModule(key)) {\n fullInvoke = true;\n invokeCreateHooks(vnode, insertedVnodeQueue);\n break\n }\n }\n if (!fullInvoke && data['class']) {\n // ensure collecting deps for deep class bindings for future updates\n traverse(data['class']);\n }\n }\n } else if (elm.data !== vnode.text) {\n elm.data = vnode.text;\n }\n return true\n }\n\n function assertNodeMatch (node, vnode, inVPre) {\n if (isDef(vnode.tag)) {\n return vnode.tag.indexOf('vue-component') === 0 || (\n !isUnknownElement$$1(vnode, inVPre) &&\n vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase())\n )\n } else {\n return node.nodeType === (vnode.isComment ? 8 : 3)\n }\n }\n\n return function patch (oldVnode, vnode, hydrating, removeOnly) {\n if (isUndef(vnode)) {\n if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); }\n return\n }\n\n var isInitialPatch = false;\n var insertedVnodeQueue = [];\n\n if (isUndef(oldVnode)) {\n // empty mount (likely as component), create new root element\n isInitialPatch = true;\n createElm(vnode, insertedVnodeQueue);\n } else {\n var isRealElement = isDef(oldVnode.nodeType);\n if (!isRealElement && sameVnode(oldVnode, vnode)) {\n // patch existing root node\n patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);\n } else {\n if (isRealElement) {\n // mounting to a real element\n // check if this is server-rendered content and if we can perform\n // a successful hydration.\n if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {\n oldVnode.removeAttribute(SSR_ATTR);\n hydrating = true;\n }\n if (isTrue(hydrating)) {\n if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {\n invokeInsertHook(vnode, insertedVnodeQueue, true);\n return oldVnode\n } else if (false) {\n warn(\n 'The client-side rendered virtual DOM tree is not matching ' +\n 'server-rendered content. This is likely caused by incorrect ' +\n 'HTML markup, for example nesting block-level elements inside ' +\n '<p>, or missing <tbody>. Bailing hydration and performing ' +\n 'full client-side render.'\n );\n }\n }\n // either not server-rendered, or hydration failed.\n // create an empty node and replace it\n oldVnode = emptyNodeAt(oldVnode);\n }\n\n // replacing existing element\n var oldElm = oldVnode.elm;\n var parentElm = nodeOps.parentNode(oldElm);\n\n // create new node\n createElm(\n vnode,\n insertedVnodeQueue,\n // extremely rare edge case: do not insert if old element is in a\n // leaving transition. Only happens when combining transition +\n // keep-alive + HOCs. (#4590)\n oldElm._leaveCb ? null : parentElm,\n nodeOps.nextSibling(oldElm)\n );\n\n // update parent placeholder node element, recursively\n if (isDef(vnode.parent)) {\n var ancestor = vnode.parent;\n var patchable = isPatchable(vnode);\n while (ancestor) {\n for (var i = 0; i < cbs.destroy.length; ++i) {\n cbs.destroy[i](ancestor);\n }\n ancestor.elm = vnode.elm;\n if (patchable) {\n for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {\n cbs.create[i$1](emptyNode, ancestor);\n }\n // #6513\n // invoke insert hooks that may have been merged by create hooks.\n // e.g. for directives that uses the \"inserted\" hook.\n var insert = ancestor.data.hook.insert;\n if (insert.merged) {\n // start at index 1 to avoid re-invoking component mounted hook\n for (var i$2 = 1; i$2 < insert.fns.length; i$2++) {\n insert.fns[i$2]();\n }\n }\n } else {\n registerRef(ancestor);\n }\n ancestor = ancestor.parent;\n }\n }\n\n // destroy old node\n if (isDef(parentElm)) {\n removeVnodes(parentElm, [oldVnode], 0, 0);\n } else if (isDef(oldVnode.tag)) {\n invokeDestroyHook(oldVnode);\n }\n }\n }\n\n invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);\n return vnode.elm\n }\n}\n\n/* */\n\nvar directives = {\n create: updateDirectives,\n update: updateDirectives,\n destroy: function unbindDirectives (vnode) {\n updateDirectives(vnode, emptyNode);\n }\n};\n\nfunction updateDirectives (oldVnode, vnode) {\n if (oldVnode.data.directives || vnode.data.directives) {\n _update(oldVnode, vnode);\n }\n}\n\nfunction _update (oldVnode, vnode) {\n var isCreate = oldVnode === emptyNode;\n var isDestroy = vnode === emptyNode;\n var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context);\n var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context);\n\n var dirsWithInsert = [];\n var dirsWithPostpatch = [];\n\n var key, oldDir, dir;\n for (key in newDirs) {\n oldDir = oldDirs[key];\n dir = newDirs[key];\n if (!oldDir) {\n // new directive, bind\n callHook$1(dir, 'bind', vnode, oldVnode);\n if (dir.def && dir.def.inserted) {\n dirsWithInsert.push(dir);\n }\n } else {\n // existing directive, update\n dir.oldValue = oldDir.value;\n dir.oldArg = oldDir.arg;\n callHook$1(dir, 'update', vnode, oldVnode);\n if (dir.def && dir.def.componentUpdated) {\n dirsWithPostpatch.push(dir);\n }\n }\n }\n\n if (dirsWithInsert.length) {\n var callInsert = function () {\n for (var i = 0; i < dirsWithInsert.length; i++) {\n callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode);\n }\n };\n if (isCreate) {\n mergeVNodeHook(vnode, 'insert', callInsert);\n } else {\n callInsert();\n }\n }\n\n if (dirsWithPostpatch.length) {\n mergeVNodeHook(vnode, 'postpatch', function () {\n for (var i = 0; i < dirsWithPostpatch.length; i++) {\n callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode);\n }\n });\n }\n\n if (!isCreate) {\n for (key in oldDirs) {\n if (!newDirs[key]) {\n // no longer present, unbind\n callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy);\n }\n }\n }\n}\n\nvar emptyModifiers = Object.create(null);\n\nfunction normalizeDirectives$1 (\n dirs,\n vm\n) {\n var res = Object.create(null);\n if (!dirs) {\n // $flow-disable-line\n return res\n }\n var i, dir;\n for (i = 0; i < dirs.length; i++) {\n dir = dirs[i];\n if (!dir.modifiers) {\n // $flow-disable-line\n dir.modifiers = emptyModifiers;\n }\n res[getRawDirName(dir)] = dir;\n dir.def = resolveAsset(vm.$options, 'directives', dir.name, true);\n }\n // $flow-disable-line\n return res\n}\n\nfunction getRawDirName (dir) {\n return dir.rawName || ((dir.name) + \".\" + (Object.keys(dir.modifiers || {}).join('.')))\n}\n\nfunction callHook$1 (dir, hook, vnode, oldVnode, isDestroy) {\n var fn = dir.def && dir.def[hook];\n if (fn) {\n try {\n fn(vnode.elm, dir, vnode, oldVnode, isDestroy);\n } catch (e) {\n handleError(e, vnode.context, (\"directive \" + (dir.name) + \" \" + hook + \" hook\"));\n }\n }\n}\n\nvar baseModules = [\n ref,\n directives\n];\n\n/* */\n\nfunction updateAttrs (oldVnode, vnode) {\n var opts = vnode.componentOptions;\n if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {\n return\n }\n if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {\n return\n }\n var key, cur, old;\n var elm = vnode.elm;\n var oldAttrs = oldVnode.data.attrs || {};\n var attrs = vnode.data.attrs || {};\n // clone observed objects, as the user probably wants to mutate it\n if (isDef(attrs.__ob__)) {\n attrs = vnode.data.attrs = extend({}, attrs);\n }\n\n for (key in attrs) {\n cur = attrs[key];\n old = oldAttrs[key];\n if (old !== cur) {\n setAttr(elm, key, cur);\n }\n }\n // #4391: in IE9, setting type can reset value for input[type=radio]\n // #6666: IE/Edge forces progress value down to 1 before setting a max\n /* istanbul ignore if */\n if ((isIE || isEdge) && attrs.value !== oldAttrs.value) {\n setAttr(elm, 'value', attrs.value);\n }\n for (key in oldAttrs) {\n if (isUndef(attrs[key])) {\n if (isXlink(key)) {\n elm.removeAttributeNS(xlinkNS, getXlinkProp(key));\n } else if (!isEnumeratedAttr(key)) {\n elm.removeAttribute(key);\n }\n }\n }\n}\n\nfunction setAttr (el, key, value) {\n if (el.tagName.indexOf('-') > -1) {\n baseSetAttr(el, key, value);\n } else if (isBooleanAttr(key)) {\n // set attribute for blank value\n // e.g. <option disabled>Select one</option>\n if (isFalsyAttrValue(value)) {\n el.removeAttribute(key);\n } else {\n // technically allowfullscreen is a boolean attribute for <iframe>,\n // but Flash expects a value of \"true\" when used on <embed> tag\n value = key === 'allowfullscreen' && el.tagName === 'EMBED'\n ? 'true'\n : key;\n el.setAttribute(key, value);\n }\n } else if (isEnumeratedAttr(key)) {\n el.setAttribute(key, convertEnumeratedValue(key, value));\n } else if (isXlink(key)) {\n if (isFalsyAttrValue(value)) {\n el.removeAttributeNS(xlinkNS, getXlinkProp(key));\n } else {\n el.setAttributeNS(xlinkNS, key, value);\n }\n } else {\n baseSetAttr(el, key, value);\n }\n}\n\nfunction baseSetAttr (el, key, value) {\n if (isFalsyAttrValue(value)) {\n el.removeAttribute(key);\n } else {\n // #7138: IE10 & 11 fires input event when setting placeholder on\n // <textarea>... block the first input event and remove the blocker\n // immediately.\n /* istanbul ignore if */\n if (\n isIE && !isIE9 &&\n el.tagName === 'TEXTAREA' &&\n key === 'placeholder' && value !== '' && !el.__ieph\n ) {\n var blocker = function (e) {\n e.stopImmediatePropagation();\n el.removeEventListener('input', blocker);\n };\n el.addEventListener('input', blocker);\n // $flow-disable-line\n el.__ieph = true; /* IE placeholder patched */\n }\n el.setAttribute(key, value);\n }\n}\n\nvar attrs = {\n create: updateAttrs,\n update: updateAttrs\n};\n\n/* */\n\nfunction updateClass (oldVnode, vnode) {\n var el = vnode.elm;\n var data = vnode.data;\n var oldData = oldVnode.data;\n if (\n isUndef(data.staticClass) &&\n isUndef(data.class) && (\n isUndef(oldData) || (\n isUndef(oldData.staticClass) &&\n isUndef(oldData.class)\n )\n )\n ) {\n return\n }\n\n var cls = genClassForVnode(vnode);\n\n // handle transition classes\n var transitionClass = el._transitionClasses;\n if (isDef(transitionClass)) {\n cls = concat(cls, stringifyClass(transitionClass));\n }\n\n // set the class\n if (cls !== el._prevClass) {\n el.setAttribute('class', cls);\n el._prevClass = cls;\n }\n}\n\nvar klass = {\n create: updateClass,\n update: updateClass\n};\n\n/* */\n\n/* */\n\n/* */\n\n/* */\n\n// in some cases, the event used has to be determined at runtime\n// so we used some reserved tokens during compile.\nvar RANGE_TOKEN = '__r';\nvar CHECKBOX_RADIO_TOKEN = '__c';\n\n/* */\n\n// normalize v-model event tokens that can only be determined at runtime.\n// it's important to place the event as the first in the array because\n// the whole point is ensuring the v-model callback gets called before\n// user-attached handlers.\nfunction normalizeEvents (on) {\n /* istanbul ignore if */\n if (isDef(on[RANGE_TOKEN])) {\n // IE input[type=range] only supports `change` event\n var event = isIE ? 'change' : 'input';\n on[event] = [].concat(on[RANGE_TOKEN], on[event] || []);\n delete on[RANGE_TOKEN];\n }\n // This was originally intended to fix #4521 but no longer necessary\n // after 2.5. Keeping it for backwards compat with generated code from < 2.4\n /* istanbul ignore if */\n if (isDef(on[CHECKBOX_RADIO_TOKEN])) {\n on.change = [].concat(on[CHECKBOX_RADIO_TOKEN], on.change || []);\n delete on[CHECKBOX_RADIO_TOKEN];\n }\n}\n\nvar target$1;\n\nfunction createOnceHandler$1 (event, handler, capture) {\n var _target = target$1; // save current target element in closure\n return function onceHandler () {\n var res = handler.apply(null, arguments);\n if (res !== null) {\n remove$2(event, onceHandler, capture, _target);\n }\n }\n}\n\n// #9446: Firefox <= 53 (in particular, ESR 52) has incorrect Event.timeStamp\n// implementation and does not fire microtasks in between event propagation, so\n// safe to exclude.\nvar useMicrotaskFix = isUsingMicroTask && !(isFF && Number(isFF[1]) <= 53);\n\nfunction add$1 (\n name,\n handler,\n capture,\n passive\n) {\n // async edge case #6566: inner click event triggers patch, event handler\n // attached to outer element during patch, and triggered again. This\n // happens because browsers fire microtask ticks between event propagation.\n // the solution is simple: we save the timestamp when a handler is attached,\n // and the handler would only fire if the event passed to it was fired\n // AFTER it was attached.\n if (useMicrotaskFix) {\n var attachedTimestamp = currentFlushTimestamp;\n var original = handler;\n handler = original._wrapper = function (e) {\n if (\n // no bubbling, should always fire.\n // this is just a safety net in case event.timeStamp is unreliable in\n // certain weird environments...\n e.target === e.currentTarget ||\n // event is fired after handler attachment\n e.timeStamp >= attachedTimestamp ||\n // bail for environments that have buggy event.timeStamp implementations\n // #9462 iOS 9 bug: event.timeStamp is 0 after history.pushState\n // #9681 QtWebEngine event.timeStamp is negative value\n e.timeStamp <= 0 ||\n // #9448 bail if event is fired in another document in a multi-page\n // electron/nw.js app, since event.timeStamp will be using a different\n // starting reference\n e.target.ownerDocument !== document\n ) {\n return original.apply(this, arguments)\n }\n };\n }\n target$1.addEventListener(\n name,\n handler,\n supportsPassive\n ? { capture: capture, passive: passive }\n : capture\n );\n}\n\nfunction remove$2 (\n name,\n handler,\n capture,\n _target\n) {\n (_target || target$1).removeEventListener(\n name,\n handler._wrapper || handler,\n capture\n );\n}\n\nfunction updateDOMListeners (oldVnode, vnode) {\n if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {\n return\n }\n var on = vnode.data.on || {};\n var oldOn = oldVnode.data.on || {};\n target$1 = vnode.elm;\n normalizeEvents(on);\n updateListeners(on, oldOn, add$1, remove$2, createOnceHandler$1, vnode.context);\n target$1 = undefined;\n}\n\nvar events = {\n create: updateDOMListeners,\n update: updateDOMListeners\n};\n\n/* */\n\nvar svgContainer;\n\nfunction updateDOMProps (oldVnode, vnode) {\n if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) {\n return\n }\n var key, cur;\n var elm = vnode.elm;\n var oldProps = oldVnode.data.domProps || {};\n var props = vnode.data.domProps || {};\n // clone observed objects, as the user probably wants to mutate it\n if (isDef(props.__ob__)) {\n props = vnode.data.domProps = extend({}, props);\n }\n\n for (key in oldProps) {\n if (!(key in props)) {\n elm[key] = '';\n }\n }\n\n for (key in props) {\n cur = props[key];\n // ignore children if the node has textContent or innerHTML,\n // as these will throw away existing DOM nodes and cause removal errors\n // on subsequent patches (#3360)\n if (key === 'textContent' || key === 'innerHTML') {\n if (vnode.children) { vnode.children.length = 0; }\n if (cur === oldProps[key]) { continue }\n // #6601 work around Chrome version <= 55 bug where single textNode\n // replaced by innerHTML/textContent retains its parentNode property\n if (elm.childNodes.length === 1) {\n elm.removeChild(elm.childNodes[0]);\n }\n }\n\n if (key === 'value' && elm.tagName !== 'PROGRESS') {\n // store value as _value as well since\n // non-string values will be stringified\n elm._value = cur;\n // avoid resetting cursor position when value is the same\n var strCur = isUndef(cur) ? '' : String(cur);\n if (shouldUpdateValue(elm, strCur)) {\n elm.value = strCur;\n }\n } else if (key === 'innerHTML' && isSVG(elm.tagName) && isUndef(elm.innerHTML)) {\n // IE doesn't support innerHTML for SVG elements\n svgContainer = svgContainer || document.createElement('div');\n svgContainer.innerHTML = \"<svg>\" + cur + \"</svg>\";\n var svg = svgContainer.firstChild;\n while (elm.firstChild) {\n elm.removeChild(elm.firstChild);\n }\n while (svg.firstChild) {\n elm.appendChild(svg.firstChild);\n }\n } else if (\n // skip the update if old and new VDOM state is the same.\n // `value` is handled separately because the DOM value may be temporarily\n // out of sync with VDOM state due to focus, composition and modifiers.\n // This #4521 by skipping the unnecesarry `checked` update.\n cur !== oldProps[key]\n ) {\n // some property updates can throw\n // e.g. `value` on <progress> w/ non-finite value\n try {\n elm[key] = cur;\n } catch (e) {}\n }\n }\n}\n\n// check platforms/web/util/attrs.js acceptValue\n\n\nfunction shouldUpdateValue (elm, checkVal) {\n return (!elm.composing && (\n elm.tagName === 'OPTION' ||\n isNotInFocusAndDirty(elm, checkVal) ||\n isDirtyWithModifiers(elm, checkVal)\n ))\n}\n\nfunction isNotInFocusAndDirty (elm, checkVal) {\n // return true when textbox (.number and .trim) loses focus and its value is\n // not equal to the updated value\n var notInFocus = true;\n // #6157\n // work around IE bug when accessing document.activeElement in an iframe\n try { notInFocus = document.activeElement !== elm; } catch (e) {}\n return notInFocus && elm.value !== checkVal\n}\n\nfunction isDirtyWithModifiers (elm, newVal) {\n var value = elm.value;\n var modifiers = elm._vModifiers; // injected by v-model runtime\n if (isDef(modifiers)) {\n if (modifiers.number) {\n return toNumber(value) !== toNumber(newVal)\n }\n if (modifiers.trim) {\n return value.trim() !== newVal.trim()\n }\n }\n return value !== newVal\n}\n\nvar domProps = {\n create: updateDOMProps,\n update: updateDOMProps\n};\n\n/* */\n\nvar parseStyleText = cached(function (cssText) {\n var res = {};\n var listDelimiter = /;(?![^(]*\\))/g;\n var propertyDelimiter = /:(.+)/;\n cssText.split(listDelimiter).forEach(function (item) {\n if (item) {\n var tmp = item.split(propertyDelimiter);\n tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim());\n }\n });\n return res\n});\n\n// merge static and dynamic style data on the same vnode\nfunction normalizeStyleData (data) {\n var style = normalizeStyleBinding(data.style);\n // static style is pre-processed into an object during compilation\n // and is always a fresh object, so it's safe to merge into it\n return data.staticStyle\n ? extend(data.staticStyle, style)\n : style\n}\n\n// normalize possible array / string values into Object\nfunction normalizeStyleBinding (bindingStyle) {\n if (Array.isArray(bindingStyle)) {\n return toObject(bindingStyle)\n }\n if (typeof bindingStyle === 'string') {\n return parseStyleText(bindingStyle)\n }\n return bindingStyle\n}\n\n/**\n * parent component style should be after child's\n * so that parent component's style could override it\n */\nfunction getStyle (vnode, checkChild) {\n var res = {};\n var styleData;\n\n if (checkChild) {\n var childNode = vnode;\n while (childNode.componentInstance) {\n childNode = childNode.componentInstance._vnode;\n if (\n childNode && childNode.data &&\n (styleData = normalizeStyleData(childNode.data))\n ) {\n extend(res, styleData);\n }\n }\n }\n\n if ((styleData = normalizeStyleData(vnode.data))) {\n extend(res, styleData);\n }\n\n var parentNode = vnode;\n while ((parentNode = parentNode.parent)) {\n if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) {\n extend(res, styleData);\n }\n }\n return res\n}\n\n/* */\n\nvar cssVarRE = /^--/;\nvar importantRE = /\\s*!important$/;\nvar setProp = function (el, name, val) {\n /* istanbul ignore if */\n if (cssVarRE.test(name)) {\n el.style.setProperty(name, val);\n } else if (importantRE.test(val)) {\n el.style.setProperty(hyphenate(name), val.replace(importantRE, ''), 'important');\n } else {\n var normalizedName = normalize(name);\n if (Array.isArray(val)) {\n // Support values array created by autoprefixer, e.g.\n // {display: [\"-webkit-box\", \"-ms-flexbox\", \"flex\"]}\n // Set them one by one, and the browser will only set those it can recognize\n for (var i = 0, len = val.length; i < len; i++) {\n el.style[normalizedName] = val[i];\n }\n } else {\n el.style[normalizedName] = val;\n }\n }\n};\n\nvar vendorNames = ['Webkit', 'Moz', 'ms'];\n\nvar emptyStyle;\nvar normalize = cached(function (prop) {\n emptyStyle = emptyStyle || document.createElement('div').style;\n prop = camelize(prop);\n if (prop !== 'filter' && (prop in emptyStyle)) {\n return prop\n }\n var capName = prop.charAt(0).toUpperCase() + prop.slice(1);\n for (var i = 0; i < vendorNames.length; i++) {\n var name = vendorNames[i] + capName;\n if (name in emptyStyle) {\n return name\n }\n }\n});\n\nfunction updateStyle (oldVnode, vnode) {\n var data = vnode.data;\n var oldData = oldVnode.data;\n\n if (isUndef(data.staticStyle) && isUndef(data.style) &&\n isUndef(oldData.staticStyle) && isUndef(oldData.style)\n ) {\n return\n }\n\n var cur, name;\n var el = vnode.elm;\n var oldStaticStyle = oldData.staticStyle;\n var oldStyleBinding = oldData.normalizedStyle || oldData.style || {};\n\n // if static style exists, stylebinding already merged into it when doing normalizeStyleData\n var oldStyle = oldStaticStyle || oldStyleBinding;\n\n var style = normalizeStyleBinding(vnode.data.style) || {};\n\n // store normalized style under a different key for next diff\n // make sure to clone it if it's reactive, since the user likely wants\n // to mutate it.\n vnode.data.normalizedStyle = isDef(style.__ob__)\n ? extend({}, style)\n : style;\n\n var newStyle = getStyle(vnode, true);\n\n for (name in oldStyle) {\n if (isUndef(newStyle[name])) {\n setProp(el, name, '');\n }\n }\n for (name in newStyle) {\n cur = newStyle[name];\n if (cur !== oldStyle[name]) {\n // ie9 setting to null has no effect, must use empty string\n setProp(el, name, cur == null ? '' : cur);\n }\n }\n}\n\nvar style = {\n create: updateStyle,\n update: updateStyle\n};\n\n/* */\n\nvar whitespaceRE = /\\s+/;\n\n/**\n * Add class with compatibility for SVG since classList is not supported on\n * SVG elements in IE\n */\nfunction addClass (el, cls) {\n /* istanbul ignore if */\n if (!cls || !(cls = cls.trim())) {\n return\n }\n\n /* istanbul ignore else */\n if (el.classList) {\n if (cls.indexOf(' ') > -1) {\n cls.split(whitespaceRE).forEach(function (c) { return el.classList.add(c); });\n } else {\n el.classList.add(cls);\n }\n } else {\n var cur = \" \" + (el.getAttribute('class') || '') + \" \";\n if (cur.indexOf(' ' + cls + ' ') < 0) {\n el.setAttribute('class', (cur + cls).trim());\n }\n }\n}\n\n/**\n * Remove class with compatibility for SVG since classList is not supported on\n * SVG elements in IE\n */\nfunction removeClass (el, cls) {\n /* istanbul ignore if */\n if (!cls || !(cls = cls.trim())) {\n return\n }\n\n /* istanbul ignore else */\n if (el.classList) {\n if (cls.indexOf(' ') > -1) {\n cls.split(whitespaceRE).forEach(function (c) { return el.classList.remove(c); });\n } else {\n el.classList.remove(cls);\n }\n if (!el.classList.length) {\n el.removeAttribute('class');\n }\n } else {\n var cur = \" \" + (el.getAttribute('class') || '') + \" \";\n var tar = ' ' + cls + ' ';\n while (cur.indexOf(tar) >= 0) {\n cur = cur.replace(tar, ' ');\n }\n cur = cur.trim();\n if (cur) {\n el.setAttribute('class', cur);\n } else {\n el.removeAttribute('class');\n }\n }\n}\n\n/* */\n\nfunction resolveTransition (def$$1) {\n if (!def$$1) {\n return\n }\n /* istanbul ignore else */\n if (typeof def$$1 === 'object') {\n var res = {};\n if (def$$1.css !== false) {\n extend(res, autoCssTransition(def$$1.name || 'v'));\n }\n extend(res, def$$1);\n return res\n } else if (typeof def$$1 === 'string') {\n return autoCssTransition(def$$1)\n }\n}\n\nvar autoCssTransition = cached(function (name) {\n return {\n enterClass: (name + \"-enter\"),\n enterToClass: (name + \"-enter-to\"),\n enterActiveClass: (name + \"-enter-active\"),\n leaveClass: (name + \"-leave\"),\n leaveToClass: (name + \"-leave-to\"),\n leaveActiveClass: (name + \"-leave-active\")\n }\n});\n\nvar hasTransition = inBrowser && !isIE9;\nvar TRANSITION = 'transition';\nvar ANIMATION = 'animation';\n\n// Transition property/event sniffing\nvar transitionProp = 'transition';\nvar transitionEndEvent = 'transitionend';\nvar animationProp = 'animation';\nvar animationEndEvent = 'animationend';\nif (hasTransition) {\n /* istanbul ignore if */\n if (window.ontransitionend === undefined &&\n window.onwebkittransitionend !== undefined\n ) {\n transitionProp = 'WebkitTransition';\n transitionEndEvent = 'webkitTransitionEnd';\n }\n if (window.onanimationend === undefined &&\n window.onwebkitanimationend !== undefined\n ) {\n animationProp = 'WebkitAnimation';\n animationEndEvent = 'webkitAnimationEnd';\n }\n}\n\n// binding to window is necessary to make hot reload work in IE in strict mode\nvar raf = inBrowser\n ? window.requestAnimationFrame\n ? window.requestAnimationFrame.bind(window)\n : setTimeout\n : /* istanbul ignore next */ function (fn) { return fn(); };\n\nfunction nextFrame (fn) {\n raf(function () {\n raf(fn);\n });\n}\n\nfunction addTransitionClass (el, cls) {\n var transitionClasses = el._transitionClasses || (el._transitionClasses = []);\n if (transitionClasses.indexOf(cls) < 0) {\n transitionClasses.push(cls);\n addClass(el, cls);\n }\n}\n\nfunction removeTransitionClass (el, cls) {\n if (el._transitionClasses) {\n remove(el._transitionClasses, cls);\n }\n removeClass(el, cls);\n}\n\nfunction whenTransitionEnds (\n el,\n expectedType,\n cb\n) {\n var ref = getTransitionInfo(el, expectedType);\n var type = ref.type;\n var timeout = ref.timeout;\n var propCount = ref.propCount;\n if (!type) { return cb() }\n var event = type === TRANSITION ? transitionEndEvent : animationEndEvent;\n var ended = 0;\n var end = function () {\n el.removeEventListener(event, onEnd);\n cb();\n };\n var onEnd = function (e) {\n if (e.target === el) {\n if (++ended >= propCount) {\n end();\n }\n }\n };\n setTimeout(function () {\n if (ended < propCount) {\n end();\n }\n }, timeout + 1);\n el.addEventListener(event, onEnd);\n}\n\nvar transformRE = /\\b(transform|all)(,|$)/;\n\nfunction getTransitionInfo (el, expectedType) {\n var styles = window.getComputedStyle(el);\n // JSDOM may return undefined for transition properties\n var transitionDelays = (styles[transitionProp + 'Delay'] || '').split(', ');\n var transitionDurations = (styles[transitionProp + 'Duration'] || '').split(', ');\n var transitionTimeout = getTimeout(transitionDelays, transitionDurations);\n var animationDelays = (styles[animationProp + 'Delay'] || '').split(', ');\n var animationDurations = (styles[animationProp + 'Duration'] || '').split(', ');\n var animationTimeout = getTimeout(animationDelays, animationDurations);\n\n var type;\n var timeout = 0;\n var propCount = 0;\n /* istanbul ignore if */\n if (expectedType === TRANSITION) {\n if (transitionTimeout > 0) {\n type = TRANSITION;\n timeout = transitionTimeout;\n propCount = transitionDurations.length;\n }\n } else if (expectedType === ANIMATION) {\n if (animationTimeout > 0) {\n type = ANIMATION;\n timeout = animationTimeout;\n propCount = animationDurations.length;\n }\n } else {\n timeout = Math.max(transitionTimeout, animationTimeout);\n type = timeout > 0\n ? transitionTimeout > animationTimeout\n ? TRANSITION\n : ANIMATION\n : null;\n propCount = type\n ? type === TRANSITION\n ? transitionDurations.length\n : animationDurations.length\n : 0;\n }\n var hasTransform =\n type === TRANSITION &&\n transformRE.test(styles[transitionProp + 'Property']);\n return {\n type: type,\n timeout: timeout,\n propCount: propCount,\n hasTransform: hasTransform\n }\n}\n\nfunction getTimeout (delays, durations) {\n /* istanbul ignore next */\n while (delays.length < durations.length) {\n delays = delays.concat(delays);\n }\n\n return Math.max.apply(null, durations.map(function (d, i) {\n return toMs(d) + toMs(delays[i])\n }))\n}\n\n// Old versions of Chromium (below 61.0.3163.100) formats floating pointer numbers\n// in a locale-dependent way, using a comma instead of a dot.\n// If comma is not replaced with a dot, the input will be rounded down (i.e. acting\n// as a floor function) causing unexpected behaviors\nfunction toMs (s) {\n return Number(s.slice(0, -1).replace(',', '.')) * 1000\n}\n\n/* */\n\nfunction enter (vnode, toggleDisplay) {\n var el = vnode.elm;\n\n // call leave callback now\n if (isDef(el._leaveCb)) {\n el._leaveCb.cancelled = true;\n el._leaveCb();\n }\n\n var data = resolveTransition(vnode.data.transition);\n if (isUndef(data)) {\n return\n }\n\n /* istanbul ignore if */\n if (isDef(el._enterCb) || el.nodeType !== 1) {\n return\n }\n\n var css = data.css;\n var type = data.type;\n var enterClass = data.enterClass;\n var enterToClass = data.enterToClass;\n var enterActiveClass = data.enterActiveClass;\n var appearClass = data.appearClass;\n var appearToClass = data.appearToClass;\n var appearActiveClass = data.appearActiveClass;\n var beforeEnter = data.beforeEnter;\n var enter = data.enter;\n var afterEnter = data.afterEnter;\n var enterCancelled = data.enterCancelled;\n var beforeAppear = data.beforeAppear;\n var appear = data.appear;\n var afterAppear = data.afterAppear;\n var appearCancelled = data.appearCancelled;\n var duration = data.duration;\n\n // activeInstance will always be the <transition> component managing this\n // transition. One edge case to check is when the <transition> is placed\n // as the root node of a child component. In that case we need to check\n // <transition>'s parent for appear check.\n var context = activeInstance;\n var transitionNode = activeInstance.$vnode;\n while (transitionNode && transitionNode.parent) {\n context = transitionNode.context;\n transitionNode = transitionNode.parent;\n }\n\n var isAppear = !context._isMounted || !vnode.isRootInsert;\n\n if (isAppear && !appear && appear !== '') {\n return\n }\n\n var startClass = isAppear && appearClass\n ? appearClass\n : enterClass;\n var activeClass = isAppear && appearActiveClass\n ? appearActiveClass\n : enterActiveClass;\n var toClass = isAppear && appearToClass\n ? appearToClass\n : enterToClass;\n\n var beforeEnterHook = isAppear\n ? (beforeAppear || beforeEnter)\n : beforeEnter;\n var enterHook = isAppear\n ? (typeof appear === 'function' ? appear : enter)\n : enter;\n var afterEnterHook = isAppear\n ? (afterAppear || afterEnter)\n : afterEnter;\n var enterCancelledHook = isAppear\n ? (appearCancelled || enterCancelled)\n : enterCancelled;\n\n var explicitEnterDuration = toNumber(\n isObject(duration)\n ? duration.enter\n : duration\n );\n\n if (false) {\n checkDuration(explicitEnterDuration, 'enter', vnode);\n }\n\n var expectsCSS = css !== false && !isIE9;\n var userWantsControl = getHookArgumentsLength(enterHook);\n\n var cb = el._enterCb = once(function () {\n if (expectsCSS) {\n removeTransitionClass(el, toClass);\n removeTransitionClass(el, activeClass);\n }\n if (cb.cancelled) {\n if (expectsCSS) {\n removeTransitionClass(el, startClass);\n }\n enterCancelledHook && enterCancelledHook(el);\n } else {\n afterEnterHook && afterEnterHook(el);\n }\n el._enterCb = null;\n });\n\n if (!vnode.data.show) {\n // remove pending leave element on enter by injecting an insert hook\n mergeVNodeHook(vnode, 'insert', function () {\n var parent = el.parentNode;\n var pendingNode = parent && parent._pending && parent._pending[vnode.key];\n if (pendingNode &&\n pendingNode.tag === vnode.tag &&\n pendingNode.elm._leaveCb\n ) {\n pendingNode.elm._leaveCb();\n }\n enterHook && enterHook(el, cb);\n });\n }\n\n // start enter transition\n beforeEnterHook && beforeEnterHook(el);\n if (expectsCSS) {\n addTransitionClass(el, startClass);\n addTransitionClass(el, activeClass);\n nextFrame(function () {\n removeTransitionClass(el, startClass);\n if (!cb.cancelled) {\n addTransitionClass(el, toClass);\n if (!userWantsControl) {\n if (isValidDuration(explicitEnterDuration)) {\n setTimeout(cb, explicitEnterDuration);\n } else {\n whenTransitionEnds(el, type, cb);\n }\n }\n }\n });\n }\n\n if (vnode.data.show) {\n toggleDisplay && toggleDisplay();\n enterHook && enterHook(el, cb);\n }\n\n if (!expectsCSS && !userWantsControl) {\n cb();\n }\n}\n\nfunction leave (vnode, rm) {\n var el = vnode.elm;\n\n // call enter callback now\n if (isDef(el._enterCb)) {\n el._enterCb.cancelled = true;\n el._enterCb();\n }\n\n var data = resolveTransition(vnode.data.transition);\n if (isUndef(data) || el.nodeType !== 1) {\n return rm()\n }\n\n /* istanbul ignore if */\n if (isDef(el._leaveCb)) {\n return\n }\n\n var css = data.css;\n var type = data.type;\n var leaveClass = data.leaveClass;\n var leaveToClass = data.leaveToClass;\n var leaveActiveClass = data.leaveActiveClass;\n var beforeLeave = data.beforeLeave;\n var leave = data.leave;\n var afterLeave = data.afterLeave;\n var leaveCancelled = data.leaveCancelled;\n var delayLeave = data.delayLeave;\n var duration = data.duration;\n\n var expectsCSS = css !== false && !isIE9;\n var userWantsControl = getHookArgumentsLength(leave);\n\n var explicitLeaveDuration = toNumber(\n isObject(duration)\n ? duration.leave\n : duration\n );\n\n if (false) {\n checkDuration(explicitLeaveDuration, 'leave', vnode);\n }\n\n var cb = el._leaveCb = once(function () {\n if (el.parentNode && el.parentNode._pending) {\n el.parentNode._pending[vnode.key] = null;\n }\n if (expectsCSS) {\n removeTransitionClass(el, leaveToClass);\n removeTransitionClass(el, leaveActiveClass);\n }\n if (cb.cancelled) {\n if (expectsCSS) {\n removeTransitionClass(el, leaveClass);\n }\n leaveCancelled && leaveCancelled(el);\n } else {\n rm();\n afterLeave && afterLeave(el);\n }\n el._leaveCb = null;\n });\n\n if (delayLeave) {\n delayLeave(performLeave);\n } else {\n performLeave();\n }\n\n function performLeave () {\n // the delayed leave may have already been cancelled\n if (cb.cancelled) {\n return\n }\n // record leaving element\n if (!vnode.data.show && el.parentNode) {\n (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode;\n }\n beforeLeave && beforeLeave(el);\n if (expectsCSS) {\n addTransitionClass(el, leaveClass);\n addTransitionClass(el, leaveActiveClass);\n nextFrame(function () {\n removeTransitionClass(el, leaveClass);\n if (!cb.cancelled) {\n addTransitionClass(el, leaveToClass);\n if (!userWantsControl) {\n if (isValidDuration(explicitLeaveDuration)) {\n setTimeout(cb, explicitLeaveDuration);\n } else {\n whenTransitionEnds(el, type, cb);\n }\n }\n }\n });\n }\n leave && leave(el, cb);\n if (!expectsCSS && !userWantsControl) {\n cb();\n }\n }\n}\n\n// only used in dev mode\nfunction checkDuration (val, name, vnode) {\n if (typeof val !== 'number') {\n warn(\n \"<transition> explicit \" + name + \" duration is not a valid number - \" +\n \"got \" + (JSON.stringify(val)) + \".\",\n vnode.context\n );\n } else if (isNaN(val)) {\n warn(\n \"<transition> explicit \" + name + \" duration is NaN - \" +\n 'the duration expression might be incorrect.',\n vnode.context\n );\n }\n}\n\nfunction isValidDuration (val) {\n return typeof val === 'number' && !isNaN(val)\n}\n\n/**\n * Normalize a transition hook's argument length. The hook may be:\n * - a merged hook (invoker) with the original in .fns\n * - a wrapped component method (check ._length)\n * - a plain function (.length)\n */\nfunction getHookArgumentsLength (fn) {\n if (isUndef(fn)) {\n return false\n }\n var invokerFns = fn.fns;\n if (isDef(invokerFns)) {\n // invoker\n return getHookArgumentsLength(\n Array.isArray(invokerFns)\n ? invokerFns[0]\n : invokerFns\n )\n } else {\n return (fn._length || fn.length) > 1\n }\n}\n\nfunction _enter (_, vnode) {\n if (vnode.data.show !== true) {\n enter(vnode);\n }\n}\n\nvar transition = inBrowser ? {\n create: _enter,\n activate: _enter,\n remove: function remove$$1 (vnode, rm) {\n /* istanbul ignore else */\n if (vnode.data.show !== true) {\n leave(vnode, rm);\n } else {\n rm();\n }\n }\n} : {};\n\nvar platformModules = [\n attrs,\n klass,\n events,\n domProps,\n style,\n transition\n];\n\n/* */\n\n// the directive module should be applied last, after all\n// built-in modules have been applied.\nvar modules = platformModules.concat(baseModules);\n\nvar patch = createPatchFunction({ nodeOps: nodeOps, modules: modules });\n\n/**\n * Not type checking this file because flow doesn't like attaching\n * properties to Elements.\n */\n\n/* istanbul ignore if */\nif (isIE9) {\n // http://www.matts411.com/post/internet-explorer-9-oninput/\n document.addEventListener('selectionchange', function () {\n var el = document.activeElement;\n if (el && el.vmodel) {\n trigger(el, 'input');\n }\n });\n}\n\nvar directive = {\n inserted: function inserted (el, binding, vnode, oldVnode) {\n if (vnode.tag === 'select') {\n // #6903\n if (oldVnode.elm && !oldVnode.elm._vOptions) {\n mergeVNodeHook(vnode, 'postpatch', function () {\n directive.componentUpdated(el, binding, vnode);\n });\n } else {\n setSelected(el, binding, vnode.context);\n }\n el._vOptions = [].map.call(el.options, getValue);\n } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {\n el._vModifiers = binding.modifiers;\n if (!binding.modifiers.lazy) {\n el.addEventListener('compositionstart', onCompositionStart);\n el.addEventListener('compositionend', onCompositionEnd);\n // Safari < 10.2 & UIWebView doesn't fire compositionend when\n // switching focus before confirming composition choice\n // this also fixes the issue where some browsers e.g. iOS Chrome\n // fires \"change\" instead of \"input\" on autocomplete.\n el.addEventListener('change', onCompositionEnd);\n /* istanbul ignore if */\n if (isIE9) {\n el.vmodel = true;\n }\n }\n }\n },\n\n componentUpdated: function componentUpdated (el, binding, vnode) {\n if (vnode.tag === 'select') {\n setSelected(el, binding, vnode.context);\n // in case the options rendered by v-for have changed,\n // it's possible that the value is out-of-sync with the rendered options.\n // detect such cases and filter out values that no longer has a matching\n // option in the DOM.\n var prevOptions = el._vOptions;\n var curOptions = el._vOptions = [].map.call(el.options, getValue);\n if (curOptions.some(function (o, i) { return !looseEqual(o, prevOptions[i]); })) {\n // trigger change event if\n // no matching option found for at least one value\n var needReset = el.multiple\n ? binding.value.some(function (v) { return hasNoMatchingOption(v, curOptions); })\n : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions);\n if (needReset) {\n trigger(el, 'change');\n }\n }\n }\n }\n};\n\nfunction setSelected (el, binding, vm) {\n actuallySetSelected(el, binding, vm);\n /* istanbul ignore if */\n if (isIE || isEdge) {\n setTimeout(function () {\n actuallySetSelected(el, binding, vm);\n }, 0);\n }\n}\n\nfunction actuallySetSelected (el, binding, vm) {\n var value = binding.value;\n var isMultiple = el.multiple;\n if (isMultiple && !Array.isArray(value)) {\n \"production\" !== 'production' && warn(\n \"<select multiple v-model=\\\"\" + (binding.expression) + \"\\\"> \" +\n \"expects an Array value for its binding, but got \" + (Object.prototype.toString.call(value).slice(8, -1)),\n vm\n );\n return\n }\n var selected, option;\n for (var i = 0, l = el.options.length; i < l; i++) {\n option = el.options[i];\n if (isMultiple) {\n selected = looseIndexOf(value, getValue(option)) > -1;\n if (option.selected !== selected) {\n option.selected = selected;\n }\n } else {\n if (looseEqual(getValue(option), value)) {\n if (el.selectedIndex !== i) {\n el.selectedIndex = i;\n }\n return\n }\n }\n }\n if (!isMultiple) {\n el.selectedIndex = -1;\n }\n}\n\nfunction hasNoMatchingOption (value, options) {\n return options.every(function (o) { return !looseEqual(o, value); })\n}\n\nfunction getValue (option) {\n return '_value' in option\n ? option._value\n : option.value\n}\n\nfunction onCompositionStart (e) {\n e.target.composing = true;\n}\n\nfunction onCompositionEnd (e) {\n // prevent triggering an input event for no reason\n if (!e.target.composing) { return }\n e.target.composing = false;\n trigger(e.target, 'input');\n}\n\nfunction trigger (el, type) {\n var e = document.createEvent('HTMLEvents');\n e.initEvent(type, true, true);\n el.dispatchEvent(e);\n}\n\n/* */\n\n// recursively search for possible transition defined inside the component root\nfunction locateNode (vnode) {\n return vnode.componentInstance && (!vnode.data || !vnode.data.transition)\n ? locateNode(vnode.componentInstance._vnode)\n : vnode\n}\n\nvar show = {\n bind: function bind (el, ref, vnode) {\n var value = ref.value;\n\n vnode = locateNode(vnode);\n var transition$$1 = vnode.data && vnode.data.transition;\n var originalDisplay = el.__vOriginalDisplay =\n el.style.display === 'none' ? '' : el.style.display;\n if (value && transition$$1) {\n vnode.data.show = true;\n enter(vnode, function () {\n el.style.display = originalDisplay;\n });\n } else {\n el.style.display = value ? originalDisplay : 'none';\n }\n },\n\n update: function update (el, ref, vnode) {\n var value = ref.value;\n var oldValue = ref.oldValue;\n\n /* istanbul ignore if */\n if (!value === !oldValue) { return }\n vnode = locateNode(vnode);\n var transition$$1 = vnode.data && vnode.data.transition;\n if (transition$$1) {\n vnode.data.show = true;\n if (value) {\n enter(vnode, function () {\n el.style.display = el.__vOriginalDisplay;\n });\n } else {\n leave(vnode, function () {\n el.style.display = 'none';\n });\n }\n } else {\n el.style.display = value ? el.__vOriginalDisplay : 'none';\n }\n },\n\n unbind: function unbind (\n el,\n binding,\n vnode,\n oldVnode,\n isDestroy\n ) {\n if (!isDestroy) {\n el.style.display = el.__vOriginalDisplay;\n }\n }\n};\n\nvar platformDirectives = {\n model: directive,\n show: show\n};\n\n/* */\n\nvar transitionProps = {\n name: String,\n appear: Boolean,\n css: Boolean,\n mode: String,\n type: String,\n enterClass: String,\n leaveClass: String,\n enterToClass: String,\n leaveToClass: String,\n enterActiveClass: String,\n leaveActiveClass: String,\n appearClass: String,\n appearActiveClass: String,\n appearToClass: String,\n duration: [Number, String, Object]\n};\n\n// in case the child is also an abstract component, e.g. <keep-alive>\n// we want to recursively retrieve the real component to be rendered\nfunction getRealChild (vnode) {\n var compOptions = vnode && vnode.componentOptions;\n if (compOptions && compOptions.Ctor.options.abstract) {\n return getRealChild(getFirstComponentChild(compOptions.children))\n } else {\n return vnode\n }\n}\n\nfunction extractTransitionData (comp) {\n var data = {};\n var options = comp.$options;\n // props\n for (var key in options.propsData) {\n data[key] = comp[key];\n }\n // events.\n // extract listeners and pass them directly to the transition methods\n var listeners = options._parentListeners;\n for (var key$1 in listeners) {\n data[camelize(key$1)] = listeners[key$1];\n }\n return data\n}\n\nfunction placeholder (h, rawChild) {\n if (/\\d-keep-alive$/.test(rawChild.tag)) {\n return h('keep-alive', {\n props: rawChild.componentOptions.propsData\n })\n }\n}\n\nfunction hasParentTransition (vnode) {\n while ((vnode = vnode.parent)) {\n if (vnode.data.transition) {\n return true\n }\n }\n}\n\nfunction isSameChild (child, oldChild) {\n return oldChild.key === child.key && oldChild.tag === child.tag\n}\n\nvar isNotTextNode = function (c) { return c.tag || isAsyncPlaceholder(c); };\n\nvar isVShowDirective = function (d) { return d.name === 'show'; };\n\nvar Transition = {\n name: 'transition',\n props: transitionProps,\n abstract: true,\n\n render: function render (h) {\n var this$1 = this;\n\n var children = this.$slots.default;\n if (!children) {\n return\n }\n\n // filter out text nodes (possible whitespaces)\n children = children.filter(isNotTextNode);\n /* istanbul ignore if */\n if (!children.length) {\n return\n }\n\n // warn multiple elements\n if (false) {\n warn(\n '<transition> can only be used on a single element. Use ' +\n '<transition-group> for lists.',\n this.$parent\n );\n }\n\n var mode = this.mode;\n\n // warn invalid mode\n if (false\n ) {\n warn(\n 'invalid <transition> mode: ' + mode,\n this.$parent\n );\n }\n\n var rawChild = children[0];\n\n // if this is a component root node and the component's\n // parent container node also has transition, skip.\n if (hasParentTransition(this.$vnode)) {\n return rawChild\n }\n\n // apply transition data to child\n // use getRealChild() to ignore abstract components e.g. keep-alive\n var child = getRealChild(rawChild);\n /* istanbul ignore if */\n if (!child) {\n return rawChild\n }\n\n if (this._leaving) {\n return placeholder(h, rawChild)\n }\n\n // ensure a key that is unique to the vnode type and to this transition\n // component instance. This key will be used to remove pending leaving nodes\n // during entering.\n var id = \"__transition-\" + (this._uid) + \"-\";\n child.key = child.key == null\n ? child.isComment\n ? id + 'comment'\n : id + child.tag\n : isPrimitive(child.key)\n ? (String(child.key).indexOf(id) === 0 ? child.key : id + child.key)\n : child.key;\n\n var data = (child.data || (child.data = {})).transition = extractTransitionData(this);\n var oldRawChild = this._vnode;\n var oldChild = getRealChild(oldRawChild);\n\n // mark v-show\n // so that the transition module can hand over the control to the directive\n if (child.data.directives && child.data.directives.some(isVShowDirective)) {\n child.data.show = true;\n }\n\n if (\n oldChild &&\n oldChild.data &&\n !isSameChild(child, oldChild) &&\n !isAsyncPlaceholder(oldChild) &&\n // #6687 component root is a comment node\n !(oldChild.componentInstance && oldChild.componentInstance._vnode.isComment)\n ) {\n // replace old child transition data with fresh one\n // important for dynamic transitions!\n var oldData = oldChild.data.transition = extend({}, data);\n // handle transition mode\n if (mode === 'out-in') {\n // return placeholder node and queue update when leave finishes\n this._leaving = true;\n mergeVNodeHook(oldData, 'afterLeave', function () {\n this$1._leaving = false;\n this$1.$forceUpdate();\n });\n return placeholder(h, rawChild)\n } else if (mode === 'in-out') {\n if (isAsyncPlaceholder(child)) {\n return oldRawChild\n }\n var delayedLeave;\n var performLeave = function () { delayedLeave(); };\n mergeVNodeHook(data, 'afterEnter', performLeave);\n mergeVNodeHook(data, 'enterCancelled', performLeave);\n mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; });\n }\n }\n\n return rawChild\n }\n};\n\n/* */\n\nvar props = extend({\n tag: String,\n moveClass: String\n}, transitionProps);\n\ndelete props.mode;\n\nvar TransitionGroup = {\n props: props,\n\n beforeMount: function beforeMount () {\n var this$1 = this;\n\n var update = this._update;\n this._update = function (vnode, hydrating) {\n var restoreActiveInstance = setActiveInstance(this$1);\n // force removing pass\n this$1.__patch__(\n this$1._vnode,\n this$1.kept,\n false, // hydrating\n true // removeOnly (!important, avoids unnecessary moves)\n );\n this$1._vnode = this$1.kept;\n restoreActiveInstance();\n update.call(this$1, vnode, hydrating);\n };\n },\n\n render: function render (h) {\n var tag = this.tag || this.$vnode.data.tag || 'span';\n var map = Object.create(null);\n var prevChildren = this.prevChildren = this.children;\n var rawChildren = this.$slots.default || [];\n var children = this.children = [];\n var transitionData = extractTransitionData(this);\n\n for (var i = 0; i < rawChildren.length; i++) {\n var c = rawChildren[i];\n if (c.tag) {\n if (c.key != null && String(c.key).indexOf('__vlist') !== 0) {\n children.push(c);\n map[c.key] = c\n ;(c.data || (c.data = {})).transition = transitionData;\n } else if (false) {\n var opts = c.componentOptions;\n var name = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag;\n warn((\"<transition-group> children must be keyed: <\" + name + \">\"));\n }\n }\n }\n\n if (prevChildren) {\n var kept = [];\n var removed = [];\n for (var i$1 = 0; i$1 < prevChildren.length; i$1++) {\n var c$1 = prevChildren[i$1];\n c$1.data.transition = transitionData;\n c$1.data.pos = c$1.elm.getBoundingClientRect();\n if (map[c$1.key]) {\n kept.push(c$1);\n } else {\n removed.push(c$1);\n }\n }\n this.kept = h(tag, null, kept);\n this.removed = removed;\n }\n\n return h(tag, null, children)\n },\n\n updated: function updated () {\n var children = this.prevChildren;\n var moveClass = this.moveClass || ((this.name || 'v') + '-move');\n if (!children.length || !this.hasMove(children[0].elm, moveClass)) {\n return\n }\n\n // we divide the work into three loops to avoid mixing DOM reads and writes\n // in each iteration - which helps prevent layout thrashing.\n children.forEach(callPendingCbs);\n children.forEach(recordPosition);\n children.forEach(applyTranslation);\n\n // force reflow to put everything in position\n // assign to this to avoid being removed in tree-shaking\n // $flow-disable-line\n this._reflow = document.body.offsetHeight;\n\n children.forEach(function (c) {\n if (c.data.moved) {\n var el = c.elm;\n var s = el.style;\n addTransitionClass(el, moveClass);\n s.transform = s.WebkitTransform = s.transitionDuration = '';\n el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) {\n if (e && e.target !== el) {\n return\n }\n if (!e || /transform$/.test(e.propertyName)) {\n el.removeEventListener(transitionEndEvent, cb);\n el._moveCb = null;\n removeTransitionClass(el, moveClass);\n }\n });\n }\n });\n },\n\n methods: {\n hasMove: function hasMove (el, moveClass) {\n /* istanbul ignore if */\n if (!hasTransition) {\n return false\n }\n /* istanbul ignore if */\n if (this._hasMove) {\n return this._hasMove\n }\n // Detect whether an element with the move class applied has\n // CSS transitions. Since the element may be inside an entering\n // transition at this very moment, we make a clone of it and remove\n // all other transition classes applied to ensure only the move class\n // is applied.\n var clone = el.cloneNode();\n if (el._transitionClasses) {\n el._transitionClasses.forEach(function (cls) { removeClass(clone, cls); });\n }\n addClass(clone, moveClass);\n clone.style.display = 'none';\n this.$el.appendChild(clone);\n var info = getTransitionInfo(clone);\n this.$el.removeChild(clone);\n return (this._hasMove = info.hasTransform)\n }\n }\n};\n\nfunction callPendingCbs (c) {\n /* istanbul ignore if */\n if (c.elm._moveCb) {\n c.elm._moveCb();\n }\n /* istanbul ignore if */\n if (c.elm._enterCb) {\n c.elm._enterCb();\n }\n}\n\nfunction recordPosition (c) {\n c.data.newPos = c.elm.getBoundingClientRect();\n}\n\nfunction applyTranslation (c) {\n var oldPos = c.data.pos;\n var newPos = c.data.newPos;\n var dx = oldPos.left - newPos.left;\n var dy = oldPos.top - newPos.top;\n if (dx || dy) {\n c.data.moved = true;\n var s = c.elm.style;\n s.transform = s.WebkitTransform = \"translate(\" + dx + \"px,\" + dy + \"px)\";\n s.transitionDuration = '0s';\n }\n}\n\nvar platformComponents = {\n Transition: Transition,\n TransitionGroup: TransitionGroup\n};\n\n/* */\n\n// install platform specific utils\nVue.config.mustUseProp = mustUseProp;\nVue.config.isReservedTag = isReservedTag;\nVue.config.isReservedAttr = isReservedAttr;\nVue.config.getTagNamespace = getTagNamespace;\nVue.config.isUnknownElement = isUnknownElement;\n\n// install platform runtime directives & components\nextend(Vue.options.directives, platformDirectives);\nextend(Vue.options.components, platformComponents);\n\n// install platform patch function\nVue.prototype.__patch__ = inBrowser ? patch : noop;\n\n// public mount method\nVue.prototype.$mount = function (\n el,\n hydrating\n) {\n el = el && inBrowser ? query(el) : undefined;\n return mountComponent(this, el, hydrating)\n};\n\n// devtools global hook\n/* istanbul ignore next */\nif (inBrowser) {\n setTimeout(function () {\n if (config.devtools) {\n if (devtools) {\n devtools.emit('init', Vue);\n } else if (\n false\n ) {\n console[console.info ? 'info' : 'log'](\n 'Download the Vue Devtools extension for a better development experience:\\n' +\n 'https://github.com/vuejs/vue-devtools'\n );\n }\n }\n if (false\n ) {\n console[console.info ? 'info' : 'log'](\n \"You are running Vue in development mode.\\n\" +\n \"Make sure to turn on production mode when deploying for production.\\n\" +\n \"See more tips at https://vuejs.org/guide/deployment.html\"\n );\n }\n }, 0);\n}\n\n/* */\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (Vue);\n\n/* WEBPACK VAR INJECTION */}.call(__webpack_exports__, __webpack_require__(4), __webpack_require__(17).setImmediate))\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports) {\n\nvar g;\r\n\r\n// This works in non-strict mode\r\ng = (function() {\r\n\treturn this;\r\n})();\r\n\r\ntry {\r\n\t// This works if eval is allowed (see CSP)\r\n\tg = g || Function(\"return this\")() || (1,eval)(\"this\");\r\n} catch(e) {\r\n\t// This works if the window reference is available\r\n\tif(typeof window === \"object\")\r\n\t\tg = window;\r\n}\r\n\r\n// g can still be undefined, but nothing to do about it...\r\n// We return undefined, instead of nothing here, so it's\r\n// easier to handle this case. if(!global) { ...}\r\n\r\nmodule.exports = g;\r\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_TreeView_vue__ = __webpack_require__(12);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_44998ed8_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_TreeView_vue__ = __webpack_require__(50);\nfunction injectStyle (ssrContext) {\n __webpack_require__(48)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_TreeView_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_44998ed8_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_TreeView_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 6 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__components_NavBar_vue__ = __webpack_require__(29);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_NavMenu_vue__ = __webpack_require__(33);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__components_OptionsPage_vue__ = __webpack_require__(37);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__components_PassViewPage_vue__ = __webpack_require__(41);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__components_RenderTaskViewPage_vue__ = __webpack_require__(45);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__components_DocumentViewPage_vue__ = __webpack_require__(52);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__components_ClipScrollTreeViewPage_vue__ = __webpack_require__(56);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__components_ScreenshotPage_vue__ = __webpack_require__(60);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n name: 'app',\n components: {\n 'app-navbar': __WEBPACK_IMPORTED_MODULE_0__components_NavBar_vue__[\"a\" /* default */],\n 'app-navmenu': __WEBPACK_IMPORTED_MODULE_1__components_NavMenu_vue__[\"a\" /* default */],\n 'app-options': __WEBPACK_IMPORTED_MODULE_2__components_OptionsPage_vue__[\"a\" /* default */],\n 'app-passview': __WEBPACK_IMPORTED_MODULE_3__components_PassViewPage_vue__[\"a\" /* default */],\n 'app-rendertaskview': __WEBPACK_IMPORTED_MODULE_4__components_RenderTaskViewPage_vue__[\"a\" /* default */],\n 'app-documentview': __WEBPACK_IMPORTED_MODULE_5__components_DocumentViewPage_vue__[\"a\" /* default */],\n 'app-clipscrolltreeview': __WEBPACK_IMPORTED_MODULE_6__components_ClipScrollTreeViewPage_vue__[\"a\" /* default */],\n 'app-screenshotview': __WEBPACK_IMPORTED_MODULE_7__components_ScreenshotPage_vue__[\"a\" /* default */]\n },\n computed: {\n page: function page() {\n return this.$store.state.page;\n }\n }\n});\n\n/***/ }),\n/* 7 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n computed: {\n isConnected: function isConnected() {\n return this.$store.state.connected;\n }\n },\n methods: {\n connect: function connect() {\n this.$store.dispatch('connect');\n },\n disconnect: function disconnect() {\n this.$store.dispatch('disconnect');\n }\n }\n});\n\n/***/ }),\n/* 8 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n methods: {\n setPage: function setPage(name) {\n this.$store.commit('setPage', name);\n }\n },\n computed: {\n page: function page() {\n return this.$store.state.page;\n }\n }\n});\n\n/***/ }),\n/* 9 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n computed: {\n disabled: function disabled() {\n return !this.$store.state.connected;\n }\n },\n methods: {\n setProfiler: function setProfiler(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_profiler\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_profiler\");\n }\n },\n setTextureCacheDebugger: function setTextureCacheDebugger(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_texture_cache_debug\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_texture_cache_debug\");\n }\n },\n setRenderTargetDebugger: function setRenderTargetDebugger(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_render_target_debug\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_render_target_debug\");\n }\n },\n setAlphaRectsDebugger: function setAlphaRectsDebugger(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_alpha_rects_debug\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_alpha_rects_debug\");\n }\n },\n setGpuTimeQueries: function setGpuTimeQueries(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_gpu_time_queries\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_gpu_time_queries\");\n }\n },\n setGpuSampleQueries: function setGpuSampleQueries(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_gpu_sample_queries\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_gpu_sample_queries\");\n }\n },\n setOpaquePass: function setOpaquePass(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_opaque_pass\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_opaque_pass\");\n }\n },\n setAlphaPass: function setAlphaPass(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_alpha_pass\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_alpha_pass\");\n }\n },\n setClipMasks: function setClipMasks(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_clip_masks\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_clip_masks\");\n }\n },\n setTextPrims: function setTextPrims(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_text_prims\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_text_prims\");\n }\n },\n setGradientPrims: function setGradientPrims(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_gradient_prims\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_gradient_prims\");\n }\n }\n }\n});\n\n/***/ }),\n/* 10 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n methods: {\n fetch: function fetch() {\n this.$store.dispatch('sendMessage', \"fetch_passes\");\n }\n },\n computed: {\n disabled: function disabled() {\n return !this.$store.state.connected;\n },\n passes: function passes() {\n return this.$store.state.passes;\n }\n }\n});\n\n/***/ }),\n/* 11 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__TreeView_vue__ = __webpack_require__(5);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n components: {\n 'app-treeview': __WEBPACK_IMPORTED_MODULE_0__TreeView_vue__[\"a\" /* default */]\n },\n methods: {\n fetch: function fetch() {\n this.$store.dispatch('sendMessage', \"fetch_render_tasks\");\n }\n },\n computed: {\n disabled: function disabled() {\n return !this.$store.state.connected;\n },\n render_tasks: function render_tasks() {\n return this.$store.state.render_tasks;\n }\n }\n});\n\n/***/ }),\n/* 12 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n name: 'treeview',\n props: ['model'],\n data: function data() {\n return {\n open: false\n };\n },\n computed: {\n isFolder: function isFolder() {\n return this.model.children && this.model.children.length;\n }\n },\n methods: {\n toggle: function toggle() {\n if (this.isFolder) {\n this.open = !this.open;\n }\n }\n }\n});\n\n/***/ }),\n/* 13 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__TreeView_vue__ = __webpack_require__(5);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n components: {\n 'app-treeview': __WEBPACK_IMPORTED_MODULE_0__TreeView_vue__[\"a\" /* default */]\n },\n methods: {\n fetch: function fetch() {\n this.$store.dispatch('sendMessage', \"fetch_documents\");\n }\n },\n computed: {\n disabled: function disabled() {\n return !this.$store.state.connected;\n },\n documents: function documents() {\n return this.$store.state.documents;\n }\n }\n});\n\n/***/ }),\n/* 14 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__TreeView_vue__ = __webpack_require__(5);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n components: {\n 'app-treeview': __WEBPACK_IMPORTED_MODULE_0__TreeView_vue__[\"a\" /* default */]\n },\n methods: {\n fetch: function fetch() {\n this.$store.dispatch('sendMessage', \"fetch_clip_scroll_tree\");\n }\n },\n computed: {\n disabled: function disabled() {\n return !this.$store.state.connected;\n },\n clip_scroll_tree: function clip_scroll_tree() {\n return this.$store.state.clip_scroll_tree;\n }\n }\n});\n\n/***/ }),\n/* 15 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n computed: {\n disabled: function disabled() {\n return !this.$store.state.connected;\n },\n screenshot: function screenshot() {\n return this.$store.state.screenshot;\n }\n },\n methods: {\n fetch: function fetch() {\n this.$store.dispatch('sendMessage', \"fetch_screenshot\");\n }\n }\n});\n\n/***/ }),\n/* 16 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_vue__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_buefy__ = __webpack_require__(20);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_buefy___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_buefy__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_buefy_dist_buefy_css__ = __webpack_require__(21);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_buefy_dist_buefy_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_buefy_dist_buefy_css__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_vue_material_design_icons_styles_css__ = __webpack_require__(24);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3_vue_material_design_icons_styles_css___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3_vue_material_design_icons_styles_css__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__App_vue__ = __webpack_require__(26);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__store__ = __webpack_require__(65);\n\n\n\n\n\n\n\n__WEBPACK_IMPORTED_MODULE_0_vue__[\"default\"].use(__WEBPACK_IMPORTED_MODULE_1_buefy___default.a);\n\nnew __WEBPACK_IMPORTED_MODULE_0_vue__[\"default\"]({\n el: '#app',\n store: __WEBPACK_IMPORTED_MODULE_5__store__[\"a\" /* default */],\n render: function render(h) {\n return h(__WEBPACK_IMPORTED_MODULE_4__App_vue__[\"a\" /* default */]);\n }\n});\n\n/***/ }),\n/* 17 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* WEBPACK VAR INJECTION */(function(global) {var scope = (typeof global !== \"undefined\" && global) ||\n (typeof self !== \"undefined\" && self) ||\n window;\nvar apply = Function.prototype.apply;\n\n// DOM APIs, for completeness\n\nexports.setTimeout = function() {\n return new Timeout(apply.call(setTimeout, scope, arguments), clearTimeout);\n};\nexports.setInterval = function() {\n return new Timeout(apply.call(setInterval, scope, arguments), clearInterval);\n};\nexports.clearTimeout =\nexports.clearInterval = function(timeout) {\n if (timeout) {\n timeout.close();\n }\n};\n\nfunction Timeout(id, clearFn) {\n this._id = id;\n this._clearFn = clearFn;\n}\nTimeout.prototype.unref = Timeout.prototype.ref = function() {};\nTimeout.prototype.close = function() {\n this._clearFn.call(scope, this._id);\n};\n\n// Does not start the time, just sets up the members needed.\nexports.enroll = function(item, msecs) {\n clearTimeout(item._idleTimeoutId);\n item._idleTimeout = msecs;\n};\n\nexports.unenroll = function(item) {\n clearTimeout(item._idleTimeoutId);\n item._idleTimeout = -1;\n};\n\nexports._unrefActive = exports.active = function(item) {\n clearTimeout(item._idleTimeoutId);\n\n var msecs = item._idleTimeout;\n if (msecs >= 0) {\n item._idleTimeoutId = setTimeout(function onTimeout() {\n if (item._onTimeout)\n item._onTimeout();\n }, msecs);\n }\n};\n\n// setimmediate attaches itself to the global object\n__webpack_require__(18);\n// On some exotic environments, it's not clear which object `setimmediate` was\n// able to install onto. Search each possibility in the same order as the\n// `setimmediate` library.\nexports.setImmediate = (typeof self !== \"undefined\" && self.setImmediate) ||\n (typeof global !== \"undefined\" && global.setImmediate) ||\n (this && this.setImmediate);\nexports.clearImmediate = (typeof self !== \"undefined\" && self.clearImmediate) ||\n (typeof global !== \"undefined\" && global.clearImmediate) ||\n (this && this.clearImmediate);\n\n/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))\n\n/***/ }),\n/* 18 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* WEBPACK VAR INJECTION */(function(global, process) {(function (global, undefined) {\n \"use strict\";\n\n if (global.setImmediate) {\n return;\n }\n\n var nextHandle = 1; // Spec says greater than zero\n var tasksByHandle = {};\n var currentlyRunningATask = false;\n var doc = global.document;\n var registerImmediate;\n\n function setImmediate(callback) {\n // Callback can either be a function or a string\n if (typeof callback !== \"function\") {\n callback = new Function(\"\" + callback);\n }\n // Copy function arguments\n var args = new Array(arguments.length - 1);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i + 1];\n }\n // Store and register the task\n var task = { callback: callback, args: args };\n tasksByHandle[nextHandle] = task;\n registerImmediate(nextHandle);\n return nextHandle++;\n }\n\n function clearImmediate(handle) {\n delete tasksByHandle[handle];\n }\n\n function run(task) {\n var callback = task.callback;\n var args = task.args;\n switch (args.length) {\n case 0:\n callback();\n break;\n case 1:\n callback(args[0]);\n break;\n case 2:\n callback(args[0], args[1]);\n break;\n case 3:\n callback(args[0], args[1], args[2]);\n break;\n default:\n callback.apply(undefined, args);\n break;\n }\n }\n\n function runIfPresent(handle) {\n // From the spec: \"Wait until any invocations of this algorithm started before this one have completed.\"\n // So if we're currently running a task, we'll need to delay this invocation.\n if (currentlyRunningATask) {\n // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a\n // \"too much recursion\" error.\n setTimeout(runIfPresent, 0, handle);\n } else {\n var task = tasksByHandle[handle];\n if (task) {\n currentlyRunningATask = true;\n try {\n run(task);\n } finally {\n clearImmediate(handle);\n currentlyRunningATask = false;\n }\n }\n }\n }\n\n function installNextTickImplementation() {\n registerImmediate = function(handle) {\n process.nextTick(function () { runIfPresent(handle); });\n };\n }\n\n function canUsePostMessage() {\n // The test against `importScripts` prevents this implementation from being installed inside a web worker,\n // where `global.postMessage` means something completely different and can't be used for this purpose.\n if (global.postMessage && !global.importScripts) {\n var postMessageIsAsynchronous = true;\n var oldOnMessage = global.onmessage;\n global.onmessage = function() {\n postMessageIsAsynchronous = false;\n };\n global.postMessage(\"\", \"*\");\n global.onmessage = oldOnMessage;\n return postMessageIsAsynchronous;\n }\n }\n\n function installPostMessageImplementation() {\n // Installs an event handler on `global` for the `message` event: see\n // * https://developer.mozilla.org/en/DOM/window.postMessage\n // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n\n var messagePrefix = \"setImmediate$\" + Math.random() + \"$\";\n var onGlobalMessage = function(event) {\n if (event.source === global &&\n typeof event.data === \"string\" &&\n event.data.indexOf(messagePrefix) === 0) {\n runIfPresent(+event.data.slice(messagePrefix.length));\n }\n };\n\n if (global.addEventListener) {\n global.addEventListener(\"message\", onGlobalMessage, false);\n } else {\n global.attachEvent(\"onmessage\", onGlobalMessage);\n }\n\n registerImmediate = function(handle) {\n global.postMessage(messagePrefix + handle, \"*\");\n };\n }\n\n function installMessageChannelImplementation() {\n var channel = new MessageChannel();\n channel.port1.onmessage = function(event) {\n var handle = event.data;\n runIfPresent(handle);\n };\n\n registerImmediate = function(handle) {\n channel.port2.postMessage(handle);\n };\n }\n\n function installReadyStateChangeImplementation() {\n var html = doc.documentElement;\n registerImmediate = function(handle) {\n // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted\n // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.\n var script = doc.createElement(\"script\");\n script.onreadystatechange = function () {\n runIfPresent(handle);\n script.onreadystatechange = null;\n html.removeChild(script);\n script = null;\n };\n html.appendChild(script);\n };\n }\n\n function installSetTimeoutImplementation() {\n registerImmediate = function(handle) {\n setTimeout(runIfPresent, 0, handle);\n };\n }\n\n // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.\n var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);\n attachTo = attachTo && attachTo.setTimeout ? attachTo : global;\n\n // Don't get fooled by e.g. browserify environments.\n if ({}.toString.call(global.process) === \"[object process]\") {\n // For Node.js before 0.9\n installNextTickImplementation();\n\n } else if (canUsePostMessage()) {\n // For non-IE10 modern browsers\n installPostMessageImplementation();\n\n } else if (global.MessageChannel) {\n // For web workers, where supported\n installMessageChannelImplementation();\n\n } else if (doc && \"onreadystatechange\" in doc.createElement(\"script\")) {\n // For IE 6–8\n installReadyStateChangeImplementation();\n\n } else {\n // For older browsers\n installSetTimeoutImplementation();\n }\n\n attachTo.setImmediate = setImmediate;\n attachTo.clearImmediate = clearImmediate;\n}(typeof self === \"undefined\" ? typeof global === \"undefined\" ? this : global : self));\n\n/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4), __webpack_require__(19)))\n\n/***/ }),\n/* 19 */\n/***/ (function(module, exports) {\n\n// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n} ())\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch(e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch(e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n\n\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while(len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\n\n\n/***/ }),\n/* 20 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/*! Buefy v0.6.7 | MIT License | github.com/buefy/buefy */ \n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(true)\n\t\tmodule.exports = factory(__webpack_require__(3));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"vue\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Buefy\"] = factory(require(\"vue\"));\n\telse\n\t\troot[\"Buefy\"] = factory(root[\"Vue\"]);\n})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_23__) {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"/\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 68);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports) {\n\n/* globals __VUE_SSR_CONTEXT__ */\n\n// this module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle\n\nmodule.exports = function normalizeComponent (\n rawScriptExports,\n compiledTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier /* server only */\n) {\n var esModule\n var scriptExports = rawScriptExports = rawScriptExports || {}\n\n // ES6 modules interop\n var type = typeof rawScriptExports.default\n if (type === 'object' || type === 'function') {\n esModule = rawScriptExports\n scriptExports = rawScriptExports.default\n }\n\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (compiledTemplate) {\n options.render = compiledTemplate.render\n options.staticRenderFns = compiledTemplate.staticRenderFns\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = injectStyles\n }\n\n if (hook) {\n var functional = options.functional\n var existing = functional\n ? options.render\n : options.beforeCreate\n if (!functional) {\n // inject component registration as beforeCreate hook\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n } else {\n // register for functioal component in vue file\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return existing(h, context)\n }\n }\n }\n\n return {\n esModule: esModule,\n exports: scriptExports,\n options: options\n }\n}\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\n\nvar _defineProperty = __webpack_require__(100);\n\nvar _defineProperty2 = _interopRequireDefault(_defineProperty);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.default = function (obj, key, value) {\n if (key in obj) {\n (0, _defineProperty2.default)(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n\n return obj;\n};\n\n/***/ }),\n/* 2 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return setOptions; });\nvar config = {\n defaultContainerElement: null,\n defaultIconPack: 'mdi',\n defaultDialogConfirmText: null,\n defaultDialogCancelText: null,\n defaultSnackbarDuration: 3500,\n defaultToastDuration: 2000,\n defaultTooltipType: 'is-primary',\n defaultTooltipAnimated: false,\n defaultInputAutocomplete: 'on',\n defaultDateFormatter: null,\n defaultDateParser: null,\n defaultDayNames: null,\n defaultMonthNames: null,\n defaultFirstDayOfWeek: null,\n defaultUnselectableDaysOfWeek: null,\n defaultTimeFormatter: null,\n defaultTimeParser: null,\n defaultModalScroll: null,\n defaultDatepickerMobileNative: true,\n defaultTimepickerMobileNative: true,\n defaultNoticeQueue: true,\n defaultInputHasCounter: true\n};\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (config);\n\nvar setOptions = function setOptions(options) {\n config = options;\n};\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(104),\n /* template */\n __webpack_require__(105),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar store = __webpack_require__(34)('wks');\nvar uid = __webpack_require__(25);\nvar Symbol = __webpack_require__(7).Symbol;\nvar USE_SYMBOL = typeof Symbol == 'function';\n\nvar $exports = module.exports = function (name) {\n return store[name] || (store[name] =\n USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name));\n};\n\n$exports.store = store;\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(87), __esModule: true };\n\n/***/ }),\n/* 6 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (immutable) */ __webpack_exports__[\"b\"] = getValueByPath;\n/* harmony export (immutable) */ __webpack_exports__[\"c\"] = indexOf;\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"d\", function() { return isMobile; });\n/* harmony export (immutable) */ __webpack_exports__[\"e\"] = removeElement;\n/* harmony export (immutable) */ __webpack_exports__[\"a\"] = escapeRegExpChars;\n/**\r\n * Get value of an object property/path even if it's nested\r\n */\nfunction getValueByPath(obj, path) {\n var value = path.split('.').reduce(function (o, i) {\n return o[i];\n }, obj);\n return value;\n}\n\n/**\r\n * Extension of indexOf method by equality function if specified\r\n */\nfunction indexOf(array, obj, fn) {\n if (!array) return -1;\n\n if (!fn || typeof fn !== 'function') return array.indexOf(obj);\n\n for (var i = 0; i < array.length; i++) {\n if (fn(array[i], obj)) {\n return i;\n }\n }\n\n return -1;\n}\n\n/**\r\n * Mobile detection\r\n * https://www.abeautifulsite.net/detecting-mobile-devices-with-javascript\r\n */\nvar isMobile = {\n Android: function Android() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/Android/i);\n },\n BlackBerry: function BlackBerry() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/BlackBerry/i);\n },\n iOS: function iOS() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/iPhone|iPad|iPod/i);\n },\n Opera: function Opera() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/Opera Mini/i);\n },\n Windows: function Windows() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/IEMobile/i);\n },\n any: function any() {\n return isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows();\n }\n};\n\nfunction removeElement(el) {\n if (typeof el.remove !== 'undefined') {\n el.remove();\n } else {\n el.parentNode.removeChild(el);\n }\n}\n\n/**\r\n * Escape regex characters\r\n * http://stackoverflow.com/a/6969486\r\n */\nfunction escapeRegExpChars(value) {\n if (!value) return value;\n\n // eslint-disable-next-line\n return value.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, '\\\\$&');\n}\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports) {\n\n// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028\nvar global = module.exports = typeof window != 'undefined' && window.Math == Math\n ? window : typeof self != 'undefined' && self.Math == Math ? self\n // eslint-disable-next-line no-new-func\n : Function('return this')();\nif (typeof __g == 'number') __g = global; // eslint-disable-line no-undef\n\n\n/***/ }),\n/* 8 */\n/***/ (function(module, exports) {\n\nvar core = module.exports = { version: '2.5.5' };\nif (typeof __e == 'number') __e = core; // eslint-disable-line no-undef\n\n\n/***/ }),\n/* 9 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar anObject = __webpack_require__(16);\nvar IE8_DOM_DEFINE = __webpack_require__(47);\nvar toPrimitive = __webpack_require__(29);\nvar dP = Object.defineProperty;\n\nexports.f = __webpack_require__(10) ? Object.defineProperty : function defineProperty(O, P, Attributes) {\n anObject(O);\n P = toPrimitive(P, true);\n anObject(Attributes);\n if (IE8_DOM_DEFINE) try {\n return dP(O, P, Attributes);\n } catch (e) { /* empty */ }\n if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!');\n if ('value' in Attributes) O[P] = Attributes.value;\n return O;\n};\n\n\n/***/ }),\n/* 10 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// Thank's IE8 for his funny defineProperty\nmodule.exports = !__webpack_require__(20)(function () {\n return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7;\n});\n\n\n/***/ }),\n/* 11 */\n/***/ (function(module, exports) {\n\nvar hasOwnProperty = {}.hasOwnProperty;\nmodule.exports = function (it, key) {\n return hasOwnProperty.call(it, key);\n};\n\n\n/***/ }),\n/* 12 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__BaseElementMixin__ = __webpack_require__(13);\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n mixins: [__WEBPACK_IMPORTED_MODULE_0__BaseElementMixin__[\"a\" /* default */]],\n props: {\n size: String,\n expanded: Boolean,\n loading: Boolean,\n rounded: Boolean,\n icon: String,\n // Native options to use in HTML5 validation\n autocomplete: String,\n maxlength: [Number, String]\n },\n data: function data() {\n return {\n isValid: true,\n isFocused: false\n };\n },\n\n computed: {\n /**\r\n * Find parent Field, max 3 levels deep.\r\n */\n parentField: function parentField() {\n var parent = this.$parent;\n for (var i = 0; i < 3; i++) {\n if (parent && !parent.$data._isField) {\n parent = parent.$parent;\n }\n }\n return parent;\n },\n\n\n /**\r\n * Get the type prop from parent if it's a Field.\r\n */\n statusType: function statusType() {\n if (!this.parentField) return;\n\n return this.parentField.newType;\n },\n\n\n /**\r\n * Get the message prop from parent if it's a Field.\r\n */\n statusMessage: function statusMessage() {\n if (!this.parentField) return;\n\n return this.parentField.newMessage;\n },\n\n\n /**\r\n * Fix icon size for inputs, large was too big\r\n */\n iconSize: function iconSize() {\n switch (this.size) {\n case 'is-small':\n return this.size;\n case 'is-medium':\n return;\n case 'is-large':\n return this.newIconPack === 'mdi' ? 'is-medium' : '';\n }\n }\n },\n methods: {\n /**\r\n * Focus method that work dynamically depending on the component.\r\n */\n focus: function focus() {\n var _this = this;\n\n if (this.$data._elementRef === undefined) return;\n\n this.$nextTick(function () {\n return _this.$el.querySelector(_this.$data._elementRef).focus();\n });\n },\n onBlur: function onBlur($event) {\n this.isFocused = false;\n this.$emit('blur', $event);\n this.checkHtml5Validity();\n },\n onFocus: function onFocus($event) {\n this.isFocused = true;\n this.$emit('focus', $event);\n },\n\n\n /**\r\n * Check HTML5 validation, set isValid property.\r\n * If validation fail, send 'is-danger' type,\r\n * and error message to parent if it's a Field.\r\n */\n checkHtml5Validity: function checkHtml5Validity() {\n if (this.$refs[this.$data._elementRef] === undefined) return;\n\n var el = this.$el.querySelector(this.$data._elementRef);\n\n var type = null;\n var message = null;\n var isValid = true;\n if (!el.checkValidity()) {\n type = 'is-danger';\n message = el.validationMessage;\n isValid = false;\n }\n this.isValid = isValid;\n\n if (this.parentField) {\n // Set type only if not defined\n if (!this.parentField.type) {\n this.parentField.newType = type;\n }\n // Set message only if not defined\n if (!this.parentField.message) {\n this.parentField.newMessage = message;\n }\n }\n\n return this.isValid;\n }\n }\n});\n\n/***/ }),\n/* 13 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__config__ = __webpack_require__(2);\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n props: {\n iconPack: String\n },\n data: function data() {\n return {\n newIconPack: this.iconPack || __WEBPACK_IMPORTED_MODULE_0__config__[\"a\" /* default */].defaultIconPack\n };\n }\n});\n\n/***/ }),\n/* 14 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(69), __esModule: true };\n\n/***/ }),\n/* 15 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar dP = __webpack_require__(9);\nvar createDesc = __webpack_require__(21);\nmodule.exports = __webpack_require__(10) ? function (object, key, value) {\n return dP.f(object, key, createDesc(1, value));\n} : function (object, key, value) {\n object[key] = value;\n return object;\n};\n\n\n/***/ }),\n/* 16 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isObject = __webpack_require__(19);\nmodule.exports = function (it) {\n if (!isObject(it)) throw TypeError(it + ' is not an object!');\n return it;\n};\n\n\n/***/ }),\n/* 17 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// to indexed object, toObject with fallback for non-array-like ES3 strings\nvar IObject = __webpack_require__(50);\nvar defined = __webpack_require__(31);\nmodule.exports = function (it) {\n return IObject(defined(it));\n};\n\n\n/***/ }),\n/* 18 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar global = __webpack_require__(7);\nvar core = __webpack_require__(8);\nvar ctx = __webpack_require__(46);\nvar hide = __webpack_require__(15);\nvar has = __webpack_require__(11);\nvar PROTOTYPE = 'prototype';\n\nvar $export = function (type, name, source) {\n var IS_FORCED = type & $export.F;\n var IS_GLOBAL = type & $export.G;\n var IS_STATIC = type & $export.S;\n var IS_PROTO = type & $export.P;\n var IS_BIND = type & $export.B;\n var IS_WRAP = type & $export.W;\n var exports = IS_GLOBAL ? core : core[name] || (core[name] = {});\n var expProto = exports[PROTOTYPE];\n var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE];\n var key, own, out;\n if (IS_GLOBAL) source = name;\n for (key in source) {\n // contains in native\n own = !IS_FORCED && target && target[key] !== undefined;\n if (own && has(exports, key)) continue;\n // export native or passed\n out = own ? target[key] : source[key];\n // prevent global pollution for namespaces\n exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key]\n // bind timers to global for call from export context\n : IS_BIND && own ? ctx(out, global)\n // wrap global constructors for prevent change them in library\n : IS_WRAP && target[key] == out ? (function (C) {\n var F = function (a, b, c) {\n if (this instanceof C) {\n switch (arguments.length) {\n case 0: return new C();\n case 1: return new C(a);\n case 2: return new C(a, b);\n } return new C(a, b, c);\n } return C.apply(this, arguments);\n };\n F[PROTOTYPE] = C[PROTOTYPE];\n return F;\n // make static versions for prototype methods\n })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;\n // export proto methods to core.%CONSTRUCTOR%.methods.%NAME%\n if (IS_PROTO) {\n (exports.virtual || (exports.virtual = {}))[key] = out;\n // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME%\n if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out);\n }\n }\n};\n// type bitmap\n$export.F = 1; // forced\n$export.G = 2; // global\n$export.S = 4; // static\n$export.P = 8; // proto\n$export.B = 16; // bind\n$export.W = 32; // wrap\n$export.U = 64; // safe\n$export.R = 128; // real proto method for `library`\nmodule.exports = $export;\n\n\n/***/ }),\n/* 19 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (it) {\n return typeof it === 'object' ? it !== null : typeof it === 'function';\n};\n\n\n/***/ }),\n/* 20 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (exec) {\n try {\n return !!exec();\n } catch (e) {\n return true;\n }\n};\n\n\n/***/ }),\n/* 21 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (bitmap, value) {\n return {\n enumerable: !(bitmap & 1),\n configurable: !(bitmap & 2),\n writable: !(bitmap & 4),\n value: value\n };\n};\n\n\n/***/ }),\n/* 22 */\n/***/ (function(module, exports) {\n\nmodule.exports = {};\n\n\n/***/ }),\n/* 23 */\n/***/ (function(module, exports) {\n\nmodule.exports = __WEBPACK_EXTERNAL_MODULE_23__;\n\n/***/ }),\n/* 24 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.2.14 / 15.2.3.14 Object.keys(O)\nvar $keys = __webpack_require__(49);\nvar enumBugKeys = __webpack_require__(35);\n\nmodule.exports = Object.keys || function keys(O) {\n return $keys(O, enumBugKeys);\n};\n\n\n/***/ }),\n/* 25 */\n/***/ (function(module, exports) {\n\nvar id = 0;\nvar px = Math.random();\nmodule.exports = function (key) {\n return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36));\n};\n\n\n/***/ }),\n/* 26 */\n/***/ (function(module, exports) {\n\nexports.f = {}.propertyIsEnumerable;\n\n\n/***/ }),\n/* 27 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(103),\n /* template */\n __webpack_require__(106),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 28 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(126),\n /* template */\n __webpack_require__(127),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 29 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 7.1.1 ToPrimitive(input [, PreferredType])\nvar isObject = __webpack_require__(19);\n// instead of the ES6 spec version, we didn't implement @@toPrimitive case\n// and the second argument - flag - preferred type is a string\nmodule.exports = function (it, S) {\n if (!isObject(it)) return it;\n var fn, val;\n if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val;\n if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val;\n if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val;\n throw TypeError(\"Can't convert object to primitive value\");\n};\n\n\n/***/ }),\n/* 30 */\n/***/ (function(module, exports) {\n\nvar toString = {}.toString;\n\nmodule.exports = function (it) {\n return toString.call(it).slice(8, -1);\n};\n\n\n/***/ }),\n/* 31 */\n/***/ (function(module, exports) {\n\n// 7.2.1 RequireObjectCoercible(argument)\nmodule.exports = function (it) {\n if (it == undefined) throw TypeError(\"Can't call method on \" + it);\n return it;\n};\n\n\n/***/ }),\n/* 32 */\n/***/ (function(module, exports) {\n\n// 7.1.4 ToInteger\nvar ceil = Math.ceil;\nvar floor = Math.floor;\nmodule.exports = function (it) {\n return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);\n};\n\n\n/***/ }),\n/* 33 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar shared = __webpack_require__(34)('keys');\nvar uid = __webpack_require__(25);\nmodule.exports = function (key) {\n return shared[key] || (shared[key] = uid(key));\n};\n\n\n/***/ }),\n/* 34 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar global = __webpack_require__(7);\nvar SHARED = '__core-js_shared__';\nvar store = global[SHARED] || (global[SHARED] = {});\nmodule.exports = function (key) {\n return store[key] || (store[key] = {});\n};\n\n\n/***/ }),\n/* 35 */\n/***/ (function(module, exports) {\n\n// IE 8- don't enum bug keys\nmodule.exports = (\n 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'\n).split(',');\n\n\n/***/ }),\n/* 36 */\n/***/ (function(module, exports) {\n\nexports.f = Object.getOwnPropertySymbols;\n\n\n/***/ }),\n/* 37 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 7.1.13 ToObject(argument)\nvar defined = __webpack_require__(31);\nmodule.exports = function (it) {\n return Object(defined(it));\n};\n\n\n/***/ }),\n/* 38 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar $at = __webpack_require__(79)(true);\n\n// 21.1.3.27 String.prototype[@@iterator]()\n__webpack_require__(54)(String, 'String', function (iterated) {\n this._t = String(iterated); // target\n this._i = 0; // next index\n// 21.1.5.2.1 %StringIteratorPrototype%.next()\n}, function () {\n var O = this._t;\n var index = this._i;\n var point;\n if (index >= O.length) return { value: undefined, done: true };\n point = $at(O, index);\n this._i += point.length;\n return { value: point, done: false };\n});\n\n\n/***/ }),\n/* 39 */\n/***/ (function(module, exports) {\n\nmodule.exports = true;\n\n\n/***/ }),\n/* 40 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar def = __webpack_require__(9).f;\nvar has = __webpack_require__(11);\nvar TAG = __webpack_require__(4)('toStringTag');\n\nmodule.exports = function (it, tag, stat) {\n if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag });\n};\n\n\n/***/ }),\n/* 41 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports.f = __webpack_require__(4);\n\n\n/***/ }),\n/* 42 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar global = __webpack_require__(7);\nvar core = __webpack_require__(8);\nvar LIBRARY = __webpack_require__(39);\nvar wksExt = __webpack_require__(41);\nvar defineProperty = __webpack_require__(9).f;\nmodule.exports = function (name) {\n var $Symbol = core.Symbol || (core.Symbol = LIBRARY ? {} : global.Symbol || {});\n if (name.charAt(0) != '_' && !(name in $Symbol)) defineProperty($Symbol, name, { value: wksExt.f(name) });\n};\n\n\n/***/ }),\n/* 43 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(118),\n /* template */\n __webpack_require__(119),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 44 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(120),\n /* template */\n __webpack_require__(121),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 45 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(122),\n /* template */\n __webpack_require__(125),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 46 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// optional / simple context binding\nvar aFunction = __webpack_require__(71);\nmodule.exports = function (fn, that, length) {\n aFunction(fn);\n if (that === undefined) return fn;\n switch (length) {\n case 1: return function (a) {\n return fn.call(that, a);\n };\n case 2: return function (a, b) {\n return fn.call(that, a, b);\n };\n case 3: return function (a, b, c) {\n return fn.call(that, a, b, c);\n };\n }\n return function (/* ...args */) {\n return fn.apply(that, arguments);\n };\n};\n\n\n/***/ }),\n/* 47 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = !__webpack_require__(10) && !__webpack_require__(20)(function () {\n return Object.defineProperty(__webpack_require__(48)('div'), 'a', { get: function () { return 7; } }).a != 7;\n});\n\n\n/***/ }),\n/* 48 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isObject = __webpack_require__(19);\nvar document = __webpack_require__(7).document;\n// typeof document.createElement is 'object' in old IE\nvar is = isObject(document) && isObject(document.createElement);\nmodule.exports = function (it) {\n return is ? document.createElement(it) : {};\n};\n\n\n/***/ }),\n/* 49 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar has = __webpack_require__(11);\nvar toIObject = __webpack_require__(17);\nvar arrayIndexOf = __webpack_require__(73)(false);\nvar IE_PROTO = __webpack_require__(33)('IE_PROTO');\n\nmodule.exports = function (object, names) {\n var O = toIObject(object);\n var i = 0;\n var result = [];\n var key;\n for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key);\n // Don't enum bug & hidden keys\n while (names.length > i) if (has(O, key = names[i++])) {\n ~arrayIndexOf(result, key) || result.push(key);\n }\n return result;\n};\n\n\n/***/ }),\n/* 50 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// fallback for non-array-like ES3 and non-enumerable old V8 strings\nvar cof = __webpack_require__(30);\n// eslint-disable-next-line no-prototype-builtins\nmodule.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) {\n return cof(it) == 'String' ? it.split('') : Object(it);\n};\n\n\n/***/ }),\n/* 51 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 7.1.15 ToLength\nvar toInteger = __webpack_require__(32);\nvar min = Math.min;\nmodule.exports = function (it) {\n return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991\n};\n\n\n/***/ }),\n/* 52 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(76),\n /* template */\n __webpack_require__(107),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 53 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\n\nvar _iterator = __webpack_require__(77);\n\nvar _iterator2 = _interopRequireDefault(_iterator);\n\nvar _symbol = __webpack_require__(5);\n\nvar _symbol2 = _interopRequireDefault(_symbol);\n\nvar _typeof = typeof _symbol2.default === \"function\" && typeof _iterator2.default === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof _symbol2.default === \"function\" && obj.constructor === _symbol2.default && obj !== _symbol2.default.prototype ? \"symbol\" : typeof obj; };\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.default = typeof _symbol2.default === \"function\" && _typeof(_iterator2.default) === \"symbol\" ? function (obj) {\n return typeof obj === \"undefined\" ? \"undefined\" : _typeof(obj);\n} : function (obj) {\n return obj && typeof _symbol2.default === \"function\" && obj.constructor === _symbol2.default && obj !== _symbol2.default.prototype ? \"symbol\" : typeof obj === \"undefined\" ? \"undefined\" : _typeof(obj);\n};\n\n/***/ }),\n/* 54 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar LIBRARY = __webpack_require__(39);\nvar $export = __webpack_require__(18);\nvar redefine = __webpack_require__(55);\nvar hide = __webpack_require__(15);\nvar Iterators = __webpack_require__(22);\nvar $iterCreate = __webpack_require__(80);\nvar setToStringTag = __webpack_require__(40);\nvar getPrototypeOf = __webpack_require__(83);\nvar ITERATOR = __webpack_require__(4)('iterator');\nvar BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next`\nvar FF_ITERATOR = '@@iterator';\nvar KEYS = 'keys';\nvar VALUES = 'values';\n\nvar returnThis = function () { return this; };\n\nmodule.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) {\n $iterCreate(Constructor, NAME, next);\n var getMethod = function (kind) {\n if (!BUGGY && kind in proto) return proto[kind];\n switch (kind) {\n case KEYS: return function keys() { return new Constructor(this, kind); };\n case VALUES: return function values() { return new Constructor(this, kind); };\n } return function entries() { return new Constructor(this, kind); };\n };\n var TAG = NAME + ' Iterator';\n var DEF_VALUES = DEFAULT == VALUES;\n var VALUES_BUG = false;\n var proto = Base.prototype;\n var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];\n var $default = $native || getMethod(DEFAULT);\n var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;\n var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;\n var methods, key, IteratorPrototype;\n // Fix native\n if ($anyNative) {\n IteratorPrototype = getPrototypeOf($anyNative.call(new Base()));\n if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) {\n // Set @@toStringTag to native iterators\n setToStringTag(IteratorPrototype, TAG, true);\n // fix for some old engines\n if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis);\n }\n }\n // fix Array#{values, @@iterator}.name in V8 / FF\n if (DEF_VALUES && $native && $native.name !== VALUES) {\n VALUES_BUG = true;\n $default = function values() { return $native.call(this); };\n }\n // Define iterator\n if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) {\n hide(proto, ITERATOR, $default);\n }\n // Plug for library\n Iterators[NAME] = $default;\n Iterators[TAG] = returnThis;\n if (DEFAULT) {\n methods = {\n values: DEF_VALUES ? $default : getMethod(VALUES),\n keys: IS_SET ? $default : getMethod(KEYS),\n entries: $entries\n };\n if (FORCED) for (key in methods) {\n if (!(key in proto)) redefine(proto, key, methods[key]);\n } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods);\n }\n return methods;\n};\n\n\n/***/ }),\n/* 55 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = __webpack_require__(15);\n\n\n/***/ }),\n/* 56 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])\nvar anObject = __webpack_require__(16);\nvar dPs = __webpack_require__(81);\nvar enumBugKeys = __webpack_require__(35);\nvar IE_PROTO = __webpack_require__(33)('IE_PROTO');\nvar Empty = function () { /* empty */ };\nvar PROTOTYPE = 'prototype';\n\n// Create object with fake `null` prototype: use iframe Object with cleared prototype\nvar createDict = function () {\n // Thrash, waste and sodomy: IE GC bug\n var iframe = __webpack_require__(48)('iframe');\n var i = enumBugKeys.length;\n var lt = '<';\n var gt = '>';\n var iframeDocument;\n iframe.style.display = 'none';\n __webpack_require__(82).appendChild(iframe);\n iframe.src = 'javascript:'; // eslint-disable-line no-script-url\n // createDict = iframe.contentWindow.Object;\n // html.removeChild(iframe);\n iframeDocument = iframe.contentWindow.document;\n iframeDocument.open();\n iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt);\n iframeDocument.close();\n createDict = iframeDocument.F;\n while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]];\n return createDict();\n};\n\nmodule.exports = Object.create || function create(O, Properties) {\n var result;\n if (O !== null) {\n Empty[PROTOTYPE] = anObject(O);\n result = new Empty();\n Empty[PROTOTYPE] = null;\n // add \"__proto__\" for Object.getPrototypeOf polyfill\n result[IE_PROTO] = O;\n } else result = createDict();\n return Properties === undefined ? result : dPs(result, Properties);\n};\n\n\n/***/ }),\n/* 57 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(84);\nvar global = __webpack_require__(7);\nvar hide = __webpack_require__(15);\nvar Iterators = __webpack_require__(22);\nvar TO_STRING_TAG = __webpack_require__(4)('toStringTag');\n\nvar DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' +\n 'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' +\n 'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' +\n 'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' +\n 'TextTrackList,TouchList').split(',');\n\nfor (var i = 0; i < DOMIterables.length; i++) {\n var NAME = DOMIterables[i];\n var Collection = global[NAME];\n var proto = Collection && Collection.prototype;\n if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME);\n Iterators[NAME] = Iterators.Array;\n}\n\n\n/***/ }),\n/* 58 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.2.7 / 15.2.3.4 Object.getOwnPropertyNames(O)\nvar $keys = __webpack_require__(49);\nvar hiddenKeys = __webpack_require__(35).concat('length', 'prototype');\n\nexports.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {\n return $keys(O, hiddenKeys);\n};\n\n\n/***/ }),\n/* 59 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(97), __esModule: true };\n\n/***/ }),\n/* 60 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar classof = __webpack_require__(99);\nvar ITERATOR = __webpack_require__(4)('iterator');\nvar Iterators = __webpack_require__(22);\nmodule.exports = __webpack_require__(8).getIteratorMethod = function (it) {\n if (it != undefined) return it[ITERATOR]\n || it['@@iterator']\n || Iterators[classof(it)];\n};\n\n\n/***/ }),\n/* 61 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(108),\n /* template */\n __webpack_require__(109),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 62 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(137),\n /* template */\n __webpack_require__(138),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 63 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__components_icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__BaseElementMixin__ = __webpack_require__(13);\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n mixins: [__WEBPACK_IMPORTED_MODULE_2__BaseElementMixin__[\"a\" /* default */]],\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__components_icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__components_icon_Icon___default.a),\n props: {\n active: {\n type: Boolean,\n default: true\n },\n title: String,\n closable: {\n type: Boolean,\n default: true\n },\n type: String,\n hasIcon: Boolean,\n size: String,\n iconSize: String,\n autoClose: {\n type: Boolean,\n default: false\n },\n duration: {\n type: Number,\n default: 5000\n }\n },\n data: function data() {\n return {\n isActive: this.active\n };\n },\n\n watch: {\n active: function active(value) {\n this.isActive = value;\n },\n isActive: function isActive(value) {\n if (value) {\n this.setAutoClose();\n } else {\n if (this.timer) {\n clearTimeout(this.timer);\n }\n }\n }\n },\n computed: {\n /**\r\n * Icon name (MDI) based on type.\r\n */\n icon: function icon() {\n switch (this.type) {\n case 'is-info':\n return 'information';\n case 'is-success':\n return 'check-circle';\n case 'is-warning':\n return 'alert';\n case 'is-danger':\n return 'alert-circle';\n default:\n return null;\n }\n }\n },\n methods: {\n /**\r\n * Close the Message and emit events.\r\n */\n close: function close() {\n this.isActive = false;\n this.$emit('close');\n this.$emit('update:active', false);\n },\n\n /**\r\n * Set timer to auto close message\r\n */\n setAutoClose: function setAutoClose() {\n var _this = this;\n\n if (this.autoClose) {\n this.timer = setTimeout(function () {\n if (_this.isActive) {\n _this.close();\n }\n }, this.duration);\n }\n }\n },\n mounted: function mounted() {\n this.setAutoClose();\n }\n});\n\n/***/ }),\n/* 64 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(149),\n /* template */\n __webpack_require__(150),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 65 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__helpers__ = __webpack_require__(6);\n\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n props: {\n type: {\n type: String,\n default: 'is-dark'\n },\n message: String,\n duration: Number,\n queue: {\n type: Boolean,\n default: undefined\n },\n position: {\n type: String,\n default: 'is-top',\n validator: function validator(value) {\n return ['is-top-right', 'is-top', 'is-top-left', 'is-bottom-right', 'is-bottom', 'is-bottom-left'].indexOf(value) > -1;\n }\n },\n container: String\n },\n data: function data() {\n return {\n isActive: false,\n parentTop: null,\n parentBottom: null,\n newContainer: this.container || __WEBPACK_IMPORTED_MODULE_0__config__[\"a\" /* default */].defaultContainerElement\n };\n },\n\n computed: {\n correctParent: function correctParent() {\n switch (this.position) {\n case 'is-top-right':\n case 'is-top':\n case 'is-top-left':\n return this.parentTop;\n\n case 'is-bottom-right':\n case 'is-bottom':\n case 'is-bottom-left':\n return this.parentBottom;\n }\n },\n transition: function transition() {\n switch (this.position) {\n case 'is-top-right':\n case 'is-top':\n case 'is-top-left':\n return {\n enter: 'fadeInDown',\n leave: 'fadeOut'\n };\n case 'is-bottom-right':\n case 'is-bottom':\n case 'is-bottom-left':\n return {\n enter: 'fadeInUp',\n leave: 'fadeOut'\n };\n }\n }\n },\n methods: {\n shouldQueue: function shouldQueue() {\n var queue = this.queue !== undefined ? this.queue : __WEBPACK_IMPORTED_MODULE_0__config__[\"a\" /* default */].defaultNoticeQueue;\n\n if (!queue) return false;\n\n return this.parentTop.childElementCount > 0 || this.parentBottom.childElementCount > 0;\n },\n close: function close() {\n var _this = this;\n\n clearTimeout(this.timer);\n this.isActive = false;\n\n // Timeout for the animation complete before destroying\n setTimeout(function () {\n _this.$destroy();\n Object(__WEBPACK_IMPORTED_MODULE_1__helpers__[\"e\" /* removeElement */])(_this.$el);\n }, 150);\n },\n showNotice: function showNotice() {\n var _this2 = this;\n\n if (this.shouldQueue()) {\n // Call recursively if should queue\n setTimeout(function () {\n return _this2.showNotice();\n }, 250);\n return;\n }\n this.correctParent.insertAdjacentElement('afterbegin', this.$el);\n this.isActive = true;\n\n if (!this.indefinite) {\n this.timer = setTimeout(function () {\n return _this2.close();\n }, this.newDuration);\n }\n },\n setupContainer: function setupContainer() {\n this.parentTop = document.querySelector('.notices.is-top');\n this.parentBottom = document.querySelector('.notices.is-bottom');\n\n if (this.parentTop && this.parentBottom) return;\n\n if (!this.parentTop) {\n this.parentTop = document.createElement('div');\n this.parentTop.className = 'notices is-top';\n }\n\n if (!this.parentBottom) {\n this.parentBottom = document.createElement('div');\n this.parentBottom.className = 'notices is-bottom';\n }\n\n var container = document.querySelector(this.newContainer) || document.body;\n\n container.appendChild(this.parentTop);\n container.appendChild(this.parentBottom);\n\n if (this.newContainer) {\n this.parentTop.classList.add('has-custom-container');\n this.parentBottom.classList.add('has-custom-container');\n }\n }\n },\n beforeMount: function beforeMount() {\n this.setupContainer();\n },\n mounted: function mounted() {\n this.showNotice();\n }\n});\n\n/***/ }),\n/* 66 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(179),\n /* template */\n __webpack_require__(180),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 67 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(188),\n /* template */\n __webpack_require__(189),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 68 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\nvar components_namespaceObject = {};\n__webpack_require__.d(components_namespaceObject, \"Autocomplete\", function() { return autocomplete; });\n__webpack_require__.d(components_namespaceObject, \"Checkbox\", function() { return components_checkbox; });\n__webpack_require__.d(components_namespaceObject, \"Collapse\", function() { return collapse; });\n__webpack_require__.d(components_namespaceObject, \"Datepicker\", function() { return datepicker; });\n__webpack_require__.d(components_namespaceObject, \"Dialog\", function() { return dialog; });\n__webpack_require__.d(components_namespaceObject, \"Dropdown\", function() { return dropdown; });\n__webpack_require__.d(components_namespaceObject, \"Field\", function() { return field; });\n__webpack_require__.d(components_namespaceObject, \"Icon\", function() { return icon; });\n__webpack_require__.d(components_namespaceObject, \"Input\", function() { return input; });\n__webpack_require__.d(components_namespaceObject, \"Loading\", function() { return loading; });\n__webpack_require__.d(components_namespaceObject, \"Message\", function() { return components_message; });\n__webpack_require__.d(components_namespaceObject, \"Modal\", function() { return modal; });\n__webpack_require__.d(components_namespaceObject, \"Notification\", function() { return notification; });\n__webpack_require__.d(components_namespaceObject, \"Pagination\", function() { return pagination; });\n__webpack_require__.d(components_namespaceObject, \"Panel\", function() { return panel; });\n__webpack_require__.d(components_namespaceObject, \"Radio\", function() { return components_radio; });\n__webpack_require__.d(components_namespaceObject, \"Select\", function() { return components_select; });\n__webpack_require__.d(components_namespaceObject, \"Snackbar\", function() { return snackbar; });\n__webpack_require__.d(components_namespaceObject, \"Switch\", function() { return components_switch; });\n__webpack_require__.d(components_namespaceObject, \"Table\", function() { return table; });\n__webpack_require__.d(components_namespaceObject, \"Tabs\", function() { return tabs; });\n__webpack_require__.d(components_namespaceObject, \"Tag\", function() { return tag; });\n__webpack_require__.d(components_namespaceObject, \"Taginput\", function() { return taginput; });\n__webpack_require__.d(components_namespaceObject, \"Timepicker\", function() { return timepicker; });\n__webpack_require__.d(components_namespaceObject, \"Toast\", function() { return toast; });\n__webpack_require__.d(components_namespaceObject, \"Tooltip\", function() { return tooltip; });\n__webpack_require__.d(components_namespaceObject, \"Upload\", function() { return upload; });\n\n// EXTERNAL MODULE: ./node_modules/babel-runtime/core-js/object/assign.js\nvar object_assign = __webpack_require__(14);\nvar assign_default = /*#__PURE__*/__webpack_require__.n(object_assign);\n\n// EXTERNAL MODULE: ./src/scss/buefy-build.scss\nvar buefy_build = __webpack_require__(75);\nvar buefy_build_default = /*#__PURE__*/__webpack_require__.n(buefy_build);\n\n// EXTERNAL MODULE: ./src/components/autocomplete/Autocomplete.vue\nvar Autocomplete = __webpack_require__(52);\nvar Autocomplete_default = /*#__PURE__*/__webpack_require__.n(Autocomplete);\n\n// CONCATENATED MODULE: ./src/utils/plugins.js\n\nvar use = function use(plugin) {\n if (typeof window !== 'undefined' && window.Vue) {\n window.Vue.use(plugin);\n }\n};\n\nvar registerComponent = function registerComponent(Vue, component) {\n Vue.component(component.name, component);\n};\n\nvar registerComponentProgrammatic = function registerComponentProgrammatic(Vue, property, component) {\n Vue.prototype[property] = component;\n};\n// CONCATENATED MODULE: ./src/components/autocomplete/index.js\n\n\n\n\nvar Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Autocomplete_default.a);\n }\n};\n\nuse(Plugin);\n\n/* harmony default export */ var autocomplete = (Plugin);\n// EXTERNAL MODULE: ./src/components/checkbox/Checkbox.vue\nvar Checkbox = __webpack_require__(61);\nvar Checkbox_default = /*#__PURE__*/__webpack_require__.n(Checkbox);\n\n// EXTERNAL MODULE: ./src/components/checkbox/CheckboxButton.vue\nvar CheckboxButton = __webpack_require__(110);\nvar CheckboxButton_default = /*#__PURE__*/__webpack_require__.n(CheckboxButton);\n\n// CONCATENATED MODULE: ./src/components/checkbox/index.js\n\n\n\n\n\nvar checkbox_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Checkbox_default.a);\n registerComponent(Vue, CheckboxButton_default.a);\n }\n};\n\nuse(checkbox_Plugin);\n\n/* harmony default export */ var components_checkbox = (checkbox_Plugin);\n// EXTERNAL MODULE: ./src/components/collapse/Collapse.vue\nvar Collapse = __webpack_require__(113);\nvar Collapse_default = /*#__PURE__*/__webpack_require__.n(Collapse);\n\n// CONCATENATED MODULE: ./src/components/collapse/index.js\n\n\n\n\nvar collapse_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Collapse_default.a);\n }\n};\n\nuse(collapse_Plugin);\n\n/* harmony default export */ var collapse = (collapse_Plugin);\n// EXTERNAL MODULE: ./src/components/datepicker/Datepicker.vue\nvar Datepicker = __webpack_require__(116);\nvar Datepicker_default = /*#__PURE__*/__webpack_require__.n(Datepicker);\n\n// CONCATENATED MODULE: ./src/components/datepicker/index.js\n\n\n\n\nvar datepicker_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Datepicker_default.a);\n }\n};\n\nuse(datepicker_Plugin);\n\n/* harmony default export */ var datepicker = (datepicker_Plugin);\n// EXTERNAL MODULE: external {\"commonjs\":\"vue\",\"commonjs2\":\"vue\",\"amd\":\"vue\",\"root\":\"Vue\"}\nvar external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue__ = __webpack_require__(23);\nvar external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default = /*#__PURE__*/__webpack_require__.n(external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue__);\n\n// EXTERNAL MODULE: ./src/components/dialog/Dialog.vue\nvar Dialog = __webpack_require__(135);\nvar Dialog_default = /*#__PURE__*/__webpack_require__.n(Dialog);\n\n// CONCATENATED MODULE: ./src/components/dialog/index.js\n\n\n\n\n\n\nfunction dialog_open(propsData) {\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var DialogComponent = vm.extend(Dialog_default.a);\n return new DialogComponent({\n el: document.createElement('div'),\n propsData: propsData\n });\n}\n\nvar DialogProgrammatic = {\n alert: function alert(params) {\n var message = void 0;\n if (typeof params === 'string') message = params;\n var defaultParam = {\n canCancel: false,\n message: message\n };\n var propsData = assign_default()(defaultParam, params);\n return dialog_open(propsData);\n },\n confirm: function confirm(params) {\n var defaultParam = {};\n var propsData = assign_default()(defaultParam, params);\n return dialog_open(propsData);\n },\n prompt: function prompt(params) {\n var defaultParam = {\n hasInput: true,\n confirmText: 'Done'\n };\n var propsData = assign_default()(defaultParam, params);\n return dialog_open(propsData);\n }\n};\n\nvar dialog_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Dialog_default.a);\n registerComponentProgrammatic(Vue, '$dialog', DialogProgrammatic);\n }\n};\n\nuse(dialog_Plugin);\n\n/* harmony default export */ var dialog = (dialog_Plugin);\n// EXTERNAL MODULE: ./src/components/dropdown/Dropdown.vue\nvar Dropdown = __webpack_require__(43);\nvar Dropdown_default = /*#__PURE__*/__webpack_require__.n(Dropdown);\n\n// EXTERNAL MODULE: ./src/components/dropdown/DropdownItem.vue\nvar DropdownItem = __webpack_require__(44);\nvar DropdownItem_default = /*#__PURE__*/__webpack_require__.n(DropdownItem);\n\n// CONCATENATED MODULE: ./src/components/dropdown/index.js\n\n\n\n\n\nvar dropdown_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Dropdown_default.a);\n registerComponent(Vue, DropdownItem_default.a);\n }\n};\n\nuse(dropdown_Plugin);\n\n/* harmony default export */ var dropdown = (dropdown_Plugin);\n// EXTERNAL MODULE: ./src/components/field/Field.vue\nvar Field = __webpack_require__(45);\nvar Field_default = /*#__PURE__*/__webpack_require__.n(Field);\n\n// CONCATENATED MODULE: ./src/components/field/index.js\n\n\n\n\nvar field_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Field_default.a);\n }\n};\n\nuse(field_Plugin);\n\n/* harmony default export */ var field = (field_Plugin);\n// EXTERNAL MODULE: ./src/components/icon/Icon.vue\nvar Icon = __webpack_require__(3);\nvar Icon_default = /*#__PURE__*/__webpack_require__.n(Icon);\n\n// CONCATENATED MODULE: ./src/components/icon/index.js\n\n\n\n\nvar icon_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Icon_default.a);\n }\n};\n\nuse(icon_Plugin);\n\n/* harmony default export */ var icon = (icon_Plugin);\n// EXTERNAL MODULE: ./src/components/input/Input.vue\nvar Input = __webpack_require__(27);\nvar Input_default = /*#__PURE__*/__webpack_require__.n(Input);\n\n// CONCATENATED MODULE: ./src/components/input/index.js\n\n\n\n\nvar input_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Input_default.a);\n }\n};\n\nuse(input_Plugin);\n\n/* harmony default export */ var input = (input_Plugin);\n// EXTERNAL MODULE: ./src/components/loading/Loading.vue\nvar Loading = __webpack_require__(140);\nvar Loading_default = /*#__PURE__*/__webpack_require__.n(Loading);\n\n// CONCATENATED MODULE: ./src/components/loading/index.js\n\n\n\n\n\n\nvar LoadingProgrammatic = {\n open: function open(params) {\n var defaultParam = {\n programmatic: true\n };\n var propsData = assign_default()(defaultParam, params);\n\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var LoadingComponent = vm.extend(Loading_default.a);\n return new LoadingComponent({\n el: document.createElement('div'),\n propsData: propsData\n });\n }\n};\n\nvar loading_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Loading_default.a);\n registerComponentProgrammatic(Vue, '$loading', LoadingProgrammatic);\n }\n};\n\nuse(loading_Plugin);\n\n/* harmony default export */ var loading = (loading_Plugin);\n// EXTERNAL MODULE: ./src/components/message/Message.vue\nvar Message = __webpack_require__(143);\nvar Message_default = /*#__PURE__*/__webpack_require__.n(Message);\n\n// CONCATENATED MODULE: ./src/components/message/index.js\n\n\n\n\nvar message_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Message_default.a);\n }\n};\n\nuse(message_Plugin);\n\n/* harmony default export */ var components_message = (message_Plugin);\n// EXTERNAL MODULE: ./src/components/modal/Modal.vue\nvar Modal = __webpack_require__(62);\nvar Modal_default = /*#__PURE__*/__webpack_require__.n(Modal);\n\n// CONCATENATED MODULE: ./src/components/modal/index.js\n\n\n\n\n\n\nvar ModalProgrammatic = {\n open: function open(params) {\n var content = void 0;\n var parent = void 0;\n if (typeof params === 'string') content = params;\n\n var defaultParam = {\n programmatic: true,\n content: content\n };\n if (params.parent) {\n parent = params.parent;\n delete params.parent;\n }\n var propsData = assign_default()(defaultParam, params);\n\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var ModalComponent = vm.extend(Modal_default.a);\n return new ModalComponent({\n parent: parent,\n el: document.createElement('div'),\n propsData: propsData\n });\n }\n};\n\nvar modal_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Modal_default.a);\n registerComponentProgrammatic(Vue, '$modal', ModalProgrammatic);\n }\n};\n\nuse(modal_Plugin);\n\n/* harmony default export */ var modal = (modal_Plugin);\n// EXTERNAL MODULE: ./src/components/notification/Notification.vue\nvar Notification = __webpack_require__(146);\nvar Notification_default = /*#__PURE__*/__webpack_require__.n(Notification);\n\n// CONCATENATED MODULE: ./src/components/notification/index.js\n\n\n\n\nvar notification_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Notification_default.a);\n }\n};\n\nuse(notification_Plugin);\n\n/* harmony default export */ var notification = (notification_Plugin);\n// EXTERNAL MODULE: ./src/components/pagination/Pagination.vue\nvar Pagination = __webpack_require__(64);\nvar Pagination_default = /*#__PURE__*/__webpack_require__.n(Pagination);\n\n// CONCATENATED MODULE: ./src/components/pagination/index.js\n\n\n\n\nvar pagination_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Pagination_default.a);\n }\n};\n\nuse(pagination_Plugin);\n\n/* harmony default export */ var pagination = (pagination_Plugin);\n// EXTERNAL MODULE: ./src/components/panel/Panel.vue\nvar Panel = __webpack_require__(151);\nvar Panel_default = /*#__PURE__*/__webpack_require__.n(Panel);\n\n// CONCATENATED MODULE: ./src/components/panel/index.js\n\n\n\n\nvar panel_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Panel_default.a);\n }\n};\n\nuse(panel_Plugin);\n\n/* harmony default export */ var panel = (panel_Plugin);\n// EXTERNAL MODULE: ./src/components/radio/Radio.vue\nvar Radio = __webpack_require__(154);\nvar Radio_default = /*#__PURE__*/__webpack_require__.n(Radio);\n\n// EXTERNAL MODULE: ./src/components/radio/RadioButton.vue\nvar RadioButton = __webpack_require__(157);\nvar RadioButton_default = /*#__PURE__*/__webpack_require__.n(RadioButton);\n\n// CONCATENATED MODULE: ./src/components/radio/index.js\n\n\n\n\n\nvar radio_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Radio_default.a);\n registerComponent(Vue, RadioButton_default.a);\n }\n};\n\nuse(radio_Plugin);\n\n/* harmony default export */ var components_radio = (radio_Plugin);\n// EXTERNAL MODULE: ./src/components/select/Select.vue\nvar Select = __webpack_require__(28);\nvar Select_default = /*#__PURE__*/__webpack_require__.n(Select);\n\n// CONCATENATED MODULE: ./src/components/select/index.js\n\n\n\n\nvar select_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Select_default.a);\n }\n};\n\nuse(select_Plugin);\n\n/* harmony default export */ var components_select = (select_Plugin);\n// EXTERNAL MODULE: ./src/components/snackbar/Snackbar.vue\nvar Snackbar = __webpack_require__(160);\nvar Snackbar_default = /*#__PURE__*/__webpack_require__.n(Snackbar);\n\n// CONCATENATED MODULE: ./src/components/snackbar/index.js\n\n\n\n\n\n\nvar SnackbarProgrammatic = {\n open: function open(params) {\n var message = void 0;\n if (typeof params === 'string') message = params;\n\n var defaultParam = {\n type: 'is-success',\n position: 'is-bottom-right',\n message: message\n };\n var propsData = assign_default()(defaultParam, params);\n\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var SnackbarComponent = vm.extend(Snackbar_default.a);\n return new SnackbarComponent({\n el: document.createElement('div'),\n propsData: propsData\n });\n }\n};\n\nvar snackbar_Plugin = {\n install: function install(Vue) {\n registerComponentProgrammatic(Vue, '$snackbar', SnackbarProgrammatic);\n }\n};\n\nuse(snackbar_Plugin);\n\n/* harmony default export */ var snackbar = (snackbar_Plugin);\n// EXTERNAL MODULE: ./src/components/switch/Switch.vue\nvar Switch = __webpack_require__(163);\nvar Switch_default = /*#__PURE__*/__webpack_require__.n(Switch);\n\n// CONCATENATED MODULE: ./src/components/switch/index.js\n\n\n\n\nvar switch_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Switch_default.a);\n }\n};\n\nuse(switch_Plugin);\n\n/* harmony default export */ var components_switch = (switch_Plugin);\n// EXTERNAL MODULE: ./src/components/table/Table.vue\nvar Table = __webpack_require__(166);\nvar Table_default = /*#__PURE__*/__webpack_require__.n(Table);\n\n// EXTERNAL MODULE: ./src/components/table/TableColumn.vue\nvar TableColumn = __webpack_require__(66);\nvar TableColumn_default = /*#__PURE__*/__webpack_require__.n(TableColumn);\n\n// CONCATENATED MODULE: ./src/components/table/index.js\n\n\n\n\n\nvar table_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Table_default.a);\n registerComponent(Vue, TableColumn_default.a);\n }\n};\n\nuse(table_Plugin);\n\n/* harmony default export */ var table = (table_Plugin);\n// EXTERNAL MODULE: ./src/components/tabs/Tabs.vue\nvar Tabs = __webpack_require__(182);\nvar Tabs_default = /*#__PURE__*/__webpack_require__.n(Tabs);\n\n// EXTERNAL MODULE: ./src/components/tabs/TabItem.vue\nvar TabItem = __webpack_require__(185);\nvar TabItem_default = /*#__PURE__*/__webpack_require__.n(TabItem);\n\n// CONCATENATED MODULE: ./src/components/tabs/index.js\n\n\n\n\n\nvar tabs_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Tabs_default.a);\n registerComponent(Vue, TabItem_default.a);\n }\n};\n\nuse(tabs_Plugin);\n\n/* harmony default export */ var tabs = (tabs_Plugin);\n// EXTERNAL MODULE: ./src/components/tag/Tag.vue\nvar Tag = __webpack_require__(67);\nvar Tag_default = /*#__PURE__*/__webpack_require__.n(Tag);\n\n// EXTERNAL MODULE: ./src/components/tag/Taglist.vue\nvar Taglist = __webpack_require__(190);\nvar Taglist_default = /*#__PURE__*/__webpack_require__.n(Taglist);\n\n// CONCATENATED MODULE: ./src/components/tag/index.js\n\n\n\n\n\nvar tag_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Tag_default.a);\n registerComponent(Vue, Taglist_default.a);\n }\n};\n\nuse(tag_Plugin);\n\n/* harmony default export */ var tag = (tag_Plugin);\n// EXTERNAL MODULE: ./src/components/taginput/Taginput.vue\nvar Taginput = __webpack_require__(193);\nvar Taginput_default = /*#__PURE__*/__webpack_require__.n(Taginput);\n\n// CONCATENATED MODULE: ./src/components/taginput/index.js\n\n\n\n\nvar taginput_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Taginput_default.a);\n }\n};\n\nuse(taginput_Plugin);\n\n/* harmony default export */ var taginput = (taginput_Plugin);\n// EXTERNAL MODULE: ./src/components/timepicker/Timepicker.vue\nvar Timepicker = __webpack_require__(196);\nvar Timepicker_default = /*#__PURE__*/__webpack_require__.n(Timepicker);\n\n// CONCATENATED MODULE: ./src/components/timepicker/index.js\n\n\n\n\nvar timepicker_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Timepicker_default.a);\n }\n};\n\nuse(timepicker_Plugin);\n\n/* harmony default export */ var timepicker = (timepicker_Plugin);\n// EXTERNAL MODULE: ./src/components/toast/Toast.vue\nvar Toast = __webpack_require__(199);\nvar Toast_default = /*#__PURE__*/__webpack_require__.n(Toast);\n\n// CONCATENATED MODULE: ./src/components/toast/index.js\n\n\n\n\n\n\nvar ToastProgrammatic = {\n open: function open(params) {\n var message = void 0;\n if (typeof params === 'string') message = params;\n\n var defaultParam = { message: message };\n var propsData = assign_default()(defaultParam, params);\n\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var ToastComponent = vm.extend(Toast_default.a);\n return new ToastComponent({\n el: document.createElement('div'),\n propsData: propsData\n });\n }\n};\n\nvar toast_Plugin = {\n install: function install(Vue) {\n registerComponentProgrammatic(Vue, '$toast', ToastProgrammatic);\n }\n};\n\nuse(toast_Plugin);\n\n/* harmony default export */ var toast = (toast_Plugin);\n// EXTERNAL MODULE: ./src/components/tooltip/Tooltip.vue\nvar Tooltip = __webpack_require__(202);\nvar Tooltip_default = /*#__PURE__*/__webpack_require__.n(Tooltip);\n\n// CONCATENATED MODULE: ./src/components/tooltip/index.js\n\n\n\n\nvar tooltip_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Tooltip_default.a);\n }\n};\n\nuse(tooltip_Plugin);\n\n/* harmony default export */ var tooltip = (tooltip_Plugin);\n// EXTERNAL MODULE: ./src/components/upload/Upload.vue\nvar Upload = __webpack_require__(205);\nvar Upload_default = /*#__PURE__*/__webpack_require__.n(Upload);\n\n// CONCATENATED MODULE: ./src/components/upload/index.js\n\n\n\n\nvar upload_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Upload_default.a);\n }\n};\n\nuse(upload_Plugin);\n\n/* harmony default export */ var upload = (upload_Plugin);\n// CONCATENATED MODULE: ./src/components/index.js\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n// EXTERNAL MODULE: ./src/utils/config.js\nvar config = __webpack_require__(2);\n\n// CONCATENATED MODULE: ./src/index.js\n\n\n\n\n\n\n\n\n\nvar Buefy = {\n install: function install(Vue) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n // Options\n Object(config[\"b\" /* setOptions */])(assign_default()(config[\"a\" /* default */], options));\n // Components\n for (var componentKey in components_namespaceObject) {\n Vue.use(components_namespaceObject[componentKey]);\n }\n }\n};\n\nuse(Buefy);\n\n/* harmony default export */ var src = __webpack_exports__[\"default\"] = (Buefy);\n\n/***/ }),\n/* 69 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(70);\nmodule.exports = __webpack_require__(8).Object.assign;\n\n\n/***/ }),\n/* 70 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.3.1 Object.assign(target, source)\nvar $export = __webpack_require__(18);\n\n$export($export.S + $export.F, 'Object', { assign: __webpack_require__(72) });\n\n\n/***/ }),\n/* 71 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (it) {\n if (typeof it != 'function') throw TypeError(it + ' is not a function!');\n return it;\n};\n\n\n/***/ }),\n/* 72 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n// 19.1.2.1 Object.assign(target, source, ...)\nvar getKeys = __webpack_require__(24);\nvar gOPS = __webpack_require__(36);\nvar pIE = __webpack_require__(26);\nvar toObject = __webpack_require__(37);\nvar IObject = __webpack_require__(50);\nvar $assign = Object.assign;\n\n// should work with symbols and should have deterministic property order (V8 bug)\nmodule.exports = !$assign || __webpack_require__(20)(function () {\n var A = {};\n var B = {};\n // eslint-disable-next-line no-undef\n var S = Symbol();\n var K = 'abcdefghijklmnopqrst';\n A[S] = 7;\n K.split('').forEach(function (k) { B[k] = k; });\n return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K;\n}) ? function assign(target, source) { // eslint-disable-line no-unused-vars\n var T = toObject(target);\n var aLen = arguments.length;\n var index = 1;\n var getSymbols = gOPS.f;\n var isEnum = pIE.f;\n while (aLen > index) {\n var S = IObject(arguments[index++]);\n var keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S);\n var length = keys.length;\n var j = 0;\n var key;\n while (length > j) if (isEnum.call(S, key = keys[j++])) T[key] = S[key];\n } return T;\n} : $assign;\n\n\n/***/ }),\n/* 73 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// false -> Array#indexOf\n// true -> Array#includes\nvar toIObject = __webpack_require__(17);\nvar toLength = __webpack_require__(51);\nvar toAbsoluteIndex = __webpack_require__(74);\nmodule.exports = function (IS_INCLUDES) {\n return function ($this, el, fromIndex) {\n var O = toIObject($this);\n var length = toLength(O.length);\n var index = toAbsoluteIndex(fromIndex, length);\n var value;\n // Array#includes uses SameValueZero equality algorithm\n // eslint-disable-next-line no-self-compare\n if (IS_INCLUDES && el != el) while (length > index) {\n value = O[index++];\n // eslint-disable-next-line no-self-compare\n if (value != value) return true;\n // Array#indexOf ignores holes, Array#includes - not\n } else for (;length > index; index++) if (IS_INCLUDES || index in O) {\n if (O[index] === el) return IS_INCLUDES || index || 0;\n } return !IS_INCLUDES && -1;\n };\n};\n\n\n/***/ }),\n/* 74 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar toInteger = __webpack_require__(32);\nvar max = Math.max;\nvar min = Math.min;\nmodule.exports = function (index, length) {\n index = toInteger(index);\n return index < 0 ? max(index + length, 0) : min(index, length);\n};\n\n\n/***/ }),\n/* 75 */\n/***/ (function(module, exports) {\n\n// removed by extract-text-webpack-plugin\n\n/***/ }),\n/* 76 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__ = __webpack_require__(53);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator__ = __webpack_require__(59);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__utils_FormElementMixin__ = __webpack_require__(12);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__input_Input__ = __webpack_require__(27);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__input_Input___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__input_Input__);\n\n\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BAutocomplete',\n components: __WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_5__input_Input___default.a.name, __WEBPACK_IMPORTED_MODULE_5__input_Input___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_4__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: [Number, String],\n data: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n field: {\n type: String,\n default: 'value'\n },\n keepFirst: Boolean,\n clearOnSelect: Boolean,\n openOnFocus: Boolean\n },\n data: function data() {\n return {\n selected: null,\n hovered: null,\n isActive: false,\n newValue: this.value,\n isListInViewportVertically: true,\n hasFocus: false,\n _isAutocomplete: true,\n _elementRef: 'input'\n };\n },\n\n computed: {\n /**\n * White-listed items to not close when clicked.\n * Add input, dropdown and all children.\n */\n whiteList: function whiteList() {\n var whiteList = [];\n whiteList.push(this.$refs.input.$el.querySelector('input'));\n whiteList.push(this.$refs.dropdown);\n // Add all chidren from dropdown\n if (this.$refs.dropdown !== undefined) {\n var children = this.$refs.dropdown.querySelectorAll('*');\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator___default()(children), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var child = _step.value;\n\n whiteList.push(child);\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n }\n\n return whiteList;\n },\n\n\n /**\n * Check if exists default slot\n */\n hasDefaultSlot: function hasDefaultSlot() {\n return !!this.$scopedSlots.default;\n },\n\n\n /**\n * Check if exists \"empty\" slot\n */\n hasEmptySlot: function hasEmptySlot() {\n return !!this.$slots.empty;\n },\n\n\n /**\n * Check if exists \"header\" slot\n */\n hasHeaderSlot: function hasHeaderSlot() {\n return !!this.$slots.header;\n }\n },\n watch: {\n /**\n * When dropdown is toggled, check the visibility to know when\n * to open upwards.\n */\n isActive: function isActive(active) {\n var _this = this;\n\n if (active) {\n this.calcDropdownInViewportVertical();\n } else {\n this.$nextTick(function () {\n return _this.setHovered(null);\n });\n // Timeout to wait for the animation to finish before recalculating\n setTimeout(function () {\n _this.calcDropdownInViewportVertical();\n }, 100);\n }\n },\n\n\n /**\n * When updating input's value\n * 1. Emit changes\n * 2. If value isn't the same as selected, set null\n * 3. Close dropdown if value is clear or else open it\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n // Check if selected is invalid\n var currentValue = this.getValue(this.selected);\n if (currentValue && currentValue !== value) {\n this.setSelected(null, false);\n }\n // Close dropdown if input is clear or else open it\n if (this.hasFocus && (!this.openOnFocus || value)) {\n this.isActive = !!value;\n }\n },\n\n\n /**\n * When v-model is changed:\n * 1. Update internal value.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.newValue = _value;\n !this.isValid && this.$refs.input.checkHtml5Validity();\n },\n\n\n /**\n * Select first option if \"keep-first\n */\n data: function data(value) {\n // Keep first option always pre-selected\n if (this.keepFirst) {\n this.selectFirstOption(value);\n }\n }\n },\n methods: {\n /**\n * Set which option is currently hovered.\n */\n setHovered: function setHovered(option) {\n if (option === undefined) return;\n\n this.hovered = option;\n },\n\n\n /**\n * Set which option is currently selected, update v-model,\n * update input value and close dropdown.\n */\n setSelected: function setSelected(option) {\n var _this2 = this;\n\n var closeDropdown = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n\n if (option === undefined) return;\n\n this.selected = option;\n this.$emit('select', this.selected);\n if (this.selected !== null) {\n this.newValue = this.clearOnSelect ? '' : this.getValue(this.selected);\n }\n closeDropdown && this.$nextTick(function () {\n _this2.isActive = false;\n });\n },\n\n\n /**\n * Select first option\n */\n selectFirstOption: function selectFirstOption(options) {\n var _this3 = this;\n\n this.$nextTick(function () {\n if (options.length) {\n // If has visible data or open on focus, keep updating the hovered\n if (_this3.openOnFocus || _this3.newValue !== '' && _this3.hovered !== options[0]) {\n _this3.setHovered(options[0]);\n }\n } else {\n _this3.setHovered(null);\n }\n });\n },\n\n\n /**\n * Enter key listener.\n * Select the hovered option.\n */\n enterPressed: function enterPressed() {\n if (this.hovered === null) return;\n this.setSelected(this.hovered);\n },\n\n\n /**\n * Tab key listener.\n * Select hovered option if it exists, close dropdown, then allow\n * native handling to move to next tabbable element.\n */\n tabPressed: function tabPressed() {\n if (this.hovered === null) {\n this.isActive = false;\n return;\n }\n this.setSelected(this.hovered);\n },\n\n\n /**\n * Close dropdown if clicked outside.\n */\n clickedOutside: function clickedOutside(event) {\n if (this.whiteList.indexOf(event.target) < 0) this.isActive = false;\n },\n\n\n /**\n * Return display text for the input.\n * If object, get value from path, or else just the value.\n * If hightlight, find the text with regex and make bold.\n */\n getValue: function getValue(option) {\n var isHighlight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n if (!option) return;\n\n var value = (typeof option === 'undefined' ? 'undefined' : __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default()(option)) === 'object' ? Object(__WEBPACK_IMPORTED_MODULE_3__utils_helpers__[\"b\" /* getValueByPath */])(option, this.field) : option;\n\n var escapedValue = typeof this.newValue === 'string' ? Object(__WEBPACK_IMPORTED_MODULE_3__utils_helpers__[\"a\" /* escapeRegExpChars */])(this.newValue) : this.newValue;\n var regex = new RegExp('(' + escapedValue + ')', 'gi');\n\n return isHighlight ? value.replace(regex, '<b>$1</b>') : value;\n },\n\n\n /**\n * Calculate if the dropdown is vertically visible when activated,\n * otherwise it is openened upwards.\n */\n calcDropdownInViewportVertical: function calcDropdownInViewportVertical() {\n var _this4 = this;\n\n this.$nextTick(function () {\n /**\n * this.$refs.dropdown may be undefined\n * when Autocomplete is conditional rendered\n */\n if (_this4.$refs.dropdown === undefined) return;\n\n var rect = _this4.$refs.dropdown.getBoundingClientRect();\n\n _this4.isListInViewportVertically = rect.top >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight);\n });\n },\n\n\n /**\n * Arrows keys listener.\n * If dropdown is active, set hovered option, or else just open.\n */\n keyArrows: function keyArrows(direction) {\n var sum = direction === 'down' ? 1 : -1;\n if (this.isActive) {\n var index = this.data.indexOf(this.hovered) + sum;\n index = index > this.data.length - 1 ? this.data.length : index;\n index = index < 0 ? 0 : index;\n\n this.setHovered(this.data[index]);\n\n var list = this.$refs.dropdown.querySelector('.dropdown-content');\n var element = list.querySelectorAll('.dropdown-item:not(.is-disabled)')[index];\n\n if (!element) return;\n\n var visMin = list.scrollTop;\n var visMax = list.scrollTop + list.clientHeight - element.clientHeight;\n\n if (element.offsetTop < visMin) {\n list.scrollTop = element.offsetTop;\n } else if (element.offsetTop >= visMax) {\n list.scrollTop = element.offsetTop - list.clientHeight + element.clientHeight;\n }\n } else {\n this.isActive = true;\n }\n },\n\n\n /**\n * Focus listener.\n * If value is the same as selected, select all text.\n */\n focused: function focused(event) {\n if (this.getValue(this.selected) === this.newValue) {\n this.$el.querySelector('input').select();\n }\n if (this.openOnFocus) {\n this.isActive = true;\n if (this.keepFirst) {\n this.selectFirstOption(this.data);\n }\n }\n this.hasFocus = true;\n this.$emit('focus', event);\n },\n\n\n /**\n * Blur listener.\n */\n onBlur: function onBlur(event) {\n this.hasFocus = false;\n this.$emit('blur', event);\n }\n },\n created: function created() {\n if (typeof window !== 'undefined') {\n document.addEventListener('click', this.clickedOutside);\n window.addEventListener('resize', this.calcDropdownInViewportVertical);\n }\n },\n beforeDestroy: function beforeDestroy() {\n if (typeof window !== 'undefined') {\n document.removeEventListener('click', this.clickedOutside);\n window.removeEventListener('resize', this.calcDropdownInViewportVertical);\n }\n }\n});\n\n/***/ }),\n/* 77 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(78), __esModule: true };\n\n/***/ }),\n/* 78 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(38);\n__webpack_require__(57);\nmodule.exports = __webpack_require__(41).f('iterator');\n\n\n/***/ }),\n/* 79 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar toInteger = __webpack_require__(32);\nvar defined = __webpack_require__(31);\n// true -> String#at\n// false -> String#codePointAt\nmodule.exports = function (TO_STRING) {\n return function (that, pos) {\n var s = String(defined(that));\n var i = toInteger(pos);\n var l = s.length;\n var a, b;\n if (i < 0 || i >= l) return TO_STRING ? '' : undefined;\n a = s.charCodeAt(i);\n return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff\n ? TO_STRING ? s.charAt(i) : a\n : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;\n };\n};\n\n\n/***/ }),\n/* 80 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar create = __webpack_require__(56);\nvar descriptor = __webpack_require__(21);\nvar setToStringTag = __webpack_require__(40);\nvar IteratorPrototype = {};\n\n// 25.1.2.1.1 %IteratorPrototype%[@@iterator]()\n__webpack_require__(15)(IteratorPrototype, __webpack_require__(4)('iterator'), function () { return this; });\n\nmodule.exports = function (Constructor, NAME, next) {\n Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) });\n setToStringTag(Constructor, NAME + ' Iterator');\n};\n\n\n/***/ }),\n/* 81 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar dP = __webpack_require__(9);\nvar anObject = __webpack_require__(16);\nvar getKeys = __webpack_require__(24);\n\nmodule.exports = __webpack_require__(10) ? Object.defineProperties : function defineProperties(O, Properties) {\n anObject(O);\n var keys = getKeys(Properties);\n var length = keys.length;\n var i = 0;\n var P;\n while (length > i) dP.f(O, P = keys[i++], Properties[P]);\n return O;\n};\n\n\n/***/ }),\n/* 82 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar document = __webpack_require__(7).document;\nmodule.exports = document && document.documentElement;\n\n\n/***/ }),\n/* 83 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O)\nvar has = __webpack_require__(11);\nvar toObject = __webpack_require__(37);\nvar IE_PROTO = __webpack_require__(33)('IE_PROTO');\nvar ObjectProto = Object.prototype;\n\nmodule.exports = Object.getPrototypeOf || function (O) {\n O = toObject(O);\n if (has(O, IE_PROTO)) return O[IE_PROTO];\n if (typeof O.constructor == 'function' && O instanceof O.constructor) {\n return O.constructor.prototype;\n } return O instanceof Object ? ObjectProto : null;\n};\n\n\n/***/ }),\n/* 84 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar addToUnscopables = __webpack_require__(85);\nvar step = __webpack_require__(86);\nvar Iterators = __webpack_require__(22);\nvar toIObject = __webpack_require__(17);\n\n// 22.1.3.4 Array.prototype.entries()\n// 22.1.3.13 Array.prototype.keys()\n// 22.1.3.29 Array.prototype.values()\n// 22.1.3.30 Array.prototype[@@iterator]()\nmodule.exports = __webpack_require__(54)(Array, 'Array', function (iterated, kind) {\n this._t = toIObject(iterated); // target\n this._i = 0; // next index\n this._k = kind; // kind\n// 22.1.5.2.1 %ArrayIteratorPrototype%.next()\n}, function () {\n var O = this._t;\n var kind = this._k;\n var index = this._i++;\n if (!O || index >= O.length) {\n this._t = undefined;\n return step(1);\n }\n if (kind == 'keys') return step(0, index);\n if (kind == 'values') return step(0, O[index]);\n return step(0, [index, O[index]]);\n}, 'values');\n\n// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7)\nIterators.Arguments = Iterators.Array;\n\naddToUnscopables('keys');\naddToUnscopables('values');\naddToUnscopables('entries');\n\n\n/***/ }),\n/* 85 */\n/***/ (function(module, exports) {\n\nmodule.exports = function () { /* empty */ };\n\n\n/***/ }),\n/* 86 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (done, value) {\n return { value: value, done: !!done };\n};\n\n\n/***/ }),\n/* 87 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(88);\n__webpack_require__(94);\n__webpack_require__(95);\n__webpack_require__(96);\nmodule.exports = __webpack_require__(8).Symbol;\n\n\n/***/ }),\n/* 88 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n// ECMAScript 6 symbols shim\nvar global = __webpack_require__(7);\nvar has = __webpack_require__(11);\nvar DESCRIPTORS = __webpack_require__(10);\nvar $export = __webpack_require__(18);\nvar redefine = __webpack_require__(55);\nvar META = __webpack_require__(89).KEY;\nvar $fails = __webpack_require__(20);\nvar shared = __webpack_require__(34);\nvar setToStringTag = __webpack_require__(40);\nvar uid = __webpack_require__(25);\nvar wks = __webpack_require__(4);\nvar wksExt = __webpack_require__(41);\nvar wksDefine = __webpack_require__(42);\nvar enumKeys = __webpack_require__(90);\nvar isArray = __webpack_require__(91);\nvar anObject = __webpack_require__(16);\nvar isObject = __webpack_require__(19);\nvar toIObject = __webpack_require__(17);\nvar toPrimitive = __webpack_require__(29);\nvar createDesc = __webpack_require__(21);\nvar _create = __webpack_require__(56);\nvar gOPNExt = __webpack_require__(92);\nvar $GOPD = __webpack_require__(93);\nvar $DP = __webpack_require__(9);\nvar $keys = __webpack_require__(24);\nvar gOPD = $GOPD.f;\nvar dP = $DP.f;\nvar gOPN = gOPNExt.f;\nvar $Symbol = global.Symbol;\nvar $JSON = global.JSON;\nvar _stringify = $JSON && $JSON.stringify;\nvar PROTOTYPE = 'prototype';\nvar HIDDEN = wks('_hidden');\nvar TO_PRIMITIVE = wks('toPrimitive');\nvar isEnum = {}.propertyIsEnumerable;\nvar SymbolRegistry = shared('symbol-registry');\nvar AllSymbols = shared('symbols');\nvar OPSymbols = shared('op-symbols');\nvar ObjectProto = Object[PROTOTYPE];\nvar USE_NATIVE = typeof $Symbol == 'function';\nvar QObject = global.QObject;\n// Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173\nvar setter = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild;\n\n// fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687\nvar setSymbolDesc = DESCRIPTORS && $fails(function () {\n return _create(dP({}, 'a', {\n get: function () { return dP(this, 'a', { value: 7 }).a; }\n })).a != 7;\n}) ? function (it, key, D) {\n var protoDesc = gOPD(ObjectProto, key);\n if (protoDesc) delete ObjectProto[key];\n dP(it, key, D);\n if (protoDesc && it !== ObjectProto) dP(ObjectProto, key, protoDesc);\n} : dP;\n\nvar wrap = function (tag) {\n var sym = AllSymbols[tag] = _create($Symbol[PROTOTYPE]);\n sym._k = tag;\n return sym;\n};\n\nvar isSymbol = USE_NATIVE && typeof $Symbol.iterator == 'symbol' ? function (it) {\n return typeof it == 'symbol';\n} : function (it) {\n return it instanceof $Symbol;\n};\n\nvar $defineProperty = function defineProperty(it, key, D) {\n if (it === ObjectProto) $defineProperty(OPSymbols, key, D);\n anObject(it);\n key = toPrimitive(key, true);\n anObject(D);\n if (has(AllSymbols, key)) {\n if (!D.enumerable) {\n if (!has(it, HIDDEN)) dP(it, HIDDEN, createDesc(1, {}));\n it[HIDDEN][key] = true;\n } else {\n if (has(it, HIDDEN) && it[HIDDEN][key]) it[HIDDEN][key] = false;\n D = _create(D, { enumerable: createDesc(0, false) });\n } return setSymbolDesc(it, key, D);\n } return dP(it, key, D);\n};\nvar $defineProperties = function defineProperties(it, P) {\n anObject(it);\n var keys = enumKeys(P = toIObject(P));\n var i = 0;\n var l = keys.length;\n var key;\n while (l > i) $defineProperty(it, key = keys[i++], P[key]);\n return it;\n};\nvar $create = function create(it, P) {\n return P === undefined ? _create(it) : $defineProperties(_create(it), P);\n};\nvar $propertyIsEnumerable = function propertyIsEnumerable(key) {\n var E = isEnum.call(this, key = toPrimitive(key, true));\n if (this === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return false;\n return E || !has(this, key) || !has(AllSymbols, key) || has(this, HIDDEN) && this[HIDDEN][key] ? E : true;\n};\nvar $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(it, key) {\n it = toIObject(it);\n key = toPrimitive(key, true);\n if (it === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return;\n var D = gOPD(it, key);\n if (D && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) D.enumerable = true;\n return D;\n};\nvar $getOwnPropertyNames = function getOwnPropertyNames(it) {\n var names = gOPN(toIObject(it));\n var result = [];\n var i = 0;\n var key;\n while (names.length > i) {\n if (!has(AllSymbols, key = names[i++]) && key != HIDDEN && key != META) result.push(key);\n } return result;\n};\nvar $getOwnPropertySymbols = function getOwnPropertySymbols(it) {\n var IS_OP = it === ObjectProto;\n var names = gOPN(IS_OP ? OPSymbols : toIObject(it));\n var result = [];\n var i = 0;\n var key;\n while (names.length > i) {\n if (has(AllSymbols, key = names[i++]) && (IS_OP ? has(ObjectProto, key) : true)) result.push(AllSymbols[key]);\n } return result;\n};\n\n// 19.4.1.1 Symbol([description])\nif (!USE_NATIVE) {\n $Symbol = function Symbol() {\n if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor!');\n var tag = uid(arguments.length > 0 ? arguments[0] : undefined);\n var $set = function (value) {\n if (this === ObjectProto) $set.call(OPSymbols, value);\n if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;\n setSymbolDesc(this, tag, createDesc(1, value));\n };\n if (DESCRIPTORS && setter) setSymbolDesc(ObjectProto, tag, { configurable: true, set: $set });\n return wrap(tag);\n };\n redefine($Symbol[PROTOTYPE], 'toString', function toString() {\n return this._k;\n });\n\n $GOPD.f = $getOwnPropertyDescriptor;\n $DP.f = $defineProperty;\n __webpack_require__(58).f = gOPNExt.f = $getOwnPropertyNames;\n __webpack_require__(26).f = $propertyIsEnumerable;\n __webpack_require__(36).f = $getOwnPropertySymbols;\n\n if (DESCRIPTORS && !__webpack_require__(39)) {\n redefine(ObjectProto, 'propertyIsEnumerable', $propertyIsEnumerable, true);\n }\n\n wksExt.f = function (name) {\n return wrap(wks(name));\n };\n}\n\n$export($export.G + $export.W + $export.F * !USE_NATIVE, { Symbol: $Symbol });\n\nfor (var es6Symbols = (\n // 19.4.2.2, 19.4.2.3, 19.4.2.4, 19.4.2.6, 19.4.2.8, 19.4.2.9, 19.4.2.10, 19.4.2.11, 19.4.2.12, 19.4.2.13, 19.4.2.14\n 'hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables'\n).split(','), j = 0; es6Symbols.length > j;)wks(es6Symbols[j++]);\n\nfor (var wellKnownSymbols = $keys(wks.store), k = 0; wellKnownSymbols.length > k;) wksDefine(wellKnownSymbols[k++]);\n\n$export($export.S + $export.F * !USE_NATIVE, 'Symbol', {\n // 19.4.2.1 Symbol.for(key)\n 'for': function (key) {\n return has(SymbolRegistry, key += '')\n ? SymbolRegistry[key]\n : SymbolRegistry[key] = $Symbol(key);\n },\n // 19.4.2.5 Symbol.keyFor(sym)\n keyFor: function keyFor(sym) {\n if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol!');\n for (var key in SymbolRegistry) if (SymbolRegistry[key] === sym) return key;\n },\n useSetter: function () { setter = true; },\n useSimple: function () { setter = false; }\n});\n\n$export($export.S + $export.F * !USE_NATIVE, 'Object', {\n // 19.1.2.2 Object.create(O [, Properties])\n create: $create,\n // 19.1.2.4 Object.defineProperty(O, P, Attributes)\n defineProperty: $defineProperty,\n // 19.1.2.3 Object.defineProperties(O, Properties)\n defineProperties: $defineProperties,\n // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)\n getOwnPropertyDescriptor: $getOwnPropertyDescriptor,\n // 19.1.2.7 Object.getOwnPropertyNames(O)\n getOwnPropertyNames: $getOwnPropertyNames,\n // 19.1.2.8 Object.getOwnPropertySymbols(O)\n getOwnPropertySymbols: $getOwnPropertySymbols\n});\n\n// 24.3.2 JSON.stringify(value [, replacer [, space]])\n$JSON && $export($export.S + $export.F * (!USE_NATIVE || $fails(function () {\n var S = $Symbol();\n // MS Edge converts symbol values to JSON as {}\n // WebKit converts symbol values to JSON as null\n // V8 throws on boxed symbols\n return _stringify([S]) != '[null]' || _stringify({ a: S }) != '{}' || _stringify(Object(S)) != '{}';\n})), 'JSON', {\n stringify: function stringify(it) {\n var args = [it];\n var i = 1;\n var replacer, $replacer;\n while (arguments.length > i) args.push(arguments[i++]);\n $replacer = replacer = args[1];\n if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined\n if (!isArray(replacer)) replacer = function (key, value) {\n if (typeof $replacer == 'function') value = $replacer.call(this, key, value);\n if (!isSymbol(value)) return value;\n };\n args[1] = replacer;\n return _stringify.apply($JSON, args);\n }\n});\n\n// 19.4.3.4 Symbol.prototype[@@toPrimitive](hint)\n$Symbol[PROTOTYPE][TO_PRIMITIVE] || __webpack_require__(15)($Symbol[PROTOTYPE], TO_PRIMITIVE, $Symbol[PROTOTYPE].valueOf);\n// 19.4.3.5 Symbol.prototype[@@toStringTag]\nsetToStringTag($Symbol, 'Symbol');\n// 20.2.1.9 Math[@@toStringTag]\nsetToStringTag(Math, 'Math', true);\n// 24.3.3 JSON[@@toStringTag]\nsetToStringTag(global.JSON, 'JSON', true);\n\n\n/***/ }),\n/* 89 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar META = __webpack_require__(25)('meta');\nvar isObject = __webpack_require__(19);\nvar has = __webpack_require__(11);\nvar setDesc = __webpack_require__(9).f;\nvar id = 0;\nvar isExtensible = Object.isExtensible || function () {\n return true;\n};\nvar FREEZE = !__webpack_require__(20)(function () {\n return isExtensible(Object.preventExtensions({}));\n});\nvar setMeta = function (it) {\n setDesc(it, META, { value: {\n i: 'O' + ++id, // object ID\n w: {} // weak collections IDs\n } });\n};\nvar fastKey = function (it, create) {\n // return primitive with prefix\n if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;\n if (!has(it, META)) {\n // can't set metadata to uncaught frozen object\n if (!isExtensible(it)) return 'F';\n // not necessary to add metadata\n if (!create) return 'E';\n // add missing metadata\n setMeta(it);\n // return object ID\n } return it[META].i;\n};\nvar getWeak = function (it, create) {\n if (!has(it, META)) {\n // can't set metadata to uncaught frozen object\n if (!isExtensible(it)) return true;\n // not necessary to add metadata\n if (!create) return false;\n // add missing metadata\n setMeta(it);\n // return hash weak collections IDs\n } return it[META].w;\n};\n// add metadata on freeze-family methods calling\nvar onFreeze = function (it) {\n if (FREEZE && meta.NEED && isExtensible(it) && !has(it, META)) setMeta(it);\n return it;\n};\nvar meta = module.exports = {\n KEY: META,\n NEED: false,\n fastKey: fastKey,\n getWeak: getWeak,\n onFreeze: onFreeze\n};\n\n\n/***/ }),\n/* 90 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// all enumerable object keys, includes symbols\nvar getKeys = __webpack_require__(24);\nvar gOPS = __webpack_require__(36);\nvar pIE = __webpack_require__(26);\nmodule.exports = function (it) {\n var result = getKeys(it);\n var getSymbols = gOPS.f;\n if (getSymbols) {\n var symbols = getSymbols(it);\n var isEnum = pIE.f;\n var i = 0;\n var key;\n while (symbols.length > i) if (isEnum.call(it, key = symbols[i++])) result.push(key);\n } return result;\n};\n\n\n/***/ }),\n/* 91 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 7.2.2 IsArray(argument)\nvar cof = __webpack_require__(30);\nmodule.exports = Array.isArray || function isArray(arg) {\n return cof(arg) == 'Array';\n};\n\n\n/***/ }),\n/* 92 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window\nvar toIObject = __webpack_require__(17);\nvar gOPN = __webpack_require__(58).f;\nvar toString = {}.toString;\n\nvar windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames\n ? Object.getOwnPropertyNames(window) : [];\n\nvar getWindowNames = function (it) {\n try {\n return gOPN(it);\n } catch (e) {\n return windowNames.slice();\n }\n};\n\nmodule.exports.f = function getOwnPropertyNames(it) {\n return windowNames && toString.call(it) == '[object Window]' ? getWindowNames(it) : gOPN(toIObject(it));\n};\n\n\n/***/ }),\n/* 93 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar pIE = __webpack_require__(26);\nvar createDesc = __webpack_require__(21);\nvar toIObject = __webpack_require__(17);\nvar toPrimitive = __webpack_require__(29);\nvar has = __webpack_require__(11);\nvar IE8_DOM_DEFINE = __webpack_require__(47);\nvar gOPD = Object.getOwnPropertyDescriptor;\n\nexports.f = __webpack_require__(10) ? gOPD : function getOwnPropertyDescriptor(O, P) {\n O = toIObject(O);\n P = toPrimitive(P, true);\n if (IE8_DOM_DEFINE) try {\n return gOPD(O, P);\n } catch (e) { /* empty */ }\n if (has(O, P)) return createDesc(!pIE.f.call(O, P), O[P]);\n};\n\n\n/***/ }),\n/* 94 */\n/***/ (function(module, exports) {\n\n\n\n/***/ }),\n/* 95 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(42)('asyncIterator');\n\n\n/***/ }),\n/* 96 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(42)('observable');\n\n\n/***/ }),\n/* 97 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(57);\n__webpack_require__(38);\nmodule.exports = __webpack_require__(98);\n\n\n/***/ }),\n/* 98 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar anObject = __webpack_require__(16);\nvar get = __webpack_require__(60);\nmodule.exports = __webpack_require__(8).getIterator = function (it) {\n var iterFn = get(it);\n if (typeof iterFn != 'function') throw TypeError(it + ' is not iterable!');\n return anObject(iterFn.call(it));\n};\n\n\n/***/ }),\n/* 99 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// getting tag from 19.1.3.6 Object.prototype.toString()\nvar cof = __webpack_require__(30);\nvar TAG = __webpack_require__(4)('toStringTag');\n// ES3 wrong here\nvar ARG = cof(function () { return arguments; }()) == 'Arguments';\n\n// fallback for IE11 Script Access Denied error\nvar tryGet = function (it, key) {\n try {\n return it[key];\n } catch (e) { /* empty */ }\n};\n\nmodule.exports = function (it) {\n var O, T, B;\n return it === undefined ? 'Undefined' : it === null ? 'Null'\n // @@toStringTag case\n : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T\n // builtinTag case\n : ARG ? cof(O)\n // ES3 arguments fallback\n : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B;\n};\n\n\n/***/ }),\n/* 100 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(101), __esModule: true };\n\n/***/ }),\n/* 101 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(102);\nvar $Object = __webpack_require__(8).Object;\nmodule.exports = function defineProperty(it, key, desc) {\n return $Object.defineProperty(it, key, desc);\n};\n\n\n/***/ }),\n/* 102 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar $export = __webpack_require__(18);\n// 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes)\n$export($export.S + $export.F * !__webpack_require__(10), 'Object', { defineProperty: __webpack_require__(9).f });\n\n\n/***/ }),\n/* 103 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__ = __webpack_require__(12);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BInput',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: [Number, String],\n type: {\n type: String,\n default: 'text'\n },\n passwordReveal: Boolean,\n hasCounter: {\n type: Boolean,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_2__utils_config__[\"a\" /* default */].defaultInputHasCounter;\n }\n }\n },\n data: function data() {\n return {\n newValue: this.value,\n newType: this.type,\n newAutocomplete: this.autocomplete || __WEBPACK_IMPORTED_MODULE_2__utils_config__[\"a\" /* default */].defaultInputAutocomplete,\n isPasswordVisible: false,\n _elementRef: this.type === 'textarea' ? 'textarea' : 'input'\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return [this.iconPosition, this.size, {\n 'is-expanded': this.expanded,\n 'is-loading': this.loading,\n 'is-clearfix': !this.hasMessage\n }];\n },\n inputClasses: function inputClasses() {\n return [this.statusType, this.size, { 'is-rounded': this.rounded }];\n },\n hasIconRight: function hasIconRight() {\n return this.passwordReveal || this.loading || this.statusType;\n },\n\n\n /**\n * Position of the icon or if it's both sides.\n */\n iconPosition: function iconPosition() {\n if (this.icon && this.hasIconRight) {\n return 'has-icons-left has-icons-right';\n } else if (!this.icon && this.hasIconRight) {\n return 'has-icons-right';\n } else if (this.icon) {\n return 'has-icons-left';\n }\n },\n\n\n /**\n * Icon name (MDI) based on the type.\n */\n statusTypeIcon: function statusTypeIcon() {\n switch (this.statusType) {\n case 'is-success':\n return 'check';\n case 'is-danger':\n return 'alert-circle';\n case 'is-info':\n return 'information';\n case 'is-warning':\n return 'alert';\n }\n },\n\n\n /**\n * Check if have any message prop from parent if it's a Field.\n */\n hasMessage: function hasMessage() {\n return !!this.statusMessage;\n },\n\n\n /**\n * Current password-reveal icon name.\n */\n passwordVisibleIcon: function passwordVisibleIcon() {\n return !this.isPasswordVisible ? 'eye' : 'eye-off';\n },\n\n /**\n * Get value length\n */\n valueLength: function valueLength() {\n if (typeof this.newValue === 'string') {\n return this.newValue.length;\n } else if (typeof this.newValue === 'number') {\n return this.newValue.toString().length;\n }\n return 0;\n }\n },\n watch: {\n /**\n * When v-model is changed:\n * 1. Set internal value.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n\n /**\n * Update user's v-model and validate again whenever\n * internal value is changed.\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n !this.isValid && this.checkHtml5Validity();\n }\n },\n methods: {\n /**\n * Toggle the visibility of a password-reveal input\n * by changing the type and focus the input right away.\n */\n togglePasswordVisibility: function togglePasswordVisibility() {\n var _this = this;\n\n this.isPasswordVisible = !this.isPasswordVisible;\n this.newType = this.isPasswordVisible ? 'text' : 'password';\n\n this.$nextTick(function () {\n _this.$refs.input.focus();\n });\n },\n\n\n /**\n * Input's 'input' event listener, 'nextTick' is used to prevent event firing\n * before ui update, helps when using masks (Cleavejs and potentially others).\n */\n onInput: function onInput(event) {\n var _this2 = this;\n\n this.$nextTick(function () {\n _this2.newValue = event.target.value;\n });\n }\n }\n});\n\n/***/ }),\n/* 104 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_config__ = __webpack_require__(2);\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BIcon',\n props: {\n type: String,\n pack: String,\n icon: String,\n size: String,\n customSize: String,\n customClass: String,\n both: Boolean // This is used internally to show both MDI and FA icon\n },\n computed: {\n /**\n * Internal icon name based on the pack.\n * If pack is 'fa', gets the equivalent FA icon name of the MDI,\n * internal icons are always MDI.\n */\n newIcon: function newIcon() {\n if (!this.both) {\n if (this.newPack === 'mdi') {\n return this.newPack + '-' + this.icon;\n } else {\n return 'fa-' + this.icon;\n }\n }\n\n return this.newPack === 'mdi' ? this.newPack + '-' + this.icon : 'fa-' + this.getEquivalentIconOf(this.icon);\n },\n newPack: function newPack() {\n return this.pack || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultIconPack;\n },\n newType: function newType() {\n if (!this.type) return;\n\n var splitType = this.type.split('-');\n if (!splitType.length) return;\n\n return 'has-text-' + splitType[1];\n },\n newCustomSize: function newCustomSize() {\n return this.customSize || this.customSizeByPack;\n },\n customSizeByPack: function customSizeByPack() {\n var defaultSize = this.newPack === 'mdi' ? 'mdi-24px' : 'fa-lg';\n var mediumSize = this.newPack === 'mdi' ? 'mdi-36px' : 'fa-2x';\n var largeSize = this.newPack === 'mdi' ? 'mdi-48px' : 'fa-3x';\n switch (this.size) {\n case 'is-small':\n return;\n case 'is-medium':\n return mediumSize;\n case 'is-large':\n return largeSize;\n default:\n return defaultSize;\n }\n }\n },\n methods: {\n /**\n * Equivalent FA icon name of the MDI.\n */\n getEquivalentIconOf: function getEquivalentIconOf(value) {\n switch (value) {\n case 'check':\n return 'check';\n case 'information':\n return 'info-circle';\n case 'check-circle':\n return 'check-circle';\n case 'alert':\n return 'exclamation-triangle';\n case 'alert-circle':\n return 'exclamation-circle';\n case 'arrow-up':\n return 'arrow-up';\n case 'chevron-right':\n return 'angle-right';\n case 'chevron-left':\n return 'angle-left';\n case 'chevron-down':\n return 'angle-down';\n case 'eye':\n return 'eye';\n case 'eye-off':\n return 'eye-slash';\n case 'menu-down':\n return 'caret-down';\n case 'menu-up':\n return 'caret-up';\n default:\n return value;\n }\n }\n }\n});\n\n/***/ }),\n/* 105 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('span', {\n staticClass: \"icon\",\n class: [_vm.newType, _vm.size]\n }, [_c('i', {\n class: [_vm.newPack, _vm.newIcon, _vm.newCustomSize, _vm.customClass]\n })])\n},staticRenderFns: []}\n\n/***/ }),\n/* 106 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"control\",\n class: _vm.rootClasses\n }, [(_vm.type !== 'textarea') ? _c('input', _vm._b({\n ref: \"input\",\n staticClass: \"input\",\n class: _vm.inputClasses,\n attrs: {\n \"type\": _vm.newType,\n \"autocomplete\": _vm.newAutocomplete,\n \"maxlength\": _vm.maxlength\n },\n domProps: {\n \"value\": _vm.newValue\n },\n on: {\n \"input\": _vm.onInput,\n \"blur\": _vm.onBlur,\n \"focus\": _vm.onFocus\n }\n }, 'input', _vm.$attrs, false)) : _c('textarea', _vm._b({\n ref: \"textarea\",\n staticClass: \"textarea\",\n class: _vm.inputClasses,\n attrs: {\n \"maxlength\": _vm.maxlength\n },\n domProps: {\n \"value\": _vm.newValue\n },\n on: {\n \"input\": _vm.onInput,\n \"blur\": _vm.onBlur,\n \"focus\": _vm.onFocus\n }\n }, 'textarea', _vm.$attrs, false)), _vm._v(\" \"), (_vm.icon) ? _c('b-icon', {\n staticClass: \"is-left\",\n attrs: {\n \"icon\": _vm.icon,\n \"pack\": _vm.iconPack,\n \"size\": _vm.iconSize\n }\n }) : _vm._e(), _vm._v(\" \"), (!_vm.loading && (_vm.passwordReveal || _vm.statusType)) ? _c('b-icon', {\n staticClass: \"is-right\",\n class: {\n 'is-clickable': _vm.passwordReveal\n },\n attrs: {\n \"icon\": _vm.passwordReveal ? _vm.passwordVisibleIcon : _vm.statusTypeIcon,\n \"pack\": _vm.iconPack,\n \"size\": _vm.iconSize,\n \"type\": !_vm.passwordReveal ? _vm.statusType : 'is-primary',\n \"both\": \"\"\n },\n nativeOn: {\n \"click\": function($event) {\n _vm.togglePasswordVisibility($event)\n }\n }\n }) : _vm._e(), _vm._v(\" \"), (_vm.maxlength && _vm.hasCounter && _vm.type !== 'number') ? _c('small', {\n staticClass: \"help counter\",\n class: {\n 'is-invisible': !_vm.isFocused\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.valueLength) + \" / \" + _vm._s(_vm.maxlength) + \"\\n \")]) : _vm._e()], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 107 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"autocomplete control\",\n class: {\n 'is-expanded': _vm.expanded\n }\n }, [_c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"size\": _vm.size,\n \"loading\": _vm.loading,\n \"rounded\": _vm.rounded,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"maxlength\": _vm.maxlength,\n \"autocomplete\": \"off\"\n },\n on: {\n \"focus\": _vm.focused,\n \"blur\": _vm.onBlur\n },\n nativeOn: {\n \"keyup\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"esc\", 27, $event.key)) { return null; }\n $event.preventDefault();\n _vm.isActive = false\n },\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"tab\", 9, $event.key)) { return null; }\n _vm.tabPressed($event)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n $event.preventDefault();\n _vm.enterPressed($event)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"up\", 38, $event.key)) { return null; }\n $event.preventDefault();\n _vm.keyArrows('up')\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"down\", 40, $event.key)) { return null; }\n $event.preventDefault();\n _vm.keyArrows('down')\n }]\n },\n model: {\n value: (_vm.newValue),\n callback: function($$v) {\n _vm.newValue = $$v\n },\n expression: \"newValue\"\n }\n }, 'b-input', _vm.$attrs, false)), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive && (_vm.data.length > 0 || _vm.hasEmptySlot || _vm.hasHeaderSlot)),\n expression: \"isActive && (data.length > 0 || hasEmptySlot || hasHeaderSlot)\"\n }],\n ref: \"dropdown\",\n staticClass: \"dropdown-menu\",\n class: {\n 'is-opened-top': !_vm.isListInViewportVertically\n }\n }, [_c('div', {\n staticClass: \"dropdown-content\"\n }, [(_vm.hasHeaderSlot) ? _c('div', {\n staticClass: \"dropdown-item\"\n }, [_vm._t(\"header\")], 2) : _vm._e(), _vm._v(\" \"), _vm._l((_vm.data), function(option, index) {\n return _c('a', {\n key: index,\n staticClass: \"dropdown-item\",\n class: {\n 'is-hovered': option === _vm.hovered\n },\n on: {\n \"click\": function($event) {\n _vm.setSelected(option)\n }\n }\n }, [(_vm.hasDefaultSlot) ? _vm._t(\"default\", null, {\n option: option,\n index: index\n }) : _c('span', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.getValue(option, true))\n }\n })], 2)\n }), _vm._v(\" \"), (_vm.data.length === 0 && _vm.hasEmptySlot) ? _c('div', {\n staticClass: \"dropdown-item is-disabled\"\n }, [_vm._t(\"empty\")], 2) : _vm._e()], 2)])])], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 108 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BCheckbox',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n type: String,\n disabled: Boolean,\n required: Boolean,\n name: String,\n size: String,\n trueValue: {\n type: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n default: true\n },\n falseValue: {\n type: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n default: false\n }\n },\n data: function data() {\n return {\n newValue: this.value\n };\n },\n\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n }\n }\n});\n\n/***/ }),\n/* 109 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('label', {\n ref: \"label\",\n staticClass: \"b-checkbox checkbox\",\n class: [_vm.size, {\n 'is-disabled': _vm.disabled\n }],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n }\n }\n }, [_c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"checkbox\",\n \"disabled\": _vm.disabled,\n \"required\": _vm.required,\n \"name\": _vm.name,\n \"true-value\": _vm.trueValue,\n \"false-value\": _vm.falseValue\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": Array.isArray(_vm.newValue) ? _vm._i(_vm.newValue, _vm.nativeValue) > -1 : _vm._q(_vm.newValue, _vm.trueValue)\n },\n on: {\n \"change\": function($event) {\n var $$a = _vm.newValue,\n $$el = $event.target,\n $$c = $$el.checked ? (_vm.trueValue) : (_vm.falseValue);\n if (Array.isArray($$a)) {\n var $$v = _vm.nativeValue,\n $$i = _vm._i($$a, $$v);\n if ($$el.checked) {\n $$i < 0 && (_vm.newValue = $$a.concat([$$v]))\n } else {\n $$i > -1 && (_vm.newValue = $$a.slice(0, $$i).concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.newValue = $$c\n }\n }\n }\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"check\",\n class: _vm.type\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"control-label\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 110 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(111),\n /* template */\n __webpack_require__(112),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 111 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BCheckboxButton',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n disabled: Boolean,\n name: String,\n size: String,\n type: {\n type: String,\n default: 'is-primary'\n }\n },\n data: function data() {\n return {\n newValue: this.value\n };\n },\n\n computed: {\n checked: function checked() {\n if (Array.isArray(this.newValue)) {\n return this.newValue.indexOf(this.nativeValue) >= 0;\n }\n return this.newValue === this.nativeValue;\n }\n },\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n }\n }\n});\n\n/***/ }),\n/* 112 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"control\"\n }, [_c('label', {\n ref: \"label\",\n staticClass: \"b-checkbox checkbox button\",\n class: [_vm.checked ? _vm.type : null, _vm.size, {\n 'is-disabled': _vm.disabled\n }],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n }\n }\n }, [_vm._t(\"default\"), _vm._v(\" \"), _c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"checkbox\",\n \"disabled\": _vm.disabled,\n \"name\": _vm.name\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": Array.isArray(_vm.newValue) ? _vm._i(_vm.newValue, _vm.nativeValue) > -1 : (_vm.newValue)\n },\n on: {\n \"change\": function($event) {\n var $$a = _vm.newValue,\n $$el = $event.target,\n $$c = $$el.checked ? (true) : (false);\n if (Array.isArray($$a)) {\n var $$v = _vm.nativeValue,\n $$i = _vm._i($$a, $$v);\n if ($$el.checked) {\n $$i < 0 && (_vm.newValue = $$a.concat([$$v]))\n } else {\n $$i > -1 && (_vm.newValue = $$a.slice(0, $$i).concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.newValue = $$c\n }\n }\n }\n })], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 113 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(114),\n /* template */\n __webpack_require__(115),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 114 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BCollapse',\n props: {\n open: {\n type: Boolean,\n default: true\n },\n animation: {\n type: String,\n default: 'fade'\n }\n },\n data: function data() {\n return {\n isOpen: this.open\n };\n },\n\n watch: {\n open: function open(value) {\n this.isOpen = value;\n }\n },\n methods: {\n /**\n * Toggle and emit events\n */\n toggle: function toggle() {\n this.isOpen = !this.isOpen;\n this.$emit('update:open', this.isOpen);\n this.$emit(this.isOpen ? 'open' : 'close');\n }\n }\n});\n\n/***/ }),\n/* 115 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"collapse\"\n }, [_c('div', {\n staticClass: \"collapse-trigger\",\n on: {\n \"click\": _vm.toggle\n }\n }, [_vm._t(\"trigger\", null, {\n open: _vm.isOpen\n })], 2), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isOpen),\n expression: \"isOpen\"\n }],\n staticClass: \"collapse-content\"\n }, [_vm._t(\"default\")], 2)])], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 116 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(117),\n /* template */\n __webpack_require__(134),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 117 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__ = __webpack_require__(12);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__ = __webpack_require__(43);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__ = __webpack_require__(44);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__input_Input__ = __webpack_require__(27);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__input_Input___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6__input_Input__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__field_Field__ = __webpack_require__(45);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__field_Field___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7__field_Field__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__select_Select__ = __webpack_require__(28);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__select_Select___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_8__select_Select__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_9__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__DatepickerTable__ = __webpack_require__(128);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__DatepickerTable___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_10__DatepickerTable__);\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDatepicker',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_10__DatepickerTable___default.a.name, __WEBPACK_IMPORTED_MODULE_10__DatepickerTable___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_6__input_Input___default.a.name, __WEBPACK_IMPORTED_MODULE_6__input_Input___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_7__field_Field___default.a.name, __WEBPACK_IMPORTED_MODULE_7__field_Field___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_8__select_Select___default.a.name, __WEBPACK_IMPORTED_MODULE_8__select_Select___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default.a.name, __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default.a.name, __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default.a), _components),\n mixins: [__WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: Date,\n dayNames: {\n type: Array,\n default: function _default() {\n if (Array.isArray(__WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDayNames)) {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDayNames;\n } else {\n return ['Su', 'M', 'Tu', 'W', 'Th', 'F', 'S'];\n }\n }\n },\n monthNames: {\n type: Array,\n default: function _default() {\n if (Array.isArray(__WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultMonthNames)) {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultMonthNames;\n } else {\n return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\n }\n }\n },\n firstDayOfWeek: {\n type: Number,\n default: function _default() {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultFirstDayOfWeek === 'number') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultFirstDayOfWeek;\n } else {\n return 0;\n }\n }\n },\n inline: Boolean,\n minDate: Date,\n maxDate: Date,\n focusedDate: Date,\n placeholder: String,\n readonly: {\n type: Boolean,\n default: true\n },\n disabled: {\n type: Boolean,\n default: false\n },\n unselectableDates: Array,\n unselectableDaysOfWeek: {\n type: Array,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultUnselectableDaysOfWeek;\n }\n },\n selectableDates: Array,\n dateFormatter: {\n type: Function,\n default: function _default(date) {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDateFormatter === 'function') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDateFormatter(date);\n } else {\n var yyyyMMdd = date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();\n var d = new Date(yyyyMMdd);\n return d.toLocaleDateString();\n }\n }\n },\n dateParser: {\n type: Function,\n default: function _default(date) {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDateParser === 'function') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDateParser(date);\n } else {\n return new Date(Date.parse(date));\n }\n }\n },\n mobileNative: {\n type: Boolean,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDatepickerMobileNative;\n }\n },\n position: String,\n events: Array,\n indicators: {\n type: String,\n default: 'dots'\n }\n },\n data: function data() {\n var focusedDate = this.value || this.focusedDate || new Date();\n\n return {\n dateSelected: this.value,\n focusedDateData: {\n month: focusedDate.getMonth(),\n year: focusedDate.getFullYear()\n },\n _elementRef: 'input',\n _isDatepicker: true\n };\n },\n\n computed: {\n /*\n * Returns an array of years for the year dropdown. If earliest/latest\n * dates are set by props, range of years will fall within those dates.\n */\n listOfYears: function listOfYears() {\n var latestYear = this.maxDate ? this.maxDate.getFullYear() : Math.max(new Date().getFullYear(), this.focusedDateData.year) + 3;\n\n var earliestYear = this.minDate ? this.minDate.getFullYear() : 1900;\n\n var arrayOfYears = [];\n for (var i = earliestYear; i <= latestYear; i++) {\n arrayOfYears.push(i);\n }\n\n return arrayOfYears.reverse();\n },\n isFirstMonth: function isFirstMonth() {\n if (!this.minDate) return false;\n var dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month);\n var date = new Date(this.minDate.getFullYear(), this.minDate.getMonth());\n return dateToCheck <= date;\n },\n isLastMonth: function isLastMonth() {\n if (!this.maxDate) return false;\n var dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month);\n var date = new Date(this.maxDate.getFullYear(), this.maxDate.getMonth());\n return dateToCheck >= date;\n },\n isMobile: function isMobile() {\n return this.mobileNative && __WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"d\" /* isMobile */].any();\n }\n },\n watch: {\n /*\n * Emit input event with selected date as payload, set isActive to false.\n * Update internal focusedDateData\n */\n dateSelected: function dateSelected(value) {\n var currentDate = !value ? new Date() : value;\n this.focusedDateData = {\n month: currentDate.getMonth(),\n year: currentDate.getFullYear()\n };\n this.$emit('input', value);\n if (this.$refs.dropdown) {\n this.$refs.dropdown.isActive = false;\n }\n },\n\n\n /**\n * When v-model is changed:\n * 1. Update internal value.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.dateSelected = _value;\n\n !this.isValid && this.$refs.input.checkHtml5Validity();\n },\n focusedDate: function focusedDate(value) {\n if (value) {\n this.focusedDateData = {\n month: value.getMonth(),\n year: value.getFullYear()\n };\n }\n },\n\n\n /*\n * Emit input event on month and/or year change\n */\n 'focusedDateData.month': function focusedDateDataMonth(value) {\n this.$emit('change-month', value);\n },\n 'focusedDateData.year': function focusedDateDataYear(value) {\n this.$emit('change-year', value);\n }\n },\n methods: {\n /*\n * Emit input event with selected date as payload for v-model in parent\n */\n updateSelectedDate: function updateSelectedDate(date) {\n this.dateSelected = date;\n },\n\n\n /*\n * Parse string into date\n */\n onChange: function onChange(value) {\n var date = this.dateParser(value);\n if (date && !isNaN(date)) {\n this.dateSelected = date;\n } else {\n // Force refresh input value when not valid date\n this.dateSelected = null;\n this.$refs.input.newValue = this.dateSelected;\n }\n },\n\n\n /*\n * Format date into string\n */\n formatValue: function formatValue(value) {\n if (value && !isNaN(value)) {\n return this.dateFormatter(value);\n } else {\n return null;\n }\n },\n\n\n /*\n * Either decrement month by 1 if not January or decrement year by 1\n * and set month to 11 (December)\n */\n decrementMonth: function decrementMonth() {\n if (this.disabled) return;\n\n if (this.focusedDateData.month > 0) {\n this.focusedDateData.month -= 1;\n } else {\n this.focusedDateData.month = 11;\n this.focusedDateData.year -= 1;\n }\n },\n\n\n /*\n * Either increment month by 1 if not December or increment year by 1\n * and set month to 0 (January)\n */\n incrementMonth: function incrementMonth() {\n if (this.disabled) return;\n\n if (this.focusedDateData.month < 11) {\n this.focusedDateData.month += 1;\n } else {\n this.focusedDateData.month = 0;\n this.focusedDateData.year += 1;\n }\n },\n\n\n /*\n * Format date into string 'YYYY-MM-DD'\n */\n formatYYYYMMDD: function formatYYYYMMDD(value) {\n var date = new Date(value);\n if (value && !isNaN(date)) {\n var year = date.getFullYear();\n var month = date.getMonth() + 1;\n var day = date.getDate();\n return year + '-' + ((month < 10 ? '0' : '') + month) + '-' + ((day < 10 ? '0' : '') + day);\n }\n return '';\n },\n\n\n /*\n * Parse date from string\n */\n onChangeNativePicker: function onChangeNativePicker(event) {\n var date = event.target.value;\n this.dateSelected = date ? new Date(date.replace(/-/g, '/')) : null;\n }\n }\n});\n\n/***/ }),\n/* 118 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator__ = __webpack_require__(59);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol__);\n\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDropdown',\n props: {\n value: {\n type: [String, Number, Boolean, Object, Array, __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol___default.a, Function],\n default: null\n },\n disabled: Boolean,\n hoverable: Boolean,\n inline: Boolean,\n position: {\n type: String,\n validator: function validator(value) {\n return ['is-top-right', 'is-top-left', 'is-bottom-left'].indexOf(value) > -1;\n }\n },\n mobileModal: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n return {\n selected: this.value,\n isActive: false,\n _isDropdown: true // Used internally by DropdownItem\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return [this.position, {\n 'is-disabled': this.disabled,\n 'is-hoverable': this.hoverable,\n 'is-inline': this.inline,\n 'is-active': this.isActive || this.inline,\n 'is-mobile-modal': this.isMobileModal\n }];\n },\n isMobileModal: function isMobileModal() {\n return this.mobileModal && !this.inline && !this.hoverable;\n }\n },\n watch: {\n /**\n * When v-model is changed set the new selected item.\n */\n value: function value(_value) {\n this.selected = _value;\n },\n\n\n /**\n * Emit event when isActive value is changed.\n */\n isActive: function isActive(value) {\n this.$emit('active-change', value);\n }\n },\n methods: {\n /**\n * Click listener from DropdownItem.\n * 1. Set new selected item.\n * 2. Emit input event to update the user v-model.\n * 3. Close the dropdown.\n */\n selectItem: function selectItem(value) {\n if (this.selected !== value) {\n this.$emit('change', value);\n this.selected = value;\n }\n this.$emit('input', value);\n this.isActive = false;\n },\n\n\n /**\n * White-listed items to not close when clicked.\n */\n isInWhiteList: function isInWhiteList(el) {\n if (el === this.$refs.dropdownMenu) return true;\n if (el === this.$refs.trigger) return true;\n // All chidren from dropdown\n if (this.$refs.dropdownMenu !== undefined) {\n var children = this.$refs.dropdownMenu.querySelectorAll('*');\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator___default()(children), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var child = _step.value;\n\n if (el === child) {\n return true;\n }\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n }\n // All children from trigger\n if (this.$refs.trigger !== undefined) {\n var _children = this.$refs.trigger.querySelectorAll('*');\n var _iteratorNormalCompletion2 = true;\n var _didIteratorError2 = false;\n var _iteratorError2 = undefined;\n\n try {\n for (var _iterator2 = __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator___default()(_children), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {\n var _child = _step2.value;\n\n if (el === _child) {\n return true;\n }\n }\n } catch (err) {\n _didIteratorError2 = true;\n _iteratorError2 = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion2 && _iterator2.return) {\n _iterator2.return();\n }\n } finally {\n if (_didIteratorError2) {\n throw _iteratorError2;\n }\n }\n }\n }\n\n return false;\n },\n\n\n /**\n * Close dropdown if clicked outside.\n */\n clickedOutside: function clickedOutside(event) {\n if (this.inline) return;\n\n if (!this.isInWhiteList(event.target)) this.isActive = false;\n },\n\n\n /**\n * Toggle dropdown if it's not disabled.\n */\n toggle: function toggle() {\n var _this = this;\n\n if (this.disabled || this.hoverable) return;\n\n if (!this.isActive) {\n // if not active, toggle after clickOutside event\n // this fixes toggling programmatic\n this.$nextTick(function () {\n _this.isActive = !_this.isActive;\n });\n } else {\n this.isActive = !this.isActive;\n }\n }\n },\n created: function created() {\n if (typeof window !== 'undefined') {\n document.addEventListener('click', this.clickedOutside);\n }\n },\n beforeDestroy: function beforeDestroy() {\n if (typeof window !== 'undefined') {\n document.removeEventListener('click', this.clickedOutside);\n }\n }\n});\n\n/***/ }),\n/* 119 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"dropdown\",\n class: _vm.rootClasses\n }, [(!_vm.inline) ? _c('div', {\n ref: \"trigger\",\n staticClass: \"dropdown-trigger\",\n attrs: {\n \"role\": \"button\"\n },\n on: {\n \"click\": _vm.toggle\n }\n }, [_vm._t(\"trigger\")], 2) : _vm._e(), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [(_vm.isMobileModal) ? _c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive),\n expression: \"isActive\"\n }],\n staticClass: \"background\"\n }) : _vm._e()]), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: ((!_vm.disabled && (_vm.isActive || _vm.hoverable)) || _vm.inline),\n expression: \"(!disabled && (isActive || hoverable)) || inline\"\n }],\n ref: \"dropdownMenu\",\n staticClass: \"dropdown-menu\"\n }, [_c('div', {\n staticClass: \"dropdown-content\"\n }, [_vm._t(\"default\")], 2)])])], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 120 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDropdownItem',\n props: {\n value: {\n type: [String, Number, Boolean, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a, Function],\n default: null\n },\n separator: Boolean,\n disabled: Boolean,\n custom: Boolean,\n paddingless: Boolean,\n hasLink: Boolean\n },\n computed: {\n anchorClasses: function anchorClasses() {\n return {\n 'is-disabled': this.$parent.disabled || this.disabled,\n 'is-paddingless': this.paddingless,\n 'is-active': this.value !== null && this.value === this.$parent.selected\n };\n },\n itemClasses: function itemClasses() {\n return {\n 'dropdown-item': !this.hasLink,\n 'is-disabled': this.disabled,\n 'is-paddingless': this.paddingless,\n 'is-active': this.value !== null && this.value === this.$parent.selected,\n 'has-link': this.hasLink\n };\n },\n\n /**\n * Check if item can be clickable.\n */\n isClickable: function isClickable() {\n return !this.$parent.disabled && !this.separator && !this.disabled && !this.custom;\n }\n },\n methods: {\n /**\n * Click listener, select the item.\n */\n selectItem: function selectItem() {\n if (!this.isClickable) return;\n\n this.$parent.selectItem(this.value);\n this.$emit('click');\n }\n },\n created: function created() {\n if (!this.$parent.$data._isDropdown) {\n this.$destroy();\n throw new Error('You should wrap bDropdownItem on a bDropdown');\n }\n }\n});\n\n/***/ }),\n/* 121 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return (_vm.separator) ? _c('hr', {\n staticClass: \"dropdown-divider\"\n }) : (!_vm.custom && !_vm.hasLink) ? _c('a', {\n staticClass: \"dropdown-item\",\n class: _vm.anchorClasses,\n on: {\n \"click\": _vm.selectItem\n }\n }, [_vm._t(\"default\")], 2) : _c('div', {\n class: _vm.itemClasses,\n on: {\n \"click\": _vm.selectItem\n }\n }, [_vm._t(\"default\")], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 122 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__FieldBody__ = __webpack_require__(123);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__FieldBody___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__FieldBody__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BField',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__FieldBody___default.a.name, __WEBPACK_IMPORTED_MODULE_1__FieldBody___default.a),\n props: {\n type: String,\n label: String,\n labelFor: String,\n message: [String, Array],\n grouped: Boolean,\n groupMultiline: Boolean,\n position: String,\n expanded: Boolean,\n horizontal: Boolean,\n addons: {\n type: Boolean,\n default: true\n },\n customClass: String\n },\n data: function data() {\n return {\n newType: this.type,\n newMessage: this.message,\n fieldLabelSize: null,\n _isField: true // Used internally by Input and Select\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return [this.newPosition, {\n 'is-expanded': this.expanded,\n 'is-grouped-multiline': this.groupMultiline,\n 'is-horizontal': this.horizontal\n }];\n },\n\n /**\n * Correct Bulma class for the side of the addon or group.\n *\n * This is not kept like the others (is-small, etc.),\n * because since 'has-addons' is set automatically it\n * doesn't make sense to teach users what addons are exactly.\n */\n newPosition: function newPosition() {\n if (this.position === undefined) return;\n\n var position = this.position.split('-');\n if (position.length < 1) return;\n\n var prefix = this.grouped ? 'is-grouped-' : 'has-addons-';\n\n if (this.position) return prefix + position[1];\n },\n\n /**\n * Formatted message in case it's an array\n * (each element is separated by <br> tag)\n */\n formattedMessage: function formattedMessage() {\n if (this.newMessage) {\n if (Array.isArray(this.newMessage)) {\n return this.newMessage.filter(function (value) {\n if (value) {\n return value;\n }\n }).join(' <br> ');\n } else {\n return this.newMessage;\n }\n } else {\n return this.newMessage;\n }\n }\n },\n watch: {\n /**\n * Set internal type when prop change.\n */\n type: function type(value) {\n this.newType = value;\n },\n\n\n /**\n * Set internal message when prop change.\n */\n message: function message(value) {\n this.newMessage = value;\n }\n },\n methods: {\n /**\n * Field has addons if there are more than one slot\n * (element / component) in the Field.\n * Or is grouped when prop is set.\n * Is a method to be called when component re-render.\n */\n fieldType: function fieldType() {\n if (this.grouped) return 'is-grouped';\n\n var renderedNode = 0;\n if (this.$slots.default) {\n renderedNode = this.$slots.default.reduce(function (i, node) {\n return node.tag ? i + 1 : i;\n }, 0);\n }\n if (renderedNode > 1 && this.addons && !this.horizontal) {\n return 'has-addons';\n }\n }\n },\n mounted: function mounted() {\n if (this.horizontal) {\n // Bulma docs: .is-normal for any .input or .button\n var elements = this.$el.querySelectorAll('.input, .select, .button, .textarea');\n if (elements.length > 0) {\n this.fieldLabelSize = 'is-normal';\n }\n }\n }\n});\n\n/***/ }),\n/* 123 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(124),\n /* template */\n null,\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 124 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BFieldBody',\n props: {\n message: {\n type: [String]\n },\n type: {\n type: [String]\n }\n },\n render: function render(h) {\n var _this = this;\n\n return h('div', { attrs: { 'class': 'field-body' } }, this.$slots.default.map(function (v) {\n // skip returns and comments\n if (!v.tag) {\n return v;\n }\n if (_this.message) {\n return h('b-field', { attrs: { message: _this.message, 'type': _this.type } }, [v]);\n }\n return h('b-field', { attrs: { 'type': _this.type } }, [v]);\n }));\n }\n});\n\n/***/ }),\n/* 125 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"field\",\n class: [_vm.rootClasses, _vm.fieldType()]\n }, [(_vm.horizontal) ? _c('div', {\n staticClass: \"field-label\",\n class: [_vm.customClass, _vm.fieldLabelSize]\n }, [(_vm.label) ? _c('label', {\n staticClass: \"label\",\n attrs: {\n \"for\": _vm.labelFor\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.label) + \"\\n \")]) : _vm._e()]) : [(_vm.label) ? _c('label', {\n staticClass: \"label\",\n class: _vm.customClass,\n attrs: {\n \"for\": _vm.labelFor\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.label) + \"\\n \")]) : _vm._e()], _vm._v(\" \"), (_vm.horizontal) ? _c('b-field-body', {\n attrs: {\n \"message\": _vm.newMessage ? _vm.formattedMessage : '',\n \"type\": _vm.newType\n }\n }, [_vm._t(\"default\")], 2) : [_vm._t(\"default\")], _vm._v(\" \"), (_vm.newMessage && !_vm.horizontal) ? _c('p', {\n staticClass: \"help\",\n class: _vm.newType,\n domProps: {\n \"innerHTML\": _vm._s(_vm.formattedMessage)\n }\n }) : _vm._e()], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 126 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__ = __webpack_require__(12);\n\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BSelect',\n components: __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: {\n type: [String, Number, Boolean, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a, Function],\n default: null\n },\n placeholder: String,\n multiple: Boolean,\n nativeSize: [String, Number]\n },\n data: function data() {\n return {\n selected: this.value,\n _isSelect: true,\n _elementRef: 'select'\n };\n },\n\n computed: {\n spanClasses: function spanClasses() {\n return [this.size, this.statusType, {\n 'is-fullwidth': this.expanded,\n 'is-loading': this.loading,\n 'is-multiple': this.multiple,\n 'is-rounded': this.rounded,\n 'is-empty': this.selected === null\n }];\n }\n },\n watch: {\n /**\n * When v-model is changed:\n * 1. Set the selected option.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.selected = _value;\n !this.isValid && this.checkHtml5Validity();\n },\n\n /**\n * When selected:\n * 1. Emit input event to update the user v-model.\n * 2. If it's invalid, validate again.\n */\n selected: function selected(value) {\n this.$emit('input', value);\n !this.isValid && this.checkHtml5Validity();\n }\n }\n});\n\n/***/ }),\n/* 127 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"control\",\n class: {\n 'is-expanded': _vm.expanded, 'has-icons-left': _vm.icon\n }\n }, [_c('span', {\n staticClass: \"select\",\n class: _vm.spanClasses\n }, [_c('select', _vm._b({\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.selected),\n expression: \"selected\"\n }],\n ref: \"select\",\n attrs: {\n \"multiple\": _vm.multiple,\n \"size\": _vm.nativeSize\n },\n on: {\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n },\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"change\": function($event) {\n var $$selectedVal = Array.prototype.filter.call($event.target.options, function(o) {\n return o.selected\n }).map(function(o) {\n var val = \"_value\" in o ? o._value : o.value;\n return val\n });\n _vm.selected = $event.target.multiple ? $$selectedVal : $$selectedVal[0]\n }\n }\n }, 'select', _vm.$attrs, false), [(_vm.placeholder) ? _c('option', {\n attrs: {\n \"selected\": \"\",\n \"disabled\": \"\",\n \"hidden\": \"\"\n },\n domProps: {\n \"value\": null\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.placeholder) + \"\\n \")]) : _vm._e(), _vm._v(\" \"), _vm._t(\"default\")], 2)]), _vm._v(\" \"), (_vm.icon) ? _c('b-icon', {\n staticClass: \"is-left\",\n attrs: {\n \"icon\": _vm.icon,\n \"pack\": _vm.iconPack,\n \"size\": _vm.iconSize\n }\n }) : _vm._e()], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 128 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(129),\n /* template */\n __webpack_require__(133),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 129 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow__ = __webpack_require__(130);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDatepickerTable',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow___default.a.name, __WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow___default.a),\n props: {\n value: Date,\n dayNames: Array,\n monthNames: Array,\n firstDayOfWeek: Number,\n events: Array,\n indicators: String,\n minDate: Date,\n maxDate: Date,\n focused: Object,\n disabled: Boolean,\n unselectableDates: Array,\n unselectableDaysOfWeek: Array,\n selectableDates: Array\n },\n computed: {\n visibleDayNames: function visibleDayNames() {\n var visibleDayNames = [];\n var index = this.firstDayOfWeek;\n while (visibleDayNames.length < this.dayNames.length) {\n var currentDayName = this.dayNames[index % this.dayNames.length];\n visibleDayNames.push(currentDayName);\n index++;\n }\n return visibleDayNames;\n },\n hasEvents: function hasEvents() {\n return this.events && this.events.length;\n },\n\n\n /*\n * Return array of all events in the specified month\n */\n eventsInThisMonth: function eventsInThisMonth() {\n if (!this.events) return [];\n\n var monthEvents = [];\n\n for (var i = 0; i < this.events.length; i++) {\n var event = this.events[i];\n\n if (!event.hasOwnProperty('date')) {\n event = { date: event };\n }\n if (!event.hasOwnProperty('type')) {\n event.type = 'is-primary';\n }\n if (event.date.getMonth() === this.focused.month && event.date.getFullYear() === this.focused.year) {\n monthEvents.push(event);\n }\n }\n\n return monthEvents;\n }\n },\n methods: {\n /*\n * Emit input event with selected date as payload for v-model in parent\n */\n updateSelectedDate: function updateSelectedDate(date) {\n this.$emit('input', date);\n },\n\n\n /*\n * Return array of all days in the week that the startingDate is within\n */\n weekBuilder: function weekBuilder(startingDate, month, year) {\n var thisMonth = new Date(year, month);\n\n var thisWeek = [];\n\n var dayOfWeek = new Date(year, month, startingDate).getDay();\n\n var end = dayOfWeek >= this.firstDayOfWeek ? dayOfWeek - this.firstDayOfWeek : 7 - this.firstDayOfWeek + dayOfWeek;\n\n var daysAgo = 1;\n for (var i = 0; i < end; i++) {\n thisWeek.unshift(new Date(thisMonth.getFullYear(), thisMonth.getMonth(), startingDate - daysAgo));\n daysAgo++;\n }\n\n thisWeek.push(new Date(year, month, startingDate));\n\n var daysForward = 1;\n while (thisWeek.length < 7) {\n thisWeek.push(new Date(year, month, startingDate + daysForward));\n daysForward++;\n }\n\n return thisWeek;\n },\n\n\n /*\n * Return array of all weeks in the specified month\n */\n weeksInThisMonth: function weeksInThisMonth(month, year) {\n var weeksInThisMonth = [];\n var daysInThisMonth = new Date(year, month + 1, 0).getDate();\n\n var startingDay = 1;\n\n while (startingDay <= daysInThisMonth + 6) {\n var newWeek = this.weekBuilder(startingDay, month, year);\n var weekValid = false;\n\n newWeek.forEach(function (day) {\n if (day.getMonth() === month) {\n weekValid = true;\n }\n });\n\n if (weekValid) {\n weeksInThisMonth.push(newWeek);\n }\n\n startingDay += 7;\n }\n\n return weeksInThisMonth;\n },\n eventsInThisWeek: function eventsInThisWeek(week, index) {\n if (!this.eventsInThisMonth.length) return [];\n\n var weekEvents = [];\n\n var weeksInThisMonth = [];\n weeksInThisMonth = this.weeksInThisMonth(this.focused.month, this.focused.year);\n\n for (var d = 0; d < weeksInThisMonth[index].length; d++) {\n for (var e = 0; e < this.eventsInThisMonth.length; e++) {\n var eventsInThisMonth = this.eventsInThisMonth[e].date.getTime();\n if (eventsInThisMonth === weeksInThisMonth[index][d].getTime()) {\n weekEvents.push(this.eventsInThisMonth[e]);\n }\n }\n }\n\n return weekEvents;\n }\n }\n});\n\n/***/ }),\n/* 130 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(131),\n /* template */\n __webpack_require__(132),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 131 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDatepickerTableRow',\n props: {\n selectedDate: Date,\n week: {\n type: Array,\n required: true\n },\n month: {\n type: Number,\n required: true\n },\n minDate: Date,\n maxDate: Date,\n disabled: Boolean,\n unselectableDates: Array,\n unselectableDaysOfWeek: Array,\n selectableDates: Array,\n events: Array,\n indicators: String\n },\n methods: {\n /*\n * Check that selected day is within earliest/latest params and\n * is within this month\n */\n selectableDate: function selectableDate(day) {\n var validity = [];\n\n if (this.minDate) {\n validity.push(day >= this.minDate);\n }\n\n if (this.maxDate) {\n validity.push(day <= this.maxDate);\n }\n\n validity.push(day.getMonth() === this.month);\n\n if (this.selectableDates) {\n for (var i = 0; i < this.selectableDates.length; i++) {\n var enabledDate = this.selectableDates[i];\n if (day.getDate() === enabledDate.getDate() && day.getFullYear() === enabledDate.getFullYear() && day.getMonth() === enabledDate.getMonth()) {\n return true;\n } else {\n validity.push(false);\n }\n }\n }\n\n if (this.unselectableDates) {\n for (var _i = 0; _i < this.unselectableDates.length; _i++) {\n var disabledDate = this.unselectableDates[_i];\n validity.push(day.getDate() !== disabledDate.getDate() || day.getFullYear() !== disabledDate.getFullYear() || day.getMonth() !== disabledDate.getMonth());\n }\n }\n\n if (this.unselectableDaysOfWeek) {\n for (var _i2 = 0; _i2 < this.unselectableDaysOfWeek.length; _i2++) {\n var dayOfWeek = this.unselectableDaysOfWeek[_i2];\n validity.push(day.getDay() !== dayOfWeek);\n }\n }\n\n return validity.indexOf(false) < 0;\n },\n\n\n /*\n * Emit select event with chosen date as payload\n */\n emitChosenDate: function emitChosenDate(day) {\n if (this.disabled) return;\n\n if (this.selectableDate(day)) {\n this.$emit('select', day);\n }\n },\n eventsDateMatch: function eventsDateMatch(day) {\n if (!this.events.length) return false;\n\n var dayEvents = [];\n\n for (var i = 0; i < this.events.length; i++) {\n if (this.events[i].date.getDay() === day.getDay()) {\n dayEvents.push(this.events[i]);\n }\n }\n\n if (!dayEvents.length) {\n return false;\n }\n\n return dayEvents;\n },\n\n\n /*\n * Build classObject for cell using validations\n */\n classObject: function classObject(day) {\n function dateMatch(dateOne, dateTwo) {\n // if either date is null or undefined, return false\n if (!dateOne || !dateTwo) {\n return false;\n }\n\n return dateOne.getDate() === dateTwo.getDate() && dateOne.getFullYear() === dateTwo.getFullYear() && dateOne.getMonth() === dateTwo.getMonth();\n }\n\n return {\n 'is-selected': dateMatch(day, this.selectedDate),\n 'is-today': dateMatch(day, new Date()),\n 'is-selectable': this.selectableDate(day) && !this.disabled,\n 'is-unselectable': !this.selectableDate(day) || this.disabled\n };\n }\n }\n});\n\n/***/ }),\n/* 132 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"datepicker-row\"\n }, [_vm._l((_vm.week), function(day, index) {\n return [(_vm.selectableDate(day) && !_vm.disabled) ? _c('a', {\n key: index,\n staticClass: \"datepicker-cell\",\n class: [_vm.classObject(day), {\n 'has-event': _vm.eventsDateMatch(day)\n }, _vm.indicators],\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.emitChosenDate(day)\n },\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n $event.preventDefault();\n _vm.emitChosenDate(day)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.emitChosenDate(day)\n }]\n }\n }, [_vm._v(\"\\n \" + _vm._s(day.getDate()) + \"\\n\\n \"), (_vm.eventsDateMatch(day)) ? _c('div', {\n staticClass: \"events\"\n }, _vm._l((_vm.eventsDateMatch(day)), function(event, index) {\n return _c('div', {\n key: index,\n staticClass: \"event\",\n class: event.type\n })\n })) : _vm._e()]) : _c('div', {\n key: index,\n staticClass: \"datepicker-cell\",\n class: _vm.classObject(day)\n }, [_vm._v(\"\\n \" + _vm._s(day.getDate()) + \"\\n \")])]\n })], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 133 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('section', {\n staticClass: \"datepicker-table\"\n }, [_c('header', {\n staticClass: \"datepicker-header\"\n }, _vm._l((_vm.visibleDayNames), function(day, index) {\n return _c('div', {\n key: index,\n staticClass: \"datepicker-cell\"\n }, [_vm._v(\"\\n \" + _vm._s(day) + \"\\n \")])\n })), _vm._v(\" \"), _c('div', {\n staticClass: \"datepicker-body\",\n class: {\n 'has-events': _vm.hasEvents\n }\n }, _vm._l((_vm.weeksInThisMonth(_vm.focused.month, _vm.focused.year)), function(week, index) {\n return _c('b-datepicker-table-row', {\n key: index,\n attrs: {\n \"selected-date\": _vm.value,\n \"week\": week,\n \"month\": _vm.focused.month,\n \"min-date\": _vm.minDate,\n \"max-date\": _vm.maxDate,\n \"disabled\": _vm.disabled,\n \"unselectable-dates\": _vm.unselectableDates,\n \"unselectable-days-of-week\": _vm.unselectableDaysOfWeek,\n \"selectable-dates\": _vm.selectableDates,\n \"events\": _vm.eventsInThisWeek(week, index),\n \"indicators\": _vm.indicators\n },\n on: {\n \"select\": _vm.updateSelectedDate\n }\n })\n }))])\n},staticRenderFns: []}\n\n/***/ }),\n/* 134 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"datepicker control\",\n class: [_vm.size, {\n 'is-expanded': _vm.expanded\n }]\n }, [(!_vm.isMobile || _vm.inline) ? _c('b-dropdown', {\n ref: \"dropdown\",\n attrs: {\n \"position\": _vm.position,\n \"disabled\": _vm.disabled,\n \"inline\": _vm.inline\n }\n }, [(!_vm.inline) ? _c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"slot\": \"trigger\",\n \"autocomplete\": \"off\",\n \"value\": _vm.formatValue(_vm.dateSelected),\n \"placeholder\": _vm.placeholder,\n \"size\": _vm.size,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"rounded\": _vm.rounded,\n \"loading\": _vm.loading,\n \"disabled\": _vm.disabled,\n \"readonly\": _vm.readonly\n },\n on: {\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n }\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onChange($event.target.value)\n }\n },\n slot: \"trigger\"\n }, 'b-input', _vm.$attrs, false)) : _vm._e(), _vm._v(\" \"), _c('b-dropdown-item', {\n attrs: {\n \"disabled\": _vm.disabled,\n \"custom\": \"\"\n }\n }, [_c('header', {\n staticClass: \"datepicker-header\"\n }, [(_vm.$slots.header !== undefined && _vm.$slots.header.length) ? [_vm._t(\"header\")] : _c('div', {\n staticClass: \"pagination field is-centered\"\n }, [(!_vm.isFirstMonth && !_vm.disabled) ? _c('a', {\n staticClass: \"pagination-previous\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.decrementMonth($event)\n },\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n $event.preventDefault();\n _vm.decrementMonth($event)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.decrementMonth($event)\n }]\n }\n }, [_c('b-icon', {\n attrs: {\n \"icon\": \"chevron-left\",\n \"pack\": _vm.iconPack,\n \"both\": \"\",\n \"type\": \"is-primary is-clickable\"\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _c('a', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (!_vm.isLastMonth && !_vm.disabled),\n expression: \"!isLastMonth && !disabled\"\n }],\n staticClass: \"pagination-next\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.incrementMonth($event)\n },\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n $event.preventDefault();\n _vm.incrementMonth($event)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.incrementMonth($event)\n }]\n }\n }, [_c('b-icon', {\n attrs: {\n \"icon\": \"chevron-right\",\n \"pack\": _vm.iconPack,\n \"both\": \"\",\n \"type\": \"is-primary is-clickable\"\n }\n })], 1), _vm._v(\" \"), _c('div', {\n staticClass: \"pagination-list\"\n }, [_c('b-field', [_c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled\n },\n model: {\n value: (_vm.focusedDateData.month),\n callback: function($$v) {\n _vm.$set(_vm.focusedDateData, \"month\", $$v)\n },\n expression: \"focusedDateData.month\"\n }\n }, _vm._l((_vm.monthNames), function(month, index) {\n return _c('option', {\n key: month,\n domProps: {\n \"value\": index\n }\n }, [_vm._v(\"\\n \" + _vm._s(month) + \"\\n \")])\n })), _vm._v(\" \"), _c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled\n },\n model: {\n value: (_vm.focusedDateData.year),\n callback: function($$v) {\n _vm.$set(_vm.focusedDateData, \"year\", $$v)\n },\n expression: \"focusedDateData.year\"\n }\n }, _vm._l((_vm.listOfYears), function(year) {\n return _c('option', {\n key: year,\n domProps: {\n \"value\": year\n }\n }, [_vm._v(\"\\n \" + _vm._s(year) + \"\\n \")])\n }))], 1)], 1)])], 2), _vm._v(\" \"), _c('b-datepicker-table', {\n attrs: {\n \"day-names\": _vm.dayNames,\n \"month-names\": _vm.monthNames,\n \"first-day-of-week\": _vm.firstDayOfWeek,\n \"min-date\": _vm.minDate,\n \"max-date\": _vm.maxDate,\n \"focused\": _vm.focusedDateData,\n \"disabled\": _vm.disabled,\n \"unselectable-dates\": _vm.unselectableDates,\n \"unselectable-days-of-week\": _vm.unselectableDaysOfWeek,\n \"selectable-dates\": _vm.selectableDates,\n \"events\": _vm.events,\n \"indicators\": _vm.indicators\n },\n on: {\n \"close\": function($event) {\n _vm.$refs.dropdown.isActive = false\n }\n },\n model: {\n value: (_vm.dateSelected),\n callback: function($$v) {\n _vm.dateSelected = $$v\n },\n expression: \"dateSelected\"\n }\n }), _vm._v(\" \"), (_vm.$slots.default !== undefined && _vm.$slots.default.length) ? _c('footer', {\n staticClass: \"datepicker-footer\"\n }, [_vm._t(\"default\")], 2) : _vm._e()], 1)], 1) : _c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"type\": \"date\",\n \"autocomplete\": \"off\",\n \"value\": _vm.formatYYYYMMDD(_vm.value),\n \"placeholder\": _vm.placeholder,\n \"size\": _vm.size,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"loading\": _vm.loading,\n \"max\": _vm.formatYYYYMMDD(_vm.maxDate),\n \"min\": _vm.formatYYYYMMDD(_vm.minDate),\n \"disabled\": _vm.disabled,\n \"readonly\": false\n },\n on: {\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n }\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onChangeNativePicker($event)\n }\n }\n }, 'b-input', _vm.$attrs, false))], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 135 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(136),\n /* template */\n __webpack_require__(139),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 136 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__modal_Modal__ = __webpack_require__(62);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__modal_Modal___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__modal_Modal__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__utils_BaseElementMixin__ = __webpack_require__(13);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDialog',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a),\n extends: __WEBPACK_IMPORTED_MODULE_2__modal_Modal___default.a,\n mixins: [__WEBPACK_IMPORTED_MODULE_5__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n title: String,\n message: String,\n icon: String,\n hasIcon: Boolean,\n type: {\n type: String,\n default: 'is-primary'\n },\n size: String,\n confirmText: {\n type: String,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDialogConfirmText ? __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDialogConfirmText : 'OK';\n }\n },\n cancelText: {\n type: String,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDialogCancelText ? __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDialogCancelText : 'Cancel';\n }\n },\n hasInput: Boolean, // Used internally to know if it's prompt\n inputAttrs: {\n type: Object,\n default: function _default() {\n return {};\n }\n },\n onConfirm: {\n type: Function,\n default: function _default() {}\n },\n focusOn: {\n type: String,\n default: 'confirm'\n }\n },\n data: function data() {\n var prompt = this.hasInput ? this.inputAttrs.value || '' : '';\n\n return {\n prompt: prompt,\n isActive: false,\n validationMessage: ''\n };\n },\n\n computed: {\n /**\n * Icon name (MDI) based on the type.\n */\n iconByType: function iconByType() {\n switch (this.type) {\n case 'is-info':\n return 'information';\n case 'is-success':\n return 'check-circle';\n case 'is-warning':\n return 'alert';\n case 'is-danger':\n return 'alert-circle';\n default:\n return null;\n }\n },\n showCancel: function showCancel() {\n return this.cancelOptions.indexOf('button') >= 0;\n }\n },\n methods: {\n /**\n * If it's a prompt Dialog, validate the input.\n * Call the onConfirm prop (function) and close the Dialog.\n */\n confirm: function confirm() {\n var _this = this;\n\n if (this.$refs.input !== undefined) {\n if (!this.$refs.input.checkValidity()) {\n this.validationMessage = this.$refs.input.validationMessage;\n this.$nextTick(function () {\n return _this.$refs.input.select();\n });\n return;\n }\n }\n\n this.onConfirm(this.prompt);\n this.close();\n },\n\n\n /**\n * Close the Dialog.\n */\n close: function close() {\n var _this2 = this;\n\n this.isActive = false;\n // Timeout for the animation complete before destroying\n setTimeout(function () {\n _this2.$destroy();\n Object(__WEBPACK_IMPORTED_MODULE_4__utils_helpers__[\"e\" /* removeElement */])(_this2.$el);\n }, 150);\n }\n },\n beforeMount: function beforeMount() {\n // Insert the Dialog component in body tag\n document.body.appendChild(this.$el);\n },\n mounted: function mounted() {\n var _this3 = this;\n\n this.isActive = true;\n\n if (typeof this.inputAttrs.required === 'undefined') {\n this.$set(this.inputAttrs, 'required', true);\n }\n\n this.$nextTick(function () {\n // Handle which element receives focus\n if (_this3.hasInput) {\n _this3.$refs.input.focus();\n } else if (_this3.focusOn === 'cancel' && _this3.showCancel) {\n _this3.$refs.cancelButton.focus();\n } else {\n _this3.$refs.confirmButton.focus();\n }\n });\n }\n});\n\n/***/ }),\n/* 137 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_config__ = __webpack_require__(2);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BModal',\n props: {\n active: Boolean,\n component: [Object, Function],\n content: String,\n programmatic: Boolean,\n props: Object,\n events: Object,\n width: {\n type: [String, Number],\n default: 960\n },\n hasModalCard: Boolean,\n animation: {\n type: String,\n default: 'zoom-out'\n },\n canCancel: {\n type: [Array, Boolean],\n default: function _default() {\n return ['escape', 'x', 'outside', 'button'];\n }\n },\n onCancel: {\n type: Function,\n default: function _default() {}\n },\n scroll: {\n type: String,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_1__utils_config__[\"a\" /* default */].defaultModalScroll ? __WEBPACK_IMPORTED_MODULE_1__utils_config__[\"a\" /* default */].defaultModalScroll : 'clip';\n },\n validator: function validator(value) {\n return ['clip', 'keep'].indexOf(value) >= 0;\n }\n }\n },\n data: function data() {\n return {\n isActive: this.active || false,\n savedScrollTop: null,\n newWidth: typeof this.width === 'number' ? this.width + 'px' : this.width\n };\n },\n\n computed: {\n cancelOptions: function cancelOptions() {\n return typeof this.canCancel === 'boolean' ? this.canCancel ? ['escape', 'x', 'outside', 'button'] : [] : this.canCancel;\n },\n showX: function showX() {\n return this.cancelOptions.indexOf('x') >= 0;\n }\n },\n watch: {\n active: function active(value) {\n this.isActive = value;\n },\n isActive: function isActive() {\n this.handleScroll();\n }\n },\n methods: {\n handleScroll: function handleScroll() {\n if (typeof window === 'undefined') return;\n\n if (this.scroll === 'clip') {\n if (this.isActive) {\n document.documentElement.classList.add('is-clipped');\n } else {\n document.documentElement.classList.remove('is-clipped');\n }\n return;\n }\n\n this.savedScrollTop = !this.savedScrollTop ? document.documentElement.scrollTop : this.savedScrollTop;\n\n if (this.isActive) {\n document.body.classList.add('is-noscroll');\n } else {\n document.body.classList.remove('is-noscroll');\n }\n\n if (this.isActive) {\n document.body.style.top = '-' + this.savedScrollTop + 'px';\n return;\n }\n\n document.documentElement.scrollTop = this.savedScrollTop;\n document.body.style.top = null;\n this.savedScrollTop = null;\n },\n\n\n /**\n * Close the Modal if canCancel and call the onCancel prop (function).\n */\n cancel: function cancel(method) {\n if (this.cancelOptions.indexOf(method) < 0) return;\n\n this.onCancel.apply(null, arguments);\n this.close();\n },\n\n\n /**\n * Call the onCancel prop (function).\n * Emit events, and destroy modal if it's programmatic.\n */\n close: function close() {\n var _this = this;\n\n this.$emit('close');\n this.$emit('update:active', false);\n\n // Timeout for the animation complete before destroying\n if (this.programmatic) {\n this.isActive = false;\n setTimeout(function () {\n _this.$destroy();\n Object(__WEBPACK_IMPORTED_MODULE_0__utils_helpers__[\"e\" /* removeElement */])(_this.$el);\n }, 150);\n }\n },\n\n\n /**\n * Keypress event that is bound to the document.\n */\n keyPress: function keyPress(event) {\n // Esc key\n if (this.isActive && event.keyCode === 27) this.cancel('escape');\n }\n },\n created: function created() {\n if (typeof window !== 'undefined') {\n document.addEventListener('keyup', this.keyPress);\n }\n },\n beforeMount: function beforeMount() {\n // Insert the Modal component in body tag\n // only if it's programmatic\n this.programmatic && document.body.appendChild(this.$el);\n },\n mounted: function mounted() {\n if (this.programmatic) this.isActive = true;else if (this.isActive) this.handleScroll();\n },\n beforeDestroy: function beforeDestroy() {\n if (typeof window !== 'undefined') {\n document.removeEventListener('keyup', this.keyPress);\n // reset scroll\n document.documentElement.classList.remove('is-clipped');\n var savedScrollTop = !this.savedScrollTop ? document.documentElement.scrollTop : this.savedScrollTop;\n document.body.classList.remove('is-noscroll');\n document.documentElement.scrollTop = savedScrollTop;\n document.body.style.top = null;\n }\n }\n});\n\n/***/ }),\n/* 138 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [(_vm.isActive) ? _c('div', {\n staticClass: \"modal is-active\"\n }, [_c('div', {\n staticClass: \"modal-background\",\n on: {\n \"click\": function($event) {\n _vm.cancel('outside')\n }\n }\n }), _vm._v(\" \"), _c('div', {\n staticClass: \"animation-content\",\n class: {\n 'modal-content': !_vm.hasModalCard\n },\n style: ({\n maxWidth: _vm.newWidth\n })\n }, [(_vm.component) ? _c(_vm.component, _vm._g(_vm._b({\n tag: \"component\",\n on: {\n \"close\": _vm.close\n }\n }, 'component', _vm.props, false), _vm.events)) : (_vm.content) ? _c('div', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.content)\n }\n }) : _vm._t(\"default\")], 2), _vm._v(\" \"), (_vm.showX) ? _c('button', {\n staticClass: \"modal-close is-large\",\n attrs: {\n \"type\": \"button\"\n },\n on: {\n \"click\": function($event) {\n _vm.cancel('x')\n }\n }\n }) : _vm._e()]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 139 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [(_vm.isActive) ? _c('div', {\n staticClass: \"dialog modal is-active\",\n class: _vm.size\n }, [_c('div', {\n staticClass: \"modal-background\",\n on: {\n \"click\": function($event) {\n _vm.cancel('outside')\n }\n }\n }), _vm._v(\" \"), _c('div', {\n staticClass: \"modal-card animation-content\"\n }, [(_vm.title) ? _c('header', {\n staticClass: \"modal-card-head\"\n }, [_c('p', {\n staticClass: \"modal-card-title\"\n }, [_vm._v(_vm._s(_vm.title))])]) : _vm._e(), _vm._v(\" \"), _c('section', {\n staticClass: \"modal-card-body\",\n class: {\n 'is-titleless': !_vm.title, 'is-flex': _vm.hasIcon\n }\n }, [_c('div', {\n staticClass: \"media\"\n }, [(_vm.hasIcon) ? _c('div', {\n staticClass: \"media-left\"\n }, [_c('b-icon', {\n attrs: {\n \"icon\": _vm.icon ? _vm.icon : _vm.iconByType,\n \"pack\": _vm.iconPack,\n \"type\": _vm.type,\n \"both\": !_vm.icon,\n \"size\": \"is-large\"\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"media-content\"\n }, [_c('p', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.message)\n }\n }), _vm._v(\" \"), (_vm.hasInput) ? _c('div', {\n staticClass: \"field\"\n }, [_c('div', {\n staticClass: \"control\"\n }, [_c('input', _vm._b({\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.prompt),\n expression: \"prompt\"\n }],\n ref: \"input\",\n staticClass: \"input\",\n class: {\n 'is-danger': _vm.validationMessage\n },\n domProps: {\n \"value\": (_vm.prompt)\n },\n on: {\n \"keyup\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n _vm.confirm($event)\n },\n \"input\": function($event) {\n if ($event.target.composing) { return; }\n _vm.prompt = $event.target.value\n }\n }\n }, 'input', _vm.inputAttrs, false))]), _vm._v(\" \"), _c('p', {\n staticClass: \"help is-danger\"\n }, [_vm._v(_vm._s(_vm.validationMessage))])]) : _vm._e()])])]), _vm._v(\" \"), _c('footer', {\n staticClass: \"modal-card-foot\"\n }, [(_vm.showCancel) ? _c('button', {\n ref: \"cancelButton\",\n staticClass: \"button\",\n on: {\n \"click\": function($event) {\n _vm.cancel('button')\n }\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.cancelText) + \"\\n \")]) : _vm._e(), _vm._v(\" \"), _c('button', {\n ref: \"confirmButton\",\n staticClass: \"button\",\n class: _vm.type,\n on: {\n \"click\": _vm.confirm\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.confirmText) + \"\\n \")])])])]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 140 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(141),\n /* template */\n __webpack_require__(142),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 141 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n\n// EXTERNAL MODULE: ./src/utils/helpers.js\nvar helpers = __webpack_require__(6);\n\n// CONCATENATED MODULE: ./src/utils/ssr.js\n// Polyfills for SSR\n\nvar isSSR = typeof window === 'undefined';\n\nvar HTMLElement = isSSR ? Object : window.HTMLElement;\n// CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/loading/Loading.vue\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ var Loading = __webpack_exports__[\"default\"] = ({\n name: 'BLoading',\n props: {\n active: Boolean,\n programmatic: Boolean,\n container: [Object, Function, HTMLElement],\n isFullPage: {\n type: Boolean,\n default: true\n },\n animation: {\n type: String,\n default: 'fade'\n },\n canCancel: {\n type: Boolean,\n default: false\n },\n onCancel: {\n type: Function,\n default: function _default() {}\n }\n },\n data: function data() {\n return {\n isActive: this.active || false\n };\n },\n\n watch: {\n active: function active(value) {\n this.isActive = value;\n }\n },\n methods: {\n /**\n * Close the Modal if canCancel.\n */\n cancel: function cancel() {\n if (!this.canCancel || !this.isActive) return;\n\n this.close();\n },\n\n /**\n * Emit events, and destroy modal if it's programmatic.\n */\n close: function close() {\n var _this = this;\n\n this.onCancel.apply(null, arguments);\n this.$emit('close');\n this.$emit('update:active', false);\n\n // Timeout for the animation complete before destroying\n if (this.programmatic) {\n this.isActive = false;\n setTimeout(function () {\n _this.$destroy();\n Object(helpers[\"e\" /* removeElement */])(_this.$el);\n }, 150);\n }\n },\n\n /**\n * Keypress event that is bound to the document.\n */\n keyPress: function keyPress(event) {\n // Esc key\n if (event.keyCode === 27) this.cancel();\n }\n },\n created: function created() {\n if (typeof window !== 'undefined') {\n document.addEventListener('keyup', this.keyPress);\n }\n },\n beforeMount: function beforeMount() {\n // Insert the Loading component in body tag\n // only if it's programmatic\n if (this.programmatic) {\n if (!this.container) {\n document.body.appendChild(this.$el);\n } else {\n this.isFullPage = false;\n this.container.appendChild(this.$el);\n }\n }\n },\n mounted: function mounted() {\n if (this.programmatic) this.isActive = true;\n },\n beforeDestroy: function beforeDestroy() {\n if (typeof window !== 'undefined') {\n document.removeEventListener('keyup', this.keyPress);\n }\n }\n});\n\n/***/ }),\n/* 142 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [(_vm.isActive) ? _c('div', {\n staticClass: \"loading-overlay is-active\",\n class: {\n 'is-full-page': _vm.isFullPage\n }\n }, [_c('div', {\n staticClass: \"loading-background\",\n on: {\n \"click\": _vm.cancel\n }\n }), _vm._v(\" \"), _c('div', {\n staticClass: \"loading-icon\"\n })]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 143 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(144),\n /* template */\n __webpack_require__(145),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 144 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__ = __webpack_require__(63);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BMessage',\n mixins: [__WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__[\"a\" /* default */]],\n data: function data() {\n return {\n newIconSize: this.iconSize || this.size || 'is-large'\n };\n }\n});\n\n/***/ }),\n/* 145 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [(_vm.isActive) ? _c('article', {\n staticClass: \"message\",\n class: [_vm.type, _vm.size]\n }, [(_vm.title) ? _c('header', {\n staticClass: \"message-header\"\n }, [_c('p', [_vm._v(_vm._s(_vm.title))]), _vm._v(\" \"), (_vm.closable) ? _c('button', {\n staticClass: \"delete\",\n attrs: {\n \"type\": \"button\"\n },\n on: {\n \"click\": _vm.close\n }\n }) : _vm._e()]) : _vm._e(), _vm._v(\" \"), _c('section', {\n staticClass: \"message-body\"\n }, [_c('div', {\n staticClass: \"media\"\n }, [(_vm.icon && _vm.hasIcon) ? _c('div', {\n staticClass: \"media-left\"\n }, [_c('b-icon', {\n class: _vm.type,\n attrs: {\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\",\n \"size\": _vm.newIconSize\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"media-content\"\n }, [_vm._t(\"default\")], 2)])])]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 146 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(147),\n /* template */\n __webpack_require__(148),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 147 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__ = __webpack_require__(63);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BNotification',\n mixins: [__WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__[\"a\" /* default */]]\n});\n\n/***/ }),\n/* 148 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [(_vm.isActive) ? _c('article', {\n staticClass: \"notification\",\n class: _vm.type\n }, [(_vm.closable) ? _c('button', {\n staticClass: \"delete\",\n attrs: {\n \"type\": \"button\"\n },\n on: {\n \"click\": _vm.close\n }\n }) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"media\"\n }, [(_vm.icon && _vm.hasIcon) ? _c('div', {\n staticClass: \"media-left\"\n }, [_c('b-icon', {\n attrs: {\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\",\n \"size\": \"is-large\"\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"media-content\"\n }, [_vm._t(\"default\")], 2)])]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 149 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__ = __webpack_require__(13);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BPagination',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n total: [Number, String],\n perPage: {\n type: [Number, String],\n default: 20\n },\n current: {\n type: [Number, String],\n default: 1\n },\n size: String,\n simple: Boolean,\n rounded: Boolean,\n order: String\n },\n computed: {\n rootClasses: function rootClasses() {\n return [this.order, this.size, {\n 'is-simple': this.simple,\n 'is-rounded': this.rounded\n }];\n },\n\n\n /**\n * Total page size (count).\n */\n pageCount: function pageCount() {\n return Math.ceil(this.total / this.perPage);\n },\n\n\n /**\n * First item of the page (count).\n */\n firstItem: function firstItem() {\n var firstItem = this.current * this.perPage - this.perPage + 1;\n return firstItem >= 0 ? firstItem : 0;\n },\n\n\n /**\n * Check if previous button is available.\n */\n hasPrev: function hasPrev() {\n return this.current > 1;\n },\n\n\n /**\n * Check if first page button should be visible.\n */\n hasFirst: function hasFirst() {\n return this.current >= 3;\n },\n\n\n /**\n * Check if first ellipsis should be visible.\n */\n hasFirstEllipsis: function hasFirstEllipsis() {\n return this.current >= 4;\n },\n\n\n /**\n * Check if last page button should be visible.\n */\n hasLast: function hasLast() {\n return this.current <= this.pageCount - 2;\n },\n\n\n /**\n * Check if last ellipsis should be visible.\n */\n hasLastEllipsis: function hasLastEllipsis() {\n return this.current < this.pageCount - 2 && this.current <= this.pageCount - 3;\n },\n\n\n /**\n * Check if next button is available.\n */\n hasNext: function hasNext() {\n return this.current < this.pageCount;\n },\n\n\n /**\n * Get near pages, 1 before and 1 after the current.\n * Also add the click event to the array.\n */\n pagesInRange: function pagesInRange() {\n var _this = this;\n\n if (this.simple) return;\n\n var left = Math.max(1, this.current - 1);\n var right = Math.min(this.current + 1, this.pageCount);\n\n var pages = [];\n\n var _loop = function _loop(i) {\n pages.push({\n number: i,\n isCurrent: _this.current === i,\n click: function click(event) {\n if (_this.current === i) return;\n _this.$emit('change', i);\n _this.$emit('update:current', i);\n\n // Set focus on element to keep tab order\n _this.$nextTick(function () {\n return event.target.focus();\n });\n }\n });\n };\n\n for (var i = left; i <= right; i++) {\n _loop(i);\n }\n return pages;\n }\n },\n watch: {\n /**\n * If current page is trying to be greater than page count, set to last.\n */\n pageCount: function pageCount(value) {\n if (this.current > value) this.last();\n }\n },\n methods: {\n /**\n * Previous button click listener.\n */\n prev: function prev() {\n if (!this.hasPrev) return;\n this.$emit('change', this.current - 1);\n this.$emit('update:current', this.current - 1);\n },\n\n\n /**\n * First button click listener.\n */\n first: function first() {\n this.$emit('change', 1);\n this.$emit('update:current', 1);\n },\n\n\n /**\n * Last button click listener.\n */\n last: function last() {\n this.$emit('change', this.pageCount);\n this.$emit('update:current', this.pageCount);\n },\n\n\n /**\n * Next button click listener.\n */\n next: function next() {\n if (!this.hasNext) return;\n this.$emit('change', this.current + 1);\n this.$emit('update:current', this.current + 1);\n }\n }\n});\n\n/***/ }),\n/* 150 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"pagination\",\n class: _vm.rootClasses\n }, [_c('a', {\n staticClass: \"pagination-previous\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": !_vm.hasPrev\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.prev($event)\n }\n }\n }, [_c('b-icon', {\n attrs: {\n \"icon\": \"chevron-left\",\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\"\n }\n })], 1), _vm._v(\" \"), _c('a', {\n staticClass: \"pagination-next\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": !_vm.hasNext\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.next($event)\n }\n }\n }, [_c('b-icon', {\n attrs: {\n \"icon\": \"chevron-right\",\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\"\n }\n })], 1), _vm._v(\" \"), (!_vm.simple) ? _c('ul', {\n staticClass: \"pagination-list\"\n }, [(_vm.hasFirst) ? _c('li', [_c('a', {\n staticClass: \"pagination-link\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\"\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.first($event)\n }\n }\n }, [_vm._v(\"\\n 1\\n \")])]) : _vm._e(), _vm._v(\" \"), (_vm.hasFirstEllipsis) ? _c('li', [_c('span', {\n staticClass: \"pagination-ellipsis\"\n }, [_vm._v(\"…\")])]) : _vm._e(), _vm._v(\" \"), _vm._l((_vm.pagesInRange), function(page) {\n return _c('li', {\n key: page.number\n }, [_c('a', {\n staticClass: \"pagination-link\",\n class: {\n 'is-current': page.isCurrent\n },\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\"\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n page.click($event)\n }\n }\n }, [_vm._v(\"\\n \" + _vm._s(page.number) + \"\\n \")])])\n }), _vm._v(\" \"), (_vm.hasLastEllipsis) ? _c('li', [_c('span', {\n staticClass: \"pagination-ellipsis\"\n }, [_vm._v(\"…\")])]) : _vm._e(), _vm._v(\" \"), (_vm.hasLast) ? _c('li', [_c('a', {\n staticClass: \"pagination-link\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\"\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.last($event)\n }\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.pageCount) + \"\\n \")])]) : _vm._e()], 2) : _vm._e(), _vm._v(\" \"), (_vm.simple) ? _c('small', {\n staticClass: \"info\"\n }, [(_vm.perPage == 1) ? [_vm._v(\"\\n \" + _vm._s(_vm.firstItem) + \" / \" + _vm._s(_vm.total) + \"\\n \")] : [_vm._v(\"\\n \" + _vm._s(_vm.firstItem) + \"-\" + _vm._s(Math.min(_vm.current * _vm.perPage, _vm.total)) + \" / \" + _vm._s(_vm.total) + \"\\n \")]], 2) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 151 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(152),\n /* template */\n __webpack_require__(153),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 152 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__ = __webpack_require__(13);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BPanel',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n collapsible: {\n type: Boolean,\n default: false\n },\n open: {\n type: Boolean,\n default: true\n },\n hasCustomTemplate: {\n type: Boolean,\n default: false\n },\n header: String,\n content: String,\n animation: {\n type: String,\n default: 'fade'\n }\n },\n data: function data() {\n return {\n isOpen: this.open\n };\n },\n\n watch: {\n open: function open(value) {\n this.isOpen = value;\n }\n },\n methods: {\n /**\n * Toggle the Panel and emit events if collapsible.\n */\n toggle: function toggle() {\n if (!this.collapsible) return;\n\n this.isOpen = !this.isOpen;\n this.$emit('update:open', this.isOpen);\n\n if (this.isOpen) {\n this.$emit('open');\n } else {\n this.$emit('close');\n }\n }\n }\n});\n\n/***/ }),\n/* 153 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('nav', {\n staticClass: \"panel\"\n }, [_c('div', {\n staticClass: \"panel-heading\",\n class: {\n 'is-collapsible': _vm.collapsible\n },\n on: {\n \"click\": _vm.toggle\n }\n }, [(_vm.header) ? _c('span', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.header)\n }\n }) : _vm._t(\"header\"), _vm._v(\" \"), (_vm.collapsible) ? _c('b-icon', {\n staticClass: \"is-pulled-right\",\n attrs: {\n \"both\": \"\",\n \"icon\": _vm.isOpen ? 'menu-up' : 'menu-down',\n \"icon-pack\": _vm.iconPack\n }\n }) : _vm._e()], 2), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isOpen),\n expression: \"isOpen\"\n }],\n staticClass: \"panel-content\",\n class: {\n 'panel-block': !_vm.hasCustomTemplate\n }\n }, [(_vm.content) ? _c('div', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.content)\n }\n }) : _vm._t(\"default\")], 2)])], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 154 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(155),\n /* template */\n __webpack_require__(156),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 155 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BRadio',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n type: String,\n disabled: Boolean,\n required: Boolean,\n name: String,\n size: String\n },\n data: function data() {\n return {\n newValue: this.value\n };\n },\n\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n // only trigger input event\n // when current bRadioButton is clicked.\n if (value === this.nativeValue) {\n this.$emit('input', value);\n }\n }\n }\n});\n\n/***/ }),\n/* 156 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('label', {\n ref: \"label\",\n staticClass: \"b-radio radio\",\n class: [_vm.size, {\n 'is-disabled': _vm.disabled\n }],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n }\n }\n }, [_c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"radio\",\n \"disabled\": _vm.disabled,\n \"required\": _vm.required,\n \"name\": _vm.name\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": _vm._q(_vm.newValue, _vm.nativeValue)\n },\n on: {\n \"change\": function($event) {\n _vm.newValue = _vm.nativeValue\n }\n }\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"check\",\n class: _vm.type\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"control-label\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 157 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(158),\n /* template */\n __webpack_require__(159),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 158 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BRadioButton',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n type: {\n type: String,\n default: 'is-primary'\n },\n disabled: Boolean,\n name: String,\n size: String\n },\n data: function data() {\n return {\n newValue: this.value\n };\n },\n\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n // only trigger input event\n // when current bRadioButton is clicked.\n if (value === this.nativeValue) {\n this.$emit('input', value);\n }\n }\n }\n});\n\n/***/ }),\n/* 159 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"control\"\n }, [_c('label', {\n ref: \"label\",\n staticClass: \"b-radio radio button\",\n class: [_vm.newValue === _vm.nativeValue ? _vm.type : null, _vm.size],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n }\n }\n }, [_vm._t(\"default\"), _vm._v(\" \"), _c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"radio\",\n \"disabled\": _vm.disabled,\n \"name\": _vm.name\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": _vm._q(_vm.newValue, _vm.nativeValue)\n },\n on: {\n \"change\": function($event) {\n _vm.newValue = _vm.nativeValue\n }\n }\n })], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 160 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(161),\n /* template */\n __webpack_require__(162),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 161 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__ = __webpack_require__(65);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BSnackbar',\n mixins: [__WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__[\"a\" /* default */]],\n props: {\n actionText: {\n type: String,\n default: 'OK'\n },\n onAction: {\n type: Function,\n default: function _default() {}\n },\n indefinite: {\n type: Boolean,\n default: false\n }\n },\n data: function data() {\n return {\n newDuration: this.duration || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultSnackbarDuration\n };\n },\n\n methods: {\n /**\n * Click listener.\n * Call action prop before closing (from Mixin).\n */\n action: function action() {\n this.onAction();\n this.close();\n }\n }\n});\n\n/***/ }),\n/* 162 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"enter-active-class\": _vm.transition.enter,\n \"leave-active-class\": _vm.transition.leave\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive),\n expression: \"isActive\"\n }],\n staticClass: \"snackbar\",\n class: [_vm.type, _vm.position]\n }, [_c('p', {\n staticClass: \"text\"\n }, [_vm._v(_vm._s(_vm.message))]), _vm._v(\" \"), (_vm.actionText) ? _c('div', {\n staticClass: \"action\",\n class: _vm.type,\n on: {\n \"click\": _vm.action\n }\n }, [_c('button', {\n staticClass: \"button is-dark\"\n }, [_vm._v(_vm._s(_vm.actionText))])]) : _vm._e()])])\n},staticRenderFns: []}\n\n/***/ }),\n/* 163 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(164),\n /* template */\n __webpack_require__(165),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 164 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BSwitch',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n disabled: Boolean,\n type: String,\n name: String,\n size: String,\n trueValue: {\n type: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n default: true\n },\n falseValue: {\n type: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n default: false\n }\n },\n data: function data() {\n return {\n newValue: this.value,\n isMouseDown: false\n };\n },\n\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n }\n }\n});\n\n/***/ }),\n/* 165 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('label', {\n ref: \"label\",\n staticClass: \"switch\",\n class: [_vm.size, {\n 'is-disabled': _vm.disabled\n }],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n },\n \"mousedown\": function($event) {\n _vm.isMouseDown = true\n },\n \"mouseup\": function($event) {\n _vm.isMouseDown = false\n },\n \"mouseout\": function($event) {\n _vm.isMouseDown = false\n },\n \"blur\": function($event) {\n _vm.isMouseDown = false\n }\n }\n }, [_c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"checkbox\",\n \"disabled\": _vm.disabled,\n \"name\": _vm.name,\n \"true-value\": _vm.trueValue,\n \"false-value\": _vm.falseValue\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": Array.isArray(_vm.newValue) ? _vm._i(_vm.newValue, _vm.nativeValue) > -1 : _vm._q(_vm.newValue, _vm.trueValue)\n },\n on: {\n \"click\": function($event) {\n $event.stopPropagation();\n },\n \"change\": function($event) {\n var $$a = _vm.newValue,\n $$el = $event.target,\n $$c = $$el.checked ? (_vm.trueValue) : (_vm.falseValue);\n if (Array.isArray($$a)) {\n var $$v = _vm.nativeValue,\n $$i = _vm._i($$a, $$v);\n if ($$el.checked) {\n $$i < 0 && (_vm.newValue = $$a.concat([$$v]))\n } else {\n $$i > -1 && (_vm.newValue = $$a.slice(0, $$i).concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.newValue = $$c\n }\n }\n }\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"check\",\n class: [{\n 'is-elastic': _vm.isMouseDown && !_vm.disabled\n }, _vm.type]\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"control-label\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 166 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(167),\n /* template */\n __webpack_require__(181),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 167 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray__ = __webpack_require__(168);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox__ = __webpack_require__(61);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__pagination_Pagination__ = __webpack_require__(64);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__pagination_Pagination___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__pagination_Pagination__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__TableMobileSort__ = __webpack_require__(176);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__TableMobileSort___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6__TableMobileSort__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__TableColumn__ = __webpack_require__(66);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__TableColumn___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7__TableColumn__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__utils_BaseElementMixin__ = __webpack_require__(13);\n\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTable',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox___default.a.name, __WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_4__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_4__icon_Icon___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_5__pagination_Pagination___default.a.name, __WEBPACK_IMPORTED_MODULE_5__pagination_Pagination___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_6__TableMobileSort___default.a.name, __WEBPACK_IMPORTED_MODULE_6__TableMobileSort___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_7__TableColumn___default.a.name, __WEBPACK_IMPORTED_MODULE_7__TableColumn___default.a), _components),\n mixins: [__WEBPACK_IMPORTED_MODULE_8__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n data: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n columns: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n bordered: Boolean,\n striped: Boolean,\n narrowed: Boolean,\n hoverable: Boolean,\n loading: Boolean,\n detailed: Boolean,\n checkable: Boolean,\n selected: Object,\n focusable: Boolean,\n customIsChecked: Function,\n isRowCheckable: {\n type: Function,\n default: function _default() {\n return true;\n }\n },\n checkedRows: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n mobileCards: {\n type: Boolean,\n default: true\n },\n defaultSort: [String, Array],\n defaultSortDirection: {\n type: String,\n default: 'asc'\n },\n paginated: Boolean,\n currentPage: {\n type: Number,\n default: 1\n },\n perPage: {\n type: [Number, String],\n default: 20\n },\n paginationSimple: Boolean,\n paginationSize: String,\n backendSorting: Boolean,\n rowClass: {\n type: Function,\n default: function _default() {\n return '';\n }\n },\n openedDetailed: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n hasDetailedVisible: {\n type: Function,\n default: function _default() {\n return true;\n }\n },\n detailKey: {\n type: String,\n default: ''\n },\n backendPagination: Boolean,\n total: {\n type: [Number, String],\n default: 0\n }\n },\n data: function data() {\n return {\n getValueByPath: __WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"b\" /* getValueByPath */],\n newColumns: [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(this.columns)),\n visibleDetailRows: this.openedDetailed,\n newData: this.data,\n newDataTotal: this.backendPagination ? this.total : this.data.length,\n newCheckedRows: [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(this.checkedRows)),\n newCurrentPage: this.currentPage,\n currentSortColumn: {},\n isAsc: true,\n firstTimeSort: true, // Used by first time initSort\n _isTable: true // Used by TableColumn\n };\n },\n\n computed: {\n tableClasses: function tableClasses() {\n return {\n 'is-bordered': this.bordered,\n 'is-striped': this.striped,\n 'is-narrow': this.narrowed,\n 'has-mobile-cards': this.mobileCards,\n 'is-hoverable': (this.hoverable || this.focusable) && this.visibleData.length\n };\n },\n\n\n /**\n * Splitted data based on the pagination.\n */\n visibleData: function visibleData() {\n if (!this.paginated) return this.newData;\n\n var currentPage = this.newCurrentPage;\n var perPage = this.perPage;\n\n if (this.newData.length <= perPage) {\n return this.newData;\n } else {\n var start = (currentPage - 1) * perPage;\n var end = parseInt(start, 10) + parseInt(perPage, 10);\n return this.newData.slice(start, end);\n }\n },\n\n\n /**\n * Check if all rows in the page are checked.\n */\n isAllChecked: function isAllChecked() {\n var _this = this;\n\n var validVisibleData = this.visibleData.filter(function (row) {\n return _this.isRowCheckable(row);\n });\n if (validVisibleData.length === 0) return false;\n var isAllChecked = validVisibleData.some(function (currentVisibleRow) {\n return Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"c\" /* indexOf */])(_this.newCheckedRows, currentVisibleRow, _this.customIsChecked) < 0;\n });\n return !isAllChecked;\n },\n\n\n /**\n * Check if all rows in the page are checkable.\n */\n isAllUncheckable: function isAllUncheckable() {\n var _this2 = this;\n\n var validVisibleData = this.visibleData.filter(function (row) {\n return _this2.isRowCheckable(row);\n });\n return validVisibleData.length === 0;\n },\n\n\n /**\n * Check if has any sortable column.\n */\n hasSortablenewColumns: function hasSortablenewColumns() {\n return this.newColumns.some(function (column) {\n return column.sortable;\n });\n },\n\n\n /**\n * Return total column count based if it's checkable or expanded\n */\n columnCount: function columnCount() {\n var count = this.newColumns.length;\n count += this.checkable ? 1 : 0;\n count += this.detailed ? 1 : 0;\n\n return count;\n }\n },\n watch: {\n /**\n * When data prop change:\n * 1. Update internal value.\n * 2. Reset newColumns (thead), in case it's on a v-for loop.\n * 3. Sort again if it's not backend-sort.\n * 4. Set new total if it's not backend-paginated.\n */\n data: function data(value) {\n var _this3 = this;\n\n // Save newColumns before resetting\n var newColumns = this.newColumns;\n\n this.newColumns = [];\n this.newData = value;\n\n // Prevent table from being headless, data could change and created hook\n // on column might not trigger\n this.$nextTick(function () {\n if (!_this3.newColumns.length) _this3.newColumns = newColumns;\n });\n\n if (!this.backendSorting) {\n this.sort(this.currentSortColumn, true);\n }\n if (!this.backendPagination) {\n this.newDataTotal = value.length;\n }\n },\n\n\n /**\n * When Pagination total change, update internal total\n * only if it's backend-paginated.\n */\n total: function total(newTotal) {\n if (!this.backendPagination) return;\n\n this.newDataTotal = newTotal;\n },\n\n\n /**\n * When checkedRows prop change, update internal value without\n * mutating original data.\n */\n checkedRows: function checkedRows(rows) {\n this.newCheckedRows = [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(rows));\n },\n columns: function columns(value) {\n this.newColumns = [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(value));\n },\n\n\n /**\n * When newColumns change, call initSort only first time (For example async data).\n */\n newColumns: function newColumns(_newColumns) {\n if (_newColumns.length && this.firstTimeSort) {\n this.initSort();\n this.firstTimeSort = false;\n } else if (_newColumns.length) {\n if (this.currentSortColumn.field) {\n for (var i = 0; i < _newColumns.length; i++) {\n if (_newColumns[i].field === this.currentSortColumn.field) {\n this.currentSortColumn = _newColumns[i];\n break;\n }\n }\n }\n }\n },\n\n\n /**\n * When the user wants to control the detailed rows via props.\n * Or wants to open the details of certain row with the router for example.\n */\n openedDetailed: function openedDetailed(expandedRows) {\n this.visibleDetailRows = expandedRows;\n },\n currentPage: function currentPage(newVal) {\n this.newCurrentPage = newVal;\n }\n },\n methods: {\n /**\n * Sort an array by key without mutating original data.\n * Call the user sort function if it was passed.\n */\n sortBy: function sortBy(array, key, fn, isAsc) {\n var sorted = [];\n // Sorting without mutating original data\n if (fn && typeof fn === 'function') {\n sorted = [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(array)).sort(function (a, b) {\n return fn(a, b, isAsc);\n });\n } else {\n sorted = [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(array)).sort(function (a, b) {\n // Get nested values from objects\n var newA = Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"b\" /* getValueByPath */])(a, key);\n var newB = Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"b\" /* getValueByPath */])(b, key);\n\n if (!newA && newA !== 0) return 1;\n if (!newB && newB !== 0) return -1;\n if (newA === newB) return 0;\n\n newA = typeof newA === 'string' ? newA.toUpperCase() : newA;\n newB = typeof newB === 'string' ? newB.toUpperCase() : newB;\n\n return isAsc ? newA > newB ? 1 : -1 : newA > newB ? -1 : 1;\n });\n }\n\n return sorted;\n },\n\n\n /**\n * Sort the column.\n * Toggle current direction on column if it's sortable\n * and not just updating the prop.\n */\n sort: function sort(column) {\n var updatingData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n if (!column || !column.sortable) return;\n\n if (!updatingData) {\n this.isAsc = column === this.currentSortColumn ? !this.isAsc : this.defaultSortDirection.toLowerCase() !== 'desc';\n }\n if (!this.firstTimeSort) {\n this.$emit('sort', column.field, this.isAsc ? 'asc' : 'desc');\n }\n if (!this.backendSorting) {\n this.newData = this.sortBy(this.newData, column.field, column.customSort, this.isAsc);\n }\n this.currentSortColumn = column;\n },\n\n\n /**\n * Check if the row is checked (is added to the array).\n */\n isRowChecked: function isRowChecked(row) {\n return Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"c\" /* indexOf */])(this.newCheckedRows, row, this.customIsChecked) >= 0;\n },\n\n\n /**\n * Remove a checked row from the array.\n */\n removeCheckedRow: function removeCheckedRow(row) {\n var index = Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"c\" /* indexOf */])(this.newCheckedRows, row, this.customIsChecked);\n if (index >= 0) {\n this.newCheckedRows.splice(index, 1);\n }\n },\n\n\n /**\n * Header checkbox click listener.\n * Add or remove all rows in current page.\n */\n checkAll: function checkAll() {\n var _this4 = this;\n\n var isAllChecked = this.isAllChecked;\n this.visibleData.forEach(function (currentRow) {\n _this4.removeCheckedRow(currentRow);\n if (!isAllChecked) {\n if (_this4.isRowCheckable(currentRow)) {\n _this4.newCheckedRows.push(currentRow);\n }\n }\n });\n\n this.$emit('check', this.newCheckedRows);\n this.$emit('check-all', this.newCheckedRows);\n\n // Emit checked rows to update user variable\n this.$emit('update:checkedRows', this.newCheckedRows);\n },\n\n\n /**\n * Row checkbox click listener.\n * Add or remove a single row.\n */\n checkRow: function checkRow(row) {\n if (!this.isRowChecked(row)) {\n this.newCheckedRows.push(row);\n } else {\n this.removeCheckedRow(row);\n }\n\n this.$emit('check', this.newCheckedRows, row);\n\n // Emit checked rows to update user variable\n this.$emit('update:checkedRows', this.newCheckedRows);\n },\n\n\n /**\n * Row click listener.\n * Emit all necessary events.\n */\n selectRow: function selectRow(row, index) {\n this.$emit('click', row);\n\n if (this.selected === row) return;\n\n // Emit new and old row\n this.$emit('select', row, this.selected);\n\n // Emit new row to update user variable\n this.$emit('update:selected', row);\n },\n\n\n /**\n * Paginator change listener.\n */\n pageChanged: function pageChanged(page) {\n this.newCurrentPage = page > 0 ? page : 1;\n this.$emit('page-change', this.newCurrentPage);\n this.$emit('update:currentPage', this.newCurrentPage);\n },\n\n\n /**\n * Toggle to show/hide details slot\n */\n toggleDetails: function toggleDetails(obj) {\n var found = this.isVisibleDetailRow(obj);\n\n if (found) {\n this.closeDetailRow(obj);\n this.$emit('details-close', obj);\n } else {\n this.openDetailRow(obj);\n this.$emit('details-open', obj);\n }\n\n // Syncs the detailed rows with the parent component\n this.$emit('update:openedDetailed', this.visibleDetailRows);\n },\n openDetailRow: function openDetailRow(obj) {\n var index = this.handleDetailKey(obj);\n this.visibleDetailRows.push(index);\n },\n closeDetailRow: function closeDetailRow(obj) {\n var index = this.handleDetailKey(obj);\n var i = this.visibleDetailRows.indexOf(index);\n this.visibleDetailRows.splice(i, 1);\n },\n isVisibleDetailRow: function isVisibleDetailRow(obj) {\n var index = this.handleDetailKey(obj);\n var result = this.visibleDetailRows.indexOf(index) >= 0;\n return result;\n },\n\n\n /**\n * When the detailKey is defined we use the object[detailKey] as index.\n * If not, use the object reference by default.\n */\n handleDetailKey: function handleDetailKey(index) {\n var key = this.detailKey;\n return !key.length ? index : index[key];\n },\n checkPredefinedDetailedRows: function checkPredefinedDetailedRows() {\n var defaultExpandedRowsDefined = this.openedDetailed.length > 0;\n if (defaultExpandedRowsDefined && !this.detailKey.length) {\n throw new Error('If you set a predefined opened-detailed, you must provide an unique key using the prop \"detail-key\"');\n }\n },\n\n\n /**\n * Check if footer slot has custom content.\n */\n hasCustomFooterSlot: function hasCustomFooterSlot() {\n if (this.$slots.footer.length > 1) return true;\n\n var tag = this.$slots.footer[0].tag;\n if (tag !== 'th' && tag !== 'td') return false;\n\n return true;\n },\n\n\n /**\n * Check if bottom-left slot exists.\n */\n hasBottomLeftSlot: function hasBottomLeftSlot() {\n return typeof this.$slots['bottom-left'] !== 'undefined';\n },\n\n\n /**\n * Table arrow keys listener, change selection.\n */\n pressedArrow: function pressedArrow(pos) {\n if (!this.visibleData.length) return;\n\n var index = this.visibleData.indexOf(this.selected) + pos;\n\n // Prevent from going up from first and down from last\n index = index < 0 ? 0 : index > this.visibleData.length - 1 ? this.visibleData.length - 1 : index;\n\n this.selectRow(this.visibleData[index]);\n },\n\n\n /**\n * Focus table element if has selected prop.\n */\n focus: function focus() {\n if (!this.focusable) return;\n\n this.$el.querySelector('table').focus();\n },\n\n\n /**\n * Initial sorted column based on the default-sort prop.\n */\n initSort: function initSort() {\n var _this5 = this;\n\n if (!this.defaultSort) return;\n\n var sortField = '';\n var sortDirection = this.defaultSortDirection;\n\n if (Array.isArray(this.defaultSort)) {\n sortField = this.defaultSort[0];\n if (this.defaultSort[1]) {\n sortDirection = this.defaultSort[1];\n }\n } else {\n sortField = this.defaultSort;\n }\n\n this.newColumns.forEach(function (column) {\n if (column.field === sortField) {\n _this5.isAsc = sortDirection.toLowerCase() !== 'desc';\n _this5.sort(column, true);\n }\n });\n }\n },\n\n mounted: function mounted() {\n this.checkPredefinedDetailedRows();\n }\n});\n\n/***/ }),\n/* 168 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\n\nvar _from = __webpack_require__(169);\n\nvar _from2 = _interopRequireDefault(_from);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.default = function (arr) {\n if (Array.isArray(arr)) {\n for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {\n arr2[i] = arr[i];\n }\n\n return arr2;\n } else {\n return (0, _from2.default)(arr);\n }\n};\n\n/***/ }),\n/* 169 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(170), __esModule: true };\n\n/***/ }),\n/* 170 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(38);\n__webpack_require__(171);\nmodule.exports = __webpack_require__(8).Array.from;\n\n\n/***/ }),\n/* 171 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar ctx = __webpack_require__(46);\nvar $export = __webpack_require__(18);\nvar toObject = __webpack_require__(37);\nvar call = __webpack_require__(172);\nvar isArrayIter = __webpack_require__(173);\nvar toLength = __webpack_require__(51);\nvar createProperty = __webpack_require__(174);\nvar getIterFn = __webpack_require__(60);\n\n$export($export.S + $export.F * !__webpack_require__(175)(function (iter) { Array.from(iter); }), 'Array', {\n // 22.1.2.1 Array.from(arrayLike, mapfn = undefined, thisArg = undefined)\n from: function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {\n var O = toObject(arrayLike);\n var C = typeof this == 'function' ? this : Array;\n var aLen = arguments.length;\n var mapfn = aLen > 1 ? arguments[1] : undefined;\n var mapping = mapfn !== undefined;\n var index = 0;\n var iterFn = getIterFn(O);\n var length, result, step, iterator;\n if (mapping) mapfn = ctx(mapfn, aLen > 2 ? arguments[2] : undefined, 2);\n // if object isn't iterable or it's array with default iterator - use simple case\n if (iterFn != undefined && !(C == Array && isArrayIter(iterFn))) {\n for (iterator = iterFn.call(O), result = new C(); !(step = iterator.next()).done; index++) {\n createProperty(result, index, mapping ? call(iterator, mapfn, [step.value, index], true) : step.value);\n }\n } else {\n length = toLength(O.length);\n for (result = new C(length); length > index; index++) {\n createProperty(result, index, mapping ? mapfn(O[index], index) : O[index]);\n }\n }\n result.length = index;\n return result;\n }\n});\n\n\n/***/ }),\n/* 172 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// call something on iterator step with safe closing on error\nvar anObject = __webpack_require__(16);\nmodule.exports = function (iterator, fn, value, entries) {\n try {\n return entries ? fn(anObject(value)[0], value[1]) : fn(value);\n // 7.4.6 IteratorClose(iterator, completion)\n } catch (e) {\n var ret = iterator['return'];\n if (ret !== undefined) anObject(ret.call(iterator));\n throw e;\n }\n};\n\n\n/***/ }),\n/* 173 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// check on default Array iterator\nvar Iterators = __webpack_require__(22);\nvar ITERATOR = __webpack_require__(4)('iterator');\nvar ArrayProto = Array.prototype;\n\nmodule.exports = function (it) {\n return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it);\n};\n\n\n/***/ }),\n/* 174 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar $defineProperty = __webpack_require__(9);\nvar createDesc = __webpack_require__(21);\n\nmodule.exports = function (object, index, value) {\n if (index in object) $defineProperty.f(object, index, createDesc(0, value));\n else object[index] = value;\n};\n\n\n/***/ }),\n/* 175 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar ITERATOR = __webpack_require__(4)('iterator');\nvar SAFE_CLOSING = false;\n\ntry {\n var riter = [7][ITERATOR]();\n riter['return'] = function () { SAFE_CLOSING = true; };\n // eslint-disable-next-line no-throw-literal\n Array.from(riter, function () { throw 2; });\n} catch (e) { /* empty */ }\n\nmodule.exports = function (exec, skipClosing) {\n if (!skipClosing && !SAFE_CLOSING) return false;\n var safe = false;\n try {\n var arr = [7];\n var iter = arr[ITERATOR]();\n iter.next = function () { return { done: safe = true }; };\n arr[ITERATOR] = function () { return iter; };\n exec(arr);\n } catch (e) { /* empty */ }\n return safe;\n};\n\n\n/***/ }),\n/* 176 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(177),\n /* template */\n __webpack_require__(178),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 177 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__select_Select__ = __webpack_require__(28);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__select_Select___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__select_Select__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__icon_Icon__);\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTableMobileSort',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_1__select_Select___default.a.name, __WEBPACK_IMPORTED_MODULE_1__select_Select___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default.a), _components),\n props: {\n currentSortColumn: Object,\n isAsc: Boolean,\n columns: Array\n },\n data: function data() {\n return {\n mobileSort: this.currentSortColumn\n };\n },\n\n watch: {\n mobileSort: function mobileSort(column) {\n if (this.currentSortColumn === column) return;\n\n this.$emit('sort', column);\n },\n currentSortColumn: function currentSortColumn(column) {\n this.mobileSort = column;\n }\n },\n methods: {\n sort: function sort() {\n this.$emit('sort', this.mobileSort);\n }\n }\n});\n\n/***/ }),\n/* 178 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"field table-mobile-sort\"\n }, [_c('div', {\n staticClass: \"field has-addons\"\n }, [_c('b-select', {\n attrs: {\n \"expanded\": \"\"\n },\n model: {\n value: (_vm.mobileSort),\n callback: function($$v) {\n _vm.mobileSort = $$v\n },\n expression: \"mobileSort\"\n }\n }, _vm._l((_vm.columns), function(column, index) {\n return (column.sortable) ? _c('option', {\n key: index,\n domProps: {\n \"value\": column\n }\n }, [_vm._v(\"\\n \" + _vm._s(column.label) + \"\\n \")]) : _vm._e()\n })), _vm._v(\" \"), _c('div', {\n staticClass: \"control\"\n }, [_c('button', {\n staticClass: \"button is-primary\",\n on: {\n \"click\": _vm.sort\n }\n }, [_c('b-icon', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.currentSortColumn === _vm.mobileSort),\n expression: \"currentSortColumn === mobileSort\"\n }],\n class: {\n 'is-desc': !_vm.isAsc\n },\n attrs: {\n \"icon\": \"arrow-up\",\n \"size\": \"is-small\",\n \"both\": \"\"\n }\n })], 1)])], 1)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 179 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTableColumn',\n props: {\n label: String,\n customKey: [String, Number],\n field: String,\n meta: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n width: [Number, String],\n numeric: Boolean,\n centered: Boolean,\n sortable: Boolean,\n visible: {\n type: Boolean,\n default: true\n },\n customSort: Function,\n internal: Boolean // Used internally by Table\n },\n data: function data() {\n return {\n newKey: this.customKey || this.label\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return {\n 'has-text-right': this.numeric && !this.centered,\n 'has-text-centered': this.centered\n };\n }\n },\n beforeMount: function beforeMount() {\n var _this = this;\n\n if (!this.$parent.$data._isTable) {\n this.$destroy();\n throw new Error('You should wrap bTableColumn on a bTable');\n }\n\n if (this.internal) return;\n\n // Since we're using scoped prop the columns gonna be multiplied,\n // this finds when to stop based on the newKey property.\n var repeated = this.$parent.columns.some(function (column) {\n return column.newKey === _this.newKey;\n });\n !repeated && this.$parent.columns.push(this);\n },\n beforeDestroy: function beforeDestroy() {\n var index = this.$parent.columns.map(function (column) {\n return column.newKey;\n }).indexOf(this.newKey);\n if (index >= 0) {\n this.$parent.columns.splice(index, 1);\n }\n }\n});\n\n/***/ }),\n/* 180 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return (_vm.visible) ? _c('td', {\n class: _vm.rootClasses,\n attrs: {\n \"data-label\": _vm.label\n }\n }, [_c('span', [_vm._t(\"default\")], 2)]) : _vm._e()\n},staticRenderFns: []}\n\n/***/ }),\n/* 181 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"b-table\",\n class: {\n 'is-loading': _vm.loading\n }\n }, [(_vm.mobileCards && _vm.hasSortablenewColumns) ? _c('b-table-mobile-sort', {\n attrs: {\n \"current-sort-column\": _vm.currentSortColumn,\n \"is-asc\": _vm.isAsc,\n \"columns\": _vm.newColumns\n },\n on: {\n \"sort\": function (column) { return _vm.sort(column); }\n }\n }) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"table-wrapper\"\n }, [_c('table', {\n staticClass: \"table\",\n class: _vm.tableClasses,\n attrs: {\n \"tabindex\": !_vm.focusable ? false : 0\n },\n on: {\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"up\", 38, $event.key)) { return null; }\n $event.preventDefault();\n _vm.pressedArrow(-1)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"down\", 40, $event.key)) { return null; }\n $event.preventDefault();\n _vm.pressedArrow(1)\n }]\n }\n }, [(_vm.newColumns.length) ? _c('thead', [_c('tr', [(_vm.detailed) ? _c('th', {\n attrs: {\n \"width\": \"40px\"\n }\n }) : _vm._e(), _vm._v(\" \"), (_vm.checkable) ? _c('th', {\n staticClass: \"checkbox-cell\"\n }, [_c('b-checkbox', {\n attrs: {\n \"value\": _vm.isAllChecked,\n \"disabled\": _vm.isAllUncheckable\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.checkAll($event)\n }\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _vm._l((_vm.newColumns), function(column, index) {\n return (column.visible || column.visible === undefined) ? _c('th', {\n key: index,\n class: {\n 'is-current-sort': _vm.currentSortColumn === column,\n 'is-sortable': column.sortable\n },\n style: ({\n width: column.width + 'px'\n }),\n on: {\n \"click\": function($event) {\n $event.stopPropagation();\n _vm.sort(column)\n }\n }\n }, [_c('div', {\n staticClass: \"th-wrap\",\n class: {\n 'is-numeric': column.numeric,\n 'is-centered': column.centered\n }\n }, [(_vm.$scopedSlots.header) ? _vm._t(\"header\", null, {\n column: column,\n index: index\n }) : [_vm._v(_vm._s(column.label))], _vm._v(\" \"), _c('b-icon', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.currentSortColumn === column),\n expression: \"currentSortColumn === column\"\n }],\n class: {\n 'is-desc': !_vm.isAsc\n },\n attrs: {\n \"icon\": \"arrow-up\",\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\",\n \"size\": \"is-small\"\n }\n })], 2)]) : _vm._e()\n })], 2)]) : _vm._e(), _vm._v(\" \"), (_vm.visibleData.length) ? _c('tbody', [_vm._l((_vm.visibleData), function(row, index) {\n return [_c('tr', {\n key: index,\n class: [_vm.rowClass(row, index), {\n 'is-selected': row === _vm.selected,\n 'is-checked': _vm.isRowChecked(row)\n }],\n on: {\n \"click\": function($event) {\n _vm.selectRow(row)\n },\n \"dblclick\": function($event) {\n _vm.$emit('dblclick', row)\n }\n }\n }, [(_vm.detailed) ? _c('td', {\n staticClass: \"chevron-cell\"\n }, [(_vm.hasDetailedVisible(row)) ? _c('a', {\n attrs: {\n \"role\": \"button\"\n },\n on: {\n \"click\": function($event) {\n $event.stopPropagation();\n _vm.toggleDetails(row)\n }\n }\n }, [_c('b-icon', {\n class: {\n 'is-expanded': _vm.isVisibleDetailRow(row)\n },\n attrs: {\n \"icon\": \"chevron-right\",\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\"\n }\n })], 1) : _vm._e()]) : _vm._e(), _vm._v(\" \"), (_vm.checkable) ? _c('td', {\n staticClass: \"checkbox-cell\"\n }, [_c('b-checkbox', {\n attrs: {\n \"disabled\": !_vm.isRowCheckable(row),\n \"value\": _vm.isRowChecked(row)\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.checkRow(row)\n }\n }\n })], 1) : _vm._e(), _vm._v(\" \"), (_vm.$scopedSlots.default) ? _vm._t(\"default\", null, {\n row: row,\n index: index\n }) : _vm._l((_vm.newColumns), function(column) {\n return _c('BTableColumn', _vm._b({\n key: column.field,\n attrs: {\n \"internal\": \"\"\n }\n }, 'BTableColumn', column, false), [(column.renderHtml) ? _c('span', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.getValueByPath(row, column.field))\n }\n }) : [_vm._v(\"\\n \" + _vm._s(_vm.getValueByPath(row, column.field)) + \"\\n \")]], 2)\n })], 2), _vm._v(\" \"), (_vm.detailed && _vm.isVisibleDetailRow(row)) ? _c('tr', {\n staticClass: \"detail\"\n }, [_c('td', {\n attrs: {\n \"colspan\": _vm.columnCount\n }\n }, [_c('div', {\n staticClass: \"detail-container\"\n }, [_vm._t(\"detail\", null, {\n row: row,\n index: index\n })], 2)])]) : _vm._e()]\n })], 2) : _c('tbody', [_c('tr', {\n staticClass: \"is-empty\"\n }, [_c('td', {\n attrs: {\n \"colspan\": _vm.columnCount\n }\n }, [_vm._t(\"empty\")], 2)])]), _vm._v(\" \"), (_vm.$slots.footer !== undefined) ? _c('tfoot', [_c('tr', {\n staticClass: \"table-footer\"\n }, [(_vm.hasCustomFooterSlot()) ? _vm._t(\"footer\") : _c('th', {\n attrs: {\n \"colspan\": _vm.columnCount\n }\n }, [_vm._t(\"footer\")], 2)], 2)]) : _vm._e()])]), _vm._v(\" \"), ((_vm.checkable && _vm.hasBottomLeftSlot()) || _vm.paginated) ? _c('div', {\n staticClass: \"level\"\n }, [_c('div', {\n staticClass: \"level-left\"\n }, [_vm._t(\"bottom-left\")], 2), _vm._v(\" \"), _c('div', {\n staticClass: \"level-right\"\n }, [(_vm.paginated) ? _c('div', {\n staticClass: \"level-item\"\n }, [_c('b-pagination', {\n attrs: {\n \"icon-pack\": _vm.iconPack,\n \"total\": _vm.newDataTotal,\n \"per-page\": _vm.perPage,\n \"simple\": _vm.paginationSimple,\n \"size\": _vm.paginationSize,\n \"current\": _vm.newCurrentPage\n },\n on: {\n \"change\": _vm.pageChanged\n }\n })], 1) : _vm._e()])]) : _vm._e()], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 182 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(183),\n /* template */\n __webpack_require__(184),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 183 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n\n// EXTERNAL MODULE: ./node_modules/babel-runtime/helpers/defineProperty.js\nvar defineProperty = __webpack_require__(1);\nvar defineProperty_default = /*#__PURE__*/__webpack_require__.n(defineProperty);\n\n// EXTERNAL MODULE: ./src/components/icon/Icon.vue\nvar Icon = __webpack_require__(3);\nvar Icon_default = /*#__PURE__*/__webpack_require__.n(Icon);\n\n// CONCATENATED MODULE: ./src/utils/SlotComponent.js\n/* harmony default export */ var SlotComponent = ({\n name: 'BSlotComponent',\n props: {\n component: {\n type: Object,\n required: true\n },\n name: {\n type: String,\n default: 'default'\n },\n tag: {\n type: String,\n default: 'div'\n },\n event: {\n type: String,\n default: 'hook:updated'\n }\n },\n methods: {\n refresh: function refresh() {\n this.$forceUpdate();\n },\n isVueComponent: function isVueComponent() {\n return this.component && this.component._isVue;\n }\n },\n created: function created() {\n if (this.isVueComponent()) {\n this.component.$on(this.event, this.refresh);\n }\n },\n beforeDestroy: function beforeDestroy() {\n if (this.isVueComponent()) {\n this.component.$off(this.event, this.refresh);\n }\n },\n render: function render(h) {\n if (this.isVueComponent()) {\n var slots = this.component.$slots[this.name];\n return h(this.tag, {}, slots);\n }\n }\n});\n// CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/tabs/Tabs.vue\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ var Tabs = __webpack_exports__[\"default\"] = ({\n name: 'BTabs',\n components: (_components = {}, defineProperty_default()(_components, Icon_default.a.name, Icon_default.a), defineProperty_default()(_components, SlotComponent.name, SlotComponent), _components),\n props: {\n value: [String, Number],\n expanded: Boolean,\n type: String,\n size: String,\n position: String,\n animated: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n return {\n activeTab: this.value || 0,\n tabItems: [],\n contentHeight: 0,\n _isTabs: true // Used internally by TabItem\n };\n },\n\n computed: {\n navClasses: function navClasses() {\n return [this.type, this.size, this.position, {\n 'is-fullwidth': this.expanded,\n 'is-toggle-rounded is-toggle': this.type === 'is-toggle-rounded'\n }];\n }\n },\n watch: {\n /**\n * When v-model is changed set the new active tab.\n */\n value: function value(_value) {\n this.changeTab(_value);\n },\n\n\n /**\n * When tab-items are updated, set active one.\n */\n tabItems: function tabItems() {\n if (this.tabItems.length) {\n this.tabItems[this.activeTab].isActive = true;\n }\n }\n },\n methods: {\n /**\n * Change the active tab and emit change event.\n */\n changeTab: function changeTab(newIndex) {\n if (this.activeTab === newIndex) return;\n\n this.tabItems[this.activeTab].deactivate(this.activeTab, newIndex);\n this.tabItems[newIndex].activate(this.activeTab, newIndex);\n this.activeTab = newIndex;\n this.$emit('change', newIndex);\n },\n\n\n /**\n * Tab click listener, emit input event and change active tab.\n */\n tabClick: function tabClick(value) {\n this.$emit('input', value);\n this.changeTab(value);\n }\n },\n mounted: function mounted() {\n if (this.tabItems.length) {\n this.tabItems[this.activeTab].isActive = true;\n }\n }\n});\n\n/***/ }),\n/* 184 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"b-tabs\",\n class: {\n 'is-fullwidth': _vm.expanded\n }\n }, [_c('nav', {\n staticClass: \"tabs\",\n class: _vm.navClasses\n }, [_c('ul', _vm._l((_vm.tabItems), function(tabItem, index) {\n return _c('li', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (tabItem.visible),\n expression: \"tabItem.visible\"\n }],\n key: index,\n class: {\n 'is-active': _vm.activeTab === index, 'is-disabled': tabItem.disabled\n }\n }, [_c('a', {\n on: {\n \"click\": function($event) {\n _vm.tabClick(index)\n }\n }\n }, [(tabItem.$slots.header) ? [_c('b-slot-component', {\n attrs: {\n \"component\": tabItem,\n \"name\": \"header\",\n \"tag\": \"span\"\n }\n })] : [(tabItem.icon) ? _c('b-icon', {\n attrs: {\n \"icon\": tabItem.icon,\n \"pack\": tabItem.iconPack,\n \"size\": _vm.size\n }\n }) : _vm._e(), _vm._v(\" \"), _c('span', [_vm._v(_vm._s(tabItem.label))])]], 2)])\n }))]), _vm._v(\" \"), _c('section', {\n staticClass: \"tab-content\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 185 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(186),\n /* template */\n __webpack_require__(187),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 186 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_BaseElementMixin__ = __webpack_require__(13);\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTabItem',\n mixins: [__WEBPACK_IMPORTED_MODULE_0__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n label: String,\n icon: String,\n disabled: Boolean,\n visible: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n return {\n isActive: false,\n transitionName: null\n };\n },\n\n methods: {\n /**\n * Activate tab, alter animation name based on the index.\n */\n activate: function activate(oldIndex, index) {\n if (!this.$parent.animated) {\n this.transitionName = null;\n } else {\n this.transitionName = index < oldIndex ? 'slide-next' : 'slide-prev';\n }\n this.isActive = true;\n },\n\n\n /**\n * Deactivate tab, alter animation name based on the index.\n */\n deactivate: function deactivate(oldIndex, index) {\n if (!this.$parent.animated) {\n this.transitionName = null;\n } else {\n this.transitionName = index < oldIndex ? 'slide-next' : 'slide-prev';\n }\n this.isActive = false;\n }\n },\n created: function created() {\n if (!this.$parent.$data._isTabs) {\n this.$destroy();\n throw new Error('You should wrap bTabItem on a bTabs');\n }\n this.$parent.tabItems.push(this);\n },\n beforeDestroy: function beforeDestroy() {\n var index = this.$parent.tabItems.indexOf(this);\n if (index >= 0) {\n this.$parent.tabItems.splice(index, 1);\n }\n }\n});\n\n/***/ }),\n/* 187 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": _vm.transitionName\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive && _vm.visible),\n expression: \"isActive && visible\"\n }],\n staticClass: \"tab-item\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 188 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTag',\n props: {\n attached: Boolean,\n closable: Boolean,\n type: String,\n size: String,\n rounded: Boolean,\n disabled: Boolean,\n ellipsis: Boolean,\n tabstop: {\n type: Boolean,\n default: true\n }\n },\n methods: {\n /**\n * Emit close event when delete button is clicked\n * or delete key is pressed.\n */\n close: function close() {\n if (this.disabled) return;\n\n this.$emit('close');\n }\n }\n});\n\n/***/ }),\n/* 189 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return (_vm.attached && _vm.closable) ? _c('div', {\n staticClass: \"tags has-addons\"\n }, [_c('span', {\n staticClass: \"tag\",\n class: [_vm.type, _vm.size, {\n 'is-rounded': _vm.rounded\n }]\n }, [_c('span', {\n class: {\n 'has-ellipsis': _vm.ellipsis\n }\n }, [_vm._t(\"default\")], 2)]), _vm._v(\" \"), _c('a', {\n staticClass: \"tag is-delete\",\n class: [_vm.size, {\n 'is-rounded': _vm.rounded\n }],\n attrs: {\n \"role\": \"button\",\n \"tabindex\": _vm.tabstop ? 0 : false,\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n _vm.close()\n },\n \"keyup\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"delete\", [8, 46], $event.key)) { return null; }\n $event.preventDefault();\n _vm.close()\n }\n }\n })]) : _c('span', {\n staticClass: \"tag\",\n class: [_vm.type, _vm.size, {\n 'is-rounded': _vm.rounded\n }]\n }, [_c('span', {\n class: {\n 'has-ellipsis': _vm.ellipsis\n }\n }, [_vm._t(\"default\")], 2), _vm._v(\" \"), (_vm.closable) ? _c('a', {\n staticClass: \"delete is-small\",\n attrs: {\n \"role\": \"button\",\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.tabstop ? 0 : false\n },\n on: {\n \"click\": function($event) {\n _vm.close()\n },\n \"keyup\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"delete\", [8, 46], $event.key)) { return null; }\n $event.preventDefault();\n _vm.close()\n }\n }\n }) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 190 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(191),\n /* template */\n __webpack_require__(192),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 191 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTaglist',\n props: {\n attached: Boolean\n }\n});\n\n/***/ }),\n/* 192 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"tags\",\n class: {\n 'has-addons': _vm.attached\n }\n }, [_vm._t(\"default\")], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 193 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(194),\n /* template */\n __webpack_require__(195),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 194 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__ = __webpack_require__(53);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__tag_Tag__ = __webpack_require__(67);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__tag_Tag___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3__tag_Tag__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete__ = __webpack_require__(52);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__utils_FormElementMixin__ = __webpack_require__(12);\n\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTaginput',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete___default.a.name, __WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_3__tag_Tag___default.a.name, __WEBPACK_IMPORTED_MODULE_3__tag_Tag___default.a), _components),\n mixins: [__WEBPACK_IMPORTED_MODULE_5__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n data: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n type: String,\n rounded: {\n type: Boolean,\n default: false\n },\n attached: {\n type: Boolean,\n default: false\n },\n maxtags: {\n type: [Number, String],\n required: false\n },\n field: {\n type: String,\n default: 'value'\n },\n autocomplete: Boolean,\n disabled: Boolean,\n ellipsis: Boolean,\n confirmKeyCodes: {\n type: Array,\n default: function _default() {\n return [13, 188];\n }\n },\n removeOnKeys: {\n type: Array,\n default: function _default() {\n return [8];\n }\n },\n allowNew: Boolean,\n onPasteSeparators: {\n type: Array,\n default: function _default() {\n return [','];\n }\n },\n beforeAdding: {\n type: Function,\n default: function _default() {\n return true;\n }\n },\n allowDuplicates: {\n type: Boolean,\n default: false\n }\n },\n data: function data() {\n return {\n tags: this.value || [],\n newTag: '',\n _elementRef: 'input',\n _isTaginput: true\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return {\n 'is-expanded': this.expanded\n };\n },\n containerClasses: function containerClasses() {\n return {\n 'is-focused': this.isFocused,\n 'is-focusable': this.hasInput\n };\n },\n valueLength: function valueLength() {\n return this.newTag.trim().length;\n },\n defaultSlotName: function defaultSlotName() {\n return this.hasDefaultSlot ? 'default' : 'dontrender';\n },\n emptySlotName: function emptySlotName() {\n return this.hasEmptySlot ? 'empty' : 'dontrender';\n },\n hasDefaultSlot: function hasDefaultSlot() {\n return !!this.$scopedSlots.default;\n },\n hasEmptySlot: function hasEmptySlot() {\n return !!this.$slots.empty;\n },\n\n\n /**\n * Show the input field if a maxtags hasn't been set or reached.\n */\n hasInput: function hasInput() {\n return this.maxtags == null || this.tagsLength < this.maxtags;\n },\n tagsLength: function tagsLength() {\n return this.tags.length;\n },\n\n\n /**\n * If Taginput has onPasteSeparators prop,\n * returning new RegExp used to split pasted string.\n */\n separatorsAsRegExp: function separatorsAsRegExp() {\n var sep = this.onPasteSeparators;\n\n return sep.length ? new RegExp(sep.map(function (s) {\n return s ? s.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&') : null;\n }).join('|'), 'g') : null;\n }\n },\n watch: {\n /**\n * When v-model is changed set internal value.\n */\n value: function value(_value) {\n this.tags = _value;\n },\n newTag: function newTag(value) {\n this.$emit('typing', value.trim());\n },\n hasInput: function hasInput() {\n if (!this.hasInput) this.onBlur();\n }\n },\n methods: {\n addTag: function addTag(tag) {\n var tagToAdd = tag || this.newTag.trim();\n\n if (tagToAdd) {\n if (!this.autocomplete) {\n var reg = this.separatorsAsRegExp;\n if (reg && tagToAdd.match(reg)) {\n tagToAdd.split(reg).map(function (t) {\n return t.trim();\n }).filter(function (t) {\n return t.length !== 0;\n }).map(this.addTag);\n return;\n }\n }\n\n // Add the tag input if it is not blank\n // or previously added (if not allowDuplicates).\n var add = !this.allowDuplicates ? this.tags.indexOf(tagToAdd) === -1 : true;\n if (add && this.beforeAdding(tagToAdd)) {\n this.tags.push(tagToAdd);\n this.$emit('input', this.tags);\n this.$emit('add', tagToAdd);\n }\n }\n\n this.newTag = '';\n },\n getNormalizedTagText: function getNormalizedTagText(tag) {\n if ((typeof tag === 'undefined' ? 'undefined' : __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default()(tag)) === 'object') {\n return Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"b\" /* getValueByPath */])(tag, this.field);\n }\n\n return tag;\n },\n customOnBlur: function customOnBlur($event) {\n // Add tag on-blur if not select only\n if (!this.autocomplete) this.addTag();\n\n this.onBlur($event);\n },\n onSelect: function onSelect(option) {\n var _this = this;\n\n if (!option) return;\n\n this.addTag(option);\n this.$nextTick(function () {\n _this.newTag = '';\n });\n },\n removeTag: function removeTag(index) {\n var tag = this.tags.splice(index, 1)[0];\n this.$emit('input', this.tags);\n this.$emit('remove', tag);\n return tag;\n },\n removeLastTag: function removeLastTag() {\n if (this.tagsLength > 0) {\n this.removeTag(this.tagsLength - 1);\n }\n },\n keydown: function keydown(event) {\n if (this.removeOnKeys.indexOf(event.keyCode) !== -1 && !this.newTag.length) {\n this.removeLastTag();\n }\n // Stop if is to accept select only\n if (this.autocomplete && !this.allowNew) return;\n\n if (this.confirmKeyCodes.indexOf(event.keyCode) >= 0) {\n event.preventDefault();\n this.addTag();\n }\n }\n }\n});\n\n/***/ }),\n/* 195 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"taginput control\",\n class: _vm.rootClasses\n }, [_c('div', {\n staticClass: \"taginput-container\",\n class: [_vm.statusType, _vm.size, _vm.containerClasses],\n attrs: {\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n _vm.hasInput && _vm.focus($event)\n }\n }\n }, [_vm._l((_vm.tags), function(tag, index) {\n return _c('b-tag', {\n key: index,\n attrs: {\n \"type\": _vm.type,\n \"size\": _vm.size,\n \"rounded\": _vm.rounded,\n \"attached\": _vm.attached,\n \"tabstop\": false,\n \"disabled\": _vm.disabled,\n \"ellipsis\": _vm.ellipsis,\n \"closable\": \"\"\n },\n on: {\n \"close\": function($event) {\n _vm.removeTag(index)\n }\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.getNormalizedTagText(tag)) + \"\\n \")])\n }), _vm._v(\" \"), (_vm.hasInput) ? _c('b-autocomplete', _vm._b({\n ref: \"autocomplete\",\n attrs: {\n \"data\": _vm.data,\n \"field\": _vm.field,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"maxlength\": _vm.maxlength,\n \"has-counter\": false,\n \"size\": _vm.size,\n \"disabled\": _vm.disabled,\n \"loading\": _vm.loading,\n \"keep-first\": \"\"\n },\n on: {\n \"focus\": _vm.onFocus,\n \"blur\": _vm.customOnBlur,\n \"select\": _vm.onSelect\n },\n nativeOn: {\n \"keydown\": function($event) {\n _vm.keydown($event)\n }\n },\n scopedSlots: _vm._u([{\n key: _vm.defaultSlotName,\n fn: function(props) {\n return [_vm._t(\"default\", null, {\n option: props.option,\n index: props.index\n })]\n }\n }]),\n model: {\n value: (_vm.newTag),\n callback: function($$v) {\n _vm.newTag = $$v\n },\n expression: \"newTag\"\n }\n }, 'b-autocomplete', _vm.$attrs, false), [_c('template', {\n slot: _vm.emptySlotName\n }, [_vm._t(\"empty\")], 2)], 2) : _vm._e()], 2), _vm._v(\" \"), (_vm.maxtags || _vm.maxlength) ? _c('p', {\n staticClass: \"help counter\"\n }, [(_vm.maxlength && _vm.valueLength > 0) ? [_vm._v(\"\\n \" + _vm._s(_vm.valueLength) + \" / \" + _vm._s(_vm.maxlength) + \"\\n \")] : (_vm.maxtags) ? [_vm._v(\"\\n \" + _vm._s(_vm.tagsLength) + \" / \" + _vm._s(_vm.maxtags) + \"\\n \")] : _vm._e()], 2) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 196 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(197),\n /* template */\n __webpack_require__(198),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 197 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__ = __webpack_require__(12);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__ = __webpack_require__(43);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__ = __webpack_require__(44);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__input_Input__ = __webpack_require__(27);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__input_Input___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6__input_Input__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__field_Field__ = __webpack_require__(45);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__field_Field___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7__field_Field__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__select_Select__ = __webpack_require__(28);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__select_Select___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_8__select_Select__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_9__icon_Icon__);\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\n\n\n\nvar AM = 'AM';\nvar PM = 'PM';\nvar HOUR_FORMAT_24 = '24';\nvar HOUR_FORMAT_12 = '12';\n\nvar formatNumber = function formatNumber(value) {\n return (value < 10 ? '0' : '') + value;\n};\n\nvar timeFormatter = function timeFormatter(date, vm) {\n var hours = date.getHours();\n var minutes = date.getMinutes();\n var am = false;\n if (vm.hourFormat === HOUR_FORMAT_12) {\n am = hours < 12;\n if (hours > 12) {\n hours -= 12;\n } else if (hours === 0) {\n hours = 12;\n }\n }\n return formatNumber(hours) + ':' + formatNumber(minutes) + (vm.hourFormat === HOUR_FORMAT_12 ? ' ' + (am ? AM : PM) : '');\n};\n\nvar timeParser = function timeParser(date, vm) {\n if (date) {\n var dateString = date;\n var am = false;\n if (vm.hourFormat === HOUR_FORMAT_12) {\n var dateString12 = date.split(' ');\n dateString = dateString12[0];\n am = dateString12[1] === AM;\n }\n var time = dateString.split(':');\n var hours = parseInt(time[0], 10);\n var minutes = parseInt(time[1], 10);\n if (isNaN(hours) || hours < 0 || hours > 23 || vm.hourFormat === HOUR_FORMAT_12 && (hours < 1 || hours > 12) || isNaN(minutes) || minutes < 0 || minutes > 59) {\n return null;\n }\n var d = null;\n if (vm.dateSelected && !isNaN(vm.dateSelected)) {\n d = new Date(vm.dateSelected);\n } else {\n d = new Date();\n d.setMilliseconds(0);\n d.setSeconds(0);\n }\n d.setMinutes(minutes);\n if (vm.hourFormat === HOUR_FORMAT_12) {\n if (am && hours === 12) {\n hours = 0;\n } else if (!am && hours !== 12) {\n hours += 12;\n }\n }\n d.setHours(hours);\n return d;\n }\n return null;\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTimepicker',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_6__input_Input___default.a.name, __WEBPACK_IMPORTED_MODULE_6__input_Input___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_7__field_Field___default.a.name, __WEBPACK_IMPORTED_MODULE_7__field_Field___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_8__select_Select___default.a.name, __WEBPACK_IMPORTED_MODULE_8__select_Select___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default.a.name, __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default.a.name, __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default.a), _components),\n mixins: [__WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: Date,\n inline: Boolean,\n minTime: Date,\n maxTime: Date,\n placeholder: String,\n readonly: {\n type: Boolean,\n default: true\n },\n disabled: {\n type: Boolean,\n default: false\n },\n hourFormat: {\n type: String,\n default: HOUR_FORMAT_24,\n validator: function validator(value) {\n return value === HOUR_FORMAT_24 || value === HOUR_FORMAT_12;\n }\n },\n incrementMinutes: {\n type: Number,\n default: 1\n },\n timeFormatter: {\n type: Function,\n default: function _default(date, vm) {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimeFormatter === 'function') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimeFormatter(date);\n } else {\n return timeFormatter(date, vm);\n }\n }\n },\n timeParser: {\n type: Function,\n default: function _default(date, vm) {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimeParser === 'function') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimeParser(date);\n } else {\n return timeParser(date, vm);\n }\n }\n },\n mobileNative: {\n type: Boolean,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimepickerMobileNative;\n }\n },\n position: String,\n unselectableTimes: Array\n },\n data: function data() {\n return {\n dateSelected: this.value,\n hoursSelected: null,\n minutesSelected: null,\n meridienSelected: null,\n _elementRef: 'input',\n _isTimepicker: true\n };\n },\n\n computed: {\n hours: function hours() {\n var hours = [];\n var numberOfHours = this.isHourFormat24 ? 24 : 12;\n for (var i = 0; i < numberOfHours; i++) {\n var value = i;\n var label = value;\n if (!this.isHourFormat24) {\n value = i + 1;\n label = value;\n if (this.meridienSelected === AM) {\n if (value === 12) {\n value = 0;\n }\n } else if (this.meridienSelected === PM) {\n if (value !== 12) {\n value += 12;\n }\n }\n }\n hours.push({\n label: formatNumber(label),\n value: value\n });\n }\n return hours;\n },\n minutes: function minutes() {\n var minutes = [];\n for (var i = 0; i < 60; i += this.incrementMinutes) {\n minutes.push({\n label: formatNumber(i),\n value: i\n });\n }\n return minutes;\n },\n meridiens: function meridiens() {\n return [AM, PM];\n },\n isMobile: function isMobile() {\n return this.mobileNative && __WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"d\" /* isMobile */].any();\n },\n isHourFormat24: function isHourFormat24() {\n return this.hourFormat === HOUR_FORMAT_24;\n }\n },\n watch: {\n hourFormat: function hourFormat(value) {\n if (this.hoursSelected !== null) {\n this.meridienSelected = this.hoursSelected >= 12 ? PM : AM;\n }\n },\n\n\n /**\n * Emit input event with selected date as payload.\n */\n dateSelected: function dateSelected(value) {\n this.$emit('input', value);\n },\n\n\n /**\n * When v-model is changed:\n * 1. Update internal value.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.updateInternalState(_value);\n this.dateSelected = _value;\n\n !this.isValid && this.$refs.input.checkHtml5Validity();\n }\n },\n methods: {\n onMeridienChange: function onMeridienChange(value) {\n if (this.hoursSelected !== null) {\n if (value === PM) {\n if (this.hoursSelected === 0) {\n this.hoursSelected = 12;\n } else {\n this.hoursSelected += 12;\n }\n } else if (value === AM) {\n if (this.hoursSelected === 12) {\n this.hoursSelected = 0;\n } else {\n this.hoursSelected -= 12;\n }\n }\n }\n this.updateDateSelected(this.hoursSelected, this.minutesSelected, value);\n },\n onHoursChange: function onHoursChange(value) {\n this.updateDateSelected(parseInt(value, 10), this.minutesSelected, this.meridienSelected);\n },\n onMinutesChange: function onMinutesChange(value) {\n this.updateDateSelected(this.hoursSelected, parseInt(value, 10), this.meridienSelected);\n },\n updateDateSelected: function updateDateSelected(hours, minutes, meridiens) {\n if (hours != null && minutes != null && (!this.isHourFormat24 && meridiens !== null || this.isHourFormat24)) {\n if (this.dateSelected && !isNaN(this.dateSelected)) {\n this.dateSelected = new Date(this.dateSelected);\n } else {\n this.dateSelected = new Date();\n this.dateSelected.setMilliseconds(0);\n this.dateSelected.setSeconds(0);\n }\n this.dateSelected.setHours(hours);\n this.dateSelected.setMinutes(minutes);\n }\n },\n updateInternalState: function updateInternalState(value) {\n if (value) {\n this.hoursSelected = value.getHours();\n this.minutesSelected = value.getMinutes();\n this.meridienSelected = value.getHours() >= 12 ? PM : AM;\n } else {\n this.hoursSelected = null;\n this.minutesSelected = null;\n this.meridienSelected = AM;\n }\n },\n isHourDisabled: function isHourDisabled(hour) {\n var _this = this;\n\n var disabled = false;\n if (this.minTime) {\n var minHours = this.minTime.getHours();\n disabled = hour < minHours;\n }\n if (this.maxTime) {\n if (!disabled) {\n var maxHours = this.maxTime.getHours();\n disabled = hour > maxHours;\n }\n }\n if (this.unselectableTimes) {\n if (!disabled) {\n if (this.minutesSelected !== null) {\n var unselectable = this.unselectableTimes.filter(function (time) {\n return time.getHours() === hour && time.getMinutes() === _this.minutesSelected;\n });\n disabled = unselectable.length > 0;\n } else {\n var _unselectable = this.unselectableTimes.filter(function (time) {\n return time.getHours() === hour;\n });\n disabled = _unselectable.length === this.minutes.length;\n }\n }\n }\n return disabled;\n },\n isMinuteDisabled: function isMinuteDisabled(minute) {\n var _this2 = this;\n\n var disabled = false;\n if (this.hoursSelected !== null) {\n if (this.isHourDisabled(this.hoursSelected)) {\n disabled = true;\n } else {\n if (this.minTime) {\n var minHours = this.minTime.getHours();\n var minMinutes = this.minTime.getMinutes();\n disabled = this.hoursSelected === minHours && minute < minMinutes;\n }\n if (this.maxTime) {\n if (!disabled) {\n var maxHours = this.maxTime.getHours();\n var _minMinutes = this.maxTime.getMinutes();\n disabled = this.hoursSelected === maxHours && minute > _minMinutes;\n }\n }\n }\n if (this.unselectableTimes) {\n if (!disabled) {\n var unselectable = this.unselectableTimes.filter(function (time) {\n return time.getHours() === _this2.hoursSelected && time.getMinutes() === minute;\n });\n disabled = unselectable.length > 0;\n }\n }\n }\n return disabled;\n },\n\n\n /*\n * Parse string into date\n */\n onChange: function onChange(value) {\n var date = this.timeParser(value, this);\n this.updateInternalState(date);\n if (date && !isNaN(date)) {\n this.dateSelected = date;\n } else {\n // Force refresh input value when not valid date\n this.dateSelected = null;\n this.$refs.input.newValue = this.dateSelected;\n }\n },\n\n\n /*\n * Format date into string\n */\n formatValue: function formatValue(value) {\n if (value && !isNaN(value)) {\n return this.timeFormatter(value, this);\n } else {\n return null;\n }\n },\n\n\n /*\n * Close dropdown time picker\n */\n close: function close() {\n if (this.$refs.dropdown) {\n this.$refs.dropdown.isActive = false;\n }\n },\n\n\n /*\n * Format date into string 'HH-MM-SS'\n */\n formatHHMMSS: function formatHHMMSS(value) {\n var date = new Date(value);\n if (value && !isNaN(date)) {\n var hours = date.getHours();\n var minutes = date.getMinutes();\n return formatNumber(hours) + ':' + formatNumber(minutes) + ':00';\n }\n return '';\n },\n\n\n /*\n * Parse time from string\n */\n onChangeNativePicker: function onChangeNativePicker(event) {\n var date = event.target.value;\n if (date) {\n if (this.dateSelected && !isNaN(this.dateSelected)) {\n this.dateSelected = new Date(this.dateSelected);\n } else {\n this.dateSelected = new Date();\n this.dateSelected.setMilliseconds(0);\n this.dateSelected.setSeconds(0);\n }\n var time = date.split(':');\n this.dateSelected.setHours(parseInt(time[0], 10));\n this.dateSelected.setMinutes(parseInt(time[1], 10));\n } else {\n this.dateSelected = null;\n }\n }\n },\n mounted: function mounted() {\n this.updateInternalState(this.value);\n }\n});\n\n/***/ }),\n/* 198 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"timepicker control\",\n class: [_vm.size, {\n 'is-expanded': _vm.expanded\n }]\n }, [(!_vm.isMobile || _vm.inline) ? _c('b-dropdown', {\n ref: \"dropdown\",\n attrs: {\n \"position\": _vm.position,\n \"disabled\": _vm.disabled,\n \"inline\": _vm.inline\n }\n }, [(!_vm.inline) ? _c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"slot\": \"trigger\",\n \"autocomplete\": \"off\",\n \"value\": _vm.formatValue(_vm.dateSelected),\n \"placeholder\": _vm.placeholder,\n \"size\": _vm.size,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"loading\": _vm.loading,\n \"disabled\": _vm.disabled,\n \"readonly\": _vm.readonly,\n \"rounded\": _vm.rounded\n },\n on: {\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n }\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onChange($event.target.value)\n }\n },\n slot: \"trigger\"\n }, 'b-input', _vm.$attrs, false)) : _vm._e(), _vm._v(\" \"), _c('b-dropdown-item', {\n attrs: {\n \"disabled\": _vm.disabled,\n \"custom\": \"\"\n }\n }, [_c('div', {\n staticClass: \"pagination-list\"\n }, [_c('b-field', [_c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled,\n \"placeholder\": \"00\"\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onHoursChange($event.target.value)\n }\n },\n model: {\n value: (_vm.hoursSelected),\n callback: function($$v) {\n _vm.hoursSelected = $$v\n },\n expression: \"hoursSelected\"\n }\n }, _vm._l((_vm.hours), function(hour) {\n return _c('option', {\n key: hour.value,\n attrs: {\n \"disabled\": _vm.isHourDisabled(hour.value)\n },\n domProps: {\n \"value\": hour.value\n }\n }, [_vm._v(\"\\n \" + _vm._s(hour.label) + \"\\n \")])\n })), _vm._v(\" \"), _c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled,\n \"placeholder\": \"00\"\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onMinutesChange($event.target.value)\n }\n },\n model: {\n value: (_vm.minutesSelected),\n callback: function($$v) {\n _vm.minutesSelected = $$v\n },\n expression: \"minutesSelected\"\n }\n }, _vm._l((_vm.minutes), function(minute) {\n return _c('option', {\n key: minute.value,\n attrs: {\n \"disabled\": _vm.isMinuteDisabled(minute.value)\n },\n domProps: {\n \"value\": minute.value\n }\n }, [_vm._v(\"\\n \" + _vm._s(minute.label) + \"\\n \")])\n })), _vm._v(\" \"), (!_vm.isHourFormat24) ? _c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onMeridienChange($event.target.value)\n }\n },\n model: {\n value: (_vm.meridienSelected),\n callback: function($$v) {\n _vm.meridienSelected = $$v\n },\n expression: \"meridienSelected\"\n }\n }, _vm._l((_vm.meridiens), function(meridien) {\n return _c('option', {\n key: meridien,\n domProps: {\n \"value\": meridien\n }\n }, [_vm._v(\"\\n \" + _vm._s(meridien) + \"\\n \")])\n })) : _vm._e()], 1)], 1), _vm._v(\" \"), (_vm.$slots.default !== undefined && _vm.$slots.default.length) ? _c('footer', {\n staticClass: \"timepicker-footer\"\n }, [_vm._t(\"default\")], 2) : _vm._e()])], 1) : _c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"type\": \"time\",\n \"autocomplete\": \"off\",\n \"value\": _vm.formatHHMMSS(_vm.value),\n \"placeholder\": _vm.placeholder,\n \"size\": _vm.size,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"loading\": _vm.loading,\n \"max\": _vm.formatHHMMSS(_vm.maxTime),\n \"min\": _vm.formatHHMMSS(_vm.minTime),\n \"disabled\": _vm.disabled,\n \"readonly\": false\n },\n on: {\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n }\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onChangeNativePicker($event)\n }\n }\n }, 'b-input', _vm.$attrs, false))], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 199 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(200),\n /* template */\n __webpack_require__(201),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 200 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__ = __webpack_require__(65);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BToast',\n mixins: [__WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__[\"a\" /* default */]],\n data: function data() {\n return {\n newDuration: this.duration || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultToastDuration\n };\n }\n});\n\n/***/ }),\n/* 201 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"enter-active-class\": _vm.transition.enter,\n \"leave-active-class\": _vm.transition.leave\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive),\n expression: \"isActive\"\n }],\n staticClass: \"toast\",\n class: [_vm.type, _vm.position]\n }, [_c('div', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.message)\n }\n })])])\n},staticRenderFns: []}\n\n/***/ }),\n/* 202 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(203),\n /* template */\n __webpack_require__(204),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 203 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_config__ = __webpack_require__(2);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTooltip',\n props: {\n active: {\n type: Boolean,\n default: true\n },\n type: String,\n label: String,\n position: {\n type: String,\n default: 'is-top',\n validator: function validator(value) {\n return ['is-top', 'is-bottom', 'is-left', 'is-right'].indexOf(value) > -1;\n }\n },\n always: Boolean,\n animated: Boolean,\n square: Boolean,\n dashed: Boolean,\n multilined: Boolean,\n size: {\n type: String,\n default: 'is-medium'\n }\n },\n computed: {\n newType: function newType() {\n return this.type || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultTooltipType;\n },\n newAnimated: function newAnimated() {\n return this.animated || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultTooltipAnimated;\n }\n }\n});\n\n/***/ }),\n/* 204 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('span', {\n class: [_vm.newType, _vm.position, _vm.size, {\n 'tooltip': _vm.active,\n 'is-square': _vm.square,\n 'is-animated': _vm.newAnimated,\n 'is-always': _vm.always,\n 'is-multiline': _vm.multilined,\n 'is-dashed': _vm.dashed\n }],\n attrs: {\n \"data-label\": _vm.label\n }\n }, [_vm._t(\"default\")], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 205 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(206),\n /* template */\n __webpack_require__(207),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 206 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_FormElementMixin__ = __webpack_require__(12);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BUpload',\n mixins: [__WEBPACK_IMPORTED_MODULE_0__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n multiple: Boolean,\n disabled: Boolean,\n accept: String,\n dragDrop: Boolean,\n type: {\n type: String,\n default: 'is-primary'\n },\n native: {\n type: Boolean,\n default: false\n }\n },\n data: function data() {\n return {\n newValue: this.value || [],\n dragDropFocus: false,\n _elementRef: 'input'\n };\n },\n\n watch: {\n /**\n * When v-model is changed:\n * 1. Set internal value.\n * 2. Reset input value if array is empty\n * 3. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.newValue = _value;\n if (!this.newValue || this.newValue.length === 0) {\n this.$refs.input.value = null;\n }\n !this.isValid && !this.dragDrop && this.checkHtml5Validity();\n }\n },\n methods: {\n\n /**\n * Listen change event on input type 'file',\n * emit 'input' event and validate\n */\n onFileChange: function onFileChange(event) {\n if (this.disabled || this.loading) return;\n if (this.dragDrop) {\n this.updateDragDropFocus(false);\n }\n var value = event.target.files || event.dataTransfer.files;\n if (value && value.length) {\n if (!this.multiple) {\n // only one element in case drag drop mode and isn't multiple\n if (this.dragDrop) {\n if (value.length === 1) {\n this.newValue = [];\n } else {\n return false;\n }\n } else {\n this.newValue = [];\n }\n } else {\n if (this.native) {\n this.newValue = [];\n }\n }\n for (var i = 0; i < value.length; i++) {\n var file = value[i];\n if (this.checkType(file)) {\n this.newValue.push(file);\n }\n }\n }\n this.$emit('input', this.newValue);\n !this.dragDrop && this.checkHtml5Validity();\n },\n\n\n /**\n * Listen drag-drop to update internal variable\n */\n updateDragDropFocus: function updateDragDropFocus(focus) {\n if (!this.disabled && !this.loading) {\n this.dragDropFocus = focus;\n }\n },\n\n\n /**\n * Check mime type of file\n */\n checkType: function checkType(file) {\n if (!this.accept) return true;\n var types = this.accept.split(',');\n if (types.length === 0) return true;\n var valid = false;\n for (var i = 0; i < types.length && !valid; i++) {\n var type = types[i].trim();\n if (type) {\n if (type.substring(0, 1) === '.') {\n // check extension\n var extIndex = file.name.lastIndexOf('.');\n if (extIndex >= 0 && file.name.substring(extIndex) === type) {\n valid = true;\n }\n } else {\n // check mime type\n if (file.type.match(type)) {\n valid = true;\n }\n }\n }\n }\n return valid;\n }\n }\n});\n\n/***/ }),\n/* 207 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('label', {\n staticClass: \"upload control\"\n }, [(!_vm.dragDrop) ? [_vm._t(\"default\")] : _c('div', {\n staticClass: \"upload-draggable\",\n class: [_vm.type, {\n 'is-loading': _vm.loading,\n 'is-disabled': _vm.disabled,\n 'is-hovered': _vm.dragDropFocus\n }],\n on: {\n \"dragover\": function($event) {\n $event.preventDefault();\n _vm.updateDragDropFocus(true)\n },\n \"dragleave\": function($event) {\n $event.preventDefault();\n _vm.updateDragDropFocus(false)\n },\n \"dragenter\": function($event) {\n $event.preventDefault();\n _vm.updateDragDropFocus(true)\n },\n \"drop\": function($event) {\n $event.preventDefault();\n _vm.onFileChange($event)\n }\n }\n }, [_vm._t(\"default\")], 2), _vm._v(\" \"), _c('input', _vm._b({\n ref: \"input\",\n attrs: {\n \"type\": \"file\",\n \"multiple\": _vm.multiple,\n \"accept\": _vm.accept,\n \"disabled\": _vm.disabled\n },\n on: {\n \"change\": _vm.onFileChange\n }\n }, 'input', _vm.$attrs, false))], 2)\n},staticRenderFns: []}\n\n/***/ })\n/******/ ]);\n});\n\n/***/ }),\n/* 21 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(22);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"6e4843aa\", content, true, {});\n\n/***/ }),\n/* 22 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"/*! Buefy v0.6.7 | MIT License | github.com/buefy/buefy */ \\n/*! bulma.io v0.7.1 | MIT License | github.com/jgthms/bulma */@keyframes spinAround{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.b-checkbox.checkbox,.b-radio.radio,.breadcrumb,.button,.delete,.file,.icon,.is-unselectable,.modal-close,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.switch,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link:after,.select:not(.is-multiple):not(.is-loading):after{border:3px solid transparent;border-radius:2px;border-right:0;border-top:0;content:\\\" \\\";display:block;height:.625em;margin-top:-.4375em;pointer-events:none;position:absolute;top:50%;transform:rotate(-45deg);transform-origin:center;width:.625em}.block:not(:last-child),.box:not(:last-child),.breadcrumb:not(:last-child),.content:not(:last-child),.highlight:not(:last-child),.level:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.progress:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.tabs:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.delete,.modal-close{-moz-appearance:none;-webkit-appearance:none;background-color:hsla(0,0%,4%,.2);border:none;border-radius:290486px;cursor:pointer;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:none;position:relative;vertical-align:top;width:20px}.delete:after,.delete:before,.modal-close:after,.modal-close:before{background-color:#fff;content:\\\"\\\";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.delete:before,.modal-close:before{height:2px;width:50%}.delete:after,.modal-close:after{height:50%;width:2px}.delete:focus,.delete:hover,.modal-close:focus,.modal-close:hover{background-color:hsla(0,0%,4%,.3)}.delete:active,.modal-close:active{background-color:hsla(0,0%,4%,.4)}.is-small.delete,.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.delete,.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.delete,.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.b-table.is-loading:after,.button.is-loading:after,.control.is-loading:after,.loader,.loading-overlay .loading-icon:after,.select.is-loading:after,.upload .upload-draggable.is-loading:after{animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:\\\"\\\";display:block;height:1em;position:relative;width:1em}.dropdown .background,.hero-video,.image.is-1by1 img,.image.is-1by2 img,.image.is-1by3 img,.image.is-2by1 img,.image.is-2by3 img,.image.is-3by1 img,.image.is-3by2 img,.image.is-3by4 img,.image.is-3by5 img,.image.is-4by3 img,.image.is-4by5 img,.image.is-5by3 img,.image.is-5by4 img,.image.is-9by16 img,.image.is-16by9 img,.image.is-square img,.is-overlay,.loading-overlay,.loading-overlay .loading-background,.modal,.modal-background{bottom:0;left:0;position:absolute;right:0;top:0}.button,.file-cta,.file-name,.input,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.select select,.taginput .taginput-container.is-focusable,.textarea{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.25em;justify-content:flex-start;line-height:1.5;padding:calc(.375em - 1px) calc(.625em - 1px);position:relative;vertical-align:top}.button:active,.button:focus,.file-cta:active,.file-cta:focus,.file-name:active,.file-name:focus,.input:active,.input:focus,.is-active.button,.is-active.file-cta,.is-active.file-name,.is-active.input,.is-active.pagination-ellipsis,.is-active.pagination-link,.is-active.pagination-next,.is-active.pagination-previous,.is-active.textarea,.is-focused.button,.is-focused.file-cta,.is-focused.file-name,.is-focused.input,.is-focused.pagination-ellipsis,.is-focused.pagination-link,.is-focused.pagination-next,.is-focused.pagination-previous,.is-focused.textarea,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link:active,.pagination-link:focus,.pagination-next:active,.pagination-next:focus,.pagination-previous:active,.pagination-previous:focus,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.taginput .is-active.taginput-container.is-focusable,.taginput .is-focused.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:active,.taginput .taginput-container.is-focusable:focus,.textarea:active,.textarea:focus{outline:none}.select select[disabled],.taginput [disabled].taginput-container.is-focusable,[disabled].button,[disabled].file-cta,[disabled].file-name,[disabled].input,[disabled].pagination-ellipsis,[disabled].pagination-link,[disabled].pagination-next,[disabled].pagination-previous,[disabled].textarea{cursor:not-allowed}\\n\\n/*! minireset.css v0.0.3 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}audio,img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0;text-align:left}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,select,textarea{font-family:BlinkMacSystemFont,-apple-system,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1rem;font-weight:400;line-height:1.5}a{color:#3273dc;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{color:#ff3860;font-size:.875em;font-weight:400;padding:.25em .5em}code,hr{background-color:#f5f5f5}hr{border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{text-align:left;vertical-align:top}table th{color:#363636}.is-clearfix:after{clear:both;content:\\\" \\\";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-clipped{overflow:hidden!important}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media print,screen and (min-width:769px){.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1087px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1088px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1280px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1472px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}.has-text-justified{text-align:justify!important}.has-text-left{text-align:left!important}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media print,screen and (min-width:769px){.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1087px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1088px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1280px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1472px){.has-text-centered-fullhd{text-align:center!important}}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media print,screen and (min-width:769px){.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1087px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1088px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1280px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1472px){.has-text-justified-fullhd{text-align:justify!important}}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media print,screen and (min-width:769px){.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1087px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1088px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1280px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1472px){.has-text-left-fullhd{text-align:left!important}}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media print,screen and (min-width:769px){.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1087px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1088px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1280px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1472px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-background-white{background-color:#fff!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-background-black{background-color:#0a0a0a!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-background-light{background-color:#f5f5f5!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-background-dark{background-color:#363636!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-background-primary{background-color:#00d1b2!important}.has-text-link{color:#3273dc!important}a.has-text-link:focus,a.has-text-link:hover{color:#205bbc!important}.has-background-link{background-color:#3273dc!important}.has-text-info{color:#209cee!important}a.has-text-info:focus,a.has-text-info:hover{color:#0f81cc!important}.has-background-info{background-color:#209cee!important}.has-text-success{color:#23d160!important}a.has-text-success:focus,a.has-text-success:hover{color:#1ca64c!important}.has-background-success{background-color:#23d160!important}.has-text-warning{color:#ffdd57!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd324!important}.has-background-warning{background-color:#ffdd57!important}.has-text-danger{color:#ff3860!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ff0537!important}.has-background-danger{background-color:#ff3860!important}.has-text-black-bis{color:#121212!important}.has-background-black-bis{background-color:#121212!important}.has-text-black-ter{color:#242424!important}.has-background-black-ter{background-color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-background-grey-darker{background-color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-background-grey-dark{background-color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-background-grey{background-color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-background-grey-light{background-color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-background-grey-lighter{background-color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-background-white-ter{background-color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-background-white-bis{background-color:#fafafa!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media print,screen and (min-width:769px){.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1087px){.is-block-touch{display:block!important}}@media screen and (min-width:1088px){.is-block-desktop{display:block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1280px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1472px){.is-block-fullhd{display:block!important}}.is-flex{display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:flex!important}}@media print,screen and (min-width:769px){.is-flex-tablet{display:flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-flex-tablet-only{display:flex!important}}@media screen and (max-width:1087px){.is-flex-touch{display:flex!important}}@media screen and (min-width:1088px){.is-flex-desktop{display:flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-flex-desktop-only{display:flex!important}}@media screen and (min-width:1280px){.is-flex-widescreen{display:flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-flex-widescreen-only{display:flex!important}}@media screen and (min-width:1472px){.is-flex-fullhd{display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media print,screen and (min-width:769px){.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1087px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1088px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1280px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1472px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media print,screen and (min-width:769px){.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1087px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1088px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1280px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1472px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:inline-flex!important}}@media print,screen and (min-width:769px){.is-inline-flex-tablet{display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-flex-tablet-only{display:inline-flex!important}}@media screen and (max-width:1087px){.is-inline-flex-touch{display:inline-flex!important}}@media screen and (min-width:1088px){.is-inline-flex-desktop{display:inline-flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-flex-desktop-only{display:inline-flex!important}}@media screen and (min-width:1280px){.is-inline-flex-widescreen{display:inline-flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-flex-widescreen-only{display:inline-flex!important}}@media screen and (min-width:1472px){.is-inline-flex-fullhd{display:inline-flex!important}}.is-hidden{display:none!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media print,screen and (min-width:769px){.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1087px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1088px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1280px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1472px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media print,screen and (min-width:769px){.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1087px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1088px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1280px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1472px){.is-invisible-fullhd{visibility:hidden!important}}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.is-radiusless{border-radius:0!important}.is-shadowless{box-shadow:none!important}.box{background-color:#fff;border-radius:6px;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);color:#4a4a4a;display:block;padding:1.25rem}a.box:focus,a.box:hover{box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px #3273dc}a.box:active{box-shadow:inset 0 1px 2px hsla(0,0%,4%,.2),0 0 0 1px #3273dc}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding:calc(.375em - 1px) .75em;text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.375em - 1px);margin-right:.1875em}.button .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:calc(-.375em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.375em - 1px);margin-right:calc(-.375em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#3273dc;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled]{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled]{background-color:#fff;border-color:transparent;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled]{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading:after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled]{background-color:#0a0a0a;border-color:transparent;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading:after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:#363636}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:#363636}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:#363636}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:#363636}.button.is-light[disabled]{background-color:#f5f5f5;border-color:transparent;box-shadow:none}.button.is-light.is-inverted{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted:hover{background-color:#292929}.button.is-light.is-inverted[disabled]{background-color:#363636;border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading:after{border-color:transparent transparent #363636 #363636!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.button.is-light.is-outlined.is-loading:after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark{background-color:#363636;border-color:transparent;color:#f5f5f5}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#f5f5f5}.button.is-dark[disabled]{background-color:#363636;border-color:transparent;box-shadow:none}.button.is-dark.is-inverted{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted:hover{background-color:#e8e8e8}.button.is-dark.is-inverted[disabled]{background-color:#f5f5f5;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading:after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.button.is-dark.is-outlined.is-loading:after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined[disabled]{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled]{background-color:#00d1b2;border-color:transparent;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading:after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined[disabled]{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link{background-color:#3273dc;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#276cda;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#2366d1;border-color:transparent;color:#fff}.button.is-link[disabled]{background-color:#3273dc;border-color:transparent;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#3273dc}.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#3273dc}.button.is-link.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;color:#3273dc}.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#3273dc;border-color:#3273dc;color:#fff}.button.is-link.is-outlined.is-loading:after{border-color:transparent transparent #3273dc #3273dc!important}.button.is-link.is-outlined[disabled]{background-color:transparent;border-color:#3273dc;box-shadow:none;color:#3273dc}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info{background-color:#209cee;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#1496ed;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#118fe4;border-color:transparent;color:#fff}.button.is-info[disabled]{background-color:#209cee;border-color:transparent;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#209cee}.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#209cee}.button.is-info.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#209cee;color:#209cee}.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#209cee;border-color:#209cee;color:#fff}.button.is-info.is-outlined.is-loading:after{border-color:transparent transparent #209cee #209cee!important}.button.is-info.is-outlined[disabled]{background-color:transparent;border-color:#209cee;box-shadow:none;color:#209cee}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#209cee}.button.is-info.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success{background-color:#23d160;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#22c65b;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#20bc56;border-color:transparent;color:#fff}.button.is-success[disabled]{background-color:#23d160;border-color:transparent;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#23d160}.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#23d160}.button.is-success.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#23d160;color:#23d160}.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#23d160;border-color:#23d160;color:#fff}.button.is-success.is-outlined.is-loading:after{border-color:transparent transparent #23d160 #23d160!important}.button.is-success.is-outlined[disabled]{background-color:transparent;border-color:#23d160;box-shadow:none;color:#23d160}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#23d160}.button.is-success.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled]{background-color:#ffdd57;border-color:transparent;box-shadow:none}.button.is-warning.is-inverted{color:#ffdd57}.button.is-warning.is-inverted,.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled]{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading:after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading:after{border-color:transparent transparent #ffdd57 #ffdd57!important}.button.is-warning.is-outlined[disabled]{background-color:transparent;border-color:#ffdd57;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-danger{background-color:#ff3860;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#ff2b56;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ff1f4b;border-color:transparent;color:#fff}.button.is-danger[disabled]{background-color:#ff3860;border-color:transparent;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#ff3860}.button.is-danger.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;color:#ff3860}.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#ff3860;border-color:#ff3860;color:#fff}.button.is-danger.is-outlined.is-loading:after{border-color:transparent transparent #ff3860 #ff3860!important}.button.is-danger.is-outlined[disabled]{background-color:transparent;border-color:#ff3860;box-shadow:none;color:#ff3860}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-small{border-radius:2px;font-size:.75rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled]{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading:after{position:absolute;left:calc(50% - 0.5em);top:calc(50% - 0.5em);position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1}.buttons.is-centered{justify-content:center}.buttons.is-right{justify-content:flex-end}.container{margin:0 auto;position:relative}@media screen and (min-width:1088px){.container{max-width:960px;width:960px}.container.is-fluid{margin-left:64px;margin-right:64px;max-width:none;width:auto}}@media screen and (max-width:1279px){.container.is-widescreen{max-width:1152px;width:auto}}@media screen and (max-width:1471px){.container.is-fullhd{max-width:1344px;width:auto}}@media screen and (min-width:1280px){.container{max-width:1152px;width:1152px}}@media screen and (min-width:1472px){.container{max-width:1344px;width:1344px}}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style:decimal outside}.content ol,.content ul{margin-left:2em;margin-top:1em}.content ul{list-style:disc outside}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636;text-align:left}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content.is-small{font-size:.75rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.input,.taginput .taginput-container.is-focusable,.textarea{background-color:#fff;border-color:#dbdbdb;color:#363636;box-shadow:inset 0 1px 2px hsla(0,0%,4%,.1);max-width:100%;width:100%}.input::-moz-placeholder,.taginput .taginput-container.is-focusable::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.taginput .taginput-container.is-focusable::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.taginput .taginput-container.is-focusable:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.taginput .taginput-container.is-focusable:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input.is-hovered,.input:hover,.taginput .is-hovered.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:hover,.textarea.is-hovered,.textarea:hover{border-color:#b5b5b5}.input.is-active,.input.is-focused,.input:active,.input:focus,.taginput .is-active.taginput-container.is-focusable,.taginput .is-focused.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:active,.taginput .taginput-container.is-focusable:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input[disabled],.taginput [disabled].taginput-container.is-focusable,.textarea[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.taginput [disabled].taginput-container.is-focusable::-moz-placeholder,.textarea[disabled]::-moz-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]::-webkit-input-placeholder,.taginput [disabled].taginput-container.is-focusable::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]:-moz-placeholder,.taginput [disabled].taginput-container.is-focusable:-moz-placeholder,.textarea[disabled]:-moz-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]:-ms-input-placeholder,.taginput [disabled].taginput-container.is-focusable:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder{color:hsla(0,0%,48%,.3)}.input[readonly],.taginput [readonly].taginput-container.is-focusable,.textarea[readonly]{box-shadow:none}.input.is-white,.taginput .is-white.taginput-container.is-focusable,.textarea.is-white{border-color:#fff}.input.is-white.is-active,.input.is-white.is-focused,.input.is-white:active,.input.is-white:focus,.taginput .is-white.is-active.taginput-container.is-focusable,.taginput .is-white.is-focused.taginput-container.is-focusable,.taginput .is-white.taginput-container.is-focusable:active,.taginput .is-white.taginput-container.is-focusable:focus,.textarea.is-white.is-active,.textarea.is-white.is-focused,.textarea.is-white:active,.textarea.is-white:focus{box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.input.is-black,.taginput .is-black.taginput-container.is-focusable,.textarea.is-black{border-color:#0a0a0a}.input.is-black.is-active,.input.is-black.is-focused,.input.is-black:active,.input.is-black:focus,.taginput .is-black.is-active.taginput-container.is-focusable,.taginput .is-black.is-focused.taginput-container.is-focusable,.taginput .is-black.taginput-container.is-focusable:active,.taginput .is-black.taginput-container.is-focusable:focus,.textarea.is-black.is-active,.textarea.is-black.is-focused,.textarea.is-black:active,.textarea.is-black:focus{box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.input.is-light,.taginput .is-light.taginput-container.is-focusable,.textarea.is-light{border-color:#f5f5f5}.input.is-light.is-active,.input.is-light.is-focused,.input.is-light:active,.input.is-light:focus,.taginput .is-light.is-active.taginput-container.is-focusable,.taginput .is-light.is-focused.taginput-container.is-focusable,.taginput .is-light.taginput-container.is-focusable:active,.taginput .is-light.taginput-container.is-focusable:focus,.textarea.is-light.is-active,.textarea.is-light.is-focused,.textarea.is-light:active,.textarea.is-light:focus{box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.input.is-dark,.taginput .is-dark.taginput-container.is-focusable,.textarea.is-dark{border-color:#363636}.input.is-dark.is-active,.input.is-dark.is-focused,.input.is-dark:active,.input.is-dark:focus,.taginput .is-dark.is-active.taginput-container.is-focusable,.taginput .is-dark.is-focused.taginput-container.is-focusable,.taginput .is-dark.taginput-container.is-focusable:active,.taginput .is-dark.taginput-container.is-focusable:focus,.textarea.is-dark.is-active,.textarea.is-dark.is-focused,.textarea.is-dark:active,.textarea.is-dark:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.input.is-primary,.taginput .is-primary.taginput-container.is-focusable,.textarea.is-primary{border-color:#00d1b2}.input.is-primary.is-active,.input.is-primary.is-focused,.input.is-primary:active,.input.is-primary:focus,.taginput .is-primary.is-active.taginput-container.is-focusable,.taginput .is-primary.is-focused.taginput-container.is-focusable,.taginput .is-primary.taginput-container.is-focusable:active,.taginput .is-primary.taginput-container.is-focusable:focus,.textarea.is-primary.is-active,.textarea.is-primary.is-focused,.textarea.is-primary:active,.textarea.is-primary:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.input.is-link,.taginput .is-link.taginput-container.is-focusable,.textarea.is-link{border-color:#3273dc}.input.is-link.is-active,.input.is-link.is-focused,.input.is-link:active,.input.is-link:focus,.taginput .is-link.is-active.taginput-container.is-focusable,.taginput .is-link.is-focused.taginput-container.is-focusable,.taginput .is-link.taginput-container.is-focusable:active,.taginput .is-link.taginput-container.is-focusable:focus,.textarea.is-link.is-active,.textarea.is-link.is-focused,.textarea.is-link:active,.textarea.is-link:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input.is-info,.taginput .is-info.taginput-container.is-focusable,.textarea.is-info{border-color:#209cee}.input.is-info.is-active,.input.is-info.is-focused,.input.is-info:active,.input.is-info:focus,.taginput .is-info.is-active.taginput-container.is-focusable,.taginput .is-info.is-focused.taginput-container.is-focusable,.taginput .is-info.taginput-container.is-focusable:active,.taginput .is-info.taginput-container.is-focusable:focus,.textarea.is-info.is-active,.textarea.is-info.is-focused,.textarea.is-info:active,.textarea.is-info:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.input.is-success,.taginput .is-success.taginput-container.is-focusable,.textarea.is-success{border-color:#23d160}.input.is-success.is-active,.input.is-success.is-focused,.input.is-success:active,.input.is-success:focus,.taginput .is-success.is-active.taginput-container.is-focusable,.taginput .is-success.is-focused.taginput-container.is-focusable,.taginput .is-success.taginput-container.is-focusable:active,.taginput .is-success.taginput-container.is-focusable:focus,.textarea.is-success.is-active,.textarea.is-success.is-focused,.textarea.is-success:active,.textarea.is-success:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.input.is-warning,.taginput .is-warning.taginput-container.is-focusable,.textarea.is-warning{border-color:#ffdd57}.input.is-warning.is-active,.input.is-warning.is-focused,.input.is-warning:active,.input.is-warning:focus,.taginput .is-warning.is-active.taginput-container.is-focusable,.taginput .is-warning.is-focused.taginput-container.is-focusable,.taginput .is-warning.taginput-container.is-focusable:active,.taginput .is-warning.taginput-container.is-focusable:focus,.textarea.is-warning.is-active,.textarea.is-warning.is-focused,.textarea.is-warning:active,.textarea.is-warning:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.input.is-danger,.taginput .is-danger.taginput-container.is-focusable,.textarea.is-danger{border-color:#ff3860}.input.is-danger.is-active,.input.is-danger.is-focused,.input.is-danger:active,.input.is-danger:focus,.taginput .is-danger.is-active.taginput-container.is-focusable,.taginput .is-danger.is-focused.taginput-container.is-focusable,.taginput .is-danger.taginput-container.is-focusable:active,.taginput .is-danger.taginput-container.is-focusable:focus,.textarea.is-danger.is-active,.textarea.is-danger.is-focused,.textarea.is-danger:active,.textarea.is-danger:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.input.is-small,.taginput .is-small.taginput-container.is-focusable,.textarea.is-small{border-radius:2px;font-size:.75rem}.input.is-medium,.taginput .is-medium.taginput-container.is-focusable,.textarea.is-medium{font-size:1.25rem}.input.is-large,.taginput .is-large.taginput-container.is-focusable,.textarea.is-large{font-size:1.5rem}.input.is-fullwidth,.taginput .is-fullwidth.taginput-container.is-focusable,.textarea.is-fullwidth{display:block;width:100%}.input.is-inline,.taginput .is-inline.taginput-container.is-focusable,.textarea.is-inline{display:inline;width:auto}.input.is-rounded,.taginput .is-rounded.taginput-container.is-focusable{border-radius:290486px;padding-left:1em;padding-right:1em}.input.is-static,.taginput .is-static.taginput-container.is-focusable{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:.625em;resize:vertical}.textarea:not([rows]){max-height:600px;min-height:120px}.textarea[rows]{height:auto}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox[disabled],.radio[disabled]{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.25em}.select:not(.is-multiple):not(.is-loading):after{border-color:#3273dc;right:1.125em;z-index:4}.select.is-rounded select{border-radius:290486px;padding-left:1em}.select select{background-color:#fff;border-color:#dbdbdb;color:#363636;cursor:pointer;display:block;font-size:1em;max-width:100%;outline:none}.select select::-moz-placeholder{color:rgba(54,54,54,.3)}.select select::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.select select:-moz-placeholder{color:rgba(54,54,54,.3)}.select select:-ms-input-placeholder{color:rgba(54,54,54,.3)}.select select.is-hovered,.select select:hover{border-color:#b5b5b5}.select select.is-active,.select select.is-focused,.select select:active,.select select:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select select[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.select select[disabled]::-moz-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]::-webkit-input-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]:-moz-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]:-ms-input-placeholder{color:hsla(0,0%,48%,.3)}.select select::-ms-expand{display:none}.select select[disabled]:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:.5em 1em}.select:not(.is-multiple):not(.is-loading):hover:after{border-color:#363636}.select.is-white:not(:hover):after,.select.is-white select{border-color:#fff}.select.is-white select.is-hovered,.select.is-white select:hover{border-color:#f2f2f2}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.select.is-black:not(:hover):after,.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-hovered,.select.is-black select:hover{border-color:#000}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.select.is-light:not(:hover):after,.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-hovered,.select.is-light select:hover{border-color:#e8e8e8}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.select.is-dark:not(:hover):after,.select.is-dark select{border-color:#363636}.select.is-dark select.is-hovered,.select.is-dark select:hover{border-color:#292929}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary:not(:hover):after,.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-hovered,.select.is-primary select:hover{border-color:#00b89c}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link:not(:hover):after,.select.is-link select{border-color:#3273dc}.select.is-link select.is-hovered,.select.is-link select:hover{border-color:#2366d1}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select.is-info:not(:hover):after,.select.is-info select{border-color:#209cee}.select.is-info select.is-hovered,.select.is-info select:hover{border-color:#118fe4}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.select.is-success:not(:hover):after,.select.is-success select{border-color:#23d160}.select.is-success select.is-hovered,.select.is-success select:hover{border-color:#20bc56}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.select.is-warning:not(:hover):after,.select.is-warning select{border-color:#ffdd57}.select.is-warning select.is-hovered,.select.is-warning select:hover{border-color:#ffd83d}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.select.is-danger:not(:hover):after,.select.is-danger select{border-color:#ff3860}.select.is-danger select.is-hovered,.select.is-danger select:hover{border-color:#ff1f4b}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled:after{border-color:#7a7a7a}.select.is-fullwidth,.select.is-fullwidth select{width:100%}.select.is-loading:after{margin-top:0;position:absolute;right:.625em;top:.625em;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,100%,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,4%,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:#363636}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:#363636}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,96%,.25);color:#363636}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:#363636}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#f5f5f5}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(54,54,54,.25);color:#f5f5f5}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#f5f5f5}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#3273dc;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#276cda;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(50,115,220,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#2366d1;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#209cee;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#1496ed;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(32,156,238,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#118fe4;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#23d160;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#22c65b;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(35,209,96,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#20bc56;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,221,87,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#ff3860;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#ff2b56;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,56,96,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ff1f4b;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:.01em;left:0;outline:none;position:absolute;top:0;width:.01em}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:left;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#3273dc}.help.is-info{color:#209cee}.help.is-success{color:#23d160}.help.is-warning{color:#ffdd57}.help.is-danger{color:#ff3860}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select,.field.has-addons .control:not(:first-child):not(:last-child) .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:not(:first-child):not(:last-child) .taginput-container.is-focusable{border-radius:0}.field.has-addons .control:first-child .button,.field.has-addons .control:first-child .input,.field.has-addons .control:first-child .select select,.field.has-addons .control:first-child .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:first-child .taginput-container.is-focusable{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child .button,.field.has-addons .control:last-child .input,.field.has-addons .control:last-child .select select,.field.has-addons .control:last-child .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:last-child .taginput-container.is-focusable{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button.is-hovered,.field.has-addons .control .button:hover,.field.has-addons .control .input.is-hovered,.field.has-addons .control .input:hover,.field.has-addons .control .select select.is-hovered,.field.has-addons .control .select select:hover,.field.has-addons .control .taginput .is-hovered.taginput-container.is-focusable,.field.has-addons .control .taginput .taginput-container.is-focusable:hover,.taginput .field.has-addons .control .is-hovered.taginput-container.is-focusable,.taginput .field.has-addons .control .taginput-container.is-focusable:hover{z-index:2}.field.has-addons .control .button.is-active,.field.has-addons .control .button.is-focused,.field.has-addons .control .button:active,.field.has-addons .control .button:focus,.field.has-addons .control .input.is-active,.field.has-addons .control .input.is-focused,.field.has-addons .control .input:active,.field.has-addons .control .input:focus,.field.has-addons .control .select select.is-active,.field.has-addons .control .select select.is-focused,.field.has-addons .control .select select:active,.field.has-addons .control .select select:focus,.field.has-addons .control .taginput .is-active.taginput-container.is-focusable,.field.has-addons .control .taginput .is-focused.taginput-container.is-focusable,.field.has-addons .control .taginput .taginput-container.is-focusable:active,.field.has-addons .control .taginput .taginput-container.is-focusable:focus,.taginput .field.has-addons .control .is-active.taginput-container.is-focusable,.taginput .field.has-addons .control .is-focused.taginput-container.is-focusable,.taginput .field.has-addons .control .taginput-container.is-focusable:active,.taginput .field.has-addons .control .taginput-container.is-focusable:focus{z-index:3}.field.has-addons .control .button.is-active:hover,.field.has-addons .control .button.is-focused:hover,.field.has-addons .control .button:active:hover,.field.has-addons .control .button:focus:hover,.field.has-addons .control .input.is-active:hover,.field.has-addons .control .input.is-focused:hover,.field.has-addons .control .input:active:hover,.field.has-addons .control .input:focus:hover,.field.has-addons .control .select select.is-active:hover,.field.has-addons .control .select select.is-focused:hover,.field.has-addons .control .select select:active:hover,.field.has-addons .control .select select:focus:hover,.field.has-addons .control .taginput .is-active.taginput-container.is-focusable:hover,.field.has-addons .control .taginput .is-focused.taginput-container.is-focusable:hover,.field.has-addons .control .taginput .taginput-container.is-focusable:active:hover,.field.has-addons .control .taginput .taginput-container.is-focusable:focus:hover,.taginput .field.has-addons .control .is-active.taginput-container.is-focusable:hover,.taginput .field.has-addons .control .is-focused.taginput-container.is-focusable:hover,.taginput .field.has-addons .control .taginput-container.is-focusable:active:hover,.taginput .field.has-addons .control .taginput-container.is-focusable:focus:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media print,screen and (min-width:769px){.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media print,screen and (min-width:769px){.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media print,screen and (min-width:769px){.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{font-size:1rem;position:relative;text-align:left}.control.has-icon .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icon .input:focus+.icon,.control.has-icon .taginput .taginput-container.is-focusable:focus+.icon,.taginput .control.has-icon .taginput-container.is-focusable:focus+.icon{color:#7a7a7a}.control.has-icon .input.is-small+.icon,.control.has-icon .taginput .is-small.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-small.taginput-container.is-focusable+.icon{font-size:.75rem}.control.has-icon .input.is-medium+.icon,.control.has-icon .taginput .is-medium.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-medium.taginput-container.is-focusable+.icon{font-size:1.25rem}.control.has-icon .input.is-large+.icon,.control.has-icon .taginput .is-large.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-large.taginput-container.is-focusable+.icon{font-size:1.5rem}.control.has-icon:not(.has-icon-right) .icon{left:0}.control.has-icon:not(.has-icon-right) .input,.control.has-icon:not(.has-icon-right) .taginput .taginput-container.is-focusable,.taginput .control.has-icon:not(.has-icon-right) .taginput-container.is-focusable{padding-left:2.25em}.control.has-icon.has-icon-right .icon{right:0}.control.has-icon.has-icon-right .input,.control.has-icon.has-icon-right .taginput .taginput-container.is-focusable,.taginput .control.has-icon.has-icon-right .taginput-container.is-focusable{padding-right:2.25em}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-left .taginput .taginput-container.is-focusable:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon,.control.has-icons-right .taginput .taginput-container.is-focusable:focus~.icon,.taginput .control.has-icons-left .taginput-container.is-focusable:focus~.icon,.taginput .control.has-icons-right .taginput-container.is-focusable:focus~.icon{color:#7a7a7a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-left .taginput .is-small.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon,.control.has-icons-right .taginput .is-small.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-small.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-small.taginput-container.is-focusable~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-left .taginput .is-medium.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon,.control.has-icons-right .taginput .is-medium.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-medium.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-medium.taginput-container.is-focusable~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-left .taginput .is-large.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon,.control.has-icons-right .taginput .is-large.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-large.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-large.taginput-container.is-focusable~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select,.control.has-icons-left .taginput .taginput-container.is-focusable,.taginput .control.has-icons-left .taginput-container.is-focusable{padding-left:2.25em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select,.control.has-icons-right .taginput .taginput-container.is-focusable,.taginput .control.has-icons-right .taginput-container.is-focusable{padding-right:2.25em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading:after{position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:290486px}.image.is-1by1 img,.image.is-1by2 img,.image.is-1by3 img,.image.is-2by1 img,.image.is-2by3 img,.image.is-3by1 img,.image.is-3by2 img,.image.is-3by4 img,.image.is-3by5 img,.image.is-4by3 img,.image.is-4by5 img,.image.is-5by3 img,.image.is-5by4 img,.image.is-9by16 img,.image.is-16by9 img,.image.is-square img{height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;padding:1.25rem 2.5rem 1.25rem 1.5rem;position:relative}.notification a:not(.button){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:transparent}.notification>.delete{position:absolute;right:.5rem;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:#363636}.notification.is-dark{background-color:#363636;color:#f5f5f5}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-link{background-color:#3273dc;color:#fff}.notification.is-info{background-color:#209cee;color:#fff}.notification.is-success{background-color:#23d160;color:#fff}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.notification.is-danger{background-color:#ff3860;color:#fff}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#dbdbdb}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-link::-webkit-progress-value{background-color:#3273dc}.progress.is-link::-moz-progress-bar{background-color:#3273dc}.progress.is-link::-ms-fill{background-color:#3273dc}.progress.is-info::-webkit-progress-value{background-color:#209cee}.progress.is-info::-moz-progress-bar{background-color:#209cee}.progress.is-info::-ms-fill{background-color:#209cee}.progress.is-success::-webkit-progress-value{background-color:#23d160}.progress.is-success::-moz-progress-bar{background-color:#23d160}.progress.is-success::-ms-fill{background-color:#23d160}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-danger::-webkit-progress-value{background-color:#ff3860}.progress.is-danger::-moz-progress-bar{background-color:#ff3860}.progress.is-danger::-ms-fill{background-color:#ff3860}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#f5f5f5}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#3273dc;border-color:#3273dc;color:#fff}.table td.is-info,.table th.is-info{background-color:#209cee;border-color:#209cee;color:#fff}.table td.is-success,.table th.is-success{background-color:#23d160;border-color:#23d160;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#ff3860;border-color:#ff3860;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table th{color:#363636;text-align:left}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(2n){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.tags.has-addons .tag:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:#363636}.tag:not(body).is-dark{background-color:#363636;color:#f5f5f5}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-link{background-color:#3273dc;color:#fff}.tag:not(body).is-info{background-color:#209cee;color:#fff}.tag:not(body).is-success{background-color:#23d160;color:#fff}.tag:not(body).is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.tag:not(body).is-danger{background-color:#ff3860;color:#fff}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete:after,.tag:not(body).is-delete:before{background-color:currentColor;content:\\\"\\\";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.tag:not(body).is-delete:before{height:1px;width:50%}.tag:not(body).is-delete:after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:290486px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.subtitle sup,.title sub,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title+.highlight{margin-top:-.75rem}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-weight:400;max-width:100%;overflow:hidden;padding:0}.highlight pre{overflow:auto;max-width:100%}.number{align-items:center;background-color:#f5f5f5;border-radius:290486px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#3273dc;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li:before{color:#b5b5b5;content:\\\"/\\\"}.breadcrumb ol,.breadcrumb ul{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li:before{content:\\\"\\\\2192\\\"}.breadcrumb.has-bullet-separator li+li:before{content:\\\"\\\\2022\\\"}.breadcrumb.has-dot-separator li+li:before{content:\\\"\\\\B7\\\"}.breadcrumb.has-succeeds-separator li+li:before{content:\\\"\\\\227B\\\"}.card{background-color:#fff;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);color:#4a4a4a;max-width:100%;position:relative}.card-header{background-color:none;align-items:stretch;box-shadow:0 1px 2px hsla(0,0%,4%,.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem}.card-header-icon,.card-header-title.is-centered{justify-content:center}.card-header-icon{align-items:center;cursor:pointer;display:flex;padding:.75rem}.card-image{display:block;position:relative}.card-content{background-color:none;padding:1.5rem}.card-footer{background-color:none;border-top:1px solid #dbdbdb;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #dbdbdb}.card .media:not(:last-child){margin-bottom:.75rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:0;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item,.dropdown .dropdown-menu .has-link a{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}.dropdown .dropdown-menu .has-link a,a.dropdown-item{padding-right:3rem;white-space:nowrap}.dropdown .dropdown-menu .has-link a:hover,a.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}.dropdown .dropdown-menu .has-link a.is-active,a.dropdown-item.is-active{background-color:#3273dc;color:#fff}.dropdown-divider{background-color:#dbdbdb;border:none;display:block;height:1px;margin:.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile,.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item{margin-right:.75rem}.level.is-mobile .level-item:not(:last-child){margin-bottom:0}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media print,screen and (min-width:769px){.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media print,screen and (min-width:769px){.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media print,screen and (min-width:769px){.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media print,screen and (min-width:769px){.level-right{display:flex}}.media{align-items:flex-start;display:flex;text-align:left}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid hsla(0,0%,86%,.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid hsla(0,0%,86%,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:left}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#3273dc;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff;color:#4d4d4d}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a;color:#090909}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:#363636}.message.is-light .message-body{border-color:#f5f5f5;color:#505050}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#f5f5f5}.message.is-dark .message-body{border-color:#363636;color:#2a2a2a}.message.is-primary{background-color:#f5fffd}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#021310}.message.is-link{background-color:#f6f9fe}.message.is-link .message-header{background-color:#3273dc;color:#fff}.message.is-link .message-body{border-color:#3273dc;color:#22509a}.message.is-info{background-color:#f6fbfe}.message.is-info .message-header{background-color:#209cee;color:#fff}.message.is-info .message-body{border-color:#209cee;color:#12537e}.message.is-success{background-color:#f6fef9}.message.is-success .message-header{background-color:#23d160;color:#fff}.message.is-success .message-body{border-color:#23d160;color:#0e301a}.message.is-warning{background-color:#fffdf5}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#3b3108}.message.is-danger{background-color:#fff5f7}.message.is-danger .message-header{background-color:#ff3860;color:#fff}.message.is-danger .message-body{border-color:#ff3860;color:#cd0930}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{align-items:center;display:none;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:hsla(0,0%,4%,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media print,screen and (min-width:769px){.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:none;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden}.modal-card-foot,.modal-card-head{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:10px}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link:after{border-color:#0a0a0a}@media screen and (min-width:1088px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link:after,.navbar.is-white .navbar-start .navbar-link:after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link:after,.navbar.is-black .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:#363636}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:#363636}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-brand .navbar-link:after{border-color:#363636}@media screen and (min-width:1088px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:#363636}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-end .navbar-link:after,.navbar.is-light .navbar-start .navbar-link:after{border-color:#363636}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#363636}}.navbar.is-dark{background-color:#363636;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link:after{border-color:#f5f5f5}@media screen and (min-width:1088px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link:after,.navbar.is-dark .navbar-start .navbar-link:after{border-color:#f5f5f5}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#f5f5f5}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link:after,.navbar.is-primary .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#3273dc;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-end .navbar-link:after,.navbar.is-link .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#3273dc;color:#fff}}.navbar.is-info{background-color:#209cee;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-end .navbar-link:after,.navbar.is-info .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#209cee;color:#fff}}.navbar.is-success{background-color:#23d160;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-end .navbar-link:after,.navbar.is-success .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#23d160;color:#fff}}.navbar.is-warning{background-color:#ffdd57}.navbar.is-warning,.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link:after{border-color:rgba(0,0,0,.7)}@media screen and (min-width:1088px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link:after,.navbar.is-warning .navbar-start .navbar-link:after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#ff3860;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-end .navbar-link:after,.navbar.is-danger .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#ff3860;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}body.has-navbar-fixed-top,html.has-navbar-fixed-top{padding-top:3.25rem}body.has-navbar-fixed-bottom,html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;transform-origin:center;transition-duration:86ms;transition-property:background-color,opacity,transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:first-child{top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:first-child{transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem .75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-.25rem;margin-right:-.25rem}.navbar-link,a.navbar-item{cursor:pointer}.navbar-link.is-active,.navbar-link:hover,a.navbar-item.is-active,a.navbar-item:hover{background-color:#fafafa;color:#3273dc}.navbar-item{display:block;flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab.is-active,.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#3273dc}.navbar-item.is-tab.is-active{border-bottom-style:solid;border-bottom-width:3px;color:#3273dc;padding-bottom:calc(.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link{padding-right:2.5em}.navbar-link:after{border-color:#3273dc;margin-top:-.375em;right:1.125em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:.5rem 0}@media screen and (max-width:1087px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link:after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px hsla(0,0%,4%,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px hsla(0,0%,4%,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top-touch .navbar-menu,.navbar.is-fixed-top .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}body.has-navbar-fixed-top-touch,html.has-navbar-fixed-top-touch{padding-top:3.25rem}body.has-navbar-fixed-bottom-touch,html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1088px){.navbar,.navbar-end,.navbar-menu,.navbar-start{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-end,.navbar.is-spaced .navbar-start{align-items:center}.navbar.is-spaced .navbar-link,.navbar.is-spaced a.navbar-item{border-radius:4px}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link,.navbar.is-transparent .navbar-link.is-active,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:hover{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item{display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link:after{transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px hsla(0,0%,4%,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown{opacity:1;pointer-events:auto;transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px hsla(0,0%,4%,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-dropdown{border-radius:6px;border-top:none;box-shadow:0 8px 8px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + -4px);transform:translateY(-5px);transition-duration:86ms;transition-property:opacity,transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-1rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-1rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px hsla(0,0%,4%,.1)}.navbar.is-fixed-top-desktop{top:0}body.has-navbar-fixed-top-desktop,html.has-navbar-fixed-top-desktop{padding-top:3.25rem}body.has-navbar-fixed-bottom-desktop,html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}body.has-spaced-navbar-fixed-top,html.has-spaced-navbar-fixed-top{padding-top:5.25rem}body.has-spaced-navbar-fixed-bottom,html.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}.navbar-link.is-active,a.navbar-item.is-active{color:#0a0a0a}.navbar-link.is-active:not(:hover),a.navbar-item.is-active:not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#fafafa}}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:290486px}.pagination.is-rounded .pagination-link{border-radius:290486px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{font-size:1em;padding-left:.5em;padding-right:.5em;justify-content:center;margin:.25rem;text-align:center}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.25em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#3273dc}.pagination-link:active,.pagination-next:active,.pagination-previous:active{box-shadow:inset 0 1px 2px hsla(0,0%,4%,.2)}.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#3273dc;border-color:#3273dc;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}@media screen and (max-width:768px){.pagination{flex-wrap:wrap}.pagination-list li,.pagination-next,.pagination-previous{flex-grow:1;flex-shrink:1}}@media print,screen and (min-width:769px){.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel-block,.panel-heading,.panel-tabs{border-bottom:1px solid #dbdbdb;border-left:1px solid #dbdbdb;border-right:1px solid #dbdbdb}.panel-block:first-child,.panel-heading:first-child,.panel-tabs:first-child{border-top:1px solid #dbdbdb}.panel-heading{background-color:#f5f5f5;border-radius:4px 4px 0 0;color:#363636;font-size:1.25em;font-weight:300;line-height:1.25;padding:.5em .75em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#3273dc}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#3273dc;color:#363636}.panel-block.is-active .panel-icon{color:#3273dc}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#3273dc;color:#3273dc}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-center,.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border:1px solid #dbdbdb;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-radius:4px 0 0 4px}.tabs.is-toggle li:last-child a{border-radius:0 4px 4px 0}.tabs.is-toggle li.is-active a{background-color:#3273dc;border-color:#3273dc;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:290486px;border-top-left-radius:290486px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:290486px;border-top-right-radius:290486px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{flex:none}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media print,screen and (min-width:769px){.column.is-narrow,.column.is-narrow-tablet{flex:none}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1087px){.column.is-narrow-touch{flex:none}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1088px){.column.is-narrow-desktop{flex:none}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1280px){.column.is-narrow-widescreen{flex:none}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1472px){.column.is-narrow-fullhd{flex:none}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:0.75rem}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media print,screen and (min-width:769px){.columns:not(.is-desktop){display:flex}}@media screen and (min-width:1088px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable .column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}.columns.is-variable.is-1{--columnGap:0.25rem}.columns.is-variable.is-2{--columnGap:0.5rem}.columns.is-variable.is-3{--columnGap:0.75rem}.columns.is-variable.is-4{--columnGap:1rem}.columns.is-variable.is-5{--columnGap:1.25rem}.columns.is-variable.is-6{--columnGap:1.5rem}.columns.is-variable.is-7{--columnGap:1.75rem}.columns.is-variable.is-8{--columnGap:2rem}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media print,screen and (min-width:769px){.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:none}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:hsla(0,0%,4%,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1087px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:hsla(0,0%,4%,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover,.hero.is-white .tabs li.is-active a{opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6,#fff 71%,#fff)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6,#fff 71%,#fff)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover,.hero.is-black .tabs li.is-active a{opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000,#0a0a0a 71%,#181616)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000,#0a0a0a 71%,#181616)}}.hero.is-light{background-color:#f5f5f5;color:#363636}.hero.is-light a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-light strong{color:inherit}.hero.is-light .title{color:#363636}.hero.is-light .subtitle{color:rgba(54,54,54,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:#363636}@media screen and (max-width:1087px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(54,54,54,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.hero.is-light .tabs a{color:#363636;opacity:.9}.hero.is-light .tabs a:hover,.hero.is-light .tabs li.is-active a{opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:#363636}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9,#f5f5f5 71%,#fff)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9,#f5f5f5 71%,#fff)}}.hero.is-dark{background-color:#363636;color:#f5f5f5}.hero.is-dark a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#f5f5f5}.hero.is-dark .subtitle{color:hsla(0,0%,96%,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#f5f5f5}@media screen and (max-width:1087px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:hsla(0,0%,96%,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.hero.is-dark .tabs a{color:#f5f5f5;opacity:.9}.hero.is-dark .tabs a:hover,.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#f5f5f5}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a,#363636 71%,#46403f)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a,#363636 71%,#46403f)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover,.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c,#00d1b2 71%,#00e7eb)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c,#00d1b2 71%,#00e7eb)}}.hero.is-link{background-color:#3273dc;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-link .navbar-menu{background-color:#3273dc}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#2366d1;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover,.hero.is-link .tabs li.is-active a{opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3273dc}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#1577c6,#3273dc 71%,#4366e5)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1577c6,#3273dc 71%,#4366e5)}}.hero.is-info{background-color:#209cee;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-info .navbar-menu{background-color:#209cee}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#118fe4;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover,.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#209cee}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#04a6d7,#209cee 71%,#3287f5)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#04a6d7,#209cee 71%,#3287f5)}}.hero.is-success{background-color:#23d160;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-success .navbar-menu{background-color:#23d160}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#20bc56;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover,.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#23d160}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#12af2f,#23d160 71%,#2ce28a)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#12af2f,#23d160 71%,#2ce28a)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1087px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover,.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffaf24,#ffdd57 71%,#fffa70)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffaf24,#ffdd57 71%,#fffa70)}}.hero.is-danger{background-color:#ff3860;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-danger .navbar-menu{background-color:#ff3860}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover,.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#ff3860}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#ff0561,#ff3860 71%,#ff5257)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ff0561,#ff3860 71%,#ff5257)}}.hero.is-small .hero-body{padding-bottom:1.5rem;padding-top:1.5rem}@media print,screen and (min-width:769px){.hero.is-medium .hero-body{padding-bottom:9rem;padding-top:9rem}}@media print,screen and (min-width:769px){.hero.is-large .hero-body{padding-bottom:18rem;padding-top:18rem}}.hero.is-fullheight .hero-body,.hero.is-halfheight .hero-body{align-items:center;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-halfheight .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media print,screen and (min-width:769px){.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0}.hero-body,.section{padding:3rem 1.5rem}@media screen and (min-width:1088px){.section.is-medium{padding:9rem 1.5rem}.section.is-large{padding:18rem 1.5rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem}.is-noscroll{position:fixed;overflow-y:hidden;width:100%;bottom:0}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{animation-name:fadeOut}@keyframes fadeOutDown{0%{opacity:1}to{opacity:0;transform:translate3d(0,100%,0)}}.fadeOutDown{animation-name:fadeOutDown}@keyframes fadeOutUp{0%{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}.fadeOutUp{animation-name:fadeOutUp}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{animation-name:fadeIn}@keyframes fadeInDown{0%{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}.fadeInDown{animation-name:fadeInDown}@keyframes fadeInUp{0%{opacity:0;transform:translate3d(0,100%,0)}to{opacity:1;transform:none}}.fadeInUp{animation-name:fadeInUp}.fade-enter-active,.fade-leave-active{transition:opacity .15s ease-out}.fade-enter,.fade-leave-to{opacity:0}.zoom-in-enter-active,.zoom-in-leave-active{transition:opacity .15s ease-out}.zoom-in-enter-active .animation-content,.zoom-in-leave-active .animation-content{transition:transform .15s ease-out}.zoom-in-enter,.zoom-in-leave-active{opacity:0}.zoom-in-enter .animation-content,.zoom-in-leave-active .animation-content{transform:scale(.95)}.zoom-out-enter-active,.zoom-out-leave-active{transition:opacity .15s ease-out}.zoom-out-enter-active .animation-content,.zoom-out-leave-active .animation-content{transition:transform .15s ease-out}.zoom-out-enter,.zoom-out-leave-active{opacity:0}.zoom-out-enter .animation-content,.zoom-out-leave-active .animation-content{transform:scale(1.05)}.slide-next-enter-active,.slide-next-leave-active,.slide-prev-enter-active,.slide-prev-leave-active{transition:transform .25s cubic-bezier(.785,.135,.15,.86)}.slide-next-enter,.slide-prev-leave-to{transform:translate3d(-100%,0,0);position:absolute;width:100%}.slide-next-leave-to,.slide-prev-enter{transform:translate3d(100%,0,0);position:absolute;width:100%}.autocomplete{position:relative}.autocomplete .dropdown-menu{display:block;min-width:100%;max-width:100%}.autocomplete .dropdown-menu.is-opened-top{top:auto;bottom:100%}.autocomplete .dropdown-content{overflow:auto;max-height:200px}.autocomplete .dropdown-item,.autocomplete .dropdown .dropdown-menu .has-link a,.dropdown .dropdown-menu .has-link .autocomplete a{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.autocomplete .dropdown-item.is-hovered,.autocomplete .dropdown .dropdown-menu .has-link a.is-hovered,.dropdown .dropdown-menu .has-link .autocomplete a.is-hovered{background:#f5f5f5;color:#0a0a0a}.autocomplete .dropdown-item.is-disabled,.autocomplete .dropdown .dropdown-menu .has-link a.is-disabled,.dropdown .dropdown-menu .has-link .autocomplete a.is-disabled{opacity:.5;cursor:not-allowed}.autocomplete.is-small{border-radius:2px;font-size:.75rem}.autocomplete.is-medium{font-size:1.25rem}.autocomplete.is-large{font-size:1.5rem}.b-checkbox.checkbox{outline:none;display:inline-flex;align-items:center}.b-checkbox.checkbox+.checkbox{margin-left:.5em}.b-checkbox.checkbox input[type=checkbox]{position:absolute;left:0;opacity:0;outline:none;z-index:-1}.b-checkbox.checkbox input[type=checkbox]+.check{width:1.25em;height:1.25em;flex-shrink:0;border-radius:4px;border:2px solid #7a7a7a;transition:background .15s ease-out}.b-checkbox.checkbox input[type=checkbox]:checked+.check{background:#00d1b2 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#00d1b2}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-white{background:#fff url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%230a0a0a'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#fff}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-black{background:#0a0a0a url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#0a0a0a}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-light{background:#f5f5f5 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23363636'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#f5f5f5}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-dark{background:#363636 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23f5f5f5'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#363636}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-primary{background:#00d1b2 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#00d1b2}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-link{background:#3273dc url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#3273dc}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-info{background:#209cee url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#209cee}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-success{background:#23d160 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#23d160}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-warning{background:#ffdd57 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='rgba(0,0,0,.7)'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#ffdd57}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-danger{background:#ff3860 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#ff3860}.b-checkbox.checkbox .control-label{padding-left:.5em}.b-checkbox.checkbox[disabled]{opacity:.5}.b-checkbox.checkbox:hover input[type=checkbox]+.check{border-color:#00d1b2}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-white{border-color:#fff}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-black{border-color:#0a0a0a}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-light{border-color:#f5f5f5}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-dark{border-color:#363636}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-primary{border-color:#00d1b2}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-link{border-color:#3273dc}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-info{border-color:#209cee}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-success{border-color:#23d160}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-warning{border-color:#ffdd57}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-danger{border-color:#ff3860}.b-checkbox.checkbox:focus input[type=checkbox]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.b-checkbox.checkbox.is-small{border-radius:2px;font-size:.75rem}.b-checkbox.checkbox.is-medium{font-size:1.25rem}.b-checkbox.checkbox.is-large{font-size:1.5rem}.collapse .collapse-trigger{display:inline;cursor:pointer}.collapse .collapse-content{display:inherit}.datepicker{font-size:.875rem}.datepicker .dropdown,.datepicker .dropdown-trigger{width:100%}.datepicker .dropdown-item,.datepicker .dropdown .dropdown-menu .has-link a,.dropdown .dropdown-menu .has-link .datepicker a{font-size:inherit}.datepicker .datepicker-header{padding-bottom:.875rem;margin-bottom:.875rem;border-bottom:1px solid #dbdbdb}.datepicker .datepicker-footer{padding-top:.875rem;border-top:1px solid #dbdbdb}.datepicker .datepicker-table{display:table;margin:0 auto .875rem}.datepicker .datepicker-table .datepicker-cell{text-align:center;vertical-align:middle;display:table-cell;border-radius:4px;padding:.5rem .75rem}.datepicker .datepicker-table .datepicker-header{display:table-header-group}.datepicker .datepicker-table .datepicker-header .datepicker-cell{color:#7a7a7a;font-weight:600}.datepicker .datepicker-table .datepicker-body{display:table-row-group}.datepicker .datepicker-table .datepicker-body .datepicker-row{display:table-row}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-unselectable{color:#b5b5b5}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-today{border:1px solid rgba(0,209,178,.5)}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable{color:#4a4a4a}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable:focus:not(.is-selected),.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable:hover:not(.is-selected){background-color:#f5f5f5;color:#0a0a0a;cursor:pointer}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selected{background-color:#00d1b2;color:#fff}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell{padding:.3rem .75rem .75rem}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event{position:relative}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events{bottom:.425rem;display:flex;justify-content:center;left:0;padding:0 .35rem;position:absolute;width:100%}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-white{background-color:#fff}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-black{background-color:#0a0a0a}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-light{background-color:#f5f5f5}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-dark{background-color:#363636}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-primary{background-color:#00d1b2}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-link{background-color:#3273dc}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-info{background-color:#209cee}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-success{background-color:#23d160}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-warning{background-color:#ffdd57}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-danger{background-color:#ff3860}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event.dots .event{border-radius:50%;height:.35em;margin:0 .1em;width:.35em}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event.bars .event{height:.25em;width:100%}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.is-selected{overflow:hidden}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.is-selected .events .event.is-primary{background-color:#1fffdd}.datepicker.is-small{border-radius:2px;font-size:.75rem}.datepicker.is-medium{font-size:1.25rem}.datepicker.is-large{font-size:1.5rem}@media screen and (min-width:769px) and (max-width:1087px){.datepicker .datepicker-table .datepicker-cell{padding:.75rem 1rem}}@media screen and (max-width:768px){.datepicker .datepicker-table .datepicker-cell{padding:.25rem .5rem}}.dialog .modal-card{max-width:460px;width:auto}.dialog .modal-card .modal-card-head{font-size:1.25rem;font-weight:600}.dialog .modal-card .modal-card-body .field{margin-top:16px}.dialog .modal-card .modal-card-body.is-titleless{border-top-left-radius:6px;border-top-right-radius:6px}.dialog .modal-card .modal-card-foot{justify-content:flex-end}.dialog .modal-card .modal-card-foot .button{display:inline;min-width:5em;font-weight:600}@media print,screen and (min-width:769px){.dialog .modal-card{min-width:320px}}.dialog.is-small .button,.dialog.is-small .input,.dialog.is-small .modal-card,.dialog.is-small .taginput .taginput-container.is-focusable,.taginput .dialog.is-small .taginput-container.is-focusable{border-radius:2px;font-size:.75rem}.dialog.is-medium .button,.dialog.is-medium .input,.dialog.is-medium .modal-card,.dialog.is-medium .taginput .taginput-container.is-focusable,.taginput .dialog.is-medium .taginput-container.is-focusable{font-size:1.25rem}.dialog.is-large .button,.dialog.is-large .input,.dialog.is-large .modal-card,.dialog.is-large .taginput .taginput-container.is-focusable,.taginput .dialog.is-large .taginput-container.is-focusable{font-size:1.5rem}.dropdown+.dropdown{margin-left:.5em}.dropdown .background{position:fixed;background-color:hsla(0,0%,4%,.86);z-index:10;cursor:pointer}@media screen and (min-width:1088px){.dropdown .background{display:none}}.dropdown .dropdown-menu .dropdown-item.is-disabled,.dropdown .dropdown-menu .has-link a.is-disabled{cursor:not-allowed}.dropdown .dropdown-menu .dropdown-item.is-disabled:hover,.dropdown .dropdown-menu .has-link a.is-disabled:hover{background:inherit;color:inherit}.dropdown .dropdown-menu .has-link a{padding-right:3rem;white-space:nowrap}.dropdown:not(.is-disabled) .dropdown-menu .dropdown-item.is-disabled,.dropdown:not(.is-disabled) .dropdown-menu .has-link a.is-disabled{opacity:.5}.dropdown .navbar-item{height:100%}.dropdown.is-disabled{opacity:.5;cursor:not-allowed}.dropdown.is-disabled .dropdown-trigger{pointer-events:none}.dropdown.is-inline .dropdown-menu{position:static;display:inline-block;padding:0}.dropdown.is-top-right .dropdown-menu{top:auto;bottom:100%}.dropdown.is-top-left .dropdown-menu{top:auto;bottom:100%;right:0;left:auto}.dropdown.is-bottom-left .dropdown-menu{right:0;left:auto}@media screen and (max-width:1087px){.dropdown.is-mobile-modal .dropdown-menu{position:fixed;width:calc(100vw - 40px);max-width:460px;max-height:calc(100vh - 120px);top:25%!important;left:50%!important;bottom:auto!important;right:auto!important;transform:translate3d(-50%,-25%,0);white-space:normal;overflow-y:auto}.dropdown.is-mobile-modal .dropdown-menu .dropdown-item,.dropdown.is-mobile-modal .dropdown-menu .has-link a{padding:1rem 1.5rem}}.label{font-weight:600}.field.is-grouped .field{flex-shrink:0}.field.is-grouped .field+.field{margin-left:.75rem}.field.is-grouped .field.is-expanded{flex-grow:1;flex-shrink:1}.field.has-addons .control:first-child .control .button,.field.has-addons .control:first-child .control .input,.field.has-addons .control:first-child .control .select select,.field.has-addons .control:first-child .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:first-child .control .taginput-container.is-focusable{border-bottom-left-radius:4px;border-top-left-radius:4px}.field.has-addons .control:last-child .control .button,.field.has-addons .control:last-child .control .input,.field.has-addons .control:last-child .control .select select,.field.has-addons .control:last-child .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:last-child .control .taginput-container.is-focusable{border-bottom-right-radius:4px;border-top-right-radius:4px}.field.has-addons .control .control .button,.field.has-addons .control .control .input,.field.has-addons .control .control .select select,.field.has-addons .control .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control .control .taginput-container.is-focusable{border-radius:0}.control .help.counter{float:right;margin-left:.5em}.control .icon.is-clickable{pointer-events:auto;cursor:pointer}.icon{cursor:inherit}.icon svg{background-color:transparent;fill:currentColor;stroke-width:0;stroke:currentColor;pointer-events:none;width:1.5rem;height:1.5rem}.loading-overlay{align-items:center;display:none;justify-content:center;overflow:hidden}.loading-overlay.is-active{display:flex}.loading-overlay.is-full-page{z-index:999;position:fixed}.loading-overlay.is-full-page .loading-icon:after{top:calc(50% - 2.5em);left:calc(50% - 2.5em);width:5em;height:5em}.loading-overlay .loading-background{background:#7f7f7f;background:hsla(0,0%,100%,.5)}.loading-overlay .loading-icon{position:relative}.loading-overlay .loading-icon:after{position:absolute;top:calc(50% - 1.5em);left:calc(50% - 1.5em);width:3em;height:3em;border-width:.25em}.message .media,.notification .media{padding-top:0;border:0}.notification>.delete{right:.5rem!important;top:.5rem!important}.modal .animation-content{margin:0 20px}.modal .animation-content .modal-card{margin:0}@media screen and (max-width:768px){.modal .animation-content{width:100%}}.notices{position:fixed;display:flex;top:0;bottom:0;left:0;right:0;padding:2em;overflow:hidden;z-index:1000;pointer-events:none}.notices .toast{display:inline-flex;animation-duration:.15s;margin:.5em 0;text-align:center;box-shadow:0 1px 4px rgba(0,0,0,.12),0 0 6px rgba(0,0,0,.04);border-radius:2em;padding:.75em 1.5em;pointer-events:auto;opacity:.92}.notices .toast.is-white{color:#0a0a0a;background:#fff}.notices .toast.is-black{color:#fff;background:#0a0a0a}.notices .toast.is-light{color:#363636;background:#f5f5f5}.notices .toast.is-dark{color:#f5f5f5;background:#363636}.notices .toast.is-primary{color:#fff;background:#00d1b2}.notices .toast.is-link{color:#fff;background:#3273dc}.notices .toast.is-info{color:#fff;background:#209cee}.notices .toast.is-success{color:#fff;background:#23d160}.notices .toast.is-warning{color:rgba(0,0,0,.7);background:#ffdd57}.notices .toast.is-danger{color:#fff;background:#ff3860}.notices .snackbar{display:inline-flex;align-items:center;justify-content:space-around;animation-duration:.15s;margin:.5em 0;box-shadow:0 1px 4px rgba(0,0,0,.12),0 0 6px rgba(0,0,0,.04);border-radius:4px;pointer-events:auto;background:#363636;color:#f5f5f5;min-height:3em}.notices .snackbar .text{padding:.5em 1em}.notices .snackbar .action{margin-left:auto;padding:.5em;padding-left:0}.notices .snackbar .action .button{font-weight:600;text-transform:uppercase}.notices .snackbar .action.is-white .button{color:#fff}.notices .snackbar .action.is-black .button{color:#0a0a0a}.notices .snackbar .action.is-light .button{color:#f5f5f5}.notices .snackbar .action.is-dark .button{color:#363636}.notices .snackbar .action.is-primary .button{color:#00d1b2}.notices .snackbar .action.is-link .button{color:#3273dc}.notices .snackbar .action.is-info .button{color:#209cee}.notices .snackbar .action.is-success .button{color:#23d160}.notices .snackbar .action.is-warning .button{color:#ffdd57}.notices .snackbar .action.is-danger .button{color:#ff3860}@media screen and (max-width:768px){.notices .snackbar{width:100%;margin:0;border-radius:0}}@media print,screen and (min-width:769px){.notices .snackbar{min-width:350px;max-width:600px;overflow:hidden}}.notices .snackbar.is-bottom,.notices .snackbar.is-top,.notices .toast.is-bottom,.notices .toast.is-top{align-self:center}.notices .snackbar.is-bottom-right,.notices .snackbar.is-top-right,.notices .toast.is-bottom-right,.notices .toast.is-top-right{align-self:flex-end}.notices .snackbar.is-bottom-left,.notices .snackbar.is-top-left,.notices .toast.is-bottom-left,.notices .toast.is-top-left{align-self:flex-start}.notices .snackbar.is-toast,.notices .toast.is-toast{opacity:.92}.notices.is-top{flex-direction:column}.notices.is-bottom{flex-direction:column-reverse}.notices.has-custom-container{position:absolute}@media screen and (max-width:768px){.notices{padding:0;position:fixed!important}}.pagination .pagination-next,.pagination .pagination-previous{padding-left:.25em;padding-right:.25em}.pagination .pagination-next.is-disabled,.pagination .pagination-previous.is-disabled{pointer-events:none;cursor:not-allowed;opacity:.5}.pagination.is-simple{justify-content:normal}.pagination .is-current{pointer-events:none;cursor:not-allowed}.panel .panel-heading.is-collapsible{cursor:pointer}.panel .panel-content{width:100%}.b-radio.radio{outline:none;display:inline-flex;align-items:center}.b-radio.radio+.radio{margin-left:.5em}.b-radio.radio input[type=radio]{position:absolute;left:0;opacity:0;outline:none;z-index:-1}.b-radio.radio input[type=radio]+.check{display:flex;align-items:center;justify-content:center;width:1.25em;height:1.25em;border:2px solid #7a7a7a;border-radius:50%;transition:background .15s ease-out}.b-radio.radio input[type=radio]+.check:before{content:\\\"\\\";border-radius:50%;width:.625em;height:.625em;background:#00d1b2;transform:scale(0);transition:transform .15s ease-out}.b-radio.radio input[type=radio]+.check.is-white:before{background:#fff}.b-radio.radio input[type=radio]+.check.is-black:before{background:#0a0a0a}.b-radio.radio input[type=radio]+.check.is-light:before{background:#f5f5f5}.b-radio.radio input[type=radio]+.check.is-dark:before{background:#363636}.b-radio.radio input[type=radio]+.check.is-primary:before{background:#00d1b2}.b-radio.radio input[type=radio]+.check.is-link:before{background:#3273dc}.b-radio.radio input[type=radio]+.check.is-info:before{background:#209cee}.b-radio.radio input[type=radio]+.check.is-success:before{background:#23d160}.b-radio.radio input[type=radio]+.check.is-warning:before{background:#ffdd57}.b-radio.radio input[type=radio]+.check.is-danger:before{background:#ff3860}.b-radio.radio input[type=radio]:checked+.check{border-color:#00d1b2}.b-radio.radio input[type=radio]:checked+.check.is-white{border-color:#fff}.b-radio.radio input[type=radio]:checked+.check.is-black{border-color:#0a0a0a}.b-radio.radio input[type=radio]:checked+.check.is-light{border-color:#f5f5f5}.b-radio.radio input[type=radio]:checked+.check.is-dark{border-color:#363636}.b-radio.radio input[type=radio]:checked+.check.is-primary{border-color:#00d1b2}.b-radio.radio input[type=radio]:checked+.check.is-link{border-color:#3273dc}.b-radio.radio input[type=radio]:checked+.check.is-info{border-color:#209cee}.b-radio.radio input[type=radio]:checked+.check.is-success{border-color:#23d160}.b-radio.radio input[type=radio]:checked+.check.is-warning{border-color:#ffdd57}.b-radio.radio input[type=radio]:checked+.check.is-danger{border-color:#ff3860}.b-radio.radio input[type=radio]:checked+.check:before{transform:scale(1)}.b-radio.radio .control-label{padding-left:.5em}.b-radio.radio[disabled]{opacity:.5}.b-radio.radio:hover input[type=radio]+.check{border-color:#00d1b2}.b-radio.radio:hover input[type=radio]+.check.is-white{border-color:#fff}.b-radio.radio:hover input[type=radio]+.check.is-black{border-color:#0a0a0a}.b-radio.radio:hover input[type=radio]+.check.is-light{border-color:#f5f5f5}.b-radio.radio:hover input[type=radio]+.check.is-dark{border-color:#363636}.b-radio.radio:hover input[type=radio]+.check.is-primary{border-color:#00d1b2}.b-radio.radio:hover input[type=radio]+.check.is-link{border-color:#3273dc}.b-radio.radio:hover input[type=radio]+.check.is-info{border-color:#209cee}.b-radio.radio:hover input[type=radio]+.check.is-success{border-color:#23d160}.b-radio.radio:hover input[type=radio]+.check.is-warning{border-color:#ffdd57}.b-radio.radio:hover input[type=radio]+.check.is-danger{border-color:#ff3860}.b-radio.radio:focus input[type=radio]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.b-radio.radio.is-small{border-radius:2px;font-size:.75rem}.b-radio.radio.is-medium{font-size:1.25rem}.b-radio.radio.is-large{font-size:1.5rem}.select select{padding-right:2.5em}.select select option{color:#4a4a4a;padding:.25em .5em}.select select option:disabled{cursor:not-allowed;opacity:.5}.select select optgroup{color:#b5b5b5;font-weight:400;font-style:normal;padding:.25em 0}.select.is-empty select{color:hsla(0,0%,48%,.7)}.switch{cursor:pointer;display:inline-flex;align-items:center}.switch+.switch{margin-left:.5em}.switch input[type=checkbox]{display:none}.switch input[type=checkbox]+.check{display:flex;align-items:center;flex-shrink:0;width:2.75em;height:1.575em;padding:.2em;background:#b5b5b5;border-radius:1em;transition:background .15s ease-out}.switch input[type=checkbox]+.check:before{content:\\\"\\\";border-radius:1em;width:1.175em;height:1.175em;background:#f5f5f5;box-shadow:0 3px 1px 0 rgba(0,0,0,.05),0 2px 2px 0 rgba(0,0,0,.1),0 3px 3px 0 rgba(0,0,0,.05);transition:transform .15s ease-out,width .15s ease-out;will-change:transform}.switch input[type=checkbox]+.check.is-elastic:before{width:1.75em}.switch input[type=checkbox]:checked+.check{background:#00d1b2}.switch input[type=checkbox]:checked+.check.is-white{background:#fff}.switch input[type=checkbox]:checked+.check.is-black{background:#0a0a0a}.switch input[type=checkbox]:checked+.check.is-light{background:#f5f5f5}.switch input[type=checkbox]:checked+.check.is-dark{background:#363636}.switch input[type=checkbox]:checked+.check.is-primary{background:#00d1b2}.switch input[type=checkbox]:checked+.check.is-link{background:#3273dc}.switch input[type=checkbox]:checked+.check.is-info{background:#209cee}.switch input[type=checkbox]:checked+.check.is-success{background:#23d160}.switch input[type=checkbox]:checked+.check.is-warning{background:#ffdd57}.switch input[type=checkbox]:checked+.check.is-danger{background:#ff3860}.switch input[type=checkbox]:checked+.check:before{transform:translate3d(100%,0,0)}.switch input[type=checkbox]:checked+.check.is-elastic:before{transform:translate3d(36.36364%,0,0)}.switch .control-label{padding-left:.5em}.switch:hover input[type=checkbox]+.check{background:hsla(0,0%,71%,.9)}.switch:hover input[type=checkbox]:checked+.check{background:rgba(0,209,178,.9)}.switch:hover input[type=checkbox]:checked+.check.is-white{background:hsla(0,0%,100%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-black{background:hsla(0,0%,4%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-light{background:hsla(0,0%,96%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-dark{background:rgba(54,54,54,.9)}.switch:hover input[type=checkbox]:checked+.check.is-primary{background:rgba(0,209,178,.9)}.switch:hover input[type=checkbox]:checked+.check.is-link{background:rgba(50,115,220,.9)}.switch:hover input[type=checkbox]:checked+.check.is-info{background:rgba(32,156,238,.9)}.switch:hover input[type=checkbox]:checked+.check.is-success{background:rgba(35,209,96,.9)}.switch:hover input[type=checkbox]:checked+.check.is-warning{background:rgba(255,221,87,.9)}.switch:hover input[type=checkbox]:checked+.check.is-danger{background:rgba(255,56,96,.9)}.switch:focus{outline:none}.switch:focus input[type=checkbox]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.6)}.switch:focus input[type=checkbox]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.switch:focus input[type=checkbox]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.switch:focus input[type=checkbox]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.switch:focus input[type=checkbox]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.switch:focus input[type=checkbox]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.switch:focus input[type=checkbox]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.switch:focus input[type=checkbox]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.switch:focus input[type=checkbox]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.switch.is-small{border-radius:2px;font-size:.75rem}.switch.is-medium{font-size:1.25rem}.switch.is-large{font-size:1.5rem}.switch[disabled]{opacity:.5;cursor:not-allowed;color:#7a7a7a}.table-wrapper .table{margin-bottom:0}.table-wrapper:not(:last-child){margin-bottom:1.5rem}@media screen and (max-width:1087px){.table-wrapper{overflow-x:auto}}.b-table{transition:opacity 86ms ease-out}@media print,screen and (min-width:769px){.b-table .table-mobile-sort{display:none}}.b-table .icon{transition:transform .15s ease-out,opacity 86ms ease-out}.b-table .icon.is-desc{transform:rotate(180deg)}.b-table .icon.is-expanded{transform:rotate(90deg)}.b-table .table{width:100%;border:1px solid transparent;border-radius:4px;border-collapse:separate}.b-table .table th{font-weight:600}.b-table .table th .th-wrap{display:flex;align-items:center}.b-table .table th .th-wrap .icon{margin-left:.5rem;margin-right:0;font-size:1rem}.b-table .table th .th-wrap.is-numeric{flex-direction:row-reverse;text-align:right}.b-table .table th .th-wrap.is-numeric .icon{margin-left:0;margin-right:.5rem}.b-table .table th .th-wrap.is-centered{justify-content:center;text-align:center}.b-table .table th.is-current-sort{border-color:#7a7a7a;font-weight:700}.b-table .table th.is-sortable:hover{border-color:#7a7a7a}.b-table .table th.is-sortable,.b-table .table th.is-sortable .th-wrap{cursor:pointer}.b-table .table tr.is-selected .checkbox input:checked+.check{background:#fff url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%2300d1b2'/%3E%3C/svg%3E\\\") no-repeat 50%}.b-table .table tr.is-selected .checkbox input+.check{border-color:#fff}.b-table .table tr.is-empty:hover{background-color:transparent}.b-table .table .chevron-cell{vertical-align:middle}.b-table .table .checkbox-cell{width:40px}.b-table .table .checkbox-cell .checkbox{vertical-align:middle}.b-table .table .checkbox-cell .checkbox .check{transition:none}.b-table .table tr.detail{box-shadow:inset 0 1px 3px #dbdbdb;background:#fafafa}.b-table .table tr.detail .detail-container{padding:1rem}.b-table .table:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.b-table .table.is-bordered th.is-current-sort,.b-table .table.is-bordered th.is-sortable:hover{border-color:#dbdbdb;background:#f5f5f5}@media screen and (max-width:768px){.b-table .table.has-mobile-cards thead{display:none}.b-table .table.has-mobile-cards tfoot th{border:0;display:inherit}.b-table .table.has-mobile-cards tr{box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);max-width:100%;position:relative;display:block}.b-table .table.has-mobile-cards tr td{border:0;display:inherit}.b-table .table.has-mobile-cards tr td:last-child{border-bottom:0}.b-table .table.has-mobile-cards tr:not(:last-child){margin-bottom:1rem}.b-table .table.has-mobile-cards tr:not([class*=is-]){background:inherit}.b-table .table.has-mobile-cards tr:not([class*=is-]):hover{background-color:inherit}.b-table .table.has-mobile-cards tr.detail{margin-top:-1rem}.b-table .table.has-mobile-cards tr:not(.detail):not(.is-empty):not(.table-footer) td{display:flex;width:auto;justify-content:space-between;text-align:right;border-bottom:1px solid #f5f5f5}.b-table .table.has-mobile-cards tr:not(.detail):not(.is-empty):not(.table-footer) td:before{content:attr(data-label);font-weight:600;padding-right:.5em;text-align:left}}.b-table .level{padding-bottom:1.5rem}.b-table.is-loading{position:relative;pointer-events:none;opacity:.5}.b-table.is-loading:after{position:absolute;top:4em;left:calc(50% - 2.5em);width:5em;height:5em;border-width:.25em}.b-tabs .tabs{margin-bottom:0;flex-shrink:0}.b-tabs .is-disabled{pointer-events:none;cursor:not-allowed;opacity:.5}.b-tabs .tab-content{position:relative;overflow:hidden;display:flex;flex-direction:column;padding:1rem}.b-tabs .tab-content .tab-item{flex-shrink:0;flex-basis:auto}.b-tabs:not(:last-child){margin-bottom:1.5rem}.b-tabs.is-fullwidth{width:100%}.tag .has-ellipsis{max-width:10em;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.taginput .taginput-container.is-focusable{padding-bottom:0;padding-top:calc(.275em - 1px);align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start;height:auto;cursor:text}.taginput .taginput-container>.tag,.taginput .taginput-container>.tags{margin-bottom:calc(.275em - 1px);font-size:.9em;height:1.7em}.taginput .taginput-container>.tag .tag,.taginput .taginput-container>.tags .tag{margin-bottom:0;font-size:.9em;height:1.7em}.taginput .taginput-container>.tag:not(:last-child),.taginput .taginput-container>.tags:not(:last-child){margin-right:.275rem}.taginput .taginput-container .autocomplete{flex:1}.taginput .taginput-container .autocomplete input{height:1.7em;margin-bottom:calc(.275em - 1px);padding-top:0;padding-bottom:0;border:none;box-shadow:none;min-width:8em}.taginput .taginput-container .autocomplete input:focus{box-shadow:none!important}.taginput .taginput-container .autocomplete .icon{height:1.7em}.taginput .taginput-container .autocomplete>.control.is-loading:after{top:.375em}.timepicker .dropdown,.timepicker .dropdown-trigger{width:100%}.dropdown .dropdown-menu .has-link .timepicker a,.timepicker .dropdown-item,.timepicker .dropdown .dropdown-menu .has-link a{font-size:inherit}.timepicker .timepicker-footer{padding-top:.875rem}.timepicker .control .select{font-size:2.5rem}.timepicker .control .select select{font-weight:600;padding-right:calc(.625em - 1px)}.timepicker .control .select select option{font-size:1.5rem}.timepicker .control .select select option:disabled{color:hsla(0,0%,48%,.7)}.timepicker .control .select:after{display:none}.timepicker .control:first-child .select select{border-right:none}.timepicker .control:first-child .select:before{content:\\\":\\\";color:#b5b5b5;display:inline-block;position:absolute;z-index:6;font-size:3rem;right:-.325rem;top:.25rem}.timepicker .control:nth-child(2) .select select{border-left:none}.timepicker.is-small{border-radius:2px;font-size:.75rem}.timepicker.is-medium{font-size:1.25rem}.timepicker.is-large{font-size:1.5rem}.tooltip{position:relative;display:inline-flex}.tooltip.is-top:after,.tooltip.is-top:before{top:auto;right:auto;bottom:calc(100% + 5px + 2px);left:50%;transform:translateX(-50%)}.tooltip.is-top.is-white:before{border-top:5px solid #fff}.tooltip.is-top.is-black:before,.tooltip.is-top.is-white:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-black:before{border-top:5px solid #0a0a0a}.tooltip.is-top.is-light:before{border-top:5px solid #f5f5f5}.tooltip.is-top.is-dark:before,.tooltip.is-top.is-light:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-dark:before{border-top:5px solid #363636}.tooltip.is-top.is-primary:before{border-top:5px solid #00d1b2}.tooltip.is-top.is-link:before,.tooltip.is-top.is-primary:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-link:before{border-top:5px solid #3273dc}.tooltip.is-top.is-info:before{border-top:5px solid #209cee}.tooltip.is-top.is-info:before,.tooltip.is-top.is-success:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-success:before{border-top:5px solid #23d160}.tooltip.is-top.is-warning:before{border-top:5px solid #ffdd57}.tooltip.is-top.is-danger:before,.tooltip.is-top.is-warning:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-danger:before{border-top:5px solid #ff3860}.tooltip.is-top.is-multiline.is-small:after{width:180px}.tooltip.is-top.is-multiline.is-medium:after{width:240px}.tooltip.is-top.is-multiline.is-large:after{width:300px}.tooltip.is-right:after,.tooltip.is-right:before{top:50%;right:auto;bottom:auto;left:calc(100% + 5px + 2px);transform:translateY(-50%)}.tooltip.is-right.is-white:before{border-right:5px solid #fff}.tooltip.is-right.is-black:before,.tooltip.is-right.is-white:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-black:before{border-right:5px solid #0a0a0a}.tooltip.is-right.is-light:before{border-right:5px solid #f5f5f5}.tooltip.is-right.is-dark:before,.tooltip.is-right.is-light:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-dark:before{border-right:5px solid #363636}.tooltip.is-right.is-primary:before{border-right:5px solid #00d1b2}.tooltip.is-right.is-link:before,.tooltip.is-right.is-primary:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-link:before{border-right:5px solid #3273dc}.tooltip.is-right.is-info:before{border-right:5px solid #209cee}.tooltip.is-right.is-info:before,.tooltip.is-right.is-success:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-success:before{border-right:5px solid #23d160}.tooltip.is-right.is-warning:before{border-right:5px solid #ffdd57}.tooltip.is-right.is-danger:before,.tooltip.is-right.is-warning:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-danger:before{border-right:5px solid #ff3860}.tooltip.is-right.is-multiline.is-small:after{width:180px}.tooltip.is-right.is-multiline.is-medium:after{width:240px}.tooltip.is-right.is-multiline.is-large:after{width:300px}.tooltip.is-bottom:after,.tooltip.is-bottom:before{top:calc(100% + 5px + 2px);right:auto;bottom:auto;left:50%;transform:translateX(-50%)}.tooltip.is-bottom.is-white:before{border-bottom:5px solid #fff}.tooltip.is-bottom.is-black:before,.tooltip.is-bottom.is-white:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-black:before{border-bottom:5px solid #0a0a0a}.tooltip.is-bottom.is-light:before{border-bottom:5px solid #f5f5f5}.tooltip.is-bottom.is-dark:before,.tooltip.is-bottom.is-light:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-dark:before{border-bottom:5px solid #363636}.tooltip.is-bottom.is-primary:before{border-bottom:5px solid #00d1b2}.tooltip.is-bottom.is-link:before,.tooltip.is-bottom.is-primary:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-link:before{border-bottom:5px solid #3273dc}.tooltip.is-bottom.is-info:before{border-bottom:5px solid #209cee}.tooltip.is-bottom.is-info:before,.tooltip.is-bottom.is-success:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-success:before{border-bottom:5px solid #23d160}.tooltip.is-bottom.is-warning:before{border-bottom:5px solid #ffdd57}.tooltip.is-bottom.is-danger:before,.tooltip.is-bottom.is-warning:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-danger:before{border-bottom:5px solid #ff3860}.tooltip.is-bottom.is-multiline.is-small:after{width:180px}.tooltip.is-bottom.is-multiline.is-medium:after{width:240px}.tooltip.is-bottom.is-multiline.is-large:after{width:300px}.tooltip.is-left:after,.tooltip.is-left:before{top:50%;right:calc(100% + 5px + 2px);bottom:auto;left:auto;transform:translateY(-50%)}.tooltip.is-left.is-white:before{border-left:5px solid #fff}.tooltip.is-left.is-black:before,.tooltip.is-left.is-white:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-black:before{border-left:5px solid #0a0a0a}.tooltip.is-left.is-light:before{border-left:5px solid #f5f5f5}.tooltip.is-left.is-dark:before,.tooltip.is-left.is-light:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-dark:before{border-left:5px solid #363636}.tooltip.is-left.is-primary:before{border-left:5px solid #00d1b2}.tooltip.is-left.is-link:before,.tooltip.is-left.is-primary:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-link:before{border-left:5px solid #3273dc}.tooltip.is-left.is-info:before{border-left:5px solid #209cee}.tooltip.is-left.is-info:before,.tooltip.is-left.is-success:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-success:before{border-left:5px solid #23d160}.tooltip.is-left.is-warning:before{border-left:5px solid #ffdd57}.tooltip.is-left.is-danger:before,.tooltip.is-left.is-warning:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-danger:before{border-left:5px solid #ff3860}.tooltip.is-left.is-multiline.is-small:after{width:180px}.tooltip.is-left.is-multiline.is-medium:after{width:240px}.tooltip.is-left.is-multiline.is-large:after{width:300px}.tooltip:after,.tooltip:before{position:absolute;content:\\\"\\\";opacity:0;visibility:hidden;pointer-events:none}.tooltip:before{z-index:889}.tooltip:after{content:attr(data-label);width:auto;padding:.35rem .75rem;border-radius:6px;font-size:.85rem;font-weight:400;box-shadow:0 1px 2px 1px rgba(0,1,0,.2);z-index:888;white-space:nowrap}.tooltip:not([data-label=\\\"\\\"]):hover:after,.tooltip:not([data-label=\\\"\\\"]):hover:before{opacity:1;visibility:visible}.tooltip.is-white:after{background:#fff;color:#0a0a0a}.tooltip.is-black:after{background:#0a0a0a;color:#fff}.tooltip.is-light:after{background:#f5f5f5;color:#363636}.tooltip.is-dark:after{background:#363636;color:#f5f5f5}.tooltip.is-primary:after{background:#00d1b2;color:#fff}.tooltip.is-link:after{background:#3273dc;color:#fff}.tooltip.is-info:after{background:#209cee;color:#fff}.tooltip.is-success:after{background:#23d160;color:#fff}.tooltip.is-warning:after{background:#ffdd57;color:rgba(0,0,0,.7)}.tooltip.is-danger:after{background:#ff3860;color:#fff}.tooltip:not([data-label=\\\"\\\"]).is-always:after,.tooltip:not([data-label=\\\"\\\"]).is-always:before{opacity:1;visibility:visible}.tooltip.is-multiline:after{display:flex-block;text-align:center;white-space:normal}.tooltip.is-dashed{border-bottom:1px dashed #b5b5b5;cursor:default}.tooltip.is-square:after{border-radius:0}.tooltip.is-animated:after,.tooltip.is-animated:before{transition:opacity 86ms ease-out,visibility 86ms ease-out}.upload{position:relative}.upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0;outline:none;z-index:-1}.upload .upload-draggable{display:inline-block;cursor:pointer;padding:.25em;border:1px dashed #b5b5b5;border-radius:6px}.upload .upload-draggable.is-disabled{opacity:.5;cursor:not-allowed}.upload .upload-draggable.is-loading{position:relative;pointer-events:none;opacity:.5}.upload .upload-draggable.is-loading:after{top:0;left:calc(50% - 1.5em);width:3em;height:3em;border-width:.25em}.upload .upload-draggable.is-hovered.is-white,.upload .upload-draggable:hover.is-white{border-color:#fff;background:hsla(0,0%,100%,.05)}.upload .upload-draggable.is-hovered.is-black,.upload .upload-draggable:hover.is-black{border-color:#0a0a0a;background:hsla(0,0%,4%,.05)}.upload .upload-draggable.is-hovered.is-light,.upload .upload-draggable:hover.is-light{border-color:#f5f5f5;background:hsla(0,0%,96%,.05)}.upload .upload-draggable.is-hovered.is-dark,.upload .upload-draggable:hover.is-dark{border-color:#363636;background:rgba(54,54,54,.05)}.upload .upload-draggable.is-hovered.is-primary,.upload .upload-draggable:hover.is-primary{border-color:#00d1b2;background:rgba(0,209,178,.05)}.upload .upload-draggable.is-hovered.is-link,.upload .upload-draggable:hover.is-link{border-color:#3273dc;background:rgba(50,115,220,.05)}.upload .upload-draggable.is-hovered.is-info,.upload .upload-draggable:hover.is-info{border-color:#209cee;background:rgba(32,156,238,.05)}.upload .upload-draggable.is-hovered.is-success,.upload .upload-draggable:hover.is-success{border-color:#23d160;background:rgba(35,209,96,.05)}.upload .upload-draggable.is-hovered.is-warning,.upload .upload-draggable:hover.is-warning{border-color:#ffdd57;background:rgba(255,221,87,.05)}.upload .upload-draggable.is-hovered.is-danger,.upload .upload-draggable:hover.is-danger{border-color:#ff3860;background:rgba(255,56,96,.05)}\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 23 */\n/***/ (function(module, exports) {\n\n/**\n * Translates the list format produced by css-loader into something\n * easier to manipulate.\n */\nmodule.exports = function listToStyles (parentId, list) {\n var styles = []\n var newStyles = {}\n for (var i = 0; i < list.length; i++) {\n var item = list[i]\n var id = item[0]\n var css = item[1]\n var media = item[2]\n var sourceMap = item[3]\n var part = {\n id: parentId + ':' + i,\n css: css,\n media: media,\n sourceMap: sourceMap\n }\n if (!newStyles[id]) {\n styles.push(newStyles[id] = { id: id, parts: [part] })\n } else {\n newStyles[id].parts.push(part)\n }\n }\n return styles\n}\n\n\n/***/ }),\n/* 24 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(25);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"bf15a564\", content, true, {});\n\n/***/ }),\n/* 25 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \".material-design-icon{display:inline-flex;align-self:center;position:relative;height:1em;width:1em;>.material-design-icon__svg{height:1em;width:1em;fill:currentColor;position:absolute;bottom:-.125em}}\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 26 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_App_vue__ = __webpack_require__(6);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_3e910c9a_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_App_vue__ = __webpack_require__(64);\nfunction injectStyle (ssrContext) {\n __webpack_require__(27)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_App_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_3e910c9a_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_App_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 27 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(28);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"0abd6ea6\", content, true, {});\n\n/***/ }),\n/* 28 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 29 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_NavBar_vue__ = __webpack_require__(7);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_657ba0aa_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_NavBar_vue__ = __webpack_require__(32);\nfunction injectStyle (ssrContext) {\n __webpack_require__(30)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_NavBar_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_657ba0aa_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_NavBar_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 30 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(31);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"432a0e31\", content, true, {});\n\n/***/ }),\n/* 31 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 32 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('nav',{staticClass:\"navbar has-shadow\"},[_vm._m(0),_vm._v(\" \"),_c('div',{staticClass:\"navbar-menu\"},[_c('div',{staticClass:\"navbar-start\"}),_vm._v(\" \"),_c('div',{staticClass:\"navbar-end\"},[_c('div',{staticClass:\"navbar-item\"},[_c('p',{staticClass:\"control\"},[(_vm.isConnected)?_c('button',{staticClass:\"button is-danger\",on:{\"click\":_vm.disconnect}},[_vm._v(\"Disconnect\")]):_c('button',{staticClass:\"button is-success\",on:{\"click\":_vm.connect}},[_vm._v(\"Connect\")])])])])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"navbar-brand\"},[_c('a',{staticClass:\"navbar-item\",attrs:{\"href\":\"#\"}},[_vm._v(\"WebRender Debugger\")])])}]\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 33 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_NavMenu_vue__ = __webpack_require__(8);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_e621d982_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_NavMenu_vue__ = __webpack_require__(36);\nfunction injectStyle (ssrContext) {\n __webpack_require__(34)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_NavMenu_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_e621d982_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_NavMenu_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 34 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(35);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"e4edc9ba\", content, true, {});\n\n/***/ }),\n/* 35 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 36 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('aside',{staticClass:\"menu\"},[_c('p',{staticClass:\"menu-label\"},[_vm._v(\"\\n Pages\\n \")]),_vm._v(\" \"),_c('ul',{staticClass:\"menu-list\"},[_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'options' },on:{\"click\":function($event){return _vm.setPage('options')}}},[_vm._v(\"Debug Options\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'passes' },on:{\"click\":function($event){return _vm.setPage('passes')}}},[_vm._v(\"Passes\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'render_tasks' },on:{\"click\":function($event){return _vm.setPage('render_tasks')}}},[_vm._v(\"Render Tasks\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'documents' },on:{\"click\":function($event){return _vm.setPage('documents')}}},[_vm._v(\"Documents\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'clip_scroll_tree' },on:{\"click\":function($event){return _vm.setPage('clip_scroll_tree')}}},[_vm._v(\"Clip-Scroll Tree\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'screenshot' },on:{\"click\":function($event){return _vm.setPage('screenshot')}}},[_vm._v(\"Screenshot\")])])])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 37 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_OptionsPage_vue__ = __webpack_require__(9);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_61a8a336_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_OptionsPage_vue__ = __webpack_require__(40);\nfunction injectStyle (ssrContext) {\n __webpack_require__(38)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_OptionsPage_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_61a8a336_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_OptionsPage_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 38 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(39);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"0e6ffcbb\", content, true, {});\n\n/***/ }),\n/* 39 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 40 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setProfiler($event.target.checked)}}}),_vm._v(\"\\n Profiler\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setTextureCacheDebugger($event.target.checked)}}}),_vm._v(\"\\n Texture cache debugger\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setRenderTargetDebugger($event.target.checked)}}}),_vm._v(\"\\n Render target debugger\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setAlphaRectsDebugger($event.target.checked)}}}),_vm._v(\"\\n Alpha primitive rects debugger\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setGpuTimeQueries($event.target.checked)}}}),_vm._v(\"\\n Enable GPU time queries\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setGpuSampleQueries($event.target.checked)}}}),_vm._v(\"\\n Enable GPU sample queries\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setOpaquePass(!$event.target.checked)}}}),_vm._v(\"\\n Disable opaque pass\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setAlphaPass(!$event.target.checked)}}}),_vm._v(\"\\n Disable alpha pass\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setClipMasks(!$event.target.checked)}}}),_vm._v(\"\\n Disable clip masks\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setTextPrims(!$event.target.checked)}}}),_vm._v(\"\\n Disable text primitives\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setGradientPrims(!$event.target.checked)}}}),_vm._v(\"\\n Disable gradient primitives\\n \")])])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 41 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_PassViewPage_vue__ = __webpack_require__(10);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_329e01f6_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_PassViewPage_vue__ = __webpack_require__(44);\nfunction injectStyle (ssrContext) {\n __webpack_require__(42)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_PassViewPage_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_329e01f6_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_PassViewPage_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 42 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(43);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"05673d18\", content, true, {});\n\n/***/ }),\n/* 43 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 44 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Passes \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_vm._l((_vm.passes),function(pass,pass_index){return _c('div',[_c('p',{staticClass:\"has-text-black-bis\"},[_vm._v(\"Pass \"+_vm._s(pass_index))]),_vm._v(\" \"),_vm._l((pass.targets),function(target,target_index){return _c('div',[_c('p',{staticClass:\"has-text-grey-dark\",staticStyle:{\"text-indent\":\"2em\"}},[_vm._v(\"Target \"+_vm._s(target_index)+\" (\"+_vm._s(target.kind)+\")\")]),_vm._v(\" \"),_vm._l((target.batches),function(batch,batch_index){return _c('div',[_c('p',{staticClass:\"has-text-grey\",staticStyle:{\"text-indent\":\"4em\"}},[_vm._v(\"Batch \"+_vm._s(batch_index)+\" (\"+_vm._s(batch.description)+\", \"+_vm._s(batch.kind)+\", \"+_vm._s(batch.count)+\" instances)\")])])})],2)}),_vm._v(\" \"),_c('hr')],2)})],2)}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 45 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_RenderTaskViewPage_vue__ = __webpack_require__(11);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_49dfebc9_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_RenderTaskViewPage_vue__ = __webpack_require__(51);\nfunction injectStyle (ssrContext) {\n __webpack_require__(46)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_RenderTaskViewPage_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_49dfebc9_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_RenderTaskViewPage_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 46 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(47);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"0d80f44e\", content, true, {});\n\n/***/ }),\n/* 47 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 48 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(49);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"77142b62\", content, true, {});\n\n/***/ }),\n/* 49 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 50 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('li',[_c('div',{on:{\"click\":_vm.toggle}},[(_vm.isFolder)?_c('span',[_vm._v(\"[\"+_vm._s(_vm.open ? '-' : '+')+\"]\")]):_vm._e(),_vm._v(\"\\n \"+_vm._s(_vm.model.description)+\"\\n \")]),_vm._v(\" \"),(_vm.isFolder)?_c('ul',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.open),expression:\"open\"}],staticStyle:{\"padding-left\":\"1em\",\"line-height\":\"1.5em\"}},_vm._l((_vm.model.children),function(model){return _c('treeview',{attrs:{\"model\":model}})}),1):_vm._e()])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 51 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Render Tasks \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_c('div',[_c('ul',[_c('app-treeview',{attrs:{\"model\":_vm.render_tasks}})],1)])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 52 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_DocumentViewPage_vue__ = __webpack_require__(13);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_1b68865d_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_DocumentViewPage_vue__ = __webpack_require__(55);\nfunction injectStyle (ssrContext) {\n __webpack_require__(53)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_DocumentViewPage_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_1b68865d_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_DocumentViewPage_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 53 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(54);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"7b986bc8\", content, true, {});\n\n/***/ }),\n/* 54 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 55 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Documents \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_c('div',[_c('ul',[_c('app-treeview',{attrs:{\"model\":_vm.documents}})],1)])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 56 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_ClipScrollTreeViewPage_vue__ = __webpack_require__(14);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_31f0a1d9_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_ClipScrollTreeViewPage_vue__ = __webpack_require__(59);\nfunction injectStyle (ssrContext) {\n __webpack_require__(57)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_ClipScrollTreeViewPage_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_31f0a1d9_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_ClipScrollTreeViewPage_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 57 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(58);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"3d63fa5b\", content, true, {});\n\n/***/ }),\n/* 58 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 59 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Clip-Scroll Tree \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_c('div',[_c('ul',[_c('app-treeview',{attrs:{\"model\":_vm.clip_scroll_tree}})],1)])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 60 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_ScreenshotPage_vue__ = __webpack_require__(15);\n/* unused harmony namespace reexport */\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_29affa2a_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_ScreenshotPage_vue__ = __webpack_require__(63);\nfunction injectStyle (ssrContext) {\n __webpack_require__(61)\n}\nvar normalizeComponent = __webpack_require__(2)\n/* script */\n\n\n/* template */\n\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __WEBPACK_IMPORTED_MODULE_0__babel_loader_node_modules_vue_loader_lib_selector_type_script_index_0_ScreenshotPage_vue__[\"a\" /* default */],\n __WEBPACK_IMPORTED_MODULE_1__node_modules_vue_loader_lib_template_compiler_index_id_data_v_29affa2a_hasScoped_false_buble_transforms_node_modules_vue_loader_lib_selector_type_template_index_0_ScreenshotPage_vue__[\"a\" /* default */],\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (Component.exports);\n\n\n/***/ }),\n/* 61 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = __webpack_require__(62);\nif(typeof content === 'string') content = [[module.i, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = __webpack_require__(1)(\"3900c1de\", content, true, {});\n\n/***/ }),\n/* 62 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports = module.exports = __webpack_require__(0)(false);\n// imports\n\n\n// module\nexports.push([module.i, \"\", \"\"]);\n\n// exports\n\n\n/***/ }),\n/* 63 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Screenshot \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_c('div',[_c('ul',[(_vm.screenshot.length > 0)?_c('img',{staticStyle:{\"transform\":\"scaleY(-1)\",\"width\":\"1024px\",\"height\":\"768px\"},attrs:{\"src\":'data:image/png;base64,' + _vm.screenshot}}):_vm._e()])])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 64 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nvar render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('app-navbar'),_vm._v(\" \"),_c('div',{staticClass:\"section\"},[_c('div',{staticClass:\"container\"},[_c('div',{staticClass:\"columns\"},[_c('div',{staticClass:\"column is-3\"},[_c('app-navmenu')],1),_vm._v(\" \"),_c('div',{staticClass:\"column\"},[(_vm.page == 'options')?_c('app-options'):_vm._e(),_vm._v(\" \"),(_vm.page == 'passes')?_c('app-passview'):_vm._e(),_vm._v(\" \"),(_vm.page == 'render_tasks')?_c('app-rendertaskview'):_vm._e(),_vm._v(\" \"),(_vm.page == 'documents')?_c('app-documentview'):_vm._e(),_vm._v(\" \"),(_vm.page == 'clip_scroll_tree')?_c('app-clipscrolltreeview'):_vm._e(),_vm._v(\" \"),(_vm.page == 'screenshot')?_c('app-screenshotview'):_vm._e()],1)])])])],1)}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\n/* harmony default export */ __webpack_exports__[\"a\"] = (esExports);\n\n/***/ }),\n/* 65 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_vue__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_vuex__ = __webpack_require__(66);\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n\n\n\n__WEBPACK_IMPORTED_MODULE_0_vue__[\"default\"].use(__WEBPACK_IMPORTED_MODULE_1_vuex__[\"a\" /* default */]);\n\nvar Connection = function () {\n function Connection() {\n _classCallCheck(this, Connection);\n\n this.ws = null;\n }\n\n _createClass(Connection, [{\n key: 'connect',\n value: function connect(context) {\n var ws = new WebSocket(\"ws://127.0.0.1:3583\");\n\n ws.onopen = function () {\n context.commit('setConnected', true);\n };\n\n ws.onmessage = function (evt) {\n var json = JSON.parse(evt.data);\n if (json['kind'] == \"passes\") {\n context.commit('setPasses', json['passes']);\n } else if (json['kind'] == \"render_tasks\") {\n context.commit('setRenderTasks', json['root']);\n } else if (json['kind'] == \"documents\") {\n context.commit('setDocuments', json['root']);\n } else if (json['kind'] == \"clip_scroll_tree\") {\n context.commit('setClipScrollTree', json['root']);\n } else if (json['kind'] == \"screenshot\") {\n context.commit('setScreenshot', json['data']);\n } else {\n console.warn(\"unknown message kind: \" + json['kind']);\n }\n };\n\n ws.onclose = function () {\n context.commit('setConnected', false);\n };\n\n this.ws = ws;\n }\n }, {\n key: 'send',\n value: function send(msg) {\n if (this.ws !== null) {\n this.ws.send(msg);\n }\n }\n }, {\n key: 'disconnect',\n value: function disconnect() {\n if (this.ws !== null) {\n this.ws.close();\n this.ws = null;\n }\n }\n }]);\n\n return Connection;\n}();\n\nvar connection = new Connection();\n\nvar store = new __WEBPACK_IMPORTED_MODULE_1_vuex__[\"a\" /* default */].Store({\n strict: true,\n state: {\n connected: false,\n page: 'options',\n passes: [],\n render_tasks: [],\n documents: [],\n clip_scroll_tree: [],\n screenshot: []\n },\n mutations: {\n setConnected: function setConnected(state, connected) {\n state.connected = connected;\n },\n setPage: function setPage(state, name) {\n state.page = name;\n },\n setPasses: function setPasses(state, passes) {\n state.passes = passes;\n },\n setRenderTasks: function setRenderTasks(state, render_tasks) {\n state.render_tasks = render_tasks;\n },\n setDocuments: function setDocuments(state, documents) {\n state.documents = documents;\n },\n setClipScrollTree: function setClipScrollTree(state, clip_scroll_tree) {\n state.clip_scroll_tree = clip_scroll_tree;\n },\n setScreenshot: function setScreenshot(state, screenshot) {\n state.screenshot = screenshot;\n }\n },\n actions: {\n connect: function connect(context) {\n connection.connect(context);\n },\n disconnect: function disconnect(context) {\n connection.disconnect();\n },\n sendMessage: function sendMessage(context, msg) {\n connection.send(msg);\n }\n }\n});\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (store);\n\n/***/ }),\n/* 66 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* unused harmony export Store */\n/* unused harmony export install */\n/* unused harmony export mapState */\n/* unused harmony export mapMutations */\n/* unused harmony export mapGetters */\n/* unused harmony export mapActions */\n/* unused harmony export createNamespacedHelpers */\n/**\n * vuex v3.1.0\n * (c) 2019 Evan You\n * @license MIT\n */\nfunction applyMixin (Vue) {\n var version = Number(Vue.version.split('.')[0]);\n\n if (version >= 2) {\n Vue.mixin({ beforeCreate: vuexInit });\n } else {\n // override init and inject vuex init procedure\n // for 1.x backwards compatibility.\n var _init = Vue.prototype._init;\n Vue.prototype._init = function (options) {\n if ( options === void 0 ) options = {};\n\n options.init = options.init\n ? [vuexInit].concat(options.init)\n : vuexInit;\n _init.call(this, options);\n };\n }\n\n /**\n * Vuex init hook, injected into each instances init hooks list.\n */\n\n function vuexInit () {\n var options = this.$options;\n // store injection\n if (options.store) {\n this.$store = typeof options.store === 'function'\n ? options.store()\n : options.store;\n } else if (options.parent && options.parent.$store) {\n this.$store = options.parent.$store;\n }\n }\n}\n\nvar devtoolHook =\n typeof window !== 'undefined' &&\n window.__VUE_DEVTOOLS_GLOBAL_HOOK__;\n\nfunction devtoolPlugin (store) {\n if (!devtoolHook) { return }\n\n store._devtoolHook = devtoolHook;\n\n devtoolHook.emit('vuex:init', store);\n\n devtoolHook.on('vuex:travel-to-state', function (targetState) {\n store.replaceState(targetState);\n });\n\n store.subscribe(function (mutation, state) {\n devtoolHook.emit('vuex:mutation', mutation, state);\n });\n}\n\n/**\n * Get the first item that pass the test\n * by second argument function\n *\n * @param {Array} list\n * @param {Function} f\n * @return {*}\n */\n\n/**\n * forEach for object\n */\nfunction forEachValue (obj, fn) {\n Object.keys(obj).forEach(function (key) { return fn(obj[key], key); });\n}\n\nfunction isObject (obj) {\n return obj !== null && typeof obj === 'object'\n}\n\nfunction isPromise (val) {\n return val && typeof val.then === 'function'\n}\n\nfunction assert (condition, msg) {\n if (!condition) { throw new Error((\"[vuex] \" + msg)) }\n}\n\n// Base data struct for store's module, package with some attribute and method\nvar Module = function Module (rawModule, runtime) {\n this.runtime = runtime;\n // Store some children item\n this._children = Object.create(null);\n // Store the origin module object which passed by programmer\n this._rawModule = rawModule;\n var rawState = rawModule.state;\n\n // Store the origin module's state\n this.state = (typeof rawState === 'function' ? rawState() : rawState) || {};\n};\n\nvar prototypeAccessors = { namespaced: { configurable: true } };\n\nprototypeAccessors.namespaced.get = function () {\n return !!this._rawModule.namespaced\n};\n\nModule.prototype.addChild = function addChild (key, module) {\n this._children[key] = module;\n};\n\nModule.prototype.removeChild = function removeChild (key) {\n delete this._children[key];\n};\n\nModule.prototype.getChild = function getChild (key) {\n return this._children[key]\n};\n\nModule.prototype.update = function update (rawModule) {\n this._rawModule.namespaced = rawModule.namespaced;\n if (rawModule.actions) {\n this._rawModule.actions = rawModule.actions;\n }\n if (rawModule.mutations) {\n this._rawModule.mutations = rawModule.mutations;\n }\n if (rawModule.getters) {\n this._rawModule.getters = rawModule.getters;\n }\n};\n\nModule.prototype.forEachChild = function forEachChild (fn) {\n forEachValue(this._children, fn);\n};\n\nModule.prototype.forEachGetter = function forEachGetter (fn) {\n if (this._rawModule.getters) {\n forEachValue(this._rawModule.getters, fn);\n }\n};\n\nModule.prototype.forEachAction = function forEachAction (fn) {\n if (this._rawModule.actions) {\n forEachValue(this._rawModule.actions, fn);\n }\n};\n\nModule.prototype.forEachMutation = function forEachMutation (fn) {\n if (this._rawModule.mutations) {\n forEachValue(this._rawModule.mutations, fn);\n }\n};\n\nObject.defineProperties( Module.prototype, prototypeAccessors );\n\nvar ModuleCollection = function ModuleCollection (rawRootModule) {\n // register root module (Vuex.Store options)\n this.register([], rawRootModule, false);\n};\n\nModuleCollection.prototype.get = function get (path) {\n return path.reduce(function (module, key) {\n return module.getChild(key)\n }, this.root)\n};\n\nModuleCollection.prototype.getNamespace = function getNamespace (path) {\n var module = this.root;\n return path.reduce(function (namespace, key) {\n module = module.getChild(key);\n return namespace + (module.namespaced ? key + '/' : '')\n }, '')\n};\n\nModuleCollection.prototype.update = function update$1 (rawRootModule) {\n update([], this.root, rawRootModule);\n};\n\nModuleCollection.prototype.register = function register (path, rawModule, runtime) {\n var this$1 = this;\n if ( runtime === void 0 ) runtime = true;\n\n if (false) {\n assertRawModule(path, rawModule);\n }\n\n var newModule = new Module(rawModule, runtime);\n if (path.length === 0) {\n this.root = newModule;\n } else {\n var parent = this.get(path.slice(0, -1));\n parent.addChild(path[path.length - 1], newModule);\n }\n\n // register nested modules\n if (rawModule.modules) {\n forEachValue(rawModule.modules, function (rawChildModule, key) {\n this$1.register(path.concat(key), rawChildModule, runtime);\n });\n }\n};\n\nModuleCollection.prototype.unregister = function unregister (path) {\n var parent = this.get(path.slice(0, -1));\n var key = path[path.length - 1];\n if (!parent.getChild(key).runtime) { return }\n\n parent.removeChild(key);\n};\n\nfunction update (path, targetModule, newModule) {\n if (false) {\n assertRawModule(path, newModule);\n }\n\n // update target module\n targetModule.update(newModule);\n\n // update nested modules\n if (newModule.modules) {\n for (var key in newModule.modules) {\n if (!targetModule.getChild(key)) {\n if (false) {\n console.warn(\n \"[vuex] trying to add a new module '\" + key + \"' on hot reloading, \" +\n 'manual reload is needed'\n );\n }\n return\n }\n update(\n path.concat(key),\n targetModule.getChild(key),\n newModule.modules[key]\n );\n }\n }\n}\n\nvar functionAssert = {\n assert: function (value) { return typeof value === 'function'; },\n expected: 'function'\n};\n\nvar objectAssert = {\n assert: function (value) { return typeof value === 'function' ||\n (typeof value === 'object' && typeof value.handler === 'function'); },\n expected: 'function or object with \"handler\" function'\n};\n\nvar assertTypes = {\n getters: functionAssert,\n mutations: functionAssert,\n actions: objectAssert\n};\n\nfunction assertRawModule (path, rawModule) {\n Object.keys(assertTypes).forEach(function (key) {\n if (!rawModule[key]) { return }\n\n var assertOptions = assertTypes[key];\n\n forEachValue(rawModule[key], function (value, type) {\n assert(\n assertOptions.assert(value),\n makeAssertionMessage(path, key, type, value, assertOptions.expected)\n );\n });\n });\n}\n\nfunction makeAssertionMessage (path, key, type, value, expected) {\n var buf = key + \" should be \" + expected + \" but \\\"\" + key + \".\" + type + \"\\\"\";\n if (path.length > 0) {\n buf += \" in module \\\"\" + (path.join('.')) + \"\\\"\";\n }\n buf += \" is \" + (JSON.stringify(value)) + \".\";\n return buf\n}\n\nvar Vue; // bind on install\n\nvar Store = function Store (options) {\n var this$1 = this;\n if ( options === void 0 ) options = {};\n\n // Auto install if it is not done yet and `window` has `Vue`.\n // To allow users to avoid auto-installation in some cases,\n // this code should be placed here. See #731\n if (!Vue && typeof window !== 'undefined' && window.Vue) {\n install(window.Vue);\n }\n\n if (false) {\n assert(Vue, \"must call Vue.use(Vuex) before creating a store instance.\");\n assert(typeof Promise !== 'undefined', \"vuex requires a Promise polyfill in this browser.\");\n assert(this instanceof Store, \"store must be called with the new operator.\");\n }\n\n var plugins = options.plugins; if ( plugins === void 0 ) plugins = [];\n var strict = options.strict; if ( strict === void 0 ) strict = false;\n\n // store internal state\n this._committing = false;\n this._actions = Object.create(null);\n this._actionSubscribers = [];\n this._mutations = Object.create(null);\n this._wrappedGetters = Object.create(null);\n this._modules = new ModuleCollection(options);\n this._modulesNamespaceMap = Object.create(null);\n this._subscribers = [];\n this._watcherVM = new Vue();\n\n // bind commit and dispatch to self\n var store = this;\n var ref = this;\n var dispatch = ref.dispatch;\n var commit = ref.commit;\n this.dispatch = function boundDispatch (type, payload) {\n return dispatch.call(store, type, payload)\n };\n this.commit = function boundCommit (type, payload, options) {\n return commit.call(store, type, payload, options)\n };\n\n // strict mode\n this.strict = strict;\n\n var state = this._modules.root.state;\n\n // init root module.\n // this also recursively registers all sub-modules\n // and collects all module getters inside this._wrappedGetters\n installModule(this, state, [], this._modules.root);\n\n // initialize the store vm, which is responsible for the reactivity\n // (also registers _wrappedGetters as computed properties)\n resetStoreVM(this, state);\n\n // apply plugins\n plugins.forEach(function (plugin) { return plugin(this$1); });\n\n var useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools;\n if (useDevtools) {\n devtoolPlugin(this);\n }\n};\n\nvar prototypeAccessors$1 = { state: { configurable: true } };\n\nprototypeAccessors$1.state.get = function () {\n return this._vm._data.$$state\n};\n\nprototypeAccessors$1.state.set = function (v) {\n if (false) {\n assert(false, \"use store.replaceState() to explicit replace store state.\");\n }\n};\n\nStore.prototype.commit = function commit (_type, _payload, _options) {\n var this$1 = this;\n\n // check object-style commit\n var ref = unifyObjectStyle(_type, _payload, _options);\n var type = ref.type;\n var payload = ref.payload;\n var options = ref.options;\n\n var mutation = { type: type, payload: payload };\n var entry = this._mutations[type];\n if (!entry) {\n if (false) {\n console.error((\"[vuex] unknown mutation type: \" + type));\n }\n return\n }\n this._withCommit(function () {\n entry.forEach(function commitIterator (handler) {\n handler(payload);\n });\n });\n this._subscribers.forEach(function (sub) { return sub(mutation, this$1.state); });\n\n if (\n false\n ) {\n console.warn(\n \"[vuex] mutation type: \" + type + \". Silent option has been removed. \" +\n 'Use the filter functionality in the vue-devtools'\n );\n }\n};\n\nStore.prototype.dispatch = function dispatch (_type, _payload) {\n var this$1 = this;\n\n // check object-style dispatch\n var ref = unifyObjectStyle(_type, _payload);\n var type = ref.type;\n var payload = ref.payload;\n\n var action = { type: type, payload: payload };\n var entry = this._actions[type];\n if (!entry) {\n if (false) {\n console.error((\"[vuex] unknown action type: \" + type));\n }\n return\n }\n\n try {\n this._actionSubscribers\n .filter(function (sub) { return sub.before; })\n .forEach(function (sub) { return sub.before(action, this$1.state); });\n } catch (e) {\n if (false) {\n console.warn(\"[vuex] error in before action subscribers: \");\n console.error(e);\n }\n }\n\n var result = entry.length > 1\n ? Promise.all(entry.map(function (handler) { return handler(payload); }))\n : entry[0](payload);\n\n return result.then(function (res) {\n try {\n this$1._actionSubscribers\n .filter(function (sub) { return sub.after; })\n .forEach(function (sub) { return sub.after(action, this$1.state); });\n } catch (e) {\n if (false) {\n console.warn(\"[vuex] error in after action subscribers: \");\n console.error(e);\n }\n }\n return res\n })\n};\n\nStore.prototype.subscribe = function subscribe (fn) {\n return genericSubscribe(fn, this._subscribers)\n};\n\nStore.prototype.subscribeAction = function subscribeAction (fn) {\n var subs = typeof fn === 'function' ? { before: fn } : fn;\n return genericSubscribe(subs, this._actionSubscribers)\n};\n\nStore.prototype.watch = function watch (getter, cb, options) {\n var this$1 = this;\n\n if (false) {\n assert(typeof getter === 'function', \"store.watch only accepts a function.\");\n }\n return this._watcherVM.$watch(function () { return getter(this$1.state, this$1.getters); }, cb, options)\n};\n\nStore.prototype.replaceState = function replaceState (state) {\n var this$1 = this;\n\n this._withCommit(function () {\n this$1._vm._data.$$state = state;\n });\n};\n\nStore.prototype.registerModule = function registerModule (path, rawModule, options) {\n if ( options === void 0 ) options = {};\n\n if (typeof path === 'string') { path = [path]; }\n\n if (false) {\n assert(Array.isArray(path), \"module path must be a string or an Array.\");\n assert(path.length > 0, 'cannot register the root module by using registerModule.');\n }\n\n this._modules.register(path, rawModule);\n installModule(this, this.state, path, this._modules.get(path), options.preserveState);\n // reset store to update getters...\n resetStoreVM(this, this.state);\n};\n\nStore.prototype.unregisterModule = function unregisterModule (path) {\n var this$1 = this;\n\n if (typeof path === 'string') { path = [path]; }\n\n if (false) {\n assert(Array.isArray(path), \"module path must be a string or an Array.\");\n }\n\n this._modules.unregister(path);\n this._withCommit(function () {\n var parentState = getNestedState(this$1.state, path.slice(0, -1));\n Vue.delete(parentState, path[path.length - 1]);\n });\n resetStore(this);\n};\n\nStore.prototype.hotUpdate = function hotUpdate (newOptions) {\n this._modules.update(newOptions);\n resetStore(this, true);\n};\n\nStore.prototype._withCommit = function _withCommit (fn) {\n var committing = this._committing;\n this._committing = true;\n fn();\n this._committing = committing;\n};\n\nObject.defineProperties( Store.prototype, prototypeAccessors$1 );\n\nfunction genericSubscribe (fn, subs) {\n if (subs.indexOf(fn) < 0) {\n subs.push(fn);\n }\n return function () {\n var i = subs.indexOf(fn);\n if (i > -1) {\n subs.splice(i, 1);\n }\n }\n}\n\nfunction resetStore (store, hot) {\n store._actions = Object.create(null);\n store._mutations = Object.create(null);\n store._wrappedGetters = Object.create(null);\n store._modulesNamespaceMap = Object.create(null);\n var state = store.state;\n // init all modules\n installModule(store, state, [], store._modules.root, true);\n // reset vm\n resetStoreVM(store, state, hot);\n}\n\nfunction resetStoreVM (store, state, hot) {\n var oldVm = store._vm;\n\n // bind store public getters\n store.getters = {};\n var wrappedGetters = store._wrappedGetters;\n var computed = {};\n forEachValue(wrappedGetters, function (fn, key) {\n // use computed to leverage its lazy-caching mechanism\n computed[key] = function () { return fn(store); };\n Object.defineProperty(store.getters, key, {\n get: function () { return store._vm[key]; },\n enumerable: true // for local getters\n });\n });\n\n // use a Vue instance to store the state tree\n // suppress warnings just in case the user has added\n // some funky global mixins\n var silent = Vue.config.silent;\n Vue.config.silent = true;\n store._vm = new Vue({\n data: {\n $$state: state\n },\n computed: computed\n });\n Vue.config.silent = silent;\n\n // enable strict mode for new vm\n if (store.strict) {\n enableStrictMode(store);\n }\n\n if (oldVm) {\n if (hot) {\n // dispatch changes in all subscribed watchers\n // to force getter re-evaluation for hot reloading.\n store._withCommit(function () {\n oldVm._data.$$state = null;\n });\n }\n Vue.nextTick(function () { return oldVm.$destroy(); });\n }\n}\n\nfunction installModule (store, rootState, path, module, hot) {\n var isRoot = !path.length;\n var namespace = store._modules.getNamespace(path);\n\n // register in namespace map\n if (module.namespaced) {\n store._modulesNamespaceMap[namespace] = module;\n }\n\n // set state\n if (!isRoot && !hot) {\n var parentState = getNestedState(rootState, path.slice(0, -1));\n var moduleName = path[path.length - 1];\n store._withCommit(function () {\n Vue.set(parentState, moduleName, module.state);\n });\n }\n\n var local = module.context = makeLocalContext(store, namespace, path);\n\n module.forEachMutation(function (mutation, key) {\n var namespacedType = namespace + key;\n registerMutation(store, namespacedType, mutation, local);\n });\n\n module.forEachAction(function (action, key) {\n var type = action.root ? key : namespace + key;\n var handler = action.handler || action;\n registerAction(store, type, handler, local);\n });\n\n module.forEachGetter(function (getter, key) {\n var namespacedType = namespace + key;\n registerGetter(store, namespacedType, getter, local);\n });\n\n module.forEachChild(function (child, key) {\n installModule(store, rootState, path.concat(key), child, hot);\n });\n}\n\n/**\n * make localized dispatch, commit, getters and state\n * if there is no namespace, just use root ones\n */\nfunction makeLocalContext (store, namespace, path) {\n var noNamespace = namespace === '';\n\n var local = {\n dispatch: noNamespace ? store.dispatch : function (_type, _payload, _options) {\n var args = unifyObjectStyle(_type, _payload, _options);\n var payload = args.payload;\n var options = args.options;\n var type = args.type;\n\n if (!options || !options.root) {\n type = namespace + type;\n if (false) {\n console.error((\"[vuex] unknown local action type: \" + (args.type) + \", global type: \" + type));\n return\n }\n }\n\n return store.dispatch(type, payload)\n },\n\n commit: noNamespace ? store.commit : function (_type, _payload, _options) {\n var args = unifyObjectStyle(_type, _payload, _options);\n var payload = args.payload;\n var options = args.options;\n var type = args.type;\n\n if (!options || !options.root) {\n type = namespace + type;\n if (false) {\n console.error((\"[vuex] unknown local mutation type: \" + (args.type) + \", global type: \" + type));\n return\n }\n }\n\n store.commit(type, payload, options);\n }\n };\n\n // getters and state object must be gotten lazily\n // because they will be changed by vm update\n Object.defineProperties(local, {\n getters: {\n get: noNamespace\n ? function () { return store.getters; }\n : function () { return makeLocalGetters(store, namespace); }\n },\n state: {\n get: function () { return getNestedState(store.state, path); }\n }\n });\n\n return local\n}\n\nfunction makeLocalGetters (store, namespace) {\n var gettersProxy = {};\n\n var splitPos = namespace.length;\n Object.keys(store.getters).forEach(function (type) {\n // skip if the target getter is not match this namespace\n if (type.slice(0, splitPos) !== namespace) { return }\n\n // extract local getter type\n var localType = type.slice(splitPos);\n\n // Add a port to the getters proxy.\n // Define as getter property because\n // we do not want to evaluate the getters in this time.\n Object.defineProperty(gettersProxy, localType, {\n get: function () { return store.getters[type]; },\n enumerable: true\n });\n });\n\n return gettersProxy\n}\n\nfunction registerMutation (store, type, handler, local) {\n var entry = store._mutations[type] || (store._mutations[type] = []);\n entry.push(function wrappedMutationHandler (payload) {\n handler.call(store, local.state, payload);\n });\n}\n\nfunction registerAction (store, type, handler, local) {\n var entry = store._actions[type] || (store._actions[type] = []);\n entry.push(function wrappedActionHandler (payload, cb) {\n var res = handler.call(store, {\n dispatch: local.dispatch,\n commit: local.commit,\n getters: local.getters,\n state: local.state,\n rootGetters: store.getters,\n rootState: store.state\n }, payload, cb);\n if (!isPromise(res)) {\n res = Promise.resolve(res);\n }\n if (store._devtoolHook) {\n return res.catch(function (err) {\n store._devtoolHook.emit('vuex:error', err);\n throw err\n })\n } else {\n return res\n }\n });\n}\n\nfunction registerGetter (store, type, rawGetter, local) {\n if (store._wrappedGetters[type]) {\n if (false) {\n console.error((\"[vuex] duplicate getter key: \" + type));\n }\n return\n }\n store._wrappedGetters[type] = function wrappedGetter (store) {\n return rawGetter(\n local.state, // local state\n local.getters, // local getters\n store.state, // root state\n store.getters // root getters\n )\n };\n}\n\nfunction enableStrictMode (store) {\n store._vm.$watch(function () { return this._data.$$state }, function () {\n if (false) {\n assert(store._committing, \"do not mutate vuex store state outside mutation handlers.\");\n }\n }, { deep: true, sync: true });\n}\n\nfunction getNestedState (state, path) {\n return path.length\n ? path.reduce(function (state, key) { return state[key]; }, state)\n : state\n}\n\nfunction unifyObjectStyle (type, payload, options) {\n if (isObject(type) && type.type) {\n options = payload;\n payload = type;\n type = type.type;\n }\n\n if (false) {\n assert(typeof type === 'string', (\"expects string as the type, but found \" + (typeof type) + \".\"));\n }\n\n return { type: type, payload: payload, options: options }\n}\n\nfunction install (_Vue) {\n if (Vue && _Vue === Vue) {\n if (false) {\n console.error(\n '[vuex] already installed. Vue.use(Vuex) should be called only once.'\n );\n }\n return\n }\n Vue = _Vue;\n applyMixin(Vue);\n}\n\n/**\n * Reduce the code which written in Vue.js for getting the state.\n * @param {String} [namespace] - Module's namespace\n * @param {Object|Array} states # Object's item can be a function which accept state and getters for param, you can do something for state and getters in it.\n * @param {Object}\n */\nvar mapState = normalizeNamespace(function (namespace, states) {\n var res = {};\n normalizeMap(states).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedState () {\n var state = this.$store.state;\n var getters = this.$store.getters;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapState', namespace);\n if (!module) {\n return\n }\n state = module.context.state;\n getters = module.context.getters;\n }\n return typeof val === 'function'\n ? val.call(this, state, getters)\n : state[val]\n };\n // mark vuex getter for devtools\n res[key].vuex = true;\n });\n return res\n});\n\n/**\n * Reduce the code which written in Vue.js for committing the mutation\n * @param {String} [namespace] - Module's namespace\n * @param {Object|Array} mutations # Object's item can be a function which accept `commit` function as the first param, it can accept anthor params. You can commit mutation and do any other things in this function. specially, You need to pass anthor params from the mapped function.\n * @return {Object}\n */\nvar mapMutations = normalizeNamespace(function (namespace, mutations) {\n var res = {};\n normalizeMap(mutations).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedMutation () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n // Get the commit method from store\n var commit = this.$store.commit;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapMutations', namespace);\n if (!module) {\n return\n }\n commit = module.context.commit;\n }\n return typeof val === 'function'\n ? val.apply(this, [commit].concat(args))\n : commit.apply(this.$store, [val].concat(args))\n };\n });\n return res\n});\n\n/**\n * Reduce the code which written in Vue.js for getting the getters\n * @param {String} [namespace] - Module's namespace\n * @param {Object|Array} getters\n * @return {Object}\n */\nvar mapGetters = normalizeNamespace(function (namespace, getters) {\n var res = {};\n normalizeMap(getters).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n // The namespace has been mutated by normalizeNamespace\n val = namespace + val;\n res[key] = function mappedGetter () {\n if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {\n return\n }\n if (false) {\n console.error((\"[vuex] unknown getter: \" + val));\n return\n }\n return this.$store.getters[val]\n };\n // mark vuex getter for devtools\n res[key].vuex = true;\n });\n return res\n});\n\n/**\n * Reduce the code which written in Vue.js for dispatch the action\n * @param {String} [namespace] - Module's namespace\n * @param {Object|Array} actions # Object's item can be a function which accept `dispatch` function as the first param, it can accept anthor params. You can dispatch action and do any other things in this function. specially, You need to pass anthor params from the mapped function.\n * @return {Object}\n */\nvar mapActions = normalizeNamespace(function (namespace, actions) {\n var res = {};\n normalizeMap(actions).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedAction () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n // get dispatch function from store\n var dispatch = this.$store.dispatch;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapActions', namespace);\n if (!module) {\n return\n }\n dispatch = module.context.dispatch;\n }\n return typeof val === 'function'\n ? val.apply(this, [dispatch].concat(args))\n : dispatch.apply(this.$store, [val].concat(args))\n };\n });\n return res\n});\n\n/**\n * Rebinding namespace param for mapXXX function in special scoped, and return them by simple object\n * @param {String} namespace\n * @return {Object}\n */\nvar createNamespacedHelpers = function (namespace) { return ({\n mapState: mapState.bind(null, namespace),\n mapGetters: mapGetters.bind(null, namespace),\n mapMutations: mapMutations.bind(null, namespace),\n mapActions: mapActions.bind(null, namespace)\n}); };\n\n/**\n * Normalize the map\n * normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]\n * normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ]\n * @param {Array|Object} map\n * @return {Object}\n */\nfunction normalizeMap (map) {\n return Array.isArray(map)\n ? map.map(function (key) { return ({ key: key, val: key }); })\n : Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); })\n}\n\n/**\n * Return a function expect two param contains namespace and map. it will normalize the namespace and then the param's function will handle the new namespace and the map.\n * @param {Function} fn\n * @return {Function}\n */\nfunction normalizeNamespace (fn) {\n return function (namespace, map) {\n if (typeof namespace !== 'string') {\n map = namespace;\n namespace = '';\n } else if (namespace.charAt(namespace.length - 1) !== '/') {\n namespace += '/';\n }\n return fn(namespace, map)\n }\n}\n\n/**\n * Search a special module from store by namespace. if module not exist, print error message.\n * @param {Object} store\n * @param {String} helper\n * @param {String} namespace\n * @return {Object}\n */\nfunction getModuleByNamespace (store, helper, namespace) {\n var module = store._modulesNamespaceMap[namespace];\n if (false) {\n console.error((\"[vuex] module namespace not found in \" + helper + \"(): \" + namespace));\n }\n return module\n}\n\nvar index_esm = {\n Store: Store,\n install: install,\n version: '3.1.0',\n mapState: mapState,\n mapMutations: mapMutations,\n mapGetters: mapGetters,\n mapActions: mapActions,\n createNamespacedHelpers: createNamespacedHelpers\n};\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (index_esm);\n\n\n\n/***/ })\n/******/ ]);\n\n\n// WEBPACK FOOTER //\n// build.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"/dist/\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 16);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap cd2236c5c64656562cc5","/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\nmodule.exports = function(useSourceMap) {\n\tvar list = [];\n\n\t// return the list of modules as css string\n\tlist.toString = function toString() {\n\t\treturn this.map(function (item) {\n\t\t\tvar content = cssWithMappingToString(item, useSourceMap);\n\t\t\tif(item[2]) {\n\t\t\t\treturn \"@media \" + item[2] + \"{\" + content + \"}\";\n\t\t\t} else {\n\t\t\t\treturn content;\n\t\t\t}\n\t\t}).join(\"\");\n\t};\n\n\t// import a list of modules into the list\n\tlist.i = function(modules, mediaQuery) {\n\t\tif(typeof modules === \"string\")\n\t\t\tmodules = [[null, modules, \"\"]];\n\t\tvar alreadyImportedModules = {};\n\t\tfor(var i = 0; i < this.length; i++) {\n\t\t\tvar id = this[i][0];\n\t\t\tif(typeof id === \"number\")\n\t\t\t\talreadyImportedModules[id] = true;\n\t\t}\n\t\tfor(i = 0; i < modules.length; i++) {\n\t\t\tvar item = modules[i];\n\t\t\t// skip already imported module\n\t\t\t// this implementation is not 100% perfect for weird media query combinations\n\t\t\t// when a module is imported multiple times with different media queries.\n\t\t\t// I hope this will never occur (Hey this way we have smaller bundles)\n\t\t\tif(typeof item[0] !== \"number\" || !alreadyImportedModules[item[0]]) {\n\t\t\t\tif(mediaQuery && !item[2]) {\n\t\t\t\t\titem[2] = mediaQuery;\n\t\t\t\t} else if(mediaQuery) {\n\t\t\t\t\titem[2] = \"(\" + item[2] + \") and (\" + mediaQuery + \")\";\n\t\t\t\t}\n\t\t\t\tlist.push(item);\n\t\t\t}\n\t\t}\n\t};\n\treturn list;\n};\n\nfunction cssWithMappingToString(item, useSourceMap) {\n\tvar content = item[1] || '';\n\tvar cssMapping = item[3];\n\tif (!cssMapping) {\n\t\treturn content;\n\t}\n\n\tif (useSourceMap && typeof btoa === 'function') {\n\t\tvar sourceMapping = toComment(cssMapping);\n\t\tvar sourceURLs = cssMapping.sources.map(function (source) {\n\t\t\treturn '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'\n\t\t});\n\n\t\treturn [content].concat(sourceURLs).concat([sourceMapping]).join('\\n');\n\t}\n\n\treturn [content].join('\\n');\n}\n\n// Adapted from convert-source-map (MIT)\nfunction toComment(sourceMap) {\n\t// eslint-disable-next-line no-undef\n\tvar base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\n\tvar data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;\n\n\treturn '/*# ' + data + ' */';\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader/lib/css-base.js\n// module id = 0\n// module chunks = 0","/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n Modified by Evan You @yyx990803\n*/\n\nvar hasDocument = typeof document !== 'undefined'\n\nif (typeof DEBUG !== 'undefined' && DEBUG) {\n if (!hasDocument) {\n throw new Error(\n 'vue-style-loader cannot be used in a non-browser environment. ' +\n \"Use { target: 'node' } in your Webpack config to indicate a server-rendering environment.\"\n ) }\n}\n\nvar listToStyles = require('./listToStyles')\n\n/*\ntype StyleObject = {\n id: number;\n parts: Array<StyleObjectPart>\n}\n\ntype StyleObjectPart = {\n css: string;\n media: string;\n sourceMap: ?string\n}\n*/\n\nvar stylesInDom = {/*\n [id: number]: {\n id: number,\n refs: number,\n parts: Array<(obj?: StyleObjectPart) => void>\n }\n*/}\n\nvar head = hasDocument && (document.head || document.getElementsByTagName('head')[0])\nvar singletonElement = null\nvar singletonCounter = 0\nvar isProduction = false\nvar noop = function () {}\nvar options = null\nvar ssrIdKey = 'data-vue-ssr-id'\n\n// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>\n// tags it will allow on a page\nvar isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase())\n\nmodule.exports = function (parentId, list, _isProduction, _options) {\n isProduction = _isProduction\n\n options = _options || {}\n\n var styles = listToStyles(parentId, list)\n addStylesToDom(styles)\n\n return function update (newList) {\n var mayRemove = []\n for (var i = 0; i < styles.length; i++) {\n var item = styles[i]\n var domStyle = stylesInDom[item.id]\n domStyle.refs--\n mayRemove.push(domStyle)\n }\n if (newList) {\n styles = listToStyles(parentId, newList)\n addStylesToDom(styles)\n } else {\n styles = []\n }\n for (var i = 0; i < mayRemove.length; i++) {\n var domStyle = mayRemove[i]\n if (domStyle.refs === 0) {\n for (var j = 0; j < domStyle.parts.length; j++) {\n domStyle.parts[j]()\n }\n delete stylesInDom[domStyle.id]\n }\n }\n }\n}\n\nfunction addStylesToDom (styles /* Array<StyleObject> */) {\n for (var i = 0; i < styles.length; i++) {\n var item = styles[i]\n var domStyle = stylesInDom[item.id]\n if (domStyle) {\n domStyle.refs++\n for (var j = 0; j < domStyle.parts.length; j++) {\n domStyle.parts[j](item.parts[j])\n }\n for (; j < item.parts.length; j++) {\n domStyle.parts.push(addStyle(item.parts[j]))\n }\n if (domStyle.parts.length > item.parts.length) {\n domStyle.parts.length = item.parts.length\n }\n } else {\n var parts = []\n for (var j = 0; j < item.parts.length; j++) {\n parts.push(addStyle(item.parts[j]))\n }\n stylesInDom[item.id] = { id: item.id, refs: 1, parts: parts }\n }\n }\n}\n\nfunction createStyleElement () {\n var styleElement = document.createElement('style')\n styleElement.type = 'text/css'\n head.appendChild(styleElement)\n return styleElement\n}\n\nfunction addStyle (obj /* StyleObjectPart */) {\n var update, remove\n var styleElement = document.querySelector('style[' + ssrIdKey + '~=\"' + obj.id + '\"]')\n\n if (styleElement) {\n if (isProduction) {\n // has SSR styles and in production mode.\n // simply do nothing.\n return noop\n } else {\n // has SSR styles but in dev mode.\n // for some reason Chrome can't handle source map in server-rendered\n // style tags - source maps in <style> only works if the style tag is\n // created and inserted dynamically. So we remove the server rendered\n // styles and inject new ones.\n styleElement.parentNode.removeChild(styleElement)\n }\n }\n\n if (isOldIE) {\n // use singleton mode for IE9.\n var styleIndex = singletonCounter++\n styleElement = singletonElement || (singletonElement = createStyleElement())\n update = applyToSingletonTag.bind(null, styleElement, styleIndex, false)\n remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true)\n } else {\n // use multi-style-tag mode in all other cases\n styleElement = createStyleElement()\n update = applyToTag.bind(null, styleElement)\n remove = function () {\n styleElement.parentNode.removeChild(styleElement)\n }\n }\n\n update(obj)\n\n return function updateStyle (newObj /* StyleObjectPart */) {\n if (newObj) {\n if (newObj.css === obj.css &&\n newObj.media === obj.media &&\n newObj.sourceMap === obj.sourceMap) {\n return\n }\n update(obj = newObj)\n } else {\n remove()\n }\n }\n}\n\nvar replaceText = (function () {\n var textStore = []\n\n return function (index, replacement) {\n textStore[index] = replacement\n return textStore.filter(Boolean).join('\\n')\n }\n})()\n\nfunction applyToSingletonTag (styleElement, index, remove, obj) {\n var css = remove ? '' : obj.css\n\n if (styleElement.styleSheet) {\n styleElement.styleSheet.cssText = replaceText(index, css)\n } else {\n var cssNode = document.createTextNode(css)\n var childNodes = styleElement.childNodes\n if (childNodes[index]) styleElement.removeChild(childNodes[index])\n if (childNodes.length) {\n styleElement.insertBefore(cssNode, childNodes[index])\n } else {\n styleElement.appendChild(cssNode)\n }\n }\n}\n\nfunction applyToTag (styleElement, obj) {\n var css = obj.css\n var media = obj.media\n var sourceMap = obj.sourceMap\n\n if (media) {\n styleElement.setAttribute('media', media)\n }\n if (options.ssrId) {\n styleElement.setAttribute(ssrIdKey, obj.id)\n }\n\n if (sourceMap) {\n // https://developer.chrome.com/devtools/docs/javascript-debugging\n // this makes source maps inside style tags work properly in Chrome\n css += '\\n/*# sourceURL=' + sourceMap.sources[0] + ' */'\n // http://stackoverflow.com/a/26603875\n css += '\\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + ' */'\n }\n\n if (styleElement.styleSheet) {\n styleElement.styleSheet.cssText = css\n } else {\n while (styleElement.firstChild) {\n styleElement.removeChild(styleElement.firstChild)\n }\n styleElement.appendChild(document.createTextNode(css))\n }\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader/lib/addStylesClient.js\n// module id = 1\n// module chunks = 0","/* globals __VUE_SSR_CONTEXT__ */\n\n// IMPORTANT: Do NOT use ES2015 features in this file.\n// This module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle.\n\nmodule.exports = function normalizeComponent (\n rawScriptExports,\n compiledTemplate,\n functionalTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier /* server only */\n) {\n var esModule\n var scriptExports = rawScriptExports = rawScriptExports || {}\n\n // ES6 modules interop\n var type = typeof rawScriptExports.default\n if (type === 'object' || type === 'function') {\n esModule = rawScriptExports\n scriptExports = rawScriptExports.default\n }\n\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (compiledTemplate) {\n options.render = compiledTemplate.render\n options.staticRenderFns = compiledTemplate.staticRenderFns\n options._compiled = true\n }\n\n // functional template\n if (functionalTemplate) {\n options.functional = true\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = injectStyles\n }\n\n if (hook) {\n var functional = options.functional\n var existing = functional\n ? options.render\n : options.beforeCreate\n\n if (!functional) {\n // inject component registration as beforeCreate hook\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n } else {\n // for template-only hot-reload because in that case the render fn doesn't\n // go through the normalizer\n options._injectStyles = hook\n // register for functioal component in vue file\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return existing(h, context)\n }\n }\n }\n\n return {\n esModule: esModule,\n exports: scriptExports,\n options: options\n }\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/component-normalizer.js\n// module id = 2\n// module chunks = 0","/*!\n * Vue.js v2.6.10\n * (c) 2014-2019 Evan You\n * Released under the MIT License.\n */\n/* */\n\nvar emptyObject = Object.freeze({});\n\n// These helpers produce better VM code in JS engines due to their\n// explicitness and function inlining.\nfunction isUndef (v) {\n return v === undefined || v === null\n}\n\nfunction isDef (v) {\n return v !== undefined && v !== null\n}\n\nfunction isTrue (v) {\n return v === true\n}\n\nfunction isFalse (v) {\n return v === false\n}\n\n/**\n * Check if value is primitive.\n */\nfunction isPrimitive (value) {\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n // $flow-disable-line\n typeof value === 'symbol' ||\n typeof value === 'boolean'\n )\n}\n\n/**\n * Quick object check - this is primarily used to tell\n * Objects from primitive values when we know the value\n * is a JSON-compliant type.\n */\nfunction isObject (obj) {\n return obj !== null && typeof obj === 'object'\n}\n\n/**\n * Get the raw type string of a value, e.g., [object Object].\n */\nvar _toString = Object.prototype.toString;\n\nfunction toRawType (value) {\n return _toString.call(value).slice(8, -1)\n}\n\n/**\n * Strict object type check. Only returns true\n * for plain JavaScript objects.\n */\nfunction isPlainObject (obj) {\n return _toString.call(obj) === '[object Object]'\n}\n\nfunction isRegExp (v) {\n return _toString.call(v) === '[object RegExp]'\n}\n\n/**\n * Check if val is a valid array index.\n */\nfunction isValidArrayIndex (val) {\n var n = parseFloat(String(val));\n return n >= 0 && Math.floor(n) === n && isFinite(val)\n}\n\nfunction isPromise (val) {\n return (\n isDef(val) &&\n typeof val.then === 'function' &&\n typeof val.catch === 'function'\n )\n}\n\n/**\n * Convert a value to a string that is actually rendered.\n */\nfunction toString (val) {\n return val == null\n ? ''\n : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)\n ? JSON.stringify(val, null, 2)\n : String(val)\n}\n\n/**\n * Convert an input value to a number for persistence.\n * If the conversion fails, return original string.\n */\nfunction toNumber (val) {\n var n = parseFloat(val);\n return isNaN(n) ? val : n\n}\n\n/**\n * Make a map and return a function for checking if a key\n * is in that map.\n */\nfunction makeMap (\n str,\n expectsLowerCase\n) {\n var map = Object.create(null);\n var list = str.split(',');\n for (var i = 0; i < list.length; i++) {\n map[list[i]] = true;\n }\n return expectsLowerCase\n ? function (val) { return map[val.toLowerCase()]; }\n : function (val) { return map[val]; }\n}\n\n/**\n * Check if a tag is a built-in tag.\n */\nvar isBuiltInTag = makeMap('slot,component', true);\n\n/**\n * Check if an attribute is a reserved attribute.\n */\nvar isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');\n\n/**\n * Remove an item from an array.\n */\nfunction remove (arr, item) {\n if (arr.length) {\n var index = arr.indexOf(item);\n if (index > -1) {\n return arr.splice(index, 1)\n }\n }\n}\n\n/**\n * Check whether an object has the property.\n */\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction hasOwn (obj, key) {\n return hasOwnProperty.call(obj, key)\n}\n\n/**\n * Create a cached version of a pure function.\n */\nfunction cached (fn) {\n var cache = Object.create(null);\n return (function cachedFn (str) {\n var hit = cache[str];\n return hit || (cache[str] = fn(str))\n })\n}\n\n/**\n * Camelize a hyphen-delimited string.\n */\nvar camelizeRE = /-(\\w)/g;\nvar camelize = cached(function (str) {\n return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })\n});\n\n/**\n * Capitalize a string.\n */\nvar capitalize = cached(function (str) {\n return str.charAt(0).toUpperCase() + str.slice(1)\n});\n\n/**\n * Hyphenate a camelCase string.\n */\nvar hyphenateRE = /\\B([A-Z])/g;\nvar hyphenate = cached(function (str) {\n return str.replace(hyphenateRE, '-$1').toLowerCase()\n});\n\n/**\n * Simple bind polyfill for environments that do not support it,\n * e.g., PhantomJS 1.x. Technically, we don't need this anymore\n * since native bind is now performant enough in most browsers.\n * But removing it would mean breaking code that was able to run in\n * PhantomJS 1.x, so this must be kept for backward compatibility.\n */\n\n/* istanbul ignore next */\nfunction polyfillBind (fn, ctx) {\n function boundFn (a) {\n var l = arguments.length;\n return l\n ? l > 1\n ? fn.apply(ctx, arguments)\n : fn.call(ctx, a)\n : fn.call(ctx)\n }\n\n boundFn._length = fn.length;\n return boundFn\n}\n\nfunction nativeBind (fn, ctx) {\n return fn.bind(ctx)\n}\n\nvar bind = Function.prototype.bind\n ? nativeBind\n : polyfillBind;\n\n/**\n * Convert an Array-like object to a real Array.\n */\nfunction toArray (list, start) {\n start = start || 0;\n var i = list.length - start;\n var ret = new Array(i);\n while (i--) {\n ret[i] = list[i + start];\n }\n return ret\n}\n\n/**\n * Mix properties into target object.\n */\nfunction extend (to, _from) {\n for (var key in _from) {\n to[key] = _from[key];\n }\n return to\n}\n\n/**\n * Merge an Array of Objects into a single Object.\n */\nfunction toObject (arr) {\n var res = {};\n for (var i = 0; i < arr.length; i++) {\n if (arr[i]) {\n extend(res, arr[i]);\n }\n }\n return res\n}\n\n/* eslint-disable no-unused-vars */\n\n/**\n * Perform no operation.\n * Stubbing args to make Flow happy without leaving useless transpiled code\n * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).\n */\nfunction noop (a, b, c) {}\n\n/**\n * Always return false.\n */\nvar no = function (a, b, c) { return false; };\n\n/* eslint-enable no-unused-vars */\n\n/**\n * Return the same value.\n */\nvar identity = function (_) { return _; };\n\n/**\n * Check if two values are loosely equal - that is,\n * if they are plain objects, do they have the same shape?\n */\nfunction looseEqual (a, b) {\n if (a === b) { return true }\n var isObjectA = isObject(a);\n var isObjectB = isObject(b);\n if (isObjectA && isObjectB) {\n try {\n var isArrayA = Array.isArray(a);\n var isArrayB = Array.isArray(b);\n if (isArrayA && isArrayB) {\n return a.length === b.length && a.every(function (e, i) {\n return looseEqual(e, b[i])\n })\n } else if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n } else if (!isArrayA && !isArrayB) {\n var keysA = Object.keys(a);\n var keysB = Object.keys(b);\n return keysA.length === keysB.length && keysA.every(function (key) {\n return looseEqual(a[key], b[key])\n })\n } else {\n /* istanbul ignore next */\n return false\n }\n } catch (e) {\n /* istanbul ignore next */\n return false\n }\n } else if (!isObjectA && !isObjectB) {\n return String(a) === String(b)\n } else {\n return false\n }\n}\n\n/**\n * Return the first index at which a loosely equal value can be\n * found in the array (if value is a plain object, the array must\n * contain an object of the same shape), or -1 if it is not present.\n */\nfunction looseIndexOf (arr, val) {\n for (var i = 0; i < arr.length; i++) {\n if (looseEqual(arr[i], val)) { return i }\n }\n return -1\n}\n\n/**\n * Ensure a function is called only once.\n */\nfunction once (fn) {\n var called = false;\n return function () {\n if (!called) {\n called = true;\n fn.apply(this, arguments);\n }\n }\n}\n\nvar SSR_ATTR = 'data-server-rendered';\n\nvar ASSET_TYPES = [\n 'component',\n 'directive',\n 'filter'\n];\n\nvar LIFECYCLE_HOOKS = [\n 'beforeCreate',\n 'created',\n 'beforeMount',\n 'mounted',\n 'beforeUpdate',\n 'updated',\n 'beforeDestroy',\n 'destroyed',\n 'activated',\n 'deactivated',\n 'errorCaptured',\n 'serverPrefetch'\n];\n\n/* */\n\n\n\nvar config = ({\n /**\n * Option merge strategies (used in core/util/options)\n */\n // $flow-disable-line\n optionMergeStrategies: Object.create(null),\n\n /**\n * Whether to suppress warnings.\n */\n silent: false,\n\n /**\n * Show production mode tip message on boot?\n */\n productionTip: process.env.NODE_ENV !== 'production',\n\n /**\n * Whether to enable devtools\n */\n devtools: process.env.NODE_ENV !== 'production',\n\n /**\n * Whether to record perf\n */\n performance: false,\n\n /**\n * Error handler for watcher errors\n */\n errorHandler: null,\n\n /**\n * Warn handler for watcher warns\n */\n warnHandler: null,\n\n /**\n * Ignore certain custom elements\n */\n ignoredElements: [],\n\n /**\n * Custom user key aliases for v-on\n */\n // $flow-disable-line\n keyCodes: Object.create(null),\n\n /**\n * Check if a tag is reserved so that it cannot be registered as a\n * component. This is platform-dependent and may be overwritten.\n */\n isReservedTag: no,\n\n /**\n * Check if an attribute is reserved so that it cannot be used as a component\n * prop. This is platform-dependent and may be overwritten.\n */\n isReservedAttr: no,\n\n /**\n * Check if a tag is an unknown element.\n * Platform-dependent.\n */\n isUnknownElement: no,\n\n /**\n * Get the namespace of an element\n */\n getTagNamespace: noop,\n\n /**\n * Parse the real tag name for the specific platform.\n */\n parsePlatformTagName: identity,\n\n /**\n * Check if an attribute must be bound using property, e.g. value\n * Platform-dependent.\n */\n mustUseProp: no,\n\n /**\n * Perform updates asynchronously. Intended to be used by Vue Test Utils\n * This will significantly reduce performance if set to false.\n */\n async: true,\n\n /**\n * Exposed for legacy reasons\n */\n _lifecycleHooks: LIFECYCLE_HOOKS\n});\n\n/* */\n\n/**\n * unicode letters used for parsing html tags, component names and property paths.\n * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname\n * skipping \\u10000-\\uEFFFF due to it freezing up PhantomJS\n */\nvar unicodeRegExp = /a-zA-Z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD/;\n\n/**\n * Check if a string starts with $ or _\n */\nfunction isReserved (str) {\n var c = (str + '').charCodeAt(0);\n return c === 0x24 || c === 0x5F\n}\n\n/**\n * Define a property.\n */\nfunction def (obj, key, val, enumerable) {\n Object.defineProperty(obj, key, {\n value: val,\n enumerable: !!enumerable,\n writable: true,\n configurable: true\n });\n}\n\n/**\n * Parse simple path.\n */\nvar bailRE = new RegExp((\"[^\" + (unicodeRegExp.source) + \".$_\\\\d]\"));\nfunction parsePath (path) {\n if (bailRE.test(path)) {\n return\n }\n var segments = path.split('.');\n return function (obj) {\n for (var i = 0; i < segments.length; i++) {\n if (!obj) { return }\n obj = obj[segments[i]];\n }\n return obj\n }\n}\n\n/* */\n\n// can we use __proto__?\nvar hasProto = '__proto__' in {};\n\n// Browser environment sniffing\nvar inBrowser = typeof window !== 'undefined';\nvar inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;\nvar weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();\nvar UA = inBrowser && window.navigator.userAgent.toLowerCase();\nvar isIE = UA && /msie|trident/.test(UA);\nvar isIE9 = UA && UA.indexOf('msie 9.0') > 0;\nvar isEdge = UA && UA.indexOf('edge/') > 0;\nvar isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android');\nvar isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios');\nvar isChrome = UA && /chrome\\/\\d+/.test(UA) && !isEdge;\nvar isPhantomJS = UA && /phantomjs/.test(UA);\nvar isFF = UA && UA.match(/firefox\\/(\\d+)/);\n\n// Firefox has a \"watch\" function on Object.prototype...\nvar nativeWatch = ({}).watch;\n\nvar supportsPassive = false;\nif (inBrowser) {\n try {\n var opts = {};\n Object.defineProperty(opts, 'passive', ({\n get: function get () {\n /* istanbul ignore next */\n supportsPassive = true;\n }\n })); // https://github.com/facebook/flow/issues/285\n window.addEventListener('test-passive', null, opts);\n } catch (e) {}\n}\n\n// this needs to be lazy-evaled because vue may be required before\n// vue-server-renderer can set VUE_ENV\nvar _isServer;\nvar isServerRendering = function () {\n if (_isServer === undefined) {\n /* istanbul ignore if */\n if (!inBrowser && !inWeex && typeof global !== 'undefined') {\n // detect presence of vue-server-renderer and avoid\n // Webpack shimming the process\n _isServer = global['process'] && global['process'].env.VUE_ENV === 'server';\n } else {\n _isServer = false;\n }\n }\n return _isServer\n};\n\n// detect devtools\nvar devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;\n\n/* istanbul ignore next */\nfunction isNative (Ctor) {\n return typeof Ctor === 'function' && /native code/.test(Ctor.toString())\n}\n\nvar hasSymbol =\n typeof Symbol !== 'undefined' && isNative(Symbol) &&\n typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys);\n\nvar _Set;\n/* istanbul ignore if */ // $flow-disable-line\nif (typeof Set !== 'undefined' && isNative(Set)) {\n // use native Set when available.\n _Set = Set;\n} else {\n // a non-standard Set polyfill that only works with primitive keys.\n _Set = /*@__PURE__*/(function () {\n function Set () {\n this.set = Object.create(null);\n }\n Set.prototype.has = function has (key) {\n return this.set[key] === true\n };\n Set.prototype.add = function add (key) {\n this.set[key] = true;\n };\n Set.prototype.clear = function clear () {\n this.set = Object.create(null);\n };\n\n return Set;\n }());\n}\n\n/* */\n\nvar warn = noop;\nvar tip = noop;\nvar generateComponentTrace = (noop); // work around flow check\nvar formatComponentName = (noop);\n\nif (process.env.NODE_ENV !== 'production') {\n var hasConsole = typeof console !== 'undefined';\n var classifyRE = /(?:^|[-_])(\\w)/g;\n var classify = function (str) { return str\n .replace(classifyRE, function (c) { return c.toUpperCase(); })\n .replace(/[-_]/g, ''); };\n\n warn = function (msg, vm) {\n var trace = vm ? generateComponentTrace(vm) : '';\n\n if (config.warnHandler) {\n config.warnHandler.call(null, msg, vm, trace);\n } else if (hasConsole && (!config.silent)) {\n console.error((\"[Vue warn]: \" + msg + trace));\n }\n };\n\n tip = function (msg, vm) {\n if (hasConsole && (!config.silent)) {\n console.warn(\"[Vue tip]: \" + msg + (\n vm ? generateComponentTrace(vm) : ''\n ));\n }\n };\n\n formatComponentName = function (vm, includeFile) {\n if (vm.$root === vm) {\n return '<Root>'\n }\n var options = typeof vm === 'function' && vm.cid != null\n ? vm.options\n : vm._isVue\n ? vm.$options || vm.constructor.options\n : vm;\n var name = options.name || options._componentTag;\n var file = options.__file;\n if (!name && file) {\n var match = file.match(/([^/\\\\]+)\\.vue$/);\n name = match && match[1];\n }\n\n return (\n (name ? (\"<\" + (classify(name)) + \">\") : \"<Anonymous>\") +\n (file && includeFile !== false ? (\" at \" + file) : '')\n )\n };\n\n var repeat = function (str, n) {\n var res = '';\n while (n) {\n if (n % 2 === 1) { res += str; }\n if (n > 1) { str += str; }\n n >>= 1;\n }\n return res\n };\n\n generateComponentTrace = function (vm) {\n if (vm._isVue && vm.$parent) {\n var tree = [];\n var currentRecursiveSequence = 0;\n while (vm) {\n if (tree.length > 0) {\n var last = tree[tree.length - 1];\n if (last.constructor === vm.constructor) {\n currentRecursiveSequence++;\n vm = vm.$parent;\n continue\n } else if (currentRecursiveSequence > 0) {\n tree[tree.length - 1] = [last, currentRecursiveSequence];\n currentRecursiveSequence = 0;\n }\n }\n tree.push(vm);\n vm = vm.$parent;\n }\n return '\\n\\nfound in\\n\\n' + tree\n .map(function (vm, i) { return (\"\" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm)\n ? ((formatComponentName(vm[0])) + \"... (\" + (vm[1]) + \" recursive calls)\")\n : formatComponentName(vm))); })\n .join('\\n')\n } else {\n return (\"\\n\\n(found in \" + (formatComponentName(vm)) + \")\")\n }\n };\n}\n\n/* */\n\nvar uid = 0;\n\n/**\n * A dep is an observable that can have multiple\n * directives subscribing to it.\n */\nvar Dep = function Dep () {\n this.id = uid++;\n this.subs = [];\n};\n\nDep.prototype.addSub = function addSub (sub) {\n this.subs.push(sub);\n};\n\nDep.prototype.removeSub = function removeSub (sub) {\n remove(this.subs, sub);\n};\n\nDep.prototype.depend = function depend () {\n if (Dep.target) {\n Dep.target.addDep(this);\n }\n};\n\nDep.prototype.notify = function notify () {\n // stabilize the subscriber list first\n var subs = this.subs.slice();\n if (process.env.NODE_ENV !== 'production' && !config.async) {\n // subs aren't sorted in scheduler if not running async\n // we need to sort them now to make sure they fire in correct\n // order\n subs.sort(function (a, b) { return a.id - b.id; });\n }\n for (var i = 0, l = subs.length; i < l; i++) {\n subs[i].update();\n }\n};\n\n// The current target watcher being evaluated.\n// This is globally unique because only one watcher\n// can be evaluated at a time.\nDep.target = null;\nvar targetStack = [];\n\nfunction pushTarget (target) {\n targetStack.push(target);\n Dep.target = target;\n}\n\nfunction popTarget () {\n targetStack.pop();\n Dep.target = targetStack[targetStack.length - 1];\n}\n\n/* */\n\nvar VNode = function VNode (\n tag,\n data,\n children,\n text,\n elm,\n context,\n componentOptions,\n asyncFactory\n) {\n this.tag = tag;\n this.data = data;\n this.children = children;\n this.text = text;\n this.elm = elm;\n this.ns = undefined;\n this.context = context;\n this.fnContext = undefined;\n this.fnOptions = undefined;\n this.fnScopeId = undefined;\n this.key = data && data.key;\n this.componentOptions = componentOptions;\n this.componentInstance = undefined;\n this.parent = undefined;\n this.raw = false;\n this.isStatic = false;\n this.isRootInsert = true;\n this.isComment = false;\n this.isCloned = false;\n this.isOnce = false;\n this.asyncFactory = asyncFactory;\n this.asyncMeta = undefined;\n this.isAsyncPlaceholder = false;\n};\n\nvar prototypeAccessors = { child: { configurable: true } };\n\n// DEPRECATED: alias for componentInstance for backwards compat.\n/* istanbul ignore next */\nprototypeAccessors.child.get = function () {\n return this.componentInstance\n};\n\nObject.defineProperties( VNode.prototype, prototypeAccessors );\n\nvar createEmptyVNode = function (text) {\n if ( text === void 0 ) text = '';\n\n var node = new VNode();\n node.text = text;\n node.isComment = true;\n return node\n};\n\nfunction createTextVNode (val) {\n return new VNode(undefined, undefined, undefined, String(val))\n}\n\n// optimized shallow clone\n// used for static nodes and slot nodes because they may be reused across\n// multiple renders, cloning them avoids errors when DOM manipulations rely\n// on their elm reference.\nfunction cloneVNode (vnode) {\n var cloned = new VNode(\n vnode.tag,\n vnode.data,\n // #7975\n // clone children array to avoid mutating original in case of cloning\n // a child.\n vnode.children && vnode.children.slice(),\n vnode.text,\n vnode.elm,\n vnode.context,\n vnode.componentOptions,\n vnode.asyncFactory\n );\n cloned.ns = vnode.ns;\n cloned.isStatic = vnode.isStatic;\n cloned.key = vnode.key;\n cloned.isComment = vnode.isComment;\n cloned.fnContext = vnode.fnContext;\n cloned.fnOptions = vnode.fnOptions;\n cloned.fnScopeId = vnode.fnScopeId;\n cloned.asyncMeta = vnode.asyncMeta;\n cloned.isCloned = true;\n return cloned\n}\n\n/*\n * not type checking this file because flow doesn't play well with\n * dynamically accessing methods on Array prototype\n */\n\nvar arrayProto = Array.prototype;\nvar arrayMethods = Object.create(arrayProto);\n\nvar methodsToPatch = [\n 'push',\n 'pop',\n 'shift',\n 'unshift',\n 'splice',\n 'sort',\n 'reverse'\n];\n\n/**\n * Intercept mutating methods and emit events\n */\nmethodsToPatch.forEach(function (method) {\n // cache original method\n var original = arrayProto[method];\n def(arrayMethods, method, function mutator () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n var result = original.apply(this, args);\n var ob = this.__ob__;\n var inserted;\n switch (method) {\n case 'push':\n case 'unshift':\n inserted = args;\n break\n case 'splice':\n inserted = args.slice(2);\n break\n }\n if (inserted) { ob.observeArray(inserted); }\n // notify change\n ob.dep.notify();\n return result\n });\n});\n\n/* */\n\nvar arrayKeys = Object.getOwnPropertyNames(arrayMethods);\n\n/**\n * In some cases we may want to disable observation inside a component's\n * update computation.\n */\nvar shouldObserve = true;\n\nfunction toggleObserving (value) {\n shouldObserve = value;\n}\n\n/**\n * Observer class that is attached to each observed\n * object. Once attached, the observer converts the target\n * object's property keys into getter/setters that\n * collect dependencies and dispatch updates.\n */\nvar Observer = function Observer (value) {\n this.value = value;\n this.dep = new Dep();\n this.vmCount = 0;\n def(value, '__ob__', this);\n if (Array.isArray(value)) {\n if (hasProto) {\n protoAugment(value, arrayMethods);\n } else {\n copyAugment(value, arrayMethods, arrayKeys);\n }\n this.observeArray(value);\n } else {\n this.walk(value);\n }\n};\n\n/**\n * Walk through all properties and convert them into\n * getter/setters. This method should only be called when\n * value type is Object.\n */\nObserver.prototype.walk = function walk (obj) {\n var keys = Object.keys(obj);\n for (var i = 0; i < keys.length; i++) {\n defineReactive$$1(obj, keys[i]);\n }\n};\n\n/**\n * Observe a list of Array items.\n */\nObserver.prototype.observeArray = function observeArray (items) {\n for (var i = 0, l = items.length; i < l; i++) {\n observe(items[i]);\n }\n};\n\n// helpers\n\n/**\n * Augment a target Object or Array by intercepting\n * the prototype chain using __proto__\n */\nfunction protoAugment (target, src) {\n /* eslint-disable no-proto */\n target.__proto__ = src;\n /* eslint-enable no-proto */\n}\n\n/**\n * Augment a target Object or Array by defining\n * hidden properties.\n */\n/* istanbul ignore next */\nfunction copyAugment (target, src, keys) {\n for (var i = 0, l = keys.length; i < l; i++) {\n var key = keys[i];\n def(target, key, src[key]);\n }\n}\n\n/**\n * Attempt to create an observer instance for a value,\n * returns the new observer if successfully observed,\n * or the existing observer if the value already has one.\n */\nfunction observe (value, asRootData) {\n if (!isObject(value) || value instanceof VNode) {\n return\n }\n var ob;\n if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {\n ob = value.__ob__;\n } else if (\n shouldObserve &&\n !isServerRendering() &&\n (Array.isArray(value) || isPlainObject(value)) &&\n Object.isExtensible(value) &&\n !value._isVue\n ) {\n ob = new Observer(value);\n }\n if (asRootData && ob) {\n ob.vmCount++;\n }\n return ob\n}\n\n/**\n * Define a reactive property on an Object.\n */\nfunction defineReactive$$1 (\n obj,\n key,\n val,\n customSetter,\n shallow\n) {\n var dep = new Dep();\n\n var property = Object.getOwnPropertyDescriptor(obj, key);\n if (property && property.configurable === false) {\n return\n }\n\n // cater for pre-defined getter/setters\n var getter = property && property.get;\n var setter = property && property.set;\n if ((!getter || setter) && arguments.length === 2) {\n val = obj[key];\n }\n\n var childOb = !shallow && observe(val);\n Object.defineProperty(obj, key, {\n enumerable: true,\n configurable: true,\n get: function reactiveGetter () {\n var value = getter ? getter.call(obj) : val;\n if (Dep.target) {\n dep.depend();\n if (childOb) {\n childOb.dep.depend();\n if (Array.isArray(value)) {\n dependArray(value);\n }\n }\n }\n return value\n },\n set: function reactiveSetter (newVal) {\n var value = getter ? getter.call(obj) : val;\n /* eslint-disable no-self-compare */\n if (newVal === value || (newVal !== newVal && value !== value)) {\n return\n }\n /* eslint-enable no-self-compare */\n if (process.env.NODE_ENV !== 'production' && customSetter) {\n customSetter();\n }\n // #7981: for accessor properties without setter\n if (getter && !setter) { return }\n if (setter) {\n setter.call(obj, newVal);\n } else {\n val = newVal;\n }\n childOb = !shallow && observe(newVal);\n dep.notify();\n }\n });\n}\n\n/**\n * Set a property on an object. Adds the new property and\n * triggers change notification if the property doesn't\n * already exist.\n */\nfunction set (target, key, val) {\n if (process.env.NODE_ENV !== 'production' &&\n (isUndef(target) || isPrimitive(target))\n ) {\n warn((\"Cannot set reactive property on undefined, null, or primitive value: \" + ((target))));\n }\n if (Array.isArray(target) && isValidArrayIndex(key)) {\n target.length = Math.max(target.length, key);\n target.splice(key, 1, val);\n return val\n }\n if (key in target && !(key in Object.prototype)) {\n target[key] = val;\n return val\n }\n var ob = (target).__ob__;\n if (target._isVue || (ob && ob.vmCount)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'Avoid adding reactive properties to a Vue instance or its root $data ' +\n 'at runtime - declare it upfront in the data option.'\n );\n return val\n }\n if (!ob) {\n target[key] = val;\n return val\n }\n defineReactive$$1(ob.value, key, val);\n ob.dep.notify();\n return val\n}\n\n/**\n * Delete a property and trigger change if necessary.\n */\nfunction del (target, key) {\n if (process.env.NODE_ENV !== 'production' &&\n (isUndef(target) || isPrimitive(target))\n ) {\n warn((\"Cannot delete reactive property on undefined, null, or primitive value: \" + ((target))));\n }\n if (Array.isArray(target) && isValidArrayIndex(key)) {\n target.splice(key, 1);\n return\n }\n var ob = (target).__ob__;\n if (target._isVue || (ob && ob.vmCount)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'Avoid deleting properties on a Vue instance or its root $data ' +\n '- just set it to null.'\n );\n return\n }\n if (!hasOwn(target, key)) {\n return\n }\n delete target[key];\n if (!ob) {\n return\n }\n ob.dep.notify();\n}\n\n/**\n * Collect dependencies on array elements when the array is touched, since\n * we cannot intercept array element access like property getters.\n */\nfunction dependArray (value) {\n for (var e = (void 0), i = 0, l = value.length; i < l; i++) {\n e = value[i];\n e && e.__ob__ && e.__ob__.dep.depend();\n if (Array.isArray(e)) {\n dependArray(e);\n }\n }\n}\n\n/* */\n\n/**\n * Option overwriting strategies are functions that handle\n * how to merge a parent option value and a child option\n * value into the final value.\n */\nvar strats = config.optionMergeStrategies;\n\n/**\n * Options with restrictions\n */\nif (process.env.NODE_ENV !== 'production') {\n strats.el = strats.propsData = function (parent, child, vm, key) {\n if (!vm) {\n warn(\n \"option \\\"\" + key + \"\\\" can only be used during instance \" +\n 'creation with the `new` keyword.'\n );\n }\n return defaultStrat(parent, child)\n };\n}\n\n/**\n * Helper that recursively merges two data objects together.\n */\nfunction mergeData (to, from) {\n if (!from) { return to }\n var key, toVal, fromVal;\n\n var keys = hasSymbol\n ? Reflect.ownKeys(from)\n : Object.keys(from);\n\n for (var i = 0; i < keys.length; i++) {\n key = keys[i];\n // in case the object is already observed...\n if (key === '__ob__') { continue }\n toVal = to[key];\n fromVal = from[key];\n if (!hasOwn(to, key)) {\n set(to, key, fromVal);\n } else if (\n toVal !== fromVal &&\n isPlainObject(toVal) &&\n isPlainObject(fromVal)\n ) {\n mergeData(toVal, fromVal);\n }\n }\n return to\n}\n\n/**\n * Data\n */\nfunction mergeDataOrFn (\n parentVal,\n childVal,\n vm\n) {\n if (!vm) {\n // in a Vue.extend merge, both should be functions\n if (!childVal) {\n return parentVal\n }\n if (!parentVal) {\n return childVal\n }\n // when parentVal & childVal are both present,\n // we need to return a function that returns the\n // merged result of both functions... no need to\n // check if parentVal is a function here because\n // it has to be a function to pass previous merges.\n return function mergedDataFn () {\n return mergeData(\n typeof childVal === 'function' ? childVal.call(this, this) : childVal,\n typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal\n )\n }\n } else {\n return function mergedInstanceDataFn () {\n // instance merge\n var instanceData = typeof childVal === 'function'\n ? childVal.call(vm, vm)\n : childVal;\n var defaultData = typeof parentVal === 'function'\n ? parentVal.call(vm, vm)\n : parentVal;\n if (instanceData) {\n return mergeData(instanceData, defaultData)\n } else {\n return defaultData\n }\n }\n }\n}\n\nstrats.data = function (\n parentVal,\n childVal,\n vm\n) {\n if (!vm) {\n if (childVal && typeof childVal !== 'function') {\n process.env.NODE_ENV !== 'production' && warn(\n 'The \"data\" option should be a function ' +\n 'that returns a per-instance value in component ' +\n 'definitions.',\n vm\n );\n\n return parentVal\n }\n return mergeDataOrFn(parentVal, childVal)\n }\n\n return mergeDataOrFn(parentVal, childVal, vm)\n};\n\n/**\n * Hooks and props are merged as arrays.\n */\nfunction mergeHook (\n parentVal,\n childVal\n) {\n var res = childVal\n ? parentVal\n ? parentVal.concat(childVal)\n : Array.isArray(childVal)\n ? childVal\n : [childVal]\n : parentVal;\n return res\n ? dedupeHooks(res)\n : res\n}\n\nfunction dedupeHooks (hooks) {\n var res = [];\n for (var i = 0; i < hooks.length; i++) {\n if (res.indexOf(hooks[i]) === -1) {\n res.push(hooks[i]);\n }\n }\n return res\n}\n\nLIFECYCLE_HOOKS.forEach(function (hook) {\n strats[hook] = mergeHook;\n});\n\n/**\n * Assets\n *\n * When a vm is present (instance creation), we need to do\n * a three-way merge between constructor options, instance\n * options and parent options.\n */\nfunction mergeAssets (\n parentVal,\n childVal,\n vm,\n key\n) {\n var res = Object.create(parentVal || null);\n if (childVal) {\n process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm);\n return extend(res, childVal)\n } else {\n return res\n }\n}\n\nASSET_TYPES.forEach(function (type) {\n strats[type + 's'] = mergeAssets;\n});\n\n/**\n * Watchers.\n *\n * Watchers hashes should not overwrite one\n * another, so we merge them as arrays.\n */\nstrats.watch = function (\n parentVal,\n childVal,\n vm,\n key\n) {\n // work around Firefox's Object.prototype.watch...\n if (parentVal === nativeWatch) { parentVal = undefined; }\n if (childVal === nativeWatch) { childVal = undefined; }\n /* istanbul ignore if */\n if (!childVal) { return Object.create(parentVal || null) }\n if (process.env.NODE_ENV !== 'production') {\n assertObjectType(key, childVal, vm);\n }\n if (!parentVal) { return childVal }\n var ret = {};\n extend(ret, parentVal);\n for (var key$1 in childVal) {\n var parent = ret[key$1];\n var child = childVal[key$1];\n if (parent && !Array.isArray(parent)) {\n parent = [parent];\n }\n ret[key$1] = parent\n ? parent.concat(child)\n : Array.isArray(child) ? child : [child];\n }\n return ret\n};\n\n/**\n * Other object hashes.\n */\nstrats.props =\nstrats.methods =\nstrats.inject =\nstrats.computed = function (\n parentVal,\n childVal,\n vm,\n key\n) {\n if (childVal && process.env.NODE_ENV !== 'production') {\n assertObjectType(key, childVal, vm);\n }\n if (!parentVal) { return childVal }\n var ret = Object.create(null);\n extend(ret, parentVal);\n if (childVal) { extend(ret, childVal); }\n return ret\n};\nstrats.provide = mergeDataOrFn;\n\n/**\n * Default strategy.\n */\nvar defaultStrat = function (parentVal, childVal) {\n return childVal === undefined\n ? parentVal\n : childVal\n};\n\n/**\n * Validate component names\n */\nfunction checkComponents (options) {\n for (var key in options.components) {\n validateComponentName(key);\n }\n}\n\nfunction validateComponentName (name) {\n if (!new RegExp((\"^[a-zA-Z][\\\\-\\\\.0-9_\" + (unicodeRegExp.source) + \"]*$\")).test(name)) {\n warn(\n 'Invalid component name: \"' + name + '\". Component names ' +\n 'should conform to valid custom element name in html5 specification.'\n );\n }\n if (isBuiltInTag(name) || config.isReservedTag(name)) {\n warn(\n 'Do not use built-in or reserved HTML elements as component ' +\n 'id: ' + name\n );\n }\n}\n\n/**\n * Ensure all props option syntax are normalized into the\n * Object-based format.\n */\nfunction normalizeProps (options, vm) {\n var props = options.props;\n if (!props) { return }\n var res = {};\n var i, val, name;\n if (Array.isArray(props)) {\n i = props.length;\n while (i--) {\n val = props[i];\n if (typeof val === 'string') {\n name = camelize(val);\n res[name] = { type: null };\n } else if (process.env.NODE_ENV !== 'production') {\n warn('props must be strings when using array syntax.');\n }\n }\n } else if (isPlainObject(props)) {\n for (var key in props) {\n val = props[key];\n name = camelize(key);\n res[name] = isPlainObject(val)\n ? val\n : { type: val };\n }\n } else if (process.env.NODE_ENV !== 'production') {\n warn(\n \"Invalid value for option \\\"props\\\": expected an Array or an Object, \" +\n \"but got \" + (toRawType(props)) + \".\",\n vm\n );\n }\n options.props = res;\n}\n\n/**\n * Normalize all injections into Object-based format\n */\nfunction normalizeInject (options, vm) {\n var inject = options.inject;\n if (!inject) { return }\n var normalized = options.inject = {};\n if (Array.isArray(inject)) {\n for (var i = 0; i < inject.length; i++) {\n normalized[inject[i]] = { from: inject[i] };\n }\n } else if (isPlainObject(inject)) {\n for (var key in inject) {\n var val = inject[key];\n normalized[key] = isPlainObject(val)\n ? extend({ from: key }, val)\n : { from: val };\n }\n } else if (process.env.NODE_ENV !== 'production') {\n warn(\n \"Invalid value for option \\\"inject\\\": expected an Array or an Object, \" +\n \"but got \" + (toRawType(inject)) + \".\",\n vm\n );\n }\n}\n\n/**\n * Normalize raw function directives into object format.\n */\nfunction normalizeDirectives (options) {\n var dirs = options.directives;\n if (dirs) {\n for (var key in dirs) {\n var def$$1 = dirs[key];\n if (typeof def$$1 === 'function') {\n dirs[key] = { bind: def$$1, update: def$$1 };\n }\n }\n }\n}\n\nfunction assertObjectType (name, value, vm) {\n if (!isPlainObject(value)) {\n warn(\n \"Invalid value for option \\\"\" + name + \"\\\": expected an Object, \" +\n \"but got \" + (toRawType(value)) + \".\",\n vm\n );\n }\n}\n\n/**\n * Merge two option objects into a new one.\n * Core utility used in both instantiation and inheritance.\n */\nfunction mergeOptions (\n parent,\n child,\n vm\n) {\n if (process.env.NODE_ENV !== 'production') {\n checkComponents(child);\n }\n\n if (typeof child === 'function') {\n child = child.options;\n }\n\n normalizeProps(child, vm);\n normalizeInject(child, vm);\n normalizeDirectives(child);\n\n // Apply extends and mixins on the child options,\n // but only if it is a raw options object that isn't\n // the result of another mergeOptions call.\n // Only merged options has the _base property.\n if (!child._base) {\n if (child.extends) {\n parent = mergeOptions(parent, child.extends, vm);\n }\n if (child.mixins) {\n for (var i = 0, l = child.mixins.length; i < l; i++) {\n parent = mergeOptions(parent, child.mixins[i], vm);\n }\n }\n }\n\n var options = {};\n var key;\n for (key in parent) {\n mergeField(key);\n }\n for (key in child) {\n if (!hasOwn(parent, key)) {\n mergeField(key);\n }\n }\n function mergeField (key) {\n var strat = strats[key] || defaultStrat;\n options[key] = strat(parent[key], child[key], vm, key);\n }\n return options\n}\n\n/**\n * Resolve an asset.\n * This function is used because child instances need access\n * to assets defined in its ancestor chain.\n */\nfunction resolveAsset (\n options,\n type,\n id,\n warnMissing\n) {\n /* istanbul ignore if */\n if (typeof id !== 'string') {\n return\n }\n var assets = options[type];\n // check local registration variations first\n if (hasOwn(assets, id)) { return assets[id] }\n var camelizedId = camelize(id);\n if (hasOwn(assets, camelizedId)) { return assets[camelizedId] }\n var PascalCaseId = capitalize(camelizedId);\n if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] }\n // fallback to prototype chain\n var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];\n if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {\n warn(\n 'Failed to resolve ' + type.slice(0, -1) + ': ' + id,\n options\n );\n }\n return res\n}\n\n/* */\n\n\n\nfunction validateProp (\n key,\n propOptions,\n propsData,\n vm\n) {\n var prop = propOptions[key];\n var absent = !hasOwn(propsData, key);\n var value = propsData[key];\n // boolean casting\n var booleanIndex = getTypeIndex(Boolean, prop.type);\n if (booleanIndex > -1) {\n if (absent && !hasOwn(prop, 'default')) {\n value = false;\n } else if (value === '' || value === hyphenate(key)) {\n // only cast empty string / same name to boolean if\n // boolean has higher priority\n var stringIndex = getTypeIndex(String, prop.type);\n if (stringIndex < 0 || booleanIndex < stringIndex) {\n value = true;\n }\n }\n }\n // check default value\n if (value === undefined) {\n value = getPropDefaultValue(vm, prop, key);\n // since the default value is a fresh copy,\n // make sure to observe it.\n var prevShouldObserve = shouldObserve;\n toggleObserving(true);\n observe(value);\n toggleObserving(prevShouldObserve);\n }\n if (\n process.env.NODE_ENV !== 'production' &&\n // skip validation for weex recycle-list child component props\n !(false)\n ) {\n assertProp(prop, key, value, vm, absent);\n }\n return value\n}\n\n/**\n * Get the default value of a prop.\n */\nfunction getPropDefaultValue (vm, prop, key) {\n // no default, return undefined\n if (!hasOwn(prop, 'default')) {\n return undefined\n }\n var def = prop.default;\n // warn against non-factory defaults for Object & Array\n if (process.env.NODE_ENV !== 'production' && isObject(def)) {\n warn(\n 'Invalid default value for prop \"' + key + '\": ' +\n 'Props with type Object/Array must use a factory function ' +\n 'to return the default value.',\n vm\n );\n }\n // the raw prop value was also undefined from previous render,\n // return previous default value to avoid unnecessary watcher trigger\n if (vm && vm.$options.propsData &&\n vm.$options.propsData[key] === undefined &&\n vm._props[key] !== undefined\n ) {\n return vm._props[key]\n }\n // call factory function for non-Function types\n // a value is Function if its prototype is function even across different execution context\n return typeof def === 'function' && getType(prop.type) !== 'Function'\n ? def.call(vm)\n : def\n}\n\n/**\n * Assert whether a prop is valid.\n */\nfunction assertProp (\n prop,\n name,\n value,\n vm,\n absent\n) {\n if (prop.required && absent) {\n warn(\n 'Missing required prop: \"' + name + '\"',\n vm\n );\n return\n }\n if (value == null && !prop.required) {\n return\n }\n var type = prop.type;\n var valid = !type || type === true;\n var expectedTypes = [];\n if (type) {\n if (!Array.isArray(type)) {\n type = [type];\n }\n for (var i = 0; i < type.length && !valid; i++) {\n var assertedType = assertType(value, type[i]);\n expectedTypes.push(assertedType.expectedType || '');\n valid = assertedType.valid;\n }\n }\n\n if (!valid) {\n warn(\n getInvalidTypeMessage(name, value, expectedTypes),\n vm\n );\n return\n }\n var validator = prop.validator;\n if (validator) {\n if (!validator(value)) {\n warn(\n 'Invalid prop: custom validator check failed for prop \"' + name + '\".',\n vm\n );\n }\n }\n}\n\nvar simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/;\n\nfunction assertType (value, type) {\n var valid;\n var expectedType = getType(type);\n if (simpleCheckRE.test(expectedType)) {\n var t = typeof value;\n valid = t === expectedType.toLowerCase();\n // for primitive wrapper objects\n if (!valid && t === 'object') {\n valid = value instanceof type;\n }\n } else if (expectedType === 'Object') {\n valid = isPlainObject(value);\n } else if (expectedType === 'Array') {\n valid = Array.isArray(value);\n } else {\n valid = value instanceof type;\n }\n return {\n valid: valid,\n expectedType: expectedType\n }\n}\n\n/**\n * Use function string name to check built-in types,\n * because a simple equality check will fail when running\n * across different vms / iframes.\n */\nfunction getType (fn) {\n var match = fn && fn.toString().match(/^\\s*function (\\w+)/);\n return match ? match[1] : ''\n}\n\nfunction isSameType (a, b) {\n return getType(a) === getType(b)\n}\n\nfunction getTypeIndex (type, expectedTypes) {\n if (!Array.isArray(expectedTypes)) {\n return isSameType(expectedTypes, type) ? 0 : -1\n }\n for (var i = 0, len = expectedTypes.length; i < len; i++) {\n if (isSameType(expectedTypes[i], type)) {\n return i\n }\n }\n return -1\n}\n\nfunction getInvalidTypeMessage (name, value, expectedTypes) {\n var message = \"Invalid prop: type check failed for prop \\\"\" + name + \"\\\".\" +\n \" Expected \" + (expectedTypes.map(capitalize).join(', '));\n var expectedType = expectedTypes[0];\n var receivedType = toRawType(value);\n var expectedValue = styleValue(value, expectedType);\n var receivedValue = styleValue(value, receivedType);\n // check if we need to specify expected value\n if (expectedTypes.length === 1 &&\n isExplicable(expectedType) &&\n !isBoolean(expectedType, receivedType)) {\n message += \" with value \" + expectedValue;\n }\n message += \", got \" + receivedType + \" \";\n // check if we need to specify received value\n if (isExplicable(receivedType)) {\n message += \"with value \" + receivedValue + \".\";\n }\n return message\n}\n\nfunction styleValue (value, type) {\n if (type === 'String') {\n return (\"\\\"\" + value + \"\\\"\")\n } else if (type === 'Number') {\n return (\"\" + (Number(value)))\n } else {\n return (\"\" + value)\n }\n}\n\nfunction isExplicable (value) {\n var explicitTypes = ['string', 'number', 'boolean'];\n return explicitTypes.some(function (elem) { return value.toLowerCase() === elem; })\n}\n\nfunction isBoolean () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; })\n}\n\n/* */\n\nfunction handleError (err, vm, info) {\n // Deactivate deps tracking while processing error handler to avoid possible infinite rendering.\n // See: https://github.com/vuejs/vuex/issues/1505\n pushTarget();\n try {\n if (vm) {\n var cur = vm;\n while ((cur = cur.$parent)) {\n var hooks = cur.$options.errorCaptured;\n if (hooks) {\n for (var i = 0; i < hooks.length; i++) {\n try {\n var capture = hooks[i].call(cur, err, vm, info) === false;\n if (capture) { return }\n } catch (e) {\n globalHandleError(e, cur, 'errorCaptured hook');\n }\n }\n }\n }\n }\n globalHandleError(err, vm, info);\n } finally {\n popTarget();\n }\n}\n\nfunction invokeWithErrorHandling (\n handler,\n context,\n args,\n vm,\n info\n) {\n var res;\n try {\n res = args ? handler.apply(context, args) : handler.call(context);\n if (res && !res._isVue && isPromise(res) && !res._handled) {\n res.catch(function (e) { return handleError(e, vm, info + \" (Promise/async)\"); });\n // issue #9511\n // avoid catch triggering multiple times when nested calls\n res._handled = true;\n }\n } catch (e) {\n handleError(e, vm, info);\n }\n return res\n}\n\nfunction globalHandleError (err, vm, info) {\n if (config.errorHandler) {\n try {\n return config.errorHandler.call(null, err, vm, info)\n } catch (e) {\n // if the user intentionally throws the original error in the handler,\n // do not log it twice\n if (e !== err) {\n logError(e, null, 'config.errorHandler');\n }\n }\n }\n logError(err, vm, info);\n}\n\nfunction logError (err, vm, info) {\n if (process.env.NODE_ENV !== 'production') {\n warn((\"Error in \" + info + \": \\\"\" + (err.toString()) + \"\\\"\"), vm);\n }\n /* istanbul ignore else */\n if ((inBrowser || inWeex) && typeof console !== 'undefined') {\n console.error(err);\n } else {\n throw err\n }\n}\n\n/* */\n\nvar isUsingMicroTask = false;\n\nvar callbacks = [];\nvar pending = false;\n\nfunction flushCallbacks () {\n pending = false;\n var copies = callbacks.slice(0);\n callbacks.length = 0;\n for (var i = 0; i < copies.length; i++) {\n copies[i]();\n }\n}\n\n// Here we have async deferring wrappers using microtasks.\n// In 2.5 we used (macro) tasks (in combination with microtasks).\n// However, it has subtle problems when state is changed right before repaint\n// (e.g. #6813, out-in transitions).\n// Also, using (macro) tasks in event handler would cause some weird behaviors\n// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).\n// So we now use microtasks everywhere, again.\n// A major drawback of this tradeoff is that there are some scenarios\n// where microtasks have too high a priority and fire in between supposedly\n// sequential events (e.g. #4521, #6690, which have workarounds)\n// or even between bubbling of the same event (#6566).\nvar timerFunc;\n\n// The nextTick behavior leverages the microtask queue, which can be accessed\n// via either native Promise.then or MutationObserver.\n// MutationObserver has wider support, however it is seriously bugged in\n// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It\n// completely stops working after triggering a few times... so, if native\n// Promise is available, we will use it:\n/* istanbul ignore next, $flow-disable-line */\nif (typeof Promise !== 'undefined' && isNative(Promise)) {\n var p = Promise.resolve();\n timerFunc = function () {\n p.then(flushCallbacks);\n // In problematic UIWebViews, Promise.then doesn't completely break, but\n // it can get stuck in a weird state where callbacks are pushed into the\n // microtask queue but the queue isn't being flushed, until the browser\n // needs to do some other work, e.g. handle a timer. Therefore we can\n // \"force\" the microtask queue to be flushed by adding an empty timer.\n if (isIOS) { setTimeout(noop); }\n };\n isUsingMicroTask = true;\n} else if (!isIE && typeof MutationObserver !== 'undefined' && (\n isNative(MutationObserver) ||\n // PhantomJS and iOS 7.x\n MutationObserver.toString() === '[object MutationObserverConstructor]'\n)) {\n // Use MutationObserver where native Promise is not available,\n // e.g. PhantomJS, iOS7, Android 4.4\n // (#6466 MutationObserver is unreliable in IE11)\n var counter = 1;\n var observer = new MutationObserver(flushCallbacks);\n var textNode = document.createTextNode(String(counter));\n observer.observe(textNode, {\n characterData: true\n });\n timerFunc = function () {\n counter = (counter + 1) % 2;\n textNode.data = String(counter);\n };\n isUsingMicroTask = true;\n} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {\n // Fallback to setImmediate.\n // Techinically it leverages the (macro) task queue,\n // but it is still a better choice than setTimeout.\n timerFunc = function () {\n setImmediate(flushCallbacks);\n };\n} else {\n // Fallback to setTimeout.\n timerFunc = function () {\n setTimeout(flushCallbacks, 0);\n };\n}\n\nfunction nextTick (cb, ctx) {\n var _resolve;\n callbacks.push(function () {\n if (cb) {\n try {\n cb.call(ctx);\n } catch (e) {\n handleError(e, ctx, 'nextTick');\n }\n } else if (_resolve) {\n _resolve(ctx);\n }\n });\n if (!pending) {\n pending = true;\n timerFunc();\n }\n // $flow-disable-line\n if (!cb && typeof Promise !== 'undefined') {\n return new Promise(function (resolve) {\n _resolve = resolve;\n })\n }\n}\n\n/* */\n\n/* not type checking this file because flow doesn't play well with Proxy */\n\nvar initProxy;\n\nif (process.env.NODE_ENV !== 'production') {\n var allowedGlobals = makeMap(\n 'Infinity,undefined,NaN,isFinite,isNaN,' +\n 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +\n 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +\n 'require' // for Webpack/Browserify\n );\n\n var warnNonPresent = function (target, key) {\n warn(\n \"Property or method \\\"\" + key + \"\\\" is not defined on the instance but \" +\n 'referenced during render. Make sure that this property is reactive, ' +\n 'either in the data option, or for class-based components, by ' +\n 'initializing the property. ' +\n 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',\n target\n );\n };\n\n var warnReservedPrefix = function (target, key) {\n warn(\n \"Property \\\"\" + key + \"\\\" must be accessed with \\\"$data.\" + key + \"\\\" because \" +\n 'properties starting with \"$\" or \"_\" are not proxied in the Vue instance to ' +\n 'prevent conflicts with Vue internals' +\n 'See: https://vuejs.org/v2/api/#data',\n target\n );\n };\n\n var hasProxy =\n typeof Proxy !== 'undefined' && isNative(Proxy);\n\n if (hasProxy) {\n var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact');\n config.keyCodes = new Proxy(config.keyCodes, {\n set: function set (target, key, value) {\n if (isBuiltInModifier(key)) {\n warn((\"Avoid overwriting built-in modifier in config.keyCodes: .\" + key));\n return false\n } else {\n target[key] = value;\n return true\n }\n }\n });\n }\n\n var hasHandler = {\n has: function has (target, key) {\n var has = key in target;\n var isAllowed = allowedGlobals(key) ||\n (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data));\n if (!has && !isAllowed) {\n if (key in target.$data) { warnReservedPrefix(target, key); }\n else { warnNonPresent(target, key); }\n }\n return has || !isAllowed\n }\n };\n\n var getHandler = {\n get: function get (target, key) {\n if (typeof key === 'string' && !(key in target)) {\n if (key in target.$data) { warnReservedPrefix(target, key); }\n else { warnNonPresent(target, key); }\n }\n return target[key]\n }\n };\n\n initProxy = function initProxy (vm) {\n if (hasProxy) {\n // determine which proxy handler to use\n var options = vm.$options;\n var handlers = options.render && options.render._withStripped\n ? getHandler\n : hasHandler;\n vm._renderProxy = new Proxy(vm, handlers);\n } else {\n vm._renderProxy = vm;\n }\n };\n}\n\n/* */\n\nvar seenObjects = new _Set();\n\n/**\n * Recursively traverse an object to evoke all converted\n * getters, so that every nested property inside the object\n * is collected as a \"deep\" dependency.\n */\nfunction traverse (val) {\n _traverse(val, seenObjects);\n seenObjects.clear();\n}\n\nfunction _traverse (val, seen) {\n var i, keys;\n var isA = Array.isArray(val);\n if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {\n return\n }\n if (val.__ob__) {\n var depId = val.__ob__.dep.id;\n if (seen.has(depId)) {\n return\n }\n seen.add(depId);\n }\n if (isA) {\n i = val.length;\n while (i--) { _traverse(val[i], seen); }\n } else {\n keys = Object.keys(val);\n i = keys.length;\n while (i--) { _traverse(val[keys[i]], seen); }\n }\n}\n\nvar mark;\nvar measure;\n\nif (process.env.NODE_ENV !== 'production') {\n var perf = inBrowser && window.performance;\n /* istanbul ignore if */\n if (\n perf &&\n perf.mark &&\n perf.measure &&\n perf.clearMarks &&\n perf.clearMeasures\n ) {\n mark = function (tag) { return perf.mark(tag); };\n measure = function (name, startTag, endTag) {\n perf.measure(name, startTag, endTag);\n perf.clearMarks(startTag);\n perf.clearMarks(endTag);\n // perf.clearMeasures(name)\n };\n }\n}\n\n/* */\n\nvar normalizeEvent = cached(function (name) {\n var passive = name.charAt(0) === '&';\n name = passive ? name.slice(1) : name;\n var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first\n name = once$$1 ? name.slice(1) : name;\n var capture = name.charAt(0) === '!';\n name = capture ? name.slice(1) : name;\n return {\n name: name,\n once: once$$1,\n capture: capture,\n passive: passive\n }\n});\n\nfunction createFnInvoker (fns, vm) {\n function invoker () {\n var arguments$1 = arguments;\n\n var fns = invoker.fns;\n if (Array.isArray(fns)) {\n var cloned = fns.slice();\n for (var i = 0; i < cloned.length; i++) {\n invokeWithErrorHandling(cloned[i], null, arguments$1, vm, \"v-on handler\");\n }\n } else {\n // return handler return value for single handlers\n return invokeWithErrorHandling(fns, null, arguments, vm, \"v-on handler\")\n }\n }\n invoker.fns = fns;\n return invoker\n}\n\nfunction updateListeners (\n on,\n oldOn,\n add,\n remove$$1,\n createOnceHandler,\n vm\n) {\n var name, def$$1, cur, old, event;\n for (name in on) {\n def$$1 = cur = on[name];\n old = oldOn[name];\n event = normalizeEvent(name);\n if (isUndef(cur)) {\n process.env.NODE_ENV !== 'production' && warn(\n \"Invalid handler for event \\\"\" + (event.name) + \"\\\": got \" + String(cur),\n vm\n );\n } else if (isUndef(old)) {\n if (isUndef(cur.fns)) {\n cur = on[name] = createFnInvoker(cur, vm);\n }\n if (isTrue(event.once)) {\n cur = on[name] = createOnceHandler(event.name, cur, event.capture);\n }\n add(event.name, cur, event.capture, event.passive, event.params);\n } else if (cur !== old) {\n old.fns = cur;\n on[name] = old;\n }\n }\n for (name in oldOn) {\n if (isUndef(on[name])) {\n event = normalizeEvent(name);\n remove$$1(event.name, oldOn[name], event.capture);\n }\n }\n}\n\n/* */\n\nfunction mergeVNodeHook (def, hookKey, hook) {\n if (def instanceof VNode) {\n def = def.data.hook || (def.data.hook = {});\n }\n var invoker;\n var oldHook = def[hookKey];\n\n function wrappedHook () {\n hook.apply(this, arguments);\n // important: remove merged hook to ensure it's called only once\n // and prevent memory leak\n remove(invoker.fns, wrappedHook);\n }\n\n if (isUndef(oldHook)) {\n // no existing hook\n invoker = createFnInvoker([wrappedHook]);\n } else {\n /* istanbul ignore if */\n if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {\n // already a merged invoker\n invoker = oldHook;\n invoker.fns.push(wrappedHook);\n } else {\n // existing plain hook\n invoker = createFnInvoker([oldHook, wrappedHook]);\n }\n }\n\n invoker.merged = true;\n def[hookKey] = invoker;\n}\n\n/* */\n\nfunction extractPropsFromVNodeData (\n data,\n Ctor,\n tag\n) {\n // we are only extracting raw values here.\n // validation and default values are handled in the child\n // component itself.\n var propOptions = Ctor.options.props;\n if (isUndef(propOptions)) {\n return\n }\n var res = {};\n var attrs = data.attrs;\n var props = data.props;\n if (isDef(attrs) || isDef(props)) {\n for (var key in propOptions) {\n var altKey = hyphenate(key);\n if (process.env.NODE_ENV !== 'production') {\n var keyInLowerCase = key.toLowerCase();\n if (\n key !== keyInLowerCase &&\n attrs && hasOwn(attrs, keyInLowerCase)\n ) {\n tip(\n \"Prop \\\"\" + keyInLowerCase + \"\\\" is passed to component \" +\n (formatComponentName(tag || Ctor)) + \", but the declared prop name is\" +\n \" \\\"\" + key + \"\\\". \" +\n \"Note that HTML attributes are case-insensitive and camelCased \" +\n \"props need to use their kebab-case equivalents when using in-DOM \" +\n \"templates. You should probably use \\\"\" + altKey + \"\\\" instead of \\\"\" + key + \"\\\".\"\n );\n }\n }\n checkProp(res, props, key, altKey, true) ||\n checkProp(res, attrs, key, altKey, false);\n }\n }\n return res\n}\n\nfunction checkProp (\n res,\n hash,\n key,\n altKey,\n preserve\n) {\n if (isDef(hash)) {\n if (hasOwn(hash, key)) {\n res[key] = hash[key];\n if (!preserve) {\n delete hash[key];\n }\n return true\n } else if (hasOwn(hash, altKey)) {\n res[key] = hash[altKey];\n if (!preserve) {\n delete hash[altKey];\n }\n return true\n }\n }\n return false\n}\n\n/* */\n\n// The template compiler attempts to minimize the need for normalization by\n// statically analyzing the template at compile time.\n//\n// For plain HTML markup, normalization can be completely skipped because the\n// generated render function is guaranteed to return Array<VNode>. There are\n// two cases where extra normalization is needed:\n\n// 1. When the children contains components - because a functional component\n// may return an Array instead of a single root. In this case, just a simple\n// normalization is needed - if any child is an Array, we flatten the whole\n// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep\n// because functional components already normalize their own children.\nfunction simpleNormalizeChildren (children) {\n for (var i = 0; i < children.length; i++) {\n if (Array.isArray(children[i])) {\n return Array.prototype.concat.apply([], children)\n }\n }\n return children\n}\n\n// 2. When the children contains constructs that always generated nested Arrays,\n// e.g. <template>, <slot>, v-for, or when the children is provided by user\n// with hand-written render functions / JSX. In such cases a full normalization\n// is needed to cater to all possible types of children values.\nfunction normalizeChildren (children) {\n return isPrimitive(children)\n ? [createTextVNode(children)]\n : Array.isArray(children)\n ? normalizeArrayChildren(children)\n : undefined\n}\n\nfunction isTextNode (node) {\n return isDef(node) && isDef(node.text) && isFalse(node.isComment)\n}\n\nfunction normalizeArrayChildren (children, nestedIndex) {\n var res = [];\n var i, c, lastIndex, last;\n for (i = 0; i < children.length; i++) {\n c = children[i];\n if (isUndef(c) || typeof c === 'boolean') { continue }\n lastIndex = res.length - 1;\n last = res[lastIndex];\n // nested\n if (Array.isArray(c)) {\n if (c.length > 0) {\n c = normalizeArrayChildren(c, ((nestedIndex || '') + \"_\" + i));\n // merge adjacent text nodes\n if (isTextNode(c[0]) && isTextNode(last)) {\n res[lastIndex] = createTextVNode(last.text + (c[0]).text);\n c.shift();\n }\n res.push.apply(res, c);\n }\n } else if (isPrimitive(c)) {\n if (isTextNode(last)) {\n // merge adjacent text nodes\n // this is necessary for SSR hydration because text nodes are\n // essentially merged when rendered to HTML strings\n res[lastIndex] = createTextVNode(last.text + c);\n } else if (c !== '') {\n // convert primitive to vnode\n res.push(createTextVNode(c));\n }\n } else {\n if (isTextNode(c) && isTextNode(last)) {\n // merge adjacent text nodes\n res[lastIndex] = createTextVNode(last.text + c.text);\n } else {\n // default key for nested array children (likely generated by v-for)\n if (isTrue(children._isVList) &&\n isDef(c.tag) &&\n isUndef(c.key) &&\n isDef(nestedIndex)) {\n c.key = \"__vlist\" + nestedIndex + \"_\" + i + \"__\";\n }\n res.push(c);\n }\n }\n }\n return res\n}\n\n/* */\n\nfunction initProvide (vm) {\n var provide = vm.$options.provide;\n if (provide) {\n vm._provided = typeof provide === 'function'\n ? provide.call(vm)\n : provide;\n }\n}\n\nfunction initInjections (vm) {\n var result = resolveInject(vm.$options.inject, vm);\n if (result) {\n toggleObserving(false);\n Object.keys(result).forEach(function (key) {\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n defineReactive$$1(vm, key, result[key], function () {\n warn(\n \"Avoid mutating an injected value directly since the changes will be \" +\n \"overwritten whenever the provided component re-renders. \" +\n \"injection being mutated: \\\"\" + key + \"\\\"\",\n vm\n );\n });\n } else {\n defineReactive$$1(vm, key, result[key]);\n }\n });\n toggleObserving(true);\n }\n}\n\nfunction resolveInject (inject, vm) {\n if (inject) {\n // inject is :any because flow is not smart enough to figure out cached\n var result = Object.create(null);\n var keys = hasSymbol\n ? Reflect.ownKeys(inject)\n : Object.keys(inject);\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n // #6574 in case the inject object is observed...\n if (key === '__ob__') { continue }\n var provideKey = inject[key].from;\n var source = vm;\n while (source) {\n if (source._provided && hasOwn(source._provided, provideKey)) {\n result[key] = source._provided[provideKey];\n break\n }\n source = source.$parent;\n }\n if (!source) {\n if ('default' in inject[key]) {\n var provideDefault = inject[key].default;\n result[key] = typeof provideDefault === 'function'\n ? provideDefault.call(vm)\n : provideDefault;\n } else if (process.env.NODE_ENV !== 'production') {\n warn((\"Injection \\\"\" + key + \"\\\" not found\"), vm);\n }\n }\n }\n return result\n }\n}\n\n/* */\n\n\n\n/**\n * Runtime helper for resolving raw children VNodes into a slot object.\n */\nfunction resolveSlots (\n children,\n context\n) {\n if (!children || !children.length) {\n return {}\n }\n var slots = {};\n for (var i = 0, l = children.length; i < l; i++) {\n var child = children[i];\n var data = child.data;\n // remove slot attribute if the node is resolved as a Vue slot node\n if (data && data.attrs && data.attrs.slot) {\n delete data.attrs.slot;\n }\n // named slots should only be respected if the vnode was rendered in the\n // same context.\n if ((child.context === context || child.fnContext === context) &&\n data && data.slot != null\n ) {\n var name = data.slot;\n var slot = (slots[name] || (slots[name] = []));\n if (child.tag === 'template') {\n slot.push.apply(slot, child.children || []);\n } else {\n slot.push(child);\n }\n } else {\n (slots.default || (slots.default = [])).push(child);\n }\n }\n // ignore slots that contains only whitespace\n for (var name$1 in slots) {\n if (slots[name$1].every(isWhitespace)) {\n delete slots[name$1];\n }\n }\n return slots\n}\n\nfunction isWhitespace (node) {\n return (node.isComment && !node.asyncFactory) || node.text === ' '\n}\n\n/* */\n\nfunction normalizeScopedSlots (\n slots,\n normalSlots,\n prevSlots\n) {\n var res;\n var hasNormalSlots = Object.keys(normalSlots).length > 0;\n var isStable = slots ? !!slots.$stable : !hasNormalSlots;\n var key = slots && slots.$key;\n if (!slots) {\n res = {};\n } else if (slots._normalized) {\n // fast path 1: child component re-render only, parent did not change\n return slots._normalized\n } else if (\n isStable &&\n prevSlots &&\n prevSlots !== emptyObject &&\n key === prevSlots.$key &&\n !hasNormalSlots &&\n !prevSlots.$hasNormal\n ) {\n // fast path 2: stable scoped slots w/ no normal slots to proxy,\n // only need to normalize once\n return prevSlots\n } else {\n res = {};\n for (var key$1 in slots) {\n if (slots[key$1] && key$1[0] !== '$') {\n res[key$1] = normalizeScopedSlot(normalSlots, key$1, slots[key$1]);\n }\n }\n }\n // expose normal slots on scopedSlots\n for (var key$2 in normalSlots) {\n if (!(key$2 in res)) {\n res[key$2] = proxyNormalSlot(normalSlots, key$2);\n }\n }\n // avoriaz seems to mock a non-extensible $scopedSlots object\n // and when that is passed down this would cause an error\n if (slots && Object.isExtensible(slots)) {\n (slots)._normalized = res;\n }\n def(res, '$stable', isStable);\n def(res, '$key', key);\n def(res, '$hasNormal', hasNormalSlots);\n return res\n}\n\nfunction normalizeScopedSlot(normalSlots, key, fn) {\n var normalized = function () {\n var res = arguments.length ? fn.apply(null, arguments) : fn({});\n res = res && typeof res === 'object' && !Array.isArray(res)\n ? [res] // single vnode\n : normalizeChildren(res);\n return res && (\n res.length === 0 ||\n (res.length === 1 && res[0].isComment) // #9658\n ) ? undefined\n : res\n };\n // this is a slot using the new v-slot syntax without scope. although it is\n // compiled as a scoped slot, render fn users would expect it to be present\n // on this.$slots because the usage is semantically a normal slot.\n if (fn.proxy) {\n Object.defineProperty(normalSlots, key, {\n get: normalized,\n enumerable: true,\n configurable: true\n });\n }\n return normalized\n}\n\nfunction proxyNormalSlot(slots, key) {\n return function () { return slots[key]; }\n}\n\n/* */\n\n/**\n * Runtime helper for rendering v-for lists.\n */\nfunction renderList (\n val,\n render\n) {\n var ret, i, l, keys, key;\n if (Array.isArray(val) || typeof val === 'string') {\n ret = new Array(val.length);\n for (i = 0, l = val.length; i < l; i++) {\n ret[i] = render(val[i], i);\n }\n } else if (typeof val === 'number') {\n ret = new Array(val);\n for (i = 0; i < val; i++) {\n ret[i] = render(i + 1, i);\n }\n } else if (isObject(val)) {\n if (hasSymbol && val[Symbol.iterator]) {\n ret = [];\n var iterator = val[Symbol.iterator]();\n var result = iterator.next();\n while (!result.done) {\n ret.push(render(result.value, ret.length));\n result = iterator.next();\n }\n } else {\n keys = Object.keys(val);\n ret = new Array(keys.length);\n for (i = 0, l = keys.length; i < l; i++) {\n key = keys[i];\n ret[i] = render(val[key], key, i);\n }\n }\n }\n if (!isDef(ret)) {\n ret = [];\n }\n (ret)._isVList = true;\n return ret\n}\n\n/* */\n\n/**\n * Runtime helper for rendering <slot>\n */\nfunction renderSlot (\n name,\n fallback,\n props,\n bindObject\n) {\n var scopedSlotFn = this.$scopedSlots[name];\n var nodes;\n if (scopedSlotFn) { // scoped slot\n props = props || {};\n if (bindObject) {\n if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) {\n warn(\n 'slot v-bind without argument expects an Object',\n this\n );\n }\n props = extend(extend({}, bindObject), props);\n }\n nodes = scopedSlotFn(props) || fallback;\n } else {\n nodes = this.$slots[name] || fallback;\n }\n\n var target = props && props.slot;\n if (target) {\n return this.$createElement('template', { slot: target }, nodes)\n } else {\n return nodes\n }\n}\n\n/* */\n\n/**\n * Runtime helper for resolving filters\n */\nfunction resolveFilter (id) {\n return resolveAsset(this.$options, 'filters', id, true) || identity\n}\n\n/* */\n\nfunction isKeyNotMatch (expect, actual) {\n if (Array.isArray(expect)) {\n return expect.indexOf(actual) === -1\n } else {\n return expect !== actual\n }\n}\n\n/**\n * Runtime helper for checking keyCodes from config.\n * exposed as Vue.prototype._k\n * passing in eventKeyName as last argument separately for backwards compat\n */\nfunction checkKeyCodes (\n eventKeyCode,\n key,\n builtInKeyCode,\n eventKeyName,\n builtInKeyName\n) {\n var mappedKeyCode = config.keyCodes[key] || builtInKeyCode;\n if (builtInKeyName && eventKeyName && !config.keyCodes[key]) {\n return isKeyNotMatch(builtInKeyName, eventKeyName)\n } else if (mappedKeyCode) {\n return isKeyNotMatch(mappedKeyCode, eventKeyCode)\n } else if (eventKeyName) {\n return hyphenate(eventKeyName) !== key\n }\n}\n\n/* */\n\n/**\n * Runtime helper for merging v-bind=\"object\" into a VNode's data.\n */\nfunction bindObjectProps (\n data,\n tag,\n value,\n asProp,\n isSync\n) {\n if (value) {\n if (!isObject(value)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'v-bind without argument expects an Object or Array value',\n this\n );\n } else {\n if (Array.isArray(value)) {\n value = toObject(value);\n }\n var hash;\n var loop = function ( key ) {\n if (\n key === 'class' ||\n key === 'style' ||\n isReservedAttribute(key)\n ) {\n hash = data;\n } else {\n var type = data.attrs && data.attrs.type;\n hash = asProp || config.mustUseProp(tag, type, key)\n ? data.domProps || (data.domProps = {})\n : data.attrs || (data.attrs = {});\n }\n var camelizedKey = camelize(key);\n var hyphenatedKey = hyphenate(key);\n if (!(camelizedKey in hash) && !(hyphenatedKey in hash)) {\n hash[key] = value[key];\n\n if (isSync) {\n var on = data.on || (data.on = {});\n on[(\"update:\" + key)] = function ($event) {\n value[key] = $event;\n };\n }\n }\n };\n\n for (var key in value) loop( key );\n }\n }\n return data\n}\n\n/* */\n\n/**\n * Runtime helper for rendering static trees.\n */\nfunction renderStatic (\n index,\n isInFor\n) {\n var cached = this._staticTrees || (this._staticTrees = []);\n var tree = cached[index];\n // if has already-rendered static tree and not inside v-for,\n // we can reuse the same tree.\n if (tree && !isInFor) {\n return tree\n }\n // otherwise, render a fresh tree.\n tree = cached[index] = this.$options.staticRenderFns[index].call(\n this._renderProxy,\n null,\n this // for render fns generated for functional component templates\n );\n markStatic(tree, (\"__static__\" + index), false);\n return tree\n}\n\n/**\n * Runtime helper for v-once.\n * Effectively it means marking the node as static with a unique key.\n */\nfunction markOnce (\n tree,\n index,\n key\n) {\n markStatic(tree, (\"__once__\" + index + (key ? (\"_\" + key) : \"\")), true);\n return tree\n}\n\nfunction markStatic (\n tree,\n key,\n isOnce\n) {\n if (Array.isArray(tree)) {\n for (var i = 0; i < tree.length; i++) {\n if (tree[i] && typeof tree[i] !== 'string') {\n markStaticNode(tree[i], (key + \"_\" + i), isOnce);\n }\n }\n } else {\n markStaticNode(tree, key, isOnce);\n }\n}\n\nfunction markStaticNode (node, key, isOnce) {\n node.isStatic = true;\n node.key = key;\n node.isOnce = isOnce;\n}\n\n/* */\n\nfunction bindObjectListeners (data, value) {\n if (value) {\n if (!isPlainObject(value)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'v-on without argument expects an Object value',\n this\n );\n } else {\n var on = data.on = data.on ? extend({}, data.on) : {};\n for (var key in value) {\n var existing = on[key];\n var ours = value[key];\n on[key] = existing ? [].concat(existing, ours) : ours;\n }\n }\n }\n return data\n}\n\n/* */\n\nfunction resolveScopedSlots (\n fns, // see flow/vnode\n res,\n // the following are added in 2.6\n hasDynamicKeys,\n contentHashKey\n) {\n res = res || { $stable: !hasDynamicKeys };\n for (var i = 0; i < fns.length; i++) {\n var slot = fns[i];\n if (Array.isArray(slot)) {\n resolveScopedSlots(slot, res, hasDynamicKeys);\n } else if (slot) {\n // marker for reverse proxying v-slot without scope on this.$slots\n if (slot.proxy) {\n slot.fn.proxy = true;\n }\n res[slot.key] = slot.fn;\n }\n }\n if (contentHashKey) {\n (res).$key = contentHashKey;\n }\n return res\n}\n\n/* */\n\nfunction bindDynamicKeys (baseObj, values) {\n for (var i = 0; i < values.length; i += 2) {\n var key = values[i];\n if (typeof key === 'string' && key) {\n baseObj[values[i]] = values[i + 1];\n } else if (process.env.NODE_ENV !== 'production' && key !== '' && key !== null) {\n // null is a speical value for explicitly removing a binding\n warn(\n (\"Invalid value for dynamic directive argument (expected string or null): \" + key),\n this\n );\n }\n }\n return baseObj\n}\n\n// helper to dynamically append modifier runtime markers to event names.\n// ensure only append when value is already string, otherwise it will be cast\n// to string and cause the type check to miss.\nfunction prependModifier (value, symbol) {\n return typeof value === 'string' ? symbol + value : value\n}\n\n/* */\n\nfunction installRenderHelpers (target) {\n target._o = markOnce;\n target._n = toNumber;\n target._s = toString;\n target._l = renderList;\n target._t = renderSlot;\n target._q = looseEqual;\n target._i = looseIndexOf;\n target._m = renderStatic;\n target._f = resolveFilter;\n target._k = checkKeyCodes;\n target._b = bindObjectProps;\n target._v = createTextVNode;\n target._e = createEmptyVNode;\n target._u = resolveScopedSlots;\n target._g = bindObjectListeners;\n target._d = bindDynamicKeys;\n target._p = prependModifier;\n}\n\n/* */\n\nfunction FunctionalRenderContext (\n data,\n props,\n children,\n parent,\n Ctor\n) {\n var this$1 = this;\n\n var options = Ctor.options;\n // ensure the createElement function in functional components\n // gets a unique context - this is necessary for correct named slot check\n var contextVm;\n if (hasOwn(parent, '_uid')) {\n contextVm = Object.create(parent);\n // $flow-disable-line\n contextVm._original = parent;\n } else {\n // the context vm passed in is a functional context as well.\n // in this case we want to make sure we are able to get a hold to the\n // real context instance.\n contextVm = parent;\n // $flow-disable-line\n parent = parent._original;\n }\n var isCompiled = isTrue(options._compiled);\n var needNormalization = !isCompiled;\n\n this.data = data;\n this.props = props;\n this.children = children;\n this.parent = parent;\n this.listeners = data.on || emptyObject;\n this.injections = resolveInject(options.inject, parent);\n this.slots = function () {\n if (!this$1.$slots) {\n normalizeScopedSlots(\n data.scopedSlots,\n this$1.$slots = resolveSlots(children, parent)\n );\n }\n return this$1.$slots\n };\n\n Object.defineProperty(this, 'scopedSlots', ({\n enumerable: true,\n get: function get () {\n return normalizeScopedSlots(data.scopedSlots, this.slots())\n }\n }));\n\n // support for compiled functional template\n if (isCompiled) {\n // exposing $options for renderStatic()\n this.$options = options;\n // pre-resolve slots for renderSlot()\n this.$slots = this.slots();\n this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots);\n }\n\n if (options._scopeId) {\n this._c = function (a, b, c, d) {\n var vnode = createElement(contextVm, a, b, c, d, needNormalization);\n if (vnode && !Array.isArray(vnode)) {\n vnode.fnScopeId = options._scopeId;\n vnode.fnContext = parent;\n }\n return vnode\n };\n } else {\n this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); };\n }\n}\n\ninstallRenderHelpers(FunctionalRenderContext.prototype);\n\nfunction createFunctionalComponent (\n Ctor,\n propsData,\n data,\n contextVm,\n children\n) {\n var options = Ctor.options;\n var props = {};\n var propOptions = options.props;\n if (isDef(propOptions)) {\n for (var key in propOptions) {\n props[key] = validateProp(key, propOptions, propsData || emptyObject);\n }\n } else {\n if (isDef(data.attrs)) { mergeProps(props, data.attrs); }\n if (isDef(data.props)) { mergeProps(props, data.props); }\n }\n\n var renderContext = new FunctionalRenderContext(\n data,\n props,\n children,\n contextVm,\n Ctor\n );\n\n var vnode = options.render.call(null, renderContext._c, renderContext);\n\n if (vnode instanceof VNode) {\n return cloneAndMarkFunctionalResult(vnode, data, renderContext.parent, options, renderContext)\n } else if (Array.isArray(vnode)) {\n var vnodes = normalizeChildren(vnode) || [];\n var res = new Array(vnodes.length);\n for (var i = 0; i < vnodes.length; i++) {\n res[i] = cloneAndMarkFunctionalResult(vnodes[i], data, renderContext.parent, options, renderContext);\n }\n return res\n }\n}\n\nfunction cloneAndMarkFunctionalResult (vnode, data, contextVm, options, renderContext) {\n // #7817 clone node before setting fnContext, otherwise if the node is reused\n // (e.g. it was from a cached normal slot) the fnContext causes named slots\n // that should not be matched to match.\n var clone = cloneVNode(vnode);\n clone.fnContext = contextVm;\n clone.fnOptions = options;\n if (process.env.NODE_ENV !== 'production') {\n (clone.devtoolsMeta = clone.devtoolsMeta || {}).renderContext = renderContext;\n }\n if (data.slot) {\n (clone.data || (clone.data = {})).slot = data.slot;\n }\n return clone\n}\n\nfunction mergeProps (to, from) {\n for (var key in from) {\n to[camelize(key)] = from[key];\n }\n}\n\n/* */\n\n/* */\n\n/* */\n\n/* */\n\n// inline hooks to be invoked on component VNodes during patch\nvar componentVNodeHooks = {\n init: function init (vnode, hydrating) {\n if (\n vnode.componentInstance &&\n !vnode.componentInstance._isDestroyed &&\n vnode.data.keepAlive\n ) {\n // kept-alive components, treat as a patch\n var mountedNode = vnode; // work around flow\n componentVNodeHooks.prepatch(mountedNode, mountedNode);\n } else {\n var child = vnode.componentInstance = createComponentInstanceForVnode(\n vnode,\n activeInstance\n );\n child.$mount(hydrating ? vnode.elm : undefined, hydrating);\n }\n },\n\n prepatch: function prepatch (oldVnode, vnode) {\n var options = vnode.componentOptions;\n var child = vnode.componentInstance = oldVnode.componentInstance;\n updateChildComponent(\n child,\n options.propsData, // updated props\n options.listeners, // updated listeners\n vnode, // new parent vnode\n options.children // new children\n );\n },\n\n insert: function insert (vnode) {\n var context = vnode.context;\n var componentInstance = vnode.componentInstance;\n if (!componentInstance._isMounted) {\n componentInstance._isMounted = true;\n callHook(componentInstance, 'mounted');\n }\n if (vnode.data.keepAlive) {\n if (context._isMounted) {\n // vue-router#1212\n // During updates, a kept-alive component's child components may\n // change, so directly walking the tree here may call activated hooks\n // on incorrect children. Instead we push them into a queue which will\n // be processed after the whole patch process ended.\n queueActivatedComponent(componentInstance);\n } else {\n activateChildComponent(componentInstance, true /* direct */);\n }\n }\n },\n\n destroy: function destroy (vnode) {\n var componentInstance = vnode.componentInstance;\n if (!componentInstance._isDestroyed) {\n if (!vnode.data.keepAlive) {\n componentInstance.$destroy();\n } else {\n deactivateChildComponent(componentInstance, true /* direct */);\n }\n }\n }\n};\n\nvar hooksToMerge = Object.keys(componentVNodeHooks);\n\nfunction createComponent (\n Ctor,\n data,\n context,\n children,\n tag\n) {\n if (isUndef(Ctor)) {\n return\n }\n\n var baseCtor = context.$options._base;\n\n // plain options object: turn it into a constructor\n if (isObject(Ctor)) {\n Ctor = baseCtor.extend(Ctor);\n }\n\n // if at this stage it's not a constructor or an async component factory,\n // reject.\n if (typeof Ctor !== 'function') {\n if (process.env.NODE_ENV !== 'production') {\n warn((\"Invalid Component definition: \" + (String(Ctor))), context);\n }\n return\n }\n\n // async component\n var asyncFactory;\n if (isUndef(Ctor.cid)) {\n asyncFactory = Ctor;\n Ctor = resolveAsyncComponent(asyncFactory, baseCtor);\n if (Ctor === undefined) {\n // return a placeholder node for async component, which is rendered\n // as a comment node but preserves all the raw information for the node.\n // the information will be used for async server-rendering and hydration.\n return createAsyncPlaceholder(\n asyncFactory,\n data,\n context,\n children,\n tag\n )\n }\n }\n\n data = data || {};\n\n // resolve constructor options in case global mixins are applied after\n // component constructor creation\n resolveConstructorOptions(Ctor);\n\n // transform component v-model data into props & events\n if (isDef(data.model)) {\n transformModel(Ctor.options, data);\n }\n\n // extract props\n var propsData = extractPropsFromVNodeData(data, Ctor, tag);\n\n // functional component\n if (isTrue(Ctor.options.functional)) {\n return createFunctionalComponent(Ctor, propsData, data, context, children)\n }\n\n // extract listeners, since these needs to be treated as\n // child component listeners instead of DOM listeners\n var listeners = data.on;\n // replace with listeners with .native modifier\n // so it gets processed during parent component patch.\n data.on = data.nativeOn;\n\n if (isTrue(Ctor.options.abstract)) {\n // abstract components do not keep anything\n // other than props & listeners & slot\n\n // work around flow\n var slot = data.slot;\n data = {};\n if (slot) {\n data.slot = slot;\n }\n }\n\n // install component management hooks onto the placeholder node\n installComponentHooks(data);\n\n // return a placeholder vnode\n var name = Ctor.options.name || tag;\n var vnode = new VNode(\n (\"vue-component-\" + (Ctor.cid) + (name ? (\"-\" + name) : '')),\n data, undefined, undefined, undefined, context,\n { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children },\n asyncFactory\n );\n\n return vnode\n}\n\nfunction createComponentInstanceForVnode (\n vnode, // we know it's MountedComponentVNode but flow doesn't\n parent // activeInstance in lifecycle state\n) {\n var options = {\n _isComponent: true,\n _parentVnode: vnode,\n parent: parent\n };\n // check inline-template render functions\n var inlineTemplate = vnode.data.inlineTemplate;\n if (isDef(inlineTemplate)) {\n options.render = inlineTemplate.render;\n options.staticRenderFns = inlineTemplate.staticRenderFns;\n }\n return new vnode.componentOptions.Ctor(options)\n}\n\nfunction installComponentHooks (data) {\n var hooks = data.hook || (data.hook = {});\n for (var i = 0; i < hooksToMerge.length; i++) {\n var key = hooksToMerge[i];\n var existing = hooks[key];\n var toMerge = componentVNodeHooks[key];\n if (existing !== toMerge && !(existing && existing._merged)) {\n hooks[key] = existing ? mergeHook$1(toMerge, existing) : toMerge;\n }\n }\n}\n\nfunction mergeHook$1 (f1, f2) {\n var merged = function (a, b) {\n // flow complains about extra args which is why we use any\n f1(a, b);\n f2(a, b);\n };\n merged._merged = true;\n return merged\n}\n\n// transform component v-model info (value and callback) into\n// prop and event handler respectively.\nfunction transformModel (options, data) {\n var prop = (options.model && options.model.prop) || 'value';\n var event = (options.model && options.model.event) || 'input'\n ;(data.attrs || (data.attrs = {}))[prop] = data.model.value;\n var on = data.on || (data.on = {});\n var existing = on[event];\n var callback = data.model.callback;\n if (isDef(existing)) {\n if (\n Array.isArray(existing)\n ? existing.indexOf(callback) === -1\n : existing !== callback\n ) {\n on[event] = [callback].concat(existing);\n }\n } else {\n on[event] = callback;\n }\n}\n\n/* */\n\nvar SIMPLE_NORMALIZE = 1;\nvar ALWAYS_NORMALIZE = 2;\n\n// wrapper function for providing a more flexible interface\n// without getting yelled at by flow\nfunction createElement (\n context,\n tag,\n data,\n children,\n normalizationType,\n alwaysNormalize\n) {\n if (Array.isArray(data) || isPrimitive(data)) {\n normalizationType = children;\n children = data;\n data = undefined;\n }\n if (isTrue(alwaysNormalize)) {\n normalizationType = ALWAYS_NORMALIZE;\n }\n return _createElement(context, tag, data, children, normalizationType)\n}\n\nfunction _createElement (\n context,\n tag,\n data,\n children,\n normalizationType\n) {\n if (isDef(data) && isDef((data).__ob__)) {\n process.env.NODE_ENV !== 'production' && warn(\n \"Avoid using observed data object as vnode data: \" + (JSON.stringify(data)) + \"\\n\" +\n 'Always create fresh vnode data objects in each render!',\n context\n );\n return createEmptyVNode()\n }\n // object syntax in v-bind\n if (isDef(data) && isDef(data.is)) {\n tag = data.is;\n }\n if (!tag) {\n // in case of component :is set to falsy value\n return createEmptyVNode()\n }\n // warn against non-primitive key\n if (process.env.NODE_ENV !== 'production' &&\n isDef(data) && isDef(data.key) && !isPrimitive(data.key)\n ) {\n {\n warn(\n 'Avoid using non-primitive value as key, ' +\n 'use string/number value instead.',\n context\n );\n }\n }\n // support single function children as default scoped slot\n if (Array.isArray(children) &&\n typeof children[0] === 'function'\n ) {\n data = data || {};\n data.scopedSlots = { default: children[0] };\n children.length = 0;\n }\n if (normalizationType === ALWAYS_NORMALIZE) {\n children = normalizeChildren(children);\n } else if (normalizationType === SIMPLE_NORMALIZE) {\n children = simpleNormalizeChildren(children);\n }\n var vnode, ns;\n if (typeof tag === 'string') {\n var Ctor;\n ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag);\n if (config.isReservedTag(tag)) {\n // platform built-in elements\n vnode = new VNode(\n config.parsePlatformTagName(tag), data, children,\n undefined, undefined, context\n );\n } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {\n // component\n vnode = createComponent(Ctor, data, context, children, tag);\n } else {\n // unknown or unlisted namespaced elements\n // check at runtime because it may get assigned a namespace when its\n // parent normalizes children\n vnode = new VNode(\n tag, data, children,\n undefined, undefined, context\n );\n }\n } else {\n // direct component options / constructor\n vnode = createComponent(tag, data, context, children);\n }\n if (Array.isArray(vnode)) {\n return vnode\n } else if (isDef(vnode)) {\n if (isDef(ns)) { applyNS(vnode, ns); }\n if (isDef(data)) { registerDeepBindings(data); }\n return vnode\n } else {\n return createEmptyVNode()\n }\n}\n\nfunction applyNS (vnode, ns, force) {\n vnode.ns = ns;\n if (vnode.tag === 'foreignObject') {\n // use default namespace inside foreignObject\n ns = undefined;\n force = true;\n }\n if (isDef(vnode.children)) {\n for (var i = 0, l = vnode.children.length; i < l; i++) {\n var child = vnode.children[i];\n if (isDef(child.tag) && (\n isUndef(child.ns) || (isTrue(force) && child.tag !== 'svg'))) {\n applyNS(child, ns, force);\n }\n }\n }\n}\n\n// ref #5318\n// necessary to ensure parent re-render when deep bindings like :style and\n// :class are used on slot nodes\nfunction registerDeepBindings (data) {\n if (isObject(data.style)) {\n traverse(data.style);\n }\n if (isObject(data.class)) {\n traverse(data.class);\n }\n}\n\n/* */\n\nfunction initRender (vm) {\n vm._vnode = null; // the root of the child tree\n vm._staticTrees = null; // v-once cached trees\n var options = vm.$options;\n var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree\n var renderContext = parentVnode && parentVnode.context;\n vm.$slots = resolveSlots(options._renderChildren, renderContext);\n vm.$scopedSlots = emptyObject;\n // bind the createElement fn to this instance\n // so that we get proper render context inside it.\n // args order: tag, data, children, normalizationType, alwaysNormalize\n // internal version is used by render functions compiled from templates\n vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };\n // normalization is always applied for the public version, used in\n // user-written render functions.\n vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };\n\n // $attrs & $listeners are exposed for easier HOC creation.\n // they need to be reactive so that HOCs using them are always updated\n var parentData = parentVnode && parentVnode.data;\n\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n defineReactive$$1(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () {\n !isUpdatingChildComponent && warn(\"$attrs is readonly.\", vm);\n }, true);\n defineReactive$$1(vm, '$listeners', options._parentListeners || emptyObject, function () {\n !isUpdatingChildComponent && warn(\"$listeners is readonly.\", vm);\n }, true);\n } else {\n defineReactive$$1(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true);\n defineReactive$$1(vm, '$listeners', options._parentListeners || emptyObject, null, true);\n }\n}\n\nvar currentRenderingInstance = null;\n\nfunction renderMixin (Vue) {\n // install runtime convenience helpers\n installRenderHelpers(Vue.prototype);\n\n Vue.prototype.$nextTick = function (fn) {\n return nextTick(fn, this)\n };\n\n Vue.prototype._render = function () {\n var vm = this;\n var ref = vm.$options;\n var render = ref.render;\n var _parentVnode = ref._parentVnode;\n\n if (_parentVnode) {\n vm.$scopedSlots = normalizeScopedSlots(\n _parentVnode.data.scopedSlots,\n vm.$slots,\n vm.$scopedSlots\n );\n }\n\n // set parent vnode. this allows render functions to have access\n // to the data on the placeholder node.\n vm.$vnode = _parentVnode;\n // render self\n var vnode;\n try {\n // There's no need to maintain a stack becaues all render fns are called\n // separately from one another. Nested component's render fns are called\n // when parent component is patched.\n currentRenderingInstance = vm;\n vnode = render.call(vm._renderProxy, vm.$createElement);\n } catch (e) {\n handleError(e, vm, \"render\");\n // return error render result,\n // or previous vnode to prevent render error causing blank component\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {\n try {\n vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e);\n } catch (e) {\n handleError(e, vm, \"renderError\");\n vnode = vm._vnode;\n }\n } else {\n vnode = vm._vnode;\n }\n } finally {\n currentRenderingInstance = null;\n }\n // if the returned array contains only a single node, allow it\n if (Array.isArray(vnode) && vnode.length === 1) {\n vnode = vnode[0];\n }\n // return empty vnode in case the render function errored out\n if (!(vnode instanceof VNode)) {\n if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {\n warn(\n 'Multiple root nodes returned from render function. Render function ' +\n 'should return a single root node.',\n vm\n );\n }\n vnode = createEmptyVNode();\n }\n // set parent\n vnode.parent = _parentVnode;\n return vnode\n };\n}\n\n/* */\n\nfunction ensureCtor (comp, base) {\n if (\n comp.__esModule ||\n (hasSymbol && comp[Symbol.toStringTag] === 'Module')\n ) {\n comp = comp.default;\n }\n return isObject(comp)\n ? base.extend(comp)\n : comp\n}\n\nfunction createAsyncPlaceholder (\n factory,\n data,\n context,\n children,\n tag\n) {\n var node = createEmptyVNode();\n node.asyncFactory = factory;\n node.asyncMeta = { data: data, context: context, children: children, tag: tag };\n return node\n}\n\nfunction resolveAsyncComponent (\n factory,\n baseCtor\n) {\n if (isTrue(factory.error) && isDef(factory.errorComp)) {\n return factory.errorComp\n }\n\n if (isDef(factory.resolved)) {\n return factory.resolved\n }\n\n var owner = currentRenderingInstance;\n if (owner && isDef(factory.owners) && factory.owners.indexOf(owner) === -1) {\n // already pending\n factory.owners.push(owner);\n }\n\n if (isTrue(factory.loading) && isDef(factory.loadingComp)) {\n return factory.loadingComp\n }\n\n if (owner && !isDef(factory.owners)) {\n var owners = factory.owners = [owner];\n var sync = true;\n var timerLoading = null;\n var timerTimeout = null\n\n ;(owner).$on('hook:destroyed', function () { return remove(owners, owner); });\n\n var forceRender = function (renderCompleted) {\n for (var i = 0, l = owners.length; i < l; i++) {\n (owners[i]).$forceUpdate();\n }\n\n if (renderCompleted) {\n owners.length = 0;\n if (timerLoading !== null) {\n clearTimeout(timerLoading);\n timerLoading = null;\n }\n if (timerTimeout !== null) {\n clearTimeout(timerTimeout);\n timerTimeout = null;\n }\n }\n };\n\n var resolve = once(function (res) {\n // cache resolved\n factory.resolved = ensureCtor(res, baseCtor);\n // invoke callbacks only if this is not a synchronous resolve\n // (async resolves are shimmed as synchronous during SSR)\n if (!sync) {\n forceRender(true);\n } else {\n owners.length = 0;\n }\n });\n\n var reject = once(function (reason) {\n process.env.NODE_ENV !== 'production' && warn(\n \"Failed to resolve async component: \" + (String(factory)) +\n (reason ? (\"\\nReason: \" + reason) : '')\n );\n if (isDef(factory.errorComp)) {\n factory.error = true;\n forceRender(true);\n }\n });\n\n var res = factory(resolve, reject);\n\n if (isObject(res)) {\n if (isPromise(res)) {\n // () => Promise\n if (isUndef(factory.resolved)) {\n res.then(resolve, reject);\n }\n } else if (isPromise(res.component)) {\n res.component.then(resolve, reject);\n\n if (isDef(res.error)) {\n factory.errorComp = ensureCtor(res.error, baseCtor);\n }\n\n if (isDef(res.loading)) {\n factory.loadingComp = ensureCtor(res.loading, baseCtor);\n if (res.delay === 0) {\n factory.loading = true;\n } else {\n timerLoading = setTimeout(function () {\n timerLoading = null;\n if (isUndef(factory.resolved) && isUndef(factory.error)) {\n factory.loading = true;\n forceRender(false);\n }\n }, res.delay || 200);\n }\n }\n\n if (isDef(res.timeout)) {\n timerTimeout = setTimeout(function () {\n timerTimeout = null;\n if (isUndef(factory.resolved)) {\n reject(\n process.env.NODE_ENV !== 'production'\n ? (\"timeout (\" + (res.timeout) + \"ms)\")\n : null\n );\n }\n }, res.timeout);\n }\n }\n }\n\n sync = false;\n // return in case resolved synchronously\n return factory.loading\n ? factory.loadingComp\n : factory.resolved\n }\n}\n\n/* */\n\nfunction isAsyncPlaceholder (node) {\n return node.isComment && node.asyncFactory\n}\n\n/* */\n\nfunction getFirstComponentChild (children) {\n if (Array.isArray(children)) {\n for (var i = 0; i < children.length; i++) {\n var c = children[i];\n if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {\n return c\n }\n }\n }\n}\n\n/* */\n\n/* */\n\nfunction initEvents (vm) {\n vm._events = Object.create(null);\n vm._hasHookEvent = false;\n // init parent attached events\n var listeners = vm.$options._parentListeners;\n if (listeners) {\n updateComponentListeners(vm, listeners);\n }\n}\n\nvar target;\n\nfunction add (event, fn) {\n target.$on(event, fn);\n}\n\nfunction remove$1 (event, fn) {\n target.$off(event, fn);\n}\n\nfunction createOnceHandler (event, fn) {\n var _target = target;\n return function onceHandler () {\n var res = fn.apply(null, arguments);\n if (res !== null) {\n _target.$off(event, onceHandler);\n }\n }\n}\n\nfunction updateComponentListeners (\n vm,\n listeners,\n oldListeners\n) {\n target = vm;\n updateListeners(listeners, oldListeners || {}, add, remove$1, createOnceHandler, vm);\n target = undefined;\n}\n\nfunction eventsMixin (Vue) {\n var hookRE = /^hook:/;\n Vue.prototype.$on = function (event, fn) {\n var vm = this;\n if (Array.isArray(event)) {\n for (var i = 0, l = event.length; i < l; i++) {\n vm.$on(event[i], fn);\n }\n } else {\n (vm._events[event] || (vm._events[event] = [])).push(fn);\n // optimize hook:event cost by using a boolean flag marked at registration\n // instead of a hash lookup\n if (hookRE.test(event)) {\n vm._hasHookEvent = true;\n }\n }\n return vm\n };\n\n Vue.prototype.$once = function (event, fn) {\n var vm = this;\n function on () {\n vm.$off(event, on);\n fn.apply(vm, arguments);\n }\n on.fn = fn;\n vm.$on(event, on);\n return vm\n };\n\n Vue.prototype.$off = function (event, fn) {\n var vm = this;\n // all\n if (!arguments.length) {\n vm._events = Object.create(null);\n return vm\n }\n // array of events\n if (Array.isArray(event)) {\n for (var i$1 = 0, l = event.length; i$1 < l; i$1++) {\n vm.$off(event[i$1], fn);\n }\n return vm\n }\n // specific event\n var cbs = vm._events[event];\n if (!cbs) {\n return vm\n }\n if (!fn) {\n vm._events[event] = null;\n return vm\n }\n // specific handler\n var cb;\n var i = cbs.length;\n while (i--) {\n cb = cbs[i];\n if (cb === fn || cb.fn === fn) {\n cbs.splice(i, 1);\n break\n }\n }\n return vm\n };\n\n Vue.prototype.$emit = function (event) {\n var vm = this;\n if (process.env.NODE_ENV !== 'production') {\n var lowerCaseEvent = event.toLowerCase();\n if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {\n tip(\n \"Event \\\"\" + lowerCaseEvent + \"\\\" is emitted in component \" +\n (formatComponentName(vm)) + \" but the handler is registered for \\\"\" + event + \"\\\". \" +\n \"Note that HTML attributes are case-insensitive and you cannot use \" +\n \"v-on to listen to camelCase events when using in-DOM templates. \" +\n \"You should probably use \\\"\" + (hyphenate(event)) + \"\\\" instead of \\\"\" + event + \"\\\".\"\n );\n }\n }\n var cbs = vm._events[event];\n if (cbs) {\n cbs = cbs.length > 1 ? toArray(cbs) : cbs;\n var args = toArray(arguments, 1);\n var info = \"event handler for \\\"\" + event + \"\\\"\";\n for (var i = 0, l = cbs.length; i < l; i++) {\n invokeWithErrorHandling(cbs[i], vm, args, vm, info);\n }\n }\n return vm\n };\n}\n\n/* */\n\nvar activeInstance = null;\nvar isUpdatingChildComponent = false;\n\nfunction setActiveInstance(vm) {\n var prevActiveInstance = activeInstance;\n activeInstance = vm;\n return function () {\n activeInstance = prevActiveInstance;\n }\n}\n\nfunction initLifecycle (vm) {\n var options = vm.$options;\n\n // locate first non-abstract parent\n var parent = options.parent;\n if (parent && !options.abstract) {\n while (parent.$options.abstract && parent.$parent) {\n parent = parent.$parent;\n }\n parent.$children.push(vm);\n }\n\n vm.$parent = parent;\n vm.$root = parent ? parent.$root : vm;\n\n vm.$children = [];\n vm.$refs = {};\n\n vm._watcher = null;\n vm._inactive = null;\n vm._directInactive = false;\n vm._isMounted = false;\n vm._isDestroyed = false;\n vm._isBeingDestroyed = false;\n}\n\nfunction lifecycleMixin (Vue) {\n Vue.prototype._update = function (vnode, hydrating) {\n var vm = this;\n var prevEl = vm.$el;\n var prevVnode = vm._vnode;\n var restoreActiveInstance = setActiveInstance(vm);\n vm._vnode = vnode;\n // Vue.prototype.__patch__ is injected in entry points\n // based on the rendering backend used.\n if (!prevVnode) {\n // initial render\n vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);\n } else {\n // updates\n vm.$el = vm.__patch__(prevVnode, vnode);\n }\n restoreActiveInstance();\n // update __vue__ reference\n if (prevEl) {\n prevEl.__vue__ = null;\n }\n if (vm.$el) {\n vm.$el.__vue__ = vm;\n }\n // if parent is an HOC, update its $el as well\n if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {\n vm.$parent.$el = vm.$el;\n }\n // updated hook is called by the scheduler to ensure that children are\n // updated in a parent's updated hook.\n };\n\n Vue.prototype.$forceUpdate = function () {\n var vm = this;\n if (vm._watcher) {\n vm._watcher.update();\n }\n };\n\n Vue.prototype.$destroy = function () {\n var vm = this;\n if (vm._isBeingDestroyed) {\n return\n }\n callHook(vm, 'beforeDestroy');\n vm._isBeingDestroyed = true;\n // remove self from parent\n var parent = vm.$parent;\n if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {\n remove(parent.$children, vm);\n }\n // teardown watchers\n if (vm._watcher) {\n vm._watcher.teardown();\n }\n var i = vm._watchers.length;\n while (i--) {\n vm._watchers[i].teardown();\n }\n // remove reference from data ob\n // frozen object may not have observer.\n if (vm._data.__ob__) {\n vm._data.__ob__.vmCount--;\n }\n // call the last hook...\n vm._isDestroyed = true;\n // invoke destroy hooks on current rendered tree\n vm.__patch__(vm._vnode, null);\n // fire destroyed hook\n callHook(vm, 'destroyed');\n // turn off all instance listeners.\n vm.$off();\n // remove __vue__ reference\n if (vm.$el) {\n vm.$el.__vue__ = null;\n }\n // release circular reference (#6759)\n if (vm.$vnode) {\n vm.$vnode.parent = null;\n }\n };\n}\n\nfunction mountComponent (\n vm,\n el,\n hydrating\n) {\n vm.$el = el;\n if (!vm.$options.render) {\n vm.$options.render = createEmptyVNode;\n if (process.env.NODE_ENV !== 'production') {\n /* istanbul ignore if */\n if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||\n vm.$options.el || el) {\n warn(\n 'You are using the runtime-only build of Vue where the template ' +\n 'compiler is not available. Either pre-compile the templates into ' +\n 'render functions, or use the compiler-included build.',\n vm\n );\n } else {\n warn(\n 'Failed to mount component: template or render function not defined.',\n vm\n );\n }\n }\n }\n callHook(vm, 'beforeMount');\n\n var updateComponent;\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && config.performance && mark) {\n updateComponent = function () {\n var name = vm._name;\n var id = vm._uid;\n var startTag = \"vue-perf-start:\" + id;\n var endTag = \"vue-perf-end:\" + id;\n\n mark(startTag);\n var vnode = vm._render();\n mark(endTag);\n measure((\"vue \" + name + \" render\"), startTag, endTag);\n\n mark(startTag);\n vm._update(vnode, hydrating);\n mark(endTag);\n measure((\"vue \" + name + \" patch\"), startTag, endTag);\n };\n } else {\n updateComponent = function () {\n vm._update(vm._render(), hydrating);\n };\n }\n\n // we set this to vm._watcher inside the watcher's constructor\n // since the watcher's initial patch may call $forceUpdate (e.g. inside child\n // component's mounted hook), which relies on vm._watcher being already defined\n new Watcher(vm, updateComponent, noop, {\n before: function before () {\n if (vm._isMounted && !vm._isDestroyed) {\n callHook(vm, 'beforeUpdate');\n }\n }\n }, true /* isRenderWatcher */);\n hydrating = false;\n\n // manually mounted instance, call mounted on self\n // mounted is called for render-created child components in its inserted hook\n if (vm.$vnode == null) {\n vm._isMounted = true;\n callHook(vm, 'mounted');\n }\n return vm\n}\n\nfunction updateChildComponent (\n vm,\n propsData,\n listeners,\n parentVnode,\n renderChildren\n) {\n if (process.env.NODE_ENV !== 'production') {\n isUpdatingChildComponent = true;\n }\n\n // determine whether component has slot children\n // we need to do this before overwriting $options._renderChildren.\n\n // check if there are dynamic scopedSlots (hand-written or compiled but with\n // dynamic slot names). Static scoped slots compiled from template has the\n // \"$stable\" marker.\n var newScopedSlots = parentVnode.data.scopedSlots;\n var oldScopedSlots = vm.$scopedSlots;\n var hasDynamicScopedSlot = !!(\n (newScopedSlots && !newScopedSlots.$stable) ||\n (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||\n (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key)\n );\n\n // Any static slot children from the parent may have changed during parent's\n // update. Dynamic scoped slots may also have changed. In such cases, a forced\n // update is necessary to ensure correctness.\n var needsForceUpdate = !!(\n renderChildren || // has new static slots\n vm.$options._renderChildren || // has old static slots\n hasDynamicScopedSlot\n );\n\n vm.$options._parentVnode = parentVnode;\n vm.$vnode = parentVnode; // update vm's placeholder node without re-render\n\n if (vm._vnode) { // update child tree's parent\n vm._vnode.parent = parentVnode;\n }\n vm.$options._renderChildren = renderChildren;\n\n // update $attrs and $listeners hash\n // these are also reactive so they may trigger child update if the child\n // used them during render\n vm.$attrs = parentVnode.data.attrs || emptyObject;\n vm.$listeners = listeners || emptyObject;\n\n // update props\n if (propsData && vm.$options.props) {\n toggleObserving(false);\n var props = vm._props;\n var propKeys = vm.$options._propKeys || [];\n for (var i = 0; i < propKeys.length; i++) {\n var key = propKeys[i];\n var propOptions = vm.$options.props; // wtf flow?\n props[key] = validateProp(key, propOptions, propsData, vm);\n }\n toggleObserving(true);\n // keep a copy of raw propsData\n vm.$options.propsData = propsData;\n }\n\n // update listeners\n listeners = listeners || emptyObject;\n var oldListeners = vm.$options._parentListeners;\n vm.$options._parentListeners = listeners;\n updateComponentListeners(vm, listeners, oldListeners);\n\n // resolve slots + force update if has children\n if (needsForceUpdate) {\n vm.$slots = resolveSlots(renderChildren, parentVnode.context);\n vm.$forceUpdate();\n }\n\n if (process.env.NODE_ENV !== 'production') {\n isUpdatingChildComponent = false;\n }\n}\n\nfunction isInInactiveTree (vm) {\n while (vm && (vm = vm.$parent)) {\n if (vm._inactive) { return true }\n }\n return false\n}\n\nfunction activateChildComponent (vm, direct) {\n if (direct) {\n vm._directInactive = false;\n if (isInInactiveTree(vm)) {\n return\n }\n } else if (vm._directInactive) {\n return\n }\n if (vm._inactive || vm._inactive === null) {\n vm._inactive = false;\n for (var i = 0; i < vm.$children.length; i++) {\n activateChildComponent(vm.$children[i]);\n }\n callHook(vm, 'activated');\n }\n}\n\nfunction deactivateChildComponent (vm, direct) {\n if (direct) {\n vm._directInactive = true;\n if (isInInactiveTree(vm)) {\n return\n }\n }\n if (!vm._inactive) {\n vm._inactive = true;\n for (var i = 0; i < vm.$children.length; i++) {\n deactivateChildComponent(vm.$children[i]);\n }\n callHook(vm, 'deactivated');\n }\n}\n\nfunction callHook (vm, hook) {\n // #7573 disable dep collection when invoking lifecycle hooks\n pushTarget();\n var handlers = vm.$options[hook];\n var info = hook + \" hook\";\n if (handlers) {\n for (var i = 0, j = handlers.length; i < j; i++) {\n invokeWithErrorHandling(handlers[i], vm, null, vm, info);\n }\n }\n if (vm._hasHookEvent) {\n vm.$emit('hook:' + hook);\n }\n popTarget();\n}\n\n/* */\n\nvar MAX_UPDATE_COUNT = 100;\n\nvar queue = [];\nvar activatedChildren = [];\nvar has = {};\nvar circular = {};\nvar waiting = false;\nvar flushing = false;\nvar index = 0;\n\n/**\n * Reset the scheduler's state.\n */\nfunction resetSchedulerState () {\n index = queue.length = activatedChildren.length = 0;\n has = {};\n if (process.env.NODE_ENV !== 'production') {\n circular = {};\n }\n waiting = flushing = false;\n}\n\n// Async edge case #6566 requires saving the timestamp when event listeners are\n// attached. However, calling performance.now() has a perf overhead especially\n// if the page has thousands of event listeners. Instead, we take a timestamp\n// every time the scheduler flushes and use that for all event listeners\n// attached during that flush.\nvar currentFlushTimestamp = 0;\n\n// Async edge case fix requires storing an event listener's attach timestamp.\nvar getNow = Date.now;\n\n// Determine what event timestamp the browser is using. Annoyingly, the\n// timestamp can either be hi-res (relative to page load) or low-res\n// (relative to UNIX epoch), so in order to compare time we have to use the\n// same timestamp type when saving the flush timestamp.\n// All IE versions use low-res event timestamps, and have problematic clock\n// implementations (#9632)\nif (inBrowser && !isIE) {\n var performance = window.performance;\n if (\n performance &&\n typeof performance.now === 'function' &&\n getNow() > document.createEvent('Event').timeStamp\n ) {\n // if the event timestamp, although evaluated AFTER the Date.now(), is\n // smaller than it, it means the event is using a hi-res timestamp,\n // and we need to use the hi-res version for event listener timestamps as\n // well.\n getNow = function () { return performance.now(); };\n }\n}\n\n/**\n * Flush both queues and run the watchers.\n */\nfunction flushSchedulerQueue () {\n currentFlushTimestamp = getNow();\n flushing = true;\n var watcher, id;\n\n // Sort queue before flush.\n // This ensures that:\n // 1. Components are updated from parent to child. (because parent is always\n // created before the child)\n // 2. A component's user watchers are run before its render watcher (because\n // user watchers are created before the render watcher)\n // 3. If a component is destroyed during a parent component's watcher run,\n // its watchers can be skipped.\n queue.sort(function (a, b) { return a.id - b.id; });\n\n // do not cache length because more watchers might be pushed\n // as we run existing watchers\n for (index = 0; index < queue.length; index++) {\n watcher = queue[index];\n if (watcher.before) {\n watcher.before();\n }\n id = watcher.id;\n has[id] = null;\n watcher.run();\n // in dev build, check and stop circular updates.\n if (process.env.NODE_ENV !== 'production' && has[id] != null) {\n circular[id] = (circular[id] || 0) + 1;\n if (circular[id] > MAX_UPDATE_COUNT) {\n warn(\n 'You may have an infinite update loop ' + (\n watcher.user\n ? (\"in watcher with expression \\\"\" + (watcher.expression) + \"\\\"\")\n : \"in a component render function.\"\n ),\n watcher.vm\n );\n break\n }\n }\n }\n\n // keep copies of post queues before resetting state\n var activatedQueue = activatedChildren.slice();\n var updatedQueue = queue.slice();\n\n resetSchedulerState();\n\n // call component updated and activated hooks\n callActivatedHooks(activatedQueue);\n callUpdatedHooks(updatedQueue);\n\n // devtool hook\n /* istanbul ignore if */\n if (devtools && config.devtools) {\n devtools.emit('flush');\n }\n}\n\nfunction callUpdatedHooks (queue) {\n var i = queue.length;\n while (i--) {\n var watcher = queue[i];\n var vm = watcher.vm;\n if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {\n callHook(vm, 'updated');\n }\n }\n}\n\n/**\n * Queue a kept-alive component that was activated during patch.\n * The queue will be processed after the entire tree has been patched.\n */\nfunction queueActivatedComponent (vm) {\n // setting _inactive to false here so that a render function can\n // rely on checking whether it's in an inactive tree (e.g. router-view)\n vm._inactive = false;\n activatedChildren.push(vm);\n}\n\nfunction callActivatedHooks (queue) {\n for (var i = 0; i < queue.length; i++) {\n queue[i]._inactive = true;\n activateChildComponent(queue[i], true /* true */);\n }\n}\n\n/**\n * Push a watcher into the watcher queue.\n * Jobs with duplicate IDs will be skipped unless it's\n * pushed when the queue is being flushed.\n */\nfunction queueWatcher (watcher) {\n var id = watcher.id;\n if (has[id] == null) {\n has[id] = true;\n if (!flushing) {\n queue.push(watcher);\n } else {\n // if already flushing, splice the watcher based on its id\n // if already past its id, it will be run next immediately.\n var i = queue.length - 1;\n while (i > index && queue[i].id > watcher.id) {\n i--;\n }\n queue.splice(i + 1, 0, watcher);\n }\n // queue the flush\n if (!waiting) {\n waiting = true;\n\n if (process.env.NODE_ENV !== 'production' && !config.async) {\n flushSchedulerQueue();\n return\n }\n nextTick(flushSchedulerQueue);\n }\n }\n}\n\n/* */\n\n\n\nvar uid$2 = 0;\n\n/**\n * A watcher parses an expression, collects dependencies,\n * and fires callback when the expression value changes.\n * This is used for both the $watch() api and directives.\n */\nvar Watcher = function Watcher (\n vm,\n expOrFn,\n cb,\n options,\n isRenderWatcher\n) {\n this.vm = vm;\n if (isRenderWatcher) {\n vm._watcher = this;\n }\n vm._watchers.push(this);\n // options\n if (options) {\n this.deep = !!options.deep;\n this.user = !!options.user;\n this.lazy = !!options.lazy;\n this.sync = !!options.sync;\n this.before = options.before;\n } else {\n this.deep = this.user = this.lazy = this.sync = false;\n }\n this.cb = cb;\n this.id = ++uid$2; // uid for batching\n this.active = true;\n this.dirty = this.lazy; // for lazy watchers\n this.deps = [];\n this.newDeps = [];\n this.depIds = new _Set();\n this.newDepIds = new _Set();\n this.expression = process.env.NODE_ENV !== 'production'\n ? expOrFn.toString()\n : '';\n // parse expression for getter\n if (typeof expOrFn === 'function') {\n this.getter = expOrFn;\n } else {\n this.getter = parsePath(expOrFn);\n if (!this.getter) {\n this.getter = noop;\n process.env.NODE_ENV !== 'production' && warn(\n \"Failed watching path: \\\"\" + expOrFn + \"\\\" \" +\n 'Watcher only accepts simple dot-delimited paths. ' +\n 'For full control, use a function instead.',\n vm\n );\n }\n }\n this.value = this.lazy\n ? undefined\n : this.get();\n};\n\n/**\n * Evaluate the getter, and re-collect dependencies.\n */\nWatcher.prototype.get = function get () {\n pushTarget(this);\n var value;\n var vm = this.vm;\n try {\n value = this.getter.call(vm, vm);\n } catch (e) {\n if (this.user) {\n handleError(e, vm, (\"getter for watcher \\\"\" + (this.expression) + \"\\\"\"));\n } else {\n throw e\n }\n } finally {\n // \"touch\" every property so they are all tracked as\n // dependencies for deep watching\n if (this.deep) {\n traverse(value);\n }\n popTarget();\n this.cleanupDeps();\n }\n return value\n};\n\n/**\n * Add a dependency to this directive.\n */\nWatcher.prototype.addDep = function addDep (dep) {\n var id = dep.id;\n if (!this.newDepIds.has(id)) {\n this.newDepIds.add(id);\n this.newDeps.push(dep);\n if (!this.depIds.has(id)) {\n dep.addSub(this);\n }\n }\n};\n\n/**\n * Clean up for dependency collection.\n */\nWatcher.prototype.cleanupDeps = function cleanupDeps () {\n var i = this.deps.length;\n while (i--) {\n var dep = this.deps[i];\n if (!this.newDepIds.has(dep.id)) {\n dep.removeSub(this);\n }\n }\n var tmp = this.depIds;\n this.depIds = this.newDepIds;\n this.newDepIds = tmp;\n this.newDepIds.clear();\n tmp = this.deps;\n this.deps = this.newDeps;\n this.newDeps = tmp;\n this.newDeps.length = 0;\n};\n\n/**\n * Subscriber interface.\n * Will be called when a dependency changes.\n */\nWatcher.prototype.update = function update () {\n /* istanbul ignore else */\n if (this.lazy) {\n this.dirty = true;\n } else if (this.sync) {\n this.run();\n } else {\n queueWatcher(this);\n }\n};\n\n/**\n * Scheduler job interface.\n * Will be called by the scheduler.\n */\nWatcher.prototype.run = function run () {\n if (this.active) {\n var value = this.get();\n if (\n value !== this.value ||\n // Deep watchers and watchers on Object/Arrays should fire even\n // when the value is the same, because the value may\n // have mutated.\n isObject(value) ||\n this.deep\n ) {\n // set new value\n var oldValue = this.value;\n this.value = value;\n if (this.user) {\n try {\n this.cb.call(this.vm, value, oldValue);\n } catch (e) {\n handleError(e, this.vm, (\"callback for watcher \\\"\" + (this.expression) + \"\\\"\"));\n }\n } else {\n this.cb.call(this.vm, value, oldValue);\n }\n }\n }\n};\n\n/**\n * Evaluate the value of the watcher.\n * This only gets called for lazy watchers.\n */\nWatcher.prototype.evaluate = function evaluate () {\n this.value = this.get();\n this.dirty = false;\n};\n\n/**\n * Depend on all deps collected by this watcher.\n */\nWatcher.prototype.depend = function depend () {\n var i = this.deps.length;\n while (i--) {\n this.deps[i].depend();\n }\n};\n\n/**\n * Remove self from all dependencies' subscriber list.\n */\nWatcher.prototype.teardown = function teardown () {\n if (this.active) {\n // remove self from vm's watcher list\n // this is a somewhat expensive operation so we skip it\n // if the vm is being destroyed.\n if (!this.vm._isBeingDestroyed) {\n remove(this.vm._watchers, this);\n }\n var i = this.deps.length;\n while (i--) {\n this.deps[i].removeSub(this);\n }\n this.active = false;\n }\n};\n\n/* */\n\nvar sharedPropertyDefinition = {\n enumerable: true,\n configurable: true,\n get: noop,\n set: noop\n};\n\nfunction proxy (target, sourceKey, key) {\n sharedPropertyDefinition.get = function proxyGetter () {\n return this[sourceKey][key]\n };\n sharedPropertyDefinition.set = function proxySetter (val) {\n this[sourceKey][key] = val;\n };\n Object.defineProperty(target, key, sharedPropertyDefinition);\n}\n\nfunction initState (vm) {\n vm._watchers = [];\n var opts = vm.$options;\n if (opts.props) { initProps(vm, opts.props); }\n if (opts.methods) { initMethods(vm, opts.methods); }\n if (opts.data) {\n initData(vm);\n } else {\n observe(vm._data = {}, true /* asRootData */);\n }\n if (opts.computed) { initComputed(vm, opts.computed); }\n if (opts.watch && opts.watch !== nativeWatch) {\n initWatch(vm, opts.watch);\n }\n}\n\nfunction initProps (vm, propsOptions) {\n var propsData = vm.$options.propsData || {};\n var props = vm._props = {};\n // cache prop keys so that future props updates can iterate using Array\n // instead of dynamic object key enumeration.\n var keys = vm.$options._propKeys = [];\n var isRoot = !vm.$parent;\n // root instance props should be converted\n if (!isRoot) {\n toggleObserving(false);\n }\n var loop = function ( key ) {\n keys.push(key);\n var value = validateProp(key, propsOptions, propsData, vm);\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n var hyphenatedKey = hyphenate(key);\n if (isReservedAttribute(hyphenatedKey) ||\n config.isReservedAttr(hyphenatedKey)) {\n warn(\n (\"\\\"\" + hyphenatedKey + \"\\\" is a reserved attribute and cannot be used as component prop.\"),\n vm\n );\n }\n defineReactive$$1(props, key, value, function () {\n if (!isRoot && !isUpdatingChildComponent) {\n warn(\n \"Avoid mutating a prop directly since the value will be \" +\n \"overwritten whenever the parent component re-renders. \" +\n \"Instead, use a data or computed property based on the prop's \" +\n \"value. Prop being mutated: \\\"\" + key + \"\\\"\",\n vm\n );\n }\n });\n } else {\n defineReactive$$1(props, key, value);\n }\n // static props are already proxied on the component's prototype\n // during Vue.extend(). We only need to proxy props defined at\n // instantiation here.\n if (!(key in vm)) {\n proxy(vm, \"_props\", key);\n }\n };\n\n for (var key in propsOptions) loop( key );\n toggleObserving(true);\n}\n\nfunction initData (vm) {\n var data = vm.$options.data;\n data = vm._data = typeof data === 'function'\n ? getData(data, vm)\n : data || {};\n if (!isPlainObject(data)) {\n data = {};\n process.env.NODE_ENV !== 'production' && warn(\n 'data functions should return an object:\\n' +\n 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',\n vm\n );\n }\n // proxy data on instance\n var keys = Object.keys(data);\n var props = vm.$options.props;\n var methods = vm.$options.methods;\n var i = keys.length;\n while (i--) {\n var key = keys[i];\n if (process.env.NODE_ENV !== 'production') {\n if (methods && hasOwn(methods, key)) {\n warn(\n (\"Method \\\"\" + key + \"\\\" has already been defined as a data property.\"),\n vm\n );\n }\n }\n if (props && hasOwn(props, key)) {\n process.env.NODE_ENV !== 'production' && warn(\n \"The data property \\\"\" + key + \"\\\" is already declared as a prop. \" +\n \"Use prop default value instead.\",\n vm\n );\n } else if (!isReserved(key)) {\n proxy(vm, \"_data\", key);\n }\n }\n // observe data\n observe(data, true /* asRootData */);\n}\n\nfunction getData (data, vm) {\n // #7573 disable dep collection when invoking data getters\n pushTarget();\n try {\n return data.call(vm, vm)\n } catch (e) {\n handleError(e, vm, \"data()\");\n return {}\n } finally {\n popTarget();\n }\n}\n\nvar computedWatcherOptions = { lazy: true };\n\nfunction initComputed (vm, computed) {\n // $flow-disable-line\n var watchers = vm._computedWatchers = Object.create(null);\n // computed properties are just getters during SSR\n var isSSR = isServerRendering();\n\n for (var key in computed) {\n var userDef = computed[key];\n var getter = typeof userDef === 'function' ? userDef : userDef.get;\n if (process.env.NODE_ENV !== 'production' && getter == null) {\n warn(\n (\"Getter is missing for computed property \\\"\" + key + \"\\\".\"),\n vm\n );\n }\n\n if (!isSSR) {\n // create internal watcher for the computed property.\n watchers[key] = new Watcher(\n vm,\n getter || noop,\n noop,\n computedWatcherOptions\n );\n }\n\n // component-defined computed properties are already defined on the\n // component prototype. We only need to define computed properties defined\n // at instantiation here.\n if (!(key in vm)) {\n defineComputed(vm, key, userDef);\n } else if (process.env.NODE_ENV !== 'production') {\n if (key in vm.$data) {\n warn((\"The computed property \\\"\" + key + \"\\\" is already defined in data.\"), vm);\n } else if (vm.$options.props && key in vm.$options.props) {\n warn((\"The computed property \\\"\" + key + \"\\\" is already defined as a prop.\"), vm);\n }\n }\n }\n}\n\nfunction defineComputed (\n target,\n key,\n userDef\n) {\n var shouldCache = !isServerRendering();\n if (typeof userDef === 'function') {\n sharedPropertyDefinition.get = shouldCache\n ? createComputedGetter(key)\n : createGetterInvoker(userDef);\n sharedPropertyDefinition.set = noop;\n } else {\n sharedPropertyDefinition.get = userDef.get\n ? shouldCache && userDef.cache !== false\n ? createComputedGetter(key)\n : createGetterInvoker(userDef.get)\n : noop;\n sharedPropertyDefinition.set = userDef.set || noop;\n }\n if (process.env.NODE_ENV !== 'production' &&\n sharedPropertyDefinition.set === noop) {\n sharedPropertyDefinition.set = function () {\n warn(\n (\"Computed property \\\"\" + key + \"\\\" was assigned to but it has no setter.\"),\n this\n );\n };\n }\n Object.defineProperty(target, key, sharedPropertyDefinition);\n}\n\nfunction createComputedGetter (key) {\n return function computedGetter () {\n var watcher = this._computedWatchers && this._computedWatchers[key];\n if (watcher) {\n if (watcher.dirty) {\n watcher.evaluate();\n }\n if (Dep.target) {\n watcher.depend();\n }\n return watcher.value\n }\n }\n}\n\nfunction createGetterInvoker(fn) {\n return function computedGetter () {\n return fn.call(this, this)\n }\n}\n\nfunction initMethods (vm, methods) {\n var props = vm.$options.props;\n for (var key in methods) {\n if (process.env.NODE_ENV !== 'production') {\n if (typeof methods[key] !== 'function') {\n warn(\n \"Method \\\"\" + key + \"\\\" has type \\\"\" + (typeof methods[key]) + \"\\\" in the component definition. \" +\n \"Did you reference the function correctly?\",\n vm\n );\n }\n if (props && hasOwn(props, key)) {\n warn(\n (\"Method \\\"\" + key + \"\\\" has already been defined as a prop.\"),\n vm\n );\n }\n if ((key in vm) && isReserved(key)) {\n warn(\n \"Method \\\"\" + key + \"\\\" conflicts with an existing Vue instance method. \" +\n \"Avoid defining component methods that start with _ or $.\"\n );\n }\n }\n vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);\n }\n}\n\nfunction initWatch (vm, watch) {\n for (var key in watch) {\n var handler = watch[key];\n if (Array.isArray(handler)) {\n for (var i = 0; i < handler.length; i++) {\n createWatcher(vm, key, handler[i]);\n }\n } else {\n createWatcher(vm, key, handler);\n }\n }\n}\n\nfunction createWatcher (\n vm,\n expOrFn,\n handler,\n options\n) {\n if (isPlainObject(handler)) {\n options = handler;\n handler = handler.handler;\n }\n if (typeof handler === 'string') {\n handler = vm[handler];\n }\n return vm.$watch(expOrFn, handler, options)\n}\n\nfunction stateMixin (Vue) {\n // flow somehow has problems with directly declared definition object\n // when using Object.defineProperty, so we have to procedurally build up\n // the object here.\n var dataDef = {};\n dataDef.get = function () { return this._data };\n var propsDef = {};\n propsDef.get = function () { return this._props };\n if (process.env.NODE_ENV !== 'production') {\n dataDef.set = function () {\n warn(\n 'Avoid replacing instance root $data. ' +\n 'Use nested data properties instead.',\n this\n );\n };\n propsDef.set = function () {\n warn(\"$props is readonly.\", this);\n };\n }\n Object.defineProperty(Vue.prototype, '$data', dataDef);\n Object.defineProperty(Vue.prototype, '$props', propsDef);\n\n Vue.prototype.$set = set;\n Vue.prototype.$delete = del;\n\n Vue.prototype.$watch = function (\n expOrFn,\n cb,\n options\n ) {\n var vm = this;\n if (isPlainObject(cb)) {\n return createWatcher(vm, expOrFn, cb, options)\n }\n options = options || {};\n options.user = true;\n var watcher = new Watcher(vm, expOrFn, cb, options);\n if (options.immediate) {\n try {\n cb.call(vm, watcher.value);\n } catch (error) {\n handleError(error, vm, (\"callback for immediate watcher \\\"\" + (watcher.expression) + \"\\\"\"));\n }\n }\n return function unwatchFn () {\n watcher.teardown();\n }\n };\n}\n\n/* */\n\nvar uid$3 = 0;\n\nfunction initMixin (Vue) {\n Vue.prototype._init = function (options) {\n var vm = this;\n // a uid\n vm._uid = uid$3++;\n\n var startTag, endTag;\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && config.performance && mark) {\n startTag = \"vue-perf-start:\" + (vm._uid);\n endTag = \"vue-perf-end:\" + (vm._uid);\n mark(startTag);\n }\n\n // a flag to avoid this being observed\n vm._isVue = true;\n // merge options\n if (options && options._isComponent) {\n // optimize internal component instantiation\n // since dynamic options merging is pretty slow, and none of the\n // internal component options needs special treatment.\n initInternalComponent(vm, options);\n } else {\n vm.$options = mergeOptions(\n resolveConstructorOptions(vm.constructor),\n options || {},\n vm\n );\n }\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n initProxy(vm);\n } else {\n vm._renderProxy = vm;\n }\n // expose real self\n vm._self = vm;\n initLifecycle(vm);\n initEvents(vm);\n initRender(vm);\n callHook(vm, 'beforeCreate');\n initInjections(vm); // resolve injections before data/props\n initState(vm);\n initProvide(vm); // resolve provide after data/props\n callHook(vm, 'created');\n\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && config.performance && mark) {\n vm._name = formatComponentName(vm, false);\n mark(endTag);\n measure((\"vue \" + (vm._name) + \" init\"), startTag, endTag);\n }\n\n if (vm.$options.el) {\n vm.$mount(vm.$options.el);\n }\n };\n}\n\nfunction initInternalComponent (vm, options) {\n var opts = vm.$options = Object.create(vm.constructor.options);\n // doing this because it's faster than dynamic enumeration.\n var parentVnode = options._parentVnode;\n opts.parent = options.parent;\n opts._parentVnode = parentVnode;\n\n var vnodeComponentOptions = parentVnode.componentOptions;\n opts.propsData = vnodeComponentOptions.propsData;\n opts._parentListeners = vnodeComponentOptions.listeners;\n opts._renderChildren = vnodeComponentOptions.children;\n opts._componentTag = vnodeComponentOptions.tag;\n\n if (options.render) {\n opts.render = options.render;\n opts.staticRenderFns = options.staticRenderFns;\n }\n}\n\nfunction resolveConstructorOptions (Ctor) {\n var options = Ctor.options;\n if (Ctor.super) {\n var superOptions = resolveConstructorOptions(Ctor.super);\n var cachedSuperOptions = Ctor.superOptions;\n if (superOptions !== cachedSuperOptions) {\n // super option changed,\n // need to resolve new options.\n Ctor.superOptions = superOptions;\n // check if there are any late-modified/attached options (#4976)\n var modifiedOptions = resolveModifiedOptions(Ctor);\n // update base extend options\n if (modifiedOptions) {\n extend(Ctor.extendOptions, modifiedOptions);\n }\n options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions);\n if (options.name) {\n options.components[options.name] = Ctor;\n }\n }\n }\n return options\n}\n\nfunction resolveModifiedOptions (Ctor) {\n var modified;\n var latest = Ctor.options;\n var sealed = Ctor.sealedOptions;\n for (var key in latest) {\n if (latest[key] !== sealed[key]) {\n if (!modified) { modified = {}; }\n modified[key] = latest[key];\n }\n }\n return modified\n}\n\nfunction Vue (options) {\n if (process.env.NODE_ENV !== 'production' &&\n !(this instanceof Vue)\n ) {\n warn('Vue is a constructor and should be called with the `new` keyword');\n }\n this._init(options);\n}\n\ninitMixin(Vue);\nstateMixin(Vue);\neventsMixin(Vue);\nlifecycleMixin(Vue);\nrenderMixin(Vue);\n\n/* */\n\nfunction initUse (Vue) {\n Vue.use = function (plugin) {\n var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));\n if (installedPlugins.indexOf(plugin) > -1) {\n return this\n }\n\n // additional parameters\n var args = toArray(arguments, 1);\n args.unshift(this);\n if (typeof plugin.install === 'function') {\n plugin.install.apply(plugin, args);\n } else if (typeof plugin === 'function') {\n plugin.apply(null, args);\n }\n installedPlugins.push(plugin);\n return this\n };\n}\n\n/* */\n\nfunction initMixin$1 (Vue) {\n Vue.mixin = function (mixin) {\n this.options = mergeOptions(this.options, mixin);\n return this\n };\n}\n\n/* */\n\nfunction initExtend (Vue) {\n /**\n * Each instance constructor, including Vue, has a unique\n * cid. This enables us to create wrapped \"child\n * constructors\" for prototypal inheritance and cache them.\n */\n Vue.cid = 0;\n var cid = 1;\n\n /**\n * Class inheritance\n */\n Vue.extend = function (extendOptions) {\n extendOptions = extendOptions || {};\n var Super = this;\n var SuperId = Super.cid;\n var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});\n if (cachedCtors[SuperId]) {\n return cachedCtors[SuperId]\n }\n\n var name = extendOptions.name || Super.options.name;\n if (process.env.NODE_ENV !== 'production' && name) {\n validateComponentName(name);\n }\n\n var Sub = function VueComponent (options) {\n this._init(options);\n };\n Sub.prototype = Object.create(Super.prototype);\n Sub.prototype.constructor = Sub;\n Sub.cid = cid++;\n Sub.options = mergeOptions(\n Super.options,\n extendOptions\n );\n Sub['super'] = Super;\n\n // For props and computed properties, we define the proxy getters on\n // the Vue instances at extension time, on the extended prototype. This\n // avoids Object.defineProperty calls for each instance created.\n if (Sub.options.props) {\n initProps$1(Sub);\n }\n if (Sub.options.computed) {\n initComputed$1(Sub);\n }\n\n // allow further extension/mixin/plugin usage\n Sub.extend = Super.extend;\n Sub.mixin = Super.mixin;\n Sub.use = Super.use;\n\n // create asset registers, so extended classes\n // can have their private assets too.\n ASSET_TYPES.forEach(function (type) {\n Sub[type] = Super[type];\n });\n // enable recursive self-lookup\n if (name) {\n Sub.options.components[name] = Sub;\n }\n\n // keep a reference to the super options at extension time.\n // later at instantiation we can check if Super's options have\n // been updated.\n Sub.superOptions = Super.options;\n Sub.extendOptions = extendOptions;\n Sub.sealedOptions = extend({}, Sub.options);\n\n // cache constructor\n cachedCtors[SuperId] = Sub;\n return Sub\n };\n}\n\nfunction initProps$1 (Comp) {\n var props = Comp.options.props;\n for (var key in props) {\n proxy(Comp.prototype, \"_props\", key);\n }\n}\n\nfunction initComputed$1 (Comp) {\n var computed = Comp.options.computed;\n for (var key in computed) {\n defineComputed(Comp.prototype, key, computed[key]);\n }\n}\n\n/* */\n\nfunction initAssetRegisters (Vue) {\n /**\n * Create asset registration methods.\n */\n ASSET_TYPES.forEach(function (type) {\n Vue[type] = function (\n id,\n definition\n ) {\n if (!definition) {\n return this.options[type + 's'][id]\n } else {\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && type === 'component') {\n validateComponentName(id);\n }\n if (type === 'component' && isPlainObject(definition)) {\n definition.name = definition.name || id;\n definition = this.options._base.extend(definition);\n }\n if (type === 'directive' && typeof definition === 'function') {\n definition = { bind: definition, update: definition };\n }\n this.options[type + 's'][id] = definition;\n return definition\n }\n };\n });\n}\n\n/* */\n\n\n\nfunction getComponentName (opts) {\n return opts && (opts.Ctor.options.name || opts.tag)\n}\n\nfunction matches (pattern, name) {\n if (Array.isArray(pattern)) {\n return pattern.indexOf(name) > -1\n } else if (typeof pattern === 'string') {\n return pattern.split(',').indexOf(name) > -1\n } else if (isRegExp(pattern)) {\n return pattern.test(name)\n }\n /* istanbul ignore next */\n return false\n}\n\nfunction pruneCache (keepAliveInstance, filter) {\n var cache = keepAliveInstance.cache;\n var keys = keepAliveInstance.keys;\n var _vnode = keepAliveInstance._vnode;\n for (var key in cache) {\n var cachedNode = cache[key];\n if (cachedNode) {\n var name = getComponentName(cachedNode.componentOptions);\n if (name && !filter(name)) {\n pruneCacheEntry(cache, key, keys, _vnode);\n }\n }\n }\n}\n\nfunction pruneCacheEntry (\n cache,\n key,\n keys,\n current\n) {\n var cached$$1 = cache[key];\n if (cached$$1 && (!current || cached$$1.tag !== current.tag)) {\n cached$$1.componentInstance.$destroy();\n }\n cache[key] = null;\n remove(keys, key);\n}\n\nvar patternTypes = [String, RegExp, Array];\n\nvar KeepAlive = {\n name: 'keep-alive',\n abstract: true,\n\n props: {\n include: patternTypes,\n exclude: patternTypes,\n max: [String, Number]\n },\n\n created: function created () {\n this.cache = Object.create(null);\n this.keys = [];\n },\n\n destroyed: function destroyed () {\n for (var key in this.cache) {\n pruneCacheEntry(this.cache, key, this.keys);\n }\n },\n\n mounted: function mounted () {\n var this$1 = this;\n\n this.$watch('include', function (val) {\n pruneCache(this$1, function (name) { return matches(val, name); });\n });\n this.$watch('exclude', function (val) {\n pruneCache(this$1, function (name) { return !matches(val, name); });\n });\n },\n\n render: function render () {\n var slot = this.$slots.default;\n var vnode = getFirstComponentChild(slot);\n var componentOptions = vnode && vnode.componentOptions;\n if (componentOptions) {\n // check pattern\n var name = getComponentName(componentOptions);\n var ref = this;\n var include = ref.include;\n var exclude = ref.exclude;\n if (\n // not included\n (include && (!name || !matches(include, name))) ||\n // excluded\n (exclude && name && matches(exclude, name))\n ) {\n return vnode\n }\n\n var ref$1 = this;\n var cache = ref$1.cache;\n var keys = ref$1.keys;\n var key = vnode.key == null\n // same constructor may get registered as different local components\n // so cid alone is not enough (#3269)\n ? componentOptions.Ctor.cid + (componentOptions.tag ? (\"::\" + (componentOptions.tag)) : '')\n : vnode.key;\n if (cache[key]) {\n vnode.componentInstance = cache[key].componentInstance;\n // make current key freshest\n remove(keys, key);\n keys.push(key);\n } else {\n cache[key] = vnode;\n keys.push(key);\n // prune oldest entry\n if (this.max && keys.length > parseInt(this.max)) {\n pruneCacheEntry(cache, keys[0], keys, this._vnode);\n }\n }\n\n vnode.data.keepAlive = true;\n }\n return vnode || (slot && slot[0])\n }\n};\n\nvar builtInComponents = {\n KeepAlive: KeepAlive\n};\n\n/* */\n\nfunction initGlobalAPI (Vue) {\n // config\n var configDef = {};\n configDef.get = function () { return config; };\n if (process.env.NODE_ENV !== 'production') {\n configDef.set = function () {\n warn(\n 'Do not replace the Vue.config object, set individual fields instead.'\n );\n };\n }\n Object.defineProperty(Vue, 'config', configDef);\n\n // exposed util methods.\n // NOTE: these are not considered part of the public API - avoid relying on\n // them unless you are aware of the risk.\n Vue.util = {\n warn: warn,\n extend: extend,\n mergeOptions: mergeOptions,\n defineReactive: defineReactive$$1\n };\n\n Vue.set = set;\n Vue.delete = del;\n Vue.nextTick = nextTick;\n\n // 2.6 explicit observable API\n Vue.observable = function (obj) {\n observe(obj);\n return obj\n };\n\n Vue.options = Object.create(null);\n ASSET_TYPES.forEach(function (type) {\n Vue.options[type + 's'] = Object.create(null);\n });\n\n // this is used to identify the \"base\" constructor to extend all plain-object\n // components with in Weex's multi-instance scenarios.\n Vue.options._base = Vue;\n\n extend(Vue.options.components, builtInComponents);\n\n initUse(Vue);\n initMixin$1(Vue);\n initExtend(Vue);\n initAssetRegisters(Vue);\n}\n\ninitGlobalAPI(Vue);\n\nObject.defineProperty(Vue.prototype, '$isServer', {\n get: isServerRendering\n});\n\nObject.defineProperty(Vue.prototype, '$ssrContext', {\n get: function get () {\n /* istanbul ignore next */\n return this.$vnode && this.$vnode.ssrContext\n }\n});\n\n// expose FunctionalRenderContext for ssr runtime helper installation\nObject.defineProperty(Vue, 'FunctionalRenderContext', {\n value: FunctionalRenderContext\n});\n\nVue.version = '2.6.10';\n\n/* */\n\n// these are reserved for web because they are directly compiled away\n// during template compilation\nvar isReservedAttr = makeMap('style,class');\n\n// attributes that should be using props for binding\nvar acceptValue = makeMap('input,textarea,option,select,progress');\nvar mustUseProp = function (tag, type, attr) {\n return (\n (attr === 'value' && acceptValue(tag)) && type !== 'button' ||\n (attr === 'selected' && tag === 'option') ||\n (attr === 'checked' && tag === 'input') ||\n (attr === 'muted' && tag === 'video')\n )\n};\n\nvar isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck');\n\nvar isValidContentEditableValue = makeMap('events,caret,typing,plaintext-only');\n\nvar convertEnumeratedValue = function (key, value) {\n return isFalsyAttrValue(value) || value === 'false'\n ? 'false'\n // allow arbitrary string value for contenteditable\n : key === 'contenteditable' && isValidContentEditableValue(value)\n ? value\n : 'true'\n};\n\nvar isBooleanAttr = makeMap(\n 'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +\n 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' +\n 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' +\n 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' +\n 'required,reversed,scoped,seamless,selected,sortable,translate,' +\n 'truespeed,typemustmatch,visible'\n);\n\nvar xlinkNS = 'http://www.w3.org/1999/xlink';\n\nvar isXlink = function (name) {\n return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'\n};\n\nvar getXlinkProp = function (name) {\n return isXlink(name) ? name.slice(6, name.length) : ''\n};\n\nvar isFalsyAttrValue = function (val) {\n return val == null || val === false\n};\n\n/* */\n\nfunction genClassForVnode (vnode) {\n var data = vnode.data;\n var parentNode = vnode;\n var childNode = vnode;\n while (isDef(childNode.componentInstance)) {\n childNode = childNode.componentInstance._vnode;\n if (childNode && childNode.data) {\n data = mergeClassData(childNode.data, data);\n }\n }\n while (isDef(parentNode = parentNode.parent)) {\n if (parentNode && parentNode.data) {\n data = mergeClassData(data, parentNode.data);\n }\n }\n return renderClass(data.staticClass, data.class)\n}\n\nfunction mergeClassData (child, parent) {\n return {\n staticClass: concat(child.staticClass, parent.staticClass),\n class: isDef(child.class)\n ? [child.class, parent.class]\n : parent.class\n }\n}\n\nfunction renderClass (\n staticClass,\n dynamicClass\n) {\n if (isDef(staticClass) || isDef(dynamicClass)) {\n return concat(staticClass, stringifyClass(dynamicClass))\n }\n /* istanbul ignore next */\n return ''\n}\n\nfunction concat (a, b) {\n return a ? b ? (a + ' ' + b) : a : (b || '')\n}\n\nfunction stringifyClass (value) {\n if (Array.isArray(value)) {\n return stringifyArray(value)\n }\n if (isObject(value)) {\n return stringifyObject(value)\n }\n if (typeof value === 'string') {\n return value\n }\n /* istanbul ignore next */\n return ''\n}\n\nfunction stringifyArray (value) {\n var res = '';\n var stringified;\n for (var i = 0, l = value.length; i < l; i++) {\n if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {\n if (res) { res += ' '; }\n res += stringified;\n }\n }\n return res\n}\n\nfunction stringifyObject (value) {\n var res = '';\n for (var key in value) {\n if (value[key]) {\n if (res) { res += ' '; }\n res += key;\n }\n }\n return res\n}\n\n/* */\n\nvar namespaceMap = {\n svg: 'http://www.w3.org/2000/svg',\n math: 'http://www.w3.org/1998/Math/MathML'\n};\n\nvar isHTMLTag = makeMap(\n 'html,body,base,head,link,meta,style,title,' +\n 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +\n 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +\n 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +\n 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +\n 'embed,object,param,source,canvas,script,noscript,del,ins,' +\n 'caption,col,colgroup,table,thead,tbody,td,th,tr,' +\n 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +\n 'output,progress,select,textarea,' +\n 'details,dialog,menu,menuitem,summary,' +\n 'content,element,shadow,template,blockquote,iframe,tfoot'\n);\n\n// this map is intentionally selective, only covering SVG elements that may\n// contain child elements.\nvar isSVG = makeMap(\n 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +\n 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +\n 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',\n true\n);\n\nvar isReservedTag = function (tag) {\n return isHTMLTag(tag) || isSVG(tag)\n};\n\nfunction getTagNamespace (tag) {\n if (isSVG(tag)) {\n return 'svg'\n }\n // basic support for MathML\n // note it doesn't support other MathML elements being component roots\n if (tag === 'math') {\n return 'math'\n }\n}\n\nvar unknownElementCache = Object.create(null);\nfunction isUnknownElement (tag) {\n /* istanbul ignore if */\n if (!inBrowser) {\n return true\n }\n if (isReservedTag(tag)) {\n return false\n }\n tag = tag.toLowerCase();\n /* istanbul ignore if */\n if (unknownElementCache[tag] != null) {\n return unknownElementCache[tag]\n }\n var el = document.createElement(tag);\n if (tag.indexOf('-') > -1) {\n // http://stackoverflow.com/a/28210364/1070244\n return (unknownElementCache[tag] = (\n el.constructor === window.HTMLUnknownElement ||\n el.constructor === window.HTMLElement\n ))\n } else {\n return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString()))\n }\n}\n\nvar isTextInputType = makeMap('text,number,password,search,email,tel,url');\n\n/* */\n\n/**\n * Query an element selector if it's not an element already.\n */\nfunction query (el) {\n if (typeof el === 'string') {\n var selected = document.querySelector(el);\n if (!selected) {\n process.env.NODE_ENV !== 'production' && warn(\n 'Cannot find element: ' + el\n );\n return document.createElement('div')\n }\n return selected\n } else {\n return el\n }\n}\n\n/* */\n\nfunction createElement$1 (tagName, vnode) {\n var elm = document.createElement(tagName);\n if (tagName !== 'select') {\n return elm\n }\n // false or null will remove the attribute but undefined will not\n if (vnode.data && vnode.data.attrs && vnode.data.attrs.multiple !== undefined) {\n elm.setAttribute('multiple', 'multiple');\n }\n return elm\n}\n\nfunction createElementNS (namespace, tagName) {\n return document.createElementNS(namespaceMap[namespace], tagName)\n}\n\nfunction createTextNode (text) {\n return document.createTextNode(text)\n}\n\nfunction createComment (text) {\n return document.createComment(text)\n}\n\nfunction insertBefore (parentNode, newNode, referenceNode) {\n parentNode.insertBefore(newNode, referenceNode);\n}\n\nfunction removeChild (node, child) {\n node.removeChild(child);\n}\n\nfunction appendChild (node, child) {\n node.appendChild(child);\n}\n\nfunction parentNode (node) {\n return node.parentNode\n}\n\nfunction nextSibling (node) {\n return node.nextSibling\n}\n\nfunction tagName (node) {\n return node.tagName\n}\n\nfunction setTextContent (node, text) {\n node.textContent = text;\n}\n\nfunction setStyleScope (node, scopeId) {\n node.setAttribute(scopeId, '');\n}\n\nvar nodeOps = /*#__PURE__*/Object.freeze({\n createElement: createElement$1,\n createElementNS: createElementNS,\n createTextNode: createTextNode,\n createComment: createComment,\n insertBefore: insertBefore,\n removeChild: removeChild,\n appendChild: appendChild,\n parentNode: parentNode,\n nextSibling: nextSibling,\n tagName: tagName,\n setTextContent: setTextContent,\n setStyleScope: setStyleScope\n});\n\n/* */\n\nvar ref = {\n create: function create (_, vnode) {\n registerRef(vnode);\n },\n update: function update (oldVnode, vnode) {\n if (oldVnode.data.ref !== vnode.data.ref) {\n registerRef(oldVnode, true);\n registerRef(vnode);\n }\n },\n destroy: function destroy (vnode) {\n registerRef(vnode, true);\n }\n};\n\nfunction registerRef (vnode, isRemoval) {\n var key = vnode.data.ref;\n if (!isDef(key)) { return }\n\n var vm = vnode.context;\n var ref = vnode.componentInstance || vnode.elm;\n var refs = vm.$refs;\n if (isRemoval) {\n if (Array.isArray(refs[key])) {\n remove(refs[key], ref);\n } else if (refs[key] === ref) {\n refs[key] = undefined;\n }\n } else {\n if (vnode.data.refInFor) {\n if (!Array.isArray(refs[key])) {\n refs[key] = [ref];\n } else if (refs[key].indexOf(ref) < 0) {\n // $flow-disable-line\n refs[key].push(ref);\n }\n } else {\n refs[key] = ref;\n }\n }\n}\n\n/**\n * Virtual DOM patching algorithm based on Snabbdom by\n * Simon Friis Vindum (@paldepind)\n * Licensed under the MIT License\n * https://github.com/paldepind/snabbdom/blob/master/LICENSE\n *\n * modified by Evan You (@yyx990803)\n *\n * Not type-checking this because this file is perf-critical and the cost\n * of making flow understand it is not worth it.\n */\n\nvar emptyNode = new VNode('', {}, []);\n\nvar hooks = ['create', 'activate', 'update', 'remove', 'destroy'];\n\nfunction sameVnode (a, b) {\n return (\n a.key === b.key && (\n (\n a.tag === b.tag &&\n a.isComment === b.isComment &&\n isDef(a.data) === isDef(b.data) &&\n sameInputType(a, b)\n ) || (\n isTrue(a.isAsyncPlaceholder) &&\n a.asyncFactory === b.asyncFactory &&\n isUndef(b.asyncFactory.error)\n )\n )\n )\n}\n\nfunction sameInputType (a, b) {\n if (a.tag !== 'input') { return true }\n var i;\n var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type;\n var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type;\n return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)\n}\n\nfunction createKeyToOldIdx (children, beginIdx, endIdx) {\n var i, key;\n var map = {};\n for (i = beginIdx; i <= endIdx; ++i) {\n key = children[i].key;\n if (isDef(key)) { map[key] = i; }\n }\n return map\n}\n\nfunction createPatchFunction (backend) {\n var i, j;\n var cbs = {};\n\n var modules = backend.modules;\n var nodeOps = backend.nodeOps;\n\n for (i = 0; i < hooks.length; ++i) {\n cbs[hooks[i]] = [];\n for (j = 0; j < modules.length; ++j) {\n if (isDef(modules[j][hooks[i]])) {\n cbs[hooks[i]].push(modules[j][hooks[i]]);\n }\n }\n }\n\n function emptyNodeAt (elm) {\n return new VNode(nodeOps.tagName(elm).toLowerCase(), {}, [], undefined, elm)\n }\n\n function createRmCb (childElm, listeners) {\n function remove$$1 () {\n if (--remove$$1.listeners === 0) {\n removeNode(childElm);\n }\n }\n remove$$1.listeners = listeners;\n return remove$$1\n }\n\n function removeNode (el) {\n var parent = nodeOps.parentNode(el);\n // element may have already been removed due to v-html / v-text\n if (isDef(parent)) {\n nodeOps.removeChild(parent, el);\n }\n }\n\n function isUnknownElement$$1 (vnode, inVPre) {\n return (\n !inVPre &&\n !vnode.ns &&\n !(\n config.ignoredElements.length &&\n config.ignoredElements.some(function (ignore) {\n return isRegExp(ignore)\n ? ignore.test(vnode.tag)\n : ignore === vnode.tag\n })\n ) &&\n config.isUnknownElement(vnode.tag)\n )\n }\n\n var creatingElmInVPre = 0;\n\n function createElm (\n vnode,\n insertedVnodeQueue,\n parentElm,\n refElm,\n nested,\n ownerArray,\n index\n ) {\n if (isDef(vnode.elm) && isDef(ownerArray)) {\n // This vnode was used in a previous render!\n // now it's used as a new node, overwriting its elm would cause\n // potential patch errors down the road when it's used as an insertion\n // reference node. Instead, we clone the node on-demand before creating\n // associated DOM element for it.\n vnode = ownerArray[index] = cloneVNode(vnode);\n }\n\n vnode.isRootInsert = !nested; // for transition enter check\n if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {\n return\n }\n\n var data = vnode.data;\n var children = vnode.children;\n var tag = vnode.tag;\n if (isDef(tag)) {\n if (process.env.NODE_ENV !== 'production') {\n if (data && data.pre) {\n creatingElmInVPre++;\n }\n if (isUnknownElement$$1(vnode, creatingElmInVPre)) {\n warn(\n 'Unknown custom element: <' + tag + '> - did you ' +\n 'register the component correctly? For recursive components, ' +\n 'make sure to provide the \"name\" option.',\n vnode.context\n );\n }\n }\n\n vnode.elm = vnode.ns\n ? nodeOps.createElementNS(vnode.ns, tag)\n : nodeOps.createElement(tag, vnode);\n setScope(vnode);\n\n /* istanbul ignore if */\n {\n createChildren(vnode, children, insertedVnodeQueue);\n if (isDef(data)) {\n invokeCreateHooks(vnode, insertedVnodeQueue);\n }\n insert(parentElm, vnode.elm, refElm);\n }\n\n if (process.env.NODE_ENV !== 'production' && data && data.pre) {\n creatingElmInVPre--;\n }\n } else if (isTrue(vnode.isComment)) {\n vnode.elm = nodeOps.createComment(vnode.text);\n insert(parentElm, vnode.elm, refElm);\n } else {\n vnode.elm = nodeOps.createTextNode(vnode.text);\n insert(parentElm, vnode.elm, refElm);\n }\n }\n\n function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {\n var i = vnode.data;\n if (isDef(i)) {\n var isReactivated = isDef(vnode.componentInstance) && i.keepAlive;\n if (isDef(i = i.hook) && isDef(i = i.init)) {\n i(vnode, false /* hydrating */);\n }\n // after calling the init hook, if the vnode is a child component\n // it should've created a child instance and mounted it. the child\n // component also has set the placeholder vnode's elm.\n // in that case we can just return the element and be done.\n if (isDef(vnode.componentInstance)) {\n initComponent(vnode, insertedVnodeQueue);\n insert(parentElm, vnode.elm, refElm);\n if (isTrue(isReactivated)) {\n reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);\n }\n return true\n }\n }\n }\n\n function initComponent (vnode, insertedVnodeQueue) {\n if (isDef(vnode.data.pendingInsert)) {\n insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert);\n vnode.data.pendingInsert = null;\n }\n vnode.elm = vnode.componentInstance.$el;\n if (isPatchable(vnode)) {\n invokeCreateHooks(vnode, insertedVnodeQueue);\n setScope(vnode);\n } else {\n // empty component root.\n // skip all element-related modules except for ref (#3455)\n registerRef(vnode);\n // make sure to invoke the insert hook\n insertedVnodeQueue.push(vnode);\n }\n }\n\n function reactivateComponent (vnode, insertedVnodeQueue, parentElm, refElm) {\n var i;\n // hack for #4339: a reactivated component with inner transition\n // does not trigger because the inner node's created hooks are not called\n // again. It's not ideal to involve module-specific logic in here but\n // there doesn't seem to be a better way to do it.\n var innerNode = vnode;\n while (innerNode.componentInstance) {\n innerNode = innerNode.componentInstance._vnode;\n if (isDef(i = innerNode.data) && isDef(i = i.transition)) {\n for (i = 0; i < cbs.activate.length; ++i) {\n cbs.activate[i](emptyNode, innerNode);\n }\n insertedVnodeQueue.push(innerNode);\n break\n }\n }\n // unlike a newly created component,\n // a reactivated keep-alive component doesn't insert itself\n insert(parentElm, vnode.elm, refElm);\n }\n\n function insert (parent, elm, ref$$1) {\n if (isDef(parent)) {\n if (isDef(ref$$1)) {\n if (nodeOps.parentNode(ref$$1) === parent) {\n nodeOps.insertBefore(parent, elm, ref$$1);\n }\n } else {\n nodeOps.appendChild(parent, elm);\n }\n }\n }\n\n function createChildren (vnode, children, insertedVnodeQueue) {\n if (Array.isArray(children)) {\n if (process.env.NODE_ENV !== 'production') {\n checkDuplicateKeys(children);\n }\n for (var i = 0; i < children.length; ++i) {\n createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i);\n }\n } else if (isPrimitive(vnode.text)) {\n nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)));\n }\n }\n\n function isPatchable (vnode) {\n while (vnode.componentInstance) {\n vnode = vnode.componentInstance._vnode;\n }\n return isDef(vnode.tag)\n }\n\n function invokeCreateHooks (vnode, insertedVnodeQueue) {\n for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {\n cbs.create[i$1](emptyNode, vnode);\n }\n i = vnode.data.hook; // Reuse variable\n if (isDef(i)) {\n if (isDef(i.create)) { i.create(emptyNode, vnode); }\n if (isDef(i.insert)) { insertedVnodeQueue.push(vnode); }\n }\n }\n\n // set scope id attribute for scoped CSS.\n // this is implemented as a special case to avoid the overhead\n // of going through the normal attribute patching process.\n function setScope (vnode) {\n var i;\n if (isDef(i = vnode.fnScopeId)) {\n nodeOps.setStyleScope(vnode.elm, i);\n } else {\n var ancestor = vnode;\n while (ancestor) {\n if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) {\n nodeOps.setStyleScope(vnode.elm, i);\n }\n ancestor = ancestor.parent;\n }\n }\n // for slot content they should also get the scopeId from the host instance.\n if (isDef(i = activeInstance) &&\n i !== vnode.context &&\n i !== vnode.fnContext &&\n isDef(i = i.$options._scopeId)\n ) {\n nodeOps.setStyleScope(vnode.elm, i);\n }\n }\n\n function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) {\n for (; startIdx <= endIdx; ++startIdx) {\n createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm, false, vnodes, startIdx);\n }\n }\n\n function invokeDestroyHook (vnode) {\n var i, j;\n var data = vnode.data;\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.destroy)) { i(vnode); }\n for (i = 0; i < cbs.destroy.length; ++i) { cbs.destroy[i](vnode); }\n }\n if (isDef(i = vnode.children)) {\n for (j = 0; j < vnode.children.length; ++j) {\n invokeDestroyHook(vnode.children[j]);\n }\n }\n }\n\n function removeVnodes (parentElm, vnodes, startIdx, endIdx) {\n for (; startIdx <= endIdx; ++startIdx) {\n var ch = vnodes[startIdx];\n if (isDef(ch)) {\n if (isDef(ch.tag)) {\n removeAndInvokeRemoveHook(ch);\n invokeDestroyHook(ch);\n } else { // Text node\n removeNode(ch.elm);\n }\n }\n }\n }\n\n function removeAndInvokeRemoveHook (vnode, rm) {\n if (isDef(rm) || isDef(vnode.data)) {\n var i;\n var listeners = cbs.remove.length + 1;\n if (isDef(rm)) {\n // we have a recursively passed down rm callback\n // increase the listeners count\n rm.listeners += listeners;\n } else {\n // directly removing\n rm = createRmCb(vnode.elm, listeners);\n }\n // recursively invoke hooks on child component root node\n if (isDef(i = vnode.componentInstance) && isDef(i = i._vnode) && isDef(i.data)) {\n removeAndInvokeRemoveHook(i, rm);\n }\n for (i = 0; i < cbs.remove.length; ++i) {\n cbs.remove[i](vnode, rm);\n }\n if (isDef(i = vnode.data.hook) && isDef(i = i.remove)) {\n i(vnode, rm);\n } else {\n rm();\n }\n } else {\n removeNode(vnode.elm);\n }\n }\n\n function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {\n var oldStartIdx = 0;\n var newStartIdx = 0;\n var oldEndIdx = oldCh.length - 1;\n var oldStartVnode = oldCh[0];\n var oldEndVnode = oldCh[oldEndIdx];\n var newEndIdx = newCh.length - 1;\n var newStartVnode = newCh[0];\n var newEndVnode = newCh[newEndIdx];\n var oldKeyToIdx, idxInOld, vnodeToMove, refElm;\n\n // removeOnly is a special flag used only by <transition-group>\n // to ensure removed elements stay in correct relative positions\n // during leaving transitions\n var canMove = !removeOnly;\n\n if (process.env.NODE_ENV !== 'production') {\n checkDuplicateKeys(newCh);\n }\n\n while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {\n if (isUndef(oldStartVnode)) {\n oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left\n } else if (isUndef(oldEndVnode)) {\n oldEndVnode = oldCh[--oldEndIdx];\n } else if (sameVnode(oldStartVnode, newStartVnode)) {\n patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n oldStartVnode = oldCh[++oldStartIdx];\n newStartVnode = newCh[++newStartIdx];\n } else if (sameVnode(oldEndVnode, newEndVnode)) {\n patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);\n oldEndVnode = oldCh[--oldEndIdx];\n newEndVnode = newCh[--newEndIdx];\n } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right\n patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);\n canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));\n oldStartVnode = oldCh[++oldStartIdx];\n newEndVnode = newCh[--newEndIdx];\n } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left\n patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);\n oldEndVnode = oldCh[--oldEndIdx];\n newStartVnode = newCh[++newStartIdx];\n } else {\n if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }\n idxInOld = isDef(newStartVnode.key)\n ? oldKeyToIdx[newStartVnode.key]\n : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);\n if (isUndef(idxInOld)) { // New element\n createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);\n } else {\n vnodeToMove = oldCh[idxInOld];\n if (sameVnode(vnodeToMove, newStartVnode)) {\n patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n oldCh[idxInOld] = undefined;\n canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);\n } else {\n // same key but different element. treat as new element\n createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);\n }\n }\n newStartVnode = newCh[++newStartIdx];\n }\n }\n if (oldStartIdx > oldEndIdx) {\n refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;\n addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);\n } else if (newStartIdx > newEndIdx) {\n removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);\n }\n }\n\n function checkDuplicateKeys (children) {\n var seenKeys = {};\n for (var i = 0; i < children.length; i++) {\n var vnode = children[i];\n var key = vnode.key;\n if (isDef(key)) {\n if (seenKeys[key]) {\n warn(\n (\"Duplicate keys detected: '\" + key + \"'. This may cause an update error.\"),\n vnode.context\n );\n } else {\n seenKeys[key] = true;\n }\n }\n }\n }\n\n function findIdxInOld (node, oldCh, start, end) {\n for (var i = start; i < end; i++) {\n var c = oldCh[i];\n if (isDef(c) && sameVnode(node, c)) { return i }\n }\n }\n\n function patchVnode (\n oldVnode,\n vnode,\n insertedVnodeQueue,\n ownerArray,\n index,\n removeOnly\n ) {\n if (oldVnode === vnode) {\n return\n }\n\n if (isDef(vnode.elm) && isDef(ownerArray)) {\n // clone reused vnode\n vnode = ownerArray[index] = cloneVNode(vnode);\n }\n\n var elm = vnode.elm = oldVnode.elm;\n\n if (isTrue(oldVnode.isAsyncPlaceholder)) {\n if (isDef(vnode.asyncFactory.resolved)) {\n hydrate(oldVnode.elm, vnode, insertedVnodeQueue);\n } else {\n vnode.isAsyncPlaceholder = true;\n }\n return\n }\n\n // reuse element for static trees.\n // note we only do this if the vnode is cloned -\n // if the new node is not cloned it means the render functions have been\n // reset by the hot-reload-api and we need to do a proper re-render.\n if (isTrue(vnode.isStatic) &&\n isTrue(oldVnode.isStatic) &&\n vnode.key === oldVnode.key &&\n (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))\n ) {\n vnode.componentInstance = oldVnode.componentInstance;\n return\n }\n\n var i;\n var data = vnode.data;\n if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {\n i(oldVnode, vnode);\n }\n\n var oldCh = oldVnode.children;\n var ch = vnode.children;\n if (isDef(data) && isPatchable(vnode)) {\n for (i = 0; i < cbs.update.length; ++i) { cbs.update[i](oldVnode, vnode); }\n if (isDef(i = data.hook) && isDef(i = i.update)) { i(oldVnode, vnode); }\n }\n if (isUndef(vnode.text)) {\n if (isDef(oldCh) && isDef(ch)) {\n if (oldCh !== ch) { updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly); }\n } else if (isDef(ch)) {\n if (process.env.NODE_ENV !== 'production') {\n checkDuplicateKeys(ch);\n }\n if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }\n addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);\n } else if (isDef(oldCh)) {\n removeVnodes(elm, oldCh, 0, oldCh.length - 1);\n } else if (isDef(oldVnode.text)) {\n nodeOps.setTextContent(elm, '');\n }\n } else if (oldVnode.text !== vnode.text) {\n nodeOps.setTextContent(elm, vnode.text);\n }\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.postpatch)) { i(oldVnode, vnode); }\n }\n }\n\n function invokeInsertHook (vnode, queue, initial) {\n // delay insert hooks for component root nodes, invoke them after the\n // element is really inserted\n if (isTrue(initial) && isDef(vnode.parent)) {\n vnode.parent.data.pendingInsert = queue;\n } else {\n for (var i = 0; i < queue.length; ++i) {\n queue[i].data.hook.insert(queue[i]);\n }\n }\n }\n\n var hydrationBailed = false;\n // list of modules that can skip create hook during hydration because they\n // are already rendered on the client or has no need for initialization\n // Note: style is excluded because it relies on initial clone for future\n // deep updates (#7063).\n var isRenderedModule = makeMap('attrs,class,staticClass,staticStyle,key');\n\n // Note: this is a browser-only function so we can assume elms are DOM nodes.\n function hydrate (elm, vnode, insertedVnodeQueue, inVPre) {\n var i;\n var tag = vnode.tag;\n var data = vnode.data;\n var children = vnode.children;\n inVPre = inVPre || (data && data.pre);\n vnode.elm = elm;\n\n if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {\n vnode.isAsyncPlaceholder = true;\n return true\n }\n // assert node match\n if (process.env.NODE_ENV !== 'production') {\n if (!assertNodeMatch(elm, vnode, inVPre)) {\n return false\n }\n }\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.init)) { i(vnode, true /* hydrating */); }\n if (isDef(i = vnode.componentInstance)) {\n // child component. it should have hydrated its own tree.\n initComponent(vnode, insertedVnodeQueue);\n return true\n }\n }\n if (isDef(tag)) {\n if (isDef(children)) {\n // empty element, allow client to pick up and populate children\n if (!elm.hasChildNodes()) {\n createChildren(vnode, children, insertedVnodeQueue);\n } else {\n // v-html and domProps: innerHTML\n if (isDef(i = data) && isDef(i = i.domProps) && isDef(i = i.innerHTML)) {\n if (i !== elm.innerHTML) {\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' &&\n typeof console !== 'undefined' &&\n !hydrationBailed\n ) {\n hydrationBailed = true;\n console.warn('Parent: ', elm);\n console.warn('server innerHTML: ', i);\n console.warn('client innerHTML: ', elm.innerHTML);\n }\n return false\n }\n } else {\n // iterate and compare children lists\n var childrenMatch = true;\n var childNode = elm.firstChild;\n for (var i$1 = 0; i$1 < children.length; i$1++) {\n if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue, inVPre)) {\n childrenMatch = false;\n break\n }\n childNode = childNode.nextSibling;\n }\n // if childNode is not null, it means the actual childNodes list is\n // longer than the virtual children list.\n if (!childrenMatch || childNode) {\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' &&\n typeof console !== 'undefined' &&\n !hydrationBailed\n ) {\n hydrationBailed = true;\n console.warn('Parent: ', elm);\n console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children);\n }\n return false\n }\n }\n }\n }\n if (isDef(data)) {\n var fullInvoke = false;\n for (var key in data) {\n if (!isRenderedModule(key)) {\n fullInvoke = true;\n invokeCreateHooks(vnode, insertedVnodeQueue);\n break\n }\n }\n if (!fullInvoke && data['class']) {\n // ensure collecting deps for deep class bindings for future updates\n traverse(data['class']);\n }\n }\n } else if (elm.data !== vnode.text) {\n elm.data = vnode.text;\n }\n return true\n }\n\n function assertNodeMatch (node, vnode, inVPre) {\n if (isDef(vnode.tag)) {\n return vnode.tag.indexOf('vue-component') === 0 || (\n !isUnknownElement$$1(vnode, inVPre) &&\n vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase())\n )\n } else {\n return node.nodeType === (vnode.isComment ? 8 : 3)\n }\n }\n\n return function patch (oldVnode, vnode, hydrating, removeOnly) {\n if (isUndef(vnode)) {\n if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); }\n return\n }\n\n var isInitialPatch = false;\n var insertedVnodeQueue = [];\n\n if (isUndef(oldVnode)) {\n // empty mount (likely as component), create new root element\n isInitialPatch = true;\n createElm(vnode, insertedVnodeQueue);\n } else {\n var isRealElement = isDef(oldVnode.nodeType);\n if (!isRealElement && sameVnode(oldVnode, vnode)) {\n // patch existing root node\n patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);\n } else {\n if (isRealElement) {\n // mounting to a real element\n // check if this is server-rendered content and if we can perform\n // a successful hydration.\n if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {\n oldVnode.removeAttribute(SSR_ATTR);\n hydrating = true;\n }\n if (isTrue(hydrating)) {\n if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {\n invokeInsertHook(vnode, insertedVnodeQueue, true);\n return oldVnode\n } else if (process.env.NODE_ENV !== 'production') {\n warn(\n 'The client-side rendered virtual DOM tree is not matching ' +\n 'server-rendered content. This is likely caused by incorrect ' +\n 'HTML markup, for example nesting block-level elements inside ' +\n '<p>, or missing <tbody>. Bailing hydration and performing ' +\n 'full client-side render.'\n );\n }\n }\n // either not server-rendered, or hydration failed.\n // create an empty node and replace it\n oldVnode = emptyNodeAt(oldVnode);\n }\n\n // replacing existing element\n var oldElm = oldVnode.elm;\n var parentElm = nodeOps.parentNode(oldElm);\n\n // create new node\n createElm(\n vnode,\n insertedVnodeQueue,\n // extremely rare edge case: do not insert if old element is in a\n // leaving transition. Only happens when combining transition +\n // keep-alive + HOCs. (#4590)\n oldElm._leaveCb ? null : parentElm,\n nodeOps.nextSibling(oldElm)\n );\n\n // update parent placeholder node element, recursively\n if (isDef(vnode.parent)) {\n var ancestor = vnode.parent;\n var patchable = isPatchable(vnode);\n while (ancestor) {\n for (var i = 0; i < cbs.destroy.length; ++i) {\n cbs.destroy[i](ancestor);\n }\n ancestor.elm = vnode.elm;\n if (patchable) {\n for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {\n cbs.create[i$1](emptyNode, ancestor);\n }\n // #6513\n // invoke insert hooks that may have been merged by create hooks.\n // e.g. for directives that uses the \"inserted\" hook.\n var insert = ancestor.data.hook.insert;\n if (insert.merged) {\n // start at index 1 to avoid re-invoking component mounted hook\n for (var i$2 = 1; i$2 < insert.fns.length; i$2++) {\n insert.fns[i$2]();\n }\n }\n } else {\n registerRef(ancestor);\n }\n ancestor = ancestor.parent;\n }\n }\n\n // destroy old node\n if (isDef(parentElm)) {\n removeVnodes(parentElm, [oldVnode], 0, 0);\n } else if (isDef(oldVnode.tag)) {\n invokeDestroyHook(oldVnode);\n }\n }\n }\n\n invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);\n return vnode.elm\n }\n}\n\n/* */\n\nvar directives = {\n create: updateDirectives,\n update: updateDirectives,\n destroy: function unbindDirectives (vnode) {\n updateDirectives(vnode, emptyNode);\n }\n};\n\nfunction updateDirectives (oldVnode, vnode) {\n if (oldVnode.data.directives || vnode.data.directives) {\n _update(oldVnode, vnode);\n }\n}\n\nfunction _update (oldVnode, vnode) {\n var isCreate = oldVnode === emptyNode;\n var isDestroy = vnode === emptyNode;\n var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context);\n var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context);\n\n var dirsWithInsert = [];\n var dirsWithPostpatch = [];\n\n var key, oldDir, dir;\n for (key in newDirs) {\n oldDir = oldDirs[key];\n dir = newDirs[key];\n if (!oldDir) {\n // new directive, bind\n callHook$1(dir, 'bind', vnode, oldVnode);\n if (dir.def && dir.def.inserted) {\n dirsWithInsert.push(dir);\n }\n } else {\n // existing directive, update\n dir.oldValue = oldDir.value;\n dir.oldArg = oldDir.arg;\n callHook$1(dir, 'update', vnode, oldVnode);\n if (dir.def && dir.def.componentUpdated) {\n dirsWithPostpatch.push(dir);\n }\n }\n }\n\n if (dirsWithInsert.length) {\n var callInsert = function () {\n for (var i = 0; i < dirsWithInsert.length; i++) {\n callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode);\n }\n };\n if (isCreate) {\n mergeVNodeHook(vnode, 'insert', callInsert);\n } else {\n callInsert();\n }\n }\n\n if (dirsWithPostpatch.length) {\n mergeVNodeHook(vnode, 'postpatch', function () {\n for (var i = 0; i < dirsWithPostpatch.length; i++) {\n callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode);\n }\n });\n }\n\n if (!isCreate) {\n for (key in oldDirs) {\n if (!newDirs[key]) {\n // no longer present, unbind\n callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy);\n }\n }\n }\n}\n\nvar emptyModifiers = Object.create(null);\n\nfunction normalizeDirectives$1 (\n dirs,\n vm\n) {\n var res = Object.create(null);\n if (!dirs) {\n // $flow-disable-line\n return res\n }\n var i, dir;\n for (i = 0; i < dirs.length; i++) {\n dir = dirs[i];\n if (!dir.modifiers) {\n // $flow-disable-line\n dir.modifiers = emptyModifiers;\n }\n res[getRawDirName(dir)] = dir;\n dir.def = resolveAsset(vm.$options, 'directives', dir.name, true);\n }\n // $flow-disable-line\n return res\n}\n\nfunction getRawDirName (dir) {\n return dir.rawName || ((dir.name) + \".\" + (Object.keys(dir.modifiers || {}).join('.')))\n}\n\nfunction callHook$1 (dir, hook, vnode, oldVnode, isDestroy) {\n var fn = dir.def && dir.def[hook];\n if (fn) {\n try {\n fn(vnode.elm, dir, vnode, oldVnode, isDestroy);\n } catch (e) {\n handleError(e, vnode.context, (\"directive \" + (dir.name) + \" \" + hook + \" hook\"));\n }\n }\n}\n\nvar baseModules = [\n ref,\n directives\n];\n\n/* */\n\nfunction updateAttrs (oldVnode, vnode) {\n var opts = vnode.componentOptions;\n if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {\n return\n }\n if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {\n return\n }\n var key, cur, old;\n var elm = vnode.elm;\n var oldAttrs = oldVnode.data.attrs || {};\n var attrs = vnode.data.attrs || {};\n // clone observed objects, as the user probably wants to mutate it\n if (isDef(attrs.__ob__)) {\n attrs = vnode.data.attrs = extend({}, attrs);\n }\n\n for (key in attrs) {\n cur = attrs[key];\n old = oldAttrs[key];\n if (old !== cur) {\n setAttr(elm, key, cur);\n }\n }\n // #4391: in IE9, setting type can reset value for input[type=radio]\n // #6666: IE/Edge forces progress value down to 1 before setting a max\n /* istanbul ignore if */\n if ((isIE || isEdge) && attrs.value !== oldAttrs.value) {\n setAttr(elm, 'value', attrs.value);\n }\n for (key in oldAttrs) {\n if (isUndef(attrs[key])) {\n if (isXlink(key)) {\n elm.removeAttributeNS(xlinkNS, getXlinkProp(key));\n } else if (!isEnumeratedAttr(key)) {\n elm.removeAttribute(key);\n }\n }\n }\n}\n\nfunction setAttr (el, key, value) {\n if (el.tagName.indexOf('-') > -1) {\n baseSetAttr(el, key, value);\n } else if (isBooleanAttr(key)) {\n // set attribute for blank value\n // e.g. <option disabled>Select one</option>\n if (isFalsyAttrValue(value)) {\n el.removeAttribute(key);\n } else {\n // technically allowfullscreen is a boolean attribute for <iframe>,\n // but Flash expects a value of \"true\" when used on <embed> tag\n value = key === 'allowfullscreen' && el.tagName === 'EMBED'\n ? 'true'\n : key;\n el.setAttribute(key, value);\n }\n } else if (isEnumeratedAttr(key)) {\n el.setAttribute(key, convertEnumeratedValue(key, value));\n } else if (isXlink(key)) {\n if (isFalsyAttrValue(value)) {\n el.removeAttributeNS(xlinkNS, getXlinkProp(key));\n } else {\n el.setAttributeNS(xlinkNS, key, value);\n }\n } else {\n baseSetAttr(el, key, value);\n }\n}\n\nfunction baseSetAttr (el, key, value) {\n if (isFalsyAttrValue(value)) {\n el.removeAttribute(key);\n } else {\n // #7138: IE10 & 11 fires input event when setting placeholder on\n // <textarea>... block the first input event and remove the blocker\n // immediately.\n /* istanbul ignore if */\n if (\n isIE && !isIE9 &&\n el.tagName === 'TEXTAREA' &&\n key === 'placeholder' && value !== '' && !el.__ieph\n ) {\n var blocker = function (e) {\n e.stopImmediatePropagation();\n el.removeEventListener('input', blocker);\n };\n el.addEventListener('input', blocker);\n // $flow-disable-line\n el.__ieph = true; /* IE placeholder patched */\n }\n el.setAttribute(key, value);\n }\n}\n\nvar attrs = {\n create: updateAttrs,\n update: updateAttrs\n};\n\n/* */\n\nfunction updateClass (oldVnode, vnode) {\n var el = vnode.elm;\n var data = vnode.data;\n var oldData = oldVnode.data;\n if (\n isUndef(data.staticClass) &&\n isUndef(data.class) && (\n isUndef(oldData) || (\n isUndef(oldData.staticClass) &&\n isUndef(oldData.class)\n )\n )\n ) {\n return\n }\n\n var cls = genClassForVnode(vnode);\n\n // handle transition classes\n var transitionClass = el._transitionClasses;\n if (isDef(transitionClass)) {\n cls = concat(cls, stringifyClass(transitionClass));\n }\n\n // set the class\n if (cls !== el._prevClass) {\n el.setAttribute('class', cls);\n el._prevClass = cls;\n }\n}\n\nvar klass = {\n create: updateClass,\n update: updateClass\n};\n\n/* */\n\n/* */\n\n/* */\n\n/* */\n\n// in some cases, the event used has to be determined at runtime\n// so we used some reserved tokens during compile.\nvar RANGE_TOKEN = '__r';\nvar CHECKBOX_RADIO_TOKEN = '__c';\n\n/* */\n\n// normalize v-model event tokens that can only be determined at runtime.\n// it's important to place the event as the first in the array because\n// the whole point is ensuring the v-model callback gets called before\n// user-attached handlers.\nfunction normalizeEvents (on) {\n /* istanbul ignore if */\n if (isDef(on[RANGE_TOKEN])) {\n // IE input[type=range] only supports `change` event\n var event = isIE ? 'change' : 'input';\n on[event] = [].concat(on[RANGE_TOKEN], on[event] || []);\n delete on[RANGE_TOKEN];\n }\n // This was originally intended to fix #4521 but no longer necessary\n // after 2.5. Keeping it for backwards compat with generated code from < 2.4\n /* istanbul ignore if */\n if (isDef(on[CHECKBOX_RADIO_TOKEN])) {\n on.change = [].concat(on[CHECKBOX_RADIO_TOKEN], on.change || []);\n delete on[CHECKBOX_RADIO_TOKEN];\n }\n}\n\nvar target$1;\n\nfunction createOnceHandler$1 (event, handler, capture) {\n var _target = target$1; // save current target element in closure\n return function onceHandler () {\n var res = handler.apply(null, arguments);\n if (res !== null) {\n remove$2(event, onceHandler, capture, _target);\n }\n }\n}\n\n// #9446: Firefox <= 53 (in particular, ESR 52) has incorrect Event.timeStamp\n// implementation and does not fire microtasks in between event propagation, so\n// safe to exclude.\nvar useMicrotaskFix = isUsingMicroTask && !(isFF && Number(isFF[1]) <= 53);\n\nfunction add$1 (\n name,\n handler,\n capture,\n passive\n) {\n // async edge case #6566: inner click event triggers patch, event handler\n // attached to outer element during patch, and triggered again. This\n // happens because browsers fire microtask ticks between event propagation.\n // the solution is simple: we save the timestamp when a handler is attached,\n // and the handler would only fire if the event passed to it was fired\n // AFTER it was attached.\n if (useMicrotaskFix) {\n var attachedTimestamp = currentFlushTimestamp;\n var original = handler;\n handler = original._wrapper = function (e) {\n if (\n // no bubbling, should always fire.\n // this is just a safety net in case event.timeStamp is unreliable in\n // certain weird environments...\n e.target === e.currentTarget ||\n // event is fired after handler attachment\n e.timeStamp >= attachedTimestamp ||\n // bail for environments that have buggy event.timeStamp implementations\n // #9462 iOS 9 bug: event.timeStamp is 0 after history.pushState\n // #9681 QtWebEngine event.timeStamp is negative value\n e.timeStamp <= 0 ||\n // #9448 bail if event is fired in another document in a multi-page\n // electron/nw.js app, since event.timeStamp will be using a different\n // starting reference\n e.target.ownerDocument !== document\n ) {\n return original.apply(this, arguments)\n }\n };\n }\n target$1.addEventListener(\n name,\n handler,\n supportsPassive\n ? { capture: capture, passive: passive }\n : capture\n );\n}\n\nfunction remove$2 (\n name,\n handler,\n capture,\n _target\n) {\n (_target || target$1).removeEventListener(\n name,\n handler._wrapper || handler,\n capture\n );\n}\n\nfunction updateDOMListeners (oldVnode, vnode) {\n if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {\n return\n }\n var on = vnode.data.on || {};\n var oldOn = oldVnode.data.on || {};\n target$1 = vnode.elm;\n normalizeEvents(on);\n updateListeners(on, oldOn, add$1, remove$2, createOnceHandler$1, vnode.context);\n target$1 = undefined;\n}\n\nvar events = {\n create: updateDOMListeners,\n update: updateDOMListeners\n};\n\n/* */\n\nvar svgContainer;\n\nfunction updateDOMProps (oldVnode, vnode) {\n if (isUndef(oldVnode.data.domProps) && isUndef(vnode.data.domProps)) {\n return\n }\n var key, cur;\n var elm = vnode.elm;\n var oldProps = oldVnode.data.domProps || {};\n var props = vnode.data.domProps || {};\n // clone observed objects, as the user probably wants to mutate it\n if (isDef(props.__ob__)) {\n props = vnode.data.domProps = extend({}, props);\n }\n\n for (key in oldProps) {\n if (!(key in props)) {\n elm[key] = '';\n }\n }\n\n for (key in props) {\n cur = props[key];\n // ignore children if the node has textContent or innerHTML,\n // as these will throw away existing DOM nodes and cause removal errors\n // on subsequent patches (#3360)\n if (key === 'textContent' || key === 'innerHTML') {\n if (vnode.children) { vnode.children.length = 0; }\n if (cur === oldProps[key]) { continue }\n // #6601 work around Chrome version <= 55 bug where single textNode\n // replaced by innerHTML/textContent retains its parentNode property\n if (elm.childNodes.length === 1) {\n elm.removeChild(elm.childNodes[0]);\n }\n }\n\n if (key === 'value' && elm.tagName !== 'PROGRESS') {\n // store value as _value as well since\n // non-string values will be stringified\n elm._value = cur;\n // avoid resetting cursor position when value is the same\n var strCur = isUndef(cur) ? '' : String(cur);\n if (shouldUpdateValue(elm, strCur)) {\n elm.value = strCur;\n }\n } else if (key === 'innerHTML' && isSVG(elm.tagName) && isUndef(elm.innerHTML)) {\n // IE doesn't support innerHTML for SVG elements\n svgContainer = svgContainer || document.createElement('div');\n svgContainer.innerHTML = \"<svg>\" + cur + \"</svg>\";\n var svg = svgContainer.firstChild;\n while (elm.firstChild) {\n elm.removeChild(elm.firstChild);\n }\n while (svg.firstChild) {\n elm.appendChild(svg.firstChild);\n }\n } else if (\n // skip the update if old and new VDOM state is the same.\n // `value` is handled separately because the DOM value may be temporarily\n // out of sync with VDOM state due to focus, composition and modifiers.\n // This #4521 by skipping the unnecesarry `checked` update.\n cur !== oldProps[key]\n ) {\n // some property updates can throw\n // e.g. `value` on <progress> w/ non-finite value\n try {\n elm[key] = cur;\n } catch (e) {}\n }\n }\n}\n\n// check platforms/web/util/attrs.js acceptValue\n\n\nfunction shouldUpdateValue (elm, checkVal) {\n return (!elm.composing && (\n elm.tagName === 'OPTION' ||\n isNotInFocusAndDirty(elm, checkVal) ||\n isDirtyWithModifiers(elm, checkVal)\n ))\n}\n\nfunction isNotInFocusAndDirty (elm, checkVal) {\n // return true when textbox (.number and .trim) loses focus and its value is\n // not equal to the updated value\n var notInFocus = true;\n // #6157\n // work around IE bug when accessing document.activeElement in an iframe\n try { notInFocus = document.activeElement !== elm; } catch (e) {}\n return notInFocus && elm.value !== checkVal\n}\n\nfunction isDirtyWithModifiers (elm, newVal) {\n var value = elm.value;\n var modifiers = elm._vModifiers; // injected by v-model runtime\n if (isDef(modifiers)) {\n if (modifiers.number) {\n return toNumber(value) !== toNumber(newVal)\n }\n if (modifiers.trim) {\n return value.trim() !== newVal.trim()\n }\n }\n return value !== newVal\n}\n\nvar domProps = {\n create: updateDOMProps,\n update: updateDOMProps\n};\n\n/* */\n\nvar parseStyleText = cached(function (cssText) {\n var res = {};\n var listDelimiter = /;(?![^(]*\\))/g;\n var propertyDelimiter = /:(.+)/;\n cssText.split(listDelimiter).forEach(function (item) {\n if (item) {\n var tmp = item.split(propertyDelimiter);\n tmp.length > 1 && (res[tmp[0].trim()] = tmp[1].trim());\n }\n });\n return res\n});\n\n// merge static and dynamic style data on the same vnode\nfunction normalizeStyleData (data) {\n var style = normalizeStyleBinding(data.style);\n // static style is pre-processed into an object during compilation\n // and is always a fresh object, so it's safe to merge into it\n return data.staticStyle\n ? extend(data.staticStyle, style)\n : style\n}\n\n// normalize possible array / string values into Object\nfunction normalizeStyleBinding (bindingStyle) {\n if (Array.isArray(bindingStyle)) {\n return toObject(bindingStyle)\n }\n if (typeof bindingStyle === 'string') {\n return parseStyleText(bindingStyle)\n }\n return bindingStyle\n}\n\n/**\n * parent component style should be after child's\n * so that parent component's style could override it\n */\nfunction getStyle (vnode, checkChild) {\n var res = {};\n var styleData;\n\n if (checkChild) {\n var childNode = vnode;\n while (childNode.componentInstance) {\n childNode = childNode.componentInstance._vnode;\n if (\n childNode && childNode.data &&\n (styleData = normalizeStyleData(childNode.data))\n ) {\n extend(res, styleData);\n }\n }\n }\n\n if ((styleData = normalizeStyleData(vnode.data))) {\n extend(res, styleData);\n }\n\n var parentNode = vnode;\n while ((parentNode = parentNode.parent)) {\n if (parentNode.data && (styleData = normalizeStyleData(parentNode.data))) {\n extend(res, styleData);\n }\n }\n return res\n}\n\n/* */\n\nvar cssVarRE = /^--/;\nvar importantRE = /\\s*!important$/;\nvar setProp = function (el, name, val) {\n /* istanbul ignore if */\n if (cssVarRE.test(name)) {\n el.style.setProperty(name, val);\n } else if (importantRE.test(val)) {\n el.style.setProperty(hyphenate(name), val.replace(importantRE, ''), 'important');\n } else {\n var normalizedName = normalize(name);\n if (Array.isArray(val)) {\n // Support values array created by autoprefixer, e.g.\n // {display: [\"-webkit-box\", \"-ms-flexbox\", \"flex\"]}\n // Set them one by one, and the browser will only set those it can recognize\n for (var i = 0, len = val.length; i < len; i++) {\n el.style[normalizedName] = val[i];\n }\n } else {\n el.style[normalizedName] = val;\n }\n }\n};\n\nvar vendorNames = ['Webkit', 'Moz', 'ms'];\n\nvar emptyStyle;\nvar normalize = cached(function (prop) {\n emptyStyle = emptyStyle || document.createElement('div').style;\n prop = camelize(prop);\n if (prop !== 'filter' && (prop in emptyStyle)) {\n return prop\n }\n var capName = prop.charAt(0).toUpperCase() + prop.slice(1);\n for (var i = 0; i < vendorNames.length; i++) {\n var name = vendorNames[i] + capName;\n if (name in emptyStyle) {\n return name\n }\n }\n});\n\nfunction updateStyle (oldVnode, vnode) {\n var data = vnode.data;\n var oldData = oldVnode.data;\n\n if (isUndef(data.staticStyle) && isUndef(data.style) &&\n isUndef(oldData.staticStyle) && isUndef(oldData.style)\n ) {\n return\n }\n\n var cur, name;\n var el = vnode.elm;\n var oldStaticStyle = oldData.staticStyle;\n var oldStyleBinding = oldData.normalizedStyle || oldData.style || {};\n\n // if static style exists, stylebinding already merged into it when doing normalizeStyleData\n var oldStyle = oldStaticStyle || oldStyleBinding;\n\n var style = normalizeStyleBinding(vnode.data.style) || {};\n\n // store normalized style under a different key for next diff\n // make sure to clone it if it's reactive, since the user likely wants\n // to mutate it.\n vnode.data.normalizedStyle = isDef(style.__ob__)\n ? extend({}, style)\n : style;\n\n var newStyle = getStyle(vnode, true);\n\n for (name in oldStyle) {\n if (isUndef(newStyle[name])) {\n setProp(el, name, '');\n }\n }\n for (name in newStyle) {\n cur = newStyle[name];\n if (cur !== oldStyle[name]) {\n // ie9 setting to null has no effect, must use empty string\n setProp(el, name, cur == null ? '' : cur);\n }\n }\n}\n\nvar style = {\n create: updateStyle,\n update: updateStyle\n};\n\n/* */\n\nvar whitespaceRE = /\\s+/;\n\n/**\n * Add class with compatibility for SVG since classList is not supported on\n * SVG elements in IE\n */\nfunction addClass (el, cls) {\n /* istanbul ignore if */\n if (!cls || !(cls = cls.trim())) {\n return\n }\n\n /* istanbul ignore else */\n if (el.classList) {\n if (cls.indexOf(' ') > -1) {\n cls.split(whitespaceRE).forEach(function (c) { return el.classList.add(c); });\n } else {\n el.classList.add(cls);\n }\n } else {\n var cur = \" \" + (el.getAttribute('class') || '') + \" \";\n if (cur.indexOf(' ' + cls + ' ') < 0) {\n el.setAttribute('class', (cur + cls).trim());\n }\n }\n}\n\n/**\n * Remove class with compatibility for SVG since classList is not supported on\n * SVG elements in IE\n */\nfunction removeClass (el, cls) {\n /* istanbul ignore if */\n if (!cls || !(cls = cls.trim())) {\n return\n }\n\n /* istanbul ignore else */\n if (el.classList) {\n if (cls.indexOf(' ') > -1) {\n cls.split(whitespaceRE).forEach(function (c) { return el.classList.remove(c); });\n } else {\n el.classList.remove(cls);\n }\n if (!el.classList.length) {\n el.removeAttribute('class');\n }\n } else {\n var cur = \" \" + (el.getAttribute('class') || '') + \" \";\n var tar = ' ' + cls + ' ';\n while (cur.indexOf(tar) >= 0) {\n cur = cur.replace(tar, ' ');\n }\n cur = cur.trim();\n if (cur) {\n el.setAttribute('class', cur);\n } else {\n el.removeAttribute('class');\n }\n }\n}\n\n/* */\n\nfunction resolveTransition (def$$1) {\n if (!def$$1) {\n return\n }\n /* istanbul ignore else */\n if (typeof def$$1 === 'object') {\n var res = {};\n if (def$$1.css !== false) {\n extend(res, autoCssTransition(def$$1.name || 'v'));\n }\n extend(res, def$$1);\n return res\n } else if (typeof def$$1 === 'string') {\n return autoCssTransition(def$$1)\n }\n}\n\nvar autoCssTransition = cached(function (name) {\n return {\n enterClass: (name + \"-enter\"),\n enterToClass: (name + \"-enter-to\"),\n enterActiveClass: (name + \"-enter-active\"),\n leaveClass: (name + \"-leave\"),\n leaveToClass: (name + \"-leave-to\"),\n leaveActiveClass: (name + \"-leave-active\")\n }\n});\n\nvar hasTransition = inBrowser && !isIE9;\nvar TRANSITION = 'transition';\nvar ANIMATION = 'animation';\n\n// Transition property/event sniffing\nvar transitionProp = 'transition';\nvar transitionEndEvent = 'transitionend';\nvar animationProp = 'animation';\nvar animationEndEvent = 'animationend';\nif (hasTransition) {\n /* istanbul ignore if */\n if (window.ontransitionend === undefined &&\n window.onwebkittransitionend !== undefined\n ) {\n transitionProp = 'WebkitTransition';\n transitionEndEvent = 'webkitTransitionEnd';\n }\n if (window.onanimationend === undefined &&\n window.onwebkitanimationend !== undefined\n ) {\n animationProp = 'WebkitAnimation';\n animationEndEvent = 'webkitAnimationEnd';\n }\n}\n\n// binding to window is necessary to make hot reload work in IE in strict mode\nvar raf = inBrowser\n ? window.requestAnimationFrame\n ? window.requestAnimationFrame.bind(window)\n : setTimeout\n : /* istanbul ignore next */ function (fn) { return fn(); };\n\nfunction nextFrame (fn) {\n raf(function () {\n raf(fn);\n });\n}\n\nfunction addTransitionClass (el, cls) {\n var transitionClasses = el._transitionClasses || (el._transitionClasses = []);\n if (transitionClasses.indexOf(cls) < 0) {\n transitionClasses.push(cls);\n addClass(el, cls);\n }\n}\n\nfunction removeTransitionClass (el, cls) {\n if (el._transitionClasses) {\n remove(el._transitionClasses, cls);\n }\n removeClass(el, cls);\n}\n\nfunction whenTransitionEnds (\n el,\n expectedType,\n cb\n) {\n var ref = getTransitionInfo(el, expectedType);\n var type = ref.type;\n var timeout = ref.timeout;\n var propCount = ref.propCount;\n if (!type) { return cb() }\n var event = type === TRANSITION ? transitionEndEvent : animationEndEvent;\n var ended = 0;\n var end = function () {\n el.removeEventListener(event, onEnd);\n cb();\n };\n var onEnd = function (e) {\n if (e.target === el) {\n if (++ended >= propCount) {\n end();\n }\n }\n };\n setTimeout(function () {\n if (ended < propCount) {\n end();\n }\n }, timeout + 1);\n el.addEventListener(event, onEnd);\n}\n\nvar transformRE = /\\b(transform|all)(,|$)/;\n\nfunction getTransitionInfo (el, expectedType) {\n var styles = window.getComputedStyle(el);\n // JSDOM may return undefined for transition properties\n var transitionDelays = (styles[transitionProp + 'Delay'] || '').split(', ');\n var transitionDurations = (styles[transitionProp + 'Duration'] || '').split(', ');\n var transitionTimeout = getTimeout(transitionDelays, transitionDurations);\n var animationDelays = (styles[animationProp + 'Delay'] || '').split(', ');\n var animationDurations = (styles[animationProp + 'Duration'] || '').split(', ');\n var animationTimeout = getTimeout(animationDelays, animationDurations);\n\n var type;\n var timeout = 0;\n var propCount = 0;\n /* istanbul ignore if */\n if (expectedType === TRANSITION) {\n if (transitionTimeout > 0) {\n type = TRANSITION;\n timeout = transitionTimeout;\n propCount = transitionDurations.length;\n }\n } else if (expectedType === ANIMATION) {\n if (animationTimeout > 0) {\n type = ANIMATION;\n timeout = animationTimeout;\n propCount = animationDurations.length;\n }\n } else {\n timeout = Math.max(transitionTimeout, animationTimeout);\n type = timeout > 0\n ? transitionTimeout > animationTimeout\n ? TRANSITION\n : ANIMATION\n : null;\n propCount = type\n ? type === TRANSITION\n ? transitionDurations.length\n : animationDurations.length\n : 0;\n }\n var hasTransform =\n type === TRANSITION &&\n transformRE.test(styles[transitionProp + 'Property']);\n return {\n type: type,\n timeout: timeout,\n propCount: propCount,\n hasTransform: hasTransform\n }\n}\n\nfunction getTimeout (delays, durations) {\n /* istanbul ignore next */\n while (delays.length < durations.length) {\n delays = delays.concat(delays);\n }\n\n return Math.max.apply(null, durations.map(function (d, i) {\n return toMs(d) + toMs(delays[i])\n }))\n}\n\n// Old versions of Chromium (below 61.0.3163.100) formats floating pointer numbers\n// in a locale-dependent way, using a comma instead of a dot.\n// If comma is not replaced with a dot, the input will be rounded down (i.e. acting\n// as a floor function) causing unexpected behaviors\nfunction toMs (s) {\n return Number(s.slice(0, -1).replace(',', '.')) * 1000\n}\n\n/* */\n\nfunction enter (vnode, toggleDisplay) {\n var el = vnode.elm;\n\n // call leave callback now\n if (isDef(el._leaveCb)) {\n el._leaveCb.cancelled = true;\n el._leaveCb();\n }\n\n var data = resolveTransition(vnode.data.transition);\n if (isUndef(data)) {\n return\n }\n\n /* istanbul ignore if */\n if (isDef(el._enterCb) || el.nodeType !== 1) {\n return\n }\n\n var css = data.css;\n var type = data.type;\n var enterClass = data.enterClass;\n var enterToClass = data.enterToClass;\n var enterActiveClass = data.enterActiveClass;\n var appearClass = data.appearClass;\n var appearToClass = data.appearToClass;\n var appearActiveClass = data.appearActiveClass;\n var beforeEnter = data.beforeEnter;\n var enter = data.enter;\n var afterEnter = data.afterEnter;\n var enterCancelled = data.enterCancelled;\n var beforeAppear = data.beforeAppear;\n var appear = data.appear;\n var afterAppear = data.afterAppear;\n var appearCancelled = data.appearCancelled;\n var duration = data.duration;\n\n // activeInstance will always be the <transition> component managing this\n // transition. One edge case to check is when the <transition> is placed\n // as the root node of a child component. In that case we need to check\n // <transition>'s parent for appear check.\n var context = activeInstance;\n var transitionNode = activeInstance.$vnode;\n while (transitionNode && transitionNode.parent) {\n context = transitionNode.context;\n transitionNode = transitionNode.parent;\n }\n\n var isAppear = !context._isMounted || !vnode.isRootInsert;\n\n if (isAppear && !appear && appear !== '') {\n return\n }\n\n var startClass = isAppear && appearClass\n ? appearClass\n : enterClass;\n var activeClass = isAppear && appearActiveClass\n ? appearActiveClass\n : enterActiveClass;\n var toClass = isAppear && appearToClass\n ? appearToClass\n : enterToClass;\n\n var beforeEnterHook = isAppear\n ? (beforeAppear || beforeEnter)\n : beforeEnter;\n var enterHook = isAppear\n ? (typeof appear === 'function' ? appear : enter)\n : enter;\n var afterEnterHook = isAppear\n ? (afterAppear || afterEnter)\n : afterEnter;\n var enterCancelledHook = isAppear\n ? (appearCancelled || enterCancelled)\n : enterCancelled;\n\n var explicitEnterDuration = toNumber(\n isObject(duration)\n ? duration.enter\n : duration\n );\n\n if (process.env.NODE_ENV !== 'production' && explicitEnterDuration != null) {\n checkDuration(explicitEnterDuration, 'enter', vnode);\n }\n\n var expectsCSS = css !== false && !isIE9;\n var userWantsControl = getHookArgumentsLength(enterHook);\n\n var cb = el._enterCb = once(function () {\n if (expectsCSS) {\n removeTransitionClass(el, toClass);\n removeTransitionClass(el, activeClass);\n }\n if (cb.cancelled) {\n if (expectsCSS) {\n removeTransitionClass(el, startClass);\n }\n enterCancelledHook && enterCancelledHook(el);\n } else {\n afterEnterHook && afterEnterHook(el);\n }\n el._enterCb = null;\n });\n\n if (!vnode.data.show) {\n // remove pending leave element on enter by injecting an insert hook\n mergeVNodeHook(vnode, 'insert', function () {\n var parent = el.parentNode;\n var pendingNode = parent && parent._pending && parent._pending[vnode.key];\n if (pendingNode &&\n pendingNode.tag === vnode.tag &&\n pendingNode.elm._leaveCb\n ) {\n pendingNode.elm._leaveCb();\n }\n enterHook && enterHook(el, cb);\n });\n }\n\n // start enter transition\n beforeEnterHook && beforeEnterHook(el);\n if (expectsCSS) {\n addTransitionClass(el, startClass);\n addTransitionClass(el, activeClass);\n nextFrame(function () {\n removeTransitionClass(el, startClass);\n if (!cb.cancelled) {\n addTransitionClass(el, toClass);\n if (!userWantsControl) {\n if (isValidDuration(explicitEnterDuration)) {\n setTimeout(cb, explicitEnterDuration);\n } else {\n whenTransitionEnds(el, type, cb);\n }\n }\n }\n });\n }\n\n if (vnode.data.show) {\n toggleDisplay && toggleDisplay();\n enterHook && enterHook(el, cb);\n }\n\n if (!expectsCSS && !userWantsControl) {\n cb();\n }\n}\n\nfunction leave (vnode, rm) {\n var el = vnode.elm;\n\n // call enter callback now\n if (isDef(el._enterCb)) {\n el._enterCb.cancelled = true;\n el._enterCb();\n }\n\n var data = resolveTransition(vnode.data.transition);\n if (isUndef(data) || el.nodeType !== 1) {\n return rm()\n }\n\n /* istanbul ignore if */\n if (isDef(el._leaveCb)) {\n return\n }\n\n var css = data.css;\n var type = data.type;\n var leaveClass = data.leaveClass;\n var leaveToClass = data.leaveToClass;\n var leaveActiveClass = data.leaveActiveClass;\n var beforeLeave = data.beforeLeave;\n var leave = data.leave;\n var afterLeave = data.afterLeave;\n var leaveCancelled = data.leaveCancelled;\n var delayLeave = data.delayLeave;\n var duration = data.duration;\n\n var expectsCSS = css !== false && !isIE9;\n var userWantsControl = getHookArgumentsLength(leave);\n\n var explicitLeaveDuration = toNumber(\n isObject(duration)\n ? duration.leave\n : duration\n );\n\n if (process.env.NODE_ENV !== 'production' && isDef(explicitLeaveDuration)) {\n checkDuration(explicitLeaveDuration, 'leave', vnode);\n }\n\n var cb = el._leaveCb = once(function () {\n if (el.parentNode && el.parentNode._pending) {\n el.parentNode._pending[vnode.key] = null;\n }\n if (expectsCSS) {\n removeTransitionClass(el, leaveToClass);\n removeTransitionClass(el, leaveActiveClass);\n }\n if (cb.cancelled) {\n if (expectsCSS) {\n removeTransitionClass(el, leaveClass);\n }\n leaveCancelled && leaveCancelled(el);\n } else {\n rm();\n afterLeave && afterLeave(el);\n }\n el._leaveCb = null;\n });\n\n if (delayLeave) {\n delayLeave(performLeave);\n } else {\n performLeave();\n }\n\n function performLeave () {\n // the delayed leave may have already been cancelled\n if (cb.cancelled) {\n return\n }\n // record leaving element\n if (!vnode.data.show && el.parentNode) {\n (el.parentNode._pending || (el.parentNode._pending = {}))[(vnode.key)] = vnode;\n }\n beforeLeave && beforeLeave(el);\n if (expectsCSS) {\n addTransitionClass(el, leaveClass);\n addTransitionClass(el, leaveActiveClass);\n nextFrame(function () {\n removeTransitionClass(el, leaveClass);\n if (!cb.cancelled) {\n addTransitionClass(el, leaveToClass);\n if (!userWantsControl) {\n if (isValidDuration(explicitLeaveDuration)) {\n setTimeout(cb, explicitLeaveDuration);\n } else {\n whenTransitionEnds(el, type, cb);\n }\n }\n }\n });\n }\n leave && leave(el, cb);\n if (!expectsCSS && !userWantsControl) {\n cb();\n }\n }\n}\n\n// only used in dev mode\nfunction checkDuration (val, name, vnode) {\n if (typeof val !== 'number') {\n warn(\n \"<transition> explicit \" + name + \" duration is not a valid number - \" +\n \"got \" + (JSON.stringify(val)) + \".\",\n vnode.context\n );\n } else if (isNaN(val)) {\n warn(\n \"<transition> explicit \" + name + \" duration is NaN - \" +\n 'the duration expression might be incorrect.',\n vnode.context\n );\n }\n}\n\nfunction isValidDuration (val) {\n return typeof val === 'number' && !isNaN(val)\n}\n\n/**\n * Normalize a transition hook's argument length. The hook may be:\n * - a merged hook (invoker) with the original in .fns\n * - a wrapped component method (check ._length)\n * - a plain function (.length)\n */\nfunction getHookArgumentsLength (fn) {\n if (isUndef(fn)) {\n return false\n }\n var invokerFns = fn.fns;\n if (isDef(invokerFns)) {\n // invoker\n return getHookArgumentsLength(\n Array.isArray(invokerFns)\n ? invokerFns[0]\n : invokerFns\n )\n } else {\n return (fn._length || fn.length) > 1\n }\n}\n\nfunction _enter (_, vnode) {\n if (vnode.data.show !== true) {\n enter(vnode);\n }\n}\n\nvar transition = inBrowser ? {\n create: _enter,\n activate: _enter,\n remove: function remove$$1 (vnode, rm) {\n /* istanbul ignore else */\n if (vnode.data.show !== true) {\n leave(vnode, rm);\n } else {\n rm();\n }\n }\n} : {};\n\nvar platformModules = [\n attrs,\n klass,\n events,\n domProps,\n style,\n transition\n];\n\n/* */\n\n// the directive module should be applied last, after all\n// built-in modules have been applied.\nvar modules = platformModules.concat(baseModules);\n\nvar patch = createPatchFunction({ nodeOps: nodeOps, modules: modules });\n\n/**\n * Not type checking this file because flow doesn't like attaching\n * properties to Elements.\n */\n\n/* istanbul ignore if */\nif (isIE9) {\n // http://www.matts411.com/post/internet-explorer-9-oninput/\n document.addEventListener('selectionchange', function () {\n var el = document.activeElement;\n if (el && el.vmodel) {\n trigger(el, 'input');\n }\n });\n}\n\nvar directive = {\n inserted: function inserted (el, binding, vnode, oldVnode) {\n if (vnode.tag === 'select') {\n // #6903\n if (oldVnode.elm && !oldVnode.elm._vOptions) {\n mergeVNodeHook(vnode, 'postpatch', function () {\n directive.componentUpdated(el, binding, vnode);\n });\n } else {\n setSelected(el, binding, vnode.context);\n }\n el._vOptions = [].map.call(el.options, getValue);\n } else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {\n el._vModifiers = binding.modifiers;\n if (!binding.modifiers.lazy) {\n el.addEventListener('compositionstart', onCompositionStart);\n el.addEventListener('compositionend', onCompositionEnd);\n // Safari < 10.2 & UIWebView doesn't fire compositionend when\n // switching focus before confirming composition choice\n // this also fixes the issue where some browsers e.g. iOS Chrome\n // fires \"change\" instead of \"input\" on autocomplete.\n el.addEventListener('change', onCompositionEnd);\n /* istanbul ignore if */\n if (isIE9) {\n el.vmodel = true;\n }\n }\n }\n },\n\n componentUpdated: function componentUpdated (el, binding, vnode) {\n if (vnode.tag === 'select') {\n setSelected(el, binding, vnode.context);\n // in case the options rendered by v-for have changed,\n // it's possible that the value is out-of-sync with the rendered options.\n // detect such cases and filter out values that no longer has a matching\n // option in the DOM.\n var prevOptions = el._vOptions;\n var curOptions = el._vOptions = [].map.call(el.options, getValue);\n if (curOptions.some(function (o, i) { return !looseEqual(o, prevOptions[i]); })) {\n // trigger change event if\n // no matching option found for at least one value\n var needReset = el.multiple\n ? binding.value.some(function (v) { return hasNoMatchingOption(v, curOptions); })\n : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions);\n if (needReset) {\n trigger(el, 'change');\n }\n }\n }\n }\n};\n\nfunction setSelected (el, binding, vm) {\n actuallySetSelected(el, binding, vm);\n /* istanbul ignore if */\n if (isIE || isEdge) {\n setTimeout(function () {\n actuallySetSelected(el, binding, vm);\n }, 0);\n }\n}\n\nfunction actuallySetSelected (el, binding, vm) {\n var value = binding.value;\n var isMultiple = el.multiple;\n if (isMultiple && !Array.isArray(value)) {\n process.env.NODE_ENV !== 'production' && warn(\n \"<select multiple v-model=\\\"\" + (binding.expression) + \"\\\"> \" +\n \"expects an Array value for its binding, but got \" + (Object.prototype.toString.call(value).slice(8, -1)),\n vm\n );\n return\n }\n var selected, option;\n for (var i = 0, l = el.options.length; i < l; i++) {\n option = el.options[i];\n if (isMultiple) {\n selected = looseIndexOf(value, getValue(option)) > -1;\n if (option.selected !== selected) {\n option.selected = selected;\n }\n } else {\n if (looseEqual(getValue(option), value)) {\n if (el.selectedIndex !== i) {\n el.selectedIndex = i;\n }\n return\n }\n }\n }\n if (!isMultiple) {\n el.selectedIndex = -1;\n }\n}\n\nfunction hasNoMatchingOption (value, options) {\n return options.every(function (o) { return !looseEqual(o, value); })\n}\n\nfunction getValue (option) {\n return '_value' in option\n ? option._value\n : option.value\n}\n\nfunction onCompositionStart (e) {\n e.target.composing = true;\n}\n\nfunction onCompositionEnd (e) {\n // prevent triggering an input event for no reason\n if (!e.target.composing) { return }\n e.target.composing = false;\n trigger(e.target, 'input');\n}\n\nfunction trigger (el, type) {\n var e = document.createEvent('HTMLEvents');\n e.initEvent(type, true, true);\n el.dispatchEvent(e);\n}\n\n/* */\n\n// recursively search for possible transition defined inside the component root\nfunction locateNode (vnode) {\n return vnode.componentInstance && (!vnode.data || !vnode.data.transition)\n ? locateNode(vnode.componentInstance._vnode)\n : vnode\n}\n\nvar show = {\n bind: function bind (el, ref, vnode) {\n var value = ref.value;\n\n vnode = locateNode(vnode);\n var transition$$1 = vnode.data && vnode.data.transition;\n var originalDisplay = el.__vOriginalDisplay =\n el.style.display === 'none' ? '' : el.style.display;\n if (value && transition$$1) {\n vnode.data.show = true;\n enter(vnode, function () {\n el.style.display = originalDisplay;\n });\n } else {\n el.style.display = value ? originalDisplay : 'none';\n }\n },\n\n update: function update (el, ref, vnode) {\n var value = ref.value;\n var oldValue = ref.oldValue;\n\n /* istanbul ignore if */\n if (!value === !oldValue) { return }\n vnode = locateNode(vnode);\n var transition$$1 = vnode.data && vnode.data.transition;\n if (transition$$1) {\n vnode.data.show = true;\n if (value) {\n enter(vnode, function () {\n el.style.display = el.__vOriginalDisplay;\n });\n } else {\n leave(vnode, function () {\n el.style.display = 'none';\n });\n }\n } else {\n el.style.display = value ? el.__vOriginalDisplay : 'none';\n }\n },\n\n unbind: function unbind (\n el,\n binding,\n vnode,\n oldVnode,\n isDestroy\n ) {\n if (!isDestroy) {\n el.style.display = el.__vOriginalDisplay;\n }\n }\n};\n\nvar platformDirectives = {\n model: directive,\n show: show\n};\n\n/* */\n\nvar transitionProps = {\n name: String,\n appear: Boolean,\n css: Boolean,\n mode: String,\n type: String,\n enterClass: String,\n leaveClass: String,\n enterToClass: String,\n leaveToClass: String,\n enterActiveClass: String,\n leaveActiveClass: String,\n appearClass: String,\n appearActiveClass: String,\n appearToClass: String,\n duration: [Number, String, Object]\n};\n\n// in case the child is also an abstract component, e.g. <keep-alive>\n// we want to recursively retrieve the real component to be rendered\nfunction getRealChild (vnode) {\n var compOptions = vnode && vnode.componentOptions;\n if (compOptions && compOptions.Ctor.options.abstract) {\n return getRealChild(getFirstComponentChild(compOptions.children))\n } else {\n return vnode\n }\n}\n\nfunction extractTransitionData (comp) {\n var data = {};\n var options = comp.$options;\n // props\n for (var key in options.propsData) {\n data[key] = comp[key];\n }\n // events.\n // extract listeners and pass them directly to the transition methods\n var listeners = options._parentListeners;\n for (var key$1 in listeners) {\n data[camelize(key$1)] = listeners[key$1];\n }\n return data\n}\n\nfunction placeholder (h, rawChild) {\n if (/\\d-keep-alive$/.test(rawChild.tag)) {\n return h('keep-alive', {\n props: rawChild.componentOptions.propsData\n })\n }\n}\n\nfunction hasParentTransition (vnode) {\n while ((vnode = vnode.parent)) {\n if (vnode.data.transition) {\n return true\n }\n }\n}\n\nfunction isSameChild (child, oldChild) {\n return oldChild.key === child.key && oldChild.tag === child.tag\n}\n\nvar isNotTextNode = function (c) { return c.tag || isAsyncPlaceholder(c); };\n\nvar isVShowDirective = function (d) { return d.name === 'show'; };\n\nvar Transition = {\n name: 'transition',\n props: transitionProps,\n abstract: true,\n\n render: function render (h) {\n var this$1 = this;\n\n var children = this.$slots.default;\n if (!children) {\n return\n }\n\n // filter out text nodes (possible whitespaces)\n children = children.filter(isNotTextNode);\n /* istanbul ignore if */\n if (!children.length) {\n return\n }\n\n // warn multiple elements\n if (process.env.NODE_ENV !== 'production' && children.length > 1) {\n warn(\n '<transition> can only be used on a single element. Use ' +\n '<transition-group> for lists.',\n this.$parent\n );\n }\n\n var mode = this.mode;\n\n // warn invalid mode\n if (process.env.NODE_ENV !== 'production' &&\n mode && mode !== 'in-out' && mode !== 'out-in'\n ) {\n warn(\n 'invalid <transition> mode: ' + mode,\n this.$parent\n );\n }\n\n var rawChild = children[0];\n\n // if this is a component root node and the component's\n // parent container node also has transition, skip.\n if (hasParentTransition(this.$vnode)) {\n return rawChild\n }\n\n // apply transition data to child\n // use getRealChild() to ignore abstract components e.g. keep-alive\n var child = getRealChild(rawChild);\n /* istanbul ignore if */\n if (!child) {\n return rawChild\n }\n\n if (this._leaving) {\n return placeholder(h, rawChild)\n }\n\n // ensure a key that is unique to the vnode type and to this transition\n // component instance. This key will be used to remove pending leaving nodes\n // during entering.\n var id = \"__transition-\" + (this._uid) + \"-\";\n child.key = child.key == null\n ? child.isComment\n ? id + 'comment'\n : id + child.tag\n : isPrimitive(child.key)\n ? (String(child.key).indexOf(id) === 0 ? child.key : id + child.key)\n : child.key;\n\n var data = (child.data || (child.data = {})).transition = extractTransitionData(this);\n var oldRawChild = this._vnode;\n var oldChild = getRealChild(oldRawChild);\n\n // mark v-show\n // so that the transition module can hand over the control to the directive\n if (child.data.directives && child.data.directives.some(isVShowDirective)) {\n child.data.show = true;\n }\n\n if (\n oldChild &&\n oldChild.data &&\n !isSameChild(child, oldChild) &&\n !isAsyncPlaceholder(oldChild) &&\n // #6687 component root is a comment node\n !(oldChild.componentInstance && oldChild.componentInstance._vnode.isComment)\n ) {\n // replace old child transition data with fresh one\n // important for dynamic transitions!\n var oldData = oldChild.data.transition = extend({}, data);\n // handle transition mode\n if (mode === 'out-in') {\n // return placeholder node and queue update when leave finishes\n this._leaving = true;\n mergeVNodeHook(oldData, 'afterLeave', function () {\n this$1._leaving = false;\n this$1.$forceUpdate();\n });\n return placeholder(h, rawChild)\n } else if (mode === 'in-out') {\n if (isAsyncPlaceholder(child)) {\n return oldRawChild\n }\n var delayedLeave;\n var performLeave = function () { delayedLeave(); };\n mergeVNodeHook(data, 'afterEnter', performLeave);\n mergeVNodeHook(data, 'enterCancelled', performLeave);\n mergeVNodeHook(oldData, 'delayLeave', function (leave) { delayedLeave = leave; });\n }\n }\n\n return rawChild\n }\n};\n\n/* */\n\nvar props = extend({\n tag: String,\n moveClass: String\n}, transitionProps);\n\ndelete props.mode;\n\nvar TransitionGroup = {\n props: props,\n\n beforeMount: function beforeMount () {\n var this$1 = this;\n\n var update = this._update;\n this._update = function (vnode, hydrating) {\n var restoreActiveInstance = setActiveInstance(this$1);\n // force removing pass\n this$1.__patch__(\n this$1._vnode,\n this$1.kept,\n false, // hydrating\n true // removeOnly (!important, avoids unnecessary moves)\n );\n this$1._vnode = this$1.kept;\n restoreActiveInstance();\n update.call(this$1, vnode, hydrating);\n };\n },\n\n render: function render (h) {\n var tag = this.tag || this.$vnode.data.tag || 'span';\n var map = Object.create(null);\n var prevChildren = this.prevChildren = this.children;\n var rawChildren = this.$slots.default || [];\n var children = this.children = [];\n var transitionData = extractTransitionData(this);\n\n for (var i = 0; i < rawChildren.length; i++) {\n var c = rawChildren[i];\n if (c.tag) {\n if (c.key != null && String(c.key).indexOf('__vlist') !== 0) {\n children.push(c);\n map[c.key] = c\n ;(c.data || (c.data = {})).transition = transitionData;\n } else if (process.env.NODE_ENV !== 'production') {\n var opts = c.componentOptions;\n var name = opts ? (opts.Ctor.options.name || opts.tag || '') : c.tag;\n warn((\"<transition-group> children must be keyed: <\" + name + \">\"));\n }\n }\n }\n\n if (prevChildren) {\n var kept = [];\n var removed = [];\n for (var i$1 = 0; i$1 < prevChildren.length; i$1++) {\n var c$1 = prevChildren[i$1];\n c$1.data.transition = transitionData;\n c$1.data.pos = c$1.elm.getBoundingClientRect();\n if (map[c$1.key]) {\n kept.push(c$1);\n } else {\n removed.push(c$1);\n }\n }\n this.kept = h(tag, null, kept);\n this.removed = removed;\n }\n\n return h(tag, null, children)\n },\n\n updated: function updated () {\n var children = this.prevChildren;\n var moveClass = this.moveClass || ((this.name || 'v') + '-move');\n if (!children.length || !this.hasMove(children[0].elm, moveClass)) {\n return\n }\n\n // we divide the work into three loops to avoid mixing DOM reads and writes\n // in each iteration - which helps prevent layout thrashing.\n children.forEach(callPendingCbs);\n children.forEach(recordPosition);\n children.forEach(applyTranslation);\n\n // force reflow to put everything in position\n // assign to this to avoid being removed in tree-shaking\n // $flow-disable-line\n this._reflow = document.body.offsetHeight;\n\n children.forEach(function (c) {\n if (c.data.moved) {\n var el = c.elm;\n var s = el.style;\n addTransitionClass(el, moveClass);\n s.transform = s.WebkitTransform = s.transitionDuration = '';\n el.addEventListener(transitionEndEvent, el._moveCb = function cb (e) {\n if (e && e.target !== el) {\n return\n }\n if (!e || /transform$/.test(e.propertyName)) {\n el.removeEventListener(transitionEndEvent, cb);\n el._moveCb = null;\n removeTransitionClass(el, moveClass);\n }\n });\n }\n });\n },\n\n methods: {\n hasMove: function hasMove (el, moveClass) {\n /* istanbul ignore if */\n if (!hasTransition) {\n return false\n }\n /* istanbul ignore if */\n if (this._hasMove) {\n return this._hasMove\n }\n // Detect whether an element with the move class applied has\n // CSS transitions. Since the element may be inside an entering\n // transition at this very moment, we make a clone of it and remove\n // all other transition classes applied to ensure only the move class\n // is applied.\n var clone = el.cloneNode();\n if (el._transitionClasses) {\n el._transitionClasses.forEach(function (cls) { removeClass(clone, cls); });\n }\n addClass(clone, moveClass);\n clone.style.display = 'none';\n this.$el.appendChild(clone);\n var info = getTransitionInfo(clone);\n this.$el.removeChild(clone);\n return (this._hasMove = info.hasTransform)\n }\n }\n};\n\nfunction callPendingCbs (c) {\n /* istanbul ignore if */\n if (c.elm._moveCb) {\n c.elm._moveCb();\n }\n /* istanbul ignore if */\n if (c.elm._enterCb) {\n c.elm._enterCb();\n }\n}\n\nfunction recordPosition (c) {\n c.data.newPos = c.elm.getBoundingClientRect();\n}\n\nfunction applyTranslation (c) {\n var oldPos = c.data.pos;\n var newPos = c.data.newPos;\n var dx = oldPos.left - newPos.left;\n var dy = oldPos.top - newPos.top;\n if (dx || dy) {\n c.data.moved = true;\n var s = c.elm.style;\n s.transform = s.WebkitTransform = \"translate(\" + dx + \"px,\" + dy + \"px)\";\n s.transitionDuration = '0s';\n }\n}\n\nvar platformComponents = {\n Transition: Transition,\n TransitionGroup: TransitionGroup\n};\n\n/* */\n\n// install platform specific utils\nVue.config.mustUseProp = mustUseProp;\nVue.config.isReservedTag = isReservedTag;\nVue.config.isReservedAttr = isReservedAttr;\nVue.config.getTagNamespace = getTagNamespace;\nVue.config.isUnknownElement = isUnknownElement;\n\n// install platform runtime directives & components\nextend(Vue.options.directives, platformDirectives);\nextend(Vue.options.components, platformComponents);\n\n// install platform patch function\nVue.prototype.__patch__ = inBrowser ? patch : noop;\n\n// public mount method\nVue.prototype.$mount = function (\n el,\n hydrating\n) {\n el = el && inBrowser ? query(el) : undefined;\n return mountComponent(this, el, hydrating)\n};\n\n// devtools global hook\n/* istanbul ignore next */\nif (inBrowser) {\n setTimeout(function () {\n if (config.devtools) {\n if (devtools) {\n devtools.emit('init', Vue);\n } else if (\n process.env.NODE_ENV !== 'production' &&\n process.env.NODE_ENV !== 'test'\n ) {\n console[console.info ? 'info' : 'log'](\n 'Download the Vue Devtools extension for a better development experience:\\n' +\n 'https://github.com/vuejs/vue-devtools'\n );\n }\n }\n if (process.env.NODE_ENV !== 'production' &&\n process.env.NODE_ENV !== 'test' &&\n config.productionTip !== false &&\n typeof console !== 'undefined'\n ) {\n console[console.info ? 'info' : 'log'](\n \"You are running Vue in development mode.\\n\" +\n \"Make sure to turn on production mode when deploying for production.\\n\" +\n \"See more tips at https://vuejs.org/guide/deployment.html\"\n );\n }\n }, 0);\n}\n\n/* */\n\nexport default Vue;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue/dist/vue.runtime.esm.js\n// module id = 3\n// module chunks = 0","var g;\r\n\r\n// This works in non-strict mode\r\ng = (function() {\r\n\treturn this;\r\n})();\r\n\r\ntry {\r\n\t// This works if eval is allowed (see CSP)\r\n\tg = g || Function(\"return this\")() || (1,eval)(\"this\");\r\n} catch(e) {\r\n\t// This works if the window reference is available\r\n\tif(typeof window === \"object\")\r\n\t\tg = window;\r\n}\r\n\r\n// g can still be undefined, but nothing to do about it...\r\n// We return undefined, instead of nothing here, so it's\r\n// easier to handle this case. if(!global) { ...}\r\n\r\nmodule.exports = g;\r\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// (webpack)/buildin/global.js\n// module id = 4\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-44998ed8\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./TreeView.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./TreeView.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./TreeView.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-44998ed8\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./TreeView.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/TreeView.vue\n// module id = 5\n// module chunks = 0","<template>\n <div>\n <app-navbar></app-navbar>\n <div class=\"section\">\n <div class=\"container\">\n <div class=\"columns\">\n <div class=\"column is-3\">\n <app-navmenu></app-navmenu>\n </div>\n <div class=\"column\">\n <app-options v-if=\"page == 'options'\"></app-options>\n <app-passview v-if=\"page == 'passes'\"></app-passview>\n <app-rendertaskview v-if=\"page == 'render_tasks'\"></app-rendertaskview>\n <app-documentview v-if=\"page == 'documents'\"></app-documentview>\n <app-clipscrolltreeview v-if=\"page == 'clip_scroll_tree'\"></app-clipscrolltreeview>\n <app-screenshotview v-if=\"page == 'screenshot'\"></app-screenshotview>\n </div>\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script>\nimport NavBar from './components/NavBar.vue'\nimport NavMenu from './components/NavMenu.vue'\nimport OptionsPage from './components/OptionsPage.vue'\nimport PassViewPage from './components/PassViewPage.vue'\nimport RenderTaskViewPage from './components/RenderTaskViewPage.vue'\nimport DocumentViewPage from './components/DocumentViewPage.vue'\nimport ClipScrollTreeViewPage from './components/ClipScrollTreeViewPage.vue'\nimport ScreenshotPage from './components/ScreenshotPage.vue'\n\nexport default {\n name: 'app',\n components: {\n 'app-navbar': NavBar,\n 'app-navmenu': NavMenu,\n 'app-options': OptionsPage,\n 'app-passview': PassViewPage,\n 'app-rendertaskview': RenderTaskViewPage,\n 'app-documentview': DocumentViewPage,\n 'app-clipscrolltreeview': ClipScrollTreeViewPage,\n 'app-screenshotview': ScreenshotPage,\n },\n computed: {\n page() {\n return this.$store.state.page;\n }\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/App.vue","<template>\n <nav class=\"navbar has-shadow\">\n <div class=\"navbar-brand\">\n <a class=\"navbar-item\" href=\"#\">WebRender Debugger</a>\n </div>\n\n <div class=\"navbar-menu\">\n <div class=\"navbar-start\"></div>\n\n <div class=\"navbar-end\">\n <div class=\"navbar-item\">\n <p class=\"control\">\n <button v-if=\"isConnected\" @click=\"disconnect\" class=\"button is-danger\">Disconnect</button>\n <button v-else @click=\"connect\" class=\"button is-success\">Connect</button>\n </p>\n </div>\n </div>\n </div>\n </nav>\n</template>\n\n<script>\nexport default {\n computed: {\n isConnected() {\n return this.$store.state.connected;\n },\n },\n methods: {\n connect() {\n this.$store.dispatch('connect');\n },\n disconnect() {\n this.$store.dispatch('disconnect');\n },\n }\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/NavBar.vue","<template>\n <aside class=\"menu\">\n <p class=\"menu-label\">\n Pages\n </p>\n <ul class=\"menu-list\">\n <li><a @click=\"setPage('options')\" :class=\"{ 'is-active': page == 'options' }\">Debug Options</a></li>\n <li><a @click=\"setPage('passes')\" :class=\"{ 'is-active': page == 'passes' }\">Passes</a></li>\n <li><a @click=\"setPage('render_tasks')\" :class=\"{ 'is-active': page == 'render_tasks' }\">Render Tasks</a></li>\n <li><a @click=\"setPage('documents')\" :class=\"{ 'is-active': page == 'documents' }\">Documents</a></li>\n <li><a @click=\"setPage('clip_scroll_tree')\" v-bind:class=\"{ 'is-active': page == 'clip_scroll_tree' }\">Clip-Scroll Tree</a></li>\n <li><a @click=\"setPage('screenshot')\" v-bind:class=\"{ 'is-active': page == 'screenshot' }\">Screenshot</a></li>\n </ul>\n </aside>\n</template>\n\n<script>\nexport default {\n methods: {\n setPage(name) {\n this.$store.commit('setPage', name);\n },\n },\n computed: {\n page() {\n return this.$store.state.page;\n }\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/NavMenu.vue","<template>\n <div class=\"box\">\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setProfiler($event.target.checked)\">\n Profiler\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setTextureCacheDebugger($event.target.checked)\">\n Texture cache debugger\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setRenderTargetDebugger($event.target.checked)\">\n Render target debugger\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setAlphaRectsDebugger($event.target.checked)\">\n Alpha primitive rects debugger\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setGpuTimeQueries($event.target.checked)\">\n Enable GPU time queries\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setGpuSampleQueries($event.target.checked)\">\n Enable GPU sample queries\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setOpaquePass(!$event.target.checked)\">\n Disable opaque pass\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setAlphaPass(!$event.target.checked)\">\n Disable alpha pass\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setClipMasks(!$event.target.checked)\">\n Disable clip masks\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setTextPrims(!$event.target.checked)\">\n Disable text primitives\n </label>\n </div>\n <div class=\"field\">\n <label class=\"checkbox\">\n <input type=\"checkbox\" :disabled=\"disabled\" v-on:click=\"setGradientPrims(!$event.target.checked)\">\n Disable gradient primitives\n </label>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n computed: {\n disabled() {\n return !this.$store.state.connected\n }\n },\n methods: {\n setProfiler(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_profiler\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_profiler\");\n }\n },\n setTextureCacheDebugger(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_texture_cache_debug\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_texture_cache_debug\");\n }\n },\n setRenderTargetDebugger(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_render_target_debug\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_render_target_debug\");\n }\n },\n setAlphaRectsDebugger(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_alpha_rects_debug\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_alpha_rects_debug\");\n }\n },\n setGpuTimeQueries(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_gpu_time_queries\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_gpu_time_queries\");\n }\n },\n setGpuSampleQueries(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_gpu_sample_queries\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_gpu_sample_queries\");\n }\n },\n setOpaquePass(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_opaque_pass\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_opaque_pass\");\n }\n },\n setAlphaPass(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_alpha_pass\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_alpha_pass\");\n }\n },\n setClipMasks(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_clip_masks\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_clip_masks\");\n }\n },\n setTextPrims(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_text_prims\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_text_prims\");\n }\n },\n setGradientPrims(enabled) {\n if (enabled) {\n this.$store.dispatch('sendMessage', \"enable_gradient_prims\");\n } else {\n this.$store.dispatch('sendMessage', \"disable_gradient_prims\");\n }\n }\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/OptionsPage.vue","<template>\n <div class=\"box\">\n <h1 class=\"title\">Passes <a :disabled=\"disabled\" v-on:click=\"fetch\" class=\"button is-info\">Refresh</a></h1>\n <hr/>\n <div v-for=\"(pass, pass_index) in passes\">\n <p class=\"has-text-black-bis\">Pass {{pass_index}}</p>\n <div v-for=\"(target, target_index) in pass.targets\">\n <p style=\"text-indent: 2em;\" class=\"has-text-grey-dark\">Target {{target_index}} ({{target.kind}})</p>\n <div v-for=\"(batch, batch_index) in target.batches\">\n <p style=\"text-indent: 4em;\" class=\"has-text-grey\">Batch {{batch_index}} ({{batch.description}}, {{batch.kind}}, {{batch.count}} instances)</p>\n </div>\n </div>\n <hr/>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n methods: {\n fetch: function() {\n this.$store.dispatch('sendMessage', \"fetch_passes\");\n }\n },\n computed: {\n disabled() {\n return !this.$store.state.connected\n },\n passes() {\n return this.$store.state.passes\n }\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/PassViewPage.vue","<template>\n <div class=\"box\">\n <h1 class=\"title\">Render Tasks <a :disabled=\"disabled\" v-on:click=\"fetch\" class=\"button is-info\">Refresh</a></h1>\n <hr/>\n <div>\n <ul>\n <app-treeview :model=render_tasks></app-treeview>\n </ul>\n </div>\n </div>\n</template>\n\n<script>\nimport TreeView from './TreeView.vue'\n\nexport default {\n components: {\n 'app-treeview': TreeView,\n },\n methods: {\n fetch: function() {\n this.$store.dispatch('sendMessage', \"fetch_render_tasks\");\n }\n },\n computed: {\n disabled() {\n return !this.$store.state.connected\n },\n render_tasks() {\n return this.$store.state.render_tasks\n }\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/RenderTaskViewPage.vue","<template>\n <li>\n <div v-on:click=\"toggle\">\n <span v-if=\"isFolder\">[{{open ? '-' : '+'}}]</span>\n {{model.description}}\n </div>\n <ul style=\"padding-left: 1em; line-height: 1.5em;\" v-show=\"open\" v-if=\"isFolder\">\n <treeview v-for=\"model in model.children\" :model=\"model\"></treeview>\n </ul>\n </li>\n</template>\n\n<script>\nexport default {\n name: 'treeview',\n props: [\n 'model',\n ],\n data: function () {\n return {\n open: false\n }\n },\n computed: {\n isFolder: function () {\n return this.model.children && this.model.children.length\n }\n },\n methods: {\n toggle: function () {\n if (this.isFolder) {\n this.open = !this.open\n }\n },\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/TreeView.vue","<template>\n <div class=\"box\">\n <h1 class=\"title\">Documents <a :disabled=\"disabled\" v-on:click=\"fetch\" class=\"button is-info\">Refresh</a></h1>\n <hr/>\n <div>\n <ul>\n <app-treeview :model=documents></app-treeview>\n </ul>\n </div>\n </div>\n</template>\n\n<script>\nimport TreeView from './TreeView.vue'\n\nexport default {\n components: {\n 'app-treeview': TreeView,\n },\n methods: {\n fetch: function() {\n this.$store.dispatch('sendMessage', \"fetch_documents\");\n }\n },\n computed: {\n disabled() {\n return !this.$store.state.connected\n },\n documents() {\n return this.$store.state.documents\n }\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/DocumentViewPage.vue","<template>\n <div class=\"box\">\n <h1 class=\"title\">Clip-Scroll Tree <a :disabled=\"disabled\" v-on:click=\"fetch\" class=\"button is-info\">Refresh</a></h1>\n <hr/>\n <div>\n <ul>\n <app-treeview :model=clip_scroll_tree></app-treeview>\n </ul>\n </div>\n </div>\n</template>\n\n<script>\nimport TreeView from './TreeView.vue'\n\nexport default {\n components: {\n 'app-treeview': TreeView,\n },\n methods: {\n fetch: function() {\n this.$store.dispatch('sendMessage', \"fetch_clip_scroll_tree\");\n }\n },\n computed: {\n disabled() {\n return !this.$store.state.connected\n },\n clip_scroll_tree() {\n return this.$store.state.clip_scroll_tree\n }\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/ClipScrollTreeViewPage.vue","<template>\n <div class=\"box\">\n <h1 class=\"title\">Screenshot <a :disabled=\"disabled\" v-on:click=\"fetch\" class=\"button is-info\">Refresh</a></h1>\n <hr/>\n <div>\n <ul>\n <img v-if=\"screenshot.length > 0\" style=\"transform: scaleY(-1); width: 1024px; height:768px\" :src=\"'data:image/png;base64,' + screenshot\" />\n </ul>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n computed: {\n disabled() {\n return !this.$store.state.connected\n },\n screenshot() {\n return this.$store.state.screenshot\n },\n },\n methods: {\n fetch: function() {\n this.$store.dispatch('sendMessage', \"fetch_screenshot\");\n }\n },\n}\n</script>\n\n<style>\n</style>\n\n\n\n// WEBPACK FOOTER //\n// src/components/ScreenshotPage.vue","import Vue from 'vue';\nimport Buefy from 'buefy';\nimport 'buefy/dist/buefy.css';\nimport \"vue-material-design-icons/styles.css\";\nimport App from './App.vue';\nimport store from './store';\n\nVue.use(Buefy);\n\nnew Vue({\n el: '#app',\n store,\n render: h => h(App)\n})\n\n\n\n// WEBPACK FOOTER //\n// ./src/main.js","var scope = (typeof global !== \"undefined\" && global) ||\n (typeof self !== \"undefined\" && self) ||\n window;\nvar apply = Function.prototype.apply;\n\n// DOM APIs, for completeness\n\nexports.setTimeout = function() {\n return new Timeout(apply.call(setTimeout, scope, arguments), clearTimeout);\n};\nexports.setInterval = function() {\n return new Timeout(apply.call(setInterval, scope, arguments), clearInterval);\n};\nexports.clearTimeout =\nexports.clearInterval = function(timeout) {\n if (timeout) {\n timeout.close();\n }\n};\n\nfunction Timeout(id, clearFn) {\n this._id = id;\n this._clearFn = clearFn;\n}\nTimeout.prototype.unref = Timeout.prototype.ref = function() {};\nTimeout.prototype.close = function() {\n this._clearFn.call(scope, this._id);\n};\n\n// Does not start the time, just sets up the members needed.\nexports.enroll = function(item, msecs) {\n clearTimeout(item._idleTimeoutId);\n item._idleTimeout = msecs;\n};\n\nexports.unenroll = function(item) {\n clearTimeout(item._idleTimeoutId);\n item._idleTimeout = -1;\n};\n\nexports._unrefActive = exports.active = function(item) {\n clearTimeout(item._idleTimeoutId);\n\n var msecs = item._idleTimeout;\n if (msecs >= 0) {\n item._idleTimeoutId = setTimeout(function onTimeout() {\n if (item._onTimeout)\n item._onTimeout();\n }, msecs);\n }\n};\n\n// setimmediate attaches itself to the global object\nrequire(\"setimmediate\");\n// On some exotic environments, it's not clear which object `setimmediate` was\n// able to install onto. Search each possibility in the same order as the\n// `setimmediate` library.\nexports.setImmediate = (typeof self !== \"undefined\" && self.setImmediate) ||\n (typeof global !== \"undefined\" && global.setImmediate) ||\n (this && this.setImmediate);\nexports.clearImmediate = (typeof self !== \"undefined\" && self.clearImmediate) ||\n (typeof global !== \"undefined\" && global.clearImmediate) ||\n (this && this.clearImmediate);\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/timers-browserify/main.js\n// module id = 17\n// module chunks = 0","(function (global, undefined) {\n \"use strict\";\n\n if (global.setImmediate) {\n return;\n }\n\n var nextHandle = 1; // Spec says greater than zero\n var tasksByHandle = {};\n var currentlyRunningATask = false;\n var doc = global.document;\n var registerImmediate;\n\n function setImmediate(callback) {\n // Callback can either be a function or a string\n if (typeof callback !== \"function\") {\n callback = new Function(\"\" + callback);\n }\n // Copy function arguments\n var args = new Array(arguments.length - 1);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i + 1];\n }\n // Store and register the task\n var task = { callback: callback, args: args };\n tasksByHandle[nextHandle] = task;\n registerImmediate(nextHandle);\n return nextHandle++;\n }\n\n function clearImmediate(handle) {\n delete tasksByHandle[handle];\n }\n\n function run(task) {\n var callback = task.callback;\n var args = task.args;\n switch (args.length) {\n case 0:\n callback();\n break;\n case 1:\n callback(args[0]);\n break;\n case 2:\n callback(args[0], args[1]);\n break;\n case 3:\n callback(args[0], args[1], args[2]);\n break;\n default:\n callback.apply(undefined, args);\n break;\n }\n }\n\n function runIfPresent(handle) {\n // From the spec: \"Wait until any invocations of this algorithm started before this one have completed.\"\n // So if we're currently running a task, we'll need to delay this invocation.\n if (currentlyRunningATask) {\n // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a\n // \"too much recursion\" error.\n setTimeout(runIfPresent, 0, handle);\n } else {\n var task = tasksByHandle[handle];\n if (task) {\n currentlyRunningATask = true;\n try {\n run(task);\n } finally {\n clearImmediate(handle);\n currentlyRunningATask = false;\n }\n }\n }\n }\n\n function installNextTickImplementation() {\n registerImmediate = function(handle) {\n process.nextTick(function () { runIfPresent(handle); });\n };\n }\n\n function canUsePostMessage() {\n // The test against `importScripts` prevents this implementation from being installed inside a web worker,\n // where `global.postMessage` means something completely different and can't be used for this purpose.\n if (global.postMessage && !global.importScripts) {\n var postMessageIsAsynchronous = true;\n var oldOnMessage = global.onmessage;\n global.onmessage = function() {\n postMessageIsAsynchronous = false;\n };\n global.postMessage(\"\", \"*\");\n global.onmessage = oldOnMessage;\n return postMessageIsAsynchronous;\n }\n }\n\n function installPostMessageImplementation() {\n // Installs an event handler on `global` for the `message` event: see\n // * https://developer.mozilla.org/en/DOM/window.postMessage\n // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages\n\n var messagePrefix = \"setImmediate$\" + Math.random() + \"$\";\n var onGlobalMessage = function(event) {\n if (event.source === global &&\n typeof event.data === \"string\" &&\n event.data.indexOf(messagePrefix) === 0) {\n runIfPresent(+event.data.slice(messagePrefix.length));\n }\n };\n\n if (global.addEventListener) {\n global.addEventListener(\"message\", onGlobalMessage, false);\n } else {\n global.attachEvent(\"onmessage\", onGlobalMessage);\n }\n\n registerImmediate = function(handle) {\n global.postMessage(messagePrefix + handle, \"*\");\n };\n }\n\n function installMessageChannelImplementation() {\n var channel = new MessageChannel();\n channel.port1.onmessage = function(event) {\n var handle = event.data;\n runIfPresent(handle);\n };\n\n registerImmediate = function(handle) {\n channel.port2.postMessage(handle);\n };\n }\n\n function installReadyStateChangeImplementation() {\n var html = doc.documentElement;\n registerImmediate = function(handle) {\n // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted\n // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.\n var script = doc.createElement(\"script\");\n script.onreadystatechange = function () {\n runIfPresent(handle);\n script.onreadystatechange = null;\n html.removeChild(script);\n script = null;\n };\n html.appendChild(script);\n };\n }\n\n function installSetTimeoutImplementation() {\n registerImmediate = function(handle) {\n setTimeout(runIfPresent, 0, handle);\n };\n }\n\n // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.\n var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);\n attachTo = attachTo && attachTo.setTimeout ? attachTo : global;\n\n // Don't get fooled by e.g. browserify environments.\n if ({}.toString.call(global.process) === \"[object process]\") {\n // For Node.js before 0.9\n installNextTickImplementation();\n\n } else if (canUsePostMessage()) {\n // For non-IE10 modern browsers\n installPostMessageImplementation();\n\n } else if (global.MessageChannel) {\n // For web workers, where supported\n installMessageChannelImplementation();\n\n } else if (doc && \"onreadystatechange\" in doc.createElement(\"script\")) {\n // For IE 6–8\n installReadyStateChangeImplementation();\n\n } else {\n // For older browsers\n installSetTimeoutImplementation();\n }\n\n attachTo.setImmediate = setImmediate;\n attachTo.clearImmediate = clearImmediate;\n}(typeof self === \"undefined\" ? typeof global === \"undefined\" ? this : global : self));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/setimmediate/setImmediate.js\n// module id = 18\n// module chunks = 0","// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n} ())\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch(e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch(e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n\n\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while(len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/process/browser.js\n// module id = 19\n// module chunks = 0","/*! Buefy v0.6.7 | MIT License | github.com/buefy/buefy */ \n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"vue\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"vue\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"Buefy\"] = factory(require(\"vue\"));\n\telse\n\t\troot[\"Buefy\"] = factory(root[\"Vue\"]);\n})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_23__) {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"/\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 68);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports) {\n\n/* globals __VUE_SSR_CONTEXT__ */\n\n// this module is a runtime utility for cleaner component module output and will\n// be included in the final webpack user bundle\n\nmodule.exports = function normalizeComponent (\n rawScriptExports,\n compiledTemplate,\n injectStyles,\n scopeId,\n moduleIdentifier /* server only */\n) {\n var esModule\n var scriptExports = rawScriptExports = rawScriptExports || {}\n\n // ES6 modules interop\n var type = typeof rawScriptExports.default\n if (type === 'object' || type === 'function') {\n esModule = rawScriptExports\n scriptExports = rawScriptExports.default\n }\n\n // Vue.extend constructor export interop\n var options = typeof scriptExports === 'function'\n ? scriptExports.options\n : scriptExports\n\n // render functions\n if (compiledTemplate) {\n options.render = compiledTemplate.render\n options.staticRenderFns = compiledTemplate.staticRenderFns\n }\n\n // scopedId\n if (scopeId) {\n options._scopeId = scopeId\n }\n\n var hook\n if (moduleIdentifier) { // server build\n hook = function (context) {\n // 2.3 injection\n context =\n context || // cached call\n (this.$vnode && this.$vnode.ssrContext) || // stateful\n (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext) // functional\n // 2.2 with runInNewContext: true\n if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') {\n context = __VUE_SSR_CONTEXT__\n }\n // inject component styles\n if (injectStyles) {\n injectStyles.call(this, context)\n }\n // register component module identifier for async chunk inferrence\n if (context && context._registeredComponents) {\n context._registeredComponents.add(moduleIdentifier)\n }\n }\n // used by ssr in case component is cached and beforeCreate\n // never gets called\n options._ssrRegister = hook\n } else if (injectStyles) {\n hook = injectStyles\n }\n\n if (hook) {\n var functional = options.functional\n var existing = functional\n ? options.render\n : options.beforeCreate\n if (!functional) {\n // inject component registration as beforeCreate hook\n options.beforeCreate = existing\n ? [].concat(existing, hook)\n : [hook]\n } else {\n // register for functioal component in vue file\n options.render = function renderWithStyleInjection (h, context) {\n hook.call(context)\n return existing(h, context)\n }\n }\n }\n\n return {\n esModule: esModule,\n exports: scriptExports,\n options: options\n }\n}\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\n\nvar _defineProperty = __webpack_require__(100);\n\nvar _defineProperty2 = _interopRequireDefault(_defineProperty);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.default = function (obj, key, value) {\n if (key in obj) {\n (0, _defineProperty2.default)(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n\n return obj;\n};\n\n/***/ }),\n/* 2 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"b\", function() { return setOptions; });\nvar config = {\n defaultContainerElement: null,\n defaultIconPack: 'mdi',\n defaultDialogConfirmText: null,\n defaultDialogCancelText: null,\n defaultSnackbarDuration: 3500,\n defaultToastDuration: 2000,\n defaultTooltipType: 'is-primary',\n defaultTooltipAnimated: false,\n defaultInputAutocomplete: 'on',\n defaultDateFormatter: null,\n defaultDateParser: null,\n defaultDayNames: null,\n defaultMonthNames: null,\n defaultFirstDayOfWeek: null,\n defaultUnselectableDaysOfWeek: null,\n defaultTimeFormatter: null,\n defaultTimeParser: null,\n defaultModalScroll: null,\n defaultDatepickerMobileNative: true,\n defaultTimepickerMobileNative: true,\n defaultNoticeQueue: true,\n defaultInputHasCounter: true\n};\n\n/* harmony default export */ __webpack_exports__[\"a\"] = (config);\n\nvar setOptions = function setOptions(options) {\n config = options;\n};\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(104),\n /* template */\n __webpack_require__(105),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar store = __webpack_require__(34)('wks');\nvar uid = __webpack_require__(25);\nvar Symbol = __webpack_require__(7).Symbol;\nvar USE_SYMBOL = typeof Symbol == 'function';\n\nvar $exports = module.exports = function (name) {\n return store[name] || (store[name] =\n USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name));\n};\n\n$exports.store = store;\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(87), __esModule: true };\n\n/***/ }),\n/* 6 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony export (immutable) */ __webpack_exports__[\"b\"] = getValueByPath;\n/* harmony export (immutable) */ __webpack_exports__[\"c\"] = indexOf;\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"d\", function() { return isMobile; });\n/* harmony export (immutable) */ __webpack_exports__[\"e\"] = removeElement;\n/* harmony export (immutable) */ __webpack_exports__[\"a\"] = escapeRegExpChars;\n/**\r\n * Get value of an object property/path even if it's nested\r\n */\nfunction getValueByPath(obj, path) {\n var value = path.split('.').reduce(function (o, i) {\n return o[i];\n }, obj);\n return value;\n}\n\n/**\r\n * Extension of indexOf method by equality function if specified\r\n */\nfunction indexOf(array, obj, fn) {\n if (!array) return -1;\n\n if (!fn || typeof fn !== 'function') return array.indexOf(obj);\n\n for (var i = 0; i < array.length; i++) {\n if (fn(array[i], obj)) {\n return i;\n }\n }\n\n return -1;\n}\n\n/**\r\n * Mobile detection\r\n * https://www.abeautifulsite.net/detecting-mobile-devices-with-javascript\r\n */\nvar isMobile = {\n Android: function Android() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/Android/i);\n },\n BlackBerry: function BlackBerry() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/BlackBerry/i);\n },\n iOS: function iOS() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/iPhone|iPad|iPod/i);\n },\n Opera: function Opera() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/Opera Mini/i);\n },\n Windows: function Windows() {\n return typeof window !== 'undefined' && window.navigator.userAgent.match(/IEMobile/i);\n },\n any: function any() {\n return isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows();\n }\n};\n\nfunction removeElement(el) {\n if (typeof el.remove !== 'undefined') {\n el.remove();\n } else {\n el.parentNode.removeChild(el);\n }\n}\n\n/**\r\n * Escape regex characters\r\n * http://stackoverflow.com/a/6969486\r\n */\nfunction escapeRegExpChars(value) {\n if (!value) return value;\n\n // eslint-disable-next-line\n return value.replace(/[\\-\\[\\]\\/\\{\\}\\(\\)\\*\\+\\?\\.\\\\\\^\\$\\|]/g, '\\\\$&');\n}\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports) {\n\n// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028\nvar global = module.exports = typeof window != 'undefined' && window.Math == Math\n ? window : typeof self != 'undefined' && self.Math == Math ? self\n // eslint-disable-next-line no-new-func\n : Function('return this')();\nif (typeof __g == 'number') __g = global; // eslint-disable-line no-undef\n\n\n/***/ }),\n/* 8 */\n/***/ (function(module, exports) {\n\nvar core = module.exports = { version: '2.5.5' };\nif (typeof __e == 'number') __e = core; // eslint-disable-line no-undef\n\n\n/***/ }),\n/* 9 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar anObject = __webpack_require__(16);\nvar IE8_DOM_DEFINE = __webpack_require__(47);\nvar toPrimitive = __webpack_require__(29);\nvar dP = Object.defineProperty;\n\nexports.f = __webpack_require__(10) ? Object.defineProperty : function defineProperty(O, P, Attributes) {\n anObject(O);\n P = toPrimitive(P, true);\n anObject(Attributes);\n if (IE8_DOM_DEFINE) try {\n return dP(O, P, Attributes);\n } catch (e) { /* empty */ }\n if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!');\n if ('value' in Attributes) O[P] = Attributes.value;\n return O;\n};\n\n\n/***/ }),\n/* 10 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// Thank's IE8 for his funny defineProperty\nmodule.exports = !__webpack_require__(20)(function () {\n return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7;\n});\n\n\n/***/ }),\n/* 11 */\n/***/ (function(module, exports) {\n\nvar hasOwnProperty = {}.hasOwnProperty;\nmodule.exports = function (it, key) {\n return hasOwnProperty.call(it, key);\n};\n\n\n/***/ }),\n/* 12 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__BaseElementMixin__ = __webpack_require__(13);\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n mixins: [__WEBPACK_IMPORTED_MODULE_0__BaseElementMixin__[\"a\" /* default */]],\n props: {\n size: String,\n expanded: Boolean,\n loading: Boolean,\n rounded: Boolean,\n icon: String,\n // Native options to use in HTML5 validation\n autocomplete: String,\n maxlength: [Number, String]\n },\n data: function data() {\n return {\n isValid: true,\n isFocused: false\n };\n },\n\n computed: {\n /**\r\n * Find parent Field, max 3 levels deep.\r\n */\n parentField: function parentField() {\n var parent = this.$parent;\n for (var i = 0; i < 3; i++) {\n if (parent && !parent.$data._isField) {\n parent = parent.$parent;\n }\n }\n return parent;\n },\n\n\n /**\r\n * Get the type prop from parent if it's a Field.\r\n */\n statusType: function statusType() {\n if (!this.parentField) return;\n\n return this.parentField.newType;\n },\n\n\n /**\r\n * Get the message prop from parent if it's a Field.\r\n */\n statusMessage: function statusMessage() {\n if (!this.parentField) return;\n\n return this.parentField.newMessage;\n },\n\n\n /**\r\n * Fix icon size for inputs, large was too big\r\n */\n iconSize: function iconSize() {\n switch (this.size) {\n case 'is-small':\n return this.size;\n case 'is-medium':\n return;\n case 'is-large':\n return this.newIconPack === 'mdi' ? 'is-medium' : '';\n }\n }\n },\n methods: {\n /**\r\n * Focus method that work dynamically depending on the component.\r\n */\n focus: function focus() {\n var _this = this;\n\n if (this.$data._elementRef === undefined) return;\n\n this.$nextTick(function () {\n return _this.$el.querySelector(_this.$data._elementRef).focus();\n });\n },\n onBlur: function onBlur($event) {\n this.isFocused = false;\n this.$emit('blur', $event);\n this.checkHtml5Validity();\n },\n onFocus: function onFocus($event) {\n this.isFocused = true;\n this.$emit('focus', $event);\n },\n\n\n /**\r\n * Check HTML5 validation, set isValid property.\r\n * If validation fail, send 'is-danger' type,\r\n * and error message to parent if it's a Field.\r\n */\n checkHtml5Validity: function checkHtml5Validity() {\n if (this.$refs[this.$data._elementRef] === undefined) return;\n\n var el = this.$el.querySelector(this.$data._elementRef);\n\n var type = null;\n var message = null;\n var isValid = true;\n if (!el.checkValidity()) {\n type = 'is-danger';\n message = el.validationMessage;\n isValid = false;\n }\n this.isValid = isValid;\n\n if (this.parentField) {\n // Set type only if not defined\n if (!this.parentField.type) {\n this.parentField.newType = type;\n }\n // Set message only if not defined\n if (!this.parentField.message) {\n this.parentField.newMessage = message;\n }\n }\n\n return this.isValid;\n }\n }\n});\n\n/***/ }),\n/* 13 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__config__ = __webpack_require__(2);\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n props: {\n iconPack: String\n },\n data: function data() {\n return {\n newIconPack: this.iconPack || __WEBPACK_IMPORTED_MODULE_0__config__[\"a\" /* default */].defaultIconPack\n };\n }\n});\n\n/***/ }),\n/* 14 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(69), __esModule: true };\n\n/***/ }),\n/* 15 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar dP = __webpack_require__(9);\nvar createDesc = __webpack_require__(21);\nmodule.exports = __webpack_require__(10) ? function (object, key, value) {\n return dP.f(object, key, createDesc(1, value));\n} : function (object, key, value) {\n object[key] = value;\n return object;\n};\n\n\n/***/ }),\n/* 16 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isObject = __webpack_require__(19);\nmodule.exports = function (it) {\n if (!isObject(it)) throw TypeError(it + ' is not an object!');\n return it;\n};\n\n\n/***/ }),\n/* 17 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// to indexed object, toObject with fallback for non-array-like ES3 strings\nvar IObject = __webpack_require__(50);\nvar defined = __webpack_require__(31);\nmodule.exports = function (it) {\n return IObject(defined(it));\n};\n\n\n/***/ }),\n/* 18 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar global = __webpack_require__(7);\nvar core = __webpack_require__(8);\nvar ctx = __webpack_require__(46);\nvar hide = __webpack_require__(15);\nvar has = __webpack_require__(11);\nvar PROTOTYPE = 'prototype';\n\nvar $export = function (type, name, source) {\n var IS_FORCED = type & $export.F;\n var IS_GLOBAL = type & $export.G;\n var IS_STATIC = type & $export.S;\n var IS_PROTO = type & $export.P;\n var IS_BIND = type & $export.B;\n var IS_WRAP = type & $export.W;\n var exports = IS_GLOBAL ? core : core[name] || (core[name] = {});\n var expProto = exports[PROTOTYPE];\n var target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE];\n var key, own, out;\n if (IS_GLOBAL) source = name;\n for (key in source) {\n // contains in native\n own = !IS_FORCED && target && target[key] !== undefined;\n if (own && has(exports, key)) continue;\n // export native or passed\n out = own ? target[key] : source[key];\n // prevent global pollution for namespaces\n exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key]\n // bind timers to global for call from export context\n : IS_BIND && own ? ctx(out, global)\n // wrap global constructors for prevent change them in library\n : IS_WRAP && target[key] == out ? (function (C) {\n var F = function (a, b, c) {\n if (this instanceof C) {\n switch (arguments.length) {\n case 0: return new C();\n case 1: return new C(a);\n case 2: return new C(a, b);\n } return new C(a, b, c);\n } return C.apply(this, arguments);\n };\n F[PROTOTYPE] = C[PROTOTYPE];\n return F;\n // make static versions for prototype methods\n })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;\n // export proto methods to core.%CONSTRUCTOR%.methods.%NAME%\n if (IS_PROTO) {\n (exports.virtual || (exports.virtual = {}))[key] = out;\n // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME%\n if (type & $export.R && expProto && !expProto[key]) hide(expProto, key, out);\n }\n }\n};\n// type bitmap\n$export.F = 1; // forced\n$export.G = 2; // global\n$export.S = 4; // static\n$export.P = 8; // proto\n$export.B = 16; // bind\n$export.W = 32; // wrap\n$export.U = 64; // safe\n$export.R = 128; // real proto method for `library`\nmodule.exports = $export;\n\n\n/***/ }),\n/* 19 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (it) {\n return typeof it === 'object' ? it !== null : typeof it === 'function';\n};\n\n\n/***/ }),\n/* 20 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (exec) {\n try {\n return !!exec();\n } catch (e) {\n return true;\n }\n};\n\n\n/***/ }),\n/* 21 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (bitmap, value) {\n return {\n enumerable: !(bitmap & 1),\n configurable: !(bitmap & 2),\n writable: !(bitmap & 4),\n value: value\n };\n};\n\n\n/***/ }),\n/* 22 */\n/***/ (function(module, exports) {\n\nmodule.exports = {};\n\n\n/***/ }),\n/* 23 */\n/***/ (function(module, exports) {\n\nmodule.exports = __WEBPACK_EXTERNAL_MODULE_23__;\n\n/***/ }),\n/* 24 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.2.14 / 15.2.3.14 Object.keys(O)\nvar $keys = __webpack_require__(49);\nvar enumBugKeys = __webpack_require__(35);\n\nmodule.exports = Object.keys || function keys(O) {\n return $keys(O, enumBugKeys);\n};\n\n\n/***/ }),\n/* 25 */\n/***/ (function(module, exports) {\n\nvar id = 0;\nvar px = Math.random();\nmodule.exports = function (key) {\n return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36));\n};\n\n\n/***/ }),\n/* 26 */\n/***/ (function(module, exports) {\n\nexports.f = {}.propertyIsEnumerable;\n\n\n/***/ }),\n/* 27 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(103),\n /* template */\n __webpack_require__(106),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 28 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(126),\n /* template */\n __webpack_require__(127),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 29 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 7.1.1 ToPrimitive(input [, PreferredType])\nvar isObject = __webpack_require__(19);\n// instead of the ES6 spec version, we didn't implement @@toPrimitive case\n// and the second argument - flag - preferred type is a string\nmodule.exports = function (it, S) {\n if (!isObject(it)) return it;\n var fn, val;\n if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val;\n if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val;\n if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val;\n throw TypeError(\"Can't convert object to primitive value\");\n};\n\n\n/***/ }),\n/* 30 */\n/***/ (function(module, exports) {\n\nvar toString = {}.toString;\n\nmodule.exports = function (it) {\n return toString.call(it).slice(8, -1);\n};\n\n\n/***/ }),\n/* 31 */\n/***/ (function(module, exports) {\n\n// 7.2.1 RequireObjectCoercible(argument)\nmodule.exports = function (it) {\n if (it == undefined) throw TypeError(\"Can't call method on \" + it);\n return it;\n};\n\n\n/***/ }),\n/* 32 */\n/***/ (function(module, exports) {\n\n// 7.1.4 ToInteger\nvar ceil = Math.ceil;\nvar floor = Math.floor;\nmodule.exports = function (it) {\n return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);\n};\n\n\n/***/ }),\n/* 33 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar shared = __webpack_require__(34)('keys');\nvar uid = __webpack_require__(25);\nmodule.exports = function (key) {\n return shared[key] || (shared[key] = uid(key));\n};\n\n\n/***/ }),\n/* 34 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar global = __webpack_require__(7);\nvar SHARED = '__core-js_shared__';\nvar store = global[SHARED] || (global[SHARED] = {});\nmodule.exports = function (key) {\n return store[key] || (store[key] = {});\n};\n\n\n/***/ }),\n/* 35 */\n/***/ (function(module, exports) {\n\n// IE 8- don't enum bug keys\nmodule.exports = (\n 'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'\n).split(',');\n\n\n/***/ }),\n/* 36 */\n/***/ (function(module, exports) {\n\nexports.f = Object.getOwnPropertySymbols;\n\n\n/***/ }),\n/* 37 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 7.1.13 ToObject(argument)\nvar defined = __webpack_require__(31);\nmodule.exports = function (it) {\n return Object(defined(it));\n};\n\n\n/***/ }),\n/* 38 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar $at = __webpack_require__(79)(true);\n\n// 21.1.3.27 String.prototype[@@iterator]()\n__webpack_require__(54)(String, 'String', function (iterated) {\n this._t = String(iterated); // target\n this._i = 0; // next index\n// 21.1.5.2.1 %StringIteratorPrototype%.next()\n}, function () {\n var O = this._t;\n var index = this._i;\n var point;\n if (index >= O.length) return { value: undefined, done: true };\n point = $at(O, index);\n this._i += point.length;\n return { value: point, done: false };\n});\n\n\n/***/ }),\n/* 39 */\n/***/ (function(module, exports) {\n\nmodule.exports = true;\n\n\n/***/ }),\n/* 40 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar def = __webpack_require__(9).f;\nvar has = __webpack_require__(11);\nvar TAG = __webpack_require__(4)('toStringTag');\n\nmodule.exports = function (it, tag, stat) {\n if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag });\n};\n\n\n/***/ }),\n/* 41 */\n/***/ (function(module, exports, __webpack_require__) {\n\nexports.f = __webpack_require__(4);\n\n\n/***/ }),\n/* 42 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar global = __webpack_require__(7);\nvar core = __webpack_require__(8);\nvar LIBRARY = __webpack_require__(39);\nvar wksExt = __webpack_require__(41);\nvar defineProperty = __webpack_require__(9).f;\nmodule.exports = function (name) {\n var $Symbol = core.Symbol || (core.Symbol = LIBRARY ? {} : global.Symbol || {});\n if (name.charAt(0) != '_' && !(name in $Symbol)) defineProperty($Symbol, name, { value: wksExt.f(name) });\n};\n\n\n/***/ }),\n/* 43 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(118),\n /* template */\n __webpack_require__(119),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 44 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(120),\n /* template */\n __webpack_require__(121),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 45 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(122),\n /* template */\n __webpack_require__(125),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 46 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// optional / simple context binding\nvar aFunction = __webpack_require__(71);\nmodule.exports = function (fn, that, length) {\n aFunction(fn);\n if (that === undefined) return fn;\n switch (length) {\n case 1: return function (a) {\n return fn.call(that, a);\n };\n case 2: return function (a, b) {\n return fn.call(that, a, b);\n };\n case 3: return function (a, b, c) {\n return fn.call(that, a, b, c);\n };\n }\n return function (/* ...args */) {\n return fn.apply(that, arguments);\n };\n};\n\n\n/***/ }),\n/* 47 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = !__webpack_require__(10) && !__webpack_require__(20)(function () {\n return Object.defineProperty(__webpack_require__(48)('div'), 'a', { get: function () { return 7; } }).a != 7;\n});\n\n\n/***/ }),\n/* 48 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar isObject = __webpack_require__(19);\nvar document = __webpack_require__(7).document;\n// typeof document.createElement is 'object' in old IE\nvar is = isObject(document) && isObject(document.createElement);\nmodule.exports = function (it) {\n return is ? document.createElement(it) : {};\n};\n\n\n/***/ }),\n/* 49 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar has = __webpack_require__(11);\nvar toIObject = __webpack_require__(17);\nvar arrayIndexOf = __webpack_require__(73)(false);\nvar IE_PROTO = __webpack_require__(33)('IE_PROTO');\n\nmodule.exports = function (object, names) {\n var O = toIObject(object);\n var i = 0;\n var result = [];\n var key;\n for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key);\n // Don't enum bug & hidden keys\n while (names.length > i) if (has(O, key = names[i++])) {\n ~arrayIndexOf(result, key) || result.push(key);\n }\n return result;\n};\n\n\n/***/ }),\n/* 50 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// fallback for non-array-like ES3 and non-enumerable old V8 strings\nvar cof = __webpack_require__(30);\n// eslint-disable-next-line no-prototype-builtins\nmodule.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) {\n return cof(it) == 'String' ? it.split('') : Object(it);\n};\n\n\n/***/ }),\n/* 51 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 7.1.15 ToLength\nvar toInteger = __webpack_require__(32);\nvar min = Math.min;\nmodule.exports = function (it) {\n return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991\n};\n\n\n/***/ }),\n/* 52 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(76),\n /* template */\n __webpack_require__(107),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 53 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\n\nvar _iterator = __webpack_require__(77);\n\nvar _iterator2 = _interopRequireDefault(_iterator);\n\nvar _symbol = __webpack_require__(5);\n\nvar _symbol2 = _interopRequireDefault(_symbol);\n\nvar _typeof = typeof _symbol2.default === \"function\" && typeof _iterator2.default === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof _symbol2.default === \"function\" && obj.constructor === _symbol2.default && obj !== _symbol2.default.prototype ? \"symbol\" : typeof obj; };\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.default = typeof _symbol2.default === \"function\" && _typeof(_iterator2.default) === \"symbol\" ? function (obj) {\n return typeof obj === \"undefined\" ? \"undefined\" : _typeof(obj);\n} : function (obj) {\n return obj && typeof _symbol2.default === \"function\" && obj.constructor === _symbol2.default && obj !== _symbol2.default.prototype ? \"symbol\" : typeof obj === \"undefined\" ? \"undefined\" : _typeof(obj);\n};\n\n/***/ }),\n/* 54 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar LIBRARY = __webpack_require__(39);\nvar $export = __webpack_require__(18);\nvar redefine = __webpack_require__(55);\nvar hide = __webpack_require__(15);\nvar Iterators = __webpack_require__(22);\nvar $iterCreate = __webpack_require__(80);\nvar setToStringTag = __webpack_require__(40);\nvar getPrototypeOf = __webpack_require__(83);\nvar ITERATOR = __webpack_require__(4)('iterator');\nvar BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next`\nvar FF_ITERATOR = '@@iterator';\nvar KEYS = 'keys';\nvar VALUES = 'values';\n\nvar returnThis = function () { return this; };\n\nmodule.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) {\n $iterCreate(Constructor, NAME, next);\n var getMethod = function (kind) {\n if (!BUGGY && kind in proto) return proto[kind];\n switch (kind) {\n case KEYS: return function keys() { return new Constructor(this, kind); };\n case VALUES: return function values() { return new Constructor(this, kind); };\n } return function entries() { return new Constructor(this, kind); };\n };\n var TAG = NAME + ' Iterator';\n var DEF_VALUES = DEFAULT == VALUES;\n var VALUES_BUG = false;\n var proto = Base.prototype;\n var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];\n var $default = $native || getMethod(DEFAULT);\n var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;\n var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;\n var methods, key, IteratorPrototype;\n // Fix native\n if ($anyNative) {\n IteratorPrototype = getPrototypeOf($anyNative.call(new Base()));\n if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) {\n // Set @@toStringTag to native iterators\n setToStringTag(IteratorPrototype, TAG, true);\n // fix for some old engines\n if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis);\n }\n }\n // fix Array#{values, @@iterator}.name in V8 / FF\n if (DEF_VALUES && $native && $native.name !== VALUES) {\n VALUES_BUG = true;\n $default = function values() { return $native.call(this); };\n }\n // Define iterator\n if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) {\n hide(proto, ITERATOR, $default);\n }\n // Plug for library\n Iterators[NAME] = $default;\n Iterators[TAG] = returnThis;\n if (DEFAULT) {\n methods = {\n values: DEF_VALUES ? $default : getMethod(VALUES),\n keys: IS_SET ? $default : getMethod(KEYS),\n entries: $entries\n };\n if (FORCED) for (key in methods) {\n if (!(key in proto)) redefine(proto, key, methods[key]);\n } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods);\n }\n return methods;\n};\n\n\n/***/ }),\n/* 55 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = __webpack_require__(15);\n\n\n/***/ }),\n/* 56 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])\nvar anObject = __webpack_require__(16);\nvar dPs = __webpack_require__(81);\nvar enumBugKeys = __webpack_require__(35);\nvar IE_PROTO = __webpack_require__(33)('IE_PROTO');\nvar Empty = function () { /* empty */ };\nvar PROTOTYPE = 'prototype';\n\n// Create object with fake `null` prototype: use iframe Object with cleared prototype\nvar createDict = function () {\n // Thrash, waste and sodomy: IE GC bug\n var iframe = __webpack_require__(48)('iframe');\n var i = enumBugKeys.length;\n var lt = '<';\n var gt = '>';\n var iframeDocument;\n iframe.style.display = 'none';\n __webpack_require__(82).appendChild(iframe);\n iframe.src = 'javascript:'; // eslint-disable-line no-script-url\n // createDict = iframe.contentWindow.Object;\n // html.removeChild(iframe);\n iframeDocument = iframe.contentWindow.document;\n iframeDocument.open();\n iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt);\n iframeDocument.close();\n createDict = iframeDocument.F;\n while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]];\n return createDict();\n};\n\nmodule.exports = Object.create || function create(O, Properties) {\n var result;\n if (O !== null) {\n Empty[PROTOTYPE] = anObject(O);\n result = new Empty();\n Empty[PROTOTYPE] = null;\n // add \"__proto__\" for Object.getPrototypeOf polyfill\n result[IE_PROTO] = O;\n } else result = createDict();\n return Properties === undefined ? result : dPs(result, Properties);\n};\n\n\n/***/ }),\n/* 57 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(84);\nvar global = __webpack_require__(7);\nvar hide = __webpack_require__(15);\nvar Iterators = __webpack_require__(22);\nvar TO_STRING_TAG = __webpack_require__(4)('toStringTag');\n\nvar DOMIterables = ('CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,' +\n 'DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,' +\n 'MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,' +\n 'SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,' +\n 'TextTrackList,TouchList').split(',');\n\nfor (var i = 0; i < DOMIterables.length; i++) {\n var NAME = DOMIterables[i];\n var Collection = global[NAME];\n var proto = Collection && Collection.prototype;\n if (proto && !proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME);\n Iterators[NAME] = Iterators.Array;\n}\n\n\n/***/ }),\n/* 58 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.2.7 / 15.2.3.4 Object.getOwnPropertyNames(O)\nvar $keys = __webpack_require__(49);\nvar hiddenKeys = __webpack_require__(35).concat('length', 'prototype');\n\nexports.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {\n return $keys(O, hiddenKeys);\n};\n\n\n/***/ }),\n/* 59 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(97), __esModule: true };\n\n/***/ }),\n/* 60 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar classof = __webpack_require__(99);\nvar ITERATOR = __webpack_require__(4)('iterator');\nvar Iterators = __webpack_require__(22);\nmodule.exports = __webpack_require__(8).getIteratorMethod = function (it) {\n if (it != undefined) return it[ITERATOR]\n || it['@@iterator']\n || Iterators[classof(it)];\n};\n\n\n/***/ }),\n/* 61 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(108),\n /* template */\n __webpack_require__(109),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 62 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(137),\n /* template */\n __webpack_require__(138),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 63 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__components_icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__components_icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__BaseElementMixin__ = __webpack_require__(13);\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n mixins: [__WEBPACK_IMPORTED_MODULE_2__BaseElementMixin__[\"a\" /* default */]],\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__components_icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__components_icon_Icon___default.a),\n props: {\n active: {\n type: Boolean,\n default: true\n },\n title: String,\n closable: {\n type: Boolean,\n default: true\n },\n type: String,\n hasIcon: Boolean,\n size: String,\n iconSize: String,\n autoClose: {\n type: Boolean,\n default: false\n },\n duration: {\n type: Number,\n default: 5000\n }\n },\n data: function data() {\n return {\n isActive: this.active\n };\n },\n\n watch: {\n active: function active(value) {\n this.isActive = value;\n },\n isActive: function isActive(value) {\n if (value) {\n this.setAutoClose();\n } else {\n if (this.timer) {\n clearTimeout(this.timer);\n }\n }\n }\n },\n computed: {\n /**\r\n * Icon name (MDI) based on type.\r\n */\n icon: function icon() {\n switch (this.type) {\n case 'is-info':\n return 'information';\n case 'is-success':\n return 'check-circle';\n case 'is-warning':\n return 'alert';\n case 'is-danger':\n return 'alert-circle';\n default:\n return null;\n }\n }\n },\n methods: {\n /**\r\n * Close the Message and emit events.\r\n */\n close: function close() {\n this.isActive = false;\n this.$emit('close');\n this.$emit('update:active', false);\n },\n\n /**\r\n * Set timer to auto close message\r\n */\n setAutoClose: function setAutoClose() {\n var _this = this;\n\n if (this.autoClose) {\n this.timer = setTimeout(function () {\n if (_this.isActive) {\n _this.close();\n }\n }, this.duration);\n }\n }\n },\n mounted: function mounted() {\n this.setAutoClose();\n }\n});\n\n/***/ }),\n/* 64 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(149),\n /* template */\n __webpack_require__(150),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 65 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__helpers__ = __webpack_require__(6);\n\n\n\n/* harmony default export */ __webpack_exports__[\"a\"] = ({\n props: {\n type: {\n type: String,\n default: 'is-dark'\n },\n message: String,\n duration: Number,\n queue: {\n type: Boolean,\n default: undefined\n },\n position: {\n type: String,\n default: 'is-top',\n validator: function validator(value) {\n return ['is-top-right', 'is-top', 'is-top-left', 'is-bottom-right', 'is-bottom', 'is-bottom-left'].indexOf(value) > -1;\n }\n },\n container: String\n },\n data: function data() {\n return {\n isActive: false,\n parentTop: null,\n parentBottom: null,\n newContainer: this.container || __WEBPACK_IMPORTED_MODULE_0__config__[\"a\" /* default */].defaultContainerElement\n };\n },\n\n computed: {\n correctParent: function correctParent() {\n switch (this.position) {\n case 'is-top-right':\n case 'is-top':\n case 'is-top-left':\n return this.parentTop;\n\n case 'is-bottom-right':\n case 'is-bottom':\n case 'is-bottom-left':\n return this.parentBottom;\n }\n },\n transition: function transition() {\n switch (this.position) {\n case 'is-top-right':\n case 'is-top':\n case 'is-top-left':\n return {\n enter: 'fadeInDown',\n leave: 'fadeOut'\n };\n case 'is-bottom-right':\n case 'is-bottom':\n case 'is-bottom-left':\n return {\n enter: 'fadeInUp',\n leave: 'fadeOut'\n };\n }\n }\n },\n methods: {\n shouldQueue: function shouldQueue() {\n var queue = this.queue !== undefined ? this.queue : __WEBPACK_IMPORTED_MODULE_0__config__[\"a\" /* default */].defaultNoticeQueue;\n\n if (!queue) return false;\n\n return this.parentTop.childElementCount > 0 || this.parentBottom.childElementCount > 0;\n },\n close: function close() {\n var _this = this;\n\n clearTimeout(this.timer);\n this.isActive = false;\n\n // Timeout for the animation complete before destroying\n setTimeout(function () {\n _this.$destroy();\n Object(__WEBPACK_IMPORTED_MODULE_1__helpers__[\"e\" /* removeElement */])(_this.$el);\n }, 150);\n },\n showNotice: function showNotice() {\n var _this2 = this;\n\n if (this.shouldQueue()) {\n // Call recursively if should queue\n setTimeout(function () {\n return _this2.showNotice();\n }, 250);\n return;\n }\n this.correctParent.insertAdjacentElement('afterbegin', this.$el);\n this.isActive = true;\n\n if (!this.indefinite) {\n this.timer = setTimeout(function () {\n return _this2.close();\n }, this.newDuration);\n }\n },\n setupContainer: function setupContainer() {\n this.parentTop = document.querySelector('.notices.is-top');\n this.parentBottom = document.querySelector('.notices.is-bottom');\n\n if (this.parentTop && this.parentBottom) return;\n\n if (!this.parentTop) {\n this.parentTop = document.createElement('div');\n this.parentTop.className = 'notices is-top';\n }\n\n if (!this.parentBottom) {\n this.parentBottom = document.createElement('div');\n this.parentBottom.className = 'notices is-bottom';\n }\n\n var container = document.querySelector(this.newContainer) || document.body;\n\n container.appendChild(this.parentTop);\n container.appendChild(this.parentBottom);\n\n if (this.newContainer) {\n this.parentTop.classList.add('has-custom-container');\n this.parentBottom.classList.add('has-custom-container');\n }\n }\n },\n beforeMount: function beforeMount() {\n this.setupContainer();\n },\n mounted: function mounted() {\n this.showNotice();\n }\n});\n\n/***/ }),\n/* 66 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(179),\n /* template */\n __webpack_require__(180),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 67 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(188),\n /* template */\n __webpack_require__(189),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 68 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\nvar components_namespaceObject = {};\n__webpack_require__.d(components_namespaceObject, \"Autocomplete\", function() { return autocomplete; });\n__webpack_require__.d(components_namespaceObject, \"Checkbox\", function() { return components_checkbox; });\n__webpack_require__.d(components_namespaceObject, \"Collapse\", function() { return collapse; });\n__webpack_require__.d(components_namespaceObject, \"Datepicker\", function() { return datepicker; });\n__webpack_require__.d(components_namespaceObject, \"Dialog\", function() { return dialog; });\n__webpack_require__.d(components_namespaceObject, \"Dropdown\", function() { return dropdown; });\n__webpack_require__.d(components_namespaceObject, \"Field\", function() { return field; });\n__webpack_require__.d(components_namespaceObject, \"Icon\", function() { return icon; });\n__webpack_require__.d(components_namespaceObject, \"Input\", function() { return input; });\n__webpack_require__.d(components_namespaceObject, \"Loading\", function() { return loading; });\n__webpack_require__.d(components_namespaceObject, \"Message\", function() { return components_message; });\n__webpack_require__.d(components_namespaceObject, \"Modal\", function() { return modal; });\n__webpack_require__.d(components_namespaceObject, \"Notification\", function() { return notification; });\n__webpack_require__.d(components_namespaceObject, \"Pagination\", function() { return pagination; });\n__webpack_require__.d(components_namespaceObject, \"Panel\", function() { return panel; });\n__webpack_require__.d(components_namespaceObject, \"Radio\", function() { return components_radio; });\n__webpack_require__.d(components_namespaceObject, \"Select\", function() { return components_select; });\n__webpack_require__.d(components_namespaceObject, \"Snackbar\", function() { return snackbar; });\n__webpack_require__.d(components_namespaceObject, \"Switch\", function() { return components_switch; });\n__webpack_require__.d(components_namespaceObject, \"Table\", function() { return table; });\n__webpack_require__.d(components_namespaceObject, \"Tabs\", function() { return tabs; });\n__webpack_require__.d(components_namespaceObject, \"Tag\", function() { return tag; });\n__webpack_require__.d(components_namespaceObject, \"Taginput\", function() { return taginput; });\n__webpack_require__.d(components_namespaceObject, \"Timepicker\", function() { return timepicker; });\n__webpack_require__.d(components_namespaceObject, \"Toast\", function() { return toast; });\n__webpack_require__.d(components_namespaceObject, \"Tooltip\", function() { return tooltip; });\n__webpack_require__.d(components_namespaceObject, \"Upload\", function() { return upload; });\n\n// EXTERNAL MODULE: ./node_modules/babel-runtime/core-js/object/assign.js\nvar object_assign = __webpack_require__(14);\nvar assign_default = /*#__PURE__*/__webpack_require__.n(object_assign);\n\n// EXTERNAL MODULE: ./src/scss/buefy-build.scss\nvar buefy_build = __webpack_require__(75);\nvar buefy_build_default = /*#__PURE__*/__webpack_require__.n(buefy_build);\n\n// EXTERNAL MODULE: ./src/components/autocomplete/Autocomplete.vue\nvar Autocomplete = __webpack_require__(52);\nvar Autocomplete_default = /*#__PURE__*/__webpack_require__.n(Autocomplete);\n\n// CONCATENATED MODULE: ./src/utils/plugins.js\n\nvar use = function use(plugin) {\n if (typeof window !== 'undefined' && window.Vue) {\n window.Vue.use(plugin);\n }\n};\n\nvar registerComponent = function registerComponent(Vue, component) {\n Vue.component(component.name, component);\n};\n\nvar registerComponentProgrammatic = function registerComponentProgrammatic(Vue, property, component) {\n Vue.prototype[property] = component;\n};\n// CONCATENATED MODULE: ./src/components/autocomplete/index.js\n\n\n\n\nvar Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Autocomplete_default.a);\n }\n};\n\nuse(Plugin);\n\n/* harmony default export */ var autocomplete = (Plugin);\n// EXTERNAL MODULE: ./src/components/checkbox/Checkbox.vue\nvar Checkbox = __webpack_require__(61);\nvar Checkbox_default = /*#__PURE__*/__webpack_require__.n(Checkbox);\n\n// EXTERNAL MODULE: ./src/components/checkbox/CheckboxButton.vue\nvar CheckboxButton = __webpack_require__(110);\nvar CheckboxButton_default = /*#__PURE__*/__webpack_require__.n(CheckboxButton);\n\n// CONCATENATED MODULE: ./src/components/checkbox/index.js\n\n\n\n\n\nvar checkbox_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Checkbox_default.a);\n registerComponent(Vue, CheckboxButton_default.a);\n }\n};\n\nuse(checkbox_Plugin);\n\n/* harmony default export */ var components_checkbox = (checkbox_Plugin);\n// EXTERNAL MODULE: ./src/components/collapse/Collapse.vue\nvar Collapse = __webpack_require__(113);\nvar Collapse_default = /*#__PURE__*/__webpack_require__.n(Collapse);\n\n// CONCATENATED MODULE: ./src/components/collapse/index.js\n\n\n\n\nvar collapse_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Collapse_default.a);\n }\n};\n\nuse(collapse_Plugin);\n\n/* harmony default export */ var collapse = (collapse_Plugin);\n// EXTERNAL MODULE: ./src/components/datepicker/Datepicker.vue\nvar Datepicker = __webpack_require__(116);\nvar Datepicker_default = /*#__PURE__*/__webpack_require__.n(Datepicker);\n\n// CONCATENATED MODULE: ./src/components/datepicker/index.js\n\n\n\n\nvar datepicker_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Datepicker_default.a);\n }\n};\n\nuse(datepicker_Plugin);\n\n/* harmony default export */ var datepicker = (datepicker_Plugin);\n// EXTERNAL MODULE: external {\"commonjs\":\"vue\",\"commonjs2\":\"vue\",\"amd\":\"vue\",\"root\":\"Vue\"}\nvar external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue__ = __webpack_require__(23);\nvar external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default = /*#__PURE__*/__webpack_require__.n(external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue__);\n\n// EXTERNAL MODULE: ./src/components/dialog/Dialog.vue\nvar Dialog = __webpack_require__(135);\nvar Dialog_default = /*#__PURE__*/__webpack_require__.n(Dialog);\n\n// CONCATENATED MODULE: ./src/components/dialog/index.js\n\n\n\n\n\n\nfunction dialog_open(propsData) {\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var DialogComponent = vm.extend(Dialog_default.a);\n return new DialogComponent({\n el: document.createElement('div'),\n propsData: propsData\n });\n}\n\nvar DialogProgrammatic = {\n alert: function alert(params) {\n var message = void 0;\n if (typeof params === 'string') message = params;\n var defaultParam = {\n canCancel: false,\n message: message\n };\n var propsData = assign_default()(defaultParam, params);\n return dialog_open(propsData);\n },\n confirm: function confirm(params) {\n var defaultParam = {};\n var propsData = assign_default()(defaultParam, params);\n return dialog_open(propsData);\n },\n prompt: function prompt(params) {\n var defaultParam = {\n hasInput: true,\n confirmText: 'Done'\n };\n var propsData = assign_default()(defaultParam, params);\n return dialog_open(propsData);\n }\n};\n\nvar dialog_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Dialog_default.a);\n registerComponentProgrammatic(Vue, '$dialog', DialogProgrammatic);\n }\n};\n\nuse(dialog_Plugin);\n\n/* harmony default export */ var dialog = (dialog_Plugin);\n// EXTERNAL MODULE: ./src/components/dropdown/Dropdown.vue\nvar Dropdown = __webpack_require__(43);\nvar Dropdown_default = /*#__PURE__*/__webpack_require__.n(Dropdown);\n\n// EXTERNAL MODULE: ./src/components/dropdown/DropdownItem.vue\nvar DropdownItem = __webpack_require__(44);\nvar DropdownItem_default = /*#__PURE__*/__webpack_require__.n(DropdownItem);\n\n// CONCATENATED MODULE: ./src/components/dropdown/index.js\n\n\n\n\n\nvar dropdown_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Dropdown_default.a);\n registerComponent(Vue, DropdownItem_default.a);\n }\n};\n\nuse(dropdown_Plugin);\n\n/* harmony default export */ var dropdown = (dropdown_Plugin);\n// EXTERNAL MODULE: ./src/components/field/Field.vue\nvar Field = __webpack_require__(45);\nvar Field_default = /*#__PURE__*/__webpack_require__.n(Field);\n\n// CONCATENATED MODULE: ./src/components/field/index.js\n\n\n\n\nvar field_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Field_default.a);\n }\n};\n\nuse(field_Plugin);\n\n/* harmony default export */ var field = (field_Plugin);\n// EXTERNAL MODULE: ./src/components/icon/Icon.vue\nvar Icon = __webpack_require__(3);\nvar Icon_default = /*#__PURE__*/__webpack_require__.n(Icon);\n\n// CONCATENATED MODULE: ./src/components/icon/index.js\n\n\n\n\nvar icon_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Icon_default.a);\n }\n};\n\nuse(icon_Plugin);\n\n/* harmony default export */ var icon = (icon_Plugin);\n// EXTERNAL MODULE: ./src/components/input/Input.vue\nvar Input = __webpack_require__(27);\nvar Input_default = /*#__PURE__*/__webpack_require__.n(Input);\n\n// CONCATENATED MODULE: ./src/components/input/index.js\n\n\n\n\nvar input_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Input_default.a);\n }\n};\n\nuse(input_Plugin);\n\n/* harmony default export */ var input = (input_Plugin);\n// EXTERNAL MODULE: ./src/components/loading/Loading.vue\nvar Loading = __webpack_require__(140);\nvar Loading_default = /*#__PURE__*/__webpack_require__.n(Loading);\n\n// CONCATENATED MODULE: ./src/components/loading/index.js\n\n\n\n\n\n\nvar LoadingProgrammatic = {\n open: function open(params) {\n var defaultParam = {\n programmatic: true\n };\n var propsData = assign_default()(defaultParam, params);\n\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var LoadingComponent = vm.extend(Loading_default.a);\n return new LoadingComponent({\n el: document.createElement('div'),\n propsData: propsData\n });\n }\n};\n\nvar loading_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Loading_default.a);\n registerComponentProgrammatic(Vue, '$loading', LoadingProgrammatic);\n }\n};\n\nuse(loading_Plugin);\n\n/* harmony default export */ var loading = (loading_Plugin);\n// EXTERNAL MODULE: ./src/components/message/Message.vue\nvar Message = __webpack_require__(143);\nvar Message_default = /*#__PURE__*/__webpack_require__.n(Message);\n\n// CONCATENATED MODULE: ./src/components/message/index.js\n\n\n\n\nvar message_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Message_default.a);\n }\n};\n\nuse(message_Plugin);\n\n/* harmony default export */ var components_message = (message_Plugin);\n// EXTERNAL MODULE: ./src/components/modal/Modal.vue\nvar Modal = __webpack_require__(62);\nvar Modal_default = /*#__PURE__*/__webpack_require__.n(Modal);\n\n// CONCATENATED MODULE: ./src/components/modal/index.js\n\n\n\n\n\n\nvar ModalProgrammatic = {\n open: function open(params) {\n var content = void 0;\n var parent = void 0;\n if (typeof params === 'string') content = params;\n\n var defaultParam = {\n programmatic: true,\n content: content\n };\n if (params.parent) {\n parent = params.parent;\n delete params.parent;\n }\n var propsData = assign_default()(defaultParam, params);\n\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var ModalComponent = vm.extend(Modal_default.a);\n return new ModalComponent({\n parent: parent,\n el: document.createElement('div'),\n propsData: propsData\n });\n }\n};\n\nvar modal_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Modal_default.a);\n registerComponentProgrammatic(Vue, '$modal', ModalProgrammatic);\n }\n};\n\nuse(modal_Plugin);\n\n/* harmony default export */ var modal = (modal_Plugin);\n// EXTERNAL MODULE: ./src/components/notification/Notification.vue\nvar Notification = __webpack_require__(146);\nvar Notification_default = /*#__PURE__*/__webpack_require__.n(Notification);\n\n// CONCATENATED MODULE: ./src/components/notification/index.js\n\n\n\n\nvar notification_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Notification_default.a);\n }\n};\n\nuse(notification_Plugin);\n\n/* harmony default export */ var notification = (notification_Plugin);\n// EXTERNAL MODULE: ./src/components/pagination/Pagination.vue\nvar Pagination = __webpack_require__(64);\nvar Pagination_default = /*#__PURE__*/__webpack_require__.n(Pagination);\n\n// CONCATENATED MODULE: ./src/components/pagination/index.js\n\n\n\n\nvar pagination_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Pagination_default.a);\n }\n};\n\nuse(pagination_Plugin);\n\n/* harmony default export */ var pagination = (pagination_Plugin);\n// EXTERNAL MODULE: ./src/components/panel/Panel.vue\nvar Panel = __webpack_require__(151);\nvar Panel_default = /*#__PURE__*/__webpack_require__.n(Panel);\n\n// CONCATENATED MODULE: ./src/components/panel/index.js\n\n\n\n\nvar panel_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Panel_default.a);\n }\n};\n\nuse(panel_Plugin);\n\n/* harmony default export */ var panel = (panel_Plugin);\n// EXTERNAL MODULE: ./src/components/radio/Radio.vue\nvar Radio = __webpack_require__(154);\nvar Radio_default = /*#__PURE__*/__webpack_require__.n(Radio);\n\n// EXTERNAL MODULE: ./src/components/radio/RadioButton.vue\nvar RadioButton = __webpack_require__(157);\nvar RadioButton_default = /*#__PURE__*/__webpack_require__.n(RadioButton);\n\n// CONCATENATED MODULE: ./src/components/radio/index.js\n\n\n\n\n\nvar radio_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Radio_default.a);\n registerComponent(Vue, RadioButton_default.a);\n }\n};\n\nuse(radio_Plugin);\n\n/* harmony default export */ var components_radio = (radio_Plugin);\n// EXTERNAL MODULE: ./src/components/select/Select.vue\nvar Select = __webpack_require__(28);\nvar Select_default = /*#__PURE__*/__webpack_require__.n(Select);\n\n// CONCATENATED MODULE: ./src/components/select/index.js\n\n\n\n\nvar select_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Select_default.a);\n }\n};\n\nuse(select_Plugin);\n\n/* harmony default export */ var components_select = (select_Plugin);\n// EXTERNAL MODULE: ./src/components/snackbar/Snackbar.vue\nvar Snackbar = __webpack_require__(160);\nvar Snackbar_default = /*#__PURE__*/__webpack_require__.n(Snackbar);\n\n// CONCATENATED MODULE: ./src/components/snackbar/index.js\n\n\n\n\n\n\nvar SnackbarProgrammatic = {\n open: function open(params) {\n var message = void 0;\n if (typeof params === 'string') message = params;\n\n var defaultParam = {\n type: 'is-success',\n position: 'is-bottom-right',\n message: message\n };\n var propsData = assign_default()(defaultParam, params);\n\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var SnackbarComponent = vm.extend(Snackbar_default.a);\n return new SnackbarComponent({\n el: document.createElement('div'),\n propsData: propsData\n });\n }\n};\n\nvar snackbar_Plugin = {\n install: function install(Vue) {\n registerComponentProgrammatic(Vue, '$snackbar', SnackbarProgrammatic);\n }\n};\n\nuse(snackbar_Plugin);\n\n/* harmony default export */ var snackbar = (snackbar_Plugin);\n// EXTERNAL MODULE: ./src/components/switch/Switch.vue\nvar Switch = __webpack_require__(163);\nvar Switch_default = /*#__PURE__*/__webpack_require__.n(Switch);\n\n// CONCATENATED MODULE: ./src/components/switch/index.js\n\n\n\n\nvar switch_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Switch_default.a);\n }\n};\n\nuse(switch_Plugin);\n\n/* harmony default export */ var components_switch = (switch_Plugin);\n// EXTERNAL MODULE: ./src/components/table/Table.vue\nvar Table = __webpack_require__(166);\nvar Table_default = /*#__PURE__*/__webpack_require__.n(Table);\n\n// EXTERNAL MODULE: ./src/components/table/TableColumn.vue\nvar TableColumn = __webpack_require__(66);\nvar TableColumn_default = /*#__PURE__*/__webpack_require__.n(TableColumn);\n\n// CONCATENATED MODULE: ./src/components/table/index.js\n\n\n\n\n\nvar table_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Table_default.a);\n registerComponent(Vue, TableColumn_default.a);\n }\n};\n\nuse(table_Plugin);\n\n/* harmony default export */ var table = (table_Plugin);\n// EXTERNAL MODULE: ./src/components/tabs/Tabs.vue\nvar Tabs = __webpack_require__(182);\nvar Tabs_default = /*#__PURE__*/__webpack_require__.n(Tabs);\n\n// EXTERNAL MODULE: ./src/components/tabs/TabItem.vue\nvar TabItem = __webpack_require__(185);\nvar TabItem_default = /*#__PURE__*/__webpack_require__.n(TabItem);\n\n// CONCATENATED MODULE: ./src/components/tabs/index.js\n\n\n\n\n\nvar tabs_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Tabs_default.a);\n registerComponent(Vue, TabItem_default.a);\n }\n};\n\nuse(tabs_Plugin);\n\n/* harmony default export */ var tabs = (tabs_Plugin);\n// EXTERNAL MODULE: ./src/components/tag/Tag.vue\nvar Tag = __webpack_require__(67);\nvar Tag_default = /*#__PURE__*/__webpack_require__.n(Tag);\n\n// EXTERNAL MODULE: ./src/components/tag/Taglist.vue\nvar Taglist = __webpack_require__(190);\nvar Taglist_default = /*#__PURE__*/__webpack_require__.n(Taglist);\n\n// CONCATENATED MODULE: ./src/components/tag/index.js\n\n\n\n\n\nvar tag_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Tag_default.a);\n registerComponent(Vue, Taglist_default.a);\n }\n};\n\nuse(tag_Plugin);\n\n/* harmony default export */ var tag = (tag_Plugin);\n// EXTERNAL MODULE: ./src/components/taginput/Taginput.vue\nvar Taginput = __webpack_require__(193);\nvar Taginput_default = /*#__PURE__*/__webpack_require__.n(Taginput);\n\n// CONCATENATED MODULE: ./src/components/taginput/index.js\n\n\n\n\nvar taginput_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Taginput_default.a);\n }\n};\n\nuse(taginput_Plugin);\n\n/* harmony default export */ var taginput = (taginput_Plugin);\n// EXTERNAL MODULE: ./src/components/timepicker/Timepicker.vue\nvar Timepicker = __webpack_require__(196);\nvar Timepicker_default = /*#__PURE__*/__webpack_require__.n(Timepicker);\n\n// CONCATENATED MODULE: ./src/components/timepicker/index.js\n\n\n\n\nvar timepicker_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Timepicker_default.a);\n }\n};\n\nuse(timepicker_Plugin);\n\n/* harmony default export */ var timepicker = (timepicker_Plugin);\n// EXTERNAL MODULE: ./src/components/toast/Toast.vue\nvar Toast = __webpack_require__(199);\nvar Toast_default = /*#__PURE__*/__webpack_require__.n(Toast);\n\n// CONCATENATED MODULE: ./src/components/toast/index.js\n\n\n\n\n\n\nvar ToastProgrammatic = {\n open: function open(params) {\n var message = void 0;\n if (typeof params === 'string') message = params;\n\n var defaultParam = { message: message };\n var propsData = assign_default()(defaultParam, params);\n\n var vm = typeof window !== 'undefined' && window.Vue ? window.Vue : external___commonjs___vue___commonjs2___vue___amd___vue___root___Vue___default.a;\n var ToastComponent = vm.extend(Toast_default.a);\n return new ToastComponent({\n el: document.createElement('div'),\n propsData: propsData\n });\n }\n};\n\nvar toast_Plugin = {\n install: function install(Vue) {\n registerComponentProgrammatic(Vue, '$toast', ToastProgrammatic);\n }\n};\n\nuse(toast_Plugin);\n\n/* harmony default export */ var toast = (toast_Plugin);\n// EXTERNAL MODULE: ./src/components/tooltip/Tooltip.vue\nvar Tooltip = __webpack_require__(202);\nvar Tooltip_default = /*#__PURE__*/__webpack_require__.n(Tooltip);\n\n// CONCATENATED MODULE: ./src/components/tooltip/index.js\n\n\n\n\nvar tooltip_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Tooltip_default.a);\n }\n};\n\nuse(tooltip_Plugin);\n\n/* harmony default export */ var tooltip = (tooltip_Plugin);\n// EXTERNAL MODULE: ./src/components/upload/Upload.vue\nvar Upload = __webpack_require__(205);\nvar Upload_default = /*#__PURE__*/__webpack_require__.n(Upload);\n\n// CONCATENATED MODULE: ./src/components/upload/index.js\n\n\n\n\nvar upload_Plugin = {\n install: function install(Vue) {\n registerComponent(Vue, Upload_default.a);\n }\n};\n\nuse(upload_Plugin);\n\n/* harmony default export */ var upload = (upload_Plugin);\n// CONCATENATED MODULE: ./src/components/index.js\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n// EXTERNAL MODULE: ./src/utils/config.js\nvar config = __webpack_require__(2);\n\n// CONCATENATED MODULE: ./src/index.js\n\n\n\n\n\n\n\n\n\nvar Buefy = {\n install: function install(Vue) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n // Options\n Object(config[\"b\" /* setOptions */])(assign_default()(config[\"a\" /* default */], options));\n // Components\n for (var componentKey in components_namespaceObject) {\n Vue.use(components_namespaceObject[componentKey]);\n }\n }\n};\n\nuse(Buefy);\n\n/* harmony default export */ var src = __webpack_exports__[\"default\"] = (Buefy);\n\n/***/ }),\n/* 69 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(70);\nmodule.exports = __webpack_require__(8).Object.assign;\n\n\n/***/ }),\n/* 70 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.3.1 Object.assign(target, source)\nvar $export = __webpack_require__(18);\n\n$export($export.S + $export.F, 'Object', { assign: __webpack_require__(72) });\n\n\n/***/ }),\n/* 71 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (it) {\n if (typeof it != 'function') throw TypeError(it + ' is not a function!');\n return it;\n};\n\n\n/***/ }),\n/* 72 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n// 19.1.2.1 Object.assign(target, source, ...)\nvar getKeys = __webpack_require__(24);\nvar gOPS = __webpack_require__(36);\nvar pIE = __webpack_require__(26);\nvar toObject = __webpack_require__(37);\nvar IObject = __webpack_require__(50);\nvar $assign = Object.assign;\n\n// should work with symbols and should have deterministic property order (V8 bug)\nmodule.exports = !$assign || __webpack_require__(20)(function () {\n var A = {};\n var B = {};\n // eslint-disable-next-line no-undef\n var S = Symbol();\n var K = 'abcdefghijklmnopqrst';\n A[S] = 7;\n K.split('').forEach(function (k) { B[k] = k; });\n return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K;\n}) ? function assign(target, source) { // eslint-disable-line no-unused-vars\n var T = toObject(target);\n var aLen = arguments.length;\n var index = 1;\n var getSymbols = gOPS.f;\n var isEnum = pIE.f;\n while (aLen > index) {\n var S = IObject(arguments[index++]);\n var keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S);\n var length = keys.length;\n var j = 0;\n var key;\n while (length > j) if (isEnum.call(S, key = keys[j++])) T[key] = S[key];\n } return T;\n} : $assign;\n\n\n/***/ }),\n/* 73 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// false -> Array#indexOf\n// true -> Array#includes\nvar toIObject = __webpack_require__(17);\nvar toLength = __webpack_require__(51);\nvar toAbsoluteIndex = __webpack_require__(74);\nmodule.exports = function (IS_INCLUDES) {\n return function ($this, el, fromIndex) {\n var O = toIObject($this);\n var length = toLength(O.length);\n var index = toAbsoluteIndex(fromIndex, length);\n var value;\n // Array#includes uses SameValueZero equality algorithm\n // eslint-disable-next-line no-self-compare\n if (IS_INCLUDES && el != el) while (length > index) {\n value = O[index++];\n // eslint-disable-next-line no-self-compare\n if (value != value) return true;\n // Array#indexOf ignores holes, Array#includes - not\n } else for (;length > index; index++) if (IS_INCLUDES || index in O) {\n if (O[index] === el) return IS_INCLUDES || index || 0;\n } return !IS_INCLUDES && -1;\n };\n};\n\n\n/***/ }),\n/* 74 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar toInteger = __webpack_require__(32);\nvar max = Math.max;\nvar min = Math.min;\nmodule.exports = function (index, length) {\n index = toInteger(index);\n return index < 0 ? max(index + length, 0) : min(index, length);\n};\n\n\n/***/ }),\n/* 75 */\n/***/ (function(module, exports) {\n\n// removed by extract-text-webpack-plugin\n\n/***/ }),\n/* 76 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__ = __webpack_require__(53);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator__ = __webpack_require__(59);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__utils_FormElementMixin__ = __webpack_require__(12);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__input_Input__ = __webpack_require__(27);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__input_Input___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__input_Input__);\n\n\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BAutocomplete',\n components: __WEBPACK_IMPORTED_MODULE_2_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_5__input_Input___default.a.name, __WEBPACK_IMPORTED_MODULE_5__input_Input___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_4__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: [Number, String],\n data: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n field: {\n type: String,\n default: 'value'\n },\n keepFirst: Boolean,\n clearOnSelect: Boolean,\n openOnFocus: Boolean\n },\n data: function data() {\n return {\n selected: null,\n hovered: null,\n isActive: false,\n newValue: this.value,\n isListInViewportVertically: true,\n hasFocus: false,\n _isAutocomplete: true,\n _elementRef: 'input'\n };\n },\n\n computed: {\n /**\n * White-listed items to not close when clicked.\n * Add input, dropdown and all children.\n */\n whiteList: function whiteList() {\n var whiteList = [];\n whiteList.push(this.$refs.input.$el.querySelector('input'));\n whiteList.push(this.$refs.dropdown);\n // Add all chidren from dropdown\n if (this.$refs.dropdown !== undefined) {\n var children = this.$refs.dropdown.querySelectorAll('*');\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_get_iterator___default()(children), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var child = _step.value;\n\n whiteList.push(child);\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n }\n\n return whiteList;\n },\n\n\n /**\n * Check if exists default slot\n */\n hasDefaultSlot: function hasDefaultSlot() {\n return !!this.$scopedSlots.default;\n },\n\n\n /**\n * Check if exists \"empty\" slot\n */\n hasEmptySlot: function hasEmptySlot() {\n return !!this.$slots.empty;\n },\n\n\n /**\n * Check if exists \"header\" slot\n */\n hasHeaderSlot: function hasHeaderSlot() {\n return !!this.$slots.header;\n }\n },\n watch: {\n /**\n * When dropdown is toggled, check the visibility to know when\n * to open upwards.\n */\n isActive: function isActive(active) {\n var _this = this;\n\n if (active) {\n this.calcDropdownInViewportVertical();\n } else {\n this.$nextTick(function () {\n return _this.setHovered(null);\n });\n // Timeout to wait for the animation to finish before recalculating\n setTimeout(function () {\n _this.calcDropdownInViewportVertical();\n }, 100);\n }\n },\n\n\n /**\n * When updating input's value\n * 1. Emit changes\n * 2. If value isn't the same as selected, set null\n * 3. Close dropdown if value is clear or else open it\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n // Check if selected is invalid\n var currentValue = this.getValue(this.selected);\n if (currentValue && currentValue !== value) {\n this.setSelected(null, false);\n }\n // Close dropdown if input is clear or else open it\n if (this.hasFocus && (!this.openOnFocus || value)) {\n this.isActive = !!value;\n }\n },\n\n\n /**\n * When v-model is changed:\n * 1. Update internal value.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.newValue = _value;\n !this.isValid && this.$refs.input.checkHtml5Validity();\n },\n\n\n /**\n * Select first option if \"keep-first\n */\n data: function data(value) {\n // Keep first option always pre-selected\n if (this.keepFirst) {\n this.selectFirstOption(value);\n }\n }\n },\n methods: {\n /**\n * Set which option is currently hovered.\n */\n setHovered: function setHovered(option) {\n if (option === undefined) return;\n\n this.hovered = option;\n },\n\n\n /**\n * Set which option is currently selected, update v-model,\n * update input value and close dropdown.\n */\n setSelected: function setSelected(option) {\n var _this2 = this;\n\n var closeDropdown = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;\n\n if (option === undefined) return;\n\n this.selected = option;\n this.$emit('select', this.selected);\n if (this.selected !== null) {\n this.newValue = this.clearOnSelect ? '' : this.getValue(this.selected);\n }\n closeDropdown && this.$nextTick(function () {\n _this2.isActive = false;\n });\n },\n\n\n /**\n * Select first option\n */\n selectFirstOption: function selectFirstOption(options) {\n var _this3 = this;\n\n this.$nextTick(function () {\n if (options.length) {\n // If has visible data or open on focus, keep updating the hovered\n if (_this3.openOnFocus || _this3.newValue !== '' && _this3.hovered !== options[0]) {\n _this3.setHovered(options[0]);\n }\n } else {\n _this3.setHovered(null);\n }\n });\n },\n\n\n /**\n * Enter key listener.\n * Select the hovered option.\n */\n enterPressed: function enterPressed() {\n if (this.hovered === null) return;\n this.setSelected(this.hovered);\n },\n\n\n /**\n * Tab key listener.\n * Select hovered option if it exists, close dropdown, then allow\n * native handling to move to next tabbable element.\n */\n tabPressed: function tabPressed() {\n if (this.hovered === null) {\n this.isActive = false;\n return;\n }\n this.setSelected(this.hovered);\n },\n\n\n /**\n * Close dropdown if clicked outside.\n */\n clickedOutside: function clickedOutside(event) {\n if (this.whiteList.indexOf(event.target) < 0) this.isActive = false;\n },\n\n\n /**\n * Return display text for the input.\n * If object, get value from path, or else just the value.\n * If hightlight, find the text with regex and make bold.\n */\n getValue: function getValue(option) {\n var isHighlight = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n if (!option) return;\n\n var value = (typeof option === 'undefined' ? 'undefined' : __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default()(option)) === 'object' ? Object(__WEBPACK_IMPORTED_MODULE_3__utils_helpers__[\"b\" /* getValueByPath */])(option, this.field) : option;\n\n var escapedValue = typeof this.newValue === 'string' ? Object(__WEBPACK_IMPORTED_MODULE_3__utils_helpers__[\"a\" /* escapeRegExpChars */])(this.newValue) : this.newValue;\n var regex = new RegExp('(' + escapedValue + ')', 'gi');\n\n return isHighlight ? value.replace(regex, '<b>$1</b>') : value;\n },\n\n\n /**\n * Calculate if the dropdown is vertically visible when activated,\n * otherwise it is openened upwards.\n */\n calcDropdownInViewportVertical: function calcDropdownInViewportVertical() {\n var _this4 = this;\n\n this.$nextTick(function () {\n /**\n * this.$refs.dropdown may be undefined\n * when Autocomplete is conditional rendered\n */\n if (_this4.$refs.dropdown === undefined) return;\n\n var rect = _this4.$refs.dropdown.getBoundingClientRect();\n\n _this4.isListInViewportVertically = rect.top >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight);\n });\n },\n\n\n /**\n * Arrows keys listener.\n * If dropdown is active, set hovered option, or else just open.\n */\n keyArrows: function keyArrows(direction) {\n var sum = direction === 'down' ? 1 : -1;\n if (this.isActive) {\n var index = this.data.indexOf(this.hovered) + sum;\n index = index > this.data.length - 1 ? this.data.length : index;\n index = index < 0 ? 0 : index;\n\n this.setHovered(this.data[index]);\n\n var list = this.$refs.dropdown.querySelector('.dropdown-content');\n var element = list.querySelectorAll('.dropdown-item:not(.is-disabled)')[index];\n\n if (!element) return;\n\n var visMin = list.scrollTop;\n var visMax = list.scrollTop + list.clientHeight - element.clientHeight;\n\n if (element.offsetTop < visMin) {\n list.scrollTop = element.offsetTop;\n } else if (element.offsetTop >= visMax) {\n list.scrollTop = element.offsetTop - list.clientHeight + element.clientHeight;\n }\n } else {\n this.isActive = true;\n }\n },\n\n\n /**\n * Focus listener.\n * If value is the same as selected, select all text.\n */\n focused: function focused(event) {\n if (this.getValue(this.selected) === this.newValue) {\n this.$el.querySelector('input').select();\n }\n if (this.openOnFocus) {\n this.isActive = true;\n if (this.keepFirst) {\n this.selectFirstOption(this.data);\n }\n }\n this.hasFocus = true;\n this.$emit('focus', event);\n },\n\n\n /**\n * Blur listener.\n */\n onBlur: function onBlur(event) {\n this.hasFocus = false;\n this.$emit('blur', event);\n }\n },\n created: function created() {\n if (typeof window !== 'undefined') {\n document.addEventListener('click', this.clickedOutside);\n window.addEventListener('resize', this.calcDropdownInViewportVertical);\n }\n },\n beforeDestroy: function beforeDestroy() {\n if (typeof window !== 'undefined') {\n document.removeEventListener('click', this.clickedOutside);\n window.removeEventListener('resize', this.calcDropdownInViewportVertical);\n }\n }\n});\n\n/***/ }),\n/* 77 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(78), __esModule: true };\n\n/***/ }),\n/* 78 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(38);\n__webpack_require__(57);\nmodule.exports = __webpack_require__(41).f('iterator');\n\n\n/***/ }),\n/* 79 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar toInteger = __webpack_require__(32);\nvar defined = __webpack_require__(31);\n// true -> String#at\n// false -> String#codePointAt\nmodule.exports = function (TO_STRING) {\n return function (that, pos) {\n var s = String(defined(that));\n var i = toInteger(pos);\n var l = s.length;\n var a, b;\n if (i < 0 || i >= l) return TO_STRING ? '' : undefined;\n a = s.charCodeAt(i);\n return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff\n ? TO_STRING ? s.charAt(i) : a\n : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;\n };\n};\n\n\n/***/ }),\n/* 80 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar create = __webpack_require__(56);\nvar descriptor = __webpack_require__(21);\nvar setToStringTag = __webpack_require__(40);\nvar IteratorPrototype = {};\n\n// 25.1.2.1.1 %IteratorPrototype%[@@iterator]()\n__webpack_require__(15)(IteratorPrototype, __webpack_require__(4)('iterator'), function () { return this; });\n\nmodule.exports = function (Constructor, NAME, next) {\n Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) });\n setToStringTag(Constructor, NAME + ' Iterator');\n};\n\n\n/***/ }),\n/* 81 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar dP = __webpack_require__(9);\nvar anObject = __webpack_require__(16);\nvar getKeys = __webpack_require__(24);\n\nmodule.exports = __webpack_require__(10) ? Object.defineProperties : function defineProperties(O, Properties) {\n anObject(O);\n var keys = getKeys(Properties);\n var length = keys.length;\n var i = 0;\n var P;\n while (length > i) dP.f(O, P = keys[i++], Properties[P]);\n return O;\n};\n\n\n/***/ }),\n/* 82 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar document = __webpack_require__(7).document;\nmodule.exports = document && document.documentElement;\n\n\n/***/ }),\n/* 83 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O)\nvar has = __webpack_require__(11);\nvar toObject = __webpack_require__(37);\nvar IE_PROTO = __webpack_require__(33)('IE_PROTO');\nvar ObjectProto = Object.prototype;\n\nmodule.exports = Object.getPrototypeOf || function (O) {\n O = toObject(O);\n if (has(O, IE_PROTO)) return O[IE_PROTO];\n if (typeof O.constructor == 'function' && O instanceof O.constructor) {\n return O.constructor.prototype;\n } return O instanceof Object ? ObjectProto : null;\n};\n\n\n/***/ }),\n/* 84 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar addToUnscopables = __webpack_require__(85);\nvar step = __webpack_require__(86);\nvar Iterators = __webpack_require__(22);\nvar toIObject = __webpack_require__(17);\n\n// 22.1.3.4 Array.prototype.entries()\n// 22.1.3.13 Array.prototype.keys()\n// 22.1.3.29 Array.prototype.values()\n// 22.1.3.30 Array.prototype[@@iterator]()\nmodule.exports = __webpack_require__(54)(Array, 'Array', function (iterated, kind) {\n this._t = toIObject(iterated); // target\n this._i = 0; // next index\n this._k = kind; // kind\n// 22.1.5.2.1 %ArrayIteratorPrototype%.next()\n}, function () {\n var O = this._t;\n var kind = this._k;\n var index = this._i++;\n if (!O || index >= O.length) {\n this._t = undefined;\n return step(1);\n }\n if (kind == 'keys') return step(0, index);\n if (kind == 'values') return step(0, O[index]);\n return step(0, [index, O[index]]);\n}, 'values');\n\n// argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7)\nIterators.Arguments = Iterators.Array;\n\naddToUnscopables('keys');\naddToUnscopables('values');\naddToUnscopables('entries');\n\n\n/***/ }),\n/* 85 */\n/***/ (function(module, exports) {\n\nmodule.exports = function () { /* empty */ };\n\n\n/***/ }),\n/* 86 */\n/***/ (function(module, exports) {\n\nmodule.exports = function (done, value) {\n return { value: value, done: !!done };\n};\n\n\n/***/ }),\n/* 87 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(88);\n__webpack_require__(94);\n__webpack_require__(95);\n__webpack_require__(96);\nmodule.exports = __webpack_require__(8).Symbol;\n\n\n/***/ }),\n/* 88 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n// ECMAScript 6 symbols shim\nvar global = __webpack_require__(7);\nvar has = __webpack_require__(11);\nvar DESCRIPTORS = __webpack_require__(10);\nvar $export = __webpack_require__(18);\nvar redefine = __webpack_require__(55);\nvar META = __webpack_require__(89).KEY;\nvar $fails = __webpack_require__(20);\nvar shared = __webpack_require__(34);\nvar setToStringTag = __webpack_require__(40);\nvar uid = __webpack_require__(25);\nvar wks = __webpack_require__(4);\nvar wksExt = __webpack_require__(41);\nvar wksDefine = __webpack_require__(42);\nvar enumKeys = __webpack_require__(90);\nvar isArray = __webpack_require__(91);\nvar anObject = __webpack_require__(16);\nvar isObject = __webpack_require__(19);\nvar toIObject = __webpack_require__(17);\nvar toPrimitive = __webpack_require__(29);\nvar createDesc = __webpack_require__(21);\nvar _create = __webpack_require__(56);\nvar gOPNExt = __webpack_require__(92);\nvar $GOPD = __webpack_require__(93);\nvar $DP = __webpack_require__(9);\nvar $keys = __webpack_require__(24);\nvar gOPD = $GOPD.f;\nvar dP = $DP.f;\nvar gOPN = gOPNExt.f;\nvar $Symbol = global.Symbol;\nvar $JSON = global.JSON;\nvar _stringify = $JSON && $JSON.stringify;\nvar PROTOTYPE = 'prototype';\nvar HIDDEN = wks('_hidden');\nvar TO_PRIMITIVE = wks('toPrimitive');\nvar isEnum = {}.propertyIsEnumerable;\nvar SymbolRegistry = shared('symbol-registry');\nvar AllSymbols = shared('symbols');\nvar OPSymbols = shared('op-symbols');\nvar ObjectProto = Object[PROTOTYPE];\nvar USE_NATIVE = typeof $Symbol == 'function';\nvar QObject = global.QObject;\n// Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173\nvar setter = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild;\n\n// fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687\nvar setSymbolDesc = DESCRIPTORS && $fails(function () {\n return _create(dP({}, 'a', {\n get: function () { return dP(this, 'a', { value: 7 }).a; }\n })).a != 7;\n}) ? function (it, key, D) {\n var protoDesc = gOPD(ObjectProto, key);\n if (protoDesc) delete ObjectProto[key];\n dP(it, key, D);\n if (protoDesc && it !== ObjectProto) dP(ObjectProto, key, protoDesc);\n} : dP;\n\nvar wrap = function (tag) {\n var sym = AllSymbols[tag] = _create($Symbol[PROTOTYPE]);\n sym._k = tag;\n return sym;\n};\n\nvar isSymbol = USE_NATIVE && typeof $Symbol.iterator == 'symbol' ? function (it) {\n return typeof it == 'symbol';\n} : function (it) {\n return it instanceof $Symbol;\n};\n\nvar $defineProperty = function defineProperty(it, key, D) {\n if (it === ObjectProto) $defineProperty(OPSymbols, key, D);\n anObject(it);\n key = toPrimitive(key, true);\n anObject(D);\n if (has(AllSymbols, key)) {\n if (!D.enumerable) {\n if (!has(it, HIDDEN)) dP(it, HIDDEN, createDesc(1, {}));\n it[HIDDEN][key] = true;\n } else {\n if (has(it, HIDDEN) && it[HIDDEN][key]) it[HIDDEN][key] = false;\n D = _create(D, { enumerable: createDesc(0, false) });\n } return setSymbolDesc(it, key, D);\n } return dP(it, key, D);\n};\nvar $defineProperties = function defineProperties(it, P) {\n anObject(it);\n var keys = enumKeys(P = toIObject(P));\n var i = 0;\n var l = keys.length;\n var key;\n while (l > i) $defineProperty(it, key = keys[i++], P[key]);\n return it;\n};\nvar $create = function create(it, P) {\n return P === undefined ? _create(it) : $defineProperties(_create(it), P);\n};\nvar $propertyIsEnumerable = function propertyIsEnumerable(key) {\n var E = isEnum.call(this, key = toPrimitive(key, true));\n if (this === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return false;\n return E || !has(this, key) || !has(AllSymbols, key) || has(this, HIDDEN) && this[HIDDEN][key] ? E : true;\n};\nvar $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(it, key) {\n it = toIObject(it);\n key = toPrimitive(key, true);\n if (it === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return;\n var D = gOPD(it, key);\n if (D && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) D.enumerable = true;\n return D;\n};\nvar $getOwnPropertyNames = function getOwnPropertyNames(it) {\n var names = gOPN(toIObject(it));\n var result = [];\n var i = 0;\n var key;\n while (names.length > i) {\n if (!has(AllSymbols, key = names[i++]) && key != HIDDEN && key != META) result.push(key);\n } return result;\n};\nvar $getOwnPropertySymbols = function getOwnPropertySymbols(it) {\n var IS_OP = it === ObjectProto;\n var names = gOPN(IS_OP ? OPSymbols : toIObject(it));\n var result = [];\n var i = 0;\n var key;\n while (names.length > i) {\n if (has(AllSymbols, key = names[i++]) && (IS_OP ? has(ObjectProto, key) : true)) result.push(AllSymbols[key]);\n } return result;\n};\n\n// 19.4.1.1 Symbol([description])\nif (!USE_NATIVE) {\n $Symbol = function Symbol() {\n if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor!');\n var tag = uid(arguments.length > 0 ? arguments[0] : undefined);\n var $set = function (value) {\n if (this === ObjectProto) $set.call(OPSymbols, value);\n if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;\n setSymbolDesc(this, tag, createDesc(1, value));\n };\n if (DESCRIPTORS && setter) setSymbolDesc(ObjectProto, tag, { configurable: true, set: $set });\n return wrap(tag);\n };\n redefine($Symbol[PROTOTYPE], 'toString', function toString() {\n return this._k;\n });\n\n $GOPD.f = $getOwnPropertyDescriptor;\n $DP.f = $defineProperty;\n __webpack_require__(58).f = gOPNExt.f = $getOwnPropertyNames;\n __webpack_require__(26).f = $propertyIsEnumerable;\n __webpack_require__(36).f = $getOwnPropertySymbols;\n\n if (DESCRIPTORS && !__webpack_require__(39)) {\n redefine(ObjectProto, 'propertyIsEnumerable', $propertyIsEnumerable, true);\n }\n\n wksExt.f = function (name) {\n return wrap(wks(name));\n };\n}\n\n$export($export.G + $export.W + $export.F * !USE_NATIVE, { Symbol: $Symbol });\n\nfor (var es6Symbols = (\n // 19.4.2.2, 19.4.2.3, 19.4.2.4, 19.4.2.6, 19.4.2.8, 19.4.2.9, 19.4.2.10, 19.4.2.11, 19.4.2.12, 19.4.2.13, 19.4.2.14\n 'hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables'\n).split(','), j = 0; es6Symbols.length > j;)wks(es6Symbols[j++]);\n\nfor (var wellKnownSymbols = $keys(wks.store), k = 0; wellKnownSymbols.length > k;) wksDefine(wellKnownSymbols[k++]);\n\n$export($export.S + $export.F * !USE_NATIVE, 'Symbol', {\n // 19.4.2.1 Symbol.for(key)\n 'for': function (key) {\n return has(SymbolRegistry, key += '')\n ? SymbolRegistry[key]\n : SymbolRegistry[key] = $Symbol(key);\n },\n // 19.4.2.5 Symbol.keyFor(sym)\n keyFor: function keyFor(sym) {\n if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol!');\n for (var key in SymbolRegistry) if (SymbolRegistry[key] === sym) return key;\n },\n useSetter: function () { setter = true; },\n useSimple: function () { setter = false; }\n});\n\n$export($export.S + $export.F * !USE_NATIVE, 'Object', {\n // 19.1.2.2 Object.create(O [, Properties])\n create: $create,\n // 19.1.2.4 Object.defineProperty(O, P, Attributes)\n defineProperty: $defineProperty,\n // 19.1.2.3 Object.defineProperties(O, Properties)\n defineProperties: $defineProperties,\n // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)\n getOwnPropertyDescriptor: $getOwnPropertyDescriptor,\n // 19.1.2.7 Object.getOwnPropertyNames(O)\n getOwnPropertyNames: $getOwnPropertyNames,\n // 19.1.2.8 Object.getOwnPropertySymbols(O)\n getOwnPropertySymbols: $getOwnPropertySymbols\n});\n\n// 24.3.2 JSON.stringify(value [, replacer [, space]])\n$JSON && $export($export.S + $export.F * (!USE_NATIVE || $fails(function () {\n var S = $Symbol();\n // MS Edge converts symbol values to JSON as {}\n // WebKit converts symbol values to JSON as null\n // V8 throws on boxed symbols\n return _stringify([S]) != '[null]' || _stringify({ a: S }) != '{}' || _stringify(Object(S)) != '{}';\n})), 'JSON', {\n stringify: function stringify(it) {\n var args = [it];\n var i = 1;\n var replacer, $replacer;\n while (arguments.length > i) args.push(arguments[i++]);\n $replacer = replacer = args[1];\n if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined\n if (!isArray(replacer)) replacer = function (key, value) {\n if (typeof $replacer == 'function') value = $replacer.call(this, key, value);\n if (!isSymbol(value)) return value;\n };\n args[1] = replacer;\n return _stringify.apply($JSON, args);\n }\n});\n\n// 19.4.3.4 Symbol.prototype[@@toPrimitive](hint)\n$Symbol[PROTOTYPE][TO_PRIMITIVE] || __webpack_require__(15)($Symbol[PROTOTYPE], TO_PRIMITIVE, $Symbol[PROTOTYPE].valueOf);\n// 19.4.3.5 Symbol.prototype[@@toStringTag]\nsetToStringTag($Symbol, 'Symbol');\n// 20.2.1.9 Math[@@toStringTag]\nsetToStringTag(Math, 'Math', true);\n// 24.3.3 JSON[@@toStringTag]\nsetToStringTag(global.JSON, 'JSON', true);\n\n\n/***/ }),\n/* 89 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar META = __webpack_require__(25)('meta');\nvar isObject = __webpack_require__(19);\nvar has = __webpack_require__(11);\nvar setDesc = __webpack_require__(9).f;\nvar id = 0;\nvar isExtensible = Object.isExtensible || function () {\n return true;\n};\nvar FREEZE = !__webpack_require__(20)(function () {\n return isExtensible(Object.preventExtensions({}));\n});\nvar setMeta = function (it) {\n setDesc(it, META, { value: {\n i: 'O' + ++id, // object ID\n w: {} // weak collections IDs\n } });\n};\nvar fastKey = function (it, create) {\n // return primitive with prefix\n if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;\n if (!has(it, META)) {\n // can't set metadata to uncaught frozen object\n if (!isExtensible(it)) return 'F';\n // not necessary to add metadata\n if (!create) return 'E';\n // add missing metadata\n setMeta(it);\n // return object ID\n } return it[META].i;\n};\nvar getWeak = function (it, create) {\n if (!has(it, META)) {\n // can't set metadata to uncaught frozen object\n if (!isExtensible(it)) return true;\n // not necessary to add metadata\n if (!create) return false;\n // add missing metadata\n setMeta(it);\n // return hash weak collections IDs\n } return it[META].w;\n};\n// add metadata on freeze-family methods calling\nvar onFreeze = function (it) {\n if (FREEZE && meta.NEED && isExtensible(it) && !has(it, META)) setMeta(it);\n return it;\n};\nvar meta = module.exports = {\n KEY: META,\n NEED: false,\n fastKey: fastKey,\n getWeak: getWeak,\n onFreeze: onFreeze\n};\n\n\n/***/ }),\n/* 90 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// all enumerable object keys, includes symbols\nvar getKeys = __webpack_require__(24);\nvar gOPS = __webpack_require__(36);\nvar pIE = __webpack_require__(26);\nmodule.exports = function (it) {\n var result = getKeys(it);\n var getSymbols = gOPS.f;\n if (getSymbols) {\n var symbols = getSymbols(it);\n var isEnum = pIE.f;\n var i = 0;\n var key;\n while (symbols.length > i) if (isEnum.call(it, key = symbols[i++])) result.push(key);\n } return result;\n};\n\n\n/***/ }),\n/* 91 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// 7.2.2 IsArray(argument)\nvar cof = __webpack_require__(30);\nmodule.exports = Array.isArray || function isArray(arg) {\n return cof(arg) == 'Array';\n};\n\n\n/***/ }),\n/* 92 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window\nvar toIObject = __webpack_require__(17);\nvar gOPN = __webpack_require__(58).f;\nvar toString = {}.toString;\n\nvar windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames\n ? Object.getOwnPropertyNames(window) : [];\n\nvar getWindowNames = function (it) {\n try {\n return gOPN(it);\n } catch (e) {\n return windowNames.slice();\n }\n};\n\nmodule.exports.f = function getOwnPropertyNames(it) {\n return windowNames && toString.call(it) == '[object Window]' ? getWindowNames(it) : gOPN(toIObject(it));\n};\n\n\n/***/ }),\n/* 93 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar pIE = __webpack_require__(26);\nvar createDesc = __webpack_require__(21);\nvar toIObject = __webpack_require__(17);\nvar toPrimitive = __webpack_require__(29);\nvar has = __webpack_require__(11);\nvar IE8_DOM_DEFINE = __webpack_require__(47);\nvar gOPD = Object.getOwnPropertyDescriptor;\n\nexports.f = __webpack_require__(10) ? gOPD : function getOwnPropertyDescriptor(O, P) {\n O = toIObject(O);\n P = toPrimitive(P, true);\n if (IE8_DOM_DEFINE) try {\n return gOPD(O, P);\n } catch (e) { /* empty */ }\n if (has(O, P)) return createDesc(!pIE.f.call(O, P), O[P]);\n};\n\n\n/***/ }),\n/* 94 */\n/***/ (function(module, exports) {\n\n\n\n/***/ }),\n/* 95 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(42)('asyncIterator');\n\n\n/***/ }),\n/* 96 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(42)('observable');\n\n\n/***/ }),\n/* 97 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(57);\n__webpack_require__(38);\nmodule.exports = __webpack_require__(98);\n\n\n/***/ }),\n/* 98 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar anObject = __webpack_require__(16);\nvar get = __webpack_require__(60);\nmodule.exports = __webpack_require__(8).getIterator = function (it) {\n var iterFn = get(it);\n if (typeof iterFn != 'function') throw TypeError(it + ' is not iterable!');\n return anObject(iterFn.call(it));\n};\n\n\n/***/ }),\n/* 99 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// getting tag from 19.1.3.6 Object.prototype.toString()\nvar cof = __webpack_require__(30);\nvar TAG = __webpack_require__(4)('toStringTag');\n// ES3 wrong here\nvar ARG = cof(function () { return arguments; }()) == 'Arguments';\n\n// fallback for IE11 Script Access Denied error\nvar tryGet = function (it, key) {\n try {\n return it[key];\n } catch (e) { /* empty */ }\n};\n\nmodule.exports = function (it) {\n var O, T, B;\n return it === undefined ? 'Undefined' : it === null ? 'Null'\n // @@toStringTag case\n : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T\n // builtinTag case\n : ARG ? cof(O)\n // ES3 arguments fallback\n : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B;\n};\n\n\n/***/ }),\n/* 100 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(101), __esModule: true };\n\n/***/ }),\n/* 101 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(102);\nvar $Object = __webpack_require__(8).Object;\nmodule.exports = function defineProperty(it, key, desc) {\n return $Object.defineProperty(it, key, desc);\n};\n\n\n/***/ }),\n/* 102 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar $export = __webpack_require__(18);\n// 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes)\n$export($export.S + $export.F * !__webpack_require__(10), 'Object', { defineProperty: __webpack_require__(9).f });\n\n\n/***/ }),\n/* 103 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__ = __webpack_require__(12);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BInput',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: [Number, String],\n type: {\n type: String,\n default: 'text'\n },\n passwordReveal: Boolean,\n hasCounter: {\n type: Boolean,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_2__utils_config__[\"a\" /* default */].defaultInputHasCounter;\n }\n }\n },\n data: function data() {\n return {\n newValue: this.value,\n newType: this.type,\n newAutocomplete: this.autocomplete || __WEBPACK_IMPORTED_MODULE_2__utils_config__[\"a\" /* default */].defaultInputAutocomplete,\n isPasswordVisible: false,\n _elementRef: this.type === 'textarea' ? 'textarea' : 'input'\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return [this.iconPosition, this.size, {\n 'is-expanded': this.expanded,\n 'is-loading': this.loading,\n 'is-clearfix': !this.hasMessage\n }];\n },\n inputClasses: function inputClasses() {\n return [this.statusType, this.size, { 'is-rounded': this.rounded }];\n },\n hasIconRight: function hasIconRight() {\n return this.passwordReveal || this.loading || this.statusType;\n },\n\n\n /**\n * Position of the icon or if it's both sides.\n */\n iconPosition: function iconPosition() {\n if (this.icon && this.hasIconRight) {\n return 'has-icons-left has-icons-right';\n } else if (!this.icon && this.hasIconRight) {\n return 'has-icons-right';\n } else if (this.icon) {\n return 'has-icons-left';\n }\n },\n\n\n /**\n * Icon name (MDI) based on the type.\n */\n statusTypeIcon: function statusTypeIcon() {\n switch (this.statusType) {\n case 'is-success':\n return 'check';\n case 'is-danger':\n return 'alert-circle';\n case 'is-info':\n return 'information';\n case 'is-warning':\n return 'alert';\n }\n },\n\n\n /**\n * Check if have any message prop from parent if it's a Field.\n */\n hasMessage: function hasMessage() {\n return !!this.statusMessage;\n },\n\n\n /**\n * Current password-reveal icon name.\n */\n passwordVisibleIcon: function passwordVisibleIcon() {\n return !this.isPasswordVisible ? 'eye' : 'eye-off';\n },\n\n /**\n * Get value length\n */\n valueLength: function valueLength() {\n if (typeof this.newValue === 'string') {\n return this.newValue.length;\n } else if (typeof this.newValue === 'number') {\n return this.newValue.toString().length;\n }\n return 0;\n }\n },\n watch: {\n /**\n * When v-model is changed:\n * 1. Set internal value.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n\n /**\n * Update user's v-model and validate again whenever\n * internal value is changed.\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n !this.isValid && this.checkHtml5Validity();\n }\n },\n methods: {\n /**\n * Toggle the visibility of a password-reveal input\n * by changing the type and focus the input right away.\n */\n togglePasswordVisibility: function togglePasswordVisibility() {\n var _this = this;\n\n this.isPasswordVisible = !this.isPasswordVisible;\n this.newType = this.isPasswordVisible ? 'text' : 'password';\n\n this.$nextTick(function () {\n _this.$refs.input.focus();\n });\n },\n\n\n /**\n * Input's 'input' event listener, 'nextTick' is used to prevent event firing\n * before ui update, helps when using masks (Cleavejs and potentially others).\n */\n onInput: function onInput(event) {\n var _this2 = this;\n\n this.$nextTick(function () {\n _this2.newValue = event.target.value;\n });\n }\n }\n});\n\n/***/ }),\n/* 104 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_config__ = __webpack_require__(2);\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BIcon',\n props: {\n type: String,\n pack: String,\n icon: String,\n size: String,\n customSize: String,\n customClass: String,\n both: Boolean // This is used internally to show both MDI and FA icon\n },\n computed: {\n /**\n * Internal icon name based on the pack.\n * If pack is 'fa', gets the equivalent FA icon name of the MDI,\n * internal icons are always MDI.\n */\n newIcon: function newIcon() {\n if (!this.both) {\n if (this.newPack === 'mdi') {\n return this.newPack + '-' + this.icon;\n } else {\n return 'fa-' + this.icon;\n }\n }\n\n return this.newPack === 'mdi' ? this.newPack + '-' + this.icon : 'fa-' + this.getEquivalentIconOf(this.icon);\n },\n newPack: function newPack() {\n return this.pack || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultIconPack;\n },\n newType: function newType() {\n if (!this.type) return;\n\n var splitType = this.type.split('-');\n if (!splitType.length) return;\n\n return 'has-text-' + splitType[1];\n },\n newCustomSize: function newCustomSize() {\n return this.customSize || this.customSizeByPack;\n },\n customSizeByPack: function customSizeByPack() {\n var defaultSize = this.newPack === 'mdi' ? 'mdi-24px' : 'fa-lg';\n var mediumSize = this.newPack === 'mdi' ? 'mdi-36px' : 'fa-2x';\n var largeSize = this.newPack === 'mdi' ? 'mdi-48px' : 'fa-3x';\n switch (this.size) {\n case 'is-small':\n return;\n case 'is-medium':\n return mediumSize;\n case 'is-large':\n return largeSize;\n default:\n return defaultSize;\n }\n }\n },\n methods: {\n /**\n * Equivalent FA icon name of the MDI.\n */\n getEquivalentIconOf: function getEquivalentIconOf(value) {\n switch (value) {\n case 'check':\n return 'check';\n case 'information':\n return 'info-circle';\n case 'check-circle':\n return 'check-circle';\n case 'alert':\n return 'exclamation-triangle';\n case 'alert-circle':\n return 'exclamation-circle';\n case 'arrow-up':\n return 'arrow-up';\n case 'chevron-right':\n return 'angle-right';\n case 'chevron-left':\n return 'angle-left';\n case 'chevron-down':\n return 'angle-down';\n case 'eye':\n return 'eye';\n case 'eye-off':\n return 'eye-slash';\n case 'menu-down':\n return 'caret-down';\n case 'menu-up':\n return 'caret-up';\n default:\n return value;\n }\n }\n }\n});\n\n/***/ }),\n/* 105 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('span', {\n staticClass: \"icon\",\n class: [_vm.newType, _vm.size]\n }, [_c('i', {\n class: [_vm.newPack, _vm.newIcon, _vm.newCustomSize, _vm.customClass]\n })])\n},staticRenderFns: []}\n\n/***/ }),\n/* 106 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"control\",\n class: _vm.rootClasses\n }, [(_vm.type !== 'textarea') ? _c('input', _vm._b({\n ref: \"input\",\n staticClass: \"input\",\n class: _vm.inputClasses,\n attrs: {\n \"type\": _vm.newType,\n \"autocomplete\": _vm.newAutocomplete,\n \"maxlength\": _vm.maxlength\n },\n domProps: {\n \"value\": _vm.newValue\n },\n on: {\n \"input\": _vm.onInput,\n \"blur\": _vm.onBlur,\n \"focus\": _vm.onFocus\n }\n }, 'input', _vm.$attrs, false)) : _c('textarea', _vm._b({\n ref: \"textarea\",\n staticClass: \"textarea\",\n class: _vm.inputClasses,\n attrs: {\n \"maxlength\": _vm.maxlength\n },\n domProps: {\n \"value\": _vm.newValue\n },\n on: {\n \"input\": _vm.onInput,\n \"blur\": _vm.onBlur,\n \"focus\": _vm.onFocus\n }\n }, 'textarea', _vm.$attrs, false)), _vm._v(\" \"), (_vm.icon) ? _c('b-icon', {\n staticClass: \"is-left\",\n attrs: {\n \"icon\": _vm.icon,\n \"pack\": _vm.iconPack,\n \"size\": _vm.iconSize\n }\n }) : _vm._e(), _vm._v(\" \"), (!_vm.loading && (_vm.passwordReveal || _vm.statusType)) ? _c('b-icon', {\n staticClass: \"is-right\",\n class: {\n 'is-clickable': _vm.passwordReveal\n },\n attrs: {\n \"icon\": _vm.passwordReveal ? _vm.passwordVisibleIcon : _vm.statusTypeIcon,\n \"pack\": _vm.iconPack,\n \"size\": _vm.iconSize,\n \"type\": !_vm.passwordReveal ? _vm.statusType : 'is-primary',\n \"both\": \"\"\n },\n nativeOn: {\n \"click\": function($event) {\n _vm.togglePasswordVisibility($event)\n }\n }\n }) : _vm._e(), _vm._v(\" \"), (_vm.maxlength && _vm.hasCounter && _vm.type !== 'number') ? _c('small', {\n staticClass: \"help counter\",\n class: {\n 'is-invisible': !_vm.isFocused\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.valueLength) + \" / \" + _vm._s(_vm.maxlength) + \"\\n \")]) : _vm._e()], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 107 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"autocomplete control\",\n class: {\n 'is-expanded': _vm.expanded\n }\n }, [_c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"size\": _vm.size,\n \"loading\": _vm.loading,\n \"rounded\": _vm.rounded,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"maxlength\": _vm.maxlength,\n \"autocomplete\": \"off\"\n },\n on: {\n \"focus\": _vm.focused,\n \"blur\": _vm.onBlur\n },\n nativeOn: {\n \"keyup\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"esc\", 27, $event.key)) { return null; }\n $event.preventDefault();\n _vm.isActive = false\n },\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"tab\", 9, $event.key)) { return null; }\n _vm.tabPressed($event)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n $event.preventDefault();\n _vm.enterPressed($event)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"up\", 38, $event.key)) { return null; }\n $event.preventDefault();\n _vm.keyArrows('up')\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"down\", 40, $event.key)) { return null; }\n $event.preventDefault();\n _vm.keyArrows('down')\n }]\n },\n model: {\n value: (_vm.newValue),\n callback: function($$v) {\n _vm.newValue = $$v\n },\n expression: \"newValue\"\n }\n }, 'b-input', _vm.$attrs, false)), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive && (_vm.data.length > 0 || _vm.hasEmptySlot || _vm.hasHeaderSlot)),\n expression: \"isActive && (data.length > 0 || hasEmptySlot || hasHeaderSlot)\"\n }],\n ref: \"dropdown\",\n staticClass: \"dropdown-menu\",\n class: {\n 'is-opened-top': !_vm.isListInViewportVertically\n }\n }, [_c('div', {\n staticClass: \"dropdown-content\"\n }, [(_vm.hasHeaderSlot) ? _c('div', {\n staticClass: \"dropdown-item\"\n }, [_vm._t(\"header\")], 2) : _vm._e(), _vm._v(\" \"), _vm._l((_vm.data), function(option, index) {\n return _c('a', {\n key: index,\n staticClass: \"dropdown-item\",\n class: {\n 'is-hovered': option === _vm.hovered\n },\n on: {\n \"click\": function($event) {\n _vm.setSelected(option)\n }\n }\n }, [(_vm.hasDefaultSlot) ? _vm._t(\"default\", null, {\n option: option,\n index: index\n }) : _c('span', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.getValue(option, true))\n }\n })], 2)\n }), _vm._v(\" \"), (_vm.data.length === 0 && _vm.hasEmptySlot) ? _c('div', {\n staticClass: \"dropdown-item is-disabled\"\n }, [_vm._t(\"empty\")], 2) : _vm._e()], 2)])])], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 108 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BCheckbox',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n type: String,\n disabled: Boolean,\n required: Boolean,\n name: String,\n size: String,\n trueValue: {\n type: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n default: true\n },\n falseValue: {\n type: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n default: false\n }\n },\n data: function data() {\n return {\n newValue: this.value\n };\n },\n\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n }\n }\n});\n\n/***/ }),\n/* 109 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('label', {\n ref: \"label\",\n staticClass: \"b-checkbox checkbox\",\n class: [_vm.size, {\n 'is-disabled': _vm.disabled\n }],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n }\n }\n }, [_c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"checkbox\",\n \"disabled\": _vm.disabled,\n \"required\": _vm.required,\n \"name\": _vm.name,\n \"true-value\": _vm.trueValue,\n \"false-value\": _vm.falseValue\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": Array.isArray(_vm.newValue) ? _vm._i(_vm.newValue, _vm.nativeValue) > -1 : _vm._q(_vm.newValue, _vm.trueValue)\n },\n on: {\n \"change\": function($event) {\n var $$a = _vm.newValue,\n $$el = $event.target,\n $$c = $$el.checked ? (_vm.trueValue) : (_vm.falseValue);\n if (Array.isArray($$a)) {\n var $$v = _vm.nativeValue,\n $$i = _vm._i($$a, $$v);\n if ($$el.checked) {\n $$i < 0 && (_vm.newValue = $$a.concat([$$v]))\n } else {\n $$i > -1 && (_vm.newValue = $$a.slice(0, $$i).concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.newValue = $$c\n }\n }\n }\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"check\",\n class: _vm.type\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"control-label\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 110 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(111),\n /* template */\n __webpack_require__(112),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 111 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BCheckboxButton',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n disabled: Boolean,\n name: String,\n size: String,\n type: {\n type: String,\n default: 'is-primary'\n }\n },\n data: function data() {\n return {\n newValue: this.value\n };\n },\n\n computed: {\n checked: function checked() {\n if (Array.isArray(this.newValue)) {\n return this.newValue.indexOf(this.nativeValue) >= 0;\n }\n return this.newValue === this.nativeValue;\n }\n },\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n }\n }\n});\n\n/***/ }),\n/* 112 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"control\"\n }, [_c('label', {\n ref: \"label\",\n staticClass: \"b-checkbox checkbox button\",\n class: [_vm.checked ? _vm.type : null, _vm.size, {\n 'is-disabled': _vm.disabled\n }],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n }\n }\n }, [_vm._t(\"default\"), _vm._v(\" \"), _c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"checkbox\",\n \"disabled\": _vm.disabled,\n \"name\": _vm.name\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": Array.isArray(_vm.newValue) ? _vm._i(_vm.newValue, _vm.nativeValue) > -1 : (_vm.newValue)\n },\n on: {\n \"change\": function($event) {\n var $$a = _vm.newValue,\n $$el = $event.target,\n $$c = $$el.checked ? (true) : (false);\n if (Array.isArray($$a)) {\n var $$v = _vm.nativeValue,\n $$i = _vm._i($$a, $$v);\n if ($$el.checked) {\n $$i < 0 && (_vm.newValue = $$a.concat([$$v]))\n } else {\n $$i > -1 && (_vm.newValue = $$a.slice(0, $$i).concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.newValue = $$c\n }\n }\n }\n })], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 113 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(114),\n /* template */\n __webpack_require__(115),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 114 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BCollapse',\n props: {\n open: {\n type: Boolean,\n default: true\n },\n animation: {\n type: String,\n default: 'fade'\n }\n },\n data: function data() {\n return {\n isOpen: this.open\n };\n },\n\n watch: {\n open: function open(value) {\n this.isOpen = value;\n }\n },\n methods: {\n /**\n * Toggle and emit events\n */\n toggle: function toggle() {\n this.isOpen = !this.isOpen;\n this.$emit('update:open', this.isOpen);\n this.$emit(this.isOpen ? 'open' : 'close');\n }\n }\n});\n\n/***/ }),\n/* 115 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"collapse\"\n }, [_c('div', {\n staticClass: \"collapse-trigger\",\n on: {\n \"click\": _vm.toggle\n }\n }, [_vm._t(\"trigger\", null, {\n open: _vm.isOpen\n })], 2), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isOpen),\n expression: \"isOpen\"\n }],\n staticClass: \"collapse-content\"\n }, [_vm._t(\"default\")], 2)])], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 116 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(117),\n /* template */\n __webpack_require__(134),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 117 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__ = __webpack_require__(12);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__ = __webpack_require__(43);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__ = __webpack_require__(44);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__input_Input__ = __webpack_require__(27);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__input_Input___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6__input_Input__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__field_Field__ = __webpack_require__(45);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__field_Field___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7__field_Field__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__select_Select__ = __webpack_require__(28);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__select_Select___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_8__select_Select__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_9__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__DatepickerTable__ = __webpack_require__(128);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__DatepickerTable___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_10__DatepickerTable__);\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDatepicker',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_10__DatepickerTable___default.a.name, __WEBPACK_IMPORTED_MODULE_10__DatepickerTable___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_6__input_Input___default.a.name, __WEBPACK_IMPORTED_MODULE_6__input_Input___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_7__field_Field___default.a.name, __WEBPACK_IMPORTED_MODULE_7__field_Field___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_8__select_Select___default.a.name, __WEBPACK_IMPORTED_MODULE_8__select_Select___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default.a.name, __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default.a.name, __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default.a), _components),\n mixins: [__WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: Date,\n dayNames: {\n type: Array,\n default: function _default() {\n if (Array.isArray(__WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDayNames)) {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDayNames;\n } else {\n return ['Su', 'M', 'Tu', 'W', 'Th', 'F', 'S'];\n }\n }\n },\n monthNames: {\n type: Array,\n default: function _default() {\n if (Array.isArray(__WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultMonthNames)) {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultMonthNames;\n } else {\n return ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\n }\n }\n },\n firstDayOfWeek: {\n type: Number,\n default: function _default() {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultFirstDayOfWeek === 'number') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultFirstDayOfWeek;\n } else {\n return 0;\n }\n }\n },\n inline: Boolean,\n minDate: Date,\n maxDate: Date,\n focusedDate: Date,\n placeholder: String,\n readonly: {\n type: Boolean,\n default: true\n },\n disabled: {\n type: Boolean,\n default: false\n },\n unselectableDates: Array,\n unselectableDaysOfWeek: {\n type: Array,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultUnselectableDaysOfWeek;\n }\n },\n selectableDates: Array,\n dateFormatter: {\n type: Function,\n default: function _default(date) {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDateFormatter === 'function') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDateFormatter(date);\n } else {\n var yyyyMMdd = date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();\n var d = new Date(yyyyMMdd);\n return d.toLocaleDateString();\n }\n }\n },\n dateParser: {\n type: Function,\n default: function _default(date) {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDateParser === 'function') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDateParser(date);\n } else {\n return new Date(Date.parse(date));\n }\n }\n },\n mobileNative: {\n type: Boolean,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDatepickerMobileNative;\n }\n },\n position: String,\n events: Array,\n indicators: {\n type: String,\n default: 'dots'\n }\n },\n data: function data() {\n var focusedDate = this.value || this.focusedDate || new Date();\n\n return {\n dateSelected: this.value,\n focusedDateData: {\n month: focusedDate.getMonth(),\n year: focusedDate.getFullYear()\n },\n _elementRef: 'input',\n _isDatepicker: true\n };\n },\n\n computed: {\n /*\n * Returns an array of years for the year dropdown. If earliest/latest\n * dates are set by props, range of years will fall within those dates.\n */\n listOfYears: function listOfYears() {\n var latestYear = this.maxDate ? this.maxDate.getFullYear() : Math.max(new Date().getFullYear(), this.focusedDateData.year) + 3;\n\n var earliestYear = this.minDate ? this.minDate.getFullYear() : 1900;\n\n var arrayOfYears = [];\n for (var i = earliestYear; i <= latestYear; i++) {\n arrayOfYears.push(i);\n }\n\n return arrayOfYears.reverse();\n },\n isFirstMonth: function isFirstMonth() {\n if (!this.minDate) return false;\n var dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month);\n var date = new Date(this.minDate.getFullYear(), this.minDate.getMonth());\n return dateToCheck <= date;\n },\n isLastMonth: function isLastMonth() {\n if (!this.maxDate) return false;\n var dateToCheck = new Date(this.focusedDateData.year, this.focusedDateData.month);\n var date = new Date(this.maxDate.getFullYear(), this.maxDate.getMonth());\n return dateToCheck >= date;\n },\n isMobile: function isMobile() {\n return this.mobileNative && __WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"d\" /* isMobile */].any();\n }\n },\n watch: {\n /*\n * Emit input event with selected date as payload, set isActive to false.\n * Update internal focusedDateData\n */\n dateSelected: function dateSelected(value) {\n var currentDate = !value ? new Date() : value;\n this.focusedDateData = {\n month: currentDate.getMonth(),\n year: currentDate.getFullYear()\n };\n this.$emit('input', value);\n if (this.$refs.dropdown) {\n this.$refs.dropdown.isActive = false;\n }\n },\n\n\n /**\n * When v-model is changed:\n * 1. Update internal value.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.dateSelected = _value;\n\n !this.isValid && this.$refs.input.checkHtml5Validity();\n },\n focusedDate: function focusedDate(value) {\n if (value) {\n this.focusedDateData = {\n month: value.getMonth(),\n year: value.getFullYear()\n };\n }\n },\n\n\n /*\n * Emit input event on month and/or year change\n */\n 'focusedDateData.month': function focusedDateDataMonth(value) {\n this.$emit('change-month', value);\n },\n 'focusedDateData.year': function focusedDateDataYear(value) {\n this.$emit('change-year', value);\n }\n },\n methods: {\n /*\n * Emit input event with selected date as payload for v-model in parent\n */\n updateSelectedDate: function updateSelectedDate(date) {\n this.dateSelected = date;\n },\n\n\n /*\n * Parse string into date\n */\n onChange: function onChange(value) {\n var date = this.dateParser(value);\n if (date && !isNaN(date)) {\n this.dateSelected = date;\n } else {\n // Force refresh input value when not valid date\n this.dateSelected = null;\n this.$refs.input.newValue = this.dateSelected;\n }\n },\n\n\n /*\n * Format date into string\n */\n formatValue: function formatValue(value) {\n if (value && !isNaN(value)) {\n return this.dateFormatter(value);\n } else {\n return null;\n }\n },\n\n\n /*\n * Either decrement month by 1 if not January or decrement year by 1\n * and set month to 11 (December)\n */\n decrementMonth: function decrementMonth() {\n if (this.disabled) return;\n\n if (this.focusedDateData.month > 0) {\n this.focusedDateData.month -= 1;\n } else {\n this.focusedDateData.month = 11;\n this.focusedDateData.year -= 1;\n }\n },\n\n\n /*\n * Either increment month by 1 if not December or increment year by 1\n * and set month to 0 (January)\n */\n incrementMonth: function incrementMonth() {\n if (this.disabled) return;\n\n if (this.focusedDateData.month < 11) {\n this.focusedDateData.month += 1;\n } else {\n this.focusedDateData.month = 0;\n this.focusedDateData.year += 1;\n }\n },\n\n\n /*\n * Format date into string 'YYYY-MM-DD'\n */\n formatYYYYMMDD: function formatYYYYMMDD(value) {\n var date = new Date(value);\n if (value && !isNaN(date)) {\n var year = date.getFullYear();\n var month = date.getMonth() + 1;\n var day = date.getDate();\n return year + '-' + ((month < 10 ? '0' : '') + month) + '-' + ((day < 10 ? '0' : '') + day);\n }\n return '';\n },\n\n\n /*\n * Parse date from string\n */\n onChangeNativePicker: function onChangeNativePicker(event) {\n var date = event.target.value;\n this.dateSelected = date ? new Date(date.replace(/-/g, '/')) : null;\n }\n }\n});\n\n/***/ }),\n/* 118 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator__ = __webpack_require__(59);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol__);\n\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDropdown',\n props: {\n value: {\n type: [String, Number, Boolean, Object, Array, __WEBPACK_IMPORTED_MODULE_1_babel_runtime_core_js_symbol___default.a, Function],\n default: null\n },\n disabled: Boolean,\n hoverable: Boolean,\n inline: Boolean,\n position: {\n type: String,\n validator: function validator(value) {\n return ['is-top-right', 'is-top-left', 'is-bottom-left'].indexOf(value) > -1;\n }\n },\n mobileModal: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n return {\n selected: this.value,\n isActive: false,\n _isDropdown: true // Used internally by DropdownItem\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return [this.position, {\n 'is-disabled': this.disabled,\n 'is-hoverable': this.hoverable,\n 'is-inline': this.inline,\n 'is-active': this.isActive || this.inline,\n 'is-mobile-modal': this.isMobileModal\n }];\n },\n isMobileModal: function isMobileModal() {\n return this.mobileModal && !this.inline && !this.hoverable;\n }\n },\n watch: {\n /**\n * When v-model is changed set the new selected item.\n */\n value: function value(_value) {\n this.selected = _value;\n },\n\n\n /**\n * Emit event when isActive value is changed.\n */\n isActive: function isActive(value) {\n this.$emit('active-change', value);\n }\n },\n methods: {\n /**\n * Click listener from DropdownItem.\n * 1. Set new selected item.\n * 2. Emit input event to update the user v-model.\n * 3. Close the dropdown.\n */\n selectItem: function selectItem(value) {\n if (this.selected !== value) {\n this.$emit('change', value);\n this.selected = value;\n }\n this.$emit('input', value);\n this.isActive = false;\n },\n\n\n /**\n * White-listed items to not close when clicked.\n */\n isInWhiteList: function isInWhiteList(el) {\n if (el === this.$refs.dropdownMenu) return true;\n if (el === this.$refs.trigger) return true;\n // All chidren from dropdown\n if (this.$refs.dropdownMenu !== undefined) {\n var children = this.$refs.dropdownMenu.querySelectorAll('*');\n var _iteratorNormalCompletion = true;\n var _didIteratorError = false;\n var _iteratorError = undefined;\n\n try {\n for (var _iterator = __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator___default()(children), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {\n var child = _step.value;\n\n if (el === child) {\n return true;\n }\n }\n } catch (err) {\n _didIteratorError = true;\n _iteratorError = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion && _iterator.return) {\n _iterator.return();\n }\n } finally {\n if (_didIteratorError) {\n throw _iteratorError;\n }\n }\n }\n }\n // All children from trigger\n if (this.$refs.trigger !== undefined) {\n var _children = this.$refs.trigger.querySelectorAll('*');\n var _iteratorNormalCompletion2 = true;\n var _didIteratorError2 = false;\n var _iteratorError2 = undefined;\n\n try {\n for (var _iterator2 = __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_get_iterator___default()(_children), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {\n var _child = _step2.value;\n\n if (el === _child) {\n return true;\n }\n }\n } catch (err) {\n _didIteratorError2 = true;\n _iteratorError2 = err;\n } finally {\n try {\n if (!_iteratorNormalCompletion2 && _iterator2.return) {\n _iterator2.return();\n }\n } finally {\n if (_didIteratorError2) {\n throw _iteratorError2;\n }\n }\n }\n }\n\n return false;\n },\n\n\n /**\n * Close dropdown if clicked outside.\n */\n clickedOutside: function clickedOutside(event) {\n if (this.inline) return;\n\n if (!this.isInWhiteList(event.target)) this.isActive = false;\n },\n\n\n /**\n * Toggle dropdown if it's not disabled.\n */\n toggle: function toggle() {\n var _this = this;\n\n if (this.disabled || this.hoverable) return;\n\n if (!this.isActive) {\n // if not active, toggle after clickOutside event\n // this fixes toggling programmatic\n this.$nextTick(function () {\n _this.isActive = !_this.isActive;\n });\n } else {\n this.isActive = !this.isActive;\n }\n }\n },\n created: function created() {\n if (typeof window !== 'undefined') {\n document.addEventListener('click', this.clickedOutside);\n }\n },\n beforeDestroy: function beforeDestroy() {\n if (typeof window !== 'undefined') {\n document.removeEventListener('click', this.clickedOutside);\n }\n }\n});\n\n/***/ }),\n/* 119 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"dropdown\",\n class: _vm.rootClasses\n }, [(!_vm.inline) ? _c('div', {\n ref: \"trigger\",\n staticClass: \"dropdown-trigger\",\n attrs: {\n \"role\": \"button\"\n },\n on: {\n \"click\": _vm.toggle\n }\n }, [_vm._t(\"trigger\")], 2) : _vm._e(), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [(_vm.isMobileModal) ? _c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive),\n expression: \"isActive\"\n }],\n staticClass: \"background\"\n }) : _vm._e()]), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: ((!_vm.disabled && (_vm.isActive || _vm.hoverable)) || _vm.inline),\n expression: \"(!disabled && (isActive || hoverable)) || inline\"\n }],\n ref: \"dropdownMenu\",\n staticClass: \"dropdown-menu\"\n }, [_c('div', {\n staticClass: \"dropdown-content\"\n }, [_vm._t(\"default\")], 2)])])], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 120 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDropdownItem',\n props: {\n value: {\n type: [String, Number, Boolean, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a, Function],\n default: null\n },\n separator: Boolean,\n disabled: Boolean,\n custom: Boolean,\n paddingless: Boolean,\n hasLink: Boolean\n },\n computed: {\n anchorClasses: function anchorClasses() {\n return {\n 'is-disabled': this.$parent.disabled || this.disabled,\n 'is-paddingless': this.paddingless,\n 'is-active': this.value !== null && this.value === this.$parent.selected\n };\n },\n itemClasses: function itemClasses() {\n return {\n 'dropdown-item': !this.hasLink,\n 'is-disabled': this.disabled,\n 'is-paddingless': this.paddingless,\n 'is-active': this.value !== null && this.value === this.$parent.selected,\n 'has-link': this.hasLink\n };\n },\n\n /**\n * Check if item can be clickable.\n */\n isClickable: function isClickable() {\n return !this.$parent.disabled && !this.separator && !this.disabled && !this.custom;\n }\n },\n methods: {\n /**\n * Click listener, select the item.\n */\n selectItem: function selectItem() {\n if (!this.isClickable) return;\n\n this.$parent.selectItem(this.value);\n this.$emit('click');\n }\n },\n created: function created() {\n if (!this.$parent.$data._isDropdown) {\n this.$destroy();\n throw new Error('You should wrap bDropdownItem on a bDropdown');\n }\n }\n});\n\n/***/ }),\n/* 121 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return (_vm.separator) ? _c('hr', {\n staticClass: \"dropdown-divider\"\n }) : (!_vm.custom && !_vm.hasLink) ? _c('a', {\n staticClass: \"dropdown-item\",\n class: _vm.anchorClasses,\n on: {\n \"click\": _vm.selectItem\n }\n }, [_vm._t(\"default\")], 2) : _c('div', {\n class: _vm.itemClasses,\n on: {\n \"click\": _vm.selectItem\n }\n }, [_vm._t(\"default\")], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 122 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__FieldBody__ = __webpack_require__(123);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__FieldBody___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__FieldBody__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BField',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__FieldBody___default.a.name, __WEBPACK_IMPORTED_MODULE_1__FieldBody___default.a),\n props: {\n type: String,\n label: String,\n labelFor: String,\n message: [String, Array],\n grouped: Boolean,\n groupMultiline: Boolean,\n position: String,\n expanded: Boolean,\n horizontal: Boolean,\n addons: {\n type: Boolean,\n default: true\n },\n customClass: String\n },\n data: function data() {\n return {\n newType: this.type,\n newMessage: this.message,\n fieldLabelSize: null,\n _isField: true // Used internally by Input and Select\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return [this.newPosition, {\n 'is-expanded': this.expanded,\n 'is-grouped-multiline': this.groupMultiline,\n 'is-horizontal': this.horizontal\n }];\n },\n\n /**\n * Correct Bulma class for the side of the addon or group.\n *\n * This is not kept like the others (is-small, etc.),\n * because since 'has-addons' is set automatically it\n * doesn't make sense to teach users what addons are exactly.\n */\n newPosition: function newPosition() {\n if (this.position === undefined) return;\n\n var position = this.position.split('-');\n if (position.length < 1) return;\n\n var prefix = this.grouped ? 'is-grouped-' : 'has-addons-';\n\n if (this.position) return prefix + position[1];\n },\n\n /**\n * Formatted message in case it's an array\n * (each element is separated by <br> tag)\n */\n formattedMessage: function formattedMessage() {\n if (this.newMessage) {\n if (Array.isArray(this.newMessage)) {\n return this.newMessage.filter(function (value) {\n if (value) {\n return value;\n }\n }).join(' <br> ');\n } else {\n return this.newMessage;\n }\n } else {\n return this.newMessage;\n }\n }\n },\n watch: {\n /**\n * Set internal type when prop change.\n */\n type: function type(value) {\n this.newType = value;\n },\n\n\n /**\n * Set internal message when prop change.\n */\n message: function message(value) {\n this.newMessage = value;\n }\n },\n methods: {\n /**\n * Field has addons if there are more than one slot\n * (element / component) in the Field.\n * Or is grouped when prop is set.\n * Is a method to be called when component re-render.\n */\n fieldType: function fieldType() {\n if (this.grouped) return 'is-grouped';\n\n var renderedNode = 0;\n if (this.$slots.default) {\n renderedNode = this.$slots.default.reduce(function (i, node) {\n return node.tag ? i + 1 : i;\n }, 0);\n }\n if (renderedNode > 1 && this.addons && !this.horizontal) {\n return 'has-addons';\n }\n }\n },\n mounted: function mounted() {\n if (this.horizontal) {\n // Bulma docs: .is-normal for any .input or .button\n var elements = this.$el.querySelectorAll('.input, .select, .button, .textarea');\n if (elements.length > 0) {\n this.fieldLabelSize = 'is-normal';\n }\n }\n }\n});\n\n/***/ }),\n/* 123 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(124),\n /* template */\n null,\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 124 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BFieldBody',\n props: {\n message: {\n type: [String]\n },\n type: {\n type: [String]\n }\n },\n render: function render(h) {\n var _this = this;\n\n return h('div', { attrs: { 'class': 'field-body' } }, this.$slots.default.map(function (v) {\n // skip returns and comments\n if (!v.tag) {\n return v;\n }\n if (_this.message) {\n return h('b-field', { attrs: { message: _this.message, 'type': _this.type } }, [v]);\n }\n return h('b-field', { attrs: { 'type': _this.type } }, [v]);\n }));\n }\n});\n\n/***/ }),\n/* 125 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"field\",\n class: [_vm.rootClasses, _vm.fieldType()]\n }, [(_vm.horizontal) ? _c('div', {\n staticClass: \"field-label\",\n class: [_vm.customClass, _vm.fieldLabelSize]\n }, [(_vm.label) ? _c('label', {\n staticClass: \"label\",\n attrs: {\n \"for\": _vm.labelFor\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.label) + \"\\n \")]) : _vm._e()]) : [(_vm.label) ? _c('label', {\n staticClass: \"label\",\n class: _vm.customClass,\n attrs: {\n \"for\": _vm.labelFor\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.label) + \"\\n \")]) : _vm._e()], _vm._v(\" \"), (_vm.horizontal) ? _c('b-field-body', {\n attrs: {\n \"message\": _vm.newMessage ? _vm.formattedMessage : '',\n \"type\": _vm.newType\n }\n }, [_vm._t(\"default\")], 2) : [_vm._t(\"default\")], _vm._v(\" \"), (_vm.newMessage && !_vm.horizontal) ? _c('p', {\n staticClass: \"help\",\n class: _vm.newType,\n domProps: {\n \"innerHTML\": _vm._s(_vm.formattedMessage)\n }\n }) : _vm._e()], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 126 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__ = __webpack_require__(12);\n\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BSelect',\n components: __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_3__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: {\n type: [String, Number, Boolean, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a, Function],\n default: null\n },\n placeholder: String,\n multiple: Boolean,\n nativeSize: [String, Number]\n },\n data: function data() {\n return {\n selected: this.value,\n _isSelect: true,\n _elementRef: 'select'\n };\n },\n\n computed: {\n spanClasses: function spanClasses() {\n return [this.size, this.statusType, {\n 'is-fullwidth': this.expanded,\n 'is-loading': this.loading,\n 'is-multiple': this.multiple,\n 'is-rounded': this.rounded,\n 'is-empty': this.selected === null\n }];\n }\n },\n watch: {\n /**\n * When v-model is changed:\n * 1. Set the selected option.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.selected = _value;\n !this.isValid && this.checkHtml5Validity();\n },\n\n /**\n * When selected:\n * 1. Emit input event to update the user v-model.\n * 2. If it's invalid, validate again.\n */\n selected: function selected(value) {\n this.$emit('input', value);\n !this.isValid && this.checkHtml5Validity();\n }\n }\n});\n\n/***/ }),\n/* 127 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"control\",\n class: {\n 'is-expanded': _vm.expanded, 'has-icons-left': _vm.icon\n }\n }, [_c('span', {\n staticClass: \"select\",\n class: _vm.spanClasses\n }, [_c('select', _vm._b({\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.selected),\n expression: \"selected\"\n }],\n ref: \"select\",\n attrs: {\n \"multiple\": _vm.multiple,\n \"size\": _vm.nativeSize\n },\n on: {\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n },\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"change\": function($event) {\n var $$selectedVal = Array.prototype.filter.call($event.target.options, function(o) {\n return o.selected\n }).map(function(o) {\n var val = \"_value\" in o ? o._value : o.value;\n return val\n });\n _vm.selected = $event.target.multiple ? $$selectedVal : $$selectedVal[0]\n }\n }\n }, 'select', _vm.$attrs, false), [(_vm.placeholder) ? _c('option', {\n attrs: {\n \"selected\": \"\",\n \"disabled\": \"\",\n \"hidden\": \"\"\n },\n domProps: {\n \"value\": null\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.placeholder) + \"\\n \")]) : _vm._e(), _vm._v(\" \"), _vm._t(\"default\")], 2)]), _vm._v(\" \"), (_vm.icon) ? _c('b-icon', {\n staticClass: \"is-left\",\n attrs: {\n \"icon\": _vm.icon,\n \"pack\": _vm.iconPack,\n \"size\": _vm.iconSize\n }\n }) : _vm._e()], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 128 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(129),\n /* template */\n __webpack_require__(133),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 129 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow__ = __webpack_require__(130);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDatepickerTable',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow___default.a.name, __WEBPACK_IMPORTED_MODULE_1__DatepickerTableRow___default.a),\n props: {\n value: Date,\n dayNames: Array,\n monthNames: Array,\n firstDayOfWeek: Number,\n events: Array,\n indicators: String,\n minDate: Date,\n maxDate: Date,\n focused: Object,\n disabled: Boolean,\n unselectableDates: Array,\n unselectableDaysOfWeek: Array,\n selectableDates: Array\n },\n computed: {\n visibleDayNames: function visibleDayNames() {\n var visibleDayNames = [];\n var index = this.firstDayOfWeek;\n while (visibleDayNames.length < this.dayNames.length) {\n var currentDayName = this.dayNames[index % this.dayNames.length];\n visibleDayNames.push(currentDayName);\n index++;\n }\n return visibleDayNames;\n },\n hasEvents: function hasEvents() {\n return this.events && this.events.length;\n },\n\n\n /*\n * Return array of all events in the specified month\n */\n eventsInThisMonth: function eventsInThisMonth() {\n if (!this.events) return [];\n\n var monthEvents = [];\n\n for (var i = 0; i < this.events.length; i++) {\n var event = this.events[i];\n\n if (!event.hasOwnProperty('date')) {\n event = { date: event };\n }\n if (!event.hasOwnProperty('type')) {\n event.type = 'is-primary';\n }\n if (event.date.getMonth() === this.focused.month && event.date.getFullYear() === this.focused.year) {\n monthEvents.push(event);\n }\n }\n\n return monthEvents;\n }\n },\n methods: {\n /*\n * Emit input event with selected date as payload for v-model in parent\n */\n updateSelectedDate: function updateSelectedDate(date) {\n this.$emit('input', date);\n },\n\n\n /*\n * Return array of all days in the week that the startingDate is within\n */\n weekBuilder: function weekBuilder(startingDate, month, year) {\n var thisMonth = new Date(year, month);\n\n var thisWeek = [];\n\n var dayOfWeek = new Date(year, month, startingDate).getDay();\n\n var end = dayOfWeek >= this.firstDayOfWeek ? dayOfWeek - this.firstDayOfWeek : 7 - this.firstDayOfWeek + dayOfWeek;\n\n var daysAgo = 1;\n for (var i = 0; i < end; i++) {\n thisWeek.unshift(new Date(thisMonth.getFullYear(), thisMonth.getMonth(), startingDate - daysAgo));\n daysAgo++;\n }\n\n thisWeek.push(new Date(year, month, startingDate));\n\n var daysForward = 1;\n while (thisWeek.length < 7) {\n thisWeek.push(new Date(year, month, startingDate + daysForward));\n daysForward++;\n }\n\n return thisWeek;\n },\n\n\n /*\n * Return array of all weeks in the specified month\n */\n weeksInThisMonth: function weeksInThisMonth(month, year) {\n var weeksInThisMonth = [];\n var daysInThisMonth = new Date(year, month + 1, 0).getDate();\n\n var startingDay = 1;\n\n while (startingDay <= daysInThisMonth + 6) {\n var newWeek = this.weekBuilder(startingDay, month, year);\n var weekValid = false;\n\n newWeek.forEach(function (day) {\n if (day.getMonth() === month) {\n weekValid = true;\n }\n });\n\n if (weekValid) {\n weeksInThisMonth.push(newWeek);\n }\n\n startingDay += 7;\n }\n\n return weeksInThisMonth;\n },\n eventsInThisWeek: function eventsInThisWeek(week, index) {\n if (!this.eventsInThisMonth.length) return [];\n\n var weekEvents = [];\n\n var weeksInThisMonth = [];\n weeksInThisMonth = this.weeksInThisMonth(this.focused.month, this.focused.year);\n\n for (var d = 0; d < weeksInThisMonth[index].length; d++) {\n for (var e = 0; e < this.eventsInThisMonth.length; e++) {\n var eventsInThisMonth = this.eventsInThisMonth[e].date.getTime();\n if (eventsInThisMonth === weeksInThisMonth[index][d].getTime()) {\n weekEvents.push(this.eventsInThisMonth[e]);\n }\n }\n }\n\n return weekEvents;\n }\n }\n});\n\n/***/ }),\n/* 130 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(131),\n /* template */\n __webpack_require__(132),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 131 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDatepickerTableRow',\n props: {\n selectedDate: Date,\n week: {\n type: Array,\n required: true\n },\n month: {\n type: Number,\n required: true\n },\n minDate: Date,\n maxDate: Date,\n disabled: Boolean,\n unselectableDates: Array,\n unselectableDaysOfWeek: Array,\n selectableDates: Array,\n events: Array,\n indicators: String\n },\n methods: {\n /*\n * Check that selected day is within earliest/latest params and\n * is within this month\n */\n selectableDate: function selectableDate(day) {\n var validity = [];\n\n if (this.minDate) {\n validity.push(day >= this.minDate);\n }\n\n if (this.maxDate) {\n validity.push(day <= this.maxDate);\n }\n\n validity.push(day.getMonth() === this.month);\n\n if (this.selectableDates) {\n for (var i = 0; i < this.selectableDates.length; i++) {\n var enabledDate = this.selectableDates[i];\n if (day.getDate() === enabledDate.getDate() && day.getFullYear() === enabledDate.getFullYear() && day.getMonth() === enabledDate.getMonth()) {\n return true;\n } else {\n validity.push(false);\n }\n }\n }\n\n if (this.unselectableDates) {\n for (var _i = 0; _i < this.unselectableDates.length; _i++) {\n var disabledDate = this.unselectableDates[_i];\n validity.push(day.getDate() !== disabledDate.getDate() || day.getFullYear() !== disabledDate.getFullYear() || day.getMonth() !== disabledDate.getMonth());\n }\n }\n\n if (this.unselectableDaysOfWeek) {\n for (var _i2 = 0; _i2 < this.unselectableDaysOfWeek.length; _i2++) {\n var dayOfWeek = this.unselectableDaysOfWeek[_i2];\n validity.push(day.getDay() !== dayOfWeek);\n }\n }\n\n return validity.indexOf(false) < 0;\n },\n\n\n /*\n * Emit select event with chosen date as payload\n */\n emitChosenDate: function emitChosenDate(day) {\n if (this.disabled) return;\n\n if (this.selectableDate(day)) {\n this.$emit('select', day);\n }\n },\n eventsDateMatch: function eventsDateMatch(day) {\n if (!this.events.length) return false;\n\n var dayEvents = [];\n\n for (var i = 0; i < this.events.length; i++) {\n if (this.events[i].date.getDay() === day.getDay()) {\n dayEvents.push(this.events[i]);\n }\n }\n\n if (!dayEvents.length) {\n return false;\n }\n\n return dayEvents;\n },\n\n\n /*\n * Build classObject for cell using validations\n */\n classObject: function classObject(day) {\n function dateMatch(dateOne, dateTwo) {\n // if either date is null or undefined, return false\n if (!dateOne || !dateTwo) {\n return false;\n }\n\n return dateOne.getDate() === dateTwo.getDate() && dateOne.getFullYear() === dateTwo.getFullYear() && dateOne.getMonth() === dateTwo.getMonth();\n }\n\n return {\n 'is-selected': dateMatch(day, this.selectedDate),\n 'is-today': dateMatch(day, new Date()),\n 'is-selectable': this.selectableDate(day) && !this.disabled,\n 'is-unselectable': !this.selectableDate(day) || this.disabled\n };\n }\n }\n});\n\n/***/ }),\n/* 132 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"datepicker-row\"\n }, [_vm._l((_vm.week), function(day, index) {\n return [(_vm.selectableDate(day) && !_vm.disabled) ? _c('a', {\n key: index,\n staticClass: \"datepicker-cell\",\n class: [_vm.classObject(day), {\n 'has-event': _vm.eventsDateMatch(day)\n }, _vm.indicators],\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.emitChosenDate(day)\n },\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n $event.preventDefault();\n _vm.emitChosenDate(day)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.emitChosenDate(day)\n }]\n }\n }, [_vm._v(\"\\n \" + _vm._s(day.getDate()) + \"\\n\\n \"), (_vm.eventsDateMatch(day)) ? _c('div', {\n staticClass: \"events\"\n }, _vm._l((_vm.eventsDateMatch(day)), function(event, index) {\n return _c('div', {\n key: index,\n staticClass: \"event\",\n class: event.type\n })\n })) : _vm._e()]) : _c('div', {\n key: index,\n staticClass: \"datepicker-cell\",\n class: _vm.classObject(day)\n }, [_vm._v(\"\\n \" + _vm._s(day.getDate()) + \"\\n \")])]\n })], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 133 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('section', {\n staticClass: \"datepicker-table\"\n }, [_c('header', {\n staticClass: \"datepicker-header\"\n }, _vm._l((_vm.visibleDayNames), function(day, index) {\n return _c('div', {\n key: index,\n staticClass: \"datepicker-cell\"\n }, [_vm._v(\"\\n \" + _vm._s(day) + \"\\n \")])\n })), _vm._v(\" \"), _c('div', {\n staticClass: \"datepicker-body\",\n class: {\n 'has-events': _vm.hasEvents\n }\n }, _vm._l((_vm.weeksInThisMonth(_vm.focused.month, _vm.focused.year)), function(week, index) {\n return _c('b-datepicker-table-row', {\n key: index,\n attrs: {\n \"selected-date\": _vm.value,\n \"week\": week,\n \"month\": _vm.focused.month,\n \"min-date\": _vm.minDate,\n \"max-date\": _vm.maxDate,\n \"disabled\": _vm.disabled,\n \"unselectable-dates\": _vm.unselectableDates,\n \"unselectable-days-of-week\": _vm.unselectableDaysOfWeek,\n \"selectable-dates\": _vm.selectableDates,\n \"events\": _vm.eventsInThisWeek(week, index),\n \"indicators\": _vm.indicators\n },\n on: {\n \"select\": _vm.updateSelectedDate\n }\n })\n }))])\n},staticRenderFns: []}\n\n/***/ }),\n/* 134 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"datepicker control\",\n class: [_vm.size, {\n 'is-expanded': _vm.expanded\n }]\n }, [(!_vm.isMobile || _vm.inline) ? _c('b-dropdown', {\n ref: \"dropdown\",\n attrs: {\n \"position\": _vm.position,\n \"disabled\": _vm.disabled,\n \"inline\": _vm.inline\n }\n }, [(!_vm.inline) ? _c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"slot\": \"trigger\",\n \"autocomplete\": \"off\",\n \"value\": _vm.formatValue(_vm.dateSelected),\n \"placeholder\": _vm.placeholder,\n \"size\": _vm.size,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"rounded\": _vm.rounded,\n \"loading\": _vm.loading,\n \"disabled\": _vm.disabled,\n \"readonly\": _vm.readonly\n },\n on: {\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n }\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onChange($event.target.value)\n }\n },\n slot: \"trigger\"\n }, 'b-input', _vm.$attrs, false)) : _vm._e(), _vm._v(\" \"), _c('b-dropdown-item', {\n attrs: {\n \"disabled\": _vm.disabled,\n \"custom\": \"\"\n }\n }, [_c('header', {\n staticClass: \"datepicker-header\"\n }, [(_vm.$slots.header !== undefined && _vm.$slots.header.length) ? [_vm._t(\"header\")] : _c('div', {\n staticClass: \"pagination field is-centered\"\n }, [(!_vm.isFirstMonth && !_vm.disabled) ? _c('a', {\n staticClass: \"pagination-previous\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.decrementMonth($event)\n },\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n $event.preventDefault();\n _vm.decrementMonth($event)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.decrementMonth($event)\n }]\n }\n }, [_c('b-icon', {\n attrs: {\n \"icon\": \"chevron-left\",\n \"pack\": _vm.iconPack,\n \"both\": \"\",\n \"type\": \"is-primary is-clickable\"\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _c('a', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (!_vm.isLastMonth && !_vm.disabled),\n expression: \"!isLastMonth && !disabled\"\n }],\n staticClass: \"pagination-next\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.incrementMonth($event)\n },\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n $event.preventDefault();\n _vm.incrementMonth($event)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.incrementMonth($event)\n }]\n }\n }, [_c('b-icon', {\n attrs: {\n \"icon\": \"chevron-right\",\n \"pack\": _vm.iconPack,\n \"both\": \"\",\n \"type\": \"is-primary is-clickable\"\n }\n })], 1), _vm._v(\" \"), _c('div', {\n staticClass: \"pagination-list\"\n }, [_c('b-field', [_c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled\n },\n model: {\n value: (_vm.focusedDateData.month),\n callback: function($$v) {\n _vm.$set(_vm.focusedDateData, \"month\", $$v)\n },\n expression: \"focusedDateData.month\"\n }\n }, _vm._l((_vm.monthNames), function(month, index) {\n return _c('option', {\n key: month,\n domProps: {\n \"value\": index\n }\n }, [_vm._v(\"\\n \" + _vm._s(month) + \"\\n \")])\n })), _vm._v(\" \"), _c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled\n },\n model: {\n value: (_vm.focusedDateData.year),\n callback: function($$v) {\n _vm.$set(_vm.focusedDateData, \"year\", $$v)\n },\n expression: \"focusedDateData.year\"\n }\n }, _vm._l((_vm.listOfYears), function(year) {\n return _c('option', {\n key: year,\n domProps: {\n \"value\": year\n }\n }, [_vm._v(\"\\n \" + _vm._s(year) + \"\\n \")])\n }))], 1)], 1)])], 2), _vm._v(\" \"), _c('b-datepicker-table', {\n attrs: {\n \"day-names\": _vm.dayNames,\n \"month-names\": _vm.monthNames,\n \"first-day-of-week\": _vm.firstDayOfWeek,\n \"min-date\": _vm.minDate,\n \"max-date\": _vm.maxDate,\n \"focused\": _vm.focusedDateData,\n \"disabled\": _vm.disabled,\n \"unselectable-dates\": _vm.unselectableDates,\n \"unselectable-days-of-week\": _vm.unselectableDaysOfWeek,\n \"selectable-dates\": _vm.selectableDates,\n \"events\": _vm.events,\n \"indicators\": _vm.indicators\n },\n on: {\n \"close\": function($event) {\n _vm.$refs.dropdown.isActive = false\n }\n },\n model: {\n value: (_vm.dateSelected),\n callback: function($$v) {\n _vm.dateSelected = $$v\n },\n expression: \"dateSelected\"\n }\n }), _vm._v(\" \"), (_vm.$slots.default !== undefined && _vm.$slots.default.length) ? _c('footer', {\n staticClass: \"datepicker-footer\"\n }, [_vm._t(\"default\")], 2) : _vm._e()], 1)], 1) : _c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"type\": \"date\",\n \"autocomplete\": \"off\",\n \"value\": _vm.formatYYYYMMDD(_vm.value),\n \"placeholder\": _vm.placeholder,\n \"size\": _vm.size,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"loading\": _vm.loading,\n \"max\": _vm.formatYYYYMMDD(_vm.maxDate),\n \"min\": _vm.formatYYYYMMDD(_vm.minDate),\n \"disabled\": _vm.disabled,\n \"readonly\": false\n },\n on: {\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n }\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onChangeNativePicker($event)\n }\n }\n }, 'b-input', _vm.$attrs, false))], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 135 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(136),\n /* template */\n __webpack_require__(139),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 136 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__modal_Modal__ = __webpack_require__(62);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__modal_Modal___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__modal_Modal__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__utils_BaseElementMixin__ = __webpack_require__(13);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BDialog',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a),\n extends: __WEBPACK_IMPORTED_MODULE_2__modal_Modal___default.a,\n mixins: [__WEBPACK_IMPORTED_MODULE_5__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n title: String,\n message: String,\n icon: String,\n hasIcon: Boolean,\n type: {\n type: String,\n default: 'is-primary'\n },\n size: String,\n confirmText: {\n type: String,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDialogConfirmText ? __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDialogConfirmText : 'OK';\n }\n },\n cancelText: {\n type: String,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDialogCancelText ? __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultDialogCancelText : 'Cancel';\n }\n },\n hasInput: Boolean, // Used internally to know if it's prompt\n inputAttrs: {\n type: Object,\n default: function _default() {\n return {};\n }\n },\n onConfirm: {\n type: Function,\n default: function _default() {}\n },\n focusOn: {\n type: String,\n default: 'confirm'\n }\n },\n data: function data() {\n var prompt = this.hasInput ? this.inputAttrs.value || '' : '';\n\n return {\n prompt: prompt,\n isActive: false,\n validationMessage: ''\n };\n },\n\n computed: {\n /**\n * Icon name (MDI) based on the type.\n */\n iconByType: function iconByType() {\n switch (this.type) {\n case 'is-info':\n return 'information';\n case 'is-success':\n return 'check-circle';\n case 'is-warning':\n return 'alert';\n case 'is-danger':\n return 'alert-circle';\n default:\n return null;\n }\n },\n showCancel: function showCancel() {\n return this.cancelOptions.indexOf('button') >= 0;\n }\n },\n methods: {\n /**\n * If it's a prompt Dialog, validate the input.\n * Call the onConfirm prop (function) and close the Dialog.\n */\n confirm: function confirm() {\n var _this = this;\n\n if (this.$refs.input !== undefined) {\n if (!this.$refs.input.checkValidity()) {\n this.validationMessage = this.$refs.input.validationMessage;\n this.$nextTick(function () {\n return _this.$refs.input.select();\n });\n return;\n }\n }\n\n this.onConfirm(this.prompt);\n this.close();\n },\n\n\n /**\n * Close the Dialog.\n */\n close: function close() {\n var _this2 = this;\n\n this.isActive = false;\n // Timeout for the animation complete before destroying\n setTimeout(function () {\n _this2.$destroy();\n Object(__WEBPACK_IMPORTED_MODULE_4__utils_helpers__[\"e\" /* removeElement */])(_this2.$el);\n }, 150);\n }\n },\n beforeMount: function beforeMount() {\n // Insert the Dialog component in body tag\n document.body.appendChild(this.$el);\n },\n mounted: function mounted() {\n var _this3 = this;\n\n this.isActive = true;\n\n if (typeof this.inputAttrs.required === 'undefined') {\n this.$set(this.inputAttrs, 'required', true);\n }\n\n this.$nextTick(function () {\n // Handle which element receives focus\n if (_this3.hasInput) {\n _this3.$refs.input.focus();\n } else if (_this3.focusOn === 'cancel' && _this3.showCancel) {\n _this3.$refs.cancelButton.focus();\n } else {\n _this3.$refs.confirmButton.focus();\n }\n });\n }\n});\n\n/***/ }),\n/* 137 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_config__ = __webpack_require__(2);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BModal',\n props: {\n active: Boolean,\n component: [Object, Function],\n content: String,\n programmatic: Boolean,\n props: Object,\n events: Object,\n width: {\n type: [String, Number],\n default: 960\n },\n hasModalCard: Boolean,\n animation: {\n type: String,\n default: 'zoom-out'\n },\n canCancel: {\n type: [Array, Boolean],\n default: function _default() {\n return ['escape', 'x', 'outside', 'button'];\n }\n },\n onCancel: {\n type: Function,\n default: function _default() {}\n },\n scroll: {\n type: String,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_1__utils_config__[\"a\" /* default */].defaultModalScroll ? __WEBPACK_IMPORTED_MODULE_1__utils_config__[\"a\" /* default */].defaultModalScroll : 'clip';\n },\n validator: function validator(value) {\n return ['clip', 'keep'].indexOf(value) >= 0;\n }\n }\n },\n data: function data() {\n return {\n isActive: this.active || false,\n savedScrollTop: null,\n newWidth: typeof this.width === 'number' ? this.width + 'px' : this.width\n };\n },\n\n computed: {\n cancelOptions: function cancelOptions() {\n return typeof this.canCancel === 'boolean' ? this.canCancel ? ['escape', 'x', 'outside', 'button'] : [] : this.canCancel;\n },\n showX: function showX() {\n return this.cancelOptions.indexOf('x') >= 0;\n }\n },\n watch: {\n active: function active(value) {\n this.isActive = value;\n },\n isActive: function isActive() {\n this.handleScroll();\n }\n },\n methods: {\n handleScroll: function handleScroll() {\n if (typeof window === 'undefined') return;\n\n if (this.scroll === 'clip') {\n if (this.isActive) {\n document.documentElement.classList.add('is-clipped');\n } else {\n document.documentElement.classList.remove('is-clipped');\n }\n return;\n }\n\n this.savedScrollTop = !this.savedScrollTop ? document.documentElement.scrollTop : this.savedScrollTop;\n\n if (this.isActive) {\n document.body.classList.add('is-noscroll');\n } else {\n document.body.classList.remove('is-noscroll');\n }\n\n if (this.isActive) {\n document.body.style.top = '-' + this.savedScrollTop + 'px';\n return;\n }\n\n document.documentElement.scrollTop = this.savedScrollTop;\n document.body.style.top = null;\n this.savedScrollTop = null;\n },\n\n\n /**\n * Close the Modal if canCancel and call the onCancel prop (function).\n */\n cancel: function cancel(method) {\n if (this.cancelOptions.indexOf(method) < 0) return;\n\n this.onCancel.apply(null, arguments);\n this.close();\n },\n\n\n /**\n * Call the onCancel prop (function).\n * Emit events, and destroy modal if it's programmatic.\n */\n close: function close() {\n var _this = this;\n\n this.$emit('close');\n this.$emit('update:active', false);\n\n // Timeout for the animation complete before destroying\n if (this.programmatic) {\n this.isActive = false;\n setTimeout(function () {\n _this.$destroy();\n Object(__WEBPACK_IMPORTED_MODULE_0__utils_helpers__[\"e\" /* removeElement */])(_this.$el);\n }, 150);\n }\n },\n\n\n /**\n * Keypress event that is bound to the document.\n */\n keyPress: function keyPress(event) {\n // Esc key\n if (this.isActive && event.keyCode === 27) this.cancel('escape');\n }\n },\n created: function created() {\n if (typeof window !== 'undefined') {\n document.addEventListener('keyup', this.keyPress);\n }\n },\n beforeMount: function beforeMount() {\n // Insert the Modal component in body tag\n // only if it's programmatic\n this.programmatic && document.body.appendChild(this.$el);\n },\n mounted: function mounted() {\n if (this.programmatic) this.isActive = true;else if (this.isActive) this.handleScroll();\n },\n beforeDestroy: function beforeDestroy() {\n if (typeof window !== 'undefined') {\n document.removeEventListener('keyup', this.keyPress);\n // reset scroll\n document.documentElement.classList.remove('is-clipped');\n var savedScrollTop = !this.savedScrollTop ? document.documentElement.scrollTop : this.savedScrollTop;\n document.body.classList.remove('is-noscroll');\n document.documentElement.scrollTop = savedScrollTop;\n document.body.style.top = null;\n }\n }\n});\n\n/***/ }),\n/* 138 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [(_vm.isActive) ? _c('div', {\n staticClass: \"modal is-active\"\n }, [_c('div', {\n staticClass: \"modal-background\",\n on: {\n \"click\": function($event) {\n _vm.cancel('outside')\n }\n }\n }), _vm._v(\" \"), _c('div', {\n staticClass: \"animation-content\",\n class: {\n 'modal-content': !_vm.hasModalCard\n },\n style: ({\n maxWidth: _vm.newWidth\n })\n }, [(_vm.component) ? _c(_vm.component, _vm._g(_vm._b({\n tag: \"component\",\n on: {\n \"close\": _vm.close\n }\n }, 'component', _vm.props, false), _vm.events)) : (_vm.content) ? _c('div', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.content)\n }\n }) : _vm._t(\"default\")], 2), _vm._v(\" \"), (_vm.showX) ? _c('button', {\n staticClass: \"modal-close is-large\",\n attrs: {\n \"type\": \"button\"\n },\n on: {\n \"click\": function($event) {\n _vm.cancel('x')\n }\n }\n }) : _vm._e()]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 139 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [(_vm.isActive) ? _c('div', {\n staticClass: \"dialog modal is-active\",\n class: _vm.size\n }, [_c('div', {\n staticClass: \"modal-background\",\n on: {\n \"click\": function($event) {\n _vm.cancel('outside')\n }\n }\n }), _vm._v(\" \"), _c('div', {\n staticClass: \"modal-card animation-content\"\n }, [(_vm.title) ? _c('header', {\n staticClass: \"modal-card-head\"\n }, [_c('p', {\n staticClass: \"modal-card-title\"\n }, [_vm._v(_vm._s(_vm.title))])]) : _vm._e(), _vm._v(\" \"), _c('section', {\n staticClass: \"modal-card-body\",\n class: {\n 'is-titleless': !_vm.title, 'is-flex': _vm.hasIcon\n }\n }, [_c('div', {\n staticClass: \"media\"\n }, [(_vm.hasIcon) ? _c('div', {\n staticClass: \"media-left\"\n }, [_c('b-icon', {\n attrs: {\n \"icon\": _vm.icon ? _vm.icon : _vm.iconByType,\n \"pack\": _vm.iconPack,\n \"type\": _vm.type,\n \"both\": !_vm.icon,\n \"size\": \"is-large\"\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"media-content\"\n }, [_c('p', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.message)\n }\n }), _vm._v(\" \"), (_vm.hasInput) ? _c('div', {\n staticClass: \"field\"\n }, [_c('div', {\n staticClass: \"control\"\n }, [_c('input', _vm._b({\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.prompt),\n expression: \"prompt\"\n }],\n ref: \"input\",\n staticClass: \"input\",\n class: {\n 'is-danger': _vm.validationMessage\n },\n domProps: {\n \"value\": (_vm.prompt)\n },\n on: {\n \"keyup\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key)) { return null; }\n _vm.confirm($event)\n },\n \"input\": function($event) {\n if ($event.target.composing) { return; }\n _vm.prompt = $event.target.value\n }\n }\n }, 'input', _vm.inputAttrs, false))]), _vm._v(\" \"), _c('p', {\n staticClass: \"help is-danger\"\n }, [_vm._v(_vm._s(_vm.validationMessage))])]) : _vm._e()])])]), _vm._v(\" \"), _c('footer', {\n staticClass: \"modal-card-foot\"\n }, [(_vm.showCancel) ? _c('button', {\n ref: \"cancelButton\",\n staticClass: \"button\",\n on: {\n \"click\": function($event) {\n _vm.cancel('button')\n }\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.cancelText) + \"\\n \")]) : _vm._e(), _vm._v(\" \"), _c('button', {\n ref: \"confirmButton\",\n staticClass: \"button\",\n class: _vm.type,\n on: {\n \"click\": _vm.confirm\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.confirmText) + \"\\n \")])])])]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 140 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(141),\n /* template */\n __webpack_require__(142),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 141 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n\n// EXTERNAL MODULE: ./src/utils/helpers.js\nvar helpers = __webpack_require__(6);\n\n// CONCATENATED MODULE: ./src/utils/ssr.js\n// Polyfills for SSR\n\nvar isSSR = typeof window === 'undefined';\n\nvar HTMLElement = isSSR ? Object : window.HTMLElement;\n// CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/loading/Loading.vue\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ var Loading = __webpack_exports__[\"default\"] = ({\n name: 'BLoading',\n props: {\n active: Boolean,\n programmatic: Boolean,\n container: [Object, Function, HTMLElement],\n isFullPage: {\n type: Boolean,\n default: true\n },\n animation: {\n type: String,\n default: 'fade'\n },\n canCancel: {\n type: Boolean,\n default: false\n },\n onCancel: {\n type: Function,\n default: function _default() {}\n }\n },\n data: function data() {\n return {\n isActive: this.active || false\n };\n },\n\n watch: {\n active: function active(value) {\n this.isActive = value;\n }\n },\n methods: {\n /**\n * Close the Modal if canCancel.\n */\n cancel: function cancel() {\n if (!this.canCancel || !this.isActive) return;\n\n this.close();\n },\n\n /**\n * Emit events, and destroy modal if it's programmatic.\n */\n close: function close() {\n var _this = this;\n\n this.onCancel.apply(null, arguments);\n this.$emit('close');\n this.$emit('update:active', false);\n\n // Timeout for the animation complete before destroying\n if (this.programmatic) {\n this.isActive = false;\n setTimeout(function () {\n _this.$destroy();\n Object(helpers[\"e\" /* removeElement */])(_this.$el);\n }, 150);\n }\n },\n\n /**\n * Keypress event that is bound to the document.\n */\n keyPress: function keyPress(event) {\n // Esc key\n if (event.keyCode === 27) this.cancel();\n }\n },\n created: function created() {\n if (typeof window !== 'undefined') {\n document.addEventListener('keyup', this.keyPress);\n }\n },\n beforeMount: function beforeMount() {\n // Insert the Loading component in body tag\n // only if it's programmatic\n if (this.programmatic) {\n if (!this.container) {\n document.body.appendChild(this.$el);\n } else {\n this.isFullPage = false;\n this.container.appendChild(this.$el);\n }\n }\n },\n mounted: function mounted() {\n if (this.programmatic) this.isActive = true;\n },\n beforeDestroy: function beforeDestroy() {\n if (typeof window !== 'undefined') {\n document.removeEventListener('keyup', this.keyPress);\n }\n }\n});\n\n/***/ }),\n/* 142 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [(_vm.isActive) ? _c('div', {\n staticClass: \"loading-overlay is-active\",\n class: {\n 'is-full-page': _vm.isFullPage\n }\n }, [_c('div', {\n staticClass: \"loading-background\",\n on: {\n \"click\": _vm.cancel\n }\n }), _vm._v(\" \"), _c('div', {\n staticClass: \"loading-icon\"\n })]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 143 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(144),\n /* template */\n __webpack_require__(145),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 144 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__ = __webpack_require__(63);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BMessage',\n mixins: [__WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__[\"a\" /* default */]],\n data: function data() {\n return {\n newIconSize: this.iconSize || this.size || 'is-large'\n };\n }\n});\n\n/***/ }),\n/* 145 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [(_vm.isActive) ? _c('article', {\n staticClass: \"message\",\n class: [_vm.type, _vm.size]\n }, [(_vm.title) ? _c('header', {\n staticClass: \"message-header\"\n }, [_c('p', [_vm._v(_vm._s(_vm.title))]), _vm._v(\" \"), (_vm.closable) ? _c('button', {\n staticClass: \"delete\",\n attrs: {\n \"type\": \"button\"\n },\n on: {\n \"click\": _vm.close\n }\n }) : _vm._e()]) : _vm._e(), _vm._v(\" \"), _c('section', {\n staticClass: \"message-body\"\n }, [_c('div', {\n staticClass: \"media\"\n }, [(_vm.icon && _vm.hasIcon) ? _c('div', {\n staticClass: \"media-left\"\n }, [_c('b-icon', {\n class: _vm.type,\n attrs: {\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\",\n \"size\": _vm.newIconSize\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"media-content\"\n }, [_vm._t(\"default\")], 2)])])]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 146 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(147),\n /* template */\n __webpack_require__(148),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 147 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__ = __webpack_require__(63);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BNotification',\n mixins: [__WEBPACK_IMPORTED_MODULE_0__utils_MessageMixin_js__[\"a\" /* default */]]\n});\n\n/***/ }),\n/* 148 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": \"fade\"\n }\n }, [(_vm.isActive) ? _c('article', {\n staticClass: \"notification\",\n class: _vm.type\n }, [(_vm.closable) ? _c('button', {\n staticClass: \"delete\",\n attrs: {\n \"type\": \"button\"\n },\n on: {\n \"click\": _vm.close\n }\n }) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"media\"\n }, [(_vm.icon && _vm.hasIcon) ? _c('div', {\n staticClass: \"media-left\"\n }, [_c('b-icon', {\n attrs: {\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\",\n \"size\": \"is-large\"\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"media-content\"\n }, [_vm._t(\"default\")], 2)])]) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 149 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__ = __webpack_require__(13);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BPagination',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n total: [Number, String],\n perPage: {\n type: [Number, String],\n default: 20\n },\n current: {\n type: [Number, String],\n default: 1\n },\n size: String,\n simple: Boolean,\n rounded: Boolean,\n order: String\n },\n computed: {\n rootClasses: function rootClasses() {\n return [this.order, this.size, {\n 'is-simple': this.simple,\n 'is-rounded': this.rounded\n }];\n },\n\n\n /**\n * Total page size (count).\n */\n pageCount: function pageCount() {\n return Math.ceil(this.total / this.perPage);\n },\n\n\n /**\n * First item of the page (count).\n */\n firstItem: function firstItem() {\n var firstItem = this.current * this.perPage - this.perPage + 1;\n return firstItem >= 0 ? firstItem : 0;\n },\n\n\n /**\n * Check if previous button is available.\n */\n hasPrev: function hasPrev() {\n return this.current > 1;\n },\n\n\n /**\n * Check if first page button should be visible.\n */\n hasFirst: function hasFirst() {\n return this.current >= 3;\n },\n\n\n /**\n * Check if first ellipsis should be visible.\n */\n hasFirstEllipsis: function hasFirstEllipsis() {\n return this.current >= 4;\n },\n\n\n /**\n * Check if last page button should be visible.\n */\n hasLast: function hasLast() {\n return this.current <= this.pageCount - 2;\n },\n\n\n /**\n * Check if last ellipsis should be visible.\n */\n hasLastEllipsis: function hasLastEllipsis() {\n return this.current < this.pageCount - 2 && this.current <= this.pageCount - 3;\n },\n\n\n /**\n * Check if next button is available.\n */\n hasNext: function hasNext() {\n return this.current < this.pageCount;\n },\n\n\n /**\n * Get near pages, 1 before and 1 after the current.\n * Also add the click event to the array.\n */\n pagesInRange: function pagesInRange() {\n var _this = this;\n\n if (this.simple) return;\n\n var left = Math.max(1, this.current - 1);\n var right = Math.min(this.current + 1, this.pageCount);\n\n var pages = [];\n\n var _loop = function _loop(i) {\n pages.push({\n number: i,\n isCurrent: _this.current === i,\n click: function click(event) {\n if (_this.current === i) return;\n _this.$emit('change', i);\n _this.$emit('update:current', i);\n\n // Set focus on element to keep tab order\n _this.$nextTick(function () {\n return event.target.focus();\n });\n }\n });\n };\n\n for (var i = left; i <= right; i++) {\n _loop(i);\n }\n return pages;\n }\n },\n watch: {\n /**\n * If current page is trying to be greater than page count, set to last.\n */\n pageCount: function pageCount(value) {\n if (this.current > value) this.last();\n }\n },\n methods: {\n /**\n * Previous button click listener.\n */\n prev: function prev() {\n if (!this.hasPrev) return;\n this.$emit('change', this.current - 1);\n this.$emit('update:current', this.current - 1);\n },\n\n\n /**\n * First button click listener.\n */\n first: function first() {\n this.$emit('change', 1);\n this.$emit('update:current', 1);\n },\n\n\n /**\n * Last button click listener.\n */\n last: function last() {\n this.$emit('change', this.pageCount);\n this.$emit('update:current', this.pageCount);\n },\n\n\n /**\n * Next button click listener.\n */\n next: function next() {\n if (!this.hasNext) return;\n this.$emit('change', this.current + 1);\n this.$emit('update:current', this.current + 1);\n }\n }\n});\n\n/***/ }),\n/* 150 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"pagination\",\n class: _vm.rootClasses\n }, [_c('a', {\n staticClass: \"pagination-previous\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": !_vm.hasPrev\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.prev($event)\n }\n }\n }, [_c('b-icon', {\n attrs: {\n \"icon\": \"chevron-left\",\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\"\n }\n })], 1), _vm._v(\" \"), _c('a', {\n staticClass: \"pagination-next\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\",\n \"disabled\": !_vm.hasNext\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.next($event)\n }\n }\n }, [_c('b-icon', {\n attrs: {\n \"icon\": \"chevron-right\",\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\"\n }\n })], 1), _vm._v(\" \"), (!_vm.simple) ? _c('ul', {\n staticClass: \"pagination-list\"\n }, [(_vm.hasFirst) ? _c('li', [_c('a', {\n staticClass: \"pagination-link\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\"\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.first($event)\n }\n }\n }, [_vm._v(\"\\n 1\\n \")])]) : _vm._e(), _vm._v(\" \"), (_vm.hasFirstEllipsis) ? _c('li', [_c('span', {\n staticClass: \"pagination-ellipsis\"\n }, [_vm._v(\"…\")])]) : _vm._e(), _vm._v(\" \"), _vm._l((_vm.pagesInRange), function(page) {\n return _c('li', {\n key: page.number\n }, [_c('a', {\n staticClass: \"pagination-link\",\n class: {\n 'is-current': page.isCurrent\n },\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\"\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n page.click($event)\n }\n }\n }, [_vm._v(\"\\n \" + _vm._s(page.number) + \"\\n \")])])\n }), _vm._v(\" \"), (_vm.hasLastEllipsis) ? _c('li', [_c('span', {\n staticClass: \"pagination-ellipsis\"\n }, [_vm._v(\"…\")])]) : _vm._e(), _vm._v(\" \"), (_vm.hasLast) ? _c('li', [_c('a', {\n staticClass: \"pagination-link\",\n attrs: {\n \"role\": \"button\",\n \"href\": \"#\"\n },\n on: {\n \"click\": function($event) {\n $event.preventDefault();\n _vm.last($event)\n }\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.pageCount) + \"\\n \")])]) : _vm._e()], 2) : _vm._e(), _vm._v(\" \"), (_vm.simple) ? _c('small', {\n staticClass: \"info\"\n }, [(_vm.perPage == 1) ? [_vm._v(\"\\n \" + _vm._s(_vm.firstItem) + \" / \" + _vm._s(_vm.total) + \"\\n \")] : [_vm._v(\"\\n \" + _vm._s(_vm.firstItem) + \"-\" + _vm._s(Math.min(_vm.current * _vm.perPage, _vm.total)) + \" / \" + _vm._s(_vm.total) + \"\\n \")]], 2) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 151 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(152),\n /* template */\n __webpack_require__(153),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 152 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__ = __webpack_require__(13);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BPanel',\n components: __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()({}, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_1__icon_Icon___default.a),\n mixins: [__WEBPACK_IMPORTED_MODULE_2__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n collapsible: {\n type: Boolean,\n default: false\n },\n open: {\n type: Boolean,\n default: true\n },\n hasCustomTemplate: {\n type: Boolean,\n default: false\n },\n header: String,\n content: String,\n animation: {\n type: String,\n default: 'fade'\n }\n },\n data: function data() {\n return {\n isOpen: this.open\n };\n },\n\n watch: {\n open: function open(value) {\n this.isOpen = value;\n }\n },\n methods: {\n /**\n * Toggle the Panel and emit events if collapsible.\n */\n toggle: function toggle() {\n if (!this.collapsible) return;\n\n this.isOpen = !this.isOpen;\n this.$emit('update:open', this.isOpen);\n\n if (this.isOpen) {\n this.$emit('open');\n } else {\n this.$emit('close');\n }\n }\n }\n});\n\n/***/ }),\n/* 153 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('nav', {\n staticClass: \"panel\"\n }, [_c('div', {\n staticClass: \"panel-heading\",\n class: {\n 'is-collapsible': _vm.collapsible\n },\n on: {\n \"click\": _vm.toggle\n }\n }, [(_vm.header) ? _c('span', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.header)\n }\n }) : _vm._t(\"header\"), _vm._v(\" \"), (_vm.collapsible) ? _c('b-icon', {\n staticClass: \"is-pulled-right\",\n attrs: {\n \"both\": \"\",\n \"icon\": _vm.isOpen ? 'menu-up' : 'menu-down',\n \"icon-pack\": _vm.iconPack\n }\n }) : _vm._e()], 2), _vm._v(\" \"), _c('transition', {\n attrs: {\n \"name\": _vm.animation\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isOpen),\n expression: \"isOpen\"\n }],\n staticClass: \"panel-content\",\n class: {\n 'panel-block': !_vm.hasCustomTemplate\n }\n }, [(_vm.content) ? _c('div', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.content)\n }\n }) : _vm._t(\"default\")], 2)])], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 154 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(155),\n /* template */\n __webpack_require__(156),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 155 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BRadio',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n type: String,\n disabled: Boolean,\n required: Boolean,\n name: String,\n size: String\n },\n data: function data() {\n return {\n newValue: this.value\n };\n },\n\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n // only trigger input event\n // when current bRadioButton is clicked.\n if (value === this.nativeValue) {\n this.$emit('input', value);\n }\n }\n }\n});\n\n/***/ }),\n/* 156 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('label', {\n ref: \"label\",\n staticClass: \"b-radio radio\",\n class: [_vm.size, {\n 'is-disabled': _vm.disabled\n }],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n }\n }\n }, [_c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"radio\",\n \"disabled\": _vm.disabled,\n \"required\": _vm.required,\n \"name\": _vm.name\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": _vm._q(_vm.newValue, _vm.nativeValue)\n },\n on: {\n \"change\": function($event) {\n _vm.newValue = _vm.nativeValue\n }\n }\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"check\",\n class: _vm.type\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"control-label\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 157 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(158),\n /* template */\n __webpack_require__(159),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 158 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BRadioButton',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n type: {\n type: String,\n default: 'is-primary'\n },\n disabled: Boolean,\n name: String,\n size: String\n },\n data: function data() {\n return {\n newValue: this.value\n };\n },\n\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n // only trigger input event\n // when current bRadioButton is clicked.\n if (value === this.nativeValue) {\n this.$emit('input', value);\n }\n }\n }\n});\n\n/***/ }),\n/* 159 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"control\"\n }, [_c('label', {\n ref: \"label\",\n staticClass: \"b-radio radio button\",\n class: [_vm.newValue === _vm.nativeValue ? _vm.type : null, _vm.size],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n }\n }\n }, [_vm._t(\"default\"), _vm._v(\" \"), _c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"radio\",\n \"disabled\": _vm.disabled,\n \"name\": _vm.name\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": _vm._q(_vm.newValue, _vm.nativeValue)\n },\n on: {\n \"change\": function($event) {\n _vm.newValue = _vm.nativeValue\n }\n }\n })], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 160 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(161),\n /* template */\n __webpack_require__(162),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 161 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__ = __webpack_require__(65);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BSnackbar',\n mixins: [__WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__[\"a\" /* default */]],\n props: {\n actionText: {\n type: String,\n default: 'OK'\n },\n onAction: {\n type: Function,\n default: function _default() {}\n },\n indefinite: {\n type: Boolean,\n default: false\n }\n },\n data: function data() {\n return {\n newDuration: this.duration || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultSnackbarDuration\n };\n },\n\n methods: {\n /**\n * Click listener.\n * Call action prop before closing (from Mixin).\n */\n action: function action() {\n this.onAction();\n this.close();\n }\n }\n});\n\n/***/ }),\n/* 162 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"enter-active-class\": _vm.transition.enter,\n \"leave-active-class\": _vm.transition.leave\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive),\n expression: \"isActive\"\n }],\n staticClass: \"snackbar\",\n class: [_vm.type, _vm.position]\n }, [_c('p', {\n staticClass: \"text\"\n }, [_vm._v(_vm._s(_vm.message))]), _vm._v(\" \"), (_vm.actionText) ? _c('div', {\n staticClass: \"action\",\n class: _vm.type,\n on: {\n \"click\": _vm.action\n }\n }, [_c('button', {\n staticClass: \"button is-dark\"\n }, [_vm._v(_vm._s(_vm.actionText))])]) : _vm._e()])])\n},staticRenderFns: []}\n\n/***/ }),\n/* 163 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(164),\n /* template */\n __webpack_require__(165),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 164 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BSwitch',\n props: {\n value: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n nativeValue: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n disabled: Boolean,\n type: String,\n name: String,\n size: String,\n trueValue: {\n type: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n default: true\n },\n falseValue: {\n type: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n default: false\n }\n },\n data: function data() {\n return {\n newValue: this.value,\n isMouseDown: false\n };\n },\n\n watch: {\n /**\n * When v-model change, set internal value.\n */\n value: function value(_value) {\n this.newValue = _value;\n },\n\n /**\n * Emit input event to update the user v-model.\n */\n newValue: function newValue(value) {\n this.$emit('input', value);\n }\n }\n});\n\n/***/ }),\n/* 165 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('label', {\n ref: \"label\",\n staticClass: \"switch\",\n class: [_vm.size, {\n 'is-disabled': _vm.disabled\n }],\n attrs: {\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.disabled ? false : 0\n },\n on: {\n \"keydown\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"enter\", 13, $event.key) && _vm._k($event.keyCode, \"space\", 32, $event.key)) { return null; }\n $event.preventDefault();\n _vm.$refs.label.click()\n },\n \"mousedown\": function($event) {\n _vm.isMouseDown = true\n },\n \"mouseup\": function($event) {\n _vm.isMouseDown = false\n },\n \"mouseout\": function($event) {\n _vm.isMouseDown = false\n },\n \"blur\": function($event) {\n _vm.isMouseDown = false\n }\n }\n }, [_c('input', {\n directives: [{\n name: \"model\",\n rawName: \"v-model\",\n value: (_vm.newValue),\n expression: \"newValue\"\n }],\n attrs: {\n \"type\": \"checkbox\",\n \"disabled\": _vm.disabled,\n \"name\": _vm.name,\n \"true-value\": _vm.trueValue,\n \"false-value\": _vm.falseValue\n },\n domProps: {\n \"value\": _vm.nativeValue,\n \"checked\": Array.isArray(_vm.newValue) ? _vm._i(_vm.newValue, _vm.nativeValue) > -1 : _vm._q(_vm.newValue, _vm.trueValue)\n },\n on: {\n \"click\": function($event) {\n $event.stopPropagation();\n },\n \"change\": function($event) {\n var $$a = _vm.newValue,\n $$el = $event.target,\n $$c = $$el.checked ? (_vm.trueValue) : (_vm.falseValue);\n if (Array.isArray($$a)) {\n var $$v = _vm.nativeValue,\n $$i = _vm._i($$a, $$v);\n if ($$el.checked) {\n $$i < 0 && (_vm.newValue = $$a.concat([$$v]))\n } else {\n $$i > -1 && (_vm.newValue = $$a.slice(0, $$i).concat($$a.slice($$i + 1)))\n }\n } else {\n _vm.newValue = $$c\n }\n }\n }\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"check\",\n class: [{\n 'is-elastic': _vm.isMouseDown && !_vm.disabled\n }, _vm.type]\n }), _vm._v(\" \"), _c('span', {\n staticClass: \"control-label\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 166 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(167),\n /* template */\n __webpack_require__(181),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 167 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray__ = __webpack_require__(168);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox__ = __webpack_require__(61);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__icon_Icon__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__pagination_Pagination__ = __webpack_require__(64);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__pagination_Pagination___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__pagination_Pagination__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__TableMobileSort__ = __webpack_require__(176);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__TableMobileSort___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6__TableMobileSort__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__TableColumn__ = __webpack_require__(66);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__TableColumn___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7__TableColumn__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__utils_BaseElementMixin__ = __webpack_require__(13);\n\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTable',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox___default.a.name, __WEBPACK_IMPORTED_MODULE_3__checkbox_Checkbox___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_4__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_4__icon_Icon___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_5__pagination_Pagination___default.a.name, __WEBPACK_IMPORTED_MODULE_5__pagination_Pagination___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_6__TableMobileSort___default.a.name, __WEBPACK_IMPORTED_MODULE_6__TableMobileSort___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_7__TableColumn___default.a.name, __WEBPACK_IMPORTED_MODULE_7__TableColumn___default.a), _components),\n mixins: [__WEBPACK_IMPORTED_MODULE_8__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n data: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n columns: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n bordered: Boolean,\n striped: Boolean,\n narrowed: Boolean,\n hoverable: Boolean,\n loading: Boolean,\n detailed: Boolean,\n checkable: Boolean,\n selected: Object,\n focusable: Boolean,\n customIsChecked: Function,\n isRowCheckable: {\n type: Function,\n default: function _default() {\n return true;\n }\n },\n checkedRows: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n mobileCards: {\n type: Boolean,\n default: true\n },\n defaultSort: [String, Array],\n defaultSortDirection: {\n type: String,\n default: 'asc'\n },\n paginated: Boolean,\n currentPage: {\n type: Number,\n default: 1\n },\n perPage: {\n type: [Number, String],\n default: 20\n },\n paginationSimple: Boolean,\n paginationSize: String,\n backendSorting: Boolean,\n rowClass: {\n type: Function,\n default: function _default() {\n return '';\n }\n },\n openedDetailed: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n hasDetailedVisible: {\n type: Function,\n default: function _default() {\n return true;\n }\n },\n detailKey: {\n type: String,\n default: ''\n },\n backendPagination: Boolean,\n total: {\n type: [Number, String],\n default: 0\n }\n },\n data: function data() {\n return {\n getValueByPath: __WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"b\" /* getValueByPath */],\n newColumns: [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(this.columns)),\n visibleDetailRows: this.openedDetailed,\n newData: this.data,\n newDataTotal: this.backendPagination ? this.total : this.data.length,\n newCheckedRows: [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(this.checkedRows)),\n newCurrentPage: this.currentPage,\n currentSortColumn: {},\n isAsc: true,\n firstTimeSort: true, // Used by first time initSort\n _isTable: true // Used by TableColumn\n };\n },\n\n computed: {\n tableClasses: function tableClasses() {\n return {\n 'is-bordered': this.bordered,\n 'is-striped': this.striped,\n 'is-narrow': this.narrowed,\n 'has-mobile-cards': this.mobileCards,\n 'is-hoverable': (this.hoverable || this.focusable) && this.visibleData.length\n };\n },\n\n\n /**\n * Splitted data based on the pagination.\n */\n visibleData: function visibleData() {\n if (!this.paginated) return this.newData;\n\n var currentPage = this.newCurrentPage;\n var perPage = this.perPage;\n\n if (this.newData.length <= perPage) {\n return this.newData;\n } else {\n var start = (currentPage - 1) * perPage;\n var end = parseInt(start, 10) + parseInt(perPage, 10);\n return this.newData.slice(start, end);\n }\n },\n\n\n /**\n * Check if all rows in the page are checked.\n */\n isAllChecked: function isAllChecked() {\n var _this = this;\n\n var validVisibleData = this.visibleData.filter(function (row) {\n return _this.isRowCheckable(row);\n });\n if (validVisibleData.length === 0) return false;\n var isAllChecked = validVisibleData.some(function (currentVisibleRow) {\n return Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"c\" /* indexOf */])(_this.newCheckedRows, currentVisibleRow, _this.customIsChecked) < 0;\n });\n return !isAllChecked;\n },\n\n\n /**\n * Check if all rows in the page are checkable.\n */\n isAllUncheckable: function isAllUncheckable() {\n var _this2 = this;\n\n var validVisibleData = this.visibleData.filter(function (row) {\n return _this2.isRowCheckable(row);\n });\n return validVisibleData.length === 0;\n },\n\n\n /**\n * Check if has any sortable column.\n */\n hasSortablenewColumns: function hasSortablenewColumns() {\n return this.newColumns.some(function (column) {\n return column.sortable;\n });\n },\n\n\n /**\n * Return total column count based if it's checkable or expanded\n */\n columnCount: function columnCount() {\n var count = this.newColumns.length;\n count += this.checkable ? 1 : 0;\n count += this.detailed ? 1 : 0;\n\n return count;\n }\n },\n watch: {\n /**\n * When data prop change:\n * 1. Update internal value.\n * 2. Reset newColumns (thead), in case it's on a v-for loop.\n * 3. Sort again if it's not backend-sort.\n * 4. Set new total if it's not backend-paginated.\n */\n data: function data(value) {\n var _this3 = this;\n\n // Save newColumns before resetting\n var newColumns = this.newColumns;\n\n this.newColumns = [];\n this.newData = value;\n\n // Prevent table from being headless, data could change and created hook\n // on column might not trigger\n this.$nextTick(function () {\n if (!_this3.newColumns.length) _this3.newColumns = newColumns;\n });\n\n if (!this.backendSorting) {\n this.sort(this.currentSortColumn, true);\n }\n if (!this.backendPagination) {\n this.newDataTotal = value.length;\n }\n },\n\n\n /**\n * When Pagination total change, update internal total\n * only if it's backend-paginated.\n */\n total: function total(newTotal) {\n if (!this.backendPagination) return;\n\n this.newDataTotal = newTotal;\n },\n\n\n /**\n * When checkedRows prop change, update internal value without\n * mutating original data.\n */\n checkedRows: function checkedRows(rows) {\n this.newCheckedRows = [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(rows));\n },\n columns: function columns(value) {\n this.newColumns = [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(value));\n },\n\n\n /**\n * When newColumns change, call initSort only first time (For example async data).\n */\n newColumns: function newColumns(_newColumns) {\n if (_newColumns.length && this.firstTimeSort) {\n this.initSort();\n this.firstTimeSort = false;\n } else if (_newColumns.length) {\n if (this.currentSortColumn.field) {\n for (var i = 0; i < _newColumns.length; i++) {\n if (_newColumns[i].field === this.currentSortColumn.field) {\n this.currentSortColumn = _newColumns[i];\n break;\n }\n }\n }\n }\n },\n\n\n /**\n * When the user wants to control the detailed rows via props.\n * Or wants to open the details of certain row with the router for example.\n */\n openedDetailed: function openedDetailed(expandedRows) {\n this.visibleDetailRows = expandedRows;\n },\n currentPage: function currentPage(newVal) {\n this.newCurrentPage = newVal;\n }\n },\n methods: {\n /**\n * Sort an array by key without mutating original data.\n * Call the user sort function if it was passed.\n */\n sortBy: function sortBy(array, key, fn, isAsc) {\n var sorted = [];\n // Sorting without mutating original data\n if (fn && typeof fn === 'function') {\n sorted = [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(array)).sort(function (a, b) {\n return fn(a, b, isAsc);\n });\n } else {\n sorted = [].concat(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_toConsumableArray___default()(array)).sort(function (a, b) {\n // Get nested values from objects\n var newA = Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"b\" /* getValueByPath */])(a, key);\n var newB = Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"b\" /* getValueByPath */])(b, key);\n\n if (!newA && newA !== 0) return 1;\n if (!newB && newB !== 0) return -1;\n if (newA === newB) return 0;\n\n newA = typeof newA === 'string' ? newA.toUpperCase() : newA;\n newB = typeof newB === 'string' ? newB.toUpperCase() : newB;\n\n return isAsc ? newA > newB ? 1 : -1 : newA > newB ? -1 : 1;\n });\n }\n\n return sorted;\n },\n\n\n /**\n * Sort the column.\n * Toggle current direction on column if it's sortable\n * and not just updating the prop.\n */\n sort: function sort(column) {\n var updatingData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;\n\n if (!column || !column.sortable) return;\n\n if (!updatingData) {\n this.isAsc = column === this.currentSortColumn ? !this.isAsc : this.defaultSortDirection.toLowerCase() !== 'desc';\n }\n if (!this.firstTimeSort) {\n this.$emit('sort', column.field, this.isAsc ? 'asc' : 'desc');\n }\n if (!this.backendSorting) {\n this.newData = this.sortBy(this.newData, column.field, column.customSort, this.isAsc);\n }\n this.currentSortColumn = column;\n },\n\n\n /**\n * Check if the row is checked (is added to the array).\n */\n isRowChecked: function isRowChecked(row) {\n return Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"c\" /* indexOf */])(this.newCheckedRows, row, this.customIsChecked) >= 0;\n },\n\n\n /**\n * Remove a checked row from the array.\n */\n removeCheckedRow: function removeCheckedRow(row) {\n var index = Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"c\" /* indexOf */])(this.newCheckedRows, row, this.customIsChecked);\n if (index >= 0) {\n this.newCheckedRows.splice(index, 1);\n }\n },\n\n\n /**\n * Header checkbox click listener.\n * Add or remove all rows in current page.\n */\n checkAll: function checkAll() {\n var _this4 = this;\n\n var isAllChecked = this.isAllChecked;\n this.visibleData.forEach(function (currentRow) {\n _this4.removeCheckedRow(currentRow);\n if (!isAllChecked) {\n if (_this4.isRowCheckable(currentRow)) {\n _this4.newCheckedRows.push(currentRow);\n }\n }\n });\n\n this.$emit('check', this.newCheckedRows);\n this.$emit('check-all', this.newCheckedRows);\n\n // Emit checked rows to update user variable\n this.$emit('update:checkedRows', this.newCheckedRows);\n },\n\n\n /**\n * Row checkbox click listener.\n * Add or remove a single row.\n */\n checkRow: function checkRow(row) {\n if (!this.isRowChecked(row)) {\n this.newCheckedRows.push(row);\n } else {\n this.removeCheckedRow(row);\n }\n\n this.$emit('check', this.newCheckedRows, row);\n\n // Emit checked rows to update user variable\n this.$emit('update:checkedRows', this.newCheckedRows);\n },\n\n\n /**\n * Row click listener.\n * Emit all necessary events.\n */\n selectRow: function selectRow(row, index) {\n this.$emit('click', row);\n\n if (this.selected === row) return;\n\n // Emit new and old row\n this.$emit('select', row, this.selected);\n\n // Emit new row to update user variable\n this.$emit('update:selected', row);\n },\n\n\n /**\n * Paginator change listener.\n */\n pageChanged: function pageChanged(page) {\n this.newCurrentPage = page > 0 ? page : 1;\n this.$emit('page-change', this.newCurrentPage);\n this.$emit('update:currentPage', this.newCurrentPage);\n },\n\n\n /**\n * Toggle to show/hide details slot\n */\n toggleDetails: function toggleDetails(obj) {\n var found = this.isVisibleDetailRow(obj);\n\n if (found) {\n this.closeDetailRow(obj);\n this.$emit('details-close', obj);\n } else {\n this.openDetailRow(obj);\n this.$emit('details-open', obj);\n }\n\n // Syncs the detailed rows with the parent component\n this.$emit('update:openedDetailed', this.visibleDetailRows);\n },\n openDetailRow: function openDetailRow(obj) {\n var index = this.handleDetailKey(obj);\n this.visibleDetailRows.push(index);\n },\n closeDetailRow: function closeDetailRow(obj) {\n var index = this.handleDetailKey(obj);\n var i = this.visibleDetailRows.indexOf(index);\n this.visibleDetailRows.splice(i, 1);\n },\n isVisibleDetailRow: function isVisibleDetailRow(obj) {\n var index = this.handleDetailKey(obj);\n var result = this.visibleDetailRows.indexOf(index) >= 0;\n return result;\n },\n\n\n /**\n * When the detailKey is defined we use the object[detailKey] as index.\n * If not, use the object reference by default.\n */\n handleDetailKey: function handleDetailKey(index) {\n var key = this.detailKey;\n return !key.length ? index : index[key];\n },\n checkPredefinedDetailedRows: function checkPredefinedDetailedRows() {\n var defaultExpandedRowsDefined = this.openedDetailed.length > 0;\n if (defaultExpandedRowsDefined && !this.detailKey.length) {\n throw new Error('If you set a predefined opened-detailed, you must provide an unique key using the prop \"detail-key\"');\n }\n },\n\n\n /**\n * Check if footer slot has custom content.\n */\n hasCustomFooterSlot: function hasCustomFooterSlot() {\n if (this.$slots.footer.length > 1) return true;\n\n var tag = this.$slots.footer[0].tag;\n if (tag !== 'th' && tag !== 'td') return false;\n\n return true;\n },\n\n\n /**\n * Check if bottom-left slot exists.\n */\n hasBottomLeftSlot: function hasBottomLeftSlot() {\n return typeof this.$slots['bottom-left'] !== 'undefined';\n },\n\n\n /**\n * Table arrow keys listener, change selection.\n */\n pressedArrow: function pressedArrow(pos) {\n if (!this.visibleData.length) return;\n\n var index = this.visibleData.indexOf(this.selected) + pos;\n\n // Prevent from going up from first and down from last\n index = index < 0 ? 0 : index > this.visibleData.length - 1 ? this.visibleData.length - 1 : index;\n\n this.selectRow(this.visibleData[index]);\n },\n\n\n /**\n * Focus table element if has selected prop.\n */\n focus: function focus() {\n if (!this.focusable) return;\n\n this.$el.querySelector('table').focus();\n },\n\n\n /**\n * Initial sorted column based on the default-sort prop.\n */\n initSort: function initSort() {\n var _this5 = this;\n\n if (!this.defaultSort) return;\n\n var sortField = '';\n var sortDirection = this.defaultSortDirection;\n\n if (Array.isArray(this.defaultSort)) {\n sortField = this.defaultSort[0];\n if (this.defaultSort[1]) {\n sortDirection = this.defaultSort[1];\n }\n } else {\n sortField = this.defaultSort;\n }\n\n this.newColumns.forEach(function (column) {\n if (column.field === sortField) {\n _this5.isAsc = sortDirection.toLowerCase() !== 'desc';\n _this5.sort(column, true);\n }\n });\n }\n },\n\n mounted: function mounted() {\n this.checkPredefinedDetailedRows();\n }\n});\n\n/***/ }),\n/* 168 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nexports.__esModule = true;\n\nvar _from = __webpack_require__(169);\n\nvar _from2 = _interopRequireDefault(_from);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\nexports.default = function (arr) {\n if (Array.isArray(arr)) {\n for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {\n arr2[i] = arr[i];\n }\n\n return arr2;\n } else {\n return (0, _from2.default)(arr);\n }\n};\n\n/***/ }),\n/* 169 */\n/***/ (function(module, exports, __webpack_require__) {\n\nmodule.exports = { \"default\": __webpack_require__(170), __esModule: true };\n\n/***/ }),\n/* 170 */\n/***/ (function(module, exports, __webpack_require__) {\n\n__webpack_require__(38);\n__webpack_require__(171);\nmodule.exports = __webpack_require__(8).Array.from;\n\n\n/***/ }),\n/* 171 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar ctx = __webpack_require__(46);\nvar $export = __webpack_require__(18);\nvar toObject = __webpack_require__(37);\nvar call = __webpack_require__(172);\nvar isArrayIter = __webpack_require__(173);\nvar toLength = __webpack_require__(51);\nvar createProperty = __webpack_require__(174);\nvar getIterFn = __webpack_require__(60);\n\n$export($export.S + $export.F * !__webpack_require__(175)(function (iter) { Array.from(iter); }), 'Array', {\n // 22.1.2.1 Array.from(arrayLike, mapfn = undefined, thisArg = undefined)\n from: function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {\n var O = toObject(arrayLike);\n var C = typeof this == 'function' ? this : Array;\n var aLen = arguments.length;\n var mapfn = aLen > 1 ? arguments[1] : undefined;\n var mapping = mapfn !== undefined;\n var index = 0;\n var iterFn = getIterFn(O);\n var length, result, step, iterator;\n if (mapping) mapfn = ctx(mapfn, aLen > 2 ? arguments[2] : undefined, 2);\n // if object isn't iterable or it's array with default iterator - use simple case\n if (iterFn != undefined && !(C == Array && isArrayIter(iterFn))) {\n for (iterator = iterFn.call(O), result = new C(); !(step = iterator.next()).done; index++) {\n createProperty(result, index, mapping ? call(iterator, mapfn, [step.value, index], true) : step.value);\n }\n } else {\n length = toLength(O.length);\n for (result = new C(length); length > index; index++) {\n createProperty(result, index, mapping ? mapfn(O[index], index) : O[index]);\n }\n }\n result.length = index;\n return result;\n }\n});\n\n\n/***/ }),\n/* 172 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// call something on iterator step with safe closing on error\nvar anObject = __webpack_require__(16);\nmodule.exports = function (iterator, fn, value, entries) {\n try {\n return entries ? fn(anObject(value)[0], value[1]) : fn(value);\n // 7.4.6 IteratorClose(iterator, completion)\n } catch (e) {\n var ret = iterator['return'];\n if (ret !== undefined) anObject(ret.call(iterator));\n throw e;\n }\n};\n\n\n/***/ }),\n/* 173 */\n/***/ (function(module, exports, __webpack_require__) {\n\n// check on default Array iterator\nvar Iterators = __webpack_require__(22);\nvar ITERATOR = __webpack_require__(4)('iterator');\nvar ArrayProto = Array.prototype;\n\nmodule.exports = function (it) {\n return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it);\n};\n\n\n/***/ }),\n/* 174 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\nvar $defineProperty = __webpack_require__(9);\nvar createDesc = __webpack_require__(21);\n\nmodule.exports = function (object, index, value) {\n if (index in object) $defineProperty.f(object, index, createDesc(0, value));\n else object[index] = value;\n};\n\n\n/***/ }),\n/* 175 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar ITERATOR = __webpack_require__(4)('iterator');\nvar SAFE_CLOSING = false;\n\ntry {\n var riter = [7][ITERATOR]();\n riter['return'] = function () { SAFE_CLOSING = true; };\n // eslint-disable-next-line no-throw-literal\n Array.from(riter, function () { throw 2; });\n} catch (e) { /* empty */ }\n\nmodule.exports = function (exec, skipClosing) {\n if (!skipClosing && !SAFE_CLOSING) return false;\n var safe = false;\n try {\n var arr = [7];\n var iter = arr[ITERATOR]();\n iter.next = function () { return { done: safe = true }; };\n arr[ITERATOR] = function () { return iter; };\n exec(arr);\n } catch (e) { /* empty */ }\n return safe;\n};\n\n\n/***/ }),\n/* 176 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(177),\n /* template */\n __webpack_require__(178),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 177 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__select_Select__ = __webpack_require__(28);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__select_Select___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1__select_Select__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_2__icon_Icon__);\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTableMobileSort',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_1__select_Select___default.a.name, __WEBPACK_IMPORTED_MODULE_1__select_Select___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_2__icon_Icon___default.a), _components),\n props: {\n currentSortColumn: Object,\n isAsc: Boolean,\n columns: Array\n },\n data: function data() {\n return {\n mobileSort: this.currentSortColumn\n };\n },\n\n watch: {\n mobileSort: function mobileSort(column) {\n if (this.currentSortColumn === column) return;\n\n this.$emit('sort', column);\n },\n currentSortColumn: function currentSortColumn(column) {\n this.mobileSort = column;\n }\n },\n methods: {\n sort: function sort() {\n this.$emit('sort', this.mobileSort);\n }\n }\n});\n\n/***/ }),\n/* 178 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"field table-mobile-sort\"\n }, [_c('div', {\n staticClass: \"field has-addons\"\n }, [_c('b-select', {\n attrs: {\n \"expanded\": \"\"\n },\n model: {\n value: (_vm.mobileSort),\n callback: function($$v) {\n _vm.mobileSort = $$v\n },\n expression: \"mobileSort\"\n }\n }, _vm._l((_vm.columns), function(column, index) {\n return (column.sortable) ? _c('option', {\n key: index,\n domProps: {\n \"value\": column\n }\n }, [_vm._v(\"\\n \" + _vm._s(column.label) + \"\\n \")]) : _vm._e()\n })), _vm._v(\" \"), _c('div', {\n staticClass: \"control\"\n }, [_c('button', {\n staticClass: \"button is-primary\",\n on: {\n \"click\": _vm.sort\n }\n }, [_c('b-icon', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.currentSortColumn === _vm.mobileSort),\n expression: \"currentSortColumn === mobileSort\"\n }],\n class: {\n 'is-desc': !_vm.isAsc\n },\n attrs: {\n \"icon\": \"arrow-up\",\n \"size\": \"is-small\",\n \"both\": \"\"\n }\n })], 1)])], 1)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 179 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__ = __webpack_require__(5);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol__);\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTableColumn',\n props: {\n label: String,\n customKey: [String, Number],\n field: String,\n meta: [String, Number, Boolean, Function, Object, Array, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_core_js_symbol___default.a],\n width: [Number, String],\n numeric: Boolean,\n centered: Boolean,\n sortable: Boolean,\n visible: {\n type: Boolean,\n default: true\n },\n customSort: Function,\n internal: Boolean // Used internally by Table\n },\n data: function data() {\n return {\n newKey: this.customKey || this.label\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return {\n 'has-text-right': this.numeric && !this.centered,\n 'has-text-centered': this.centered\n };\n }\n },\n beforeMount: function beforeMount() {\n var _this = this;\n\n if (!this.$parent.$data._isTable) {\n this.$destroy();\n throw new Error('You should wrap bTableColumn on a bTable');\n }\n\n if (this.internal) return;\n\n // Since we're using scoped prop the columns gonna be multiplied,\n // this finds when to stop based on the newKey property.\n var repeated = this.$parent.columns.some(function (column) {\n return column.newKey === _this.newKey;\n });\n !repeated && this.$parent.columns.push(this);\n },\n beforeDestroy: function beforeDestroy() {\n var index = this.$parent.columns.map(function (column) {\n return column.newKey;\n }).indexOf(this.newKey);\n if (index >= 0) {\n this.$parent.columns.splice(index, 1);\n }\n }\n});\n\n/***/ }),\n/* 180 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return (_vm.visible) ? _c('td', {\n class: _vm.rootClasses,\n attrs: {\n \"data-label\": _vm.label\n }\n }, [_c('span', [_vm._t(\"default\")], 2)]) : _vm._e()\n},staticRenderFns: []}\n\n/***/ }),\n/* 181 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"b-table\",\n class: {\n 'is-loading': _vm.loading\n }\n }, [(_vm.mobileCards && _vm.hasSortablenewColumns) ? _c('b-table-mobile-sort', {\n attrs: {\n \"current-sort-column\": _vm.currentSortColumn,\n \"is-asc\": _vm.isAsc,\n \"columns\": _vm.newColumns\n },\n on: {\n \"sort\": function (column) { return _vm.sort(column); }\n }\n }) : _vm._e(), _vm._v(\" \"), _c('div', {\n staticClass: \"table-wrapper\"\n }, [_c('table', {\n staticClass: \"table\",\n class: _vm.tableClasses,\n attrs: {\n \"tabindex\": !_vm.focusable ? false : 0\n },\n on: {\n \"keydown\": [function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"up\", 38, $event.key)) { return null; }\n $event.preventDefault();\n _vm.pressedArrow(-1)\n }, function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"down\", 40, $event.key)) { return null; }\n $event.preventDefault();\n _vm.pressedArrow(1)\n }]\n }\n }, [(_vm.newColumns.length) ? _c('thead', [_c('tr', [(_vm.detailed) ? _c('th', {\n attrs: {\n \"width\": \"40px\"\n }\n }) : _vm._e(), _vm._v(\" \"), (_vm.checkable) ? _c('th', {\n staticClass: \"checkbox-cell\"\n }, [_c('b-checkbox', {\n attrs: {\n \"value\": _vm.isAllChecked,\n \"disabled\": _vm.isAllUncheckable\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.checkAll($event)\n }\n }\n })], 1) : _vm._e(), _vm._v(\" \"), _vm._l((_vm.newColumns), function(column, index) {\n return (column.visible || column.visible === undefined) ? _c('th', {\n key: index,\n class: {\n 'is-current-sort': _vm.currentSortColumn === column,\n 'is-sortable': column.sortable\n },\n style: ({\n width: column.width + 'px'\n }),\n on: {\n \"click\": function($event) {\n $event.stopPropagation();\n _vm.sort(column)\n }\n }\n }, [_c('div', {\n staticClass: \"th-wrap\",\n class: {\n 'is-numeric': column.numeric,\n 'is-centered': column.centered\n }\n }, [(_vm.$scopedSlots.header) ? _vm._t(\"header\", null, {\n column: column,\n index: index\n }) : [_vm._v(_vm._s(column.label))], _vm._v(\" \"), _c('b-icon', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.currentSortColumn === column),\n expression: \"currentSortColumn === column\"\n }],\n class: {\n 'is-desc': !_vm.isAsc\n },\n attrs: {\n \"icon\": \"arrow-up\",\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\",\n \"size\": \"is-small\"\n }\n })], 2)]) : _vm._e()\n })], 2)]) : _vm._e(), _vm._v(\" \"), (_vm.visibleData.length) ? _c('tbody', [_vm._l((_vm.visibleData), function(row, index) {\n return [_c('tr', {\n key: index,\n class: [_vm.rowClass(row, index), {\n 'is-selected': row === _vm.selected,\n 'is-checked': _vm.isRowChecked(row)\n }],\n on: {\n \"click\": function($event) {\n _vm.selectRow(row)\n },\n \"dblclick\": function($event) {\n _vm.$emit('dblclick', row)\n }\n }\n }, [(_vm.detailed) ? _c('td', {\n staticClass: \"chevron-cell\"\n }, [(_vm.hasDetailedVisible(row)) ? _c('a', {\n attrs: {\n \"role\": \"button\"\n },\n on: {\n \"click\": function($event) {\n $event.stopPropagation();\n _vm.toggleDetails(row)\n }\n }\n }, [_c('b-icon', {\n class: {\n 'is-expanded': _vm.isVisibleDetailRow(row)\n },\n attrs: {\n \"icon\": \"chevron-right\",\n \"icon-pack\": _vm.iconPack,\n \"both\": \"\"\n }\n })], 1) : _vm._e()]) : _vm._e(), _vm._v(\" \"), (_vm.checkable) ? _c('td', {\n staticClass: \"checkbox-cell\"\n }, [_c('b-checkbox', {\n attrs: {\n \"disabled\": !_vm.isRowCheckable(row),\n \"value\": _vm.isRowChecked(row)\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.checkRow(row)\n }\n }\n })], 1) : _vm._e(), _vm._v(\" \"), (_vm.$scopedSlots.default) ? _vm._t(\"default\", null, {\n row: row,\n index: index\n }) : _vm._l((_vm.newColumns), function(column) {\n return _c('BTableColumn', _vm._b({\n key: column.field,\n attrs: {\n \"internal\": \"\"\n }\n }, 'BTableColumn', column, false), [(column.renderHtml) ? _c('span', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.getValueByPath(row, column.field))\n }\n }) : [_vm._v(\"\\n \" + _vm._s(_vm.getValueByPath(row, column.field)) + \"\\n \")]], 2)\n })], 2), _vm._v(\" \"), (_vm.detailed && _vm.isVisibleDetailRow(row)) ? _c('tr', {\n staticClass: \"detail\"\n }, [_c('td', {\n attrs: {\n \"colspan\": _vm.columnCount\n }\n }, [_c('div', {\n staticClass: \"detail-container\"\n }, [_vm._t(\"detail\", null, {\n row: row,\n index: index\n })], 2)])]) : _vm._e()]\n })], 2) : _c('tbody', [_c('tr', {\n staticClass: \"is-empty\"\n }, [_c('td', {\n attrs: {\n \"colspan\": _vm.columnCount\n }\n }, [_vm._t(\"empty\")], 2)])]), _vm._v(\" \"), (_vm.$slots.footer !== undefined) ? _c('tfoot', [_c('tr', {\n staticClass: \"table-footer\"\n }, [(_vm.hasCustomFooterSlot()) ? _vm._t(\"footer\") : _c('th', {\n attrs: {\n \"colspan\": _vm.columnCount\n }\n }, [_vm._t(\"footer\")], 2)], 2)]) : _vm._e()])]), _vm._v(\" \"), ((_vm.checkable && _vm.hasBottomLeftSlot()) || _vm.paginated) ? _c('div', {\n staticClass: \"level\"\n }, [_c('div', {\n staticClass: \"level-left\"\n }, [_vm._t(\"bottom-left\")], 2), _vm._v(\" \"), _c('div', {\n staticClass: \"level-right\"\n }, [(_vm.paginated) ? _c('div', {\n staticClass: \"level-item\"\n }, [_c('b-pagination', {\n attrs: {\n \"icon-pack\": _vm.iconPack,\n \"total\": _vm.newDataTotal,\n \"per-page\": _vm.perPage,\n \"simple\": _vm.paginationSimple,\n \"size\": _vm.paginationSize,\n \"current\": _vm.newCurrentPage\n },\n on: {\n \"change\": _vm.pageChanged\n }\n })], 1) : _vm._e()])]) : _vm._e()], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 182 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(183),\n /* template */\n __webpack_require__(184),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 183 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n\n// EXTERNAL MODULE: ./node_modules/babel-runtime/helpers/defineProperty.js\nvar defineProperty = __webpack_require__(1);\nvar defineProperty_default = /*#__PURE__*/__webpack_require__.n(defineProperty);\n\n// EXTERNAL MODULE: ./src/components/icon/Icon.vue\nvar Icon = __webpack_require__(3);\nvar Icon_default = /*#__PURE__*/__webpack_require__.n(Icon);\n\n// CONCATENATED MODULE: ./src/utils/SlotComponent.js\n/* harmony default export */ var SlotComponent = ({\n name: 'BSlotComponent',\n props: {\n component: {\n type: Object,\n required: true\n },\n name: {\n type: String,\n default: 'default'\n },\n tag: {\n type: String,\n default: 'div'\n },\n event: {\n type: String,\n default: 'hook:updated'\n }\n },\n methods: {\n refresh: function refresh() {\n this.$forceUpdate();\n },\n isVueComponent: function isVueComponent() {\n return this.component && this.component._isVue;\n }\n },\n created: function created() {\n if (this.isVueComponent()) {\n this.component.$on(this.event, this.refresh);\n }\n },\n beforeDestroy: function beforeDestroy() {\n if (this.isVueComponent()) {\n this.component.$off(this.event, this.refresh);\n }\n },\n render: function render(h) {\n if (this.isVueComponent()) {\n var slots = this.component.$slots[this.name];\n return h(this.tag, {}, slots);\n }\n }\n});\n// CONCATENATED MODULE: ./node_modules/babel-loader/lib!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/tabs/Tabs.vue\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ var Tabs = __webpack_exports__[\"default\"] = ({\n name: 'BTabs',\n components: (_components = {}, defineProperty_default()(_components, Icon_default.a.name, Icon_default.a), defineProperty_default()(_components, SlotComponent.name, SlotComponent), _components),\n props: {\n value: [String, Number],\n expanded: Boolean,\n type: String,\n size: String,\n position: String,\n animated: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n return {\n activeTab: this.value || 0,\n tabItems: [],\n contentHeight: 0,\n _isTabs: true // Used internally by TabItem\n };\n },\n\n computed: {\n navClasses: function navClasses() {\n return [this.type, this.size, this.position, {\n 'is-fullwidth': this.expanded,\n 'is-toggle-rounded is-toggle': this.type === 'is-toggle-rounded'\n }];\n }\n },\n watch: {\n /**\n * When v-model is changed set the new active tab.\n */\n value: function value(_value) {\n this.changeTab(_value);\n },\n\n\n /**\n * When tab-items are updated, set active one.\n */\n tabItems: function tabItems() {\n if (this.tabItems.length) {\n this.tabItems[this.activeTab].isActive = true;\n }\n }\n },\n methods: {\n /**\n * Change the active tab and emit change event.\n */\n changeTab: function changeTab(newIndex) {\n if (this.activeTab === newIndex) return;\n\n this.tabItems[this.activeTab].deactivate(this.activeTab, newIndex);\n this.tabItems[newIndex].activate(this.activeTab, newIndex);\n this.activeTab = newIndex;\n this.$emit('change', newIndex);\n },\n\n\n /**\n * Tab click listener, emit input event and change active tab.\n */\n tabClick: function tabClick(value) {\n this.$emit('input', value);\n this.changeTab(value);\n }\n },\n mounted: function mounted() {\n if (this.tabItems.length) {\n this.tabItems[this.activeTab].isActive = true;\n }\n }\n});\n\n/***/ }),\n/* 184 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"b-tabs\",\n class: {\n 'is-fullwidth': _vm.expanded\n }\n }, [_c('nav', {\n staticClass: \"tabs\",\n class: _vm.navClasses\n }, [_c('ul', _vm._l((_vm.tabItems), function(tabItem, index) {\n return _c('li', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (tabItem.visible),\n expression: \"tabItem.visible\"\n }],\n key: index,\n class: {\n 'is-active': _vm.activeTab === index, 'is-disabled': tabItem.disabled\n }\n }, [_c('a', {\n on: {\n \"click\": function($event) {\n _vm.tabClick(index)\n }\n }\n }, [(tabItem.$slots.header) ? [_c('b-slot-component', {\n attrs: {\n \"component\": tabItem,\n \"name\": \"header\",\n \"tag\": \"span\"\n }\n })] : [(tabItem.icon) ? _c('b-icon', {\n attrs: {\n \"icon\": tabItem.icon,\n \"pack\": tabItem.iconPack,\n \"size\": _vm.size\n }\n }) : _vm._e(), _vm._v(\" \"), _c('span', [_vm._v(_vm._s(tabItem.label))])]], 2)])\n }))]), _vm._v(\" \"), _c('section', {\n staticClass: \"tab-content\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 185 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(186),\n /* template */\n __webpack_require__(187),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 186 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_BaseElementMixin__ = __webpack_require__(13);\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTabItem',\n mixins: [__WEBPACK_IMPORTED_MODULE_0__utils_BaseElementMixin__[\"a\" /* default */]],\n props: {\n label: String,\n icon: String,\n disabled: Boolean,\n visible: {\n type: Boolean,\n default: true\n }\n },\n data: function data() {\n return {\n isActive: false,\n transitionName: null\n };\n },\n\n methods: {\n /**\n * Activate tab, alter animation name based on the index.\n */\n activate: function activate(oldIndex, index) {\n if (!this.$parent.animated) {\n this.transitionName = null;\n } else {\n this.transitionName = index < oldIndex ? 'slide-next' : 'slide-prev';\n }\n this.isActive = true;\n },\n\n\n /**\n * Deactivate tab, alter animation name based on the index.\n */\n deactivate: function deactivate(oldIndex, index) {\n if (!this.$parent.animated) {\n this.transitionName = null;\n } else {\n this.transitionName = index < oldIndex ? 'slide-next' : 'slide-prev';\n }\n this.isActive = false;\n }\n },\n created: function created() {\n if (!this.$parent.$data._isTabs) {\n this.$destroy();\n throw new Error('You should wrap bTabItem on a bTabs');\n }\n this.$parent.tabItems.push(this);\n },\n beforeDestroy: function beforeDestroy() {\n var index = this.$parent.tabItems.indexOf(this);\n if (index >= 0) {\n this.$parent.tabItems.splice(index, 1);\n }\n }\n});\n\n/***/ }),\n/* 187 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"name\": _vm.transitionName\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive && _vm.visible),\n expression: \"isActive && visible\"\n }],\n staticClass: \"tab-item\"\n }, [_vm._t(\"default\")], 2)])\n},staticRenderFns: []}\n\n/***/ }),\n/* 188 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTag',\n props: {\n attached: Boolean,\n closable: Boolean,\n type: String,\n size: String,\n rounded: Boolean,\n disabled: Boolean,\n ellipsis: Boolean,\n tabstop: {\n type: Boolean,\n default: true\n }\n },\n methods: {\n /**\n * Emit close event when delete button is clicked\n * or delete key is pressed.\n */\n close: function close() {\n if (this.disabled) return;\n\n this.$emit('close');\n }\n }\n});\n\n/***/ }),\n/* 189 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return (_vm.attached && _vm.closable) ? _c('div', {\n staticClass: \"tags has-addons\"\n }, [_c('span', {\n staticClass: \"tag\",\n class: [_vm.type, _vm.size, {\n 'is-rounded': _vm.rounded\n }]\n }, [_c('span', {\n class: {\n 'has-ellipsis': _vm.ellipsis\n }\n }, [_vm._t(\"default\")], 2)]), _vm._v(\" \"), _c('a', {\n staticClass: \"tag is-delete\",\n class: [_vm.size, {\n 'is-rounded': _vm.rounded\n }],\n attrs: {\n \"role\": \"button\",\n \"tabindex\": _vm.tabstop ? 0 : false,\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n _vm.close()\n },\n \"keyup\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"delete\", [8, 46], $event.key)) { return null; }\n $event.preventDefault();\n _vm.close()\n }\n }\n })]) : _c('span', {\n staticClass: \"tag\",\n class: [_vm.type, _vm.size, {\n 'is-rounded': _vm.rounded\n }]\n }, [_c('span', {\n class: {\n 'has-ellipsis': _vm.ellipsis\n }\n }, [_vm._t(\"default\")], 2), _vm._v(\" \"), (_vm.closable) ? _c('a', {\n staticClass: \"delete is-small\",\n attrs: {\n \"role\": \"button\",\n \"disabled\": _vm.disabled,\n \"tabindex\": _vm.tabstop ? 0 : false\n },\n on: {\n \"click\": function($event) {\n _vm.close()\n },\n \"keyup\": function($event) {\n if (!('button' in $event) && _vm._k($event.keyCode, \"delete\", [8, 46], $event.key)) { return null; }\n $event.preventDefault();\n _vm.close()\n }\n }\n }) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 190 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(191),\n /* template */\n __webpack_require__(192),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 191 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n//\n//\n//\n//\n//\n//\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTaglist',\n props: {\n attached: Boolean\n }\n});\n\n/***/ }),\n/* 192 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"tags\",\n class: {\n 'has-addons': _vm.attached\n }\n }, [_vm._t(\"default\")], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 193 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(194),\n /* template */\n __webpack_require__(195),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 194 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__ = __webpack_require__(53);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__tag_Tag__ = __webpack_require__(67);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__tag_Tag___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_3__tag_Tag__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete__ = __webpack_require__(52);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__utils_FormElementMixin__ = __webpack_require__(12);\n\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTaginput',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete___default.a.name, __WEBPACK_IMPORTED_MODULE_4__autocomplete_Autocomplete___default.a), __WEBPACK_IMPORTED_MODULE_1_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_3__tag_Tag___default.a.name, __WEBPACK_IMPORTED_MODULE_3__tag_Tag___default.a), _components),\n mixins: [__WEBPACK_IMPORTED_MODULE_5__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n data: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n type: String,\n rounded: {\n type: Boolean,\n default: false\n },\n attached: {\n type: Boolean,\n default: false\n },\n maxtags: {\n type: [Number, String],\n required: false\n },\n field: {\n type: String,\n default: 'value'\n },\n autocomplete: Boolean,\n disabled: Boolean,\n ellipsis: Boolean,\n confirmKeyCodes: {\n type: Array,\n default: function _default() {\n return [13, 188];\n }\n },\n removeOnKeys: {\n type: Array,\n default: function _default() {\n return [8];\n }\n },\n allowNew: Boolean,\n onPasteSeparators: {\n type: Array,\n default: function _default() {\n return [','];\n }\n },\n beforeAdding: {\n type: Function,\n default: function _default() {\n return true;\n }\n },\n allowDuplicates: {\n type: Boolean,\n default: false\n }\n },\n data: function data() {\n return {\n tags: this.value || [],\n newTag: '',\n _elementRef: 'input',\n _isTaginput: true\n };\n },\n\n computed: {\n rootClasses: function rootClasses() {\n return {\n 'is-expanded': this.expanded\n };\n },\n containerClasses: function containerClasses() {\n return {\n 'is-focused': this.isFocused,\n 'is-focusable': this.hasInput\n };\n },\n valueLength: function valueLength() {\n return this.newTag.trim().length;\n },\n defaultSlotName: function defaultSlotName() {\n return this.hasDefaultSlot ? 'default' : 'dontrender';\n },\n emptySlotName: function emptySlotName() {\n return this.hasEmptySlot ? 'empty' : 'dontrender';\n },\n hasDefaultSlot: function hasDefaultSlot() {\n return !!this.$scopedSlots.default;\n },\n hasEmptySlot: function hasEmptySlot() {\n return !!this.$slots.empty;\n },\n\n\n /**\n * Show the input field if a maxtags hasn't been set or reached.\n */\n hasInput: function hasInput() {\n return this.maxtags == null || this.tagsLength < this.maxtags;\n },\n tagsLength: function tagsLength() {\n return this.tags.length;\n },\n\n\n /**\n * If Taginput has onPasteSeparators prop,\n * returning new RegExp used to split pasted string.\n */\n separatorsAsRegExp: function separatorsAsRegExp() {\n var sep = this.onPasteSeparators;\n\n return sep.length ? new RegExp(sep.map(function (s) {\n return s ? s.replace(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g, '\\\\$&') : null;\n }).join('|'), 'g') : null;\n }\n },\n watch: {\n /**\n * When v-model is changed set internal value.\n */\n value: function value(_value) {\n this.tags = _value;\n },\n newTag: function newTag(value) {\n this.$emit('typing', value.trim());\n },\n hasInput: function hasInput() {\n if (!this.hasInput) this.onBlur();\n }\n },\n methods: {\n addTag: function addTag(tag) {\n var tagToAdd = tag || this.newTag.trim();\n\n if (tagToAdd) {\n if (!this.autocomplete) {\n var reg = this.separatorsAsRegExp;\n if (reg && tagToAdd.match(reg)) {\n tagToAdd.split(reg).map(function (t) {\n return t.trim();\n }).filter(function (t) {\n return t.length !== 0;\n }).map(this.addTag);\n return;\n }\n }\n\n // Add the tag input if it is not blank\n // or previously added (if not allowDuplicates).\n var add = !this.allowDuplicates ? this.tags.indexOf(tagToAdd) === -1 : true;\n if (add && this.beforeAdding(tagToAdd)) {\n this.tags.push(tagToAdd);\n this.$emit('input', this.tags);\n this.$emit('add', tagToAdd);\n }\n }\n\n this.newTag = '';\n },\n getNormalizedTagText: function getNormalizedTagText(tag) {\n if ((typeof tag === 'undefined' ? 'undefined' : __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_typeof___default()(tag)) === 'object') {\n return Object(__WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"b\" /* getValueByPath */])(tag, this.field);\n }\n\n return tag;\n },\n customOnBlur: function customOnBlur($event) {\n // Add tag on-blur if not select only\n if (!this.autocomplete) this.addTag();\n\n this.onBlur($event);\n },\n onSelect: function onSelect(option) {\n var _this = this;\n\n if (!option) return;\n\n this.addTag(option);\n this.$nextTick(function () {\n _this.newTag = '';\n });\n },\n removeTag: function removeTag(index) {\n var tag = this.tags.splice(index, 1)[0];\n this.$emit('input', this.tags);\n this.$emit('remove', tag);\n return tag;\n },\n removeLastTag: function removeLastTag() {\n if (this.tagsLength > 0) {\n this.removeTag(this.tagsLength - 1);\n }\n },\n keydown: function keydown(event) {\n if (this.removeOnKeys.indexOf(event.keyCode) !== -1 && !this.newTag.length) {\n this.removeLastTag();\n }\n // Stop if is to accept select only\n if (this.autocomplete && !this.allowNew) return;\n\n if (this.confirmKeyCodes.indexOf(event.keyCode) >= 0) {\n event.preventDefault();\n this.addTag();\n }\n }\n }\n});\n\n/***/ }),\n/* 195 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"taginput control\",\n class: _vm.rootClasses\n }, [_c('div', {\n staticClass: \"taginput-container\",\n class: [_vm.statusType, _vm.size, _vm.containerClasses],\n attrs: {\n \"disabled\": _vm.disabled\n },\n on: {\n \"click\": function($event) {\n _vm.hasInput && _vm.focus($event)\n }\n }\n }, [_vm._l((_vm.tags), function(tag, index) {\n return _c('b-tag', {\n key: index,\n attrs: {\n \"type\": _vm.type,\n \"size\": _vm.size,\n \"rounded\": _vm.rounded,\n \"attached\": _vm.attached,\n \"tabstop\": false,\n \"disabled\": _vm.disabled,\n \"ellipsis\": _vm.ellipsis,\n \"closable\": \"\"\n },\n on: {\n \"close\": function($event) {\n _vm.removeTag(index)\n }\n }\n }, [_vm._v(\"\\n \" + _vm._s(_vm.getNormalizedTagText(tag)) + \"\\n \")])\n }), _vm._v(\" \"), (_vm.hasInput) ? _c('b-autocomplete', _vm._b({\n ref: \"autocomplete\",\n attrs: {\n \"data\": _vm.data,\n \"field\": _vm.field,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"maxlength\": _vm.maxlength,\n \"has-counter\": false,\n \"size\": _vm.size,\n \"disabled\": _vm.disabled,\n \"loading\": _vm.loading,\n \"keep-first\": \"\"\n },\n on: {\n \"focus\": _vm.onFocus,\n \"blur\": _vm.customOnBlur,\n \"select\": _vm.onSelect\n },\n nativeOn: {\n \"keydown\": function($event) {\n _vm.keydown($event)\n }\n },\n scopedSlots: _vm._u([{\n key: _vm.defaultSlotName,\n fn: function(props) {\n return [_vm._t(\"default\", null, {\n option: props.option,\n index: props.index\n })]\n }\n }]),\n model: {\n value: (_vm.newTag),\n callback: function($$v) {\n _vm.newTag = $$v\n },\n expression: \"newTag\"\n }\n }, 'b-autocomplete', _vm.$attrs, false), [_c('template', {\n slot: _vm.emptySlotName\n }, [_vm._t(\"empty\")], 2)], 2) : _vm._e()], 2), _vm._v(\" \"), (_vm.maxtags || _vm.maxlength) ? _c('p', {\n staticClass: \"help counter\"\n }, [(_vm.maxlength && _vm.valueLength > 0) ? [_vm._v(\"\\n \" + _vm._s(_vm.valueLength) + \" / \" + _vm._s(_vm.maxlength) + \"\\n \")] : (_vm.maxtags) ? [_vm._v(\"\\n \" + _vm._s(_vm.tagsLength) + \" / \" + _vm._s(_vm.maxtags) + \"\\n \")] : _vm._e()], 2) : _vm._e()])\n},staticRenderFns: []}\n\n/***/ }),\n/* 196 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(197),\n /* template */\n __webpack_require__(198),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 197 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__ = __webpack_require__(1);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__ = __webpack_require__(12);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__utils_helpers__ = __webpack_require__(6);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__ = __webpack_require__(43);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__ = __webpack_require__(44);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__input_Input__ = __webpack_require__(27);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__input_Input___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_6__input_Input__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__field_Field__ = __webpack_require__(45);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__field_Field___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_7__field_Field__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__select_Select__ = __webpack_require__(28);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__select_Select___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_8__select_Select__);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__icon_Icon__ = __webpack_require__(3);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_9__icon_Icon__);\n\n\nvar _components;\n\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n\n\n\n\n\n\n\n\nvar AM = 'AM';\nvar PM = 'PM';\nvar HOUR_FORMAT_24 = '24';\nvar HOUR_FORMAT_12 = '12';\n\nvar formatNumber = function formatNumber(value) {\n return (value < 10 ? '0' : '') + value;\n};\n\nvar timeFormatter = function timeFormatter(date, vm) {\n var hours = date.getHours();\n var minutes = date.getMinutes();\n var am = false;\n if (vm.hourFormat === HOUR_FORMAT_12) {\n am = hours < 12;\n if (hours > 12) {\n hours -= 12;\n } else if (hours === 0) {\n hours = 12;\n }\n }\n return formatNumber(hours) + ':' + formatNumber(minutes) + (vm.hourFormat === HOUR_FORMAT_12 ? ' ' + (am ? AM : PM) : '');\n};\n\nvar timeParser = function timeParser(date, vm) {\n if (date) {\n var dateString = date;\n var am = false;\n if (vm.hourFormat === HOUR_FORMAT_12) {\n var dateString12 = date.split(' ');\n dateString = dateString12[0];\n am = dateString12[1] === AM;\n }\n var time = dateString.split(':');\n var hours = parseInt(time[0], 10);\n var minutes = parseInt(time[1], 10);\n if (isNaN(hours) || hours < 0 || hours > 23 || vm.hourFormat === HOUR_FORMAT_12 && (hours < 1 || hours > 12) || isNaN(minutes) || minutes < 0 || minutes > 59) {\n return null;\n }\n var d = null;\n if (vm.dateSelected && !isNaN(vm.dateSelected)) {\n d = new Date(vm.dateSelected);\n } else {\n d = new Date();\n d.setMilliseconds(0);\n d.setSeconds(0);\n }\n d.setMinutes(minutes);\n if (vm.hourFormat === HOUR_FORMAT_12) {\n if (am && hours === 12) {\n hours = 0;\n } else if (!am && hours !== 12) {\n hours += 12;\n }\n }\n d.setHours(hours);\n return d;\n }\n return null;\n};\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTimepicker',\n components: (_components = {}, __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_6__input_Input___default.a.name, __WEBPACK_IMPORTED_MODULE_6__input_Input___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_7__field_Field___default.a.name, __WEBPACK_IMPORTED_MODULE_7__field_Field___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_8__select_Select___default.a.name, __WEBPACK_IMPORTED_MODULE_8__select_Select___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default.a.name, __WEBPACK_IMPORTED_MODULE_9__icon_Icon___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default.a.name, __WEBPACK_IMPORTED_MODULE_4__dropdown_Dropdown___default.a), __WEBPACK_IMPORTED_MODULE_0_babel_runtime_helpers_defineProperty___default()(_components, __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default.a.name, __WEBPACK_IMPORTED_MODULE_5__dropdown_DropdownItem___default.a), _components),\n mixins: [__WEBPACK_IMPORTED_MODULE_1__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: Date,\n inline: Boolean,\n minTime: Date,\n maxTime: Date,\n placeholder: String,\n readonly: {\n type: Boolean,\n default: true\n },\n disabled: {\n type: Boolean,\n default: false\n },\n hourFormat: {\n type: String,\n default: HOUR_FORMAT_24,\n validator: function validator(value) {\n return value === HOUR_FORMAT_24 || value === HOUR_FORMAT_12;\n }\n },\n incrementMinutes: {\n type: Number,\n default: 1\n },\n timeFormatter: {\n type: Function,\n default: function _default(date, vm) {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimeFormatter === 'function') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimeFormatter(date);\n } else {\n return timeFormatter(date, vm);\n }\n }\n },\n timeParser: {\n type: Function,\n default: function _default(date, vm) {\n if (typeof __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimeParser === 'function') {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimeParser(date);\n } else {\n return timeParser(date, vm);\n }\n }\n },\n mobileNative: {\n type: Boolean,\n default: function _default() {\n return __WEBPACK_IMPORTED_MODULE_3__utils_config__[\"a\" /* default */].defaultTimepickerMobileNative;\n }\n },\n position: String,\n unselectableTimes: Array\n },\n data: function data() {\n return {\n dateSelected: this.value,\n hoursSelected: null,\n minutesSelected: null,\n meridienSelected: null,\n _elementRef: 'input',\n _isTimepicker: true\n };\n },\n\n computed: {\n hours: function hours() {\n var hours = [];\n var numberOfHours = this.isHourFormat24 ? 24 : 12;\n for (var i = 0; i < numberOfHours; i++) {\n var value = i;\n var label = value;\n if (!this.isHourFormat24) {\n value = i + 1;\n label = value;\n if (this.meridienSelected === AM) {\n if (value === 12) {\n value = 0;\n }\n } else if (this.meridienSelected === PM) {\n if (value !== 12) {\n value += 12;\n }\n }\n }\n hours.push({\n label: formatNumber(label),\n value: value\n });\n }\n return hours;\n },\n minutes: function minutes() {\n var minutes = [];\n for (var i = 0; i < 60; i += this.incrementMinutes) {\n minutes.push({\n label: formatNumber(i),\n value: i\n });\n }\n return minutes;\n },\n meridiens: function meridiens() {\n return [AM, PM];\n },\n isMobile: function isMobile() {\n return this.mobileNative && __WEBPACK_IMPORTED_MODULE_2__utils_helpers__[\"d\" /* isMobile */].any();\n },\n isHourFormat24: function isHourFormat24() {\n return this.hourFormat === HOUR_FORMAT_24;\n }\n },\n watch: {\n hourFormat: function hourFormat(value) {\n if (this.hoursSelected !== null) {\n this.meridienSelected = this.hoursSelected >= 12 ? PM : AM;\n }\n },\n\n\n /**\n * Emit input event with selected date as payload.\n */\n dateSelected: function dateSelected(value) {\n this.$emit('input', value);\n },\n\n\n /**\n * When v-model is changed:\n * 1. Update internal value.\n * 2. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.updateInternalState(_value);\n this.dateSelected = _value;\n\n !this.isValid && this.$refs.input.checkHtml5Validity();\n }\n },\n methods: {\n onMeridienChange: function onMeridienChange(value) {\n if (this.hoursSelected !== null) {\n if (value === PM) {\n if (this.hoursSelected === 0) {\n this.hoursSelected = 12;\n } else {\n this.hoursSelected += 12;\n }\n } else if (value === AM) {\n if (this.hoursSelected === 12) {\n this.hoursSelected = 0;\n } else {\n this.hoursSelected -= 12;\n }\n }\n }\n this.updateDateSelected(this.hoursSelected, this.minutesSelected, value);\n },\n onHoursChange: function onHoursChange(value) {\n this.updateDateSelected(parseInt(value, 10), this.minutesSelected, this.meridienSelected);\n },\n onMinutesChange: function onMinutesChange(value) {\n this.updateDateSelected(this.hoursSelected, parseInt(value, 10), this.meridienSelected);\n },\n updateDateSelected: function updateDateSelected(hours, minutes, meridiens) {\n if (hours != null && minutes != null && (!this.isHourFormat24 && meridiens !== null || this.isHourFormat24)) {\n if (this.dateSelected && !isNaN(this.dateSelected)) {\n this.dateSelected = new Date(this.dateSelected);\n } else {\n this.dateSelected = new Date();\n this.dateSelected.setMilliseconds(0);\n this.dateSelected.setSeconds(0);\n }\n this.dateSelected.setHours(hours);\n this.dateSelected.setMinutes(minutes);\n }\n },\n updateInternalState: function updateInternalState(value) {\n if (value) {\n this.hoursSelected = value.getHours();\n this.minutesSelected = value.getMinutes();\n this.meridienSelected = value.getHours() >= 12 ? PM : AM;\n } else {\n this.hoursSelected = null;\n this.minutesSelected = null;\n this.meridienSelected = AM;\n }\n },\n isHourDisabled: function isHourDisabled(hour) {\n var _this = this;\n\n var disabled = false;\n if (this.minTime) {\n var minHours = this.minTime.getHours();\n disabled = hour < minHours;\n }\n if (this.maxTime) {\n if (!disabled) {\n var maxHours = this.maxTime.getHours();\n disabled = hour > maxHours;\n }\n }\n if (this.unselectableTimes) {\n if (!disabled) {\n if (this.minutesSelected !== null) {\n var unselectable = this.unselectableTimes.filter(function (time) {\n return time.getHours() === hour && time.getMinutes() === _this.minutesSelected;\n });\n disabled = unselectable.length > 0;\n } else {\n var _unselectable = this.unselectableTimes.filter(function (time) {\n return time.getHours() === hour;\n });\n disabled = _unselectable.length === this.minutes.length;\n }\n }\n }\n return disabled;\n },\n isMinuteDisabled: function isMinuteDisabled(minute) {\n var _this2 = this;\n\n var disabled = false;\n if (this.hoursSelected !== null) {\n if (this.isHourDisabled(this.hoursSelected)) {\n disabled = true;\n } else {\n if (this.minTime) {\n var minHours = this.minTime.getHours();\n var minMinutes = this.minTime.getMinutes();\n disabled = this.hoursSelected === minHours && minute < minMinutes;\n }\n if (this.maxTime) {\n if (!disabled) {\n var maxHours = this.maxTime.getHours();\n var _minMinutes = this.maxTime.getMinutes();\n disabled = this.hoursSelected === maxHours && minute > _minMinutes;\n }\n }\n }\n if (this.unselectableTimes) {\n if (!disabled) {\n var unselectable = this.unselectableTimes.filter(function (time) {\n return time.getHours() === _this2.hoursSelected && time.getMinutes() === minute;\n });\n disabled = unselectable.length > 0;\n }\n }\n }\n return disabled;\n },\n\n\n /*\n * Parse string into date\n */\n onChange: function onChange(value) {\n var date = this.timeParser(value, this);\n this.updateInternalState(date);\n if (date && !isNaN(date)) {\n this.dateSelected = date;\n } else {\n // Force refresh input value when not valid date\n this.dateSelected = null;\n this.$refs.input.newValue = this.dateSelected;\n }\n },\n\n\n /*\n * Format date into string\n */\n formatValue: function formatValue(value) {\n if (value && !isNaN(value)) {\n return this.timeFormatter(value, this);\n } else {\n return null;\n }\n },\n\n\n /*\n * Close dropdown time picker\n */\n close: function close() {\n if (this.$refs.dropdown) {\n this.$refs.dropdown.isActive = false;\n }\n },\n\n\n /*\n * Format date into string 'HH-MM-SS'\n */\n formatHHMMSS: function formatHHMMSS(value) {\n var date = new Date(value);\n if (value && !isNaN(date)) {\n var hours = date.getHours();\n var minutes = date.getMinutes();\n return formatNumber(hours) + ':' + formatNumber(minutes) + ':00';\n }\n return '';\n },\n\n\n /*\n * Parse time from string\n */\n onChangeNativePicker: function onChangeNativePicker(event) {\n var date = event.target.value;\n if (date) {\n if (this.dateSelected && !isNaN(this.dateSelected)) {\n this.dateSelected = new Date(this.dateSelected);\n } else {\n this.dateSelected = new Date();\n this.dateSelected.setMilliseconds(0);\n this.dateSelected.setSeconds(0);\n }\n var time = date.split(':');\n this.dateSelected.setHours(parseInt(time[0], 10));\n this.dateSelected.setMinutes(parseInt(time[1], 10));\n } else {\n this.dateSelected = null;\n }\n }\n },\n mounted: function mounted() {\n this.updateInternalState(this.value);\n }\n});\n\n/***/ }),\n/* 198 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('div', {\n staticClass: \"timepicker control\",\n class: [_vm.size, {\n 'is-expanded': _vm.expanded\n }]\n }, [(!_vm.isMobile || _vm.inline) ? _c('b-dropdown', {\n ref: \"dropdown\",\n attrs: {\n \"position\": _vm.position,\n \"disabled\": _vm.disabled,\n \"inline\": _vm.inline\n }\n }, [(!_vm.inline) ? _c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"slot\": \"trigger\",\n \"autocomplete\": \"off\",\n \"value\": _vm.formatValue(_vm.dateSelected),\n \"placeholder\": _vm.placeholder,\n \"size\": _vm.size,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"loading\": _vm.loading,\n \"disabled\": _vm.disabled,\n \"readonly\": _vm.readonly,\n \"rounded\": _vm.rounded\n },\n on: {\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n }\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onChange($event.target.value)\n }\n },\n slot: \"trigger\"\n }, 'b-input', _vm.$attrs, false)) : _vm._e(), _vm._v(\" \"), _c('b-dropdown-item', {\n attrs: {\n \"disabled\": _vm.disabled,\n \"custom\": \"\"\n }\n }, [_c('div', {\n staticClass: \"pagination-list\"\n }, [_c('b-field', [_c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled,\n \"placeholder\": \"00\"\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onHoursChange($event.target.value)\n }\n },\n model: {\n value: (_vm.hoursSelected),\n callback: function($$v) {\n _vm.hoursSelected = $$v\n },\n expression: \"hoursSelected\"\n }\n }, _vm._l((_vm.hours), function(hour) {\n return _c('option', {\n key: hour.value,\n attrs: {\n \"disabled\": _vm.isHourDisabled(hour.value)\n },\n domProps: {\n \"value\": hour.value\n }\n }, [_vm._v(\"\\n \" + _vm._s(hour.label) + \"\\n \")])\n })), _vm._v(\" \"), _c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled,\n \"placeholder\": \"00\"\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onMinutesChange($event.target.value)\n }\n },\n model: {\n value: (_vm.minutesSelected),\n callback: function($$v) {\n _vm.minutesSelected = $$v\n },\n expression: \"minutesSelected\"\n }\n }, _vm._l((_vm.minutes), function(minute) {\n return _c('option', {\n key: minute.value,\n attrs: {\n \"disabled\": _vm.isMinuteDisabled(minute.value)\n },\n domProps: {\n \"value\": minute.value\n }\n }, [_vm._v(\"\\n \" + _vm._s(minute.label) + \"\\n \")])\n })), _vm._v(\" \"), (!_vm.isHourFormat24) ? _c('b-select', {\n attrs: {\n \"disabled\": _vm.disabled\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onMeridienChange($event.target.value)\n }\n },\n model: {\n value: (_vm.meridienSelected),\n callback: function($$v) {\n _vm.meridienSelected = $$v\n },\n expression: \"meridienSelected\"\n }\n }, _vm._l((_vm.meridiens), function(meridien) {\n return _c('option', {\n key: meridien,\n domProps: {\n \"value\": meridien\n }\n }, [_vm._v(\"\\n \" + _vm._s(meridien) + \"\\n \")])\n })) : _vm._e()], 1)], 1), _vm._v(\" \"), (_vm.$slots.default !== undefined && _vm.$slots.default.length) ? _c('footer', {\n staticClass: \"timepicker-footer\"\n }, [_vm._t(\"default\")], 2) : _vm._e()])], 1) : _c('b-input', _vm._b({\n ref: \"input\",\n attrs: {\n \"type\": \"time\",\n \"autocomplete\": \"off\",\n \"value\": _vm.formatHHMMSS(_vm.value),\n \"placeholder\": _vm.placeholder,\n \"size\": _vm.size,\n \"icon\": _vm.icon,\n \"icon-pack\": _vm.iconPack,\n \"loading\": _vm.loading,\n \"max\": _vm.formatHHMMSS(_vm.maxTime),\n \"min\": _vm.formatHHMMSS(_vm.minTime),\n \"disabled\": _vm.disabled,\n \"readonly\": false\n },\n on: {\n \"focus\": function($event) {\n _vm.$emit('focus', $event)\n },\n \"blur\": function($event) {\n _vm.$emit('blur', $event) && _vm.checkHtml5Validity()\n }\n },\n nativeOn: {\n \"change\": function($event) {\n _vm.onChangeNativePicker($event)\n }\n }\n }, 'b-input', _vm.$attrs, false))], 1)\n},staticRenderFns: []}\n\n/***/ }),\n/* 199 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(200),\n /* template */\n __webpack_require__(201),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 200 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_config__ = __webpack_require__(2);\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__ = __webpack_require__(65);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BToast',\n mixins: [__WEBPACK_IMPORTED_MODULE_1__utils_NoticeMixin_js__[\"a\" /* default */]],\n data: function data() {\n return {\n newDuration: this.duration || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultToastDuration\n };\n }\n});\n\n/***/ }),\n/* 201 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('transition', {\n attrs: {\n \"enter-active-class\": _vm.transition.enter,\n \"leave-active-class\": _vm.transition.leave\n }\n }, [_c('div', {\n directives: [{\n name: \"show\",\n rawName: \"v-show\",\n value: (_vm.isActive),\n expression: \"isActive\"\n }],\n staticClass: \"toast\",\n class: [_vm.type, _vm.position]\n }, [_c('div', {\n domProps: {\n \"innerHTML\": _vm._s(_vm.message)\n }\n })])])\n},staticRenderFns: []}\n\n/***/ }),\n/* 202 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(203),\n /* template */\n __webpack_require__(204),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 203 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_config__ = __webpack_require__(2);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BTooltip',\n props: {\n active: {\n type: Boolean,\n default: true\n },\n type: String,\n label: String,\n position: {\n type: String,\n default: 'is-top',\n validator: function validator(value) {\n return ['is-top', 'is-bottom', 'is-left', 'is-right'].indexOf(value) > -1;\n }\n },\n always: Boolean,\n animated: Boolean,\n square: Boolean,\n dashed: Boolean,\n multilined: Boolean,\n size: {\n type: String,\n default: 'is-medium'\n }\n },\n computed: {\n newType: function newType() {\n return this.type || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultTooltipType;\n },\n newAnimated: function newAnimated() {\n return this.animated || __WEBPACK_IMPORTED_MODULE_0__utils_config__[\"a\" /* default */].defaultTooltipAnimated;\n }\n }\n});\n\n/***/ }),\n/* 204 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('span', {\n class: [_vm.newType, _vm.position, _vm.size, {\n 'tooltip': _vm.active,\n 'is-square': _vm.square,\n 'is-animated': _vm.newAnimated,\n 'is-always': _vm.always,\n 'is-multiline': _vm.multilined,\n 'is-dashed': _vm.dashed\n }],\n attrs: {\n \"data-label\": _vm.label\n }\n }, [_vm._t(\"default\")], 2)\n},staticRenderFns: []}\n\n/***/ }),\n/* 205 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Component = __webpack_require__(0)(\n /* script */\n __webpack_require__(206),\n /* template */\n __webpack_require__(207),\n /* styles */\n null,\n /* scopeId */\n null,\n /* moduleIdentifier (server only) */\n null\n)\n\nmodule.exports = Component.exports\n\n\n/***/ }),\n/* 206 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\nObject.defineProperty(__webpack_exports__, \"__esModule\", { value: true });\n/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__utils_FormElementMixin__ = __webpack_require__(12);\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n name: 'BUpload',\n mixins: [__WEBPACK_IMPORTED_MODULE_0__utils_FormElementMixin__[\"a\" /* default */]],\n inheritAttrs: false,\n props: {\n value: {\n type: Array,\n default: function _default() {\n return [];\n }\n },\n multiple: Boolean,\n disabled: Boolean,\n accept: String,\n dragDrop: Boolean,\n type: {\n type: String,\n default: 'is-primary'\n },\n native: {\n type: Boolean,\n default: false\n }\n },\n data: function data() {\n return {\n newValue: this.value || [],\n dragDropFocus: false,\n _elementRef: 'input'\n };\n },\n\n watch: {\n /**\n * When v-model is changed:\n * 1. Set internal value.\n * 2. Reset input value if array is empty\n * 3. If it's invalid, validate again.\n */\n value: function value(_value) {\n this.newValue = _value;\n if (!this.newValue || this.newValue.length === 0) {\n this.$refs.input.value = null;\n }\n !this.isValid && !this.dragDrop && this.checkHtml5Validity();\n }\n },\n methods: {\n\n /**\n * Listen change event on input type 'file',\n * emit 'input' event and validate\n */\n onFileChange: function onFileChange(event) {\n if (this.disabled || this.loading) return;\n if (this.dragDrop) {\n this.updateDragDropFocus(false);\n }\n var value = event.target.files || event.dataTransfer.files;\n if (value && value.length) {\n if (!this.multiple) {\n // only one element in case drag drop mode and isn't multiple\n if (this.dragDrop) {\n if (value.length === 1) {\n this.newValue = [];\n } else {\n return false;\n }\n } else {\n this.newValue = [];\n }\n } else {\n if (this.native) {\n this.newValue = [];\n }\n }\n for (var i = 0; i < value.length; i++) {\n var file = value[i];\n if (this.checkType(file)) {\n this.newValue.push(file);\n }\n }\n }\n this.$emit('input', this.newValue);\n !this.dragDrop && this.checkHtml5Validity();\n },\n\n\n /**\n * Listen drag-drop to update internal variable\n */\n updateDragDropFocus: function updateDragDropFocus(focus) {\n if (!this.disabled && !this.loading) {\n this.dragDropFocus = focus;\n }\n },\n\n\n /**\n * Check mime type of file\n */\n checkType: function checkType(file) {\n if (!this.accept) return true;\n var types = this.accept.split(',');\n if (types.length === 0) return true;\n var valid = false;\n for (var i = 0; i < types.length && !valid; i++) {\n var type = types[i].trim();\n if (type) {\n if (type.substring(0, 1) === '.') {\n // check extension\n var extIndex = file.name.lastIndexOf('.');\n if (extIndex >= 0 && file.name.substring(extIndex) === type) {\n valid = true;\n }\n } else {\n // check mime type\n if (file.type.match(type)) {\n valid = true;\n }\n }\n }\n }\n return valid;\n }\n }\n});\n\n/***/ }),\n/* 207 */\n/***/ (function(module, exports) {\n\nmodule.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;\n return _c('label', {\n staticClass: \"upload control\"\n }, [(!_vm.dragDrop) ? [_vm._t(\"default\")] : _c('div', {\n staticClass: \"upload-draggable\",\n class: [_vm.type, {\n 'is-loading': _vm.loading,\n 'is-disabled': _vm.disabled,\n 'is-hovered': _vm.dragDropFocus\n }],\n on: {\n \"dragover\": function($event) {\n $event.preventDefault();\n _vm.updateDragDropFocus(true)\n },\n \"dragleave\": function($event) {\n $event.preventDefault();\n _vm.updateDragDropFocus(false)\n },\n \"dragenter\": function($event) {\n $event.preventDefault();\n _vm.updateDragDropFocus(true)\n },\n \"drop\": function($event) {\n $event.preventDefault();\n _vm.onFileChange($event)\n }\n }\n }, [_vm._t(\"default\")], 2), _vm._v(\" \"), _c('input', _vm._b({\n ref: \"input\",\n attrs: {\n \"type\": \"file\",\n \"multiple\": _vm.multiple,\n \"accept\": _vm.accept,\n \"disabled\": _vm.disabled\n },\n on: {\n \"change\": _vm.onFileChange\n }\n }, 'input', _vm.$attrs, false))], 2)\n},staticRenderFns: []}\n\n/***/ })\n/******/ ]);\n});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/buefy/dist/buefy.js\n// module id = 20\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../css-loader/index.js!./buefy.css\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../vue-style-loader/lib/addStylesClient.js\")(\"6e4843aa\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/buefy/dist/buefy.css\n// module id = 21\n// module chunks = 0","exports = module.exports = require(\"../../css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"/*! Buefy v0.6.7 | MIT License | github.com/buefy/buefy */ \\n/*! bulma.io v0.7.1 | MIT License | github.com/jgthms/bulma */@keyframes spinAround{0%{transform:rotate(0deg)}to{transform:rotate(359deg)}}.b-checkbox.checkbox,.b-radio.radio,.breadcrumb,.button,.delete,.file,.icon,.is-unselectable,.modal-close,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.switch,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link:after,.select:not(.is-multiple):not(.is-loading):after{border:3px solid transparent;border-radius:2px;border-right:0;border-top:0;content:\\\" \\\";display:block;height:.625em;margin-top:-.4375em;pointer-events:none;position:absolute;top:50%;transform:rotate(-45deg);transform-origin:center;width:.625em}.block:not(:last-child),.box:not(:last-child),.breadcrumb:not(:last-child),.content:not(:last-child),.highlight:not(:last-child),.level:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.progress:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.tabs:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.delete,.modal-close{-moz-appearance:none;-webkit-appearance:none;background-color:hsla(0,0%,4%,.2);border:none;border-radius:290486px;cursor:pointer;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:none;position:relative;vertical-align:top;width:20px}.delete:after,.delete:before,.modal-close:after,.modal-close:before{background-color:#fff;content:\\\"\\\";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.delete:before,.modal-close:before{height:2px;width:50%}.delete:after,.modal-close:after{height:50%;width:2px}.delete:focus,.delete:hover,.modal-close:focus,.modal-close:hover{background-color:hsla(0,0%,4%,.3)}.delete:active,.modal-close:active{background-color:hsla(0,0%,4%,.4)}.is-small.delete,.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.delete,.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.delete,.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.b-table.is-loading:after,.button.is-loading:after,.control.is-loading:after,.loader,.loading-overlay .loading-icon:after,.select.is-loading:after,.upload .upload-draggable.is-loading:after{animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:\\\"\\\";display:block;height:1em;position:relative;width:1em}.dropdown .background,.hero-video,.image.is-1by1 img,.image.is-1by2 img,.image.is-1by3 img,.image.is-2by1 img,.image.is-2by3 img,.image.is-3by1 img,.image.is-3by2 img,.image.is-3by4 img,.image.is-3by5 img,.image.is-4by3 img,.image.is-4by5 img,.image.is-5by3 img,.image.is-5by4 img,.image.is-9by16 img,.image.is-16by9 img,.image.is-square img,.is-overlay,.loading-overlay,.loading-overlay .loading-background,.modal,.modal-background{bottom:0;left:0;position:absolute;right:0;top:0}.button,.file-cta,.file-name,.input,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.select select,.taginput .taginput-container.is-focusable,.textarea{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.25em;justify-content:flex-start;line-height:1.5;padding:calc(.375em - 1px) calc(.625em - 1px);position:relative;vertical-align:top}.button:active,.button:focus,.file-cta:active,.file-cta:focus,.file-name:active,.file-name:focus,.input:active,.input:focus,.is-active.button,.is-active.file-cta,.is-active.file-name,.is-active.input,.is-active.pagination-ellipsis,.is-active.pagination-link,.is-active.pagination-next,.is-active.pagination-previous,.is-active.textarea,.is-focused.button,.is-focused.file-cta,.is-focused.file-name,.is-focused.input,.is-focused.pagination-ellipsis,.is-focused.pagination-link,.is-focused.pagination-next,.is-focused.pagination-previous,.is-focused.textarea,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link:active,.pagination-link:focus,.pagination-next:active,.pagination-next:focus,.pagination-previous:active,.pagination-previous:focus,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.taginput .is-active.taginput-container.is-focusable,.taginput .is-focused.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:active,.taginput .taginput-container.is-focusable:focus,.textarea:active,.textarea:focus{outline:none}.select select[disabled],.taginput [disabled].taginput-container.is-focusable,[disabled].button,[disabled].file-cta,[disabled].file-name,[disabled].input,[disabled].pagination-ellipsis,[disabled].pagination-link,[disabled].pagination-next,[disabled].pagination-previous,[disabled].textarea{cursor:not-allowed}\\n\\n/*! minireset.css v0.0.3 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,:after,:before{box-sizing:inherit}audio,img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0;text-align:left}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,select,textarea{font-family:BlinkMacSystemFont,-apple-system,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1rem;font-weight:400;line-height:1.5}a{color:#3273dc;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{color:#ff3860;font-size:.875em;font-weight:400;padding:.25em .5em}code,hr{background-color:#f5f5f5}hr{border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{text-align:left;vertical-align:top}table th{color:#363636}.is-clearfix:after{clear:both;content:\\\" \\\";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-clipped{overflow:hidden!important}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media print,screen and (min-width:769px){.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1087px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1088px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1280px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1472px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}.has-text-justified{text-align:justify!important}.has-text-left{text-align:left!important}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media print,screen and (min-width:769px){.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1087px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1088px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1280px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1472px){.has-text-centered-fullhd{text-align:center!important}}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media print,screen and (min-width:769px){.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1087px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1088px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1280px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1472px){.has-text-justified-fullhd{text-align:justify!important}}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media print,screen and (min-width:769px){.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1087px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1088px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1280px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1472px){.has-text-left-fullhd{text-align:left!important}}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media print,screen and (min-width:769px){.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1087px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1088px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1280px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1472px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-background-white{background-color:#fff!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-background-black{background-color:#0a0a0a!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-background-light{background-color:#f5f5f5!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-background-dark{background-color:#363636!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-background-primary{background-color:#00d1b2!important}.has-text-link{color:#3273dc!important}a.has-text-link:focus,a.has-text-link:hover{color:#205bbc!important}.has-background-link{background-color:#3273dc!important}.has-text-info{color:#209cee!important}a.has-text-info:focus,a.has-text-info:hover{color:#0f81cc!important}.has-background-info{background-color:#209cee!important}.has-text-success{color:#23d160!important}a.has-text-success:focus,a.has-text-success:hover{color:#1ca64c!important}.has-background-success{background-color:#23d160!important}.has-text-warning{color:#ffdd57!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd324!important}.has-background-warning{background-color:#ffdd57!important}.has-text-danger{color:#ff3860!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ff0537!important}.has-background-danger{background-color:#ff3860!important}.has-text-black-bis{color:#121212!important}.has-background-black-bis{background-color:#121212!important}.has-text-black-ter{color:#242424!important}.has-background-black-ter{background-color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-background-grey-darker{background-color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-background-grey-dark{background-color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-background-grey{background-color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-background-grey-light{background-color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-background-grey-lighter{background-color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-background-white-ter{background-color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-background-white-bis{background-color:#fafafa!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media print,screen and (min-width:769px){.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1087px){.is-block-touch{display:block!important}}@media screen and (min-width:1088px){.is-block-desktop{display:block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1280px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1472px){.is-block-fullhd{display:block!important}}.is-flex{display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:flex!important}}@media print,screen and (min-width:769px){.is-flex-tablet{display:flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-flex-tablet-only{display:flex!important}}@media screen and (max-width:1087px){.is-flex-touch{display:flex!important}}@media screen and (min-width:1088px){.is-flex-desktop{display:flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-flex-desktop-only{display:flex!important}}@media screen and (min-width:1280px){.is-flex-widescreen{display:flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-flex-widescreen-only{display:flex!important}}@media screen and (min-width:1472px){.is-flex-fullhd{display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media print,screen and (min-width:769px){.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1087px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1088px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1280px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1472px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media print,screen and (min-width:769px){.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1087px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1088px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1280px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1472px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:inline-flex!important}}@media print,screen and (min-width:769px){.is-inline-flex-tablet{display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-flex-tablet-only{display:inline-flex!important}}@media screen and (max-width:1087px){.is-inline-flex-touch{display:inline-flex!important}}@media screen and (min-width:1088px){.is-inline-flex-desktop{display:inline-flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-flex-desktop-only{display:inline-flex!important}}@media screen and (min-width:1280px){.is-inline-flex-widescreen{display:inline-flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-flex-widescreen-only{display:inline-flex!important}}@media screen and (min-width:1472px){.is-inline-flex-fullhd{display:inline-flex!important}}.is-hidden{display:none!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media print,screen and (min-width:769px){.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1087px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1088px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1280px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1472px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media print,screen and (min-width:769px){.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1087px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1088px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1280px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1472px){.is-invisible-fullhd{visibility:hidden!important}}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.is-radiusless{border-radius:0!important}.is-shadowless{box-shadow:none!important}.box{background-color:#fff;border-radius:6px;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);color:#4a4a4a;display:block;padding:1.25rem}a.box:focus,a.box:hover{box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px #3273dc}a.box:active{box-shadow:inset 0 1px 2px hsla(0,0%,4%,.2),0 0 0 1px #3273dc}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding:calc(.375em - 1px) .75em;text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.375em - 1px);margin-right:.1875em}.button .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:calc(-.375em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.375em - 1px);margin-right:calc(-.375em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#3273dc;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled]{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled]{background-color:#fff;border-color:transparent;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled]{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading:after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled]{background-color:#0a0a0a;border-color:transparent;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading:after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:#363636}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:#363636}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:#363636}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:#363636}.button.is-light[disabled]{background-color:#f5f5f5;border-color:transparent;box-shadow:none}.button.is-light.is-inverted{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted:hover{background-color:#292929}.button.is-light.is-inverted[disabled]{background-color:#363636;border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading:after{border-color:transparent transparent #363636 #363636!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.button.is-light.is-outlined.is-loading:after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark{background-color:#363636;border-color:transparent;color:#f5f5f5}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#f5f5f5}.button.is-dark[disabled]{background-color:#363636;border-color:transparent;box-shadow:none}.button.is-dark.is-inverted{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted:hover{background-color:#e8e8e8}.button.is-dark.is-inverted[disabled]{background-color:#f5f5f5;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading:after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.button.is-dark.is-outlined.is-loading:after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined[disabled]{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled]{background-color:#00d1b2;border-color:transparent;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading:after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined[disabled]{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link{background-color:#3273dc;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#276cda;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#2366d1;border-color:transparent;color:#fff}.button.is-link[disabled]{background-color:#3273dc;border-color:transparent;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#3273dc}.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#3273dc}.button.is-link.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;color:#3273dc}.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#3273dc;border-color:#3273dc;color:#fff}.button.is-link.is-outlined.is-loading:after{border-color:transparent transparent #3273dc #3273dc!important}.button.is-link.is-outlined[disabled]{background-color:transparent;border-color:#3273dc;box-shadow:none;color:#3273dc}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info{background-color:#209cee;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#1496ed;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#118fe4;border-color:transparent;color:#fff}.button.is-info[disabled]{background-color:#209cee;border-color:transparent;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#209cee}.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#209cee}.button.is-info.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#209cee;color:#209cee}.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#209cee;border-color:#209cee;color:#fff}.button.is-info.is-outlined.is-loading:after{border-color:transparent transparent #209cee #209cee!important}.button.is-info.is-outlined[disabled]{background-color:transparent;border-color:#209cee;box-shadow:none;color:#209cee}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#209cee}.button.is-info.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success{background-color:#23d160;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#22c65b;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#20bc56;border-color:transparent;color:#fff}.button.is-success[disabled]{background-color:#23d160;border-color:transparent;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#23d160}.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#23d160}.button.is-success.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#23d160;color:#23d160}.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#23d160;border-color:#23d160;color:#fff}.button.is-success.is-outlined.is-loading:after{border-color:transparent transparent #23d160 #23d160!important}.button.is-success.is-outlined[disabled]{background-color:transparent;border-color:#23d160;box-shadow:none;color:#23d160}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#23d160}.button.is-success.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled]{background-color:#ffdd57;border-color:transparent;box-shadow:none}.button.is-warning.is-inverted{color:#ffdd57}.button.is-warning.is-inverted,.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled]{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading:after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading:after{border-color:transparent transparent #ffdd57 #ffdd57!important}.button.is-warning.is-outlined[disabled]{background-color:transparent;border-color:#ffdd57;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-danger{background-color:#ff3860;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#ff2b56;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ff1f4b;border-color:transparent;color:#fff}.button.is-danger[disabled]{background-color:#ff3860;border-color:transparent;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#ff3860}.button.is-danger.is-loading:after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;color:#ff3860}.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#ff3860;border-color:#ff3860;color:#fff}.button.is-danger.is-outlined.is-loading:after{border-color:transparent transparent #ff3860 #ff3860!important}.button.is-danger.is-outlined[disabled]{background-color:transparent;border-color:#ff3860;box-shadow:none;color:#ff3860}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-small{border-radius:2px;font-size:.75rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled]{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading:after{position:absolute;left:calc(50% - 0.5em);top:calc(50% - 0.5em);position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1}.buttons.is-centered{justify-content:center}.buttons.is-right{justify-content:flex-end}.container{margin:0 auto;position:relative}@media screen and (min-width:1088px){.container{max-width:960px;width:960px}.container.is-fluid{margin-left:64px;margin-right:64px;max-width:none;width:auto}}@media screen and (max-width:1279px){.container.is-widescreen{max-width:1152px;width:auto}}@media screen and (max-width:1471px){.container.is-fullhd{max-width:1344px;width:auto}}@media screen and (min-width:1280px){.container{max-width:1152px;width:1152px}}@media screen and (min-width:1472px){.container{max-width:1344px;width:1344px}}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style:decimal outside}.content ol,.content ul{margin-left:2em;margin-top:1em}.content ul{list-style:disc outside}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636;text-align:left}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content.is-small{font-size:.75rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.input,.taginput .taginput-container.is-focusable,.textarea{background-color:#fff;border-color:#dbdbdb;color:#363636;box-shadow:inset 0 1px 2px hsla(0,0%,4%,.1);max-width:100%;width:100%}.input::-moz-placeholder,.taginput .taginput-container.is-focusable::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.taginput .taginput-container.is-focusable::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.taginput .taginput-container.is-focusable:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.taginput .taginput-container.is-focusable:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input.is-hovered,.input:hover,.taginput .is-hovered.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:hover,.textarea.is-hovered,.textarea:hover{border-color:#b5b5b5}.input.is-active,.input.is-focused,.input:active,.input:focus,.taginput .is-active.taginput-container.is-focusable,.taginput .is-focused.taginput-container.is-focusable,.taginput .taginput-container.is-focusable:active,.taginput .taginput-container.is-focusable:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input[disabled],.taginput [disabled].taginput-container.is-focusable,.textarea[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.taginput [disabled].taginput-container.is-focusable::-moz-placeholder,.textarea[disabled]::-moz-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]::-webkit-input-placeholder,.taginput [disabled].taginput-container.is-focusable::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]:-moz-placeholder,.taginput [disabled].taginput-container.is-focusable:-moz-placeholder,.textarea[disabled]:-moz-placeholder{color:hsla(0,0%,48%,.3)}.input[disabled]:-ms-input-placeholder,.taginput [disabled].taginput-container.is-focusable:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder{color:hsla(0,0%,48%,.3)}.input[readonly],.taginput [readonly].taginput-container.is-focusable,.textarea[readonly]{box-shadow:none}.input.is-white,.taginput .is-white.taginput-container.is-focusable,.textarea.is-white{border-color:#fff}.input.is-white.is-active,.input.is-white.is-focused,.input.is-white:active,.input.is-white:focus,.taginput .is-white.is-active.taginput-container.is-focusable,.taginput .is-white.is-focused.taginput-container.is-focusable,.taginput .is-white.taginput-container.is-focusable:active,.taginput .is-white.taginput-container.is-focusable:focus,.textarea.is-white.is-active,.textarea.is-white.is-focused,.textarea.is-white:active,.textarea.is-white:focus{box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.input.is-black,.taginput .is-black.taginput-container.is-focusable,.textarea.is-black{border-color:#0a0a0a}.input.is-black.is-active,.input.is-black.is-focused,.input.is-black:active,.input.is-black:focus,.taginput .is-black.is-active.taginput-container.is-focusable,.taginput .is-black.is-focused.taginput-container.is-focusable,.taginput .is-black.taginput-container.is-focusable:active,.taginput .is-black.taginput-container.is-focusable:focus,.textarea.is-black.is-active,.textarea.is-black.is-focused,.textarea.is-black:active,.textarea.is-black:focus{box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.input.is-light,.taginput .is-light.taginput-container.is-focusable,.textarea.is-light{border-color:#f5f5f5}.input.is-light.is-active,.input.is-light.is-focused,.input.is-light:active,.input.is-light:focus,.taginput .is-light.is-active.taginput-container.is-focusable,.taginput .is-light.is-focused.taginput-container.is-focusable,.taginput .is-light.taginput-container.is-focusable:active,.taginput .is-light.taginput-container.is-focusable:focus,.textarea.is-light.is-active,.textarea.is-light.is-focused,.textarea.is-light:active,.textarea.is-light:focus{box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.input.is-dark,.taginput .is-dark.taginput-container.is-focusable,.textarea.is-dark{border-color:#363636}.input.is-dark.is-active,.input.is-dark.is-focused,.input.is-dark:active,.input.is-dark:focus,.taginput .is-dark.is-active.taginput-container.is-focusable,.taginput .is-dark.is-focused.taginput-container.is-focusable,.taginput .is-dark.taginput-container.is-focusable:active,.taginput .is-dark.taginput-container.is-focusable:focus,.textarea.is-dark.is-active,.textarea.is-dark.is-focused,.textarea.is-dark:active,.textarea.is-dark:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.input.is-primary,.taginput .is-primary.taginput-container.is-focusable,.textarea.is-primary{border-color:#00d1b2}.input.is-primary.is-active,.input.is-primary.is-focused,.input.is-primary:active,.input.is-primary:focus,.taginput .is-primary.is-active.taginput-container.is-focusable,.taginput .is-primary.is-focused.taginput-container.is-focusable,.taginput .is-primary.taginput-container.is-focusable:active,.taginput .is-primary.taginput-container.is-focusable:focus,.textarea.is-primary.is-active,.textarea.is-primary.is-focused,.textarea.is-primary:active,.textarea.is-primary:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.input.is-link,.taginput .is-link.taginput-container.is-focusable,.textarea.is-link{border-color:#3273dc}.input.is-link.is-active,.input.is-link.is-focused,.input.is-link:active,.input.is-link:focus,.taginput .is-link.is-active.taginput-container.is-focusable,.taginput .is-link.is-focused.taginput-container.is-focusable,.taginput .is-link.taginput-container.is-focusable:active,.taginput .is-link.taginput-container.is-focusable:focus,.textarea.is-link.is-active,.textarea.is-link.is-focused,.textarea.is-link:active,.textarea.is-link:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input.is-info,.taginput .is-info.taginput-container.is-focusable,.textarea.is-info{border-color:#209cee}.input.is-info.is-active,.input.is-info.is-focused,.input.is-info:active,.input.is-info:focus,.taginput .is-info.is-active.taginput-container.is-focusable,.taginput .is-info.is-focused.taginput-container.is-focusable,.taginput .is-info.taginput-container.is-focusable:active,.taginput .is-info.taginput-container.is-focusable:focus,.textarea.is-info.is-active,.textarea.is-info.is-focused,.textarea.is-info:active,.textarea.is-info:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.input.is-success,.taginput .is-success.taginput-container.is-focusable,.textarea.is-success{border-color:#23d160}.input.is-success.is-active,.input.is-success.is-focused,.input.is-success:active,.input.is-success:focus,.taginput .is-success.is-active.taginput-container.is-focusable,.taginput .is-success.is-focused.taginput-container.is-focusable,.taginput .is-success.taginput-container.is-focusable:active,.taginput .is-success.taginput-container.is-focusable:focus,.textarea.is-success.is-active,.textarea.is-success.is-focused,.textarea.is-success:active,.textarea.is-success:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.input.is-warning,.taginput .is-warning.taginput-container.is-focusable,.textarea.is-warning{border-color:#ffdd57}.input.is-warning.is-active,.input.is-warning.is-focused,.input.is-warning:active,.input.is-warning:focus,.taginput .is-warning.is-active.taginput-container.is-focusable,.taginput .is-warning.is-focused.taginput-container.is-focusable,.taginput .is-warning.taginput-container.is-focusable:active,.taginput .is-warning.taginput-container.is-focusable:focus,.textarea.is-warning.is-active,.textarea.is-warning.is-focused,.textarea.is-warning:active,.textarea.is-warning:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.input.is-danger,.taginput .is-danger.taginput-container.is-focusable,.textarea.is-danger{border-color:#ff3860}.input.is-danger.is-active,.input.is-danger.is-focused,.input.is-danger:active,.input.is-danger:focus,.taginput .is-danger.is-active.taginput-container.is-focusable,.taginput .is-danger.is-focused.taginput-container.is-focusable,.taginput .is-danger.taginput-container.is-focusable:active,.taginput .is-danger.taginput-container.is-focusable:focus,.textarea.is-danger.is-active,.textarea.is-danger.is-focused,.textarea.is-danger:active,.textarea.is-danger:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.input.is-small,.taginput .is-small.taginput-container.is-focusable,.textarea.is-small{border-radius:2px;font-size:.75rem}.input.is-medium,.taginput .is-medium.taginput-container.is-focusable,.textarea.is-medium{font-size:1.25rem}.input.is-large,.taginput .is-large.taginput-container.is-focusable,.textarea.is-large{font-size:1.5rem}.input.is-fullwidth,.taginput .is-fullwidth.taginput-container.is-focusable,.textarea.is-fullwidth{display:block;width:100%}.input.is-inline,.taginput .is-inline.taginput-container.is-focusable,.textarea.is-inline{display:inline;width:auto}.input.is-rounded,.taginput .is-rounded.taginput-container.is-focusable{border-radius:290486px;padding-left:1em;padding-right:1em}.input.is-static,.taginput .is-static.taginput-container.is-focusable{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:.625em;resize:vertical}.textarea:not([rows]){max-height:600px;min-height:120px}.textarea[rows]{height:auto}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox[disabled],.radio[disabled]{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.25em}.select:not(.is-multiple):not(.is-loading):after{border-color:#3273dc;right:1.125em;z-index:4}.select.is-rounded select{border-radius:290486px;padding-left:1em}.select select{background-color:#fff;border-color:#dbdbdb;color:#363636;cursor:pointer;display:block;font-size:1em;max-width:100%;outline:none}.select select::-moz-placeholder{color:rgba(54,54,54,.3)}.select select::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.select select:-moz-placeholder{color:rgba(54,54,54,.3)}.select select:-ms-input-placeholder{color:rgba(54,54,54,.3)}.select select.is-hovered,.select select:hover{border-color:#b5b5b5}.select select.is-active,.select select.is-focused,.select select:active,.select select:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select select[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.select select[disabled]::-moz-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]::-webkit-input-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]:-moz-placeholder{color:hsla(0,0%,48%,.3)}.select select[disabled]:-ms-input-placeholder{color:hsla(0,0%,48%,.3)}.select select::-ms-expand{display:none}.select select[disabled]:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:auto;padding:0}.select select[multiple] option{padding:.5em 1em}.select:not(.is-multiple):not(.is-loading):hover:after{border-color:#363636}.select.is-white:not(:hover):after,.select.is-white select{border-color:#fff}.select.is-white select.is-hovered,.select.is-white select:hover{border-color:#f2f2f2}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{box-shadow:0 0 0 .125em hsla(0,0%,100%,.25)}.select.is-black:not(:hover):after,.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-hovered,.select.is-black select:hover{border-color:#000}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{box-shadow:0 0 0 .125em hsla(0,0%,4%,.25)}.select.is-light:not(:hover):after,.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-hovered,.select.is-light select:hover{border-color:#e8e8e8}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{box-shadow:0 0 0 .125em hsla(0,0%,96%,.25)}.select.is-dark:not(:hover):after,.select.is-dark select{border-color:#363636}.select.is-dark select.is-hovered,.select.is-dark select:hover{border-color:#292929}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary:not(:hover):after,.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-hovered,.select.is-primary select:hover{border-color:#00b89c}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link:not(:hover):after,.select.is-link select{border-color:#3273dc}.select.is-link select.is-hovered,.select.is-link select:hover{border-color:#2366d1}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select.is-info:not(:hover):after,.select.is-info select{border-color:#209cee}.select.is-info select.is-hovered,.select.is-info select:hover{border-color:#118fe4}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.select.is-success:not(:hover):after,.select.is-success select{border-color:#23d160}.select.is-success select.is-hovered,.select.is-success select:hover{border-color:#20bc56}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.select.is-warning:not(:hover):after,.select.is-warning select{border-color:#ffdd57}.select.is-warning select.is-hovered,.select.is-warning select:hover{border-color:#ffd83d}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.select.is-danger:not(:hover):after,.select.is-danger select{border-color:#ff3860}.select.is-danger select.is-hovered,.select.is-danger select:hover{border-color:#ff1f4b}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled:after{border-color:#7a7a7a}.select.is-fullwidth,.select.is-fullwidth select{width:100%}.select.is-loading:after{margin-top:0;position:absolute;right:.625em;top:.625em;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,100%,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,4%,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:#363636}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:#363636}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em hsla(0,0%,96%,.25);color:#363636}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:#363636}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#f5f5f5}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(54,54,54,.25);color:#f5f5f5}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#f5f5f5}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#3273dc;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#276cda;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(50,115,220,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#2366d1;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#209cee;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#1496ed;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(32,156,238,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#118fe4;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#23d160;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#22c65b;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(35,209,96,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#20bc56;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,221,87,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#ff3860;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#ff2b56;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,56,96,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ff1f4b;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:.01em;left:0;outline:none;position:absolute;top:0;width:.01em}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:left;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#3273dc}.help.is-info{color:#209cee}.help.is-success{color:#23d160}.help.is-warning{color:#ffdd57}.help.is-danger{color:#ff3860}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select,.field.has-addons .control:not(:first-child):not(:last-child) .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:not(:first-child):not(:last-child) .taginput-container.is-focusable{border-radius:0}.field.has-addons .control:first-child .button,.field.has-addons .control:first-child .input,.field.has-addons .control:first-child .select select,.field.has-addons .control:first-child .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:first-child .taginput-container.is-focusable{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child .button,.field.has-addons .control:last-child .input,.field.has-addons .control:last-child .select select,.field.has-addons .control:last-child .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:last-child .taginput-container.is-focusable{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button.is-hovered,.field.has-addons .control .button:hover,.field.has-addons .control .input.is-hovered,.field.has-addons .control .input:hover,.field.has-addons .control .select select.is-hovered,.field.has-addons .control .select select:hover,.field.has-addons .control .taginput .is-hovered.taginput-container.is-focusable,.field.has-addons .control .taginput .taginput-container.is-focusable:hover,.taginput .field.has-addons .control .is-hovered.taginput-container.is-focusable,.taginput .field.has-addons .control .taginput-container.is-focusable:hover{z-index:2}.field.has-addons .control .button.is-active,.field.has-addons .control .button.is-focused,.field.has-addons .control .button:active,.field.has-addons .control .button:focus,.field.has-addons .control .input.is-active,.field.has-addons .control .input.is-focused,.field.has-addons .control .input:active,.field.has-addons .control .input:focus,.field.has-addons .control .select select.is-active,.field.has-addons .control .select select.is-focused,.field.has-addons .control .select select:active,.field.has-addons .control .select select:focus,.field.has-addons .control .taginput .is-active.taginput-container.is-focusable,.field.has-addons .control .taginput .is-focused.taginput-container.is-focusable,.field.has-addons .control .taginput .taginput-container.is-focusable:active,.field.has-addons .control .taginput .taginput-container.is-focusable:focus,.taginput .field.has-addons .control .is-active.taginput-container.is-focusable,.taginput .field.has-addons .control .is-focused.taginput-container.is-focusable,.taginput .field.has-addons .control .taginput-container.is-focusable:active,.taginput .field.has-addons .control .taginput-container.is-focusable:focus{z-index:3}.field.has-addons .control .button.is-active:hover,.field.has-addons .control .button.is-focused:hover,.field.has-addons .control .button:active:hover,.field.has-addons .control .button:focus:hover,.field.has-addons .control .input.is-active:hover,.field.has-addons .control .input.is-focused:hover,.field.has-addons .control .input:active:hover,.field.has-addons .control .input:focus:hover,.field.has-addons .control .select select.is-active:hover,.field.has-addons .control .select select.is-focused:hover,.field.has-addons .control .select select:active:hover,.field.has-addons .control .select select:focus:hover,.field.has-addons .control .taginput .is-active.taginput-container.is-focusable:hover,.field.has-addons .control .taginput .is-focused.taginput-container.is-focusable:hover,.field.has-addons .control .taginput .taginput-container.is-focusable:active:hover,.field.has-addons .control .taginput .taginput-container.is-focusable:focus:hover,.taginput .field.has-addons .control .is-active.taginput-container.is-focusable:hover,.taginput .field.has-addons .control .is-focused.taginput-container.is-focusable:hover,.taginput .field.has-addons .control .taginput-container.is-focusable:active:hover,.taginput .field.has-addons .control .taginput-container.is-focusable:focus:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media print,screen and (min-width:769px){.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media print,screen and (min-width:769px){.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media print,screen and (min-width:769px){.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{font-size:1rem;position:relative;text-align:left}.control.has-icon .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icon .input:focus+.icon,.control.has-icon .taginput .taginput-container.is-focusable:focus+.icon,.taginput .control.has-icon .taginput-container.is-focusable:focus+.icon{color:#7a7a7a}.control.has-icon .input.is-small+.icon,.control.has-icon .taginput .is-small.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-small.taginput-container.is-focusable+.icon{font-size:.75rem}.control.has-icon .input.is-medium+.icon,.control.has-icon .taginput .is-medium.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-medium.taginput-container.is-focusable+.icon{font-size:1.25rem}.control.has-icon .input.is-large+.icon,.control.has-icon .taginput .is-large.taginput-container.is-focusable+.icon,.taginput .control.has-icon .is-large.taginput-container.is-focusable+.icon{font-size:1.5rem}.control.has-icon:not(.has-icon-right) .icon{left:0}.control.has-icon:not(.has-icon-right) .input,.control.has-icon:not(.has-icon-right) .taginput .taginput-container.is-focusable,.taginput .control.has-icon:not(.has-icon-right) .taginput-container.is-focusable{padding-left:2.25em}.control.has-icon.has-icon-right .icon{right:0}.control.has-icon.has-icon-right .input,.control.has-icon.has-icon-right .taginput .taginput-container.is-focusable,.taginput .control.has-icon.has-icon-right .taginput-container.is-focusable{padding-right:2.25em}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-left .taginput .taginput-container.is-focusable:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon,.control.has-icons-right .taginput .taginput-container.is-focusable:focus~.icon,.taginput .control.has-icons-left .taginput-container.is-focusable:focus~.icon,.taginput .control.has-icons-right .taginput-container.is-focusable:focus~.icon{color:#7a7a7a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-left .taginput .is-small.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon,.control.has-icons-right .taginput .is-small.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-small.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-small.taginput-container.is-focusable~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-left .taginput .is-medium.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon,.control.has-icons-right .taginput .is-medium.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-medium.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-medium.taginput-container.is-focusable~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-left .taginput .is-large.taginput-container.is-focusable~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon,.control.has-icons-right .taginput .is-large.taginput-container.is-focusable~.icon,.taginput .control.has-icons-left .is-large.taginput-container.is-focusable~.icon,.taginput .control.has-icons-right .is-large.taginput-container.is-focusable~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select,.control.has-icons-left .taginput .taginput-container.is-focusable,.taginput .control.has-icons-left .taginput-container.is-focusable{padding-left:2.25em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select,.control.has-icons-right .taginput .taginput-container.is-focusable,.taginput .control.has-icons-right .taginput-container.is-focusable{padding-right:2.25em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading:after{position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:290486px}.image.is-1by1 img,.image.is-1by2 img,.image.is-1by3 img,.image.is-2by1 img,.image.is-2by3 img,.image.is-3by1 img,.image.is-3by2 img,.image.is-3by4 img,.image.is-3by5 img,.image.is-4by3 img,.image.is-4by5 img,.image.is-5by3 img,.image.is-5by4 img,.image.is-9by16 img,.image.is-16by9 img,.image.is-square img{height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;padding:1.25rem 2.5rem 1.25rem 1.5rem;position:relative}.notification a:not(.button){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:transparent}.notification>.delete{position:absolute;right:.5rem;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:#363636}.notification.is-dark{background-color:#363636;color:#f5f5f5}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-link{background-color:#3273dc;color:#fff}.notification.is-info{background-color:#209cee;color:#fff}.notification.is-success{background-color:#23d160;color:#fff}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.notification.is-danger{background-color:#ff3860;color:#fff}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#dbdbdb}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-link::-webkit-progress-value{background-color:#3273dc}.progress.is-link::-moz-progress-bar{background-color:#3273dc}.progress.is-link::-ms-fill{background-color:#3273dc}.progress.is-info::-webkit-progress-value{background-color:#209cee}.progress.is-info::-moz-progress-bar{background-color:#209cee}.progress.is-info::-ms-fill{background-color:#209cee}.progress.is-success::-webkit-progress-value{background-color:#23d160}.progress.is-success::-moz-progress-bar{background-color:#23d160}.progress.is-success::-ms-fill{background-color:#23d160}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-danger::-webkit-progress-value{background-color:#ff3860}.progress.is-danger::-moz-progress-bar{background-color:#ff3860}.progress.is-danger::-ms-fill{background-color:#ff3860}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#f5f5f5}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#3273dc;border-color:#3273dc;color:#fff}.table td.is-info,.table th.is-info{background-color:#209cee;border-color:#209cee;color:#fff}.table td.is-success,.table th.is-success{background-color:#23d160;border-color:#23d160;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#ff3860;border-color:#ff3860;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table th{color:#363636;text-align:left}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(2n){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.tags.has-addons .tag:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:#363636}.tag:not(body).is-dark{background-color:#363636;color:#f5f5f5}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-link{background-color:#3273dc;color:#fff}.tag:not(body).is-info{background-color:#209cee;color:#fff}.tag:not(body).is-success{background-color:#23d160;color:#fff}.tag:not(body).is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.tag:not(body).is-danger{background-color:#ff3860;color:#fff}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete:after,.tag:not(body).is-delete:before{background-color:currentColor;content:\\\"\\\";display:block;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%) rotate(45deg);transform-origin:center center}.tag:not(body).is-delete:before{height:1px;width:50%}.tag:not(body).is-delete:after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:290486px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.subtitle sup,.title sub,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title+.highlight{margin-top:-.75rem}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-weight:400;max-width:100%;overflow:hidden;padding:0}.highlight pre{overflow:auto;max-width:100%}.number{align-items:center;background-color:#f5f5f5;border-radius:290486px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#3273dc;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li:before{color:#b5b5b5;content:\\\"/\\\"}.breadcrumb ol,.breadcrumb ul{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li:before{content:\\\"\\\\2192\\\"}.breadcrumb.has-bullet-separator li+li:before{content:\\\"\\\\2022\\\"}.breadcrumb.has-dot-separator li+li:before{content:\\\"\\\\B7\\\"}.breadcrumb.has-succeeds-separator li+li:before{content:\\\"\\\\227B\\\"}.card{background-color:#fff;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);color:#4a4a4a;max-width:100%;position:relative}.card-header{background-color:none;align-items:stretch;box-shadow:0 1px 2px hsla(0,0%,4%,.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem}.card-header-icon,.card-header-title.is-centered{justify-content:center}.card-header-icon{align-items:center;cursor:pointer;display:flex;padding:.75rem}.card-image{display:block;position:relative}.card-content{background-color:none;padding:1.5rem}.card-footer{background-color:none;border-top:1px solid #dbdbdb;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #dbdbdb}.card .media:not(:last-child){margin-bottom:.75rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:0;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item,.dropdown .dropdown-menu .has-link a{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}.dropdown .dropdown-menu .has-link a,a.dropdown-item{padding-right:3rem;white-space:nowrap}.dropdown .dropdown-menu .has-link a:hover,a.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}.dropdown .dropdown-menu .has-link a.is-active,a.dropdown-item.is-active{background-color:#3273dc;color:#fff}.dropdown-divider{background-color:#dbdbdb;border:none;display:block;height:1px;margin:.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile,.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item{margin-right:.75rem}.level.is-mobile .level-item:not(:last-child){margin-bottom:0}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media print,screen and (min-width:769px){.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media print,screen and (min-width:769px){.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media print,screen and (min-width:769px){.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media print,screen and (min-width:769px){.level-right{display:flex}}.media{align-items:flex-start;display:flex;text-align:left}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid hsla(0,0%,86%,.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid hsla(0,0%,86%,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:left}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#3273dc;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff;color:#4d4d4d}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a;color:#090909}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:#363636}.message.is-light .message-body{border-color:#f5f5f5;color:#505050}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#f5f5f5}.message.is-dark .message-body{border-color:#363636;color:#2a2a2a}.message.is-primary{background-color:#f5fffd}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#021310}.message.is-link{background-color:#f6f9fe}.message.is-link .message-header{background-color:#3273dc;color:#fff}.message.is-link .message-body{border-color:#3273dc;color:#22509a}.message.is-info{background-color:#f6fbfe}.message.is-info .message-header{background-color:#209cee;color:#fff}.message.is-info .message-body{border-color:#209cee;color:#12537e}.message.is-success{background-color:#f6fef9}.message.is-success .message-header{background-color:#23d160;color:#fff}.message.is-success .message-body{border-color:#23d160;color:#0e301a}.message.is-warning{background-color:#fffdf5}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#3b3108}.message.is-danger{background-color:#fff5f7}.message.is-danger .message-header{background-color:#ff3860;color:#fff}.message.is-danger .message-body{border-color:#ff3860;color:#cd0930}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{align-items:center;display:none;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:hsla(0,0%,4%,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media print,screen and (min-width:769px){.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:none;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden}.modal-card-foot,.modal-card-head{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:10px}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link:after{border-color:#0a0a0a}@media screen and (min-width:1088px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link:after,.navbar.is-white .navbar-start .navbar-link:after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link:after,.navbar.is-black .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:#363636}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:#363636}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-brand .navbar-link:after{border-color:#363636}@media screen and (min-width:1088px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:#363636}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-end .navbar-link:after,.navbar.is-light .navbar-start .navbar-link:after{border-color:#363636}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#363636}}.navbar.is-dark{background-color:#363636;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link:after{border-color:#f5f5f5}@media screen and (min-width:1088px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link:after,.navbar.is-dark .navbar-start .navbar-link:after{border-color:#f5f5f5}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#f5f5f5}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link:after,.navbar.is-primary .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#3273dc;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-end .navbar-link:after,.navbar.is-link .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#3273dc;color:#fff}}.navbar.is-info{background-color:#209cee;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-end .navbar-link:after,.navbar.is-info .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#209cee;color:#fff}}.navbar.is-success{background-color:#23d160;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-end .navbar-link:after,.navbar.is-success .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#23d160;color:#fff}}.navbar.is-warning{background-color:#ffdd57}.navbar.is-warning,.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link:after{border-color:rgba(0,0,0,.7)}@media screen and (min-width:1088px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link:after,.navbar.is-warning .navbar-start .navbar-link:after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#ff3860;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-brand .navbar-link:after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-end .navbar-link:after,.navbar.is-danger .navbar-start .navbar-link:after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#ff3860;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}body.has-navbar-fixed-top,html.has-navbar-fixed-top{padding-top:3.25rem}body.has-navbar-fixed-bottom,html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;transform-origin:center;transition-duration:86ms;transition-property:background-color,opacity,transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:first-child{top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:first-child{transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem .75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-.25rem;margin-right:-.25rem}.navbar-link,a.navbar-item{cursor:pointer}.navbar-link.is-active,.navbar-link:hover,a.navbar-item.is-active,a.navbar-item:hover{background-color:#fafafa;color:#3273dc}.navbar-item{display:block;flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab.is-active,.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#3273dc}.navbar-item.is-tab.is-active{border-bottom-style:solid;border-bottom-width:3px;color:#3273dc;padding-bottom:calc(.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link{padding-right:2.5em}.navbar-link:after{border-color:#3273dc;margin-top:-.375em;right:1.125em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:.5rem 0}@media screen and (max-width:1087px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link:after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px hsla(0,0%,4%,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px hsla(0,0%,4%,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top-touch .navbar-menu,.navbar.is-fixed-top .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}body.has-navbar-fixed-top-touch,html.has-navbar-fixed-top-touch{padding-top:3.25rem}body.has-navbar-fixed-bottom-touch,html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1088px){.navbar,.navbar-end,.navbar-menu,.navbar-start{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-end,.navbar.is-spaced .navbar-start{align-items:center}.navbar.is-spaced .navbar-link,.navbar.is-spaced a.navbar-item{border-radius:4px}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link,.navbar.is-transparent .navbar-link.is-active,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:hover{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item{display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link:after{transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px hsla(0,0%,4%,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown{opacity:1;pointer-events:auto;transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px hsla(0,0%,4%,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-dropdown{border-radius:6px;border-top:none;box-shadow:0 8px 8px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + -4px);transform:translateY(-5px);transition-duration:86ms;transition-property:opacity,transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-1rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-1rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px hsla(0,0%,4%,.1)}.navbar.is-fixed-top-desktop{top:0}body.has-navbar-fixed-top-desktop,html.has-navbar-fixed-top-desktop{padding-top:3.25rem}body.has-navbar-fixed-bottom-desktop,html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}body.has-spaced-navbar-fixed-top,html.has-spaced-navbar-fixed-top{padding-top:5.25rem}body.has-spaced-navbar-fixed-bottom,html.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}.navbar-link.is-active,a.navbar-item.is-active{color:#0a0a0a}.navbar-link.is-active:not(:hover),a.navbar-item.is-active:not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#fafafa}}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:290486px}.pagination.is-rounded .pagination-link{border-radius:290486px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{font-size:1em;padding-left:.5em;padding-right:.5em;justify-content:center;margin:.25rem;text-align:center}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.25em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#3273dc}.pagination-link:active,.pagination-next:active,.pagination-previous:active{box-shadow:inset 0 1px 2px hsla(0,0%,4%,.2)}.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#3273dc;border-color:#3273dc;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}@media screen and (max-width:768px){.pagination{flex-wrap:wrap}.pagination-list li,.pagination-next,.pagination-previous{flex-grow:1;flex-shrink:1}}@media print,screen and (min-width:769px){.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel-block,.panel-heading,.panel-tabs{border-bottom:1px solid #dbdbdb;border-left:1px solid #dbdbdb;border-right:1px solid #dbdbdb}.panel-block:first-child,.panel-heading:first-child,.panel-tabs:first-child{border-top:1px solid #dbdbdb}.panel-heading{background-color:#f5f5f5;border-radius:4px 4px 0 0;color:#363636;font-size:1.25em;font-weight:300;line-height:1.25;padding:.5em .75em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#3273dc}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#3273dc;color:#363636}.panel-block.is-active .panel-icon{color:#3273dc}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#3273dc;color:#3273dc}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-center,.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border:1px solid #dbdbdb;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-radius:4px 0 0 4px}.tabs.is-toggle li:last-child a{border-radius:0 4px 4px 0}.tabs.is-toggle li.is-active a{background-color:#3273dc;border-color:#3273dc;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:290486px;border-top-left-radius:290486px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:290486px;border-top-right-radius:290486px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{flex:none}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media print,screen and (min-width:769px){.column.is-narrow,.column.is-narrow-tablet{flex:none}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1087px){.column.is-narrow-touch{flex:none}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1088px){.column.is-narrow-desktop{flex:none}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1280px){.column.is-narrow-widescreen{flex:none}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1472px){.column.is-narrow-fullhd{flex:none}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:0.75rem}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media print,screen and (min-width:769px){.columns:not(.is-desktop){display:flex}}@media screen and (min-width:1088px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable .column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}.columns.is-variable.is-1{--columnGap:0.25rem}.columns.is-variable.is-2{--columnGap:0.5rem}.columns.is-variable.is-3{--columnGap:0.75rem}.columns.is-variable.is-4{--columnGap:1rem}.columns.is-variable.is-5{--columnGap:1.25rem}.columns.is-variable.is-6{--columnGap:1.5rem}.columns.is-variable.is-7{--columnGap:1.75rem}.columns.is-variable.is-8{--columnGap:2rem}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media print,screen and (min-width:769px){.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:none}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:hsla(0,0%,4%,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1087px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:hsla(0,0%,4%,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover,.hero.is-white .tabs li.is-active a{opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6,#fff 71%,#fff)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6,#fff 71%,#fff)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover,.hero.is-black .tabs li.is-active a{opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000,#0a0a0a 71%,#181616)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000,#0a0a0a 71%,#181616)}}.hero.is-light{background-color:#f5f5f5;color:#363636}.hero.is-light a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-light strong{color:inherit}.hero.is-light .title{color:#363636}.hero.is-light .subtitle{color:rgba(54,54,54,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:#363636}@media screen and (max-width:1087px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(54,54,54,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.hero.is-light .tabs a{color:#363636;opacity:.9}.hero.is-light .tabs a:hover,.hero.is-light .tabs li.is-active a{opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:#363636}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9,#f5f5f5 71%,#fff)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9,#f5f5f5 71%,#fff)}}.hero.is-dark{background-color:#363636;color:#f5f5f5}.hero.is-dark a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#f5f5f5}.hero.is-dark .subtitle{color:hsla(0,0%,96%,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#f5f5f5}@media screen and (max-width:1087px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:hsla(0,0%,96%,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.hero.is-dark .tabs a{color:#f5f5f5;opacity:.9}.hero.is-dark .tabs a:hover,.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#f5f5f5}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a,#363636 71%,#46403f)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a,#363636 71%,#46403f)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover,.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c,#00d1b2 71%,#00e7eb)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c,#00d1b2 71%,#00e7eb)}}.hero.is-link{background-color:#3273dc;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-link .navbar-menu{background-color:#3273dc}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#2366d1;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover,.hero.is-link .tabs li.is-active a{opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3273dc}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#1577c6,#3273dc 71%,#4366e5)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1577c6,#3273dc 71%,#4366e5)}}.hero.is-info{background-color:#209cee;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-info .navbar-menu{background-color:#209cee}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#118fe4;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover,.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#209cee}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#04a6d7,#209cee 71%,#3287f5)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#04a6d7,#209cee 71%,#3287f5)}}.hero.is-success{background-color:#23d160;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-success .navbar-menu{background-color:#23d160}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#20bc56;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover,.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#23d160}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#12af2f,#23d160 71%,#2ce28a)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#12af2f,#23d160 71%,#2ce28a)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1087px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover,.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffaf24,#ffdd57 71%,#fffa70)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffaf24,#ffdd57 71%,#fffa70)}}.hero.is-danger{background-color:#ff3860;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.dropdown .dropdown-menu .has-link a):not(.tag),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:hsla(0,0%,100%,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-danger .navbar-menu{background-color:#ff3860}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:hsla(0,0%,100%,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover,.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:hsla(0,0%,4%,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#ff3860}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#ff0561,#ff3860 71%,#ff5257)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ff0561,#ff3860 71%,#ff5257)}}.hero.is-small .hero-body{padding-bottom:1.5rem;padding-top:1.5rem}@media print,screen and (min-width:769px){.hero.is-medium .hero-body{padding-bottom:9rem;padding-top:9rem}}@media print,screen and (min-width:769px){.hero.is-large .hero-body{padding-bottom:18rem;padding-top:18rem}}.hero.is-fullheight .hero-body,.hero.is-halfheight .hero-body{align-items:center;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-halfheight .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media print,screen and (min-width:769px){.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0}.hero-body,.section{padding:3rem 1.5rem}@media screen and (min-width:1088px){.section.is-medium{padding:9rem 1.5rem}.section.is-large{padding:18rem 1.5rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem}.is-noscroll{position:fixed;overflow-y:hidden;width:100%;bottom:0}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.fadeOut{animation-name:fadeOut}@keyframes fadeOutDown{0%{opacity:1}to{opacity:0;transform:translate3d(0,100%,0)}}.fadeOutDown{animation-name:fadeOutDown}@keyframes fadeOutUp{0%{opacity:1}to{opacity:0;transform:translate3d(0,-100%,0)}}.fadeOutUp{animation-name:fadeOutUp}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.fadeIn{animation-name:fadeIn}@keyframes fadeInDown{0%{opacity:0;transform:translate3d(0,-100%,0)}to{opacity:1;transform:none}}.fadeInDown{animation-name:fadeInDown}@keyframes fadeInUp{0%{opacity:0;transform:translate3d(0,100%,0)}to{opacity:1;transform:none}}.fadeInUp{animation-name:fadeInUp}.fade-enter-active,.fade-leave-active{transition:opacity .15s ease-out}.fade-enter,.fade-leave-to{opacity:0}.zoom-in-enter-active,.zoom-in-leave-active{transition:opacity .15s ease-out}.zoom-in-enter-active .animation-content,.zoom-in-leave-active .animation-content{transition:transform .15s ease-out}.zoom-in-enter,.zoom-in-leave-active{opacity:0}.zoom-in-enter .animation-content,.zoom-in-leave-active .animation-content{transform:scale(.95)}.zoom-out-enter-active,.zoom-out-leave-active{transition:opacity .15s ease-out}.zoom-out-enter-active .animation-content,.zoom-out-leave-active .animation-content{transition:transform .15s ease-out}.zoom-out-enter,.zoom-out-leave-active{opacity:0}.zoom-out-enter .animation-content,.zoom-out-leave-active .animation-content{transform:scale(1.05)}.slide-next-enter-active,.slide-next-leave-active,.slide-prev-enter-active,.slide-prev-leave-active{transition:transform .25s cubic-bezier(.785,.135,.15,.86)}.slide-next-enter,.slide-prev-leave-to{transform:translate3d(-100%,0,0);position:absolute;width:100%}.slide-next-leave-to,.slide-prev-enter{transform:translate3d(100%,0,0);position:absolute;width:100%}.autocomplete{position:relative}.autocomplete .dropdown-menu{display:block;min-width:100%;max-width:100%}.autocomplete .dropdown-menu.is-opened-top{top:auto;bottom:100%}.autocomplete .dropdown-content{overflow:auto;max-height:200px}.autocomplete .dropdown-item,.autocomplete .dropdown .dropdown-menu .has-link a,.dropdown .dropdown-menu .has-link .autocomplete a{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.autocomplete .dropdown-item.is-hovered,.autocomplete .dropdown .dropdown-menu .has-link a.is-hovered,.dropdown .dropdown-menu .has-link .autocomplete a.is-hovered{background:#f5f5f5;color:#0a0a0a}.autocomplete .dropdown-item.is-disabled,.autocomplete .dropdown .dropdown-menu .has-link a.is-disabled,.dropdown .dropdown-menu .has-link .autocomplete a.is-disabled{opacity:.5;cursor:not-allowed}.autocomplete.is-small{border-radius:2px;font-size:.75rem}.autocomplete.is-medium{font-size:1.25rem}.autocomplete.is-large{font-size:1.5rem}.b-checkbox.checkbox{outline:none;display:inline-flex;align-items:center}.b-checkbox.checkbox+.checkbox{margin-left:.5em}.b-checkbox.checkbox input[type=checkbox]{position:absolute;left:0;opacity:0;outline:none;z-index:-1}.b-checkbox.checkbox input[type=checkbox]+.check{width:1.25em;height:1.25em;flex-shrink:0;border-radius:4px;border:2px solid #7a7a7a;transition:background .15s ease-out}.b-checkbox.checkbox input[type=checkbox]:checked+.check{background:#00d1b2 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#00d1b2}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-white{background:#fff url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%230a0a0a'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#fff}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-black{background:#0a0a0a url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#0a0a0a}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-light{background:#f5f5f5 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23363636'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#f5f5f5}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-dark{background:#363636 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23f5f5f5'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#363636}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-primary{background:#00d1b2 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#00d1b2}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-link{background:#3273dc url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#3273dc}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-info{background:#209cee url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#209cee}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-success{background:#23d160 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#23d160}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-warning{background:#ffdd57 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='rgba(0,0,0,.7)'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#ffdd57}.b-checkbox.checkbox input[type=checkbox]:checked+.check.is-danger{background:#ff3860 url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%23fff'/%3E%3C/svg%3E\\\") no-repeat 50%;border-color:#ff3860}.b-checkbox.checkbox .control-label{padding-left:.5em}.b-checkbox.checkbox[disabled]{opacity:.5}.b-checkbox.checkbox:hover input[type=checkbox]+.check{border-color:#00d1b2}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-white{border-color:#fff}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-black{border-color:#0a0a0a}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-light{border-color:#f5f5f5}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-dark{border-color:#363636}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-primary{border-color:#00d1b2}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-link{border-color:#3273dc}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-info{border-color:#209cee}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-success{border-color:#23d160}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-warning{border-color:#ffdd57}.b-checkbox.checkbox:hover input[type=checkbox]+.check.is-danger{border-color:#ff3860}.b-checkbox.checkbox:focus input[type=checkbox]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.b-checkbox.checkbox:focus input[type=checkbox]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.b-checkbox.checkbox.is-small{border-radius:2px;font-size:.75rem}.b-checkbox.checkbox.is-medium{font-size:1.25rem}.b-checkbox.checkbox.is-large{font-size:1.5rem}.collapse .collapse-trigger{display:inline;cursor:pointer}.collapse .collapse-content{display:inherit}.datepicker{font-size:.875rem}.datepicker .dropdown,.datepicker .dropdown-trigger{width:100%}.datepicker .dropdown-item,.datepicker .dropdown .dropdown-menu .has-link a,.dropdown .dropdown-menu .has-link .datepicker a{font-size:inherit}.datepicker .datepicker-header{padding-bottom:.875rem;margin-bottom:.875rem;border-bottom:1px solid #dbdbdb}.datepicker .datepicker-footer{padding-top:.875rem;border-top:1px solid #dbdbdb}.datepicker .datepicker-table{display:table;margin:0 auto .875rem}.datepicker .datepicker-table .datepicker-cell{text-align:center;vertical-align:middle;display:table-cell;border-radius:4px;padding:.5rem .75rem}.datepicker .datepicker-table .datepicker-header{display:table-header-group}.datepicker .datepicker-table .datepicker-header .datepicker-cell{color:#7a7a7a;font-weight:600}.datepicker .datepicker-table .datepicker-body{display:table-row-group}.datepicker .datepicker-table .datepicker-body .datepicker-row{display:table-row}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-unselectable{color:#b5b5b5}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-today{border:1px solid rgba(0,209,178,.5)}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable{color:#4a4a4a}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable:focus:not(.is-selected),.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selectable:hover:not(.is-selected){background-color:#f5f5f5;color:#0a0a0a;cursor:pointer}.datepicker .datepicker-table .datepicker-body .datepicker-row .datepicker-cell.is-selected{background-color:#00d1b2;color:#fff}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell{padding:.3rem .75rem .75rem}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event{position:relative}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events{bottom:.425rem;display:flex;justify-content:center;left:0;padding:0 .35rem;position:absolute;width:100%}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-white{background-color:#fff}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-black{background-color:#0a0a0a}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-light{background-color:#f5f5f5}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-dark{background-color:#363636}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-primary{background-color:#00d1b2}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-link{background-color:#3273dc}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-info{background-color:#209cee}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-success{background-color:#23d160}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-warning{background-color:#ffdd57}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event .events .event.is-danger{background-color:#ff3860}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event.dots .event{border-radius:50%;height:.35em;margin:0 .1em;width:.35em}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.has-event.bars .event{height:.25em;width:100%}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.is-selected{overflow:hidden}.datepicker .datepicker-table .datepicker-body.has-events .datepicker-cell.is-selected .events .event.is-primary{background-color:#1fffdd}.datepicker.is-small{border-radius:2px;font-size:.75rem}.datepicker.is-medium{font-size:1.25rem}.datepicker.is-large{font-size:1.5rem}@media screen and (min-width:769px) and (max-width:1087px){.datepicker .datepicker-table .datepicker-cell{padding:.75rem 1rem}}@media screen and (max-width:768px){.datepicker .datepicker-table .datepicker-cell{padding:.25rem .5rem}}.dialog .modal-card{max-width:460px;width:auto}.dialog .modal-card .modal-card-head{font-size:1.25rem;font-weight:600}.dialog .modal-card .modal-card-body .field{margin-top:16px}.dialog .modal-card .modal-card-body.is-titleless{border-top-left-radius:6px;border-top-right-radius:6px}.dialog .modal-card .modal-card-foot{justify-content:flex-end}.dialog .modal-card .modal-card-foot .button{display:inline;min-width:5em;font-weight:600}@media print,screen and (min-width:769px){.dialog .modal-card{min-width:320px}}.dialog.is-small .button,.dialog.is-small .input,.dialog.is-small .modal-card,.dialog.is-small .taginput .taginput-container.is-focusable,.taginput .dialog.is-small .taginput-container.is-focusable{border-radius:2px;font-size:.75rem}.dialog.is-medium .button,.dialog.is-medium .input,.dialog.is-medium .modal-card,.dialog.is-medium .taginput .taginput-container.is-focusable,.taginput .dialog.is-medium .taginput-container.is-focusable{font-size:1.25rem}.dialog.is-large .button,.dialog.is-large .input,.dialog.is-large .modal-card,.dialog.is-large .taginput .taginput-container.is-focusable,.taginput .dialog.is-large .taginput-container.is-focusable{font-size:1.5rem}.dropdown+.dropdown{margin-left:.5em}.dropdown .background{position:fixed;background-color:hsla(0,0%,4%,.86);z-index:10;cursor:pointer}@media screen and (min-width:1088px){.dropdown .background{display:none}}.dropdown .dropdown-menu .dropdown-item.is-disabled,.dropdown .dropdown-menu .has-link a.is-disabled{cursor:not-allowed}.dropdown .dropdown-menu .dropdown-item.is-disabled:hover,.dropdown .dropdown-menu .has-link a.is-disabled:hover{background:inherit;color:inherit}.dropdown .dropdown-menu .has-link a{padding-right:3rem;white-space:nowrap}.dropdown:not(.is-disabled) .dropdown-menu .dropdown-item.is-disabled,.dropdown:not(.is-disabled) .dropdown-menu .has-link a.is-disabled{opacity:.5}.dropdown .navbar-item{height:100%}.dropdown.is-disabled{opacity:.5;cursor:not-allowed}.dropdown.is-disabled .dropdown-trigger{pointer-events:none}.dropdown.is-inline .dropdown-menu{position:static;display:inline-block;padding:0}.dropdown.is-top-right .dropdown-menu{top:auto;bottom:100%}.dropdown.is-top-left .dropdown-menu{top:auto;bottom:100%;right:0;left:auto}.dropdown.is-bottom-left .dropdown-menu{right:0;left:auto}@media screen and (max-width:1087px){.dropdown.is-mobile-modal .dropdown-menu{position:fixed;width:calc(100vw - 40px);max-width:460px;max-height:calc(100vh - 120px);top:25%!important;left:50%!important;bottom:auto!important;right:auto!important;transform:translate3d(-50%,-25%,0);white-space:normal;overflow-y:auto}.dropdown.is-mobile-modal .dropdown-menu .dropdown-item,.dropdown.is-mobile-modal .dropdown-menu .has-link a{padding:1rem 1.5rem}}.label{font-weight:600}.field.is-grouped .field{flex-shrink:0}.field.is-grouped .field+.field{margin-left:.75rem}.field.is-grouped .field.is-expanded{flex-grow:1;flex-shrink:1}.field.has-addons .control:first-child .control .button,.field.has-addons .control:first-child .control .input,.field.has-addons .control:first-child .control .select select,.field.has-addons .control:first-child .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:first-child .control .taginput-container.is-focusable{border-bottom-left-radius:4px;border-top-left-radius:4px}.field.has-addons .control:last-child .control .button,.field.has-addons .control:last-child .control .input,.field.has-addons .control:last-child .control .select select,.field.has-addons .control:last-child .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control:last-child .control .taginput-container.is-focusable{border-bottom-right-radius:4px;border-top-right-radius:4px}.field.has-addons .control .control .button,.field.has-addons .control .control .input,.field.has-addons .control .control .select select,.field.has-addons .control .control .taginput .taginput-container.is-focusable,.taginput .field.has-addons .control .control .taginput-container.is-focusable{border-radius:0}.control .help.counter{float:right;margin-left:.5em}.control .icon.is-clickable{pointer-events:auto;cursor:pointer}.icon{cursor:inherit}.icon svg{background-color:transparent;fill:currentColor;stroke-width:0;stroke:currentColor;pointer-events:none;width:1.5rem;height:1.5rem}.loading-overlay{align-items:center;display:none;justify-content:center;overflow:hidden}.loading-overlay.is-active{display:flex}.loading-overlay.is-full-page{z-index:999;position:fixed}.loading-overlay.is-full-page .loading-icon:after{top:calc(50% - 2.5em);left:calc(50% - 2.5em);width:5em;height:5em}.loading-overlay .loading-background{background:#7f7f7f;background:hsla(0,0%,100%,.5)}.loading-overlay .loading-icon{position:relative}.loading-overlay .loading-icon:after{position:absolute;top:calc(50% - 1.5em);left:calc(50% - 1.5em);width:3em;height:3em;border-width:.25em}.message .media,.notification .media{padding-top:0;border:0}.notification>.delete{right:.5rem!important;top:.5rem!important}.modal .animation-content{margin:0 20px}.modal .animation-content .modal-card{margin:0}@media screen and (max-width:768px){.modal .animation-content{width:100%}}.notices{position:fixed;display:flex;top:0;bottom:0;left:0;right:0;padding:2em;overflow:hidden;z-index:1000;pointer-events:none}.notices .toast{display:inline-flex;animation-duration:.15s;margin:.5em 0;text-align:center;box-shadow:0 1px 4px rgba(0,0,0,.12),0 0 6px rgba(0,0,0,.04);border-radius:2em;padding:.75em 1.5em;pointer-events:auto;opacity:.92}.notices .toast.is-white{color:#0a0a0a;background:#fff}.notices .toast.is-black{color:#fff;background:#0a0a0a}.notices .toast.is-light{color:#363636;background:#f5f5f5}.notices .toast.is-dark{color:#f5f5f5;background:#363636}.notices .toast.is-primary{color:#fff;background:#00d1b2}.notices .toast.is-link{color:#fff;background:#3273dc}.notices .toast.is-info{color:#fff;background:#209cee}.notices .toast.is-success{color:#fff;background:#23d160}.notices .toast.is-warning{color:rgba(0,0,0,.7);background:#ffdd57}.notices .toast.is-danger{color:#fff;background:#ff3860}.notices .snackbar{display:inline-flex;align-items:center;justify-content:space-around;animation-duration:.15s;margin:.5em 0;box-shadow:0 1px 4px rgba(0,0,0,.12),0 0 6px rgba(0,0,0,.04);border-radius:4px;pointer-events:auto;background:#363636;color:#f5f5f5;min-height:3em}.notices .snackbar .text{padding:.5em 1em}.notices .snackbar .action{margin-left:auto;padding:.5em;padding-left:0}.notices .snackbar .action .button{font-weight:600;text-transform:uppercase}.notices .snackbar .action.is-white .button{color:#fff}.notices .snackbar .action.is-black .button{color:#0a0a0a}.notices .snackbar .action.is-light .button{color:#f5f5f5}.notices .snackbar .action.is-dark .button{color:#363636}.notices .snackbar .action.is-primary .button{color:#00d1b2}.notices .snackbar .action.is-link .button{color:#3273dc}.notices .snackbar .action.is-info .button{color:#209cee}.notices .snackbar .action.is-success .button{color:#23d160}.notices .snackbar .action.is-warning .button{color:#ffdd57}.notices .snackbar .action.is-danger .button{color:#ff3860}@media screen and (max-width:768px){.notices .snackbar{width:100%;margin:0;border-radius:0}}@media print,screen and (min-width:769px){.notices .snackbar{min-width:350px;max-width:600px;overflow:hidden}}.notices .snackbar.is-bottom,.notices .snackbar.is-top,.notices .toast.is-bottom,.notices .toast.is-top{align-self:center}.notices .snackbar.is-bottom-right,.notices .snackbar.is-top-right,.notices .toast.is-bottom-right,.notices .toast.is-top-right{align-self:flex-end}.notices .snackbar.is-bottom-left,.notices .snackbar.is-top-left,.notices .toast.is-bottom-left,.notices .toast.is-top-left{align-self:flex-start}.notices .snackbar.is-toast,.notices .toast.is-toast{opacity:.92}.notices.is-top{flex-direction:column}.notices.is-bottom{flex-direction:column-reverse}.notices.has-custom-container{position:absolute}@media screen and (max-width:768px){.notices{padding:0;position:fixed!important}}.pagination .pagination-next,.pagination .pagination-previous{padding-left:.25em;padding-right:.25em}.pagination .pagination-next.is-disabled,.pagination .pagination-previous.is-disabled{pointer-events:none;cursor:not-allowed;opacity:.5}.pagination.is-simple{justify-content:normal}.pagination .is-current{pointer-events:none;cursor:not-allowed}.panel .panel-heading.is-collapsible{cursor:pointer}.panel .panel-content{width:100%}.b-radio.radio{outline:none;display:inline-flex;align-items:center}.b-radio.radio+.radio{margin-left:.5em}.b-radio.radio input[type=radio]{position:absolute;left:0;opacity:0;outline:none;z-index:-1}.b-radio.radio input[type=radio]+.check{display:flex;align-items:center;justify-content:center;width:1.25em;height:1.25em;border:2px solid #7a7a7a;border-radius:50%;transition:background .15s ease-out}.b-radio.radio input[type=radio]+.check:before{content:\\\"\\\";border-radius:50%;width:.625em;height:.625em;background:#00d1b2;transform:scale(0);transition:transform .15s ease-out}.b-radio.radio input[type=radio]+.check.is-white:before{background:#fff}.b-radio.radio input[type=radio]+.check.is-black:before{background:#0a0a0a}.b-radio.radio input[type=radio]+.check.is-light:before{background:#f5f5f5}.b-radio.radio input[type=radio]+.check.is-dark:before{background:#363636}.b-radio.radio input[type=radio]+.check.is-primary:before{background:#00d1b2}.b-radio.radio input[type=radio]+.check.is-link:before{background:#3273dc}.b-radio.radio input[type=radio]+.check.is-info:before{background:#209cee}.b-radio.radio input[type=radio]+.check.is-success:before{background:#23d160}.b-radio.radio input[type=radio]+.check.is-warning:before{background:#ffdd57}.b-radio.radio input[type=radio]+.check.is-danger:before{background:#ff3860}.b-radio.radio input[type=radio]:checked+.check{border-color:#00d1b2}.b-radio.radio input[type=radio]:checked+.check.is-white{border-color:#fff}.b-radio.radio input[type=radio]:checked+.check.is-black{border-color:#0a0a0a}.b-radio.radio input[type=radio]:checked+.check.is-light{border-color:#f5f5f5}.b-radio.radio input[type=radio]:checked+.check.is-dark{border-color:#363636}.b-radio.radio input[type=radio]:checked+.check.is-primary{border-color:#00d1b2}.b-radio.radio input[type=radio]:checked+.check.is-link{border-color:#3273dc}.b-radio.radio input[type=radio]:checked+.check.is-info{border-color:#209cee}.b-radio.radio input[type=radio]:checked+.check.is-success{border-color:#23d160}.b-radio.radio input[type=radio]:checked+.check.is-warning{border-color:#ffdd57}.b-radio.radio input[type=radio]:checked+.check.is-danger{border-color:#ff3860}.b-radio.radio input[type=radio]:checked+.check:before{transform:scale(1)}.b-radio.radio .control-label{padding-left:.5em}.b-radio.radio[disabled]{opacity:.5}.b-radio.radio:hover input[type=radio]+.check{border-color:#00d1b2}.b-radio.radio:hover input[type=radio]+.check.is-white{border-color:#fff}.b-radio.radio:hover input[type=radio]+.check.is-black{border-color:#0a0a0a}.b-radio.radio:hover input[type=radio]+.check.is-light{border-color:#f5f5f5}.b-radio.radio:hover input[type=radio]+.check.is-dark{border-color:#363636}.b-radio.radio:hover input[type=radio]+.check.is-primary{border-color:#00d1b2}.b-radio.radio:hover input[type=radio]+.check.is-link{border-color:#3273dc}.b-radio.radio:hover input[type=radio]+.check.is-info{border-color:#209cee}.b-radio.radio:hover input[type=radio]+.check.is-success{border-color:#23d160}.b-radio.radio:hover input[type=radio]+.check.is-warning{border-color:#ffdd57}.b-radio.radio:hover input[type=radio]+.check.is-danger{border-color:#ff3860}.b-radio.radio:focus input[type=radio]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.b-radio.radio:focus input[type=radio]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.b-radio.radio.is-small{border-radius:2px;font-size:.75rem}.b-radio.radio.is-medium{font-size:1.25rem}.b-radio.radio.is-large{font-size:1.5rem}.select select{padding-right:2.5em}.select select option{color:#4a4a4a;padding:.25em .5em}.select select option:disabled{cursor:not-allowed;opacity:.5}.select select optgroup{color:#b5b5b5;font-weight:400;font-style:normal;padding:.25em 0}.select.is-empty select{color:hsla(0,0%,48%,.7)}.switch{cursor:pointer;display:inline-flex;align-items:center}.switch+.switch{margin-left:.5em}.switch input[type=checkbox]{display:none}.switch input[type=checkbox]+.check{display:flex;align-items:center;flex-shrink:0;width:2.75em;height:1.575em;padding:.2em;background:#b5b5b5;border-radius:1em;transition:background .15s ease-out}.switch input[type=checkbox]+.check:before{content:\\\"\\\";border-radius:1em;width:1.175em;height:1.175em;background:#f5f5f5;box-shadow:0 3px 1px 0 rgba(0,0,0,.05),0 2px 2px 0 rgba(0,0,0,.1),0 3px 3px 0 rgba(0,0,0,.05);transition:transform .15s ease-out,width .15s ease-out;will-change:transform}.switch input[type=checkbox]+.check.is-elastic:before{width:1.75em}.switch input[type=checkbox]:checked+.check{background:#00d1b2}.switch input[type=checkbox]:checked+.check.is-white{background:#fff}.switch input[type=checkbox]:checked+.check.is-black{background:#0a0a0a}.switch input[type=checkbox]:checked+.check.is-light{background:#f5f5f5}.switch input[type=checkbox]:checked+.check.is-dark{background:#363636}.switch input[type=checkbox]:checked+.check.is-primary{background:#00d1b2}.switch input[type=checkbox]:checked+.check.is-link{background:#3273dc}.switch input[type=checkbox]:checked+.check.is-info{background:#209cee}.switch input[type=checkbox]:checked+.check.is-success{background:#23d160}.switch input[type=checkbox]:checked+.check.is-warning{background:#ffdd57}.switch input[type=checkbox]:checked+.check.is-danger{background:#ff3860}.switch input[type=checkbox]:checked+.check:before{transform:translate3d(100%,0,0)}.switch input[type=checkbox]:checked+.check.is-elastic:before{transform:translate3d(36.36364%,0,0)}.switch .control-label{padding-left:.5em}.switch:hover input[type=checkbox]+.check{background:hsla(0,0%,71%,.9)}.switch:hover input[type=checkbox]:checked+.check{background:rgba(0,209,178,.9)}.switch:hover input[type=checkbox]:checked+.check.is-white{background:hsla(0,0%,100%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-black{background:hsla(0,0%,4%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-light{background:hsla(0,0%,96%,.9)}.switch:hover input[type=checkbox]:checked+.check.is-dark{background:rgba(54,54,54,.9)}.switch:hover input[type=checkbox]:checked+.check.is-primary{background:rgba(0,209,178,.9)}.switch:hover input[type=checkbox]:checked+.check.is-link{background:rgba(50,115,220,.9)}.switch:hover input[type=checkbox]:checked+.check.is-info{background:rgba(32,156,238,.9)}.switch:hover input[type=checkbox]:checked+.check.is-success{background:rgba(35,209,96,.9)}.switch:hover input[type=checkbox]:checked+.check.is-warning{background:rgba(255,221,87,.9)}.switch:hover input[type=checkbox]:checked+.check.is-danger{background:rgba(255,56,96,.9)}.switch:focus{outline:none}.switch:focus input[type=checkbox]+.check{box-shadow:0 0 .5em hsla(0,0%,48%,.6)}.switch:focus input[type=checkbox]:checked+.check{box-shadow:0 0 .5em rgba(0,209,178,.8)}.switch:focus input[type=checkbox]:checked+.check.is-white{box-shadow:0 0 .5em hsla(0,0%,100%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-black{box-shadow:0 0 .5em hsla(0,0%,4%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-light{box-shadow:0 0 .5em hsla(0,0%,96%,.8)}.switch:focus input[type=checkbox]:checked+.check.is-dark{box-shadow:0 0 .5em rgba(54,54,54,.8)}.switch:focus input[type=checkbox]:checked+.check.is-primary{box-shadow:0 0 .5em rgba(0,209,178,.8)}.switch:focus input[type=checkbox]:checked+.check.is-link{box-shadow:0 0 .5em rgba(50,115,220,.8)}.switch:focus input[type=checkbox]:checked+.check.is-info{box-shadow:0 0 .5em rgba(32,156,238,.8)}.switch:focus input[type=checkbox]:checked+.check.is-success{box-shadow:0 0 .5em rgba(35,209,96,.8)}.switch:focus input[type=checkbox]:checked+.check.is-warning{box-shadow:0 0 .5em rgba(255,221,87,.8)}.switch:focus input[type=checkbox]:checked+.check.is-danger{box-shadow:0 0 .5em rgba(255,56,96,.8)}.switch.is-small{border-radius:2px;font-size:.75rem}.switch.is-medium{font-size:1.25rem}.switch.is-large{font-size:1.5rem}.switch[disabled]{opacity:.5;cursor:not-allowed;color:#7a7a7a}.table-wrapper .table{margin-bottom:0}.table-wrapper:not(:last-child){margin-bottom:1.5rem}@media screen and (max-width:1087px){.table-wrapper{overflow-x:auto}}.b-table{transition:opacity 86ms ease-out}@media print,screen and (min-width:769px){.b-table .table-mobile-sort{display:none}}.b-table .icon{transition:transform .15s ease-out,opacity 86ms ease-out}.b-table .icon.is-desc{transform:rotate(180deg)}.b-table .icon.is-expanded{transform:rotate(90deg)}.b-table .table{width:100%;border:1px solid transparent;border-radius:4px;border-collapse:separate}.b-table .table th{font-weight:600}.b-table .table th .th-wrap{display:flex;align-items:center}.b-table .table th .th-wrap .icon{margin-left:.5rem;margin-right:0;font-size:1rem}.b-table .table th .th-wrap.is-numeric{flex-direction:row-reverse;text-align:right}.b-table .table th .th-wrap.is-numeric .icon{margin-left:0;margin-right:.5rem}.b-table .table th .th-wrap.is-centered{justify-content:center;text-align:center}.b-table .table th.is-current-sort{border-color:#7a7a7a;font-weight:700}.b-table .table th.is-sortable:hover{border-color:#7a7a7a}.b-table .table th.is-sortable,.b-table .table th.is-sortable .th-wrap{cursor:pointer}.b-table .table tr.is-selected .checkbox input:checked+.check{background:#fff url(\\\"data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'%3E%3Cpath d='M.04.627L.146.52.43.804.323.91zm.177.177L.854.167.96.273.323.91z' fill='%2300d1b2'/%3E%3C/svg%3E\\\") no-repeat 50%}.b-table .table tr.is-selected .checkbox input+.check{border-color:#fff}.b-table .table tr.is-empty:hover{background-color:transparent}.b-table .table .chevron-cell{vertical-align:middle}.b-table .table .checkbox-cell{width:40px}.b-table .table .checkbox-cell .checkbox{vertical-align:middle}.b-table .table .checkbox-cell .checkbox .check{transition:none}.b-table .table tr.detail{box-shadow:inset 0 1px 3px #dbdbdb;background:#fafafa}.b-table .table tr.detail .detail-container{padding:1rem}.b-table .table:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.b-table .table.is-bordered th.is-current-sort,.b-table .table.is-bordered th.is-sortable:hover{border-color:#dbdbdb;background:#f5f5f5}@media screen and (max-width:768px){.b-table .table.has-mobile-cards thead{display:none}.b-table .table.has-mobile-cards tfoot th{border:0;display:inherit}.b-table .table.has-mobile-cards tr{box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1);max-width:100%;position:relative;display:block}.b-table .table.has-mobile-cards tr td{border:0;display:inherit}.b-table .table.has-mobile-cards tr td:last-child{border-bottom:0}.b-table .table.has-mobile-cards tr:not(:last-child){margin-bottom:1rem}.b-table .table.has-mobile-cards tr:not([class*=is-]){background:inherit}.b-table .table.has-mobile-cards tr:not([class*=is-]):hover{background-color:inherit}.b-table .table.has-mobile-cards tr.detail{margin-top:-1rem}.b-table .table.has-mobile-cards tr:not(.detail):not(.is-empty):not(.table-footer) td{display:flex;width:auto;justify-content:space-between;text-align:right;border-bottom:1px solid #f5f5f5}.b-table .table.has-mobile-cards tr:not(.detail):not(.is-empty):not(.table-footer) td:before{content:attr(data-label);font-weight:600;padding-right:.5em;text-align:left}}.b-table .level{padding-bottom:1.5rem}.b-table.is-loading{position:relative;pointer-events:none;opacity:.5}.b-table.is-loading:after{position:absolute;top:4em;left:calc(50% - 2.5em);width:5em;height:5em;border-width:.25em}.b-tabs .tabs{margin-bottom:0;flex-shrink:0}.b-tabs .is-disabled{pointer-events:none;cursor:not-allowed;opacity:.5}.b-tabs .tab-content{position:relative;overflow:hidden;display:flex;flex-direction:column;padding:1rem}.b-tabs .tab-content .tab-item{flex-shrink:0;flex-basis:auto}.b-tabs:not(:last-child){margin-bottom:1.5rem}.b-tabs.is-fullwidth{width:100%}.tag .has-ellipsis{max-width:10em;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.taginput .taginput-container.is-focusable{padding-bottom:0;padding-top:calc(.275em - 1px);align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start;height:auto;cursor:text}.taginput .taginput-container>.tag,.taginput .taginput-container>.tags{margin-bottom:calc(.275em - 1px);font-size:.9em;height:1.7em}.taginput .taginput-container>.tag .tag,.taginput .taginput-container>.tags .tag{margin-bottom:0;font-size:.9em;height:1.7em}.taginput .taginput-container>.tag:not(:last-child),.taginput .taginput-container>.tags:not(:last-child){margin-right:.275rem}.taginput .taginput-container .autocomplete{flex:1}.taginput .taginput-container .autocomplete input{height:1.7em;margin-bottom:calc(.275em - 1px);padding-top:0;padding-bottom:0;border:none;box-shadow:none;min-width:8em}.taginput .taginput-container .autocomplete input:focus{box-shadow:none!important}.taginput .taginput-container .autocomplete .icon{height:1.7em}.taginput .taginput-container .autocomplete>.control.is-loading:after{top:.375em}.timepicker .dropdown,.timepicker .dropdown-trigger{width:100%}.dropdown .dropdown-menu .has-link .timepicker a,.timepicker .dropdown-item,.timepicker .dropdown .dropdown-menu .has-link a{font-size:inherit}.timepicker .timepicker-footer{padding-top:.875rem}.timepicker .control .select{font-size:2.5rem}.timepicker .control .select select{font-weight:600;padding-right:calc(.625em - 1px)}.timepicker .control .select select option{font-size:1.5rem}.timepicker .control .select select option:disabled{color:hsla(0,0%,48%,.7)}.timepicker .control .select:after{display:none}.timepicker .control:first-child .select select{border-right:none}.timepicker .control:first-child .select:before{content:\\\":\\\";color:#b5b5b5;display:inline-block;position:absolute;z-index:6;font-size:3rem;right:-.325rem;top:.25rem}.timepicker .control:nth-child(2) .select select{border-left:none}.timepicker.is-small{border-radius:2px;font-size:.75rem}.timepicker.is-medium{font-size:1.25rem}.timepicker.is-large{font-size:1.5rem}.tooltip{position:relative;display:inline-flex}.tooltip.is-top:after,.tooltip.is-top:before{top:auto;right:auto;bottom:calc(100% + 5px + 2px);left:50%;transform:translateX(-50%)}.tooltip.is-top.is-white:before{border-top:5px solid #fff}.tooltip.is-top.is-black:before,.tooltip.is-top.is-white:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-black:before{border-top:5px solid #0a0a0a}.tooltip.is-top.is-light:before{border-top:5px solid #f5f5f5}.tooltip.is-top.is-dark:before,.tooltip.is-top.is-light:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-dark:before{border-top:5px solid #363636}.tooltip.is-top.is-primary:before{border-top:5px solid #00d1b2}.tooltip.is-top.is-link:before,.tooltip.is-top.is-primary:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-link:before{border-top:5px solid #3273dc}.tooltip.is-top.is-info:before{border-top:5px solid #209cee}.tooltip.is-top.is-info:before,.tooltip.is-top.is-success:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-success:before{border-top:5px solid #23d160}.tooltip.is-top.is-warning:before{border-top:5px solid #ffdd57}.tooltip.is-top.is-danger:before,.tooltip.is-top.is-warning:before{border-right:5px solid transparent;border-left:5px solid transparent;bottom:calc(100% + 2px)}.tooltip.is-top.is-danger:before{border-top:5px solid #ff3860}.tooltip.is-top.is-multiline.is-small:after{width:180px}.tooltip.is-top.is-multiline.is-medium:after{width:240px}.tooltip.is-top.is-multiline.is-large:after{width:300px}.tooltip.is-right:after,.tooltip.is-right:before{top:50%;right:auto;bottom:auto;left:calc(100% + 5px + 2px);transform:translateY(-50%)}.tooltip.is-right.is-white:before{border-right:5px solid #fff}.tooltip.is-right.is-black:before,.tooltip.is-right.is-white:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-black:before{border-right:5px solid #0a0a0a}.tooltip.is-right.is-light:before{border-right:5px solid #f5f5f5}.tooltip.is-right.is-dark:before,.tooltip.is-right.is-light:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-dark:before{border-right:5px solid #363636}.tooltip.is-right.is-primary:before{border-right:5px solid #00d1b2}.tooltip.is-right.is-link:before,.tooltip.is-right.is-primary:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-link:before{border-right:5px solid #3273dc}.tooltip.is-right.is-info:before{border-right:5px solid #209cee}.tooltip.is-right.is-info:before,.tooltip.is-right.is-success:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-success:before{border-right:5px solid #23d160}.tooltip.is-right.is-warning:before{border-right:5px solid #ffdd57}.tooltip.is-right.is-danger:before,.tooltip.is-right.is-warning:before{border-top:5px solid transparent;border-bottom:5px solid transparent;left:calc(100% + 2px)}.tooltip.is-right.is-danger:before{border-right:5px solid #ff3860}.tooltip.is-right.is-multiline.is-small:after{width:180px}.tooltip.is-right.is-multiline.is-medium:after{width:240px}.tooltip.is-right.is-multiline.is-large:after{width:300px}.tooltip.is-bottom:after,.tooltip.is-bottom:before{top:calc(100% + 5px + 2px);right:auto;bottom:auto;left:50%;transform:translateX(-50%)}.tooltip.is-bottom.is-white:before{border-bottom:5px solid #fff}.tooltip.is-bottom.is-black:before,.tooltip.is-bottom.is-white:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-black:before{border-bottom:5px solid #0a0a0a}.tooltip.is-bottom.is-light:before{border-bottom:5px solid #f5f5f5}.tooltip.is-bottom.is-dark:before,.tooltip.is-bottom.is-light:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-dark:before{border-bottom:5px solid #363636}.tooltip.is-bottom.is-primary:before{border-bottom:5px solid #00d1b2}.tooltip.is-bottom.is-link:before,.tooltip.is-bottom.is-primary:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-link:before{border-bottom:5px solid #3273dc}.tooltip.is-bottom.is-info:before{border-bottom:5px solid #209cee}.tooltip.is-bottom.is-info:before,.tooltip.is-bottom.is-success:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-success:before{border-bottom:5px solid #23d160}.tooltip.is-bottom.is-warning:before{border-bottom:5px solid #ffdd57}.tooltip.is-bottom.is-danger:before,.tooltip.is-bottom.is-warning:before{border-right:5px solid transparent;border-left:5px solid transparent;top:calc(100% + 2px)}.tooltip.is-bottom.is-danger:before{border-bottom:5px solid #ff3860}.tooltip.is-bottom.is-multiline.is-small:after{width:180px}.tooltip.is-bottom.is-multiline.is-medium:after{width:240px}.tooltip.is-bottom.is-multiline.is-large:after{width:300px}.tooltip.is-left:after,.tooltip.is-left:before{top:50%;right:calc(100% + 5px + 2px);bottom:auto;left:auto;transform:translateY(-50%)}.tooltip.is-left.is-white:before{border-left:5px solid #fff}.tooltip.is-left.is-black:before,.tooltip.is-left.is-white:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-black:before{border-left:5px solid #0a0a0a}.tooltip.is-left.is-light:before{border-left:5px solid #f5f5f5}.tooltip.is-left.is-dark:before,.tooltip.is-left.is-light:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-dark:before{border-left:5px solid #363636}.tooltip.is-left.is-primary:before{border-left:5px solid #00d1b2}.tooltip.is-left.is-link:before,.tooltip.is-left.is-primary:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-link:before{border-left:5px solid #3273dc}.tooltip.is-left.is-info:before{border-left:5px solid #209cee}.tooltip.is-left.is-info:before,.tooltip.is-left.is-success:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-success:before{border-left:5px solid #23d160}.tooltip.is-left.is-warning:before{border-left:5px solid #ffdd57}.tooltip.is-left.is-danger:before,.tooltip.is-left.is-warning:before{border-top:5px solid transparent;border-bottom:5px solid transparent;right:calc(100% + 2px)}.tooltip.is-left.is-danger:before{border-left:5px solid #ff3860}.tooltip.is-left.is-multiline.is-small:after{width:180px}.tooltip.is-left.is-multiline.is-medium:after{width:240px}.tooltip.is-left.is-multiline.is-large:after{width:300px}.tooltip:after,.tooltip:before{position:absolute;content:\\\"\\\";opacity:0;visibility:hidden;pointer-events:none}.tooltip:before{z-index:889}.tooltip:after{content:attr(data-label);width:auto;padding:.35rem .75rem;border-radius:6px;font-size:.85rem;font-weight:400;box-shadow:0 1px 2px 1px rgba(0,1,0,.2);z-index:888;white-space:nowrap}.tooltip:not([data-label=\\\"\\\"]):hover:after,.tooltip:not([data-label=\\\"\\\"]):hover:before{opacity:1;visibility:visible}.tooltip.is-white:after{background:#fff;color:#0a0a0a}.tooltip.is-black:after{background:#0a0a0a;color:#fff}.tooltip.is-light:after{background:#f5f5f5;color:#363636}.tooltip.is-dark:after{background:#363636;color:#f5f5f5}.tooltip.is-primary:after{background:#00d1b2;color:#fff}.tooltip.is-link:after{background:#3273dc;color:#fff}.tooltip.is-info:after{background:#209cee;color:#fff}.tooltip.is-success:after{background:#23d160;color:#fff}.tooltip.is-warning:after{background:#ffdd57;color:rgba(0,0,0,.7)}.tooltip.is-danger:after{background:#ff3860;color:#fff}.tooltip:not([data-label=\\\"\\\"]).is-always:after,.tooltip:not([data-label=\\\"\\\"]).is-always:before{opacity:1;visibility:visible}.tooltip.is-multiline:after{display:flex-block;text-align:center;white-space:normal}.tooltip.is-dashed{border-bottom:1px dashed #b5b5b5;cursor:default}.tooltip.is-square:after{border-radius:0}.tooltip.is-animated:after,.tooltip.is-animated:before{transition:opacity 86ms ease-out,visibility 86ms ease-out}.upload{position:relative}.upload input[type=file]{position:absolute;top:0;left:0;width:100%;opacity:0;outline:none;z-index:-1}.upload .upload-draggable{display:inline-block;cursor:pointer;padding:.25em;border:1px dashed #b5b5b5;border-radius:6px}.upload .upload-draggable.is-disabled{opacity:.5;cursor:not-allowed}.upload .upload-draggable.is-loading{position:relative;pointer-events:none;opacity:.5}.upload .upload-draggable.is-loading:after{top:0;left:calc(50% - 1.5em);width:3em;height:3em;border-width:.25em}.upload .upload-draggable.is-hovered.is-white,.upload .upload-draggable:hover.is-white{border-color:#fff;background:hsla(0,0%,100%,.05)}.upload .upload-draggable.is-hovered.is-black,.upload .upload-draggable:hover.is-black{border-color:#0a0a0a;background:hsla(0,0%,4%,.05)}.upload .upload-draggable.is-hovered.is-light,.upload .upload-draggable:hover.is-light{border-color:#f5f5f5;background:hsla(0,0%,96%,.05)}.upload .upload-draggable.is-hovered.is-dark,.upload .upload-draggable:hover.is-dark{border-color:#363636;background:rgba(54,54,54,.05)}.upload .upload-draggable.is-hovered.is-primary,.upload .upload-draggable:hover.is-primary{border-color:#00d1b2;background:rgba(0,209,178,.05)}.upload .upload-draggable.is-hovered.is-link,.upload .upload-draggable:hover.is-link{border-color:#3273dc;background:rgba(50,115,220,.05)}.upload .upload-draggable.is-hovered.is-info,.upload .upload-draggable:hover.is-info{border-color:#209cee;background:rgba(32,156,238,.05)}.upload .upload-draggable.is-hovered.is-success,.upload .upload-draggable:hover.is-success{border-color:#23d160;background:rgba(35,209,96,.05)}.upload .upload-draggable.is-hovered.is-warning,.upload .upload-draggable:hover.is-warning{border-color:#ffdd57;background:rgba(255,221,87,.05)}.upload .upload-draggable.is-hovered.is-danger,.upload .upload-draggable:hover.is-danger{border-color:#ff3860;background:rgba(255,56,96,.05)}\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader!./node_modules/buefy/dist/buefy.css\n// module id = 22\n// module chunks = 0","/**\n * Translates the list format produced by css-loader into something\n * easier to manipulate.\n */\nmodule.exports = function listToStyles (parentId, list) {\n var styles = []\n var newStyles = {}\n for (var i = 0; i < list.length; i++) {\n var item = list[i]\n var id = item[0]\n var css = item[1]\n var media = item[2]\n var sourceMap = item[3]\n var part = {\n id: parentId + ':' + i,\n css: css,\n media: media,\n sourceMap: sourceMap\n }\n if (!newStyles[id]) {\n styles.push(newStyles[id] = { id: id, parts: [part] })\n } else {\n newStyles[id].parts.push(part)\n }\n }\n return styles\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader/lib/listToStyles.js\n// module id = 23\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../css-loader/index.js!./styles.css\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../vue-style-loader/lib/addStylesClient.js\")(\"bf15a564\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-material-design-icons/styles.css\n// module id = 24\n// module chunks = 0","exports = module.exports = require(\"../css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \".material-design-icon{display:inline-flex;align-self:center;position:relative;height:1em;width:1em;>.material-design-icon__svg{height:1em;width:1em;fill:currentColor;position:absolute;bottom:-.125em}}\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader!./node_modules/vue-material-design-icons/styles.css\n// module id = 25\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-3e910c9a\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../node_modules/vue-loader/lib/selector?type=styles&index=0!./App.vue\")\n}\nvar normalizeComponent = require(\"!../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../node_modules/vue-loader/lib/selector?type=script&index=0!./App.vue\"\nimport __vue_script__ from \"!!babel-loader!../node_modules/vue-loader/lib/selector?type=script&index=0!./App.vue\"\n/* template */\nimport __vue_template__ from \"!!../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-3e910c9a\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../node_modules/vue-loader/lib/selector?type=template&index=0!./App.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/App.vue\n// module id = 26\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../node_modules/css-loader/index.js?minimize!../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-3e910c9a\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./App.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"0abd6ea6\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-3e910c9a\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue\n// module id = 27\n// module chunks = 0","exports = module.exports = require(\"../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-3e910c9a\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/App.vue\n// module id = 28\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-657ba0aa\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./NavBar.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./NavBar.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./NavBar.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-657ba0aa\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./NavBar.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/NavBar.vue\n// module id = 29\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-657ba0aa\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./NavBar.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"432a0e31\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-657ba0aa\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/NavBar.vue\n// module id = 30\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-657ba0aa\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/NavBar.vue\n// module id = 31\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('nav',{staticClass:\"navbar has-shadow\"},[_vm._m(0),_vm._v(\" \"),_c('div',{staticClass:\"navbar-menu\"},[_c('div',{staticClass:\"navbar-start\"}),_vm._v(\" \"),_c('div',{staticClass:\"navbar-end\"},[_c('div',{staticClass:\"navbar-item\"},[_c('p',{staticClass:\"control\"},[(_vm.isConnected)?_c('button',{staticClass:\"button is-danger\",on:{\"click\":_vm.disconnect}},[_vm._v(\"Disconnect\")]):_c('button',{staticClass:\"button is-success\",on:{\"click\":_vm.connect}},[_vm._v(\"Connect\")])])])])])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"navbar-brand\"},[_c('a',{staticClass:\"navbar-item\",attrs:{\"href\":\"#\"}},[_vm._v(\"WebRender Debugger\")])])}]\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-657ba0aa\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/NavBar.vue\n// module id = 32\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-e621d982\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./NavMenu.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./NavMenu.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./NavMenu.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-e621d982\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./NavMenu.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/NavMenu.vue\n// module id = 33\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-e621d982\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./NavMenu.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"e4edc9ba\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-e621d982\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/NavMenu.vue\n// module id = 34\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-e621d982\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/NavMenu.vue\n// module id = 35\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('aside',{staticClass:\"menu\"},[_c('p',{staticClass:\"menu-label\"},[_vm._v(\"\\n Pages\\n \")]),_vm._v(\" \"),_c('ul',{staticClass:\"menu-list\"},[_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'options' },on:{\"click\":function($event){return _vm.setPage('options')}}},[_vm._v(\"Debug Options\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'passes' },on:{\"click\":function($event){return _vm.setPage('passes')}}},[_vm._v(\"Passes\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'render_tasks' },on:{\"click\":function($event){return _vm.setPage('render_tasks')}}},[_vm._v(\"Render Tasks\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'documents' },on:{\"click\":function($event){return _vm.setPage('documents')}}},[_vm._v(\"Documents\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'clip_scroll_tree' },on:{\"click\":function($event){return _vm.setPage('clip_scroll_tree')}}},[_vm._v(\"Clip-Scroll Tree\")])]),_vm._v(\" \"),_c('li',[_c('a',{class:{ 'is-active': _vm.page == 'screenshot' },on:{\"click\":function($event){return _vm.setPage('screenshot')}}},[_vm._v(\"Screenshot\")])])])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-e621d982\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/NavMenu.vue\n// module id = 36\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-61a8a336\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./OptionsPage.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./OptionsPage.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./OptionsPage.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-61a8a336\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./OptionsPage.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/OptionsPage.vue\n// module id = 37\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-61a8a336\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./OptionsPage.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"0e6ffcbb\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-61a8a336\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/OptionsPage.vue\n// module id = 38\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-61a8a336\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/OptionsPage.vue\n// module id = 39\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setProfiler($event.target.checked)}}}),_vm._v(\"\\n Profiler\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setTextureCacheDebugger($event.target.checked)}}}),_vm._v(\"\\n Texture cache debugger\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setRenderTargetDebugger($event.target.checked)}}}),_vm._v(\"\\n Render target debugger\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setAlphaRectsDebugger($event.target.checked)}}}),_vm._v(\"\\n Alpha primitive rects debugger\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setGpuTimeQueries($event.target.checked)}}}),_vm._v(\"\\n Enable GPU time queries\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setGpuSampleQueries($event.target.checked)}}}),_vm._v(\"\\n Enable GPU sample queries\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setOpaquePass(!$event.target.checked)}}}),_vm._v(\"\\n Disable opaque pass\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setAlphaPass(!$event.target.checked)}}}),_vm._v(\"\\n Disable alpha pass\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setClipMasks(!$event.target.checked)}}}),_vm._v(\"\\n Disable clip masks\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setTextPrims(!$event.target.checked)}}}),_vm._v(\"\\n Disable text primitives\\n \")])]),_vm._v(\" \"),_c('div',{staticClass:\"field\"},[_c('label',{staticClass:\"checkbox\"},[_c('input',{attrs:{\"type\":\"checkbox\",\"disabled\":_vm.disabled},on:{\"click\":function($event){return _vm.setGradientPrims(!$event.target.checked)}}}),_vm._v(\"\\n Disable gradient primitives\\n \")])])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-61a8a336\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/OptionsPage.vue\n// module id = 40\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-329e01f6\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./PassViewPage.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./PassViewPage.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./PassViewPage.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-329e01f6\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./PassViewPage.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/PassViewPage.vue\n// module id = 41\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-329e01f6\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./PassViewPage.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"05673d18\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-329e01f6\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/PassViewPage.vue\n// module id = 42\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-329e01f6\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/PassViewPage.vue\n// module id = 43\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Passes \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_vm._l((_vm.passes),function(pass,pass_index){return _c('div',[_c('p',{staticClass:\"has-text-black-bis\"},[_vm._v(\"Pass \"+_vm._s(pass_index))]),_vm._v(\" \"),_vm._l((pass.targets),function(target,target_index){return _c('div',[_c('p',{staticClass:\"has-text-grey-dark\",staticStyle:{\"text-indent\":\"2em\"}},[_vm._v(\"Target \"+_vm._s(target_index)+\" (\"+_vm._s(target.kind)+\")\")]),_vm._v(\" \"),_vm._l((target.batches),function(batch,batch_index){return _c('div',[_c('p',{staticClass:\"has-text-grey\",staticStyle:{\"text-indent\":\"4em\"}},[_vm._v(\"Batch \"+_vm._s(batch_index)+\" (\"+_vm._s(batch.description)+\", \"+_vm._s(batch.kind)+\", \"+_vm._s(batch.count)+\" instances)\")])])})],2)}),_vm._v(\" \"),_c('hr')],2)})],2)}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-329e01f6\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/PassViewPage.vue\n// module id = 44\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-49dfebc9\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./RenderTaskViewPage.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./RenderTaskViewPage.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./RenderTaskViewPage.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-49dfebc9\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./RenderTaskViewPage.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/RenderTaskViewPage.vue\n// module id = 45\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-49dfebc9\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./RenderTaskViewPage.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"0d80f44e\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-49dfebc9\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/RenderTaskViewPage.vue\n// module id = 46\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-49dfebc9\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/RenderTaskViewPage.vue\n// module id = 47\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-44998ed8\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./TreeView.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"77142b62\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-44998ed8\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/TreeView.vue\n// module id = 48\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-44998ed8\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/TreeView.vue\n// module id = 49\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('li',[_c('div',{on:{\"click\":_vm.toggle}},[(_vm.isFolder)?_c('span',[_vm._v(\"[\"+_vm._s(_vm.open ? '-' : '+')+\"]\")]):_vm._e(),_vm._v(\"\\n \"+_vm._s(_vm.model.description)+\"\\n \")]),_vm._v(\" \"),(_vm.isFolder)?_c('ul',{directives:[{name:\"show\",rawName:\"v-show\",value:(_vm.open),expression:\"open\"}],staticStyle:{\"padding-left\":\"1em\",\"line-height\":\"1.5em\"}},_vm._l((_vm.model.children),function(model){return _c('treeview',{attrs:{\"model\":model}})}),1):_vm._e()])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-44998ed8\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/TreeView.vue\n// module id = 50\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Render Tasks \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_c('div',[_c('ul',[_c('app-treeview',{attrs:{\"model\":_vm.render_tasks}})],1)])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-49dfebc9\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/RenderTaskViewPage.vue\n// module id = 51\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-1b68865d\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./DocumentViewPage.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./DocumentViewPage.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./DocumentViewPage.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-1b68865d\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./DocumentViewPage.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/DocumentViewPage.vue\n// module id = 52\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-1b68865d\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./DocumentViewPage.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"7b986bc8\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-1b68865d\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/DocumentViewPage.vue\n// module id = 53\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-1b68865d\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/DocumentViewPage.vue\n// module id = 54\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Documents \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_c('div',[_c('ul',[_c('app-treeview',{attrs:{\"model\":_vm.documents}})],1)])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-1b68865d\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/DocumentViewPage.vue\n// module id = 55\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-31f0a1d9\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./ClipScrollTreeViewPage.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./ClipScrollTreeViewPage.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./ClipScrollTreeViewPage.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-31f0a1d9\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./ClipScrollTreeViewPage.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/ClipScrollTreeViewPage.vue\n// module id = 56\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-31f0a1d9\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./ClipScrollTreeViewPage.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"3d63fa5b\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-31f0a1d9\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/ClipScrollTreeViewPage.vue\n// module id = 57\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-31f0a1d9\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/ClipScrollTreeViewPage.vue\n// module id = 58\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Clip-Scroll Tree \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_c('div',[_c('ul',[_c('app-treeview',{attrs:{\"model\":_vm.clip_scroll_tree}})],1)])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-31f0a1d9\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/ClipScrollTreeViewPage.vue\n// module id = 59\n// module chunks = 0","function injectStyle (ssrContext) {\n require(\"!!vue-style-loader!css-loader?minimize!../../node_modules/vue-loader/lib/style-compiler/index?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-29affa2a\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector?type=styles&index=0!./ScreenshotPage.vue\")\n}\nvar normalizeComponent = require(\"!../../node_modules/vue-loader/lib/component-normalizer\")\n/* script */\nexport * from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./ScreenshotPage.vue\"\nimport __vue_script__ from \"!!babel-loader!../../node_modules/vue-loader/lib/selector?type=script&index=0!./ScreenshotPage.vue\"\n/* template */\nimport __vue_template__ from \"!!../../node_modules/vue-loader/lib/template-compiler/index?{\\\"id\\\":\\\"data-v-29affa2a\\\",\\\"hasScoped\\\":false,\\\"buble\\\":{\\\"transforms\\\":{}}}!../../node_modules/vue-loader/lib/selector?type=template&index=0!./ScreenshotPage.vue\"\n/* template functional */\nvar __vue_template_functional__ = false\n/* styles */\nvar __vue_styles__ = injectStyle\n/* scopeId */\nvar __vue_scopeId__ = null\n/* moduleIdentifier (server only) */\nvar __vue_module_identifier__ = null\nvar Component = normalizeComponent(\n __vue_script__,\n __vue_template__,\n __vue_template_functional__,\n __vue_styles__,\n __vue_scopeId__,\n __vue_module_identifier__\n)\n\nexport default Component.exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/components/ScreenshotPage.vue\n// module id = 60\n// module chunks = 0","// style-loader: Adds some css to the DOM by adding a <style> tag\n\n// load the styles\nvar content = require(\"!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\\\"vue\\\":true,\\\"id\\\":\\\"data-v-29affa2a\\\",\\\"scoped\\\":false,\\\"hasInlineConfig\\\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./ScreenshotPage.vue\");\nif(typeof content === 'string') content = [[module.id, content, '']];\nif(content.locals) module.exports = content.locals;\n// add the styles to the DOM\nvar update = require(\"!../../node_modules/vue-style-loader/lib/addStylesClient.js\")(\"3900c1de\", content, true, {});\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-style-loader!./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-29affa2a\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/ScreenshotPage.vue\n// module id = 61\n// module chunks = 0","exports = module.exports = require(\"../../node_modules/css-loader/lib/css-base.js\")(false);\n// imports\n\n\n// module\nexports.push([module.id, \"\", \"\"]);\n\n// exports\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/css-loader?minimize!./node_modules/vue-loader/lib/style-compiler?{\"vue\":true,\"id\":\"data-v-29affa2a\",\"scoped\":false,\"hasInlineConfig\":false}!./node_modules/vue-loader/lib/selector.js?type=styles&index=0!./src/components/ScreenshotPage.vue\n// module id = 62\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:\"box\"},[_c('h1',{staticClass:\"title\"},[_vm._v(\"Screenshot \"),_c('a',{staticClass:\"button is-info\",attrs:{\"disabled\":_vm.disabled},on:{\"click\":_vm.fetch}},[_vm._v(\"Refresh\")])]),_vm._v(\" \"),_c('hr'),_vm._v(\" \"),_c('div',[_c('ul',[(_vm.screenshot.length > 0)?_c('img',{staticStyle:{\"transform\":\"scaleY(-1)\",\"width\":\"1024px\",\"height\":\"768px\"},attrs:{\"src\":'data:image/png;base64,' + _vm.screenshot}}):_vm._e()])])])}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-29affa2a\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/components/ScreenshotPage.vue\n// module id = 63\n// module chunks = 0","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('app-navbar'),_vm._v(\" \"),_c('div',{staticClass:\"section\"},[_c('div',{staticClass:\"container\"},[_c('div',{staticClass:\"columns\"},[_c('div',{staticClass:\"column is-3\"},[_c('app-navmenu')],1),_vm._v(\" \"),_c('div',{staticClass:\"column\"},[(_vm.page == 'options')?_c('app-options'):_vm._e(),_vm._v(\" \"),(_vm.page == 'passes')?_c('app-passview'):_vm._e(),_vm._v(\" \"),(_vm.page == 'render_tasks')?_c('app-rendertaskview'):_vm._e(),_vm._v(\" \"),(_vm.page == 'documents')?_c('app-documentview'):_vm._e(),_vm._v(\" \"),(_vm.page == 'clip_scroll_tree')?_c('app-clipscrolltreeview'):_vm._e(),_vm._v(\" \"),(_vm.page == 'screenshot')?_c('app-screenshotview'):_vm._e()],1)])])])],1)}\nvar staticRenderFns = []\nvar esExports = { render: render, staticRenderFns: staticRenderFns }\nexport default esExports\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vue-loader/lib/template-compiler?{\"id\":\"data-v-3e910c9a\",\"hasScoped\":false,\"buble\":{\"transforms\":{}}}!./node_modules/vue-loader/lib/selector.js?type=template&index=0!./src/App.vue\n// module id = 64\n// module chunks = 0","import Vue from 'vue'\nimport Vuex from 'vuex'\n\nVue.use(Vuex)\n\nclass Connection {\n constructor() {\n this.ws = null;\n }\n\n connect(context) {\n var ws = new WebSocket(\"ws://127.0.0.1:3583\");\n\n ws.onopen = function() {\n context.commit('setConnected', true);\n }\n\n ws.onmessage = function(evt) {\n var json = JSON.parse(evt.data);\n if (json['kind'] == \"passes\") {\n context.commit('setPasses', json['passes']);\n } else if (json['kind'] == \"render_tasks\") {\n context.commit('setRenderTasks', json['root']);\n } else if (json['kind'] == \"documents\") {\n context.commit('setDocuments', json['root']);\n } else if (json['kind'] == \"clip_scroll_tree\") {\n context.commit('setClipScrollTree', json['root']);\n } else if (json['kind'] == \"screenshot\") {\n context.commit('setScreenshot', json['data']);\n } else {\n console.warn(\"unknown message kind: \" + json['kind']);\n }\n }\n\n ws.onclose = function() {\n context.commit('setConnected', false);\n }\n\n this.ws = ws;\n }\n\n send(msg) {\n if (this.ws !== null) {\n this.ws.send(msg);\n }\n }\n\n disconnect() {\n if (this.ws !== null) {\n this.ws.close();\n this.ws = null;\n }\n }\n}\n\nvar connection = new Connection();\n\nconst store = new Vuex.Store({\n strict: true,\n state: {\n connected: false,\n page: 'options',\n passes: [],\n render_tasks: [],\n documents: [],\n clip_scroll_tree: [],\n screenshot: [],\n },\n mutations: {\n setConnected(state, connected) {\n state.connected = connected;\n },\n setPage(state, name) {\n state.page = name;\n },\n setPasses(state, passes) {\n state.passes = passes;\n },\n setRenderTasks(state, render_tasks) {\n state.render_tasks = render_tasks;\n },\n setDocuments(state, documents) {\n state.documents = documents;\n },\n setClipScrollTree(state, clip_scroll_tree) {\n state.clip_scroll_tree = clip_scroll_tree;\n },\n setScreenshot(state, screenshot) {\n state.screenshot = screenshot;\n },\n },\n actions: {\n connect(context) {\n connection.connect(context);\n },\n disconnect(context) {\n connection.disconnect();\n },\n sendMessage(context, msg) {\n connection.send(msg);\n }\n }\n});\n\nexport default store;\n\n\n\n// WEBPACK FOOTER //\n// ./src/store/index.js","/**\n * vuex v3.1.0\n * (c) 2019 Evan You\n * @license MIT\n */\nfunction applyMixin (Vue) {\n var version = Number(Vue.version.split('.')[0]);\n\n if (version >= 2) {\n Vue.mixin({ beforeCreate: vuexInit });\n } else {\n // override init and inject vuex init procedure\n // for 1.x backwards compatibility.\n var _init = Vue.prototype._init;\n Vue.prototype._init = function (options) {\n if ( options === void 0 ) options = {};\n\n options.init = options.init\n ? [vuexInit].concat(options.init)\n : vuexInit;\n _init.call(this, options);\n };\n }\n\n /**\n * Vuex init hook, injected into each instances init hooks list.\n */\n\n function vuexInit () {\n var options = this.$options;\n // store injection\n if (options.store) {\n this.$store = typeof options.store === 'function'\n ? options.store()\n : options.store;\n } else if (options.parent && options.parent.$store) {\n this.$store = options.parent.$store;\n }\n }\n}\n\nvar devtoolHook =\n typeof window !== 'undefined' &&\n window.__VUE_DEVTOOLS_GLOBAL_HOOK__;\n\nfunction devtoolPlugin (store) {\n if (!devtoolHook) { return }\n\n store._devtoolHook = devtoolHook;\n\n devtoolHook.emit('vuex:init', store);\n\n devtoolHook.on('vuex:travel-to-state', function (targetState) {\n store.replaceState(targetState);\n });\n\n store.subscribe(function (mutation, state) {\n devtoolHook.emit('vuex:mutation', mutation, state);\n });\n}\n\n/**\n * Get the first item that pass the test\n * by second argument function\n *\n * @param {Array} list\n * @param {Function} f\n * @return {*}\n */\n\n/**\n * forEach for object\n */\nfunction forEachValue (obj, fn) {\n Object.keys(obj).forEach(function (key) { return fn(obj[key], key); });\n}\n\nfunction isObject (obj) {\n return obj !== null && typeof obj === 'object'\n}\n\nfunction isPromise (val) {\n return val && typeof val.then === 'function'\n}\n\nfunction assert (condition, msg) {\n if (!condition) { throw new Error((\"[vuex] \" + msg)) }\n}\n\n// Base data struct for store's module, package with some attribute and method\nvar Module = function Module (rawModule, runtime) {\n this.runtime = runtime;\n // Store some children item\n this._children = Object.create(null);\n // Store the origin module object which passed by programmer\n this._rawModule = rawModule;\n var rawState = rawModule.state;\n\n // Store the origin module's state\n this.state = (typeof rawState === 'function' ? rawState() : rawState) || {};\n};\n\nvar prototypeAccessors = { namespaced: { configurable: true } };\n\nprototypeAccessors.namespaced.get = function () {\n return !!this._rawModule.namespaced\n};\n\nModule.prototype.addChild = function addChild (key, module) {\n this._children[key] = module;\n};\n\nModule.prototype.removeChild = function removeChild (key) {\n delete this._children[key];\n};\n\nModule.prototype.getChild = function getChild (key) {\n return this._children[key]\n};\n\nModule.prototype.update = function update (rawModule) {\n this._rawModule.namespaced = rawModule.namespaced;\n if (rawModule.actions) {\n this._rawModule.actions = rawModule.actions;\n }\n if (rawModule.mutations) {\n this._rawModule.mutations = rawModule.mutations;\n }\n if (rawModule.getters) {\n this._rawModule.getters = rawModule.getters;\n }\n};\n\nModule.prototype.forEachChild = function forEachChild (fn) {\n forEachValue(this._children, fn);\n};\n\nModule.prototype.forEachGetter = function forEachGetter (fn) {\n if (this._rawModule.getters) {\n forEachValue(this._rawModule.getters, fn);\n }\n};\n\nModule.prototype.forEachAction = function forEachAction (fn) {\n if (this._rawModule.actions) {\n forEachValue(this._rawModule.actions, fn);\n }\n};\n\nModule.prototype.forEachMutation = function forEachMutation (fn) {\n if (this._rawModule.mutations) {\n forEachValue(this._rawModule.mutations, fn);\n }\n};\n\nObject.defineProperties( Module.prototype, prototypeAccessors );\n\nvar ModuleCollection = function ModuleCollection (rawRootModule) {\n // register root module (Vuex.Store options)\n this.register([], rawRootModule, false);\n};\n\nModuleCollection.prototype.get = function get (path) {\n return path.reduce(function (module, key) {\n return module.getChild(key)\n }, this.root)\n};\n\nModuleCollection.prototype.getNamespace = function getNamespace (path) {\n var module = this.root;\n return path.reduce(function (namespace, key) {\n module = module.getChild(key);\n return namespace + (module.namespaced ? key + '/' : '')\n }, '')\n};\n\nModuleCollection.prototype.update = function update$1 (rawRootModule) {\n update([], this.root, rawRootModule);\n};\n\nModuleCollection.prototype.register = function register (path, rawModule, runtime) {\n var this$1 = this;\n if ( runtime === void 0 ) runtime = true;\n\n if (process.env.NODE_ENV !== 'production') {\n assertRawModule(path, rawModule);\n }\n\n var newModule = new Module(rawModule, runtime);\n if (path.length === 0) {\n this.root = newModule;\n } else {\n var parent = this.get(path.slice(0, -1));\n parent.addChild(path[path.length - 1], newModule);\n }\n\n // register nested modules\n if (rawModule.modules) {\n forEachValue(rawModule.modules, function (rawChildModule, key) {\n this$1.register(path.concat(key), rawChildModule, runtime);\n });\n }\n};\n\nModuleCollection.prototype.unregister = function unregister (path) {\n var parent = this.get(path.slice(0, -1));\n var key = path[path.length - 1];\n if (!parent.getChild(key).runtime) { return }\n\n parent.removeChild(key);\n};\n\nfunction update (path, targetModule, newModule) {\n if (process.env.NODE_ENV !== 'production') {\n assertRawModule(path, newModule);\n }\n\n // update target module\n targetModule.update(newModule);\n\n // update nested modules\n if (newModule.modules) {\n for (var key in newModule.modules) {\n if (!targetModule.getChild(key)) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n \"[vuex] trying to add a new module '\" + key + \"' on hot reloading, \" +\n 'manual reload is needed'\n );\n }\n return\n }\n update(\n path.concat(key),\n targetModule.getChild(key),\n newModule.modules[key]\n );\n }\n }\n}\n\nvar functionAssert = {\n assert: function (value) { return typeof value === 'function'; },\n expected: 'function'\n};\n\nvar objectAssert = {\n assert: function (value) { return typeof value === 'function' ||\n (typeof value === 'object' && typeof value.handler === 'function'); },\n expected: 'function or object with \"handler\" function'\n};\n\nvar assertTypes = {\n getters: functionAssert,\n mutations: functionAssert,\n actions: objectAssert\n};\n\nfunction assertRawModule (path, rawModule) {\n Object.keys(assertTypes).forEach(function (key) {\n if (!rawModule[key]) { return }\n\n var assertOptions = assertTypes[key];\n\n forEachValue(rawModule[key], function (value, type) {\n assert(\n assertOptions.assert(value),\n makeAssertionMessage(path, key, type, value, assertOptions.expected)\n );\n });\n });\n}\n\nfunction makeAssertionMessage (path, key, type, value, expected) {\n var buf = key + \" should be \" + expected + \" but \\\"\" + key + \".\" + type + \"\\\"\";\n if (path.length > 0) {\n buf += \" in module \\\"\" + (path.join('.')) + \"\\\"\";\n }\n buf += \" is \" + (JSON.stringify(value)) + \".\";\n return buf\n}\n\nvar Vue; // bind on install\n\nvar Store = function Store (options) {\n var this$1 = this;\n if ( options === void 0 ) options = {};\n\n // Auto install if it is not done yet and `window` has `Vue`.\n // To allow users to avoid auto-installation in some cases,\n // this code should be placed here. See #731\n if (!Vue && typeof window !== 'undefined' && window.Vue) {\n install(window.Vue);\n }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(Vue, \"must call Vue.use(Vuex) before creating a store instance.\");\n assert(typeof Promise !== 'undefined', \"vuex requires a Promise polyfill in this browser.\");\n assert(this instanceof Store, \"store must be called with the new operator.\");\n }\n\n var plugins = options.plugins; if ( plugins === void 0 ) plugins = [];\n var strict = options.strict; if ( strict === void 0 ) strict = false;\n\n // store internal state\n this._committing = false;\n this._actions = Object.create(null);\n this._actionSubscribers = [];\n this._mutations = Object.create(null);\n this._wrappedGetters = Object.create(null);\n this._modules = new ModuleCollection(options);\n this._modulesNamespaceMap = Object.create(null);\n this._subscribers = [];\n this._watcherVM = new Vue();\n\n // bind commit and dispatch to self\n var store = this;\n var ref = this;\n var dispatch = ref.dispatch;\n var commit = ref.commit;\n this.dispatch = function boundDispatch (type, payload) {\n return dispatch.call(store, type, payload)\n };\n this.commit = function boundCommit (type, payload, options) {\n return commit.call(store, type, payload, options)\n };\n\n // strict mode\n this.strict = strict;\n\n var state = this._modules.root.state;\n\n // init root module.\n // this also recursively registers all sub-modules\n // and collects all module getters inside this._wrappedGetters\n installModule(this, state, [], this._modules.root);\n\n // initialize the store vm, which is responsible for the reactivity\n // (also registers _wrappedGetters as computed properties)\n resetStoreVM(this, state);\n\n // apply plugins\n plugins.forEach(function (plugin) { return plugin(this$1); });\n\n var useDevtools = options.devtools !== undefined ? options.devtools : Vue.config.devtools;\n if (useDevtools) {\n devtoolPlugin(this);\n }\n};\n\nvar prototypeAccessors$1 = { state: { configurable: true } };\n\nprototypeAccessors$1.state.get = function () {\n return this._vm._data.$$state\n};\n\nprototypeAccessors$1.state.set = function (v) {\n if (process.env.NODE_ENV !== 'production') {\n assert(false, \"use store.replaceState() to explicit replace store state.\");\n }\n};\n\nStore.prototype.commit = function commit (_type, _payload, _options) {\n var this$1 = this;\n\n // check object-style commit\n var ref = unifyObjectStyle(_type, _payload, _options);\n var type = ref.type;\n var payload = ref.payload;\n var options = ref.options;\n\n var mutation = { type: type, payload: payload };\n var entry = this._mutations[type];\n if (!entry) {\n if (process.env.NODE_ENV !== 'production') {\n console.error((\"[vuex] unknown mutation type: \" + type));\n }\n return\n }\n this._withCommit(function () {\n entry.forEach(function commitIterator (handler) {\n handler(payload);\n });\n });\n this._subscribers.forEach(function (sub) { return sub(mutation, this$1.state); });\n\n if (\n process.env.NODE_ENV !== 'production' &&\n options && options.silent\n ) {\n console.warn(\n \"[vuex] mutation type: \" + type + \". Silent option has been removed. \" +\n 'Use the filter functionality in the vue-devtools'\n );\n }\n};\n\nStore.prototype.dispatch = function dispatch (_type, _payload) {\n var this$1 = this;\n\n // check object-style dispatch\n var ref = unifyObjectStyle(_type, _payload);\n var type = ref.type;\n var payload = ref.payload;\n\n var action = { type: type, payload: payload };\n var entry = this._actions[type];\n if (!entry) {\n if (process.env.NODE_ENV !== 'production') {\n console.error((\"[vuex] unknown action type: \" + type));\n }\n return\n }\n\n try {\n this._actionSubscribers\n .filter(function (sub) { return sub.before; })\n .forEach(function (sub) { return sub.before(action, this$1.state); });\n } catch (e) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\"[vuex] error in before action subscribers: \");\n console.error(e);\n }\n }\n\n var result = entry.length > 1\n ? Promise.all(entry.map(function (handler) { return handler(payload); }))\n : entry[0](payload);\n\n return result.then(function (res) {\n try {\n this$1._actionSubscribers\n .filter(function (sub) { return sub.after; })\n .forEach(function (sub) { return sub.after(action, this$1.state); });\n } catch (e) {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\"[vuex] error in after action subscribers: \");\n console.error(e);\n }\n }\n return res\n })\n};\n\nStore.prototype.subscribe = function subscribe (fn) {\n return genericSubscribe(fn, this._subscribers)\n};\n\nStore.prototype.subscribeAction = function subscribeAction (fn) {\n var subs = typeof fn === 'function' ? { before: fn } : fn;\n return genericSubscribe(subs, this._actionSubscribers)\n};\n\nStore.prototype.watch = function watch (getter, cb, options) {\n var this$1 = this;\n\n if (process.env.NODE_ENV !== 'production') {\n assert(typeof getter === 'function', \"store.watch only accepts a function.\");\n }\n return this._watcherVM.$watch(function () { return getter(this$1.state, this$1.getters); }, cb, options)\n};\n\nStore.prototype.replaceState = function replaceState (state) {\n var this$1 = this;\n\n this._withCommit(function () {\n this$1._vm._data.$$state = state;\n });\n};\n\nStore.prototype.registerModule = function registerModule (path, rawModule, options) {\n if ( options === void 0 ) options = {};\n\n if (typeof path === 'string') { path = [path]; }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(Array.isArray(path), \"module path must be a string or an Array.\");\n assert(path.length > 0, 'cannot register the root module by using registerModule.');\n }\n\n this._modules.register(path, rawModule);\n installModule(this, this.state, path, this._modules.get(path), options.preserveState);\n // reset store to update getters...\n resetStoreVM(this, this.state);\n};\n\nStore.prototype.unregisterModule = function unregisterModule (path) {\n var this$1 = this;\n\n if (typeof path === 'string') { path = [path]; }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(Array.isArray(path), \"module path must be a string or an Array.\");\n }\n\n this._modules.unregister(path);\n this._withCommit(function () {\n var parentState = getNestedState(this$1.state, path.slice(0, -1));\n Vue.delete(parentState, path[path.length - 1]);\n });\n resetStore(this);\n};\n\nStore.prototype.hotUpdate = function hotUpdate (newOptions) {\n this._modules.update(newOptions);\n resetStore(this, true);\n};\n\nStore.prototype._withCommit = function _withCommit (fn) {\n var committing = this._committing;\n this._committing = true;\n fn();\n this._committing = committing;\n};\n\nObject.defineProperties( Store.prototype, prototypeAccessors$1 );\n\nfunction genericSubscribe (fn, subs) {\n if (subs.indexOf(fn) < 0) {\n subs.push(fn);\n }\n return function () {\n var i = subs.indexOf(fn);\n if (i > -1) {\n subs.splice(i, 1);\n }\n }\n}\n\nfunction resetStore (store, hot) {\n store._actions = Object.create(null);\n store._mutations = Object.create(null);\n store._wrappedGetters = Object.create(null);\n store._modulesNamespaceMap = Object.create(null);\n var state = store.state;\n // init all modules\n installModule(store, state, [], store._modules.root, true);\n // reset vm\n resetStoreVM(store, state, hot);\n}\n\nfunction resetStoreVM (store, state, hot) {\n var oldVm = store._vm;\n\n // bind store public getters\n store.getters = {};\n var wrappedGetters = store._wrappedGetters;\n var computed = {};\n forEachValue(wrappedGetters, function (fn, key) {\n // use computed to leverage its lazy-caching mechanism\n computed[key] = function () { return fn(store); };\n Object.defineProperty(store.getters, key, {\n get: function () { return store._vm[key]; },\n enumerable: true // for local getters\n });\n });\n\n // use a Vue instance to store the state tree\n // suppress warnings just in case the user has added\n // some funky global mixins\n var silent = Vue.config.silent;\n Vue.config.silent = true;\n store._vm = new Vue({\n data: {\n $$state: state\n },\n computed: computed\n });\n Vue.config.silent = silent;\n\n // enable strict mode for new vm\n if (store.strict) {\n enableStrictMode(store);\n }\n\n if (oldVm) {\n if (hot) {\n // dispatch changes in all subscribed watchers\n // to force getter re-evaluation for hot reloading.\n store._withCommit(function () {\n oldVm._data.$$state = null;\n });\n }\n Vue.nextTick(function () { return oldVm.$destroy(); });\n }\n}\n\nfunction installModule (store, rootState, path, module, hot) {\n var isRoot = !path.length;\n var namespace = store._modules.getNamespace(path);\n\n // register in namespace map\n if (module.namespaced) {\n store._modulesNamespaceMap[namespace] = module;\n }\n\n // set state\n if (!isRoot && !hot) {\n var parentState = getNestedState(rootState, path.slice(0, -1));\n var moduleName = path[path.length - 1];\n store._withCommit(function () {\n Vue.set(parentState, moduleName, module.state);\n });\n }\n\n var local = module.context = makeLocalContext(store, namespace, path);\n\n module.forEachMutation(function (mutation, key) {\n var namespacedType = namespace + key;\n registerMutation(store, namespacedType, mutation, local);\n });\n\n module.forEachAction(function (action, key) {\n var type = action.root ? key : namespace + key;\n var handler = action.handler || action;\n registerAction(store, type, handler, local);\n });\n\n module.forEachGetter(function (getter, key) {\n var namespacedType = namespace + key;\n registerGetter(store, namespacedType, getter, local);\n });\n\n module.forEachChild(function (child, key) {\n installModule(store, rootState, path.concat(key), child, hot);\n });\n}\n\n/**\n * make localized dispatch, commit, getters and state\n * if there is no namespace, just use root ones\n */\nfunction makeLocalContext (store, namespace, path) {\n var noNamespace = namespace === '';\n\n var local = {\n dispatch: noNamespace ? store.dispatch : function (_type, _payload, _options) {\n var args = unifyObjectStyle(_type, _payload, _options);\n var payload = args.payload;\n var options = args.options;\n var type = args.type;\n\n if (!options || !options.root) {\n type = namespace + type;\n if (process.env.NODE_ENV !== 'production' && !store._actions[type]) {\n console.error((\"[vuex] unknown local action type: \" + (args.type) + \", global type: \" + type));\n return\n }\n }\n\n return store.dispatch(type, payload)\n },\n\n commit: noNamespace ? store.commit : function (_type, _payload, _options) {\n var args = unifyObjectStyle(_type, _payload, _options);\n var payload = args.payload;\n var options = args.options;\n var type = args.type;\n\n if (!options || !options.root) {\n type = namespace + type;\n if (process.env.NODE_ENV !== 'production' && !store._mutations[type]) {\n console.error((\"[vuex] unknown local mutation type: \" + (args.type) + \", global type: \" + type));\n return\n }\n }\n\n store.commit(type, payload, options);\n }\n };\n\n // getters and state object must be gotten lazily\n // because they will be changed by vm update\n Object.defineProperties(local, {\n getters: {\n get: noNamespace\n ? function () { return store.getters; }\n : function () { return makeLocalGetters(store, namespace); }\n },\n state: {\n get: function () { return getNestedState(store.state, path); }\n }\n });\n\n return local\n}\n\nfunction makeLocalGetters (store, namespace) {\n var gettersProxy = {};\n\n var splitPos = namespace.length;\n Object.keys(store.getters).forEach(function (type) {\n // skip if the target getter is not match this namespace\n if (type.slice(0, splitPos) !== namespace) { return }\n\n // extract local getter type\n var localType = type.slice(splitPos);\n\n // Add a port to the getters proxy.\n // Define as getter property because\n // we do not want to evaluate the getters in this time.\n Object.defineProperty(gettersProxy, localType, {\n get: function () { return store.getters[type]; },\n enumerable: true\n });\n });\n\n return gettersProxy\n}\n\nfunction registerMutation (store, type, handler, local) {\n var entry = store._mutations[type] || (store._mutations[type] = []);\n entry.push(function wrappedMutationHandler (payload) {\n handler.call(store, local.state, payload);\n });\n}\n\nfunction registerAction (store, type, handler, local) {\n var entry = store._actions[type] || (store._actions[type] = []);\n entry.push(function wrappedActionHandler (payload, cb) {\n var res = handler.call(store, {\n dispatch: local.dispatch,\n commit: local.commit,\n getters: local.getters,\n state: local.state,\n rootGetters: store.getters,\n rootState: store.state\n }, payload, cb);\n if (!isPromise(res)) {\n res = Promise.resolve(res);\n }\n if (store._devtoolHook) {\n return res.catch(function (err) {\n store._devtoolHook.emit('vuex:error', err);\n throw err\n })\n } else {\n return res\n }\n });\n}\n\nfunction registerGetter (store, type, rawGetter, local) {\n if (store._wrappedGetters[type]) {\n if (process.env.NODE_ENV !== 'production') {\n console.error((\"[vuex] duplicate getter key: \" + type));\n }\n return\n }\n store._wrappedGetters[type] = function wrappedGetter (store) {\n return rawGetter(\n local.state, // local state\n local.getters, // local getters\n store.state, // root state\n store.getters // root getters\n )\n };\n}\n\nfunction enableStrictMode (store) {\n store._vm.$watch(function () { return this._data.$$state }, function () {\n if (process.env.NODE_ENV !== 'production') {\n assert(store._committing, \"do not mutate vuex store state outside mutation handlers.\");\n }\n }, { deep: true, sync: true });\n}\n\nfunction getNestedState (state, path) {\n return path.length\n ? path.reduce(function (state, key) { return state[key]; }, state)\n : state\n}\n\nfunction unifyObjectStyle (type, payload, options) {\n if (isObject(type) && type.type) {\n options = payload;\n payload = type;\n type = type.type;\n }\n\n if (process.env.NODE_ENV !== 'production') {\n assert(typeof type === 'string', (\"expects string as the type, but found \" + (typeof type) + \".\"));\n }\n\n return { type: type, payload: payload, options: options }\n}\n\nfunction install (_Vue) {\n if (Vue && _Vue === Vue) {\n if (process.env.NODE_ENV !== 'production') {\n console.error(\n '[vuex] already installed. Vue.use(Vuex) should be called only once.'\n );\n }\n return\n }\n Vue = _Vue;\n applyMixin(Vue);\n}\n\n/**\n * Reduce the code which written in Vue.js for getting the state.\n * @param {String} [namespace] - Module's namespace\n * @param {Object|Array} states # Object's item can be a function which accept state and getters for param, you can do something for state and getters in it.\n * @param {Object}\n */\nvar mapState = normalizeNamespace(function (namespace, states) {\n var res = {};\n normalizeMap(states).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedState () {\n var state = this.$store.state;\n var getters = this.$store.getters;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapState', namespace);\n if (!module) {\n return\n }\n state = module.context.state;\n getters = module.context.getters;\n }\n return typeof val === 'function'\n ? val.call(this, state, getters)\n : state[val]\n };\n // mark vuex getter for devtools\n res[key].vuex = true;\n });\n return res\n});\n\n/**\n * Reduce the code which written in Vue.js for committing the mutation\n * @param {String} [namespace] - Module's namespace\n * @param {Object|Array} mutations # Object's item can be a function which accept `commit` function as the first param, it can accept anthor params. You can commit mutation and do any other things in this function. specially, You need to pass anthor params from the mapped function.\n * @return {Object}\n */\nvar mapMutations = normalizeNamespace(function (namespace, mutations) {\n var res = {};\n normalizeMap(mutations).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedMutation () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n // Get the commit method from store\n var commit = this.$store.commit;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapMutations', namespace);\n if (!module) {\n return\n }\n commit = module.context.commit;\n }\n return typeof val === 'function'\n ? val.apply(this, [commit].concat(args))\n : commit.apply(this.$store, [val].concat(args))\n };\n });\n return res\n});\n\n/**\n * Reduce the code which written in Vue.js for getting the getters\n * @param {String} [namespace] - Module's namespace\n * @param {Object|Array} getters\n * @return {Object}\n */\nvar mapGetters = normalizeNamespace(function (namespace, getters) {\n var res = {};\n normalizeMap(getters).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n // The namespace has been mutated by normalizeNamespace\n val = namespace + val;\n res[key] = function mappedGetter () {\n if (namespace && !getModuleByNamespace(this.$store, 'mapGetters', namespace)) {\n return\n }\n if (process.env.NODE_ENV !== 'production' && !(val in this.$store.getters)) {\n console.error((\"[vuex] unknown getter: \" + val));\n return\n }\n return this.$store.getters[val]\n };\n // mark vuex getter for devtools\n res[key].vuex = true;\n });\n return res\n});\n\n/**\n * Reduce the code which written in Vue.js for dispatch the action\n * @param {String} [namespace] - Module's namespace\n * @param {Object|Array} actions # Object's item can be a function which accept `dispatch` function as the first param, it can accept anthor params. You can dispatch action and do any other things in this function. specially, You need to pass anthor params from the mapped function.\n * @return {Object}\n */\nvar mapActions = normalizeNamespace(function (namespace, actions) {\n var res = {};\n normalizeMap(actions).forEach(function (ref) {\n var key = ref.key;\n var val = ref.val;\n\n res[key] = function mappedAction () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n // get dispatch function from store\n var dispatch = this.$store.dispatch;\n if (namespace) {\n var module = getModuleByNamespace(this.$store, 'mapActions', namespace);\n if (!module) {\n return\n }\n dispatch = module.context.dispatch;\n }\n return typeof val === 'function'\n ? val.apply(this, [dispatch].concat(args))\n : dispatch.apply(this.$store, [val].concat(args))\n };\n });\n return res\n});\n\n/**\n * Rebinding namespace param for mapXXX function in special scoped, and return them by simple object\n * @param {String} namespace\n * @return {Object}\n */\nvar createNamespacedHelpers = function (namespace) { return ({\n mapState: mapState.bind(null, namespace),\n mapGetters: mapGetters.bind(null, namespace),\n mapMutations: mapMutations.bind(null, namespace),\n mapActions: mapActions.bind(null, namespace)\n}); };\n\n/**\n * Normalize the map\n * normalizeMap([1, 2, 3]) => [ { key: 1, val: 1 }, { key: 2, val: 2 }, { key: 3, val: 3 } ]\n * normalizeMap({a: 1, b: 2, c: 3}) => [ { key: 'a', val: 1 }, { key: 'b', val: 2 }, { key: 'c', val: 3 } ]\n * @param {Array|Object} map\n * @return {Object}\n */\nfunction normalizeMap (map) {\n return Array.isArray(map)\n ? map.map(function (key) { return ({ key: key, val: key }); })\n : Object.keys(map).map(function (key) { return ({ key: key, val: map[key] }); })\n}\n\n/**\n * Return a function expect two param contains namespace and map. it will normalize the namespace and then the param's function will handle the new namespace and the map.\n * @param {Function} fn\n * @return {Function}\n */\nfunction normalizeNamespace (fn) {\n return function (namespace, map) {\n if (typeof namespace !== 'string') {\n map = namespace;\n namespace = '';\n } else if (namespace.charAt(namespace.length - 1) !== '/') {\n namespace += '/';\n }\n return fn(namespace, map)\n }\n}\n\n/**\n * Search a special module from store by namespace. if module not exist, print error message.\n * @param {Object} store\n * @param {String} helper\n * @param {String} namespace\n * @return {Object}\n */\nfunction getModuleByNamespace (store, helper, namespace) {\n var module = store._modulesNamespaceMap[namespace];\n if (process.env.NODE_ENV !== 'production' && !module) {\n console.error((\"[vuex] module namespace not found in \" + helper + \"(): \" + namespace));\n }\n return module\n}\n\nvar index_esm = {\n Store: Store,\n install: install,\n version: '3.1.0',\n mapState: mapState,\n mapMutations: mapMutations,\n mapGetters: mapGetters,\n mapActions: mapActions,\n createNamespacedHelpers: createNamespacedHelpers\n};\n\nexport default index_esm;\nexport { Store, install, mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers };\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./node_modules/vuex/dist/vuex.esm.js\n// module id = 66\n// module chunks = 0"],"sourceRoot":""}
\ No newline at end of file diff --git a/third_party/webrender/debugger/index.html b/third_party/webrender/debugger/index.html new file mode 100644 index 00000000000..5ac80cebb23 --- /dev/null +++ b/third_party/webrender/debugger/index.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>WebRender Debugger</title> + </head> + <body> + <div id="app"></div> + <script src="dist/build.js"></script> + </body> +</html> diff --git a/third_party/webrender/debugger/package-lock.json b/third_party/webrender/debugger/package-lock.json new file mode 100644 index 00000000000..d5fdd52fd93 --- /dev/null +++ b/third_party/webrender/debugger/package-lock.json @@ -0,0 +1,7606 @@ +{ + "name": "debugger", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "dev": true, + "requires": { + "mime-types": "~2.1.18", + "negotiator": "0.6.1" + } + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "dev": true, + "requires": { + "acorn": "^4.0.3" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } + } + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==", + "dev": true + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + } + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", + "dev": true + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "autoprefixer": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", + "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", + "dev": true, + "requires": { + "browserslist": "^1.7.6", + "caniuse-db": "^1.0.30000634", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^5.2.16", + "postcss-value-parser": "^3.2.3" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + } + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-loader": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-7.1.5.tgz", + "integrity": "sha512-iCHfbieL5d1LfOQeeVJEUyD9rTwBcP/fcEbRCfempxTDuqrKpu0AZjLAQHEQa3Yqyj9ORKe2iHfoj4rHLf7xpw==", + "dev": true, + "requires": { + "find-cache-dir": "^1.0.0", + "loader-utils": "^1.0.2", + "mkdirp": "^0.5.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-async-generators": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz", + "integrity": "sha1-a8lj67FuzLrmuStZbrfzXDQqi5o=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", + "integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-generator-functions": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz", + "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-generators": "^6.5.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz", + "integrity": "sha1-DzZpLVD+9rfi1LOsFHgTepY7ewY=", + "dev": true, + "requires": { + "babel-plugin-syntax-object-rest-spread": "^6.8.0", + "babel-runtime": "^6.26.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^3.2.6", + "invariant": "^2.2.2", + "semver": "^5.3.0" + } + }, + "babel-preset-stage-3": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz", + "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", + "dev": true, + "requires": { + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-generator-functions": "^6.24.1", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-exponentiation-operator": "^6.24.1", + "babel-plugin-transform-object-rest-spread": "^6.22.0" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "base64-js": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "bluebird": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", + "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", + "dev": true + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true + }, + "body-parser": { + "version": "1.18.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", + "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "dev": true, + "requires": { + "bytes": "3.0.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "~1.6.3", + "iconv-lite": "0.4.23", + "on-finished": "~2.3.0", + "qs": "6.5.2", + "raw-body": "2.3.3", + "type-is": "~1.6.16" + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "dev": true, + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "buefy": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/buefy/-/buefy-0.6.7.tgz", + "integrity": "sha512-/d8srEpXAXWQjquHNP3ic9AVzwj8ar+jHfc7YjbspHT1AVeY3CDmzY2R1Am8ICnhioKAjRUfIie2/I9DiytXMQ==", + "requires": { + "bulma": "0.7.1" + } + }, + "buffer": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bulma": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.1.tgz", + "integrity": "sha512-wRSO2LXB+qI9Pyz2id+uZr4quz5aftSN7Ay1ysr1+krzVp3utD+Ci4CeKuZdrYGc800t65b7heXBL6qw2Wo/lQ==" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "caniuse-api": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", + "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", + "dev": true, + "requires": { + "browserslist": "^1.3.6", + "caniuse-db": "^1.0.30000529", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + } + } + }, + "caniuse-db": { + "version": "1.0.30000960", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000960.tgz", + "integrity": "sha512-lgvTGHSJcROw38PaUzjAA1M3VA9hBkGsrTfo+Uiq6jSqfQXRSflpcQsM3MbytlFwBhPH6VD0GwLPihJgjqWrew==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30000960", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000960.tgz", + "integrity": "sha512-7nK5qs17icQaX6V3/RYrJkOsZyRNnroA4+ZwxaKJzIKy+crIy0Mz5CBlLySd2SNV+4nbUZeqeNfiaEieUBu3aA==", + "dev": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "chokidar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", + "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", + "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", + "dev": true, + "requires": { + "chalk": "^1.1.3" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "coa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", + "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", + "dev": true, + "requires": { + "q": "^1.1.2" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", + "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", + "dev": true, + "requires": { + "clone": "^1.0.2", + "color-convert": "^1.3.0", + "color-string": "^0.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true, + "requires": { + "color-name": "^1.0.0" + } + }, + "colormin": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", + "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", + "dev": true, + "requires": { + "color": "^0.11.0", + "css-color-names": "0.0.4", + "has": "^1.0.1" + } + }, + "colors": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "compressible": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz", + "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==", + "dev": true, + "requires": { + "mime-db": ">= 1.38.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "consolidate": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.14.5.tgz", + "integrity": "sha1-WiUEe8dvcwcmZ8jLUsmJiI9JTGM=", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", + "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", + "dev": true, + "requires": { + "is-directory": "^0.3.1", + "js-yaml": "^3.4.3", + "minimist": "^1.2.0", + "object-assign": "^4.1.0", + "os-homedir": "^1.0.1", + "parse-json": "^2.2.0", + "require-from-string": "^1.1.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-env": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-5.2.0.tgz", + "integrity": "sha512-jtdNFfFW1hB7sMhr/H6rW1Z45LFqyI431m3qU6bFXcQ3Eh7LtBuG3h74o7ohHZ3crrRkkqHlo4jYHFPcjroANg==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5", + "is-windows": "^1.0.0" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", + "dev": true + }, + "css-loader": { + "version": "0.28.11", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz", + "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "css-selector-tokenizer": "^0.7.0", + "cssnano": "^3.10.0", + "icss-utils": "^2.1.0", + "loader-utils": "^1.0.2", + "lodash.camelcase": "^4.3.0", + "object-assign": "^4.1.1", + "postcss": "^5.0.6", + "postcss-modules-extract-imports": "^1.2.0", + "postcss-modules-local-by-default": "^1.2.0", + "postcss-modules-scope": "^1.1.0", + "postcss-modules-values": "^1.3.0", + "postcss-value-parser": "^3.3.0", + "source-list-map": "^2.0.0" + } + }, + "css-selector-tokenizer": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", + "dev": true, + "requires": { + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" + }, + "dependencies": { + "regexpu-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", + "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + } + } + }, + "cssesc": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", + "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", + "dev": true + }, + "cssnano": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", + "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", + "dev": true, + "requires": { + "autoprefixer": "^6.3.1", + "decamelize": "^1.1.2", + "defined": "^1.0.0", + "has": "^1.0.1", + "object-assign": "^4.0.1", + "postcss": "^5.0.14", + "postcss-calc": "^5.2.0", + "postcss-colormin": "^2.1.8", + "postcss-convert-values": "^2.3.4", + "postcss-discard-comments": "^2.0.4", + "postcss-discard-duplicates": "^2.0.1", + "postcss-discard-empty": "^2.0.1", + "postcss-discard-overridden": "^0.1.1", + "postcss-discard-unused": "^2.2.1", + "postcss-filter-plugins": "^2.0.0", + "postcss-merge-idents": "^2.1.5", + "postcss-merge-longhand": "^2.0.1", + "postcss-merge-rules": "^2.0.3", + "postcss-minify-font-values": "^1.0.2", + "postcss-minify-gradients": "^1.0.1", + "postcss-minify-params": "^1.0.4", + "postcss-minify-selectors": "^2.0.4", + "postcss-normalize-charset": "^1.1.0", + "postcss-normalize-url": "^3.0.7", + "postcss-ordered-values": "^2.1.0", + "postcss-reduce-idents": "^2.2.2", + "postcss-reduce-initial": "^1.0.0", + "postcss-reduce-transforms": "^1.0.3", + "postcss-svgo": "^2.1.1", + "postcss-unique-selectors": "^2.0.2", + "postcss-value-parser": "^3.2.3", + "postcss-zindex": "^2.0.1" + } + }, + "csso": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", + "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", + "dev": true, + "requires": { + "clap": "^1.0.9", + "source-map": "^0.5.3" + } + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "dev": true, + "requires": { + "es5-ext": "^0.10.9" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", + "dev": true, + "requires": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", + "dev": true + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.124", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.124.tgz", + "integrity": "sha512-glecGr/kFdfeXUHOHAWvGcXrxNU+1wSO/t5B23tT1dtlvYB26GY8aHzZSWD7HqhqC800Lr+w/hQul6C5AF542w==", + "dev": true + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "dev": true, + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.49", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.49.tgz", + "integrity": "sha512-3NMEhi57E31qdzmYp2jwRArIUsj1HI/RxbQ4bgnSB+AIKIxsAmTiK83bYMifIcpWvEc3P1X30DhUKOqEtF/kvg==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "^1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "~0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "dev": true, + "requires": { + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "eventemitter3": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "dev": true + }, + "events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", + "dev": true + }, + "eventsource": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", + "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "dev": true, + "requires": { + "original": ">=0.0.5" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "express": { + "version": "4.16.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz", + "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "array-flatten": "1.1.1", + "body-parser": "1.18.3", + "content-disposition": "0.5.2", + "content-type": "~1.0.4", + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.1.1", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.4", + "qs": "6.5.2", + "range-parser": "~1.2.0", + "safe-buffer": "5.1.2", + "send": "0.16.2", + "serve-static": "1.13.2", + "setprototypeof": "1.1.0", + "statuses": "~1.4.0", + "type-is": "~1.6.16", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==", + "dev": true + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "file-loader": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-1.1.11.tgz", + "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", + "dev": true, + "requires": { + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" + } + }, + "find-cache-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", + "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flatten": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", + "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", + "dev": true + }, + "follow-redirects": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz", + "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==", + "dev": true, + "requires": { + "debug": "^3.2.6" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.8.tgz", + "integrity": "sha512-tPvHgPGB7m40CZ68xqFGkKuzN+RnpGmSV+hgeKxhRpbxdqKXUFJGC3yonBOLzQBcJyGpdZFDfCsdOC2KFsXzeA==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", + "dev": true + }, + "handle-thing": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-comment-regex": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz", + "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==", + "dev": true + }, + "html-entities": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", + "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "http-parser-js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz", + "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==", + "dev": true + }, + "http-proxy": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "dev": true, + "requires": { + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-replace-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", + "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", + "dev": true + }, + "icss-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz", + "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "import-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", + "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "dev": true, + "requires": { + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "internal-ip": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-1.2.0.tgz", + "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", + "dev": true, + "requires": { + "meow": "^3.3.0" + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "dev": true, + "requires": { + "is-path-inside": "^1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-svg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", + "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", + "dev": true, + "requires": { + "html-comment-regex": "^1.1.0" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "js-base64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", + "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + }, + "json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "loglevel": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", + "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "math-expression-evaluator": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", + "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "mime-db": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.39.0.tgz", + "integrity": "sha512-DTsrw/iWVvwHH+9Otxccdyy0Tgiil6TWK/xhfARJZF/QFhwOgZgOIvA2/VIGpM8U7Q8z5nDmdDWC6tuVMJNibw==", + "dev": true + }, + "mime-types": { + "version": "2.1.23", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.23.tgz", + "integrity": "sha512-ROk/m+gMVSrRxTkMlaQOvFmFmYDc7sZgrjjM76abqmd2Cc5fCV7jAMA5XUccEtJ3cYiYdgixUVI+fApc2LkXlw==", + "dev": true, + "requires": { + "mime-db": "~1.39.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "nan": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", + "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "negotiator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", + "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "dev": true + }, + "neo-async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", + "dev": true + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-forge": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", + "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz", + "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.0", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "0.0.4" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "dev": true, + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "dev": true + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "pako": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", + "dev": true + }, + "parse-asn1": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz", + "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==", + "dev": true, + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "portfinder": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz", + "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==", + "dev": true, + "requires": { + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "js-base64": "^2.1.9", + "source-map": "^0.5.6", + "supports-color": "^3.2.3" + }, + "dependencies": { + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "postcss-calc": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", + "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", + "dev": true, + "requires": { + "postcss": "^5.0.2", + "postcss-message-helpers": "^2.0.0", + "reduce-css-calc": "^1.2.6" + } + }, + "postcss-colormin": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", + "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", + "dev": true, + "requires": { + "colormin": "^1.0.5", + "postcss": "^5.0.13", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-convert-values": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", + "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", + "dev": true, + "requires": { + "postcss": "^5.0.11", + "postcss-value-parser": "^3.1.2" + } + }, + "postcss-discard-comments": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", + "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-duplicates": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", + "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-discard-empty": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", + "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", + "dev": true, + "requires": { + "postcss": "^5.0.14" + } + }, + "postcss-discard-overridden": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", + "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", + "dev": true, + "requires": { + "postcss": "^5.0.16" + } + }, + "postcss-discard-unused": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", + "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", + "dev": true, + "requires": { + "postcss": "^5.0.14", + "uniqs": "^2.0.0" + } + }, + "postcss-filter-plugins": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz", + "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-load-config": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-1.2.0.tgz", + "integrity": "sha1-U56a/J3chiASHr+djDZz4M5Q0oo=", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0", + "postcss-load-options": "^1.2.0", + "postcss-load-plugins": "^2.3.0" + } + }, + "postcss-load-options": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-load-options/-/postcss-load-options-1.2.0.tgz", + "integrity": "sha1-sJixVZ3awt8EvAuzdfmaXP4rbYw=", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.0", + "object-assign": "^4.1.0" + } + }, + "postcss-load-plugins": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz", + "integrity": "sha1-dFdoEWWZrKLwCfrUJrABdQSdjZI=", + "dev": true, + "requires": { + "cosmiconfig": "^2.1.1", + "object-assign": "^4.1.0" + } + }, + "postcss-merge-idents": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", + "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.10", + "postcss-value-parser": "^3.1.1" + } + }, + "postcss-merge-longhand": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", + "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-merge-rules": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", + "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", + "dev": true, + "requires": { + "browserslist": "^1.5.2", + "caniuse-api": "^1.5.2", + "postcss": "^5.0.4", + "postcss-selector-parser": "^2.2.2", + "vendors": "^1.0.0" + }, + "dependencies": { + "browserslist": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", + "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", + "dev": true, + "requires": { + "caniuse-db": "^1.0.30000639", + "electron-to-chromium": "^1.2.7" + } + } + } + }, + "postcss-message-helpers": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", + "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", + "dev": true + }, + "postcss-minify-font-values": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", + "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-minify-gradients": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", + "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", + "dev": true, + "requires": { + "postcss": "^5.0.12", + "postcss-value-parser": "^3.3.0" + } + }, + "postcss-minify-params": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", + "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.2", + "postcss-value-parser": "^3.0.2", + "uniqs": "^2.0.0" + } + }, + "postcss-minify-selectors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", + "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.2", + "has": "^1.0.1", + "postcss": "^5.0.14", + "postcss-selector-parser": "^2.0.0" + } + }, + "postcss-modules-extract-imports": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", + "dev": true, + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-local-by-default": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", + "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", + "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", + "dev": true, + "requires": { + "css-selector-tokenizer": "^0.7.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-modules-values": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", + "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", + "dev": true, + "requires": { + "icss-replace-symbols": "^1.1.0", + "postcss": "^6.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-normalize-charset": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", + "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", + "dev": true, + "requires": { + "postcss": "^5.0.5" + } + }, + "postcss-normalize-url": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", + "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^1.4.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3" + } + }, + "postcss-ordered-values": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", + "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-reduce-idents": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", + "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", + "dev": true, + "requires": { + "postcss": "^5.0.4", + "postcss-value-parser": "^3.0.2" + } + }, + "postcss-reduce-initial": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", + "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", + "dev": true, + "requires": { + "postcss": "^5.0.4" + } + }, + "postcss-reduce-transforms": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", + "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.8", + "postcss-value-parser": "^3.0.1" + } + }, + "postcss-selector-parser": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", + "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", + "dev": true, + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-svgo": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", + "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", + "dev": true, + "requires": { + "is-svg": "^2.0.0", + "postcss": "^5.0.14", + "postcss-value-parser": "^3.2.3", + "svgo": "^0.7.0" + } + }, + "postcss-unique-selectors": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", + "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "postcss-zindex": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", + "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", + "dev": true, + "requires": { + "has": "^1.0.1", + "postcss": "^5.0.4", + "uniqs": "^2.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "prettier": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.17.0.tgz", + "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==", + "dev": true + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "dev": true, + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=", + "dev": true + }, + "raw-body": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", + "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "dev": true, + "requires": { + "bytes": "3.0.0", + "http-errors": "1.6.3", + "iconv-lite": "0.4.23", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reduce-css-calc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", + "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", + "dev": true, + "requires": { + "balanced-match": "^0.4.2", + "math-expression-evaluator": "^1.2.14", + "reduce-function-call": "^1.0.1" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "reduce-function-call": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", + "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", + "dev": true, + "requires": { + "balanced-match": "^0.4.2" + }, + "dependencies": { + "balanced-match": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", + "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "dev": true + } + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-1.2.1.tgz", + "integrity": "sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", + "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "dev": true, + "requires": { + "node-forge": "0.7.5" + } + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + } + }, + "sockjs": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", + "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", + "dev": true, + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" + } + }, + "sockjs-client": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz", + "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=", + "dev": true, + "requires": { + "debug": "^2.6.6", + "eventsource": "0.1.6", + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.8" + }, + "dependencies": { + "faye-websocket": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", + "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", + "dev": true + }, + "spdy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz", + "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "readable-stream": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.3.0.tgz", + "integrity": "sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "svgo": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", + "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", + "dev": true, + "requires": { + "coa": "~1.0.1", + "colors": "~1.1.2", + "csso": "~2.3.1", + "js-yaml": "~3.7.0", + "mkdirp": "~0.5.1", + "sax": "~1.2.1", + "whet.extend": "~0.9.9" + }, + "dependencies": { + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "js-yaml": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", + "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^2.6.0" + } + } + } + }, + "tapable": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==", + "dev": true + }, + "thunky": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz", + "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", + "dev": true + }, + "time-stamp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-2.2.0.tgz", + "integrity": "sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", + "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "type-is": { + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.18" + } + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + }, + "dependencies": { + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "uglifyjs-webpack-plugin": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", + "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", + "dev": true, + "requires": { + "source-map": "^0.5.6", + "uglify-js": "^2.8.29", + "webpack-sources": "^1.0.1" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-parse": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.6.tgz", + "integrity": "sha512-/B8AD9iQ01seoXmXf9z/MjLZQIdOoYl/+gvsQF6+mpnxaTfG9P7srYaiqaDMyKkR36XMXfhqSHss5MyFAO8lew==", + "dev": true, + "requires": { + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vendors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz", + "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==", + "dev": true + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "dev": true, + "requires": { + "indexof": "0.0.1" + } + }, + "vue": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz", + "integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ==" + }, + "vue-hot-reload-api": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.3.tgz", + "integrity": "sha512-KmvZVtmM26BQOMK1rwUZsrqxEGeKiYSZGA7SNWE6uExx8UX/cj9hq2MRV/wWC3Cq6AoeDGk57rL9YMFRel/q+g==", + "dev": true + }, + "vue-loader": { + "version": "13.7.3", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-13.7.3.tgz", + "integrity": "sha512-ACCwbfeC6HjY2pnDii+Zer+MZ6sdOtwvLmDXRK/BoD3WNR551V22R6KEagwHoTRJ0ZlIhpCBkptpCU6+Ri/05w==", + "dev": true, + "requires": { + "consolidate": "^0.14.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "lru-cache": "^4.1.1", + "postcss": "^6.0.8", + "postcss-load-config": "^1.1.0", + "postcss-selector-parser": "^2.0.0", + "prettier": "^1.7.0", + "resolve": "^1.4.0", + "source-map": "^0.6.1", + "vue-hot-reload-api": "^2.2.0", + "vue-style-loader": "^3.0.0", + "vue-template-es2015-compiler": "^1.6.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "vue-material-design-icons": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/vue-material-design-icons/-/vue-material-design-icons-0.8.2.tgz", + "integrity": "sha512-/NSZtfgcracfNRGf564Du3vH2EQIvQWmlQ3t9Zbg+GAe5kvGMp+tqlY+Yxu9/ow/obxOauR6BRrkLQ07rtqxUA==" + }, + "vue-style-loader": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-3.1.2.tgz", + "integrity": "sha512-ICtVdK/p+qXWpdSs2alWtsXt9YnDoYjQe0w5616j9+/EhjoxZkbun34uWgsMFnC1MhrMMwaWiImz3K2jK1Yp2Q==", + "dev": true, + "requires": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "vue-template-compiler": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.10.tgz", + "integrity": "sha512-jVZkw4/I/HT5ZMvRnhv78okGusqe0+qH2A0Em0Cp8aq78+NK9TII263CDVz2QXZsIT+yyV/gZc/j/vlwa+Epyg==", + "dev": true, + "requires": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "vuex": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.1.0.tgz", + "integrity": "sha512-mdHeHT/7u4BncpUZMlxNaIdcN/HIt1GsGG5LKByArvYG/v6DvHcOxvDCts+7SRdCoIRGllK8IMZvQtQXLppDYg==" + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "dev": true, + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webpack": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.12.0.tgz", + "integrity": "sha512-Sw7MdIIOv/nkzPzee4o0EdvCuPmxT98+vVpIvwtcwcF1Q4SDSNp92vwcKc4REe7NItH9f1S4ra9FuQ7yuYZ8bQ==", + "dev": true, + "requires": { + "acorn": "^5.0.0", + "acorn-dynamic-import": "^2.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "async": "^2.1.2", + "enhanced-resolve": "^3.4.0", + "escope": "^3.6.0", + "interpret": "^1.0.0", + "json-loader": "^0.5.4", + "json5": "^0.5.1", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "mkdirp": "~0.5.0", + "node-libs-browser": "^2.0.0", + "source-map": "^0.5.3", + "supports-color": "^4.2.1", + "tapable": "^0.2.7", + "uglifyjs-webpack-plugin": "^0.4.6", + "watchpack": "^1.4.0", + "webpack-sources": "^1.0.1", + "yargs": "^8.0.2" + }, + "dependencies": { + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "^2.0.0" + } + } + } + }, + "webpack-dev-middleware": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz", + "integrity": "sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==", + "dev": true, + "requires": { + "memory-fs": "~0.4.1", + "mime": "^1.5.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "time-stamp": "^2.0.0" + }, + "dependencies": { + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + } + } + }, + "webpack-dev-server": { + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-2.11.5.tgz", + "integrity": "sha512-7TdOKKt7G3sWEhPKV0zP+nD0c4V9YKUJ3wDdBwQsZNo58oZIRoVIu66pg7PYkBW8A74msP9C2kLwmxGHndz/pw==", + "dev": true, + "requires": { + "ansi-html": "0.0.7", + "array-includes": "^3.0.3", + "bonjour": "^3.5.0", + "chokidar": "^2.1.2", + "compression": "^1.7.3", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "^0.19.1", + "import-local": "^1.0.0", + "internal-ip": "1.2.0", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "selfsigned": "^1.9.1", + "serve-index": "^1.9.1", + "sockjs": "0.3.19", + "sockjs-client": "1.1.5", + "spdy": "^4.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", + "webpack-dev-middleware": "1.12.2", + "yargs": "6.6.0" + }, + "dependencies": { + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "yargs": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^4.2.0" + } + }, + "yargs-parser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + } + } + }, + "webpack-sources": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "dev": true, + "requires": { + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", + "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==", + "dev": true + }, + "whet.extend": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", + "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", + "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "read-pkg-up": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^7.0.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + } + } + }, + "yargs-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", + "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + } + } + } + } +} diff --git a/third_party/webrender/debugger/package.json b/third_party/webrender/debugger/package.json new file mode 100644 index 00000000000..b7f9aadfc07 --- /dev/null +++ b/third_party/webrender/debugger/package.json @@ -0,0 +1,36 @@ +{ + "name": "debugger", + "description": "WebRender Debugger", + "version": "1.0.0", + "author": "Glenn Watson <github@intuitionlibrary.com>", + "license": "MIT", + "private": true, + "scripts": { + "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot", + "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" + }, + "dependencies": { + "buefy": "^0.6.7", + "vue": "^2.5.11", + "vue-material-design-icons": "^0.8.2", + "vuex": "^3.0.1" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ], + "devDependencies": { + "babel-core": "^6.26.0", + "babel-loader": "^7.1.2", + "babel-preset-env": "^1.6.0", + "babel-preset-stage-3": "^6.24.1", + "cross-env": "^5.0.5", + "css-loader": "^0.28.7", + "file-loader": "^1.1.4", + "vue-loader": "^13.0.5", + "vue-template-compiler": "^2.4.4", + "webpack": "^3.6.0", + "webpack-dev-server": "^2.9.1" + } +} diff --git a/third_party/webrender/debugger/src/App.vue b/third_party/webrender/debugger/src/App.vue new file mode 100644 index 00000000000..01d303e79e5 --- /dev/null +++ b/third_party/webrender/debugger/src/App.vue @@ -0,0 +1,55 @@ +<template> + <div> + <app-navbar></app-navbar> + <div class="section"> + <div class="container"> + <div class="columns"> + <div class="column is-3"> + <app-navmenu></app-navmenu> + </div> + <div class="column"> + <app-options v-if="page == 'options'"></app-options> + <app-passview v-if="page == 'passes'"></app-passview> + <app-rendertaskview v-if="page == 'render_tasks'"></app-rendertaskview> + <app-documentview v-if="page == 'documents'"></app-documentview> + <app-clipscrolltreeview v-if="page == 'clip_scroll_tree'"></app-clipscrolltreeview> + <app-screenshotview v-if="page == 'screenshot'"></app-screenshotview> + </div> + </div> + </div> + </div> + </div> +</template> + +<script> +import NavBar from './components/NavBar.vue' +import NavMenu from './components/NavMenu.vue' +import OptionsPage from './components/OptionsPage.vue' +import PassViewPage from './components/PassViewPage.vue' +import RenderTaskViewPage from './components/RenderTaskViewPage.vue' +import DocumentViewPage from './components/DocumentViewPage.vue' +import ClipScrollTreeViewPage from './components/ClipScrollTreeViewPage.vue' +import ScreenshotPage from './components/ScreenshotPage.vue' + +export default { + name: 'app', + components: { + 'app-navbar': NavBar, + 'app-navmenu': NavMenu, + 'app-options': OptionsPage, + 'app-passview': PassViewPage, + 'app-rendertaskview': RenderTaskViewPage, + 'app-documentview': DocumentViewPage, + 'app-clipscrolltreeview': ClipScrollTreeViewPage, + 'app-screenshotview': ScreenshotPage, + }, + computed: { + page() { + return this.$store.state.page; + } + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/ClipScrollTreeViewPage.vue b/third_party/webrender/debugger/src/components/ClipScrollTreeViewPage.vue new file mode 100644 index 00000000000..66a1edaf304 --- /dev/null +++ b/third_party/webrender/debugger/src/components/ClipScrollTreeViewPage.vue @@ -0,0 +1,37 @@ +<template> + <div class="box"> + <h1 class="title">Clip-Scroll Tree <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1> + <hr/> + <div> + <ul> + <app-treeview :model=clip_scroll_tree></app-treeview> + </ul> + </div> + </div> +</template> + +<script> +import TreeView from './TreeView.vue' + +export default { + components: { + 'app-treeview': TreeView, + }, + methods: { + fetch: function() { + this.$store.dispatch('sendMessage', "fetch_clip_scroll_tree"); + } + }, + computed: { + disabled() { + return !this.$store.state.connected + }, + clip_scroll_tree() { + return this.$store.state.clip_scroll_tree + } + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/DocumentViewPage.vue b/third_party/webrender/debugger/src/components/DocumentViewPage.vue new file mode 100644 index 00000000000..6d5f02da735 --- /dev/null +++ b/third_party/webrender/debugger/src/components/DocumentViewPage.vue @@ -0,0 +1,37 @@ +<template> + <div class="box"> + <h1 class="title">Documents <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1> + <hr/> + <div> + <ul> + <app-treeview :model=documents></app-treeview> + </ul> + </div> + </div> +</template> + +<script> +import TreeView from './TreeView.vue' + +export default { + components: { + 'app-treeview': TreeView, + }, + methods: { + fetch: function() { + this.$store.dispatch('sendMessage', "fetch_documents"); + } + }, + computed: { + disabled() { + return !this.$store.state.connected + }, + documents() { + return this.$store.state.documents + } + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/NavBar.vue b/third_party/webrender/debugger/src/components/NavBar.vue new file mode 100644 index 00000000000..2f8008d67d4 --- /dev/null +++ b/third_party/webrender/debugger/src/components/NavBar.vue @@ -0,0 +1,41 @@ +<template> + <nav class="navbar has-shadow"> + <div class="navbar-brand"> + <a class="navbar-item" href="#">WebRender Debugger</a> + </div> + + <div class="navbar-menu"> + <div class="navbar-start"></div> + + <div class="navbar-end"> + <div class="navbar-item"> + <p class="control"> + <button v-if="isConnected" @click="disconnect" class="button is-danger">Disconnect</button> + <button v-else @click="connect" class="button is-success">Connect</button> + </p> + </div> + </div> + </div> + </nav> +</template> + +<script> +export default { + computed: { + isConnected() { + return this.$store.state.connected; + }, + }, + methods: { + connect() { + this.$store.dispatch('connect'); + }, + disconnect() { + this.$store.dispatch('disconnect'); + }, + } +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/NavMenu.vue b/third_party/webrender/debugger/src/components/NavMenu.vue new file mode 100644 index 00000000000..5a7cc3512a4 --- /dev/null +++ b/third_party/webrender/debugger/src/components/NavMenu.vue @@ -0,0 +1,33 @@ +<template> + <aside class="menu"> + <p class="menu-label"> + Pages + </p> + <ul class="menu-list"> + <li><a @click="setPage('options')" :class="{ 'is-active': page == 'options' }">Debug Options</a></li> + <li><a @click="setPage('passes')" :class="{ 'is-active': page == 'passes' }">Passes</a></li> + <li><a @click="setPage('render_tasks')" :class="{ 'is-active': page == 'render_tasks' }">Render Tasks</a></li> + <li><a @click="setPage('documents')" :class="{ 'is-active': page == 'documents' }">Documents</a></li> + <li><a @click="setPage('clip_scroll_tree')" v-bind:class="{ 'is-active': page == 'clip_scroll_tree' }">Clip-Scroll Tree</a></li> + <li><a @click="setPage('screenshot')" v-bind:class="{ 'is-active': page == 'screenshot' }">Screenshot</a></li> + </ul> + </aside> +</template> + +<script> +export default { + methods: { + setPage(name) { + this.$store.commit('setPage', name); + }, + }, + computed: { + page() { + return this.$store.state.page; + } + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/OptionsPage.vue b/third_party/webrender/debugger/src/components/OptionsPage.vue new file mode 100644 index 00000000000..fd8288c42cc --- /dev/null +++ b/third_party/webrender/debugger/src/components/OptionsPage.vue @@ -0,0 +1,162 @@ +<template> + <div class="box"> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setProfiler($event.target.checked)"> + Profiler + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setTextureCacheDebugger($event.target.checked)"> + Texture cache debugger + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setRenderTargetDebugger($event.target.checked)"> + Render target debugger + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setAlphaRectsDebugger($event.target.checked)"> + Alpha primitive rects debugger + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setGpuTimeQueries($event.target.checked)"> + Enable GPU time queries + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setGpuSampleQueries($event.target.checked)"> + Enable GPU sample queries + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setOpaquePass(!$event.target.checked)"> + Disable opaque pass + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setAlphaPass(!$event.target.checked)"> + Disable alpha pass + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setClipMasks(!$event.target.checked)"> + Disable clip masks + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setTextPrims(!$event.target.checked)"> + Disable text primitives + </label> + </div> + <div class="field"> + <label class="checkbox"> + <input type="checkbox" :disabled="disabled" v-on:click="setGradientPrims(!$event.target.checked)"> + Disable gradient primitives + </label> + </div> + </div> +</template> + +<script> +export default { + computed: { + disabled() { + return !this.$store.state.connected + } + }, + methods: { + setProfiler(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_profiler"); + } else { + this.$store.dispatch('sendMessage', "disable_profiler"); + } + }, + setTextureCacheDebugger(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_texture_cache_debug"); + } else { + this.$store.dispatch('sendMessage', "disable_texture_cache_debug"); + } + }, + setRenderTargetDebugger(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_render_target_debug"); + } else { + this.$store.dispatch('sendMessage', "disable_render_target_debug"); + } + }, + setAlphaRectsDebugger(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_alpha_rects_debug"); + } else { + this.$store.dispatch('sendMessage', "disable_alpha_rects_debug"); + } + }, + setGpuTimeQueries(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_gpu_time_queries"); + } else { + this.$store.dispatch('sendMessage', "disable_gpu_time_queries"); + } + }, + setGpuSampleQueries(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_gpu_sample_queries"); + } else { + this.$store.dispatch('sendMessage', "disable_gpu_sample_queries"); + } + }, + setOpaquePass(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_opaque_pass"); + } else { + this.$store.dispatch('sendMessage', "disable_opaque_pass"); + } + }, + setAlphaPass(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_alpha_pass"); + } else { + this.$store.dispatch('sendMessage', "disable_alpha_pass"); + } + }, + setClipMasks(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_clip_masks"); + } else { + this.$store.dispatch('sendMessage', "disable_clip_masks"); + } + }, + setTextPrims(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_text_prims"); + } else { + this.$store.dispatch('sendMessage', "disable_text_prims"); + } + }, + setGradientPrims(enabled) { + if (enabled) { + this.$store.dispatch('sendMessage', "enable_gradient_prims"); + } else { + this.$store.dispatch('sendMessage', "disable_gradient_prims"); + } + } + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/PassViewPage.vue b/third_party/webrender/debugger/src/components/PassViewPage.vue new file mode 100644 index 00000000000..0ab6d7c4464 --- /dev/null +++ b/third_party/webrender/debugger/src/components/PassViewPage.vue @@ -0,0 +1,37 @@ +<template> + <div class="box"> + <h1 class="title">Passes <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1> + <hr/> + <div v-for="(pass, pass_index) in passes"> + <p class="has-text-black-bis">Pass {{pass_index}}</p> + <div v-for="(target, target_index) in pass.targets"> + <p style="text-indent: 2em;" class="has-text-grey-dark">Target {{target_index}} ({{target.kind}})</p> + <div v-for="(batch, batch_index) in target.batches"> + <p style="text-indent: 4em;" class="has-text-grey">Batch {{batch_index}} ({{batch.description}}, {{batch.kind}}, {{batch.count}} instances)</p> + </div> + </div> + <hr/> + </div> + </div> +</template> + +<script> +export default { + methods: { + fetch: function() { + this.$store.dispatch('sendMessage', "fetch_passes"); + } + }, + computed: { + disabled() { + return !this.$store.state.connected + }, + passes() { + return this.$store.state.passes + } + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/RenderTaskViewPage.vue b/third_party/webrender/debugger/src/components/RenderTaskViewPage.vue new file mode 100644 index 00000000000..c3937fe0ce2 --- /dev/null +++ b/third_party/webrender/debugger/src/components/RenderTaskViewPage.vue @@ -0,0 +1,37 @@ +<template> + <div class="box"> + <h1 class="title">Render Tasks <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1> + <hr/> + <div> + <ul> + <app-treeview :model=render_tasks></app-treeview> + </ul> + </div> + </div> +</template> + +<script> +import TreeView from './TreeView.vue' + +export default { + components: { + 'app-treeview': TreeView, + }, + methods: { + fetch: function() { + this.$store.dispatch('sendMessage', "fetch_render_tasks"); + } + }, + computed: { + disabled() { + return !this.$store.state.connected + }, + render_tasks() { + return this.$store.state.render_tasks + } + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/ScreenshotPage.vue b/third_party/webrender/debugger/src/components/ScreenshotPage.vue new file mode 100644 index 00000000000..8d4017856e4 --- /dev/null +++ b/third_party/webrender/debugger/src/components/ScreenshotPage.vue @@ -0,0 +1,32 @@ +<template> + <div class="box"> + <h1 class="title">Screenshot <a :disabled="disabled" v-on:click="fetch" class="button is-info">Refresh</a></h1> + <hr/> + <div> + <ul> + <img v-if="screenshot.length > 0" style="transform: scaleY(-1); width: 1024px; height:768px" :src="'data:image/png;base64,' + screenshot" /> + </ul> + </div> + </div> +</template> + +<script> +export default { + computed: { + disabled() { + return !this.$store.state.connected + }, + screenshot() { + return this.$store.state.screenshot + }, + }, + methods: { + fetch: function() { + this.$store.dispatch('sendMessage', "fetch_screenshot"); + } + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/components/TreeView.vue b/third_party/webrender/debugger/src/components/TreeView.vue new file mode 100644 index 00000000000..bde473bda2f --- /dev/null +++ b/third_party/webrender/debugger/src/components/TreeView.vue @@ -0,0 +1,40 @@ +<template> + <li> + <div v-on:click="toggle"> + <span v-if="isFolder">[{{open ? '-' : '+'}}]</span> + {{model.description}} + </div> + <ul style="padding-left: 1em; line-height: 1.5em;" v-show="open" v-if="isFolder"> + <treeview v-for="model in model.children" :model="model"></treeview> + </ul> + </li> +</template> + +<script> +export default { + name: 'treeview', + props: [ + 'model', + ], + data: function () { + return { + open: false + } + }, + computed: { + isFolder: function () { + return this.model.children && this.model.children.length + } + }, + methods: { + toggle: function () { + if (this.isFolder) { + this.open = !this.open + } + }, + }, +} +</script> + +<style> +</style> diff --git a/third_party/webrender/debugger/src/main.js b/third_party/webrender/debugger/src/main.js new file mode 100644 index 00000000000..1259c32a0f3 --- /dev/null +++ b/third_party/webrender/debugger/src/main.js @@ -0,0 +1,14 @@ +import Vue from 'vue'; +import Buefy from 'buefy'; +import 'buefy/dist/buefy.css'; +import "vue-material-design-icons/styles.css"; +import App from './App.vue'; +import store from './store'; + +Vue.use(Buefy); + +new Vue({ + el: '#app', + store, + render: h => h(App) +}) diff --git a/third_party/webrender/debugger/src/store/index.js b/third_party/webrender/debugger/src/store/index.js new file mode 100644 index 00000000000..7749d7a8416 --- /dev/null +++ b/third_party/webrender/debugger/src/store/index.js @@ -0,0 +1,105 @@ +import Vue from 'vue' +import Vuex from 'vuex' + +Vue.use(Vuex) + +class Connection { + constructor() { + this.ws = null; + } + + connect(context) { + var ws = new WebSocket("ws://127.0.0.1:3583"); + + ws.onopen = function() { + context.commit('setConnected', true); + } + + ws.onmessage = function(evt) { + var json = JSON.parse(evt.data); + if (json['kind'] == "passes") { + context.commit('setPasses', json['passes']); + } else if (json['kind'] == "render_tasks") { + context.commit('setRenderTasks', json['root']); + } else if (json['kind'] == "documents") { + context.commit('setDocuments', json['root']); + } else if (json['kind'] == "clip_scroll_tree") { + context.commit('setClipScrollTree', json['root']); + } else if (json['kind'] == "screenshot") { + context.commit('setScreenshot', json['data']); + } else { + console.warn("unknown message kind: " + json['kind']); + } + } + + ws.onclose = function() { + context.commit('setConnected', false); + } + + this.ws = ws; + } + + send(msg) { + if (this.ws !== null) { + this.ws.send(msg); + } + } + + disconnect() { + if (this.ws !== null) { + this.ws.close(); + this.ws = null; + } + } +} + +var connection = new Connection(); + +const store = new Vuex.Store({ + strict: true, + state: { + connected: false, + page: 'options', + passes: [], + render_tasks: [], + documents: [], + clip_scroll_tree: [], + screenshot: [], + }, + mutations: { + setConnected(state, connected) { + state.connected = connected; + }, + setPage(state, name) { + state.page = name; + }, + setPasses(state, passes) { + state.passes = passes; + }, + setRenderTasks(state, render_tasks) { + state.render_tasks = render_tasks; + }, + setDocuments(state, documents) { + state.documents = documents; + }, + setClipScrollTree(state, clip_scroll_tree) { + state.clip_scroll_tree = clip_scroll_tree; + }, + setScreenshot(state, screenshot) { + state.screenshot = screenshot; + }, + }, + actions: { + connect(context) { + connection.connect(context); + }, + disconnect(context) { + connection.disconnect(); + }, + sendMessage(context, msg) { + connection.send(msg); + } + } +}); + +export default store; diff --git a/third_party/webrender/debugger/webpack.config.js b/third_party/webrender/debugger/webpack.config.js new file mode 100644 index 00000000000..6edcbfcc3c3 --- /dev/null +++ b/third_party/webrender/debugger/webpack.config.js @@ -0,0 +1,81 @@ +var path = require('path') +var webpack = require('webpack') + +module.exports = { + entry: './src/main.js', + output: { + path: path.resolve(__dirname, './dist'), + publicPath: '/dist/', + filename: 'build.js' + }, + module: { + rules: [ + { + test: /\.css$/, + use: [ + 'vue-style-loader', + 'css-loader' + ], + }, { + test: /\.vue$/, + loader: 'vue-loader', + options: { + loaders: { + } + // other vue-loader options go here + } + }, + { + test: /\.js$/, + loader: 'babel-loader', + exclude: /node_modules/ + }, + { + test: /\.(png|jpg|gif|svg)$/, + loader: 'file-loader', + options: { + name: '[name].[ext]?[hash]' + } + } + ] + }, + resolve: { + alias: { + 'vue$': 'vue/dist/vue.esm.js' + }, + alias : { + "icons": path.resolve(__dirname, "node_modules/vue-material-design-icons") + }, + extensions: ['*', '.js', '.vue', '.json'] + }, + devServer: { + historyApiFallback: true, + noInfo: true, + overlay: true + }, + performance: { + hints: false + }, + devtool: '#eval-source-map' +} + +if (process.env.NODE_ENV === 'production') { + module.exports.devtool = '#source-map' + // http://vue-loader.vuejs.org/en/workflow/production.html + module.exports.plugins = (module.exports.plugins || []).concat([ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: '"production"' + } + }), + new webpack.optimize.UglifyJsPlugin({ + sourceMap: true, + compress: { + warnings: false + } + }), + new webpack.LoaderOptionsPlugin({ + minimize: true + }) + ]) +} diff --git a/third_party/webrender/direct-composition/Cargo.toml b/third_party/webrender/direct-composition/Cargo.toml new file mode 100644 index 00000000000..59528f98ce2 --- /dev/null +++ b/third_party/webrender/direct-composition/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "direct-composition" +version = "0.1.0" +authors = ["Simon Sapin <simon.sapin@exyr.org>"] +license = "MPL-2.0" +edition = "2018" + +[target.'cfg(windows)'.dependencies] +euclid = "0.22" +gleam = "0.12" +mozangle = {version = "0.3.1", features = ["egl"]} +webrender = {path = "../webrender"} +winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]} +winit = "0.19" diff --git a/third_party/webrender/direct-composition/src/com.rs b/third_party/webrender/direct-composition/src/com.rs new file mode 100644 index 00000000000..8fb384695cc --- /dev/null +++ b/third_party/webrender/direct-composition/src/com.rs @@ -0,0 +1,112 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use std::ops; +use std::ptr; +use winapi::Interface; +use winapi::ctypes::c_void; +use winapi::shared::guiddef::GUID; +use winapi::shared::winerror::HRESULT; +use winapi::shared::winerror::SUCCEEDED; +use winapi::um::unknwnbase::IUnknown; + +pub fn as_ptr<T>(x: &T) -> *mut T { + x as *const T as _ +} + +pub trait CheckHResult { + fn check_hresult(self); +} + +impl CheckHResult for HRESULT { + fn check_hresult(self) { + if !SUCCEEDED(self) { + panic_com(self) + } + } +} + +fn panic_com(hresult: HRESULT) -> ! { + panic!("COM error 0x{:08X}", hresult as u32) +} + +/// Forked from <https://github.com/retep998/wio-rs/blob/44093f7db8/src/com.rs> +#[derive(PartialEq, Debug)] +pub struct ComPtr<T>(*mut T) where T: Interface; + +impl<T> ComPtr<T> where T: Interface { + /// Creates a `ComPtr` to wrap a raw pointer. + /// It takes ownership over the pointer which means it does __not__ call `AddRef`. + /// `T` __must__ be a COM interface that inherits from `IUnknown`. + pub unsafe fn from_raw(ptr: *mut T) -> ComPtr<T> { + assert!(!ptr.is_null()); + ComPtr(ptr) + } + + /// For use with APIs that take an interface UUID and + /// "return" a new COM object through a `*mut *mut c_void` out-parameter. + pub unsafe fn new_with_uuid<F>(f: F) -> Self + where F: FnOnce(&GUID, *mut *mut c_void) -> HRESULT + { + Self::new_with(|ptr| f(&T::uuidof(), ptr as _)) + } + + /// For use with APIs that "return" a new COM object through a `*mut *mut T` out-parameter. + pub unsafe fn new_with<F>(f: F) -> Self + where F: FnOnce(*mut *mut T) -> HRESULT + { + let mut ptr = ptr::null_mut(); + let hresult = f(&mut ptr); + if SUCCEEDED(hresult) { + ComPtr::from_raw(ptr) + } else { + if !ptr.is_null() { + let ptr = ptr as *mut IUnknown; + (*ptr).Release(); + } + panic_com(hresult) + } + } + + pub fn as_raw(&self) -> *mut T { + self.0 + } + + fn as_unknown(&self) -> &IUnknown { + unsafe { + &*(self.0 as *mut IUnknown) + } + } + + /// Performs QueryInterface fun. + pub fn cast<U>(&self) -> ComPtr<U> where U: Interface { + unsafe { + ComPtr::<U>::new_with_uuid(|uuid, ptr| self.as_unknown().QueryInterface(uuid, ptr)) + } + } +} + +impl<T> ops::Deref for ComPtr<T> where T: Interface { + type Target = T; + fn deref(&self) -> &T { + unsafe { &*self.0 } + } +} + +impl<T> Clone for ComPtr<T> where T: Interface { + fn clone(&self) -> Self { + unsafe { + self.as_unknown().AddRef(); + ComPtr(self.0) + } + } +} + +impl<T> Drop for ComPtr<T> where T: Interface { + fn drop(&mut self) { + unsafe { + self.as_unknown().Release(); + } + } +} diff --git a/third_party/webrender/direct-composition/src/egl.rs b/third_party/webrender/direct-composition/src/egl.rs new file mode 100644 index 00000000000..8bd5afb72a6 --- /dev/null +++ b/third_party/webrender/direct-composition/src/egl.rs @@ -0,0 +1,174 @@ +/* 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 mozangle::egl::ffi::*; +use std::os::raw::c_void; +use std::ptr; +use std::rc::Rc; +use winapi::um::d3d11::ID3D11Device; +use winapi::um::d3d11::ID3D11Texture2D; + +pub use mozangle::egl::get_proc_address; + +pub struct SharedEglThings { + device: EGLDeviceEXT, + display: types::EGLDisplay, + config: types::EGLConfig, + context: types::EGLContext, +} + +fn cast_attributes(slice: &[types::EGLenum]) -> &EGLint { + unsafe { + &*(slice.as_ptr() as *const EGLint) + } +} + +macro_rules! attributes { + ($( $key: expr => $value: expr, )*) => { + cast_attributes(&[ + $( $key, $value, )* + NONE, + ]) + } +} + +impl SharedEglThings { + pub unsafe fn new(d3d_device: *mut ID3D11Device) -> Rc<Self> { + let device = eglCreateDeviceANGLE( + D3D11_DEVICE_ANGLE, + d3d_device as *mut c_void, + ptr::null(), + ).check(); + let display = GetPlatformDisplayEXT( + PLATFORM_DEVICE_EXT, + device, + attributes! [ + EXPERIMENTAL_PRESENT_PATH_ANGLE => EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE, + ], + ).check(); + Initialize(display, ptr::null_mut(), ptr::null_mut()).check(); + + // Adapted from + // https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#635 + let mut configs = [ptr::null(); 64]; + let mut num_configs = 0; + ChooseConfig( + display, + attributes! [ + SURFACE_TYPE => WINDOW_BIT, + RENDERABLE_TYPE => OPENGL_ES2_BIT, + RED_SIZE => 8, + GREEN_SIZE => 8, + BLUE_SIZE => 8, + ALPHA_SIZE => 8, + ], + configs.as_mut_ptr(), + configs.len() as i32, + &mut num_configs, + ).check(); + let config = pick_config(&configs[..num_configs as usize]); + + let context = CreateContext( + display, config, NO_CONTEXT, + attributes![ + CONTEXT_CLIENT_VERSION => 3, + ] + ).check(); + MakeCurrent(display, NO_SURFACE, NO_SURFACE, context).check(); + + Rc::new(SharedEglThings { device, display, config, context }) + } +} + +fn pick_config(configs: &[types::EGLConfig]) -> types::EGLConfig { + // FIXME: better criteria to make this choice? + // Firefox uses GetConfigAttrib to find a config that has the requested r/g/b/a sizes + // https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#662-685 + + configs[0] +} + +impl Drop for SharedEglThings { + fn drop(&mut self) { + unsafe { + // FIXME does EGLDisplay or EGLConfig need clean up? How? + DestroyContext(self.display, self.context).check(); + eglReleaseDeviceANGLE(self.device).check(); + } + } +} + +pub struct PerVisualEglThings { + shared: Rc<SharedEglThings>, + surface: types::EGLSurface, +} + +impl PerVisualEglThings { + pub unsafe fn new(shared: Rc<SharedEglThings>, buffer: *const ID3D11Texture2D, + width: u32, height: u32) + -> Self { + let surface = CreatePbufferFromClientBuffer( + shared.display, + D3D_TEXTURE_ANGLE, + buffer as types::EGLClientBuffer, + shared.config, + attributes! [ + WIDTH => width, + HEIGHT => height, + FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE => TRUE, + ], + ).check(); + + PerVisualEglThings { shared, surface } + } + + pub fn make_current(&self) { + unsafe { + MakeCurrent(self.shared.display, self.surface, self.surface, self.shared.context).check(); + } + } +} + +impl Drop for PerVisualEglThings { + fn drop(&mut self) { + unsafe { + DestroySurface(self.shared.display, self.surface).check(); + } + } +} + +fn check_error() { + unsafe { + let error = GetError() as types::EGLenum; + assert_eq!(error, SUCCESS, "0x{:x} != 0x{:x}", error, SUCCESS); + } +} + +trait Check { + fn check(self) -> Self; +} + +impl Check for *const c_void { + fn check(self) -> Self { + check_error(); + assert!(!self.is_null()); + self + } +} + +impl Check for *mut c_void { + fn check(self) -> Self { + check_error(); + assert!(!self.is_null()); + self + } +} + +impl Check for types::EGLBoolean { + fn check(self) -> Self { + check_error(); + assert_eq!(self, TRUE); + self + } +} diff --git a/third_party/webrender/direct-composition/src/lib.rs b/third_party/webrender/direct-composition/src/lib.rs new file mode 100644 index 00000000000..fa94b4b0e3f --- /dev/null +++ b/third_party/webrender/direct-composition/src/lib.rs @@ -0,0 +1,179 @@ +/* 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/. */ + +#![cfg(windows)] + +use gleam; +use mozangle; +use winapi; + +use com::{ComPtr, CheckHResult, as_ptr}; +use std::ptr; +use std::rc::Rc; +use winapi::shared::dxgi1_2::DXGI_SWAP_CHAIN_DESC1; +use winapi::shared::dxgi1_2::IDXGIFactory2; +use winapi::shared::minwindef::{TRUE, FALSE}; +use winapi::shared::windef::HWND; +use winapi::um::d3d11::ID3D11Device; +use winapi::um::dcomp::IDCompositionDevice; +use winapi::um::dcomp::IDCompositionTarget; +use winapi::um::dcomp::IDCompositionVisual; + +mod com; +mod egl; + +pub struct DirectComposition { + d3d_device: ComPtr<ID3D11Device>, + dxgi_factory: ComPtr<IDXGIFactory2>, + + egl: Rc<egl::SharedEglThings>, + pub gleam: Rc<dyn gleam::gl::Gl>, + + composition_device: ComPtr<IDCompositionDevice>, + root_visual: ComPtr<IDCompositionVisual>, + + #[allow(unused)] // Needs to be kept alive + composition_target: ComPtr<IDCompositionTarget>, +} + +impl DirectComposition { + /// Initialize DirectComposition in the given window + /// + /// # Safety + /// + /// `hwnd` must be a valid handle to a window. + pub unsafe fn new(hwnd: HWND) -> Self { + let d3d_device = ComPtr::new_with(|ptr_ptr| winapi::um::d3d11::D3D11CreateDevice( + ptr::null_mut(), + winapi::um::d3dcommon::D3D_DRIVER_TYPE_HARDWARE, + ptr::null_mut(), + winapi::um::d3d11::D3D11_CREATE_DEVICE_BGRA_SUPPORT | + if cfg!(debug_assertions) { + winapi::um::d3d11::D3D11_CREATE_DEVICE_DEBUG + } else { + 0 + }, + ptr::null_mut(), + 0, + winapi::um::d3d11::D3D11_SDK_VERSION, + ptr_ptr, + &mut 0, + ptr::null_mut(), + )); + + let egl = egl::SharedEglThings::new(d3d_device.as_raw()); + let gleam = gleam::gl::GlesFns::load_with(egl::get_proc_address); + + let dxgi_device = d3d_device.cast::<winapi::shared::dxgi::IDXGIDevice>(); + + // https://msdn.microsoft.com/en-us/library/windows/desktop/hh404556(v=vs.85).aspx#code-snippet-1 + // “Because you can create a Direct3D device without creating a swap chain, + // you might need to retrieve the factory that is used to create the device + // in order to create a swap chain.” + let adapter = ComPtr::new_with(|ptr_ptr| dxgi_device.GetAdapter(ptr_ptr)); + let dxgi_factory = ComPtr::<IDXGIFactory2>::new_with_uuid(|uuid, ptr_ptr| { + adapter.GetParent(uuid, ptr_ptr) + }); + + // Create the DirectComposition device object. + let composition_device = ComPtr::<IDCompositionDevice>::new_with_uuid(|uuid, ptr_ptr| { + winapi::um::dcomp::DCompositionCreateDevice(&*dxgi_device, uuid, ptr_ptr) + }); + + // Create the composition target object based on the + // specified application window. + let composition_target = ComPtr::new_with(|ptr_ptr| { + composition_device.CreateTargetForHwnd(hwnd, TRUE, ptr_ptr) + }); + + let root_visual = ComPtr::new_with(|ptr_ptr| composition_device.CreateVisual(ptr_ptr)); + composition_target.SetRoot(&*root_visual).check_hresult(); + + DirectComposition { + d3d_device, dxgi_factory, + egl, gleam, + composition_device, composition_target, root_visual, + } + } + + /// Execute changes to the DirectComposition scene. + pub fn commit(&self) { + unsafe { + self.composition_device.Commit().check_hresult() + } + } + + pub fn create_angle_visual(&self, width: u32, height: u32) -> AngleVisual { + unsafe { + let desc = DXGI_SWAP_CHAIN_DESC1 { + Width: width, + Height: height, + Format: winapi::shared::dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM, + Stereo: FALSE, + SampleDesc: winapi::shared::dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: winapi::shared::dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 2, + Scaling: winapi::shared::dxgi1_2::DXGI_SCALING_STRETCH, + SwapEffect: winapi::shared::dxgi::DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, + AlphaMode: winapi::shared::dxgi1_2::DXGI_ALPHA_MODE_PREMULTIPLIED, + Flags: 0, + }; + let swap_chain = ComPtr::<winapi::shared::dxgi1_2::IDXGISwapChain1>::new_with(|ptr_ptr| { + self.dxgi_factory.CreateSwapChainForComposition( + as_ptr(&self.d3d_device), + &desc, + ptr::null_mut(), + ptr_ptr, + ) + }); + let back_buffer = ComPtr::<winapi::um::d3d11::ID3D11Texture2D>::new_with_uuid(|uuid, ptr_ptr| { + swap_chain.GetBuffer(0, uuid, ptr_ptr) + }); + let egl = egl::PerVisualEglThings::new(self.egl.clone(), &*back_buffer, width, height); + let gleam = self.gleam.clone(); + + let visual = ComPtr::new_with(|ptr_ptr| self.composition_device.CreateVisual(ptr_ptr)); + visual.SetContent(&*****swap_chain).check_hresult(); + self.root_visual.AddVisual(&*visual, FALSE, ptr::null_mut()).check_hresult(); + + AngleVisual { visual, swap_chain, egl, gleam } + } + } +} + +/// A DirectComposition "visual" configured for rendering with Direct3D. +pub struct AngleVisual { + visual: ComPtr<IDCompositionVisual>, + swap_chain: ComPtr<winapi::shared::dxgi1_2::IDXGISwapChain1>, + egl: egl::PerVisualEglThings, + pub gleam: Rc<dyn gleam::gl::Gl>, +} + +impl AngleVisual { + pub fn set_offset_x(&self, offset_x: f32) { + unsafe { + self.visual.SetOffsetX_1(offset_x).check_hresult() + } + } + + pub fn set_offset_y(&self, offset_y: f32) { + unsafe { + self.visual.SetOffsetY_1(offset_y).check_hresult() + } + } + + pub fn make_current(&self) { + self.egl.make_current() + } + + pub fn present(&self) { + self.gleam.finish(); + unsafe { + self.swap_chain.Present(0, 0).check_hresult() + } + } +} diff --git a/third_party/webrender/webrender/src/compositor/mod.rs b/third_party/webrender/direct-composition/src/main.rs index e517f227193..e1999f5f8f1 100644 --- a/third_party/webrender/webrender/src/compositor/mod.rs +++ b/third_party/webrender/direct-composition/src/main.rs @@ -2,5 +2,10 @@ * 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/. */ -#[cfg(feature = "sw_compositor")] -pub mod sw_compositor; +#[cfg(not(windows))] +fn main() { + println!("This demo only runs on Windows."); +} + +#[cfg(windows)] +include!("main_windows.rs"); diff --git a/third_party/webrender/direct-composition/src/main_windows.rs b/third_party/webrender/direct-composition/src/main_windows.rs new file mode 100644 index 00000000000..ff6608b4f00 --- /dev/null +++ b/third_party/webrender/direct-composition/src/main_windows.rs @@ -0,0 +1,212 @@ +/* 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 direct_composition; +use euclid; +use gleam; +use webrender; +use winit; + +use euclid::size2; +use direct_composition::DirectComposition; +use std::sync::mpsc; +use webrender::api; +use winit::os::windows::{WindowExt, WindowBuilderExt}; +use winit::dpi::LogicalSize; + +fn main() { + let mut events_loop = winit::EventsLoop::new(); + + let (tx, rx) = mpsc::channel(); + let notifier = Box::new(Notifier { events_proxy: events_loop.create_proxy(), tx }); + + let window = winit::WindowBuilder::new() + .with_title("WebRender + ANGLE + DirectComposition") + .with_dimensions(LogicalSize::new(1024., 768.)) + .with_decorations(true) + .with_transparency(true) + .with_no_redirection_bitmap(true) + .build(&events_loop) + .unwrap(); + + let composition = direct_composition_from_window(&window); + let factor = window.get_hidpi_factor() as f32; + + let mut clicks: usize = 0; + let mut offset_y = 100.; + let mut rects = [ + Rectangle::new(&composition, ¬ifier, factor, size2(300, 200), 0., 0.2, 0.4, 1.), + Rectangle::new(&composition, ¬ifier, factor, size2(400, 300), 0., 0.5, 0., 0.5), + ]; + rects[0].render(factor, &rx); + rects[1].render(factor, &rx); + + rects[0].visual.set_offset_x(100.); + rects[0].visual.set_offset_y(50.); + + rects[1].visual.set_offset_x(200.); + rects[1].visual.set_offset_y(offset_y); + + composition.commit(); + + events_loop.run_forever(|event| { + if let winit::Event::WindowEvent { event, .. } = event { + match event { + winit::WindowEvent::CloseRequested => { + return winit::ControlFlow::Break + } + winit::WindowEvent::MouseWheel { delta, .. } => { + let dy = match delta { + winit::MouseScrollDelta::LineDelta(_, dy) => dy, + winit::MouseScrollDelta::PixelDelta(pos) => pos.y as f32, + }; + offset_y = (offset_y - 10. * dy).max(0.).min(468.); + + rects[1].visual.set_offset_y(offset_y); + composition.commit(); + } + winit::WindowEvent::MouseInput { + button: winit::MouseButton::Left, + state: winit::ElementState::Pressed, + .. + } => { + clicks += 1; + let rect = &mut rects[clicks % 2]; + rect.color.g += 0.1; + rect.color.g %= 1.; + rect.render(factor, &rx) + } + _ => {} + } + } + winit::ControlFlow::Continue + }); +} + +fn direct_composition_from_window(window: &winit::Window) -> DirectComposition { + unsafe { + DirectComposition::new(window.get_hwnd() as _) + } +} + +struct Rectangle { + visual: direct_composition::AngleVisual, + renderer: Option<webrender::Renderer>, + api: api::RenderApi, + document_id: api::DocumentId, + size: api::units::DeviceIntSize, + color: api::ColorF, +} + +impl Rectangle { + fn new(composition: &DirectComposition, notifier: &Box<Notifier>, + device_pixel_ratio: f32, size: api::units::DeviceIntSize, r: f32, g: f32, b: f32, a: f32) + -> Self { + let visual = composition.create_angle_visual(size.width as u32, size.height as u32); + visual.make_current(); + + let (renderer, sender) = webrender::Renderer::new( + composition.gleam.clone(), + notifier.clone(), + webrender::RendererOptions { + clear_color: Some(api::ColorF::new(0., 0., 0., 0.)), + device_pixel_ratio, + ..webrender::RendererOptions::default() + }, + None, + size, + ).unwrap(); + let api = sender.create_api(); + + Rectangle { + visual, + renderer: Some(renderer), + document_id: api.add_document(size, 0), + api, + size, + color: api::ColorF { r, g, b, a }, + } + } + + fn render(&mut self, device_pixel_ratio: f32, rx: &mpsc::Receiver<()>) { + self.visual.make_current(); + + let pipeline_id = api::PipelineId(0, 0); + let layout_size = self.size.to_f32() / euclid::Scale::new(device_pixel_ratio); + let mut builder = api::DisplayListBuilder::new(pipeline_id, layout_size); + + let rect = euclid::Rect::new(euclid::Point2D::zero(), layout_size); + + let region = api::ComplexClipRegion::new( + rect, + api::BorderRadius::uniform(20.), + api::ClipMode::Clip + ); + let clip_id = builder.define_clip_rounded_rect( + &api::SpaceAndClipInfo::root_scroll(pipeline_id), + region, + ); + + builder.push_rect( + &api::CommonItemProperties::new( + rect, + api::SpaceAndClipInfo { + spatial_id: api::SpatialId::root_scroll_node(pipeline_id), + clip_id, + }, + ), + rect, + self.color, + ); + + let mut transaction = api::Transaction::new(); + transaction.set_display_list( + api::Epoch(0), + None, + layout_size, + builder.finalize(), + true, + ); + transaction.set_root_pipeline(pipeline_id); + transaction.generate_frame(); + self.api.send_transaction(self.document_id, transaction); + rx.recv().unwrap(); + let renderer = self.renderer.as_mut().unwrap(); + renderer.update(); + renderer.render(self.size).unwrap(); + let _ = renderer.flush_pipeline_info(); + self.visual.present(); + } +} + +impl Drop for Rectangle { + fn drop(&mut self) { + self.renderer.take().unwrap().deinit() + } +} + +#[derive(Clone)] +struct Notifier { + events_proxy: winit::EventsLoopProxy, + tx: mpsc::Sender<()>, +} + +impl api::RenderNotifier for Notifier { + fn clone(&self) -> Box<dyn api::RenderNotifier> { + Box::new(Clone::clone(self)) + } + + fn wake_up(&self) { + self.tx.send(()).unwrap(); + let _ = self.events_proxy.wakeup(); + } + + fn new_frame_ready(&self, + _: api::DocumentId, + _: bool, + _: bool, + _: Option<u64>) { + self.wake_up(); + } +} diff --git a/third_party/webrender/example-compositor/compositor-wayland/Cargo.toml b/third_party/webrender/example-compositor/compositor-wayland/Cargo.toml deleted file mode 100644 index 0f5bba73b5c..00000000000 --- a/third_party/webrender/example-compositor/compositor-wayland/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "compositor-wayland" -version = "0.1.0" -authors = ["Glenn Watson <gw@intuitionlibrary.com>", - "Robert Mader <robert.mader@posteo.de>"] -edition = "2018" -license = "MPL-2.0" - -[build-dependencies] -cc = "1.0" -pkg-config = "^0.3.17" diff --git a/third_party/webrender/example-compositor/compositor-wayland/build.rs b/third_party/webrender/example-compositor/compositor-wayland/build.rs deleted file mode 100644 index ab418b94c79..00000000000 --- a/third_party/webrender/example-compositor/compositor-wayland/build.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use std::process::Command; -use std::env; -use std::fs; - -extern crate pkg_config; - -fn main() { - let out_dir = env::var("OUT_DIR").unwrap(); - - fs::create_dir_all(&format!("{}/include", out_dir)).unwrap(); - Command::new("wayland-scanner") - .args(&["client-header", "/usr/share/wayland-protocols/stable/viewporter/viewporter.xml"]) - .arg(&format!("{}/include/viewporter-client-protocol.h", out_dir)) - .status().unwrap(); - - Command::new("wayland-scanner") - .args(&["public-code", "/usr/share/wayland-protocols/stable/viewporter/viewporter.xml"]) - .arg(&format!("{}/viewporter-protocol.c", out_dir)) - .status().unwrap(); - - Command::new("wayland-scanner") - .args(&["client-header", "/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml"]) - .arg(&format!("{}/include/xdg-shell-client-protocol.h", out_dir)) - .status().unwrap(); - - Command::new("wayland-scanner") - .args(&["public-code", "/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml"]) - .arg(&format!("{}/xdg-shell-protocol.c", out_dir)) - .status().unwrap(); - - cc::Build::new() - .include(&format!("{}/include", out_dir)) - .file("src/lib.cpp") - .file(&format!("{}/viewporter-protocol.c", out_dir)) - .file(&format!("{}/xdg-shell-protocol.c", out_dir)) - .compile("wayland"); - - println!("cargo:rustc-link-lib=dylib=stdc++"); - - pkg_config::Config::new() - .atleast_version("1") - .probe("egl") - .unwrap(); - pkg_config::Config::new() - .atleast_version("1") - .probe("gl") - .unwrap(); - pkg_config::Config::new() - .atleast_version("1") - .probe("wayland-client") - .unwrap(); - pkg_config::Config::new() - .atleast_version("1") - .probe("wayland-egl") - .unwrap(); - - println!("cargo:rerun-if-changed=src/lib.rs"); - println!("cargo:rerun-if-changed=src/lib.cpp"); -} diff --git a/third_party/webrender/example-compositor/compositor-wayland/src/lib.cpp b/third_party/webrender/example-compositor/compositor-wayland/src/lib.cpp deleted file mode 100644 index 5529c987a9e..00000000000 --- a/third_party/webrender/example-compositor/compositor-wayland/src/lib.cpp +++ /dev/null @@ -1,772 +0,0 @@ -/* 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/. */ - -#define UNICODE - -#include <algorithm> -#include <assert.h> -#include <errno.h> -#include <fcntl.h> -#include <map> -#include <math.h> -#include <stdio.h> -#include <string.h> -#include <sys/mman.h> -#include <unistd.h> -#include <unordered_map> -#include <vector> - -#include <wayland-client.h> -#include <wayland-egl.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GL/gl.h> -#include <GLES2/gl2.h> - -#include "viewporter-client-protocol.h" -#include "xdg-shell-client-protocol.h" - -#define UNUSED(x) (void)(x) - -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) -#define MAX(x, y) (((x) > (y)) ? (x) : (y)) - -#define NUM_QUERIES 2 - -#define VIRTUAL_OFFSET 512 * 1024 - -enum SyncMode { - None_ = 0, - Swap = 1, - Commit = 2, - Flush = 3, - Query = 4, -}; - -// The OS compositor representation of a picture cache tile. -struct Tile { - uint64_t surface_id; - int x; - int y; - - struct wl_surface* surface; - struct wl_subsurface* subsurface; - struct wp_viewport* viewport; - struct wl_egl_window* egl_window; - EGLSurface egl_surface; - bool is_visible; - - std::vector<EGLint> damage_rects; -}; - -struct TileKey { - int x; - int y; - - TileKey(int ax, int ay) : x(ax), y(ay) {} -}; - -bool operator==(const TileKey& k0, const TileKey& k1) { - return k0.x == k1.x && k0.y == k1.y; -} - -struct TileKeyHasher { - size_t operator()(const TileKey& key) const { return key.x ^ key.y; } -}; - -struct Surface { - uint64_t id; - int tile_width; - int tile_height; - bool is_opaque; - std::unordered_map<TileKey, Tile*, TileKeyHasher> tiles; -}; - -struct WLDisplay { - struct wl_display* display; - struct wl_registry* registry; - struct wl_compositor* compositor; - struct wl_subcompositor* subcompositor; - struct xdg_wm_base* wm_base; - struct wl_seat* seat; - struct wl_pointer* pointer; - struct wl_touch* touch; - struct wl_keyboard* keyboard; - struct wl_shm* shm; - struct wl_cursor_theme* cursor_theme; - struct wl_cursor* default_cursor; - struct wl_surface* cursor_surface; - struct wp_viewporter* viewporter; - - PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage; -}; - -struct WLGeometry { - int width, height; -}; - -struct WLWindow { - WLGeometry geometry; - bool enable_compositor; - SyncMode sync_mode; - bool closed; - - WLDisplay* display; - struct wl_surface* surface; - struct xdg_surface* xdg_surface; - struct xdg_toplevel* xdg_toplevel; - struct wl_callback* callback; - struct wp_viewport* viewport; - bool wait_for_configure; - - struct wl_egl_window* egl_window; - EGLSurface egl_surface; - - EGLDeviceEXT eglDevice; - EGLDisplay eglDisplay; - EGLContext eglContext; - EGLConfig config; - - // Maintain list of layer state between frames to avoid visual tree rebuild. - std::vector<uint64_t> currentLayers; - std::vector<uint64_t> prevLayers; - - // Maps WR surface IDs to each OS surface - std::unordered_map<uint64_t, Surface> surfaces; - std::vector<Tile*> destroyedTiles; - std::vector<Tile*> hiddenTiles; -}; - -extern "C" { - -static void init_wl_registry(WLWindow* window); -static void init_xdg_window(WLWindow* window); - -WLWindow* com_wl_create_window(int width, int height, bool enable_compositor, - SyncMode sync_mode) { - WLDisplay* display = new WLDisplay; - WLWindow* window = new WLWindow; - - window->display = display; - window->geometry.width = width; - window->geometry.height = height; - window->enable_compositor = enable_compositor; - window->sync_mode = sync_mode; - window->closed = false; - - display->display = wl_display_connect(NULL); - assert(display->display); - - init_wl_registry(window); - if (enable_compositor && !display->viewporter) { - fprintf(stderr, "Native compositor mode requires wp_viewporter support\n"); - window->closed = true; - } - - window->eglDisplay = - eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display->display, NULL); - - eglInitialize(window->eglDisplay, nullptr, nullptr); - eglBindAPI(EGL_OPENGL_API); - - EGLint num_configs = 0; - EGLint cfg_attribs[] = {EGL_SURFACE_TYPE, - EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, - EGL_OPENGL_BIT, - EGL_RED_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_BLUE_SIZE, - 8, - EGL_ALPHA_SIZE, - 8, - EGL_DEPTH_SIZE, - 24, - EGL_NONE}; - EGLConfig configs[32]; - - eglChooseConfig(window->eglDisplay, cfg_attribs, configs, - sizeof(configs) / sizeof(EGLConfig), &num_configs); - assert(num_configs > 0); - window->config = configs[0]; - - EGLint ctx_attribs[] = {EGL_CONTEXT_OPENGL_PROFILE_MASK, - EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, - EGL_CONTEXT_MAJOR_VERSION, - 3, - EGL_CONTEXT_MINOR_VERSION, - 2, - EGL_NONE}; - - // Create an EGL context that can be used for drawing - window->eglContext = eglCreateContext(window->eglDisplay, window->config, - EGL_NO_CONTEXT, ctx_attribs); - - window->surface = wl_compositor_create_surface(display->compositor); - init_xdg_window(window); - - struct wl_region* region = - wl_compositor_create_region(window->display->compositor); - wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_set_opaque_region(window->surface, region); - wl_region_destroy(region); - - if (enable_compositor) { - xdg_toplevel_set_title(window->xdg_toplevel, - "example-compositor (Wayland)"); - } else { - xdg_toplevel_set_title(window->xdg_toplevel, "example-compositor (Simple)"); - } - - window->wait_for_configure = true; - wl_surface_commit(window->surface); - - EGLBoolean ok = eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE, - EGL_NO_SURFACE, window->eglContext); - assert(ok); - - display->swap_buffers_with_damage = - (PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)eglGetProcAddress( - "eglSwapBuffersWithDamageKHR"); - - return window; -} - -bool com_wl_tick(WLWindow* window) { - if (window->wait_for_configure) { - int ret = 0; - while (window->wait_for_configure && !window->closed && ret != -1) { - wl_display_dispatch(window->display->display); - } - } else { - wl_display_dispatch_pending(window->display->display); - } - - return !window->closed; -} - -static void unmap_hidden_tiles(WLWindow* window) { - for (Tile* tile : window->hiddenTiles) { - if (tile->subsurface) { - wl_subsurface_destroy(tile->subsurface); - tile->subsurface = nullptr; - } - } - window->hiddenTiles.clear(); -} - -static void clean_up_tiles(WLWindow* window) { - for (Tile* tile : window->destroyedTiles) { - eglDestroySurface(window->eglDisplay, tile->egl_surface); - wl_egl_window_destroy(tile->egl_window); - wp_viewport_destroy(tile->viewport); - wl_surface_destroy(tile->surface); - delete tile; - } - window->destroyedTiles.clear(); -} - -static void handle_callback(void* data, struct wl_callback* callback, - uint32_t time) { - WLWindow* window = (WLWindow*)data; - UNUSED(time); - - assert(window->callback == callback); - - wl_callback_destroy(callback); - window->callback = nullptr; -} - -static const struct wl_callback_listener frame_listener = {handle_callback}; - -void com_wl_swap_buffers(WLWindow* window) { - if (window->enable_compositor) { - for (auto surface_it = window->surfaces.begin(); - surface_it != window->surfaces.end(); ++surface_it) { - Surface* surface = &surface_it->second; - - for (auto tile_it = surface->tiles.begin(); - tile_it != surface->tiles.end(); ++tile_it) { - Tile* tile = tile_it->second; - - if (!tile->damage_rects.empty() && tile->is_visible) { - eglMakeCurrent(window->eglDisplay, tile->egl_surface, - tile->egl_surface, window->eglContext); - eglSwapInterval(window->eglDisplay, 0); - - /* if (window->display->swap_buffers_with_damage) { - window->display->swap_buffers_with_damage( - window->eglDisplay, tile->egl_surface, - tile->damage_rects.data(), tile->damage_rects.size() / 4); - } else */ - eglSwapBuffers(window->eglDisplay, tile->egl_surface); - tile->damage_rects.clear(); - - eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, - window->eglContext); - } else { - wl_surface_commit(tile->surface); - } - } - } - wl_surface_commit(window->surface); - unmap_hidden_tiles(window); - clean_up_tiles(window); - - int ret = 0; - switch (window->sync_mode) { - case SyncMode::None_: - wl_display_roundtrip(window->display->display); - break; - case SyncMode::Swap: - window->callback = wl_surface_frame(window->surface); - wl_callback_add_listener(window->callback, &frame_listener, window); - wl_surface_commit(window->surface); - - while (window->callback && !window->closed && ret != -1) { - ret = wl_display_dispatch(window->display->display); - } - break; - default: - assert(false); - break; - } - } else { - // If not using native mode, then do a normal EGL swap buffers. - switch (window->sync_mode) { - case SyncMode::None_: - eglSwapInterval(window->eglDisplay, 0); - break; - case SyncMode::Swap: - eglSwapInterval(window->eglDisplay, 1); - break; - default: - assert(false); - break; - } - eglSwapBuffers(window->eglDisplay, window->egl_surface); - } -} - -// Create a new native surface -void com_wl_create_surface(WLWindow* window, uint64_t surface_id, - int tile_width, int tile_height, bool is_opaque) { - assert(window->surfaces.count(surface_id) == 0); - - Surface surface; - surface.id = surface_id; - surface.tile_width = tile_width; - surface.tile_height = tile_height; - surface.is_opaque = is_opaque; - - window->surfaces.emplace(surface_id, surface); -} - -void com_wl_create_tile(WLWindow* window, uint64_t surface_id, int x, int y) { - WLDisplay* display = window->display; - - assert(window->surfaces.count(surface_id) == 1); - Surface* surface = &window->surfaces.at(surface_id); - - TileKey key(x, y); - assert(surface->tiles.count(key) == 0); - - Tile* tile = new Tile; - tile->surface_id = surface_id; - tile->x = x; - tile->y = y; - tile->is_visible = false; - - tile->surface = wl_compositor_create_surface(display->compositor); - tile->viewport = - wp_viewporter_get_viewport(display->viewporter, tile->surface); - - if (surface->is_opaque) { - struct wl_region* region = - wl_compositor_create_region(window->display->compositor); - wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_set_opaque_region(tile->surface, region); - wl_region_destroy(region); - } - - tile->egl_window = wl_egl_window_create(tile->surface, surface->tile_width, - surface->tile_height); - tile->egl_surface = eglCreateWindowSurface(window->eglDisplay, window->config, - tile->egl_window, NULL); - assert(tile->egl_surface != EGL_NO_SURFACE); - - surface->tiles.emplace(key, tile); -} - -static void show_tile(WLWindow* window, Tile* tile) { - if (tile->is_visible) { - assert(tile->subsurface); - return; - } - - tile->subsurface = wl_subcompositor_get_subsurface( - window->display->subcompositor, tile->surface, window->surface); - - /* This is not comprehensive yet, see hide_tile() */ - Surface* surface = &window->surfaces.at(tile->surface_id); - for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end(); - ++tile_it) { - Tile* other_tile = tile_it->second; - - if (other_tile->is_visible) { - wl_subsurface_place_above(tile->subsurface, other_tile->surface); - } - } - - tile->is_visible = true; -} - -static void hide_tile(WLWindow* window, Tile* tile) { - if (!tile->is_visible) { - return; - } - - /* - * This is a workaround for missing API on the egl-wayland platform. We - * likely want to replace it a solution that detaches the buffer from - * the surface, which would require us to manage buffers manually. - */ - wl_subsurface_set_position(tile->subsurface, window->geometry.width / 2, - window->geometry.height / 2); - wp_viewport_set_source(tile->viewport, wl_fixed_from_int(0), - wl_fixed_from_int(0), wl_fixed_from_int(1), - wl_fixed_from_int(1)); - wl_subsurface_place_below(tile->subsurface, window->surface); - tile->is_visible = false; - window->hiddenTiles.push_back(tile); -} - -void com_wl_destroy_tile(WLWindow* window, uint64_t surface_id, int x, int y) { - assert(window->surfaces.count(surface_id) == 1); - - Surface* surface = &window->surfaces.at(surface_id); - TileKey key(x, y); - assert(surface->tiles.count(key) == 1); - Tile* tile = surface->tiles[key]; - - hide_tile(window, tile); - wl_surface_commit(tile->surface); - - window->destroyedTiles.push_back(tile); - surface->tiles.erase(key); -} - -void com_wl_destroy_surface(WLWindow* window, uint64_t surface_id) { - assert(window->surfaces.count(surface_id) == 1); - - Surface* surface = &window->surfaces.at(surface_id); - for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end(); - tile_it = surface->tiles.begin()) { - Tile* tile = tile_it->second; - - com_wl_destroy_tile(window, surface_id, tile->x, tile->y); - } - - window->surfaces.erase(surface_id); -} - -void com_wl_destroy_window(WLWindow* window) { - for (auto surface_it = window->surfaces.begin(); - surface_it != window->surfaces.end(); ++surface_it) { - Surface& surface = surface_it->second; - - com_wl_destroy_surface(window, surface.id); - } - - if (window->egl_surface != EGL_NO_SURFACE) { - eglDestroySurface(window->eglDisplay, window->egl_surface); - } - eglDestroyContext(window->eglDisplay, window->eglContext); - eglTerminate(window->eglDisplay); - - delete window; -} - -// Bind a native surface to allow issuing GL commands to it -GLuint com_wl_bind_surface(WLWindow* window, uint64_t surface_id, int tile_x, - int tile_y, int* x_offset, int* y_offset, - int dirty_x0, int dirty_y0, int dirty_width, - int dirty_height) { - *x_offset = 0; - *y_offset = 0; - - assert(window->surfaces.count(surface_id) == 1); - Surface* surface = &window->surfaces[surface_id]; - - TileKey key(tile_x, tile_y); - assert(surface->tiles.count(key) == 1); - Tile* tile = surface->tiles[key]; - - tile->damage_rects.push_back(dirty_x0); - tile->damage_rects.push_back(dirty_y0); - tile->damage_rects.push_back(dirty_width); - tile->damage_rects.push_back(dirty_height); - - EGLBoolean ok = eglMakeCurrent(window->eglDisplay, tile->egl_surface, - tile->egl_surface, window->eglContext); - assert(ok); - - return 0; -} - -// Unbind a currently bound native surface -void com_wl_unbind_surface(WLWindow* window) { - eglMakeCurrent(window->eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, - window->eglContext); -} - -void com_wl_begin_transaction(WLWindow*) {} - -// Add a native surface to the visual tree. Called per-frame to build the -// composition. -void com_wl_add_surface(WLWindow* window, uint64_t surface_id, int offset_x, - int offset_y, int clip_x, int clip_y, int clip_w, - int clip_h) { - Surface* surface = &window->surfaces[surface_id]; - window->currentLayers.push_back(surface_id); - - for (auto tile_it = surface->tiles.begin(); tile_it != surface->tiles.end(); - ++tile_it) { - Tile* tile = tile_it->second; - - int pos_x = MAX((tile->x * surface->tile_width) + offset_x, clip_x); - int pos_y = MAX((tile->y * surface->tile_height) + offset_y, clip_y); - - float view_x = MAX((clip_x - offset_x) - tile->x * surface->tile_width, 0); - float view_y = MAX((clip_y - offset_y) - tile->y * surface->tile_height, 0); - - float view_w = MIN(surface->tile_width - view_x, (clip_x + clip_w) - pos_x); - float view_h = - MIN(surface->tile_height - view_y, (clip_y + clip_h) - pos_y); - view_w = MIN(window->geometry.width - pos_x, view_w); - view_h = MIN(window->geometry.height - pos_y, view_h); - - if (view_w > 0 && view_h > 0) { - show_tile(window, tile); - - wl_surface_set_buffer_transform(tile->surface, - WL_OUTPUT_TRANSFORM_FLIPPED_180); - wl_subsurface_set_position(tile->subsurface, pos_x, pos_y); - wp_viewport_set_source(tile->viewport, wl_fixed_from_double(view_x), - wl_fixed_from_double(view_y), - wl_fixed_from_double(view_w), - wl_fixed_from_double(view_h)); - } else { - hide_tile(window, tile); - } - } -} - -void com_wl_end_transaction(WLWindow* window) { - bool same = window->prevLayers == window->currentLayers; - if (!same) { - struct wl_surface* prev_surface = window->surface; - - for (auto it = window->currentLayers.begin(); - it != window->currentLayers.end(); ++it) { - Surface* surface = &window->surfaces[*it]; - - struct wl_surface* next_surface = nullptr; - for (auto tile_it = surface->tiles.begin(); - tile_it != surface->tiles.end(); ++tile_it) { - Tile* tile = tile_it->second; - - if (tile->is_visible) { - wl_subsurface_place_above(tile->subsurface, prev_surface); - - if (!next_surface) { - next_surface = tile->surface; - } - } - } - prev_surface = next_surface; - } - } - - window->prevLayers.swap(window->currentLayers); - window->currentLayers.clear(); -} - -void glInvalidateFramebuffer(GLenum target, GLsizei numAttachments, - const GLenum* attachments) { - UNUSED(target); - UNUSED(numAttachments); - UNUSED(attachments); -} - -// Get a pointer to an EGL symbol -void* com_wl_get_proc_address(const char* name) { - /* Disable glInvalidateFramebuffer for now as it triggers errors. - * This is likely due to the egl-wayland platform, which we may want to - * replace with a custom implementation in order to have more control - * over the low-lever bits. - */ - if (strcmp(name, "glInvalidateFramebuffer") == 0) { - return (void*)glInvalidateFramebuffer; - } - - return (void*)eglGetProcAddress(name); -} - -void com_wl_deinit(WLWindow* window) { UNUSED(window); } - -static void handle_xdg_surface_configure(void* data, - struct xdg_surface* surface, - uint32_t serial) { - WLWindow* window = (WLWindow*)data; - - xdg_surface_ack_configure(surface, serial); - - if (window->wait_for_configure) { - if (window->enable_compositor) { - int width = window->geometry.width; - int height = window->geometry.height; - - window->egl_window = wl_egl_window_create(window->surface, 1, 1); - window->egl_surface = eglCreateWindowSurface( - window->eglDisplay, window->config, window->egl_window, NULL); - assert(window->egl_surface != EGL_NO_SURFACE); - - EGLBoolean ok = eglMakeCurrent(window->eglDisplay, window->egl_surface, - window->egl_surface, window->eglContext); - assert(ok); - - glClearColor(1.0, 1.0, 1.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - window->viewport = wp_viewporter_get_viewport(window->display->viewporter, - window->surface); - wp_viewport_set_destination(window->viewport, width, height); - - eglSwapBuffers(window->eglDisplay, window->egl_surface); - } else { - window->egl_window = wl_egl_window_create( - window->surface, window->geometry.width, window->geometry.height); - window->egl_surface = eglCreateWindowSurface( - window->eglDisplay, window->config, window->egl_window, NULL); - assert(window->egl_surface != EGL_NO_SURFACE); - - EGLBoolean ok = eglMakeCurrent(window->eglDisplay, window->egl_surface, - window->egl_surface, window->eglContext); - assert(ok); - } - } - - window->wait_for_configure = false; -} - -static const struct xdg_surface_listener xdg_surface_listener = { - handle_xdg_surface_configure}; - -static void handle_xdg_toplevel_configure(void* data, - struct xdg_toplevel* toplevel, - int32_t width, int32_t height, - struct wl_array* states) { - WLWindow* window = (WLWindow*)data; - UNUSED(toplevel); - UNUSED(states); - - if (width > 0 && height > 0) { - window->geometry.width = width; - window->geometry.height = height; - - if (!window->wait_for_configure) { - if (window->enable_compositor) { - wp_viewport_set_destination(window->viewport, window->geometry.width, - window->geometry.height); - } else { - wl_egl_window_resize(window->egl_window, window->geometry.width, - window->geometry.height, 0, 0); - } - } - } -} - -static void handle_xdg_toplevel_close(void* data, - struct xdg_toplevel* toplevel) { - UNUSED(toplevel); - WLWindow* window = (WLWindow*)data; - window->closed = true; -} - -static const struct xdg_toplevel_listener xdg_toplevel_listener = { - handle_xdg_toplevel_configure, - handle_xdg_toplevel_close, -}; - -static void xdg_wm_base_ping(void* data, struct xdg_wm_base* shell, - uint32_t serial) { - UNUSED(data); - xdg_wm_base_pong(shell, serial); -} - -static const struct xdg_wm_base_listener wm_base_listener = { - xdg_wm_base_ping, -}; - -static void registry_handle_global(void* data, struct wl_registry* registry, - uint32_t name, const char* interface, - uint32_t version) { - WLDisplay* d = (WLDisplay*)data; - - if (strcmp(interface, "wl_compositor") == 0) { - d->compositor = (struct wl_compositor*)wl_registry_bind( - registry, name, &wl_compositor_interface, MIN(version, 4)); - } else if (strcmp(interface, "wp_viewporter") == 0) { - d->viewporter = (struct wp_viewporter*)wl_registry_bind( - registry, name, &wp_viewporter_interface, 1); - } else if (strcmp(interface, "xdg_wm_base") == 0) { - d->wm_base = (struct xdg_wm_base*)wl_registry_bind( - registry, name, &xdg_wm_base_interface, 1); - xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, NULL); - } else if (strcmp(interface, "wl_subcompositor") == 0) { - d->subcompositor = (struct wl_subcompositor*)wl_registry_bind( - registry, name, &wl_subcompositor_interface, 1); - } -} - -static void registry_handle_global_remove(void* data, - struct wl_registry* registry, - uint32_t name) { - UNUSED(data); - UNUSED(registry); - UNUSED(name); -} - -static const struct wl_registry_listener registry_listener = { - registry_handle_global, registry_handle_global_remove}; - -static void init_wl_registry(WLWindow* window) { - WLDisplay* display = window->display; - - display->registry = wl_display_get_registry(display->display); - wl_registry_add_listener(display->registry, ®istry_listener, display); - - wl_display_roundtrip(display->display); - - assert(display->compositor); - assert(display->wm_base); - assert(display->subcompositor); -} - -static void init_xdg_window(WLWindow* window) { - window->xdg_surface = - xdg_wm_base_get_xdg_surface(window->display->wm_base, window->surface); - assert(window->xdg_surface); - xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window); - - window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); - xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, - window); - assert(window->xdg_toplevel); -} -} diff --git a/third_party/webrender/example-compositor/compositor-wayland/src/lib.rs b/third_party/webrender/example-compositor/compositor-wayland/src/lib.rs deleted file mode 100644 index daddbb6495e..00000000000 --- a/third_party/webrender/example-compositor/compositor-wayland/src/lib.rs +++ /dev/null @@ -1,269 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use std::os::raw::{c_void, c_char}; - -/* - - This is a very simple (and unsafe!) rust wrapper for the Wayland / EGL - implementation in lib.cpp. - - It just proxies the calls from the Compositor impl to the C99 code. This is very - hacky and not suitable for production! - - */ - -// Opaque wrapper for the Window type in lib.cpp -#[repr(C)] -pub struct Window { - _unused: [u8; 0] -} - -// C99 functions that do the compositor work -extern { - fn com_wl_create_window( - width: i32, - height: i32, - enable_compositor: bool, - sync_mode: i32, - ) -> *mut Window; - fn com_wl_destroy_window(window: *mut Window); - fn com_wl_tick(window: *mut Window) -> bool; - fn com_wl_get_proc_address(name: *const c_char) -> *const c_void; - fn com_wl_swap_buffers(window: *mut Window); - - fn com_wl_create_surface( - window: *mut Window, - id: u64, - tile_width: i32, - tile_height: i32, - is_opaque: bool, - ); - - fn com_wl_create_tile( - window: *mut Window, - id: u64, - x: i32, - y: i32, - ); - - fn com_wl_destroy_tile( - window: *mut Window, - id: u64, - x: i32, - y: i32, - ); - - fn com_wl_destroy_surface( - window: *mut Window, - id: u64, - ); - - fn com_wl_bind_surface( - window: *mut Window, - surface_id: u64, - tile_x: i32, - tile_y: i32, - x_offset: &mut i32, - y_offset: &mut i32, - dirty_x0: i32, - dirty_y0: i32, - dirty_width: i32, - dirty_height: i32, - ) -> u32; - fn com_wl_unbind_surface(window: *mut Window); - - fn com_wl_begin_transaction(window: *mut Window); - - fn com_wl_add_surface( - window: *mut Window, - id: u64, - x: i32, - y: i32, - clip_x: i32, - clip_y: i32, - clip_w: i32, - clip_h: i32, - ); - - fn com_wl_end_transaction(window: *mut Window); - - fn com_wl_deinit(window: *mut Window); -} - -pub fn create_window( - width: i32, - height: i32, - enable_compositor: bool, - sync_mode: i32, -) -> *mut Window { - unsafe { - com_wl_create_window(width, height, enable_compositor, sync_mode) - } -} - -pub fn destroy_window(window: *mut Window) { - unsafe { - com_wl_destroy_window(window); - } -} - -pub fn tick(window: *mut Window) -> bool { - unsafe { - com_wl_tick(window) - } -} - -pub fn get_proc_address(name: *const c_char) -> *const c_void { - unsafe { - com_wl_get_proc_address(name) - } -} - -pub fn create_surface( - window: *mut Window, - id: u64, - tile_width: i32, - tile_height: i32, - is_opaque: bool, -) { - unsafe { - com_wl_create_surface( - window, - id, - tile_width, - tile_height, - is_opaque, - ) - } -} - -pub fn create_tile( - window: *mut Window, - id: u64, - x: i32, - y: i32, -) { - unsafe { - com_wl_create_tile( - window, - id, - x, - y, - ) - } -} - -pub fn destroy_tile( - window: *mut Window, - id: u64, - x: i32, - y: i32, -) { - unsafe { - com_wl_destroy_tile( - window, - id, - x, - y, - ) - } -} - -pub fn destroy_surface( - window: *mut Window, - id: u64, -) { - unsafe { - com_wl_destroy_surface( - window, - id, - ) - } -} - -pub fn bind_surface( - window: *mut Window, - surface_id: u64, - tile_x: i32, - tile_y: i32, - dirty_x0: i32, - dirty_y0: i32, - dirty_width: i32, - dirty_height: i32, -) -> (u32, i32, i32) { - unsafe { - let mut x_offset = 0; - let mut y_offset = 0; - - let fbo_id = com_wl_bind_surface( - window, - surface_id, - tile_x, - tile_y, - &mut x_offset, - &mut y_offset, - dirty_x0, - dirty_y0, - dirty_width, - dirty_height, - ); - - (fbo_id, x_offset, y_offset) - } -} - -pub fn add_surface( - window: *mut Window, - id: u64, - x: i32, - y: i32, - clip_x: i32, - clip_y: i32, - clip_w: i32, - clip_h: i32, -) { - unsafe { - com_wl_add_surface( - window, - id, - x, - y, - clip_x, - clip_y, - clip_w, - clip_h, - ) - } -} - -pub fn begin_transaction(window: *mut Window) { - unsafe { - com_wl_begin_transaction(window) - } -} - -pub fn unbind_surface(window: *mut Window) { - unsafe { - com_wl_unbind_surface(window) - } -} - -pub fn end_transaction(window: *mut Window) { - unsafe { - com_wl_end_transaction(window) - } -} - -pub fn swap_buffers(window: *mut Window) { - unsafe { - com_wl_swap_buffers(window); - } -} - -pub fn deinit(window: *mut Window) { - unsafe { - com_wl_deinit(window); - } -} diff --git a/third_party/webrender/example-compositor/compositor-windows/src/lib.cpp b/third_party/webrender/example-compositor/compositor-windows/src/lib.cpp index d39726ea0ab..e17f602a2ec 100644 --- a/third_party/webrender/example-compositor/compositor-windows/src/lib.cpp +++ b/third_party/webrender/example-compositor/compositor-windows/src/lib.cpp @@ -31,664 +31,772 @@ #define VIRTUAL_OFFSET 512 * 1024 enum SyncMode { - None = 0, - Swap = 1, - Commit = 2, - Flush = 3, - Query = 4, + None = 0, + Swap = 1, + Commit = 2, + Flush = 3, + Query = 4, }; // The OS compositor representation of a picture cache tile. struct Tile { #ifndef USE_VIRTUAL_SURFACES - // Represents the underlying DirectComposition surface texture that gets drawn - // into. - IDCompositionSurface* pSurface; - // Represents the node in the visual tree that defines the properties of this - // tile (clip, position etc). - IDCompositionVisual2* pVisual; + // Represents the underlying DirectComposition surface texture that gets drawn into. + IDCompositionSurface *pSurface; + // Represents the node in the visual tree that defines the properties of this tile (clip, position etc). + IDCompositionVisual2 *pVisual; #endif }; struct TileKey { - int x; - int y; + int x; + int y; - TileKey(int ax, int ay) : x(ax), y(ay) {} + TileKey(int ax, int ay) : x(ax), y(ay) {} }; -bool operator==(const TileKey& k0, const TileKey& k1) { - return k0.x == k1.x && k0.y == k1.y; +bool operator==(const TileKey &k0, const TileKey &k1) { + return k0.x == k1.x && k0.y == k1.y; } struct TileKeyHasher { - size_t operator()(const TileKey& key) const { return key.x ^ key.y; } + size_t operator()(const TileKey &key) const { + return key.x ^ key.y; + } }; struct Surface { - int tile_width; - int tile_height; - bool is_opaque; - std::unordered_map<TileKey, Tile, TileKeyHasher> tiles; - IDCompositionVisual2* pVisual; + int tile_width; + int tile_height; + bool is_opaque; + std::unordered_map<TileKey, Tile, TileKeyHasher> tiles; + IDCompositionVisual2 *pVisual; #ifdef USE_VIRTUAL_SURFACES - IDCompositionVirtualSurface* pVirtualSurface; + IDCompositionVirtualSurface *pVirtualSurface; #endif }; struct CachedFrameBuffer { - int width; - int height; - GLuint fboId; - GLuint depthRboId; + int width; + int height; + GLuint fboId; + GLuint depthRboId; }; struct Window { - // Win32 window details - HWND hWnd; - HINSTANCE hInstance; - bool enable_compositor; - RECT client_rect; - SyncMode sync_mode; - - // Main interfaces to D3D11 and DirectComposition - ID3D11Device* pD3D11Device; - IDCompositionDesktopDevice* pDCompDevice; - IDCompositionTarget* pDCompTarget; - IDXGIDevice* pDXGIDevice; - ID3D11Query* pQueries[NUM_QUERIES]; - int current_query; - - // ANGLE interfaces that wrap the D3D device - EGLDeviceEXT EGLDevice; - EGLDisplay EGLDisplay; - EGLContext EGLContext; - EGLConfig config; - // Framebuffer surface for debug mode when we are not using DC - EGLSurface fb_surface; - - // The currently bound surface, valid during bind() and unbind() - IDCompositionSurface* pCurrentSurface; - EGLImage mEGLImage; - GLuint mColorRBO; - - // The root of the DC visual tree. Nothing is drawn on this, but - // all child tiles are parented to here. - IDCompositionVisual2* pRoot; - IDCompositionVisualDebug* pVisualDebug; - std::vector<CachedFrameBuffer> mFrameBuffers; - - // Maintain list of layer state between frames to avoid visual tree rebuild. - std::vector<uint64_t> mCurrentLayers; - std::vector<uint64_t> mPrevLayers; - - // Maps WR surface IDs to each OS surface - std::unordered_map<uint64_t, Surface> surfaces; + // Win32 window details + HWND hWnd; + HINSTANCE hInstance; + bool enable_compositor; + RECT client_rect; + SyncMode sync_mode; + + // Main interfaces to D3D11 and DirectComposition + ID3D11Device *pD3D11Device; + IDCompositionDesktopDevice *pDCompDevice; + IDCompositionTarget *pDCompTarget; + IDXGIDevice *pDXGIDevice; + ID3D11Query *pQueries[NUM_QUERIES]; + int current_query; + + // ANGLE interfaces that wrap the D3D device + EGLDeviceEXT EGLDevice; + EGLDisplay EGLDisplay; + EGLContext EGLContext; + EGLConfig config; + // Framebuffer surface for debug mode when we are not using DC + EGLSurface fb_surface; + + // The currently bound surface, valid during bind() and unbind() + IDCompositionSurface *pCurrentSurface; + EGLImage mEGLImage; + GLuint mColorRBO; + + // The root of the DC visual tree. Nothing is drawn on this, but + // all child tiles are parented to here. + IDCompositionVisual2 *pRoot; + IDCompositionVisualDebug *pVisualDebug; + std::vector<CachedFrameBuffer> mFrameBuffers; + + // Maintain list of layer state between frames to avoid visual tree rebuild. + std::vector<uint64_t> mCurrentLayers; + std::vector<uint64_t> mPrevLayers; + + // Maps WR surface IDs to each OS surface + std::unordered_map<uint64_t, Surface> surfaces; }; -static const wchar_t* CLASS_NAME = L"WR DirectComposite"; +static const wchar_t *CLASS_NAME = L"WR DirectComposite"; -static GLuint GetOrCreateFbo(Window* window, int aWidth, int aHeight) { - GLuint fboId = 0; +static GLuint GetOrCreateFbo(Window *window, int aWidth, int aHeight) { + GLuint fboId = 0; - // Check if we have a cached FBO with matching dimensions - for (auto it = window->mFrameBuffers.begin(); - it != window->mFrameBuffers.end(); ++it) { - if (it->width == aWidth && it->height == aHeight) { - fboId = it->fboId; - break; + // Check if we have a cached FBO with matching dimensions + for (auto it = window->mFrameBuffers.begin(); it != window->mFrameBuffers.end(); ++it) { + if (it->width == aWidth && it->height == aHeight) { + fboId = it->fboId; + break; + } } - } - // If not, create a new FBO with attached depth buffer - if (fboId == 0) { - // Create the depth buffer - GLuint depthRboId; - glGenRenderbuffers(1, &depthRboId); - glBindRenderbuffer(GL_RENDERBUFFER, depthRboId); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, aWidth, - aHeight); - - // Create the framebuffer and attach the depth buffer to it - glGenFramebuffers(1, &fboId); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, depthRboId); - - // Store this in the cache for future calls. - CachedFrameBuffer frame_buffer_info; - frame_buffer_info.width = aWidth; - frame_buffer_info.height = aHeight; - frame_buffer_info.fboId = fboId; - frame_buffer_info.depthRboId = depthRboId; - window->mFrameBuffers.push_back(frame_buffer_info); - } - - return fboId; + // If not, create a new FBO with attached depth buffer + if (fboId == 0) { + // Create the depth buffer + GLuint depthRboId; + glGenRenderbuffers(1, &depthRboId); + glBindRenderbuffer(GL_RENDERBUFFER, depthRboId); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, + aWidth, aHeight); + + // Create the framebuffer and attach the depth buffer to it + glGenFramebuffers(1, &fboId); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, depthRboId); + + // Store this in the cache for future calls. + CachedFrameBuffer frame_buffer_info; + frame_buffer_info.width = aWidth; + frame_buffer_info.height = aHeight; + frame_buffer_info.fboId = fboId; + frame_buffer_info.depthRboId = depthRboId; + window->mFrameBuffers.push_back(frame_buffer_info); + } + + return fboId; } -static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, - LPARAM lParam) { - switch (message) { - case WM_DESTROY: - PostQuitMessage(0); - return 1; - } +static LRESULT CALLBACK WndProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam +) { + switch (message) { + case WM_DESTROY: + PostQuitMessage(0); + return 1; + } - return DefWindowProc(hwnd, message, wParam, lParam); + return DefWindowProc(hwnd, message, wParam, lParam); } extern "C" { -Window* com_dc_create_window(int width, int height, bool enable_compositor, - SyncMode sync_mode) { - // Create a simple Win32 window - Window* window = new Window; - window->hInstance = GetModuleHandle(NULL); - window->enable_compositor = enable_compositor; - window->mEGLImage = EGL_NO_IMAGE; - window->sync_mode = sync_mode; - - WNDCLASSEX wcex = {sizeof(WNDCLASSEX)}; - wcex.style = CS_HREDRAW | CS_VREDRAW; - wcex.lpfnWndProc = WndProc; - wcex.cbClsExtra = 0; - wcex.cbWndExtra = 0; - wcex.hInstance = window->hInstance; - wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); - ; - wcex.lpszMenuName = nullptr; - wcex.hCursor = LoadCursor(NULL, IDC_ARROW); - wcex.lpszClassName = CLASS_NAME; - RegisterClassEx(&wcex); - - int dpiX = 0; - int dpiY = 0; - HDC hdc = GetDC(NULL); - if (hdc) { - dpiX = GetDeviceCaps(hdc, LOGPIXELSX); - dpiY = GetDeviceCaps(hdc, LOGPIXELSY); - ReleaseDC(NULL, hdc); - } - - RECT window_rect = {0, 0, width, height}; - AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, FALSE); - UINT window_width = static_cast<UINT>( - ceil(float(window_rect.right - window_rect.left) * dpiX / 96.f)); - UINT window_height = static_cast<UINT>( - ceil(float(window_rect.bottom - window_rect.top) * dpiY / 96.f)); - - LPCWSTR name; - DWORD style; - if (enable_compositor) { - name = L"example-compositor (DirectComposition)"; - style = WS_EX_NOREDIRECTIONBITMAP; - } else { - name = L"example-compositor (Simple)"; - style = 0; - } - - window->hWnd = - CreateWindowEx(style, CLASS_NAME, name, WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, window_width, window_height, - NULL, NULL, window->hInstance, NULL); - - ShowWindow(window->hWnd, SW_SHOWNORMAL); - UpdateWindow(window->hWnd); - GetClientRect(window->hWnd, &window->client_rect); - - // Create a D3D11 device - D3D_FEATURE_LEVEL featureLevelSupported; - HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, NULL, 0, - D3D11_SDK_VERSION, &window->pD3D11Device, - &featureLevelSupported, nullptr); - assert(SUCCEEDED(hr)); - - D3D11_QUERY_DESC query_desc; - memset(&query_desc, 0, sizeof(query_desc)); - query_desc.Query = D3D11_QUERY_EVENT; - for (int i = 0; i < NUM_QUERIES; ++i) { - hr = window->pD3D11Device->CreateQuery(&query_desc, &window->pQueries[i]); - assert(SUCCEEDED(hr)); - } - window->current_query = 0; - - hr = window->pD3D11Device->QueryInterface(&window->pDXGIDevice); - assert(SUCCEEDED(hr)); - - // Create a DirectComposition device - hr = DCompositionCreateDevice2(window->pDXGIDevice, - __uuidof(IDCompositionDesktopDevice), - (void**)&window->pDCompDevice); - assert(SUCCEEDED(hr)); - - // Create a DirectComposition target for a Win32 window handle - hr = window->pDCompDevice->CreateTargetForHwnd(window->hWnd, TRUE, - &window->pDCompTarget); - assert(SUCCEEDED(hr)); - - // Create an ANGLE EGL device that wraps D3D11 - window->EGLDevice = eglCreateDeviceANGLE(EGL_D3D11_DEVICE_ANGLE, - window->pD3D11Device, nullptr); - - EGLint display_attribs[] = {EGL_NONE}; - - window->EGLDisplay = eglGetPlatformDisplayEXT( - EGL_PLATFORM_DEVICE_EXT, window->EGLDevice, display_attribs); - - eglInitialize(window->EGLDisplay, nullptr, nullptr); - - EGLint num_configs = 0; - EGLint cfg_attribs[] = {EGL_SURFACE_TYPE, - EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, - EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, - 8, - EGL_GREEN_SIZE, - 8, - EGL_BLUE_SIZE, - 8, - EGL_ALPHA_SIZE, - 8, - EGL_DEPTH_SIZE, - 24, - EGL_NONE}; - EGLConfig configs[32]; - - eglChooseConfig(window->EGLDisplay, cfg_attribs, configs, - sizeof(configs) / sizeof(EGLConfig), &num_configs); - assert(num_configs > 0); - window->config = configs[0]; - - if (window->enable_compositor) { - window->fb_surface = EGL_NO_SURFACE; - } else { - window->fb_surface = eglCreateWindowSurface( - window->EGLDisplay, window->config, window->hWnd, NULL); - assert(window->fb_surface != EGL_NO_SURFACE); - } - - EGLint ctx_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; - - // Create an EGL context that can be used for drawing - window->EGLContext = eglCreateContext(window->EGLDisplay, window->config, - EGL_NO_CONTEXT, ctx_attribs); - - // Create the root of the DirectComposition visual tree - hr = window->pDCompDevice->CreateVisual(&window->pRoot); - assert(SUCCEEDED(hr)); - hr = window->pDCompTarget->SetRoot(window->pRoot); - assert(SUCCEEDED(hr)); - - hr = window->pRoot->QueryInterface(__uuidof(IDCompositionVisualDebug), - (void**)&window->pVisualDebug); - assert(SUCCEEDED(hr)); - - // Uncomment this to see redraw regions during composite - // window->pVisualDebug->EnableRedrawRegions(); - - EGLBoolean ok = eglMakeCurrent(window->EGLDisplay, window->fb_surface, - window->fb_surface, window->EGLContext); - assert(ok); - - return window; -} + Window *com_dc_create_window(int width, int height, bool enable_compositor, SyncMode sync_mode) { + // Create a simple Win32 window + Window *window = new Window; + window->hInstance = GetModuleHandle(NULL); + window->enable_compositor = enable_compositor; + window->mEGLImage = EGL_NO_IMAGE; + window->sync_mode = sync_mode; + + WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = window->hInstance; + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);; + wcex.lpszMenuName = nullptr; + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.lpszClassName = CLASS_NAME; + RegisterClassEx(&wcex); + + int dpiX = 0; + int dpiY = 0; + HDC hdc = GetDC(NULL); + if (hdc) { + dpiX = GetDeviceCaps(hdc, LOGPIXELSX); + dpiY = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(NULL, hdc); + } + + RECT window_rect = { 0, 0, width, height }; + AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, FALSE); + UINT window_width = static_cast<UINT>(ceil(float(window_rect.right - window_rect.left) * dpiX / 96.f)); + UINT window_height = static_cast<UINT>(ceil(float(window_rect.bottom - window_rect.top) * dpiY / 96.f)); + + LPCWSTR name; + DWORD style; + if (enable_compositor) { + name = L"example-compositor (DirectComposition)"; + style = WS_EX_NOREDIRECTIONBITMAP; + } else { + name = L"example-compositor (Simple)"; + style = 0; + } + + window->hWnd = CreateWindowEx( + style, + CLASS_NAME, + name, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + window_width, + window_height, + NULL, + NULL, + window->hInstance, + NULL + ); + + ShowWindow(window->hWnd, SW_SHOWNORMAL); + UpdateWindow(window->hWnd); + GetClientRect(window->hWnd, &window->client_rect); + + // Create a D3D11 device + D3D_FEATURE_LEVEL featureLevelSupported; + HRESULT hr = D3D11CreateDevice( + nullptr, + D3D_DRIVER_TYPE_HARDWARE, + NULL, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + NULL, + 0, + D3D11_SDK_VERSION, + &window->pD3D11Device, + &featureLevelSupported, + nullptr + ); + assert(SUCCEEDED(hr)); + + D3D11_QUERY_DESC query_desc; + memset(&query_desc, 0, sizeof(query_desc)); + query_desc.Query = D3D11_QUERY_EVENT; + for (int i=0 ; i < NUM_QUERIES ; ++i) { + hr = window->pD3D11Device->CreateQuery(&query_desc, &window->pQueries[i]); + assert(SUCCEEDED(hr)); + } + window->current_query = 0; + + hr = window->pD3D11Device->QueryInterface(&window->pDXGIDevice); + assert(SUCCEEDED(hr)); + + // Create a DirectComposition device + hr = DCompositionCreateDevice2( + window->pDXGIDevice, + __uuidof(IDCompositionDesktopDevice), + (void **) &window->pDCompDevice + ); + assert(SUCCEEDED(hr)); + + // Create a DirectComposition target for a Win32 window handle + hr = window->pDCompDevice->CreateTargetForHwnd( + window->hWnd, + TRUE, + &window->pDCompTarget + ); + assert(SUCCEEDED(hr)); + + // Create an ANGLE EGL device that wraps D3D11 + window->EGLDevice = eglCreateDeviceANGLE( + EGL_D3D11_DEVICE_ANGLE, + window->pD3D11Device, + nullptr + ); + + EGLint display_attribs[] = { + EGL_NONE + }; + + window->EGLDisplay = eglGetPlatformDisplayEXT( + EGL_PLATFORM_DEVICE_EXT, + window->EGLDevice, + display_attribs + ); + + eglInitialize( + window->EGLDisplay, + nullptr, + nullptr + ); + + EGLint num_configs = 0; + EGLint cfg_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 24, + EGL_NONE + }; + EGLConfig configs[32]; + + eglChooseConfig( + window->EGLDisplay, + cfg_attribs, + configs, + sizeof(configs) / sizeof(EGLConfig), + &num_configs + ); + assert(num_configs > 0); + window->config = configs[0]; + + if (window->enable_compositor) { + window->fb_surface = EGL_NO_SURFACE; + } else { + window->fb_surface = eglCreateWindowSurface( + window->EGLDisplay, + window->config, + window->hWnd, + NULL + ); + assert(window->fb_surface != EGL_NO_SURFACE); + } + + EGLint ctx_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE + }; + + // Create an EGL context that can be used for drawing + window->EGLContext = eglCreateContext( + window->EGLDisplay, + window->config, + EGL_NO_CONTEXT, + ctx_attribs + ); + + // Create the root of the DirectComposition visual tree + hr = window->pDCompDevice->CreateVisual(&window->pRoot); + assert(SUCCEEDED(hr)); + hr = window->pDCompTarget->SetRoot(window->pRoot); + assert(SUCCEEDED(hr)); + + hr = window->pRoot->QueryInterface( + __uuidof(IDCompositionVisualDebug), + (void **) &window->pVisualDebug + ); + assert(SUCCEEDED(hr)); + + // Uncomment this to see redraw regions during composite + // window->pVisualDebug->EnableRedrawRegions(); + + EGLBoolean ok = eglMakeCurrent( + window->EGLDisplay, + window->fb_surface, + window->fb_surface, + window->EGLContext + ); + assert(ok); + + return window; + } -void com_dc_destroy_window(Window* window) { - for (auto surface_it = window->surfaces.begin(); - surface_it != window->surfaces.end(); ++surface_it) { - Surface& surface = surface_it->second; + void com_dc_destroy_window(Window *window) { + for (auto surface_it=window->surfaces.begin() ; surface_it != window->surfaces.end() ; ++surface_it) { + Surface &surface = surface_it->second; #ifndef USE_VIRTUAL_SURFACES - for (auto tile_it = surface.tiles.begin(); tile_it != surface.tiles.end(); - ++tile_it) { - tile_it->second.pSurface->Release(); - tile_it->second.pVisual->Release(); - } + for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) { + tile_it->second.pSurface->Release(); + tile_it->second.pVisual->Release(); + } #endif - surface.pVisual->Release(); - } - - if (window->fb_surface != EGL_NO_SURFACE) { - eglDestroySurface(window->EGLDisplay, window->fb_surface); - } - eglDestroyContext(window->EGLDisplay, window->EGLContext); - eglTerminate(window->EGLDisplay); - eglReleaseDeviceANGLE(window->EGLDevice); - - for (int i = 0; i < NUM_QUERIES; ++i) { - window->pQueries[i]->Release(); - } - window->pRoot->Release(); - window->pVisualDebug->Release(); - window->pD3D11Device->Release(); - window->pDXGIDevice->Release(); - window->pDCompDevice->Release(); - window->pDCompTarget->Release(); - - CloseWindow(window->hWnd); - UnregisterClass(CLASS_NAME, window->hInstance); - - delete window; -} - -bool com_dc_tick(Window*) { - // Check and dispatch the windows event loop - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - if (msg.message == WM_QUIT) { - return false; + surface.pVisual->Release(); + } + + if (window->fb_surface != EGL_NO_SURFACE) { + eglDestroySurface(window->EGLDisplay, window->fb_surface); + } + eglDestroyContext(window->EGLDisplay, window->EGLContext); + eglTerminate(window->EGLDisplay); + eglReleaseDeviceANGLE(window->EGLDevice); + + for (int i=0 ; i < NUM_QUERIES ; ++i) { + window->pQueries[i]->Release(); + } + window->pRoot->Release(); + window->pVisualDebug->Release(); + window->pD3D11Device->Release(); + window->pDXGIDevice->Release(); + window->pDCompDevice->Release(); + window->pDCompTarget->Release(); + + CloseWindow(window->hWnd); + UnregisterClass(CLASS_NAME, window->hInstance); + + delete window; } - TranslateMessage(&msg); - DispatchMessage(&msg); - } + bool com_dc_tick(Window *) { + // Check and dispatch the windows event loop + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) { + return false; + } - return true; -} + TranslateMessage(&msg); + DispatchMessage(&msg); + } -void com_dc_swap_buffers(Window* window) { - // If not using DC mode, then do a normal EGL swap buffers. - if (window->fb_surface != EGL_NO_SURFACE) { - switch (window->sync_mode) { - case SyncMode::None: - eglSwapInterval(window->EGLDisplay, 0); - break; - case SyncMode::Swap: - eglSwapInterval(window->EGLDisplay, 1); - break; - default: - assert(false); // unexpected vsync mode for simple compositor. - break; + return true; } - eglSwapBuffers(window->EGLDisplay, window->fb_surface); - } else { - switch (window->sync_mode) { - case SyncMode::None: - break; - case SyncMode::Commit: - window->pDCompDevice->WaitForCommitCompletion(); - break; - case SyncMode::Flush: - DwmFlush(); - break; - case SyncMode::Query: - // todo!!!! - break; - default: - assert(false); // unexpected vsync mode for native compositor - break; + void com_dc_swap_buffers(Window *window) { + // If not using DC mode, then do a normal EGL swap buffers. + if (window->fb_surface != EGL_NO_SURFACE) { + switch (window->sync_mode) { + case SyncMode::None: + eglSwapInterval(window->EGLDisplay, 0); + break; + case SyncMode::Swap: + eglSwapInterval(window->EGLDisplay, 1); + break; + default: + assert(false); // unexpected vsync mode for simple compositor. + break; + } + + eglSwapBuffers(window->EGLDisplay, window->fb_surface); + } else { + switch (window->sync_mode) { + case SyncMode::None: + break; + case SyncMode::Commit: + window->pDCompDevice->WaitForCommitCompletion(); + break; + case SyncMode::Flush: + DwmFlush(); + break; + case SyncMode::Query: + // todo!!!! + break; + default: + assert(false); // unexpected vsync mode for native compositor + break; + } + } } - } -} - -// Create a new DC surface -void com_dc_create_surface(Window* window, uint64_t id, int tile_width, - int tile_height, bool is_opaque) { - assert(window->surfaces.count(id) == 0); - Surface surface; - surface.tile_width = tile_width; - surface.tile_height = tile_height; - surface.is_opaque = is_opaque; - - // Create the visual node in the DC tree that stores properties - HRESULT hr = window->pDCompDevice->CreateVisual(&surface.pVisual); - assert(SUCCEEDED(hr)); + // Create a new DC surface + void com_dc_create_surface( + Window *window, + uint64_t id, + int tile_width, + int tile_height, + bool is_opaque + ) { + assert(window->surfaces.count(id) == 0); + + Surface surface; + surface.tile_width = tile_width; + surface.tile_height = tile_height; + surface.is_opaque = is_opaque; + + // Create the visual node in the DC tree that stores properties + HRESULT hr = window->pDCompDevice->CreateVisual(&surface.pVisual); + assert(SUCCEEDED(hr)); #ifdef USE_VIRTUAL_SURFACES - DXGI_ALPHA_MODE alpha_mode = surface.is_opaque - ? DXGI_ALPHA_MODE_IGNORE - : DXGI_ALPHA_MODE_PREMULTIPLIED; - - hr = window->pDCompDevice->CreateVirtualSurface( - VIRTUAL_OFFSET * 2, VIRTUAL_OFFSET * 2, DXGI_FORMAT_B8G8R8A8_UNORM, - alpha_mode, &surface.pVirtualSurface); - assert(SUCCEEDED(hr)); - - // Bind the surface memory to this visual - hr = surface.pVisual->SetContent(surface.pVirtualSurface); - assert(SUCCEEDED(hr)); + DXGI_ALPHA_MODE alpha_mode = surface.is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; + + hr = window->pDCompDevice->CreateVirtualSurface( + VIRTUAL_OFFSET * 2, + VIRTUAL_OFFSET * 2, + DXGI_FORMAT_B8G8R8A8_UNORM, + alpha_mode, + &surface.pVirtualSurface + ); + assert(SUCCEEDED(hr)); + + // Bind the surface memory to this visual + hr = surface.pVisual->SetContent(surface.pVirtualSurface); + assert(SUCCEEDED(hr)); #endif - window->surfaces[id] = surface; -} + window->surfaces[id] = surface; + } -void com_dc_create_tile(Window* window, uint64_t id, int x, int y) { - assert(window->surfaces.count(id) == 1); - Surface& surface = window->surfaces[id]; + void com_dc_create_tile( + Window *window, + uint64_t id, + int x, + int y + ) { + assert(window->surfaces.count(id) == 1); + Surface &surface = window->surfaces[id]; - TileKey key(x, y); - assert(surface.tiles.count(key) == 0); + TileKey key(x, y); + assert(surface.tiles.count(key) == 0); - Tile tile; + Tile tile; #ifndef USE_VIRTUAL_SURFACES - // Create the video memory surface. - DXGI_ALPHA_MODE alpha_mode = surface.is_opaque - ? DXGI_ALPHA_MODE_IGNORE - : DXGI_ALPHA_MODE_PREMULTIPLIED; - HRESULT hr = window->pDCompDevice->CreateSurface( - surface.tile_width, surface.tile_height, DXGI_FORMAT_B8G8R8A8_UNORM, - alpha_mode, &tile.pSurface); - assert(SUCCEEDED(hr)); - - // Create the visual node in the DC tree that stores properties - hr = window->pDCompDevice->CreateVisual(&tile.pVisual); - assert(SUCCEEDED(hr)); - - // Bind the surface memory to this visual - hr = tile.pVisual->SetContent(tile.pSurface); - assert(SUCCEEDED(hr)); - - // Place the visual in local-space of this surface - float offset_x = (float)(x * surface.tile_width); - float offset_y = (float)(y * surface.tile_height); - tile.pVisual->SetOffsetX(offset_x); - tile.pVisual->SetOffsetY(offset_y); - - surface.pVisual->AddVisual(tile.pVisual, FALSE, NULL); + // Create the video memory surface. + DXGI_ALPHA_MODE alpha_mode = surface.is_opaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED; + HRESULT hr = window->pDCompDevice->CreateSurface( + surface.tile_width, + surface.tile_height, + DXGI_FORMAT_B8G8R8A8_UNORM, + alpha_mode, + &tile.pSurface + ); + assert(SUCCEEDED(hr)); + + // Create the visual node in the DC tree that stores properties + hr = window->pDCompDevice->CreateVisual(&tile.pVisual); + assert(SUCCEEDED(hr)); + + // Bind the surface memory to this visual + hr = tile.pVisual->SetContent(tile.pSurface); + assert(SUCCEEDED(hr)); + + // Place the visual in local-space of this surface + float offset_x = (float) (x * surface.tile_width); + float offset_y = (float) (y * surface.tile_height); + tile.pVisual->SetOffsetX(offset_x); + tile.pVisual->SetOffsetY(offset_y); + + surface.pVisual->AddVisual( + tile.pVisual, + FALSE, + NULL + ); #endif - surface.tiles[key] = tile; -} + surface.tiles[key] = tile; + } -void com_dc_destroy_tile(Window* window, uint64_t id, int x, int y) { - assert(window->surfaces.count(id) == 1); - Surface& surface = window->surfaces[id]; + void com_dc_destroy_tile( + Window *window, + uint64_t id, + int x, + int y + ) { + assert(window->surfaces.count(id) == 1); + Surface &surface = window->surfaces[id]; - TileKey key(x, y); - assert(surface.tiles.count(key) == 1); - Tile& tile = surface.tiles[key]; + TileKey key(x, y); + assert(surface.tiles.count(key) == 1); + Tile &tile = surface.tiles[key]; #ifndef USE_VIRTUAL_SURFACES - surface.pVisual->RemoveVisual(tile.pVisual); + surface.pVisual->RemoveVisual(tile.pVisual); - tile.pVisual->Release(); - tile.pSurface->Release(); + tile.pVisual->Release(); + tile.pSurface->Release(); #endif - surface.tiles.erase(key); -} + surface.tiles.erase(key); + } -void com_dc_destroy_surface(Window* window, uint64_t id) { - assert(window->surfaces.count(id) == 1); - Surface& surface = window->surfaces[id]; + void com_dc_destroy_surface( + Window *window, + uint64_t id + ) { + assert(window->surfaces.count(id) == 1); + Surface &surface = window->surfaces[id]; - window->pRoot->RemoveVisual(surface.pVisual); + window->pRoot->RemoveVisual(surface.pVisual); #ifdef USE_VIRTUAL_SURFACES - surface.pVirtualSurface->Release(); + surface.pVirtualSurface->Release(); #else - // Release the video memory and visual in the tree - for (auto tile_it = surface.tiles.begin(); tile_it != surface.tiles.end(); - ++tile_it) { - tile_it->second.pSurface->Release(); - tile_it->second.pVisual->Release(); - } + // Release the video memory and visual in the tree + for (auto tile_it=surface.tiles.begin() ; tile_it != surface.tiles.end() ; ++tile_it) { + tile_it->second.pSurface->Release(); + tile_it->second.pVisual->Release(); + } #endif - surface.pVisual->Release(); - window->surfaces.erase(id); -} + surface.pVisual->Release(); + window->surfaces.erase(id); + } -// Bind a DC surface to allow issuing GL commands to it -GLuint com_dc_bind_surface(Window* window, uint64_t surface_id, int tile_x, - int tile_y, int* x_offset, int* y_offset, - int dirty_x0, int dirty_y0, int dirty_width, - int dirty_height) { - assert(window->surfaces.count(surface_id) == 1); - Surface& surface = window->surfaces[surface_id]; - - TileKey key(tile_x, tile_y); - assert(surface.tiles.count(key) == 1); - Tile& tile = surface.tiles[key]; - - // Inform DC that we want to draw on this surface. DC uses texture - // atlases when the tiles are small. It returns an offset where the - // client code must draw into this surface when this happens. - RECT update_rect; - update_rect.left = dirty_x0; - update_rect.top = dirty_y0; - update_rect.right = dirty_x0 + dirty_width; - update_rect.bottom = dirty_y0 + dirty_height; - POINT offset; - D3D11_TEXTURE2D_DESC desc; - ID3D11Texture2D* pTexture; - HRESULT hr; - - // Store the current surface for unbinding later + // Bind a DC surface to allow issuing GL commands to it + GLuint com_dc_bind_surface( + Window *window, + uint64_t surface_id, + int tile_x, + int tile_y, + int *x_offset, + int *y_offset, + int dirty_x0, + int dirty_y0, + int dirty_width, + int dirty_height + ) { + assert(window->surfaces.count(surface_id) == 1); + Surface &surface = window->surfaces[surface_id]; + + TileKey key(tile_x, tile_y); + assert(surface.tiles.count(key) == 1); + Tile &tile = surface.tiles[key]; + + // Inform DC that we want to draw on this surface. DC uses texture + // atlases when the tiles are small. It returns an offset where the + // client code must draw into this surface when this happens. + RECT update_rect; + update_rect.left = dirty_x0; + update_rect.top = dirty_y0; + update_rect.right = dirty_x0 + dirty_width; + update_rect.bottom = dirty_y0 + dirty_height; + POINT offset; + D3D11_TEXTURE2D_DESC desc; + ID3D11Texture2D *pTexture; + HRESULT hr; + + // Store the current surface for unbinding later #ifdef USE_VIRTUAL_SURFACES - LONG tile_offset_x = VIRTUAL_OFFSET + tile_x * surface.tile_width; - LONG tile_offset_y = VIRTUAL_OFFSET + tile_y * surface.tile_height; - - update_rect.left += tile_offset_x; - update_rect.top += tile_offset_y; - update_rect.right += tile_offset_x; - update_rect.bottom += tile_offset_y; - - hr = surface.pVirtualSurface->BeginDraw( - &update_rect, __uuidof(ID3D11Texture2D), (void**)&pTexture, &offset); - window->pCurrentSurface = surface.pVirtualSurface; + LONG tile_offset_x = VIRTUAL_OFFSET + tile_x * surface.tile_width; + LONG tile_offset_y = VIRTUAL_OFFSET + tile_y * surface.tile_height; + + update_rect.left += tile_offset_x; + update_rect.top += tile_offset_y; + update_rect.right += tile_offset_x; + update_rect.bottom += tile_offset_y; + + hr = surface.pVirtualSurface->BeginDraw( + &update_rect, + __uuidof(ID3D11Texture2D), + (void **) &pTexture, + &offset + ); + window->pCurrentSurface = surface.pVirtualSurface; #else - hr = tile.pSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D), - (void**)&pTexture, &offset); - window->pCurrentSurface = tile.pSurface; + hr = tile.pSurface->BeginDraw( + &update_rect, + __uuidof(ID3D11Texture2D), + (void **) &pTexture, + &offset + ); + window->pCurrentSurface = tile.pSurface; #endif - // DC includes the origin of the dirty / update rect in the draw offset, - // undo that here since WR expects it to be an absolute offset. - assert(SUCCEEDED(hr)); - offset.x -= dirty_x0; - offset.y -= dirty_y0; - pTexture->GetDesc(&desc); - *x_offset = offset.x; - *y_offset = offset.y; - - // Construct an EGLImage wrapper around the D3D texture for ANGLE. - const EGLAttrib attribs[] = {EGL_NONE}; - window->mEGLImage = eglCreateImage( - window->EGLDisplay, EGL_NO_CONTEXT, EGL_D3D11_TEXTURE_ANGLE, - static_cast<EGLClientBuffer>(pTexture), attribs); - - // Get the current FBO and RBO id, so we can restore them later - GLint currentFboId, currentRboId; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFboId); - glGetIntegerv(GL_RENDERBUFFER_BINDING, ¤tRboId); - - // Create a render buffer object that is backed by the EGL image. - glGenRenderbuffers(1, &window->mColorRBO); - glBindRenderbuffer(GL_RENDERBUFFER, window->mColorRBO); - glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, window->mEGLImage); - - // Get or create an FBO for the specified dimensions - GLuint fboId = GetOrCreateFbo(window, desc.Width, desc.Height); - - // Attach the new renderbuffer to the FBO - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId); - glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, window->mColorRBO); - - // Restore previous FBO and RBO bindings - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFboId); - glBindRenderbuffer(GL_RENDERBUFFER, currentRboId); - - return fboId; -} - -// Unbind a currently bound DC surface -void com_dc_unbind_surface(Window* window) { - HRESULT hr = window->pCurrentSurface->EndDraw(); - assert(SUCCEEDED(hr)); + // DC includes the origin of the dirty / update rect in the draw offset, + // undo that here since WR expects it to be an absolute offset. + assert(SUCCEEDED(hr)); + offset.x -= dirty_x0; + offset.y -= dirty_y0; + pTexture->GetDesc(&desc); + *x_offset = offset.x; + *y_offset = offset.y; + + // Construct an EGLImage wrapper around the D3D texture for ANGLE. + const EGLAttrib attribs[] = { EGL_NONE }; + window->mEGLImage = eglCreateImage( + window->EGLDisplay, + EGL_NO_CONTEXT, + EGL_D3D11_TEXTURE_ANGLE, + static_cast<EGLClientBuffer>(pTexture), + attribs + ); + + // Get the current FBO and RBO id, so we can restore them later + GLint currentFboId, currentRboId; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tFboId); + glGetIntegerv(GL_RENDERBUFFER_BINDING, ¤tRboId); + + // Create a render buffer object that is backed by the EGL image. + glGenRenderbuffers(1, &window->mColorRBO); + glBindRenderbuffer(GL_RENDERBUFFER, window->mColorRBO); + glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, window->mEGLImage); + + // Get or create an FBO for the specified dimensions + GLuint fboId = GetOrCreateFbo(window, desc.Width, desc.Height); + + // Attach the new renderbuffer to the FBO + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId); + glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, + window->mColorRBO); + + // Restore previous FBO and RBO bindings + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFboId); + glBindRenderbuffer(GL_RENDERBUFFER, currentRboId); + + return fboId; + } - glDeleteRenderbuffers(1, &window->mColorRBO); - window->mColorRBO = 0; + // Unbind a currently bound DC surface + void com_dc_unbind_surface(Window *window) { + HRESULT hr = window->pCurrentSurface->EndDraw(); + assert(SUCCEEDED(hr)); - eglDestroyImage(window->EGLDisplay, window->mEGLImage); - window->mEGLImage = EGL_NO_IMAGE; -} + glDeleteRenderbuffers(1, &window->mColorRBO); + window->mColorRBO = 0; -void com_dc_begin_transaction(Window*) {} + eglDestroyImage(window->EGLDisplay, window->mEGLImage); + window->mEGLImage = EGL_NO_IMAGE; + } -// Add a DC surface to the visual tree. Called per-frame to build the -// composition. -void com_dc_add_surface(Window* window, uint64_t id, int x, int y, int clip_x, - int clip_y, int clip_w, int clip_h) { - Surface surface = window->surfaces[id]; - window->mCurrentLayers.push_back(id); + void com_dc_begin_transaction(Window *) { + } - // Place the visual - this changes frame to frame based on scroll position - // of the slice. - float offset_x = (float)(x + window->client_rect.left); - float offset_y = (float)(y + window->client_rect.top); + // Add a DC surface to the visual tree. Called per-frame to build the composition. + void com_dc_add_surface( + Window *window, + uint64_t id, + int x, + int y, + int clip_x, + int clip_y, + int clip_w, + int clip_h + ) { + Surface surface = window->surfaces[id]; + window->mCurrentLayers.push_back(id); + + // Place the visual - this changes frame to frame based on scroll position + // of the slice. + float offset_x = (float) (x + window->client_rect.left); + float offset_y = (float) (y + window->client_rect.top); #ifdef USE_VIRTUAL_SURFACES - offset_x -= VIRTUAL_OFFSET; - offset_y -= VIRTUAL_OFFSET; + offset_x -= VIRTUAL_OFFSET; + offset_y -= VIRTUAL_OFFSET; #endif - surface.pVisual->SetOffsetX(offset_x); - surface.pVisual->SetOffsetY(offset_y); - - // Set the clip rect - converting from world space to the pre-offset space - // that DC requires for rectangle clips. - D2D_RECT_F clip_rect; - clip_rect.left = clip_x - offset_x; - clip_rect.top = clip_y - offset_y; - clip_rect.right = clip_rect.left + clip_w; - clip_rect.bottom = clip_rect.top + clip_h; - surface.pVisual->SetClip(clip_rect); -} - -// Finish the composition transaction, telling DC to composite -void com_dc_end_transaction(Window* window) { - bool same = window->mPrevLayers == window->mCurrentLayers; - - if (!same) { - HRESULT hr = window->pRoot->RemoveAllVisuals(); - assert(SUCCEEDED(hr)); - - for (auto it = window->mCurrentLayers.begin(); - it != window->mCurrentLayers.end(); ++it) { - Surface& surface = window->surfaces[*it]; - - // Add this visual as the last element in the visual tree (z-order is - // implicit, based on the order tiles are added). - hr = window->pRoot->AddVisual(surface.pVisual, FALSE, NULL); - assert(SUCCEEDED(hr)); + surface.pVisual->SetOffsetX(offset_x); + surface.pVisual->SetOffsetY(offset_y); + + // Set the clip rect - converting from world space to the pre-offset space + // that DC requires for rectangle clips. + D2D_RECT_F clip_rect; + clip_rect.left = clip_x - offset_x; + clip_rect.top = clip_y - offset_y; + clip_rect.right = clip_rect.left + clip_w; + clip_rect.bottom = clip_rect.top + clip_h; + surface.pVisual->SetClip(clip_rect); } - } - window->mPrevLayers.swap(window->mCurrentLayers); - window->mCurrentLayers.clear(); - - HRESULT hr = window->pDCompDevice->Commit(); - assert(SUCCEEDED(hr)); -} + // Finish the composition transaction, telling DC to composite + void com_dc_end_transaction(Window *window) { + bool same = window->mPrevLayers == window->mCurrentLayers; + + if (!same) { + HRESULT hr = window->pRoot->RemoveAllVisuals(); + assert(SUCCEEDED(hr)); + + for (auto it = window->mCurrentLayers.begin(); it != window->mCurrentLayers.end(); ++it) { + Surface &surface = window->surfaces[*it]; + + // Add this visual as the last element in the visual tree (z-order is implicit, + // based on the order tiles are added). + hr = window->pRoot->AddVisual( + surface.pVisual, + FALSE, + NULL + ); + assert(SUCCEEDED(hr)); + } + } + + window->mPrevLayers.swap(window->mCurrentLayers); + window->mCurrentLayers.clear(); + + HRESULT hr = window->pDCompDevice->Commit(); + assert(SUCCEEDED(hr)); + } -// Get a pointer to an EGL symbol -void* com_dc_get_proc_address(const char* name) { - return eglGetProcAddress(name); -} + // Get a pointer to an EGL symbol + void *com_dc_get_proc_address(const char *name) { + return eglGetProcAddress(name); + } } diff --git a/third_party/webrender/example-compositor/compositor-windows/src/lib.rs b/third_party/webrender/example-compositor/compositor-windows/src/lib.rs index df15b0348ab..18b80b5d1e9 100644 --- a/third_party/webrender/example-compositor/compositor-windows/src/lib.rs +++ b/third_party/webrender/example-compositor/compositor-windows/src/lib.rs @@ -88,8 +88,6 @@ extern { ); fn com_dc_end_transaction(window: *mut Window); - - fn deinit(window: *mut Window); } pub fn create_window( @@ -261,7 +259,3 @@ pub fn swap_buffers(window: *mut Window) { com_dc_swap_buffers(window); } } - -pub fn deinit(window: *mut Window) { - todo!() -} diff --git a/third_party/webrender/example-compositor/compositor/Cargo.toml b/third_party/webrender/example-compositor/compositor/Cargo.toml index 02c6ebe0ce2..d505e9ad295 100644 --- a/third_party/webrender/example-compositor/compositor/Cargo.toml +++ b/third_party/webrender/example-compositor/compositor/Cargo.toml @@ -7,10 +7,7 @@ license = "MPL-2.0" [dependencies] webrender = { path = "../../webrender" } -gleam = "0.15" +gleam = "0.12.0" [target.'cfg(windows)'.dependencies] compositor-windows = { path = "../compositor-windows" } - -[target.'cfg(target_os = "linux")'.dependencies] -compositor-wayland = { path = "../compositor-wayland" } diff --git a/third_party/webrender/example-compositor/compositor/src/main.rs b/third_party/webrender/example-compositor/compositor/src/main.rs index fef9438ec20..b2a7aac3a94 100644 --- a/third_party/webrender/example-compositor/compositor/src/main.rs +++ b/third_party/webrender/example-compositor/compositor/src/main.rs @@ -16,12 +16,10 @@ use euclid::Angle; use gleam::gl; use std::ffi::CString; use std::sync::mpsc; -use webrender::{CompositorSurfaceTransform, Transaction, api::*, euclid::point2}; +use webrender::api::*; use webrender::api::units::*; #[cfg(target_os = "windows")] use compositor_windows as compositor; -#[cfg(target_os = "linux")] -use compositor_wayland as compositor; use std::{env, f32, process}; // A very hacky integration with DirectComposite. It proxies calls from the compositor @@ -44,7 +42,6 @@ impl webrender::Compositor for DirectCompositeInterface { fn create_surface( &mut self, id: webrender::NativeSurfaceId, - _virtual_offset: DeviceIntPoint, tile_size: DeviceIntSize, is_opaque: bool, ) { @@ -92,7 +89,6 @@ impl webrender::Compositor for DirectCompositeInterface { &mut self, id: webrender::NativeTileId, dirty_rect: DeviceIntRect, - _valid_rect: DeviceIntRect, ) -> webrender::NativeSurfaceInfo { let (fbo_id, x, y) = compositor::bind_surface( self.window, @@ -122,15 +118,14 @@ impl webrender::Compositor for DirectCompositeInterface { fn add_surface( &mut self, id: webrender::NativeSurfaceId, - transform: CompositorSurfaceTransform, + position: DeviceIntPoint, clip_rect: DeviceIntRect, - _image_rendering: ImageRendering, ) { compositor::add_surface( self.window, id.0, - transform.transform_point2d(point2(0., 0.)).unwrap().x as i32, - transform.transform_point2d(point2(0., 0.)).unwrap().y as i32, + position.x, + position.y, clip_rect.origin.x, clip_rect.origin.y, clip_rect.size.width, @@ -141,42 +136,6 @@ impl webrender::Compositor for DirectCompositeInterface { fn end_frame(&mut self) { compositor::end_transaction(self.window); } - fn create_external_surface(&mut self, _: webrender::NativeSurfaceId, _: bool) { todo!() } - - fn attach_external_image( - &mut self, - _id: webrender::NativeSurfaceId, - _external_image: ExternalImageId - ) { - todo!() - } - - fn enable_native_compositor(&mut self, _enable: bool) { - todo!() - } - - fn deinit(&mut self) { - compositor::deinit(self.window); - } - - fn get_capabilities(&self) -> webrender::CompositorCapabilities { - webrender::CompositorCapabilities { - virtual_surface_size: 1024 * 1024, - ..Default::default() - } - } - - fn invalidate_tile( - &mut self, - _id: webrender::NativeTileId, - _valid_rect: DeviceIntRect, - ) {} - - fn start_compositing( - &mut self, - _dirty_rects: &[DeviceIntRect], - _opaque_rects: &[DeviceIntRect], - ) {} } // Simplisitic implementation of the WR notifier interface to know when a frame @@ -200,7 +159,7 @@ impl RenderNotifier for Notifier { }) } - fn wake_up(&self, _composite_needed: bool) { + fn wake_up(&self) { } fn new_frame_ready(&self, @@ -222,7 +181,7 @@ fn push_rotated_rect( time: f32, ) { let color = color.scale_rgb(time); - let rotation = LayoutTransform::rotation( + let rotation = LayoutTransform::create_rotation( 0.0, 0.0, 1.0, @@ -235,16 +194,13 @@ fn push_rotated_rect( ); let transform = rotation .pre_translate(-transform_origin) - .then_translate(transform_origin); + .post_translate(transform_origin); let spatial_id = builder.push_reference_frame( LayoutPoint::zero(), spatial_id, TransformStyle::Flat, PropertyBinding::Value(transform), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, + ReferenceFrameKind::Transform, ); builder.push_rect( &CommonItemProperties::new( @@ -279,7 +235,7 @@ fn build_display_list( let scroll_space_info = builder.define_scroll_frame( &fixed_space_info, - scroll_id, + Some(scroll_id), LayoutRect::new(LayoutPoint::zero(), layout_size), LayoutRect::new(LayoutPoint::zero(), layout_size), ScrollSensitivity::Script, @@ -333,32 +289,6 @@ fn build_display_list( 0.1, time, ); - - push_rotated_rect( - builder, - LayoutRect::new( - LayoutPoint::new(100.0, 600.0), - LayoutSize::new(size_factor * 400.0, size_factor * 400.0), - ), - ColorF::new(1.0, 1.0, 0.0, 1.0), - scroll_space_info.spatial_id, - root_pipeline_id, - time, - time, - ); - - push_rotated_rect( - builder, - LayoutRect::new( - LayoutPoint::new(700.0, 600.0), - LayoutSize::new(size_factor * 400.0, size_factor * 400.0), - ), - ColorF::new(0.0, 1.0, 1.0, 1.0), - scroll_space_info.spatial_id, - root_pipeline_id, - time, - time, - ); } #[derive(Debug, Copy, Clone)] @@ -428,15 +358,13 @@ fn main() { } else { webrender::CompositorConfig::Draw { max_partial_present_rects: 0, - draw_previous_partial_present_regions: false, - partial_present: None, } }; let opts = webrender::RendererOptions { clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)), debug_flags, + enable_picture_caching: true, compositor_config, - surface_origin_is_top_left: false, ..webrender::RendererOptions::default() }; let (tx, rx) = mpsc::channel(); @@ -455,9 +383,10 @@ fn main() { notifier, opts, None, + device_size, ).unwrap(); - let mut api = sender.create_api(); - let document_id = api.add_document(device_size); + let api = sender.create_api(); + let document_id = api.add_document(device_size, 0); let device_pixel_ratio = 1.0; let mut current_epoch = Epoch(0); let root_pipeline_id = PipelineId(0, 0); @@ -470,7 +399,7 @@ fn main() { txn.set_root_pipeline(root_pipeline_id); if let Invalidations::Scrolling = inv_mode { - let mut root_builder = DisplayListBuilder::new(root_pipeline_id); + let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size); build_display_list( &mut root_builder, @@ -490,7 +419,7 @@ fn main() { ); } - txn.generate_frame(0); + txn.generate_frame(); api.send_transaction(document_id, txn); // Tick the compositor (in this sample, we don't block on UI events) @@ -500,7 +429,7 @@ fn main() { // Update and render. This will invoke the native compositor interface implemented above // as required. renderer.update(); - renderer.render(device_size, 0).unwrap(); + renderer.render(device_size).unwrap(); let _ = renderer.flush_pipeline_info(); // Construct a simple display list that can be drawn and composited by DC. @@ -508,7 +437,7 @@ fn main() { match inv_mode { Invalidations::Small | Invalidations::Large => { - let mut root_builder = DisplayListBuilder::new(root_pipeline_id); + let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size); build_display_list( &mut root_builder, @@ -537,7 +466,7 @@ fn main() { } } - txn.generate_frame(0); + txn.generate_frame(); api.send_transaction(document_id, txn); current_epoch.0 += 1; time += 0.001; diff --git a/third_party/webrender/examples/Cargo.toml b/third_party/webrender/examples/Cargo.toml index f1a4718b04a..4cf5d2232d6 100644 --- a/third_party/webrender/examples/Cargo.toml +++ b/third_party/webrender/examples/Cargo.toml @@ -27,6 +27,10 @@ name = "document" path = "document.rs" [[bin]] +name = "frame_output" +path = "frame_output.rs" + +[[bin]] name = "iframe" path = "iframe.rs" @@ -51,13 +55,13 @@ name = "yuv" path = "yuv.rs" [features] -debug = ["webrender/capture", "webrender/profiler"] +debug = ["webrender/capture", "webrender/debugger", "webrender/profiler"] [dependencies] app_units = "0.7" env_logger = "0.5" euclid = "0.22" -gleam = "0.15" +gleam = "0.12" glutin = "0.21" rayon = "1" webrender = { path = "../webrender" } diff --git a/third_party/webrender/examples/alpha_perf.rs b/third_party/webrender/examples/alpha_perf.rs index 3d8d019de1f..9200e65c8d2 100644 --- a/third_party/webrender/examples/alpha_perf.rs +++ b/third_party/webrender/examples/alpha_perf.rs @@ -2,11 +2,11 @@ * 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 euclid; -extern crate gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use euclid; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -14,7 +14,6 @@ mod boilerplate; use crate::boilerplate::{Example, HandyDandyRectBuilder}; use std::cmp; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::DeviceIntSize; diff --git a/third_party/webrender/examples/animation.rs b/third_party/webrender/examples/animation.rs index 00e5c0a9a4a..2e87e78b177 100644 --- a/third_party/webrender/examples/animation.rs +++ b/third_party/webrender/examples/animation.rs @@ -10,11 +10,11 @@ //! rounded cornered rectangle, which is done automatically during the //! scene building for render optimization. -extern crate euclid; -extern crate gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use euclid; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -22,7 +22,6 @@ mod boilerplate; use crate::boilerplate::{Example, HandyDandyRectBuilder}; use euclid::Angle; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; @@ -63,10 +62,7 @@ impl App { SpatialId::root_scroll_node(pipeline_id), TransformStyle::Flat, PropertyBinding::Binding(property_key, LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, + ReferenceFrameKind::Transform, ); builder.push_simple_stacking_context_with_filters( @@ -198,7 +194,7 @@ impl Example for App { colors: vec![], }, ); - txn.generate_frame(0); + txn.generate_frame(); api.send_transaction(document_id, txn); } _ => (), diff --git a/third_party/webrender/examples/basic.rs b/third_party/webrender/examples/basic.rs index 549d0e39a11..0844994400a 100644 --- a/third_party/webrender/examples/basic.rs +++ b/third_party/webrender/examples/basic.rs @@ -2,11 +2,11 @@ * 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 euclid; -extern crate gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use euclid; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -17,7 +17,6 @@ use winit::TouchPhase; use std::collections::HashMap; use webrender::ShaderPrecacheFlags; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; @@ -191,7 +190,7 @@ impl Example for App { pipeline_id: PipelineId, _document_id: DocumentId, ) { - let content_bounds = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(800.0, 600.0)); + let content_bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size()); let root_space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id); let spatial_id = root_space_and_clip.spatial_id; @@ -221,8 +220,6 @@ impl Example for App { let mask_clip_id = builder.define_clip_image_mask( &root_space_and_clip, mask, - &vec![], - FillRule::Nonzero, ); let clip_id = builder.define_clip_rounded_rect( &SpaceAndClipInfo { @@ -315,7 +312,7 @@ impl Example for App { } if !txn.is_empty() { - txn.generate_frame(0); + txn.generate_frame(); api.send_transaction(document_id, txn); } diff --git a/third_party/webrender/examples/blob.rs b/third_party/webrender/examples/blob.rs index 65c048c87be..ea6536cea83 100644 --- a/third_party/webrender/examples/blob.rs +++ b/third_party/webrender/examples/blob.rs @@ -2,11 +2,11 @@ * 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 gleam; -extern crate glutin; -extern crate rayon; -extern crate webrender; -extern crate winit; +use gleam; +use glutin; +use rayon; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -16,10 +16,9 @@ use rayon::{ThreadPool, ThreadPoolBuilder}; use rayon::prelude::*; use std::collections::HashMap; use std::sync::Arc; -use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, PrimitiveFlags}; +use webrender::api::{self, DisplayListBuilder, DocumentId, PipelineId, PrimitiveFlags, RenderApi, Transaction}; use webrender::api::{ColorF, CommonItemProperties, SpaceAndClipInfo, ImageDescriptorFlags}; use webrender::api::units::*; -use webrender::render_api::*; use webrender::euclid::size2; // This example shows how to implement a very basic BlobImageHandler that can only render diff --git a/third_party/webrender/examples/common/boilerplate.rs b/third_party/webrender/examples/common/boilerplate.rs index d07084031ae..a48a00b9194 100644 --- a/third_party/webrender/examples/common/boilerplate.rs +++ b/third_party/webrender/examples/common/boilerplate.rs @@ -10,7 +10,6 @@ use webrender; use winit; use webrender::{DebugFlags, ShaderPrecacheFlags}; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; struct Notifier { @@ -30,7 +29,7 @@ impl RenderNotifier for Notifier { }) } - fn wake_up(&self, _composite_needed: bool) { + fn wake_up(&self) { #[cfg(not(target_os = "android"))] let _ = self.events_proxy.wakeup(); } @@ -38,9 +37,9 @@ impl RenderNotifier for Notifier { fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, - composite_needed: bool, + _composite_needed: bool, _render_time: Option<u64>) { - self.wake_up(composite_needed); + self.wake_up(); } } @@ -89,11 +88,12 @@ pub trait Example { ) -> bool { false } - fn get_image_handler( + fn get_image_handlers( &mut self, _gl: &dyn gl::Gl, - ) -> Option<Box<dyn ExternalImageHandler>> { - None + ) -> (Option<Box<dyn ExternalImageHandler>>, + Option<Box<dyn OutputImageHandler>>) { + (None, None) } fn draw_custom(&mut self, _gl: &dyn gl::Gl) { } @@ -183,11 +183,16 @@ pub fn main_wrapper<E: Example>( notifier, opts, None, + device_size, ).unwrap(); let mut api = sender.create_api(); - let document_id = api.add_document(device_size); + let document_id = api.add_document(device_size, 0); - let external = example.get_image_handler(&*gl); + let (external, output) = example.get_image_handlers(&*gl); + + if let Some(output_image_handler) = output { + renderer.set_output_image_handler(output_image_handler); + } if let Some(external_image_handler) = external { renderer.set_external_image_handler(external_image_handler); @@ -196,7 +201,7 @@ pub fn main_wrapper<E: Example>( let epoch = Epoch(0); let pipeline_id = PipelineId(0, 0); let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio); - let mut builder = DisplayListBuilder::new(pipeline_id); + let mut builder = DisplayListBuilder::new(pipeline_id, layout_size); let mut txn = Transaction::new(); example.render( @@ -215,7 +220,7 @@ pub fn main_wrapper<E: Example>( true, ); txn.set_root_pipeline(pipeline_id); - txn.generate_frame(0); + txn.generate_frame(); api.send_transaction(document_id, txn); println!("Entering event loop"); @@ -254,10 +259,14 @@ pub fn main_wrapper<E: Example>( winit::VirtualKeyCode::P => debug_flags.toggle(DebugFlags::PROFILER_DBG), winit::VirtualKeyCode::O => debug_flags.toggle(DebugFlags::RENDER_TARGET_DBG), winit::VirtualKeyCode::I => debug_flags.toggle(DebugFlags::TEXTURE_CACHE_DBG), + winit::VirtualKeyCode::S => debug_flags.toggle(DebugFlags::COMPACT_PROFILER), winit::VirtualKeyCode::T => debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG), winit::VirtualKeyCode::Q => debug_flags.toggle( DebugFlags::GPU_TIME_QUERIES | DebugFlags::GPU_SAMPLE_QUERIES ), + winit::VirtualKeyCode::F => debug_flags.toggle( + DebugFlags::NEW_FRAME_INDICATOR | DebugFlags::NEW_SCENE_INDICATOR + ), winit::VirtualKeyCode::G => debug_flags.toggle(DebugFlags::GPU_CACHE_DBG), winit::VirtualKeyCode::Key1 => txn.set_document_view( device_size.into(), @@ -295,7 +304,7 @@ pub fn main_wrapper<E: Example>( } if custom_event { - let mut builder = DisplayListBuilder::new(pipeline_id); + let mut builder = DisplayListBuilder::new(pipeline_id, layout_size); example.render( &mut api, @@ -312,12 +321,12 @@ pub fn main_wrapper<E: Example>( builder.finalize(), true, ); - txn.generate_frame(0); + txn.generate_frame(); } api.send_transaction(document_id, txn); renderer.update(); - renderer.render(device_size, 0).unwrap(); + renderer.render(device_size).unwrap(); let _ = renderer.flush_pipeline_info(); example.draw_custom(&*gl); windowed_context.swap_buffers().ok(); diff --git a/third_party/webrender/examples/document.rs b/third_party/webrender/examples/document.rs index c81a3f5c25b..e33eff4665b 100644 --- a/third_party/webrender/examples/document.rs +++ b/third_party/webrender/examples/document.rs @@ -2,11 +2,11 @@ * 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 euclid; -extern crate gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use euclid; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -14,7 +14,6 @@ mod boilerplate; use crate::boilerplate::Example; use euclid::Scale; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; // This example creates multiple documents overlapping each other with @@ -40,31 +39,35 @@ impl App { let init_data = vec![ ( PipelineId(1, 0), + -2, ColorF::new(0.0, 1.0, 0.0, 1.0), DeviceIntPoint::new(0, 0), ), ( PipelineId(2, 0), + -1, ColorF::new(1.0, 1.0, 0.0, 1.0), DeviceIntPoint::new(200, 0), ), ( PipelineId(3, 0), + 0, ColorF::new(1.0, 0.0, 0.0, 1.0), DeviceIntPoint::new(200, 200), ), ( PipelineId(4, 0), + 1, ColorF::new(1.0, 0.0, 1.0, 1.0), DeviceIntPoint::new(0, 200), ), ]; - for (pipeline_id, color, offset) in init_data { + for (pipeline_id, layer, color, offset) in init_data { let size = DeviceIntSize::new(250, 250); let bounds = DeviceIntRect::new(offset, size); - let document_id = api.add_document(size); + let document_id = api.add_document(size, layer); let mut txn = Transaction::new(); txn.set_document_view(bounds, device_pixel_ratio); txn.set_root_pipeline(pipeline_id); @@ -87,22 +90,25 @@ impl Example for App { fn render( &mut self, api: &mut RenderApi, - _base_builder: &mut DisplayListBuilder, + base_builder: &mut DisplayListBuilder, _txn: &mut Transaction, - _device_size: DeviceIntSize, + device_size: DeviceIntSize, _pipeline_id: PipelineId, _: DocumentId, ) { if self.documents.is_empty() { + let device_pixel_ratio = device_size.width as f32 / + base_builder.content_size().width; // this is the first run, hack around the boilerplate, // which assumes an example only needs one document - self.init(api, 1.0); + self.init(api, device_pixel_ratio); } for doc in &self.documents { let space_and_clip = SpaceAndClipInfo::root_scroll(doc.pipeline_id); let mut builder = DisplayListBuilder::new( doc.pipeline_id, + doc.content_rect.size, ); let local_rect = LayoutRect::new( LayoutPoint::zero(), @@ -129,7 +135,7 @@ impl Example for App { builder.finalize(), true, ); - txn.generate_frame(0); + txn.generate_frame(); api.send_transaction(doc.id, txn); } } diff --git a/third_party/webrender/examples/frame_output.rs b/third_party/webrender/examples/frame_output.rs new file mode 100644 index 00000000000..2cd612c9b4c --- /dev/null +++ b/third_party/webrender/examples/frame_output.rs @@ -0,0 +1,238 @@ +/* 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 euclid; +use gleam; +use glutin; +use webrender; +use winit; + +#[path = "common/boilerplate.rs"] +mod boilerplate; + +use crate::boilerplate::{Example, HandyDandyRectBuilder}; +use euclid::Scale; +use gleam::gl; +use webrender::api::*; +use webrender::api::units::*; + + +// This example demonstrates using the frame output feature to copy +// the output of a WR framebuffer to a custom texture. + +#[derive(Debug)] +struct Document { + id: DocumentId, + pipeline_id: PipelineId, + content_rect: LayoutRect, + color: ColorF, +} + + +struct App { + external_image_key: Option<ImageKey>, + output_document: Option<Document> +} + +struct OutputHandler { + texture_id: gl::GLuint +} + +struct ExternalHandler { + texture_id: gl::GLuint +} + +impl OutputImageHandler for OutputHandler { + fn lock(&mut self, _id: PipelineId) -> Option<(u32, FramebufferIntSize)> { + Some((self.texture_id, FramebufferIntSize::new(500, 500))) + } + + fn unlock(&mut self, _id: PipelineId) {} +} + +impl ExternalImageHandler for ExternalHandler { + fn lock( + &mut self, + _key: ExternalImageId, + _channel_index: u8, + _rendering: ImageRendering + ) -> ExternalImage { + ExternalImage { + uv: TexelRect::new(0.0, 0.0, 1.0, 1.0), + source: ExternalImageSource::NativeTexture(self.texture_id), + } + } + fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {} +} + +impl App { + fn init_output_document( + &mut self, + api: &mut RenderApi, + device_size: DeviceIntSize, + device_pixel_ratio: f32, + ) { + // Generate the external image key that will be used to render the output document to the root document. + self.external_image_key = Some(api.generate_image_key()); + + let pipeline_id = PipelineId(1, 0); + let layer = 1; + let color = ColorF::new(1., 1., 0., 1.); + let document_id = api.add_document(device_size, layer); + api.enable_frame_output(document_id, pipeline_id, true); + api.set_document_view( + document_id, + device_size.into(), + device_pixel_ratio, + ); + + let document = Document { + id: document_id, + pipeline_id, + content_rect: LayoutRect::new( + LayoutPoint::zero(), + device_size.to_f32() / Scale::new(device_pixel_ratio), + ), + color, + }; + + let mut txn = Transaction::new(); + + txn.add_image( + self.external_image_key.unwrap(), + ImageDescriptor::new(100, 100, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE), + ImageData::External(ExternalImageData { + id: ExternalImageId(0), + channel_index: 0, + image_type: ExternalImageType::TextureHandle(TextureTarget::Default), + }), + None, + ); + + let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id); + let mut builder = DisplayListBuilder::new( + document.pipeline_id, + document.content_rect.size, + ); + + builder.push_simple_stacking_context( + document.content_rect.origin, + space_and_clip.spatial_id, + PrimitiveFlags::IS_BACKFACE_VISIBLE, + ); + + builder.push_rect( + &CommonItemProperties::new(document.content_rect, space_and_clip), + document.content_rect, + ColorF::new(1.0, 1.0, 0.0, 1.0) + ); + builder.pop_stacking_context(); + + txn.set_root_pipeline(pipeline_id); + txn.set_display_list( + Epoch(0), + Some(document.color), + document.content_rect.size, + builder.finalize(), + true, + ); + txn.generate_frame(); + api.send_transaction(document.id, txn); + self.output_document = Some(document); + } +} + +impl Example for App { + fn render( + &mut self, + api: &mut RenderApi, + builder: &mut DisplayListBuilder, + _txn: &mut Transaction, + device_size: DeviceIntSize, + pipeline_id: PipelineId, + _document_id: DocumentId, + ) { + if self.output_document.is_none() { + let device_pixel_ratio = device_size.width as f32 / + builder.content_size().width; + self.init_output_document(api, DeviceIntSize::new(200, 200), device_pixel_ratio); + } + + let bounds = (100, 100).to(200, 200); + let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id); + + builder.push_simple_stacking_context( + bounds.origin, + space_and_clip.spatial_id, + PrimitiveFlags::IS_BACKFACE_VISIBLE, + ); + + builder.push_image( + &CommonItemProperties::new(bounds, space_and_clip), + bounds, + ImageRendering::Auto, + AlphaType::PremultipliedAlpha, + self.external_image_key.unwrap(), + ColorF::WHITE, + ); + + builder.pop_stacking_context(); + } + + fn get_image_handlers( + &mut self, + gl: &dyn gl::Gl, + ) -> (Option<Box<dyn ExternalImageHandler>>, + Option<Box<dyn OutputImageHandler>>) { + let texture_id = gl.gen_textures(1)[0]; + + gl.bind_texture(gl::TEXTURE_2D, texture_id); + gl.tex_parameter_i( + gl::TEXTURE_2D, + gl::TEXTURE_MAG_FILTER, + gl::LINEAR as gl::GLint, + ); + gl.tex_parameter_i( + gl::TEXTURE_2D, + gl::TEXTURE_MIN_FILTER, + gl::LINEAR as gl::GLint, + ); + gl.tex_parameter_i( + gl::TEXTURE_2D, + gl::TEXTURE_WRAP_S, + gl::CLAMP_TO_EDGE as gl::GLint, + ); + gl.tex_parameter_i( + gl::TEXTURE_2D, + gl::TEXTURE_WRAP_T, + gl::CLAMP_TO_EDGE as gl::GLint, + ); + gl.tex_image_2d( + gl::TEXTURE_2D, + 0, + gl::RGBA as gl::GLint, + 100, + 100, + 0, + gl::BGRA, + gl::UNSIGNED_BYTE, + None, + ); + gl.bind_texture(gl::TEXTURE_2D, 0); + + ( + Some(Box::new(ExternalHandler { texture_id })), + Some(Box::new(OutputHandler { texture_id })) + ) + } +} + +fn main() { + let mut app = App { + external_image_key: None, + output_document: None + }; + + boilerplate::main_wrapper(&mut app, None); +} diff --git a/third_party/webrender/examples/iframe.rs b/third_party/webrender/examples/iframe.rs index 8c7188b75a0..32c0b3a8fe8 100644 --- a/third_party/webrender/examples/iframe.rs +++ b/third_party/webrender/examples/iframe.rs @@ -2,17 +2,16 @@ * 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 gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; use crate::boilerplate::{Example, HandyDandyRectBuilder}; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; // This example uses the push_iframe API to nest a second pipeline's displaylist @@ -36,7 +35,7 @@ impl Example for App { let sub_bounds = (0, 0).to(sub_size.width as i32, sub_size.height as i32); let sub_pipeline_id = PipelineId(pipeline_id.0, 42); - let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id); + let mut sub_builder = DisplayListBuilder::new(sub_pipeline_id, sub_bounds.size); let mut space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id); sub_builder.push_simple_stacking_context( @@ -68,10 +67,7 @@ impl Example for App { space_and_clip.spatial_id, TransformStyle::Flat, PropertyBinding::Binding(PropertyBindingKey::new(42), LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, + ReferenceFrameKind::Transform, ); // And this is for the root pipeline diff --git a/third_party/webrender/examples/image_resize.rs b/third_party/webrender/examples/image_resize.rs index b057a4035ec..e28dd8e03d4 100644 --- a/third_party/webrender/examples/image_resize.rs +++ b/third_party/webrender/examples/image_resize.rs @@ -2,10 +2,10 @@ * 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 gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -14,7 +14,6 @@ mod image_helper; use crate::boilerplate::{Example, HandyDandyRectBuilder}; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; struct App { @@ -104,7 +103,7 @@ impl Example for App { &DirtyRect::All, ); let mut txn = Transaction::new(); - txn.generate_frame(0); + txn.generate_frame(); api.send_transaction(document_id, txn); } _ => {} diff --git a/third_party/webrender/examples/multiwindow.rs b/third_party/webrender/examples/multiwindow.rs index aa0ac87151a..2183b941445 100644 --- a/third_party/webrender/examples/multiwindow.rs +++ b/third_party/webrender/examples/multiwindow.rs @@ -2,11 +2,11 @@ * 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 euclid; -extern crate gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use euclid; +use gleam; +use glutin; +use webrender; +use winit; use gleam::gl; use glutin::NotCurrent; @@ -14,7 +14,6 @@ use std::fs::File; use std::io::Read; use webrender::api::*; use webrender::api::units::*; -use webrender::render_api::*; use webrender::DebugFlags; use winit::dpi::LogicalSize; @@ -35,7 +34,7 @@ impl RenderNotifier for Notifier { }) } - fn wake_up(&self, _composite_needed: bool) { + fn wake_up(&self) { #[cfg(not(target_os = "android"))] let _ = self.events_proxy.wakeup(); } @@ -43,9 +42,9 @@ impl RenderNotifier for Notifier { fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, - composite_needed: bool, + _composite_needed: bool, _render_time: Option<u64>) { - self.wake_up(composite_needed); + self.wake_up(); } } @@ -105,9 +104,9 @@ impl Window { DeviceIntSize::new(size.width as i32, size.height as i32) }; let notifier = Box::new(Notifier::new(events_loop.create_proxy())); - let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts, None).unwrap(); + let (renderer, sender) = webrender::Renderer::new(gl.clone(), notifier, opts, None, device_size).unwrap(); let mut api = sender.create_api(); - let document_id = api.add_document(device_size); + let document_id = api.add_document(device_size, 0); let epoch = Epoch(0); let pipeline_id = PipelineId(0, 0); @@ -184,10 +183,10 @@ impl Window { }; let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio); let mut txn = Transaction::new(); - let mut builder = DisplayListBuilder::new(self.pipeline_id); + let mut builder = DisplayListBuilder::new(self.pipeline_id, layout_size); let space_and_clip = SpaceAndClipInfo::root_scroll(self.pipeline_id); - let bounds = LayoutRect::new(LayoutPoint::zero(), layout_size); + let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size()); builder.push_simple_stacking_context( bounds.origin, space_and_clip.spatial_id, @@ -285,11 +284,11 @@ impl Window { true, ); txn.set_root_pipeline(self.pipeline_id); - txn.generate_frame(0); + txn.generate_frame(); api.send_transaction(self.document_id, txn); renderer.update(); - renderer.render(device_size, 0).unwrap(); + renderer.render(device_size).unwrap(); context.swap_buffers().ok(); self.context = Some(unsafe { context.make_not_current().unwrap() }); diff --git a/third_party/webrender/examples/scrolling.rs b/third_party/webrender/examples/scrolling.rs index 034af952449..745c4f9bd34 100644 --- a/third_party/webrender/examples/scrolling.rs +++ b/third_party/webrender/examples/scrolling.rs @@ -2,11 +2,11 @@ * 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 euclid; -extern crate gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use euclid; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -14,17 +14,12 @@ mod boilerplate; use crate::boilerplate::{Example, HandyDandyRectBuilder}; use euclid::SideOffsets2D; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; use winit::dpi::LogicalPosition; -const EXT_SCROLL_ID_ROOT: u64 = 1; -const EXT_SCROLL_ID_CONTENT: u64 = 2; - struct App { cursor_position: WorldPoint, - scroll_origin: LayoutPoint, } impl Example for App { @@ -56,7 +51,7 @@ impl Example for App { // set the scrolling clip let space_and_clip1 = builder.define_scroll_frame( &root_space_and_clip, - ExternalScrollId(EXT_SCROLL_ID_ROOT, PipelineId::dummy()), + None, (0, 0).by(1000, 1000), scrollbox, ScrollSensitivity::ScriptAndInputEvents, @@ -65,22 +60,22 @@ impl Example for App { // now put some content into it. // start with a white background - let info = CommonItemProperties::new((0, 0).to(1000, 1000), space_and_clip1); - builder.push_hit_test(&info, (0, 1)); + let mut info = CommonItemProperties::new((0, 0).to(1000, 1000), space_and_clip1); + info.hit_info = Some((0, 1)); builder.push_rect(&info, info.clip_rect, ColorF::new(1.0, 1.0, 1.0, 1.0)); // let's make a 50x50 blue square as a visual reference - let info = CommonItemProperties::new((0, 0).to(50, 50), space_and_clip1); - builder.push_hit_test(&info, (0, 2)); + let mut info = CommonItemProperties::new((0, 0).to(50, 50), space_and_clip1); + info.hit_info = Some((0, 2)); builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 0.0, 1.0, 1.0)); // and a 50x50 green square next to it with an offset clip // to see what that looks like - let info = CommonItemProperties::new( + let mut info = CommonItemProperties::new( (50, 0).to(100, 50).intersection(&(60, 10).to(110, 60)).unwrap(), space_and_clip1, ); - builder.push_hit_test(&info, (0, 3)); + info.hit_info = Some((0, 3)); builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 0.0, 1.0)); // Below the above rectangles, set up a nested scrollbox. It's still in @@ -88,7 +83,7 @@ impl Example for App { // be relative to the stacking context. let space_and_clip2 = builder.define_scroll_frame( &space_and_clip1, - ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), + None, (0, 100).to(300, 1000), (0, 100).to(200, 300), ScrollSensitivity::ScriptAndInputEvents, @@ -97,17 +92,17 @@ impl Example for App { // give it a giant gray background just to distinguish it and to easily // visually identify the nested scrollbox - let info = CommonItemProperties::new( + let mut info = CommonItemProperties::new( (-1000, -1000).to(5000, 5000), space_and_clip2, ); - builder.push_hit_test(&info, (0, 4)); + info.hit_info = Some((0, 4)); builder.push_rect(&info, info.clip_rect, ColorF::new(0.5, 0.5, 0.5, 1.0)); // add a teal square to visualize the scrolling/clipping behaviour // as you scroll the nested scrollbox - let info = CommonItemProperties::new((0, 200).to(50, 250), space_and_clip2); - builder.push_hit_test(&info, (0, 5)); + let mut info = CommonItemProperties::new((0, 200).to(50, 250), space_and_clip2); + info.hit_info = Some((0, 5)); builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0)); // Add a sticky frame. It will "stick" twice while scrolling, once @@ -123,14 +118,14 @@ impl Example for App { LayoutVector2D::new(0.0, 0.0) ); - let info = CommonItemProperties::new( + let mut info = CommonItemProperties::new( (50, 350).by(50, 50), SpaceAndClipInfo { spatial_id: sticky_id, clip_id: space_and_clip2.clip_id, }, ); - builder.push_hit_test(&info, (0, 6)); + info.hit_info = Some((0, 6)); builder.push_rect( &info, info.clip_rect, @@ -139,11 +134,11 @@ impl Example for App { // just for good measure add another teal square further down and to // the right, which can be scrolled into view by the user - let info = CommonItemProperties::new( + let mut info = CommonItemProperties::new( (250, 350).to(300, 400), space_and_clip2, ); - builder.push_hit_test(&info, (0, 7)); + info.hit_info = Some((0, 7)); builder.push_rect(&info, info.clip_rect, ColorF::new(0.0, 1.0, 1.0, 1.0)); builder.pop_stacking_context(); @@ -164,10 +159,10 @@ impl Example for App { .. } => { let offset = match key { - winit::VirtualKeyCode::Down => Some(LayoutVector2D::new(0.0, -10.0)), - winit::VirtualKeyCode::Up => Some(LayoutVector2D::new(0.0, 10.0)), - winit::VirtualKeyCode::Right => Some(LayoutVector2D::new(-10.0, 0.0)), - winit::VirtualKeyCode::Left => Some(LayoutVector2D::new(10.0, 0.0)), + winit::VirtualKeyCode::Down => Some((0.0, -10.0)), + winit::VirtualKeyCode::Up => Some((0.0, 10.0)), + winit::VirtualKeyCode::Right => Some((-10.0, 0.0)), + winit::VirtualKeyCode::Left => Some((10.0, 0.0)), _ => None, }; let zoom = match key { @@ -178,18 +173,15 @@ impl Example for App { }; if let Some(offset) = offset { - self.scroll_origin += offset; - - txn.scroll_node_with_id( - self.scroll_origin, - ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), - ScrollClamping::ToContentBounds, + txn.scroll( + ScrollLocation::Delta(LayoutVector2D::new(offset.0, offset.1)), + self.cursor_position, ); - txn.generate_frame(0); + txn.generate_frame(); } if let Some(zoom) = zoom { txn.set_pinch_zoom(ZoomFactor::new(zoom)); - txn.generate_frame(0); + txn.generate_frame(); } } winit::WindowEvent::CursorMoved { position: LogicalPosition { x, y }, .. } => { @@ -202,21 +194,18 @@ impl Example for App { winit::MouseScrollDelta::PixelDelta(pos) => (pos.x as f32, pos.y as f32), }; - self.scroll_origin += LayoutVector2D::new(dx, dy); - - txn.scroll_node_with_id( - self.scroll_origin, - ExternalScrollId(EXT_SCROLL_ID_CONTENT, PipelineId::dummy()), - ScrollClamping::ToContentBounds, + txn.scroll( + ScrollLocation::Delta(LayoutVector2D::new(dx, dy)), + self.cursor_position, ); - - txn.generate_frame(0); + txn.generate_frame(); } winit::WindowEvent::MouseInput { .. } => { let results = api.hit_test( document_id, None, self.cursor_position, + HitTestFlags::FIND_ALL ); println!("Hit test results:"); @@ -237,7 +226,6 @@ impl Example for App { fn main() { let mut app = App { cursor_position: WorldPoint::zero(), - scroll_origin: LayoutPoint::zero(), }; boilerplate::main_wrapper(&mut app, None); } diff --git a/third_party/webrender/examples/texture_cache_stress.rs b/third_party/webrender/examples/texture_cache_stress.rs index 9b029fbceb8..a065649b732 100644 --- a/third_party/webrender/examples/texture_cache_stress.rs +++ b/third_party/webrender/examples/texture_cache_stress.rs @@ -2,10 +2,10 @@ * 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 gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -14,7 +14,6 @@ use crate::boilerplate::{Example, HandyDandyRectBuilder}; use gleam::gl; use std::mem; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; @@ -302,11 +301,12 @@ impl Example for App { false } - fn get_image_handler( + fn get_image_handlers( &mut self, _gl: &dyn gl::Gl, - ) -> Option<Box<dyn ExternalImageHandler>> { - Some(Box::new(ImageGenerator::new())) + ) -> (Option<Box<dyn ExternalImageHandler>>, + Option<Box<dyn OutputImageHandler>>) { + (Some(Box::new(ImageGenerator::new())), None) } } diff --git a/third_party/webrender/examples/yuv.rs b/third_party/webrender/examples/yuv.rs index eacd97daa9a..3943bd23a6c 100644 --- a/third_party/webrender/examples/yuv.rs +++ b/third_party/webrender/examples/yuv.rs @@ -2,10 +2,10 @@ * 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 gleam; -extern crate glutin; -extern crate webrender; -extern crate winit; +use gleam; +use glutin; +use webrender; +use winit; #[path = "common/boilerplate.rs"] mod boilerplate; @@ -13,7 +13,6 @@ mod boilerplate; use crate::boilerplate::Example; use gleam::gl; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; @@ -94,7 +93,7 @@ impl Example for App { pipeline_id: PipelineId, _document_id: DocumentId, ) { - let bounds = LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(500.0, 500.0)); + let bounds = LayoutRect::new(LayoutPoint::zero(), builder.content_size()); let space_and_clip = SpaceAndClipInfo::root_scroll(pipeline_id); builder.push_simple_stacking_context( @@ -114,7 +113,7 @@ impl Example for App { id: ExternalImageId(0), channel_index: 0, image_type: ExternalImageType::TextureHandle( - ImageBufferKind::Texture2D, + TextureTarget::Default, ), }), None, @@ -126,7 +125,7 @@ impl Example for App { id: ExternalImageId(1), channel_index: 0, image_type: ExternalImageType::TextureHandle( - ImageBufferKind::Texture2D, + TextureTarget::Default, ), }), None, @@ -138,7 +137,7 @@ impl Example for App { id: ExternalImageId(2), channel_index: 0, image_type: ExternalImageType::TextureHandle( - ImageBufferKind::Texture2D, + TextureTarget::Default, ), }), None, @@ -150,7 +149,7 @@ impl Example for App { id: ExternalImageId(3), channel_index: 0, image_type: ExternalImageType::TextureHandle( - ImageBufferKind::Texture2D, + TextureTarget::Default, ), }), None, @@ -196,13 +195,14 @@ impl Example for App { false } - fn get_image_handler( + fn get_image_handlers( &mut self, gl: &dyn gl::Gl, - ) -> Option<Box<dyn ExternalImageHandler>> { + ) -> (Option<Box<dyn ExternalImageHandler>>, + Option<Box<dyn OutputImageHandler>>) { let provider = YuvImageProvider::new(gl); self.texture_id = provider.texture_ids[0]; - Some(Box::new(provider)) + (Some(Box::new(provider)), None) } fn draw_custom(&mut self, gl: &dyn gl::Gl) { @@ -218,6 +218,7 @@ fn main() { }; let opts = webrender::RendererOptions { + debug_flags: webrender::DebugFlags::NEW_FRAME_INDICATOR | webrender::DebugFlags::NEW_SCENE_INDICATOR, ..Default::default() }; diff --git a/third_party/webrender/glsl-to-cxx/Cargo.toml b/third_party/webrender/glsl-to-cxx/Cargo.toml index 8bcc992c6a9..1ff4298b541 100644 --- a/third_party/webrender/glsl-to-cxx/Cargo.toml +++ b/third_party/webrender/glsl-to-cxx/Cargo.toml @@ -2,7 +2,7 @@ name = "glsl-to-cxx" version = "0.1.0" license = "MPL-2.0" -authors = ["The Mozilla Project Developers", "Dimitri Sabadie"] +authors = ["The Mozilla Project Developers"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/third_party/webrender/glsl-to-cxx/README.md b/third_party/webrender/glsl-to-cxx/README.md index 54dcbc7d612..1b5b4845d27 100644 --- a/third_party/webrender/glsl-to-cxx/README.md +++ b/third_party/webrender/glsl-to-cxx/README.md @@ -1,21 +1,3 @@ A GLSL to C++ translator. Translates GLSL to vectorized C++. Intended for use with WebRender software backend. - -Architecture ------------- -GLSL code is parsed by the glsl crate. In hir.rs we traverse the resulting AST -and build a higher level representation by doing type checking and name -resolution. The resulting hir tree is traversed by lib.rs to output C++ code. - -The generated C++ code is 4x wider then the original glsl. i.e. a glsl 'float' -becomes a C++ 'Float' which is represented by a xmm register (a vector of 4 floats). -Likewise, a vec4 becomes a struct of 4 'Float's for a total of 4 xmm registers and -16 floating point values. - -Vector branching is flattened to non-branching code that unconditionally runs both -sides of the branch and combines the results with a mask based on the condition. - -The compiler also supports scalarization. Values that are known to be the same -across all vector lanes are translated to scalars instead of vectors. Branches on -scalars are translated as actual branches. diff --git a/third_party/webrender/glsl-to-cxx/src/hir.rs b/third_party/webrender/glsl-to-cxx/src/hir.rs index 287ae8d035b..710b50b7af2 100644 --- a/third_party/webrender/glsl-to-cxx/src/hir.rs +++ b/third_party/webrender/glsl-to-cxx/src/hir.rs @@ -1,38 +1,6 @@ /* 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/. - * - * Large chunks of this file are derived from the glsl crate which is: - * Copyright (c) 2018, Dimitri Sabadie <dimitri.sabadie@gmail.com> - * - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * * Neither the name of Dimitri Sabadie <dimitri.sabadie@gmail.com> nor the names of other - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use glsl::syntax; use glsl::syntax::{ArrayedIdentifier, ArraySpecifier, AssignmentOp, BinaryOp, Identifier}; @@ -77,7 +45,6 @@ pub enum SamplerFormat { RGBA32F, RGBA32I, R8, - RG8, } impl SamplerFormat { @@ -88,7 +55,6 @@ impl SamplerFormat { SamplerFormat::RGBA32F => Some("RGBA32F"), SamplerFormat::RGBA32I => Some("RGBA32I"), SamplerFormat::R8 => Some("R8"), - SamplerFormat::RG8 => Some("RG8"), } } } @@ -788,14 +754,6 @@ impl Type { array_sizes: None, } } - - pub fn new_array(kind: TypeKind, size: i32) -> Self { - Type { - kind, - precision: None, - array_sizes: Some(Box::new(ArraySizes { sizes: vec![make_const(TypeKind::Int, size)] })), - } - } } impl LiftFrom<&syntax::FullySpecifiedType> for Type { @@ -902,7 +860,7 @@ impl RunClass { #[derive(Debug, Clone, PartialEq)] pub enum SymDecl { - NativeFunction(FunctionType, Option<&'static str>, RunClass), + NativeFunction(FunctionType, Option<&'static str>), UserFunction(Rc<FunctionDefinition>, RunClass), Local(StorageClass, Type, RunClass), Global( @@ -969,8 +927,6 @@ pub struct State { modified_globals: RefCell<Vec<SymRef>>, pub used_globals: RefCell<Vec<SymRef>>, pub texel_fetches: HashMap<(SymRef, SymRef), TexelFetchOffsets>, - clip_dist_sym: SymRef, - pub used_clip_dist: u32, } impl State { @@ -986,8 +942,6 @@ impl State { modified_globals: RefCell::new(Vec::new()), used_globals: RefCell::new(Vec::new()), texel_fetches: HashMap::new(), - clip_dist_sym: SymRef(0), - used_clip_dist: 0, } } @@ -1795,9 +1749,6 @@ fn translate_variable_declaration( "r8" => { storage = StorageClass::Sampler(SamplerFormat::R8); } - "rg8" => { - storage = StorageClass::Sampler(SamplerFormat::RG8); - } _ => {} } } @@ -1904,33 +1855,11 @@ fn translate_declaration( syntax::Declaration::FunctionPrototype(p) => { Declaration::FunctionPrototype(translate_function_prototype(state, p)) } - syntax::Declaration::Global(ty, ids) => { - // glsl non-es supports requalifying variables, but we don't yet. - // However, we still want to allow global layout qualifiers for - // KHR_advanced_blend_equation. - if !ids.is_empty() { - panic!(); - } - let _ = for qual in &ty.qualifiers { - match qual { - syntax::TypeQualifierSpec::Layout(l) => { - for id in &l.ids { - match id { - syntax::LayoutQualifierSpec::Identifier(key, _) => { - match key.as_str() { - "blend_support_all_equations" => (), - _ => panic!(), - } - } - _ => panic!(), - } - } - } - syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Out) => (), - _ => panic!(), - } - }; - Declaration::Global(lift_type_qualifier_for_declaration(state, &Some(ty.clone())).unwrap(), ids.clone()) + 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) @@ -1944,9 +1873,6 @@ fn is_vector(ty: &Type) -> bool { TypeKind::Vec2 | TypeKind::Vec3 | TypeKind::Vec4 - | TypeKind::BVec2 - | TypeKind::BVec3 - | TypeKind::BVec4 | TypeKind::IVec2 | TypeKind::IVec3 | TypeKind::IVec4 => ty.array_sizes == None, @@ -1954,32 +1880,6 @@ fn is_vector(ty: &Type) -> bool { } } -fn index_vector(ty: &Type) -> Option<TypeKind> { - use TypeKind::*; - if ty.array_sizes != None { - return None; - } - Some(match ty.kind { - Vec2 => Float, - Vec3 => Float, - Vec4 => Float, - DVec2 => Double, - DVec3 => Double, - DVec4 => Double, - BVec2 => Bool, - BVec3 => Bool, - BVec4 => Bool, - IVec2 => Int, - IVec3 => Int, - IVec4 => Int, - UVec2 => UInt, - UVec3 => UInt, - UVec4 => UInt, - _ => return None, - }) - -} - fn index_matrix(ty: &Type) -> Option<TypeKind> { use TypeKind::*; if ty.array_sizes != None { @@ -2015,28 +1915,30 @@ fn is_ivec(ty: &Type) -> bool { } } -fn can_implicitly_convert_to(src: &Type, dst: &Type) -> bool { +fn compatible_type(lhs: &Type, rhs: &Type) -> bool { // XXX: use an underlying type helper - if src == &Type::new(TypeKind::Double) && dst == &Type::new(TypeKind::Float) { - // We're not supposed to implicitly convert from double to float but glsl 4 has a bug - // where it parses unannotated float constants as double. + 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 dst == &Type::new(TypeKind::Double) && src == &Type::new(TypeKind::Float) { + } else if rhs == &Type::new(TypeKind::Int) && + (lhs == &Type::new(TypeKind::Float) || lhs == &Type::new(TypeKind::Double)) + { true - } else if (dst == &Type::new(TypeKind::Float) || dst == &Type::new(TypeKind::Double)) && - src == &Type::new(TypeKind::Int) + } else if (rhs == &Type::new(TypeKind::Float) || rhs == &Type::new(TypeKind::Double)) && + lhs == &Type::new(TypeKind::Int) { true - } else if (dst == &Type::new(TypeKind::Vec2) || dst == &Type::new(TypeKind::DVec2)) && - src == &Type::new(TypeKind::IVec2) + } else if (rhs == &Type::new(TypeKind::Vec2) || rhs == &Type::new(TypeKind::DVec2)) && + lhs == &Type::new(TypeKind::IVec2) { true - } else if dst == &Type::new(TypeKind::IVec2) && - (src == &Type::new(TypeKind::Vec2) || src == &Type::new(TypeKind::DVec2)) + } else if rhs == &Type::new(TypeKind::IVec2) && + (lhs == &Type::new(TypeKind::Vec2) || lhs == &Type::new(TypeKind::DVec2)) { true } else { - src.kind == dst.kind && src.array_sizes == dst.array_sizes + lhs.kind == rhs.kind && lhs.array_sizes == rhs.array_sizes } } @@ -2086,7 +1988,7 @@ pub fn is_output(expr: &Expr, state: &State) -> Option<SymRef> { match &expr.kind { ExprKind::Variable(i) => match state.sym(*i).decl { SymDecl::Global(storage, ..) => match storage { - StorageClass::In | StorageClass::Out => return Some(*i), + StorageClass::Out => return Some(*i), _ => {} }, SymDecl::Local(..) => {} @@ -2230,18 +2132,6 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { if !globals.contains(&global) { globals.push(global); } - if global == state.clip_dist_sym { - if let ExprKind::Bracket(_, idx) = &lhs.kind { - // Get the constant array index used for gl_ClipDistance and add it to the used mask. - let idx = match idx.kind { - ExprKind::IntConst(idx) => idx, - ExprKind::UIntConst(idx) => idx as i32, - _ => panic!("bad index for gl_ClipDistance"), - }; - assert!(idx >= 0 && idx < 4); - state.used_clip_dist |= 1 << idx; - } - } } Expr { kind: ExprKind::Assignment(lhs, op.clone(), rhs), @@ -2251,23 +2141,28 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { 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::NonEqual | BinaryOp::GT | BinaryOp::GTE | BinaryOp::LT | BinaryOp::LTE => { - // comparison operators have a bool result + BinaryOp::Equal | BinaryOp::GT | BinaryOp::GTE | BinaryOp::LT | BinaryOp::LTE => { Type::new(TypeKind::Bool) } - BinaryOp::Mult => { - match (lhs.ty.kind, rhs.ty.kind) { - (TypeKind::Mat2, TypeKind::Vec2) | - (TypeKind::Mat3, TypeKind::Vec3) | - (TypeKind::Mat4, TypeKind::Vec4) => rhs.ty.clone(), - (TypeKind::Mat2, TypeKind::Float) | - (TypeKind::Mat3, TypeKind::Float) | - (TypeKind::Mat4, TypeKind::Float) => lhs.ty.clone(), - _ => promoted_type(&lhs.ty, &rhs.ty), - } - } - _ => promoted_type(&lhs.ty, &rhs.ty), + _ => ty, }; Expr { @@ -2330,19 +2225,6 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { .insert((sampler, base), TexelFetchOffsets::new(x, y)); } } - } else if name == "swgl_stepInterp" { - let mut globals = state.modified_globals.borrow_mut(); - for (i, sym) in state.syms.iter().enumerate() { - match &sym.borrow().decl { - SymDecl::Global(StorageClass::In, _, _, RunClass::Vector) => { - let symref = SymRef(i as u32); - if !globals.contains(&symref) { - globals.push(symref); - } - } - _ => {} - } - } } let sym = match state.lookup(name) { Some(s) => s, @@ -2358,31 +2240,19 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { } } match &state.sym(sym).decl { - SymDecl::NativeFunction(fn_ty, _, _) => { - // Search for a signature where all parameter types are - // compatible. If there are many compatible signatures, - // then choose the one with the most exact matches. - // This is an approximation of the algorith described in - // the "Function Definitions" section of the spec. + SymDecl::NativeFunction(fn_ty, _) => { let mut ret = None; - let mut best_score = 0; - 'next_sig: for sig in &fn_ty.signatures { - let mut score = 0; + for sig in &fn_ty.signatures { + let mut matching = true; for (e, p) in params.iter().zip(sig.params.iter()) { - if e.ty == *p { - score += 1; - } else if !can_implicitly_convert_to(&e.ty, p) { - continue 'next_sig; + if !compatible_type(&e.ty, p) { + matching = false; + break; } } - if score >= best_score { + if matching { ret = Some(sig.ret.clone()); - best_score = score; - // If all parameters match exactly, then there - // is no need to search for other matches. - if best_score >= params.len() { - break; - } + break; } } ret_ty = match ret { @@ -2417,7 +2287,7 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { } _ => {} } - can_implicitly_convert_to(&e.ty, &d.ty) + compatible_type(&e.ty, &d.ty) } FunctionParameterDeclaration::Unnamed(..) => panic!(), }; @@ -2438,7 +2308,6 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { syntax::Expr::Variable(i) => match i.as_str() { "vec4" => TypeKind::Vec4, "vec2" => TypeKind::Vec2, - "int" => TypeKind::Int, _ => panic!("unexpected type constructor {:?}", i), }, _ => panic!(), @@ -2563,8 +2432,8 @@ fn translate_expression(state: &mut State, e: &syntax::Expr) -> Expr { } syntax::Expr::Bracket(e, specifier) => { let e = Box::new(translate_expression(state, e)); - let ty = if let Some(ty) = index_vector(&e.ty) { - Type::new(ty) + let ty = if is_vector(&e.ty) { + Type::new(TypeKind::Float) } else if let Some(ty) = index_matrix(&e.ty) { Type::new(ty) } else { @@ -2895,13 +2764,12 @@ fn translate_external_declaration( } } -fn declare_function_ext( +fn declare_function( state: &mut State, name: &str, cxx_name: Option<&'static str>, ret: Type, params: Vec<Type>, - run_class: RunClass, ) { let sig = FunctionSignature { ret, params }; match state.lookup_sym_mut(name) { @@ -2917,7 +2785,6 @@ fn declare_function_ext( signatures: NonEmpty::new(sig), }, cxx_name, - run_class, ), ); } @@ -2926,16 +2793,6 @@ fn declare_function_ext( //state.declare(name, Type::Function(FunctionType{ v})) } -fn declare_function( - state: &mut State, - name: &str, - cxx_name: Option<&'static str>, - ret: Type, - params: Vec<Type>, -) { - declare_function_ext(state, name, cxx_name, ret, params, RunClass::Unknown) -} - pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> TranslationUnit { // global scope state.push_scope("global".into()); @@ -2952,13 +2809,6 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio "vec2", Some("make_vec2"), Type::new(Vec2), - vec![Type::new(Float), Type::new(Float)], - ); - declare_function( - state, - "vec2", - Some("make_vec2"), - Type::new(Vec2), vec![Type::new(IVec2)], ); declare_function( @@ -2994,13 +2844,6 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio "vec4", Some("make_vec4"), Type::new(Vec4), - vec![Type::new(Float)], - ); - declare_function( - state, - "vec4", - Some("make_vec4"), - Type::new(Vec4), vec![Type::new(Vec3), Type::new(Float)], ); declare_function( @@ -3053,32 +2896,12 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio ); declare_function( state, - "bvec3", - Some("make_bvec3"), - Type::new(BVec3), - vec![Type::new(Bool)], - ); - declare_function( - state, - "bvec4", - Some("make_bvec4"), - Type::new(BVec4), - vec![Type::new(Bool)], - ); - declare_function( - state, "bvec4", Some("make_bvec4"), Type::new(BVec4), vec![Type::new(BVec2), Type::new(BVec2)], ); - declare_function( - state, - "bvec4", - Some("make_bvec4"), - Type::new(BVec4), - vec![Type::new(Bool), Type::new(Bool), Type::new(Bool), Type::new(Bool)], - ); + declare_function( state, "int", @@ -3166,13 +2989,6 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio "ivec4", Some("make_ivec4"), Type::new(IVec4), - vec![Type::new(Vec4)], - ); - declare_function( - state, - "ivec4", - Some("make_ivec4"), - Type::new(IVec4), vec![Type::new(IVec2), Type::new(Int), Type::new(Int)], ); @@ -3267,9 +3083,6 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio 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, "sign", None, Type::new(Vec2), vec![Type::new(Vec2)]); - declare_function(state, "sign", None, Type::new(Vec3), vec![Type::new(Vec3)]); - declare_function(state, "sign", None, Type::new(Float), vec![Type::new(Float)]); declare_function( state, "dot", @@ -3284,38 +3097,56 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio Type::new(Float), vec![Type::new(Vec2), Type::new(Vec2)], ); - for t in &[Vec2, Vec3, Vec4] { - declare_function( - state, - "min", - None, - Type::new(*t), - vec![Type::new(*t), Type::new(Float)], - ); - declare_function( - state, - "max", - None, - Type::new(*t), - vec![Type::new(*t), Type::new(Float)], - ); - } - for t in &[Int, Float, Vec2, Vec3, Vec4] { - declare_function( - state, - "min", - None, - Type::new(*t), - vec![Type::new(*t), Type::new(*t)], - ); - declare_function( - state, - "max", - None, - Type::new(*t), - vec![Type::new(*t), Type::new(*t)], - ); - } + 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, @@ -3405,152 +3236,96 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio state, "step", None, - Type::new(Vec2), - vec![Type::new(Float), Type::new(Vec2)], - ); - declare_function( - state, - "step", - None, Type::new(Vec3), vec![Type::new(Vec3), Type::new(Vec3)], ); declare_function( state, - "step", - None, - Type::new(Vec4), - vec![Type::new(Float), Type::new(Vec4)], - ); - declare_function( - state, "notEqual", None, Type::new(BVec4), vec![Type::new(IVec4), Type::new(IVec4)], ); - declare_function_ext( + declare_function( state, "fwidth", None, Type::new(Vec2), vec![Type::new(Vec2)], - RunClass::Scalar, ); - declare_function_ext( + declare_function(state, "cos", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "sin", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "tan", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "atan", None, Type::new(Float), vec![Type::new(Float)]); + declare_function(state, "atan", None, Type::new(Float), vec![Type::new(Float), Type::new(Float)]); + declare_function( state, - "dFdx", + "clamp", None, - Type::new(Float), - vec![Type::new(Float)], - RunClass::Scalar, + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Float), Type::new(Float)], ); - declare_function_ext( + declare_function( state, - "dFdx", + "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)], - RunClass::Scalar, + vec![Type::new(Vec2), Type::new(Vec2), 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, "tan", None, Type::new(Float), vec![Type::new(Float)]); - declare_function(state, "atan", None, Type::new(Float), vec![Type::new(Float)]); - declare_function(state, "atan", None, Type::new(Float), vec![Type::new(Float), Type::new(Float)]); - for t in &[Vec2, Vec3, Vec4] { - declare_function( - state, - "clamp", - None, - Type::new(*t), - vec![Type::new(*t), Type::new(Float), Type::new(Float)], - ); - } - for t in &[Float, Vec2, Vec3, Vec4] { - declare_function( - state, - "clamp", - None, - Type::new(*t), - vec![Type::new(*t), Type::new(*t), Type::new(*t)], - ); - } declare_function( state, - "length", + "clamp", None, - Type::new(Float), - vec![Type::new(Vec2)], + Type::new(Vec3), + vec![Type::new(Vec3), Type::new(Vec3), Type::new(Vec3)], ); - 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, "exp2", None, Type::new(Float), vec![Type::new(Float)]); - declare_function(state, "log", None, Type::new(Float), vec![Type::new(Float)]); - declare_function(state, "log2", None, Type::new(Float), vec![Type::new(Float)]); - for t in &[Float, Vec2] { - // recip is non-standard - declare_function( - state, - "recip", - None, - Type::new(*t), - vec![Type::new(*t)], - ); - declare_function( - state, - "inversesqrt", - None, - Type::new(*t), - vec![Type::new(*t)], - ); - declare_function( - state, - "sqrt", - None, - Type::new(*t), - vec![Type::new(*t)], - ); - } declare_function( state, - "distance", + "clamp", None, - Type::new(Float), - vec![Type::new(Vec2), Type::new(Vec2)], + Type::new(Vec4), + vec![Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], ); - declare_function( state, - "equal", + "length", None, - Type::new(BVec2), - vec![Type::new(Vec2), Type::new(Vec2)], + 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, - "equal", + "inversesqrt", None, - Type::new(BVec4), - vec![Type::new(Vec4), Type::new(Vec4)], + Type::new(Float), + vec![Type::new(Float)], ); declare_function( state, - "notEqual", + "sqrt", None, - Type::new(BVec2), - vec![Type::new(Vec2), Type::new(Vec2)], + Type::new(Float), + vec![Type::new(Float)], ); declare_function( state, - "notEqual", + "distance", None, - Type::new(BVec4), - vec![Type::new(Vec4), Type::new(Vec4)], + Type::new(Float), + vec![Type::new(Vec2), Type::new(Vec2)], ); + declare_function( state, "lessThanEqual", @@ -3581,13 +3356,6 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio ); declare_function( state, - "lessThan", - None, - Type::new(BVec4), - vec![Type::new(Vec4), Type::new(Vec4)], - ); - declare_function( - state, "greaterThan", None, Type::new(BVec2), @@ -3595,13 +3363,6 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio ); declare_function( state, - "greaterThan", - None, - Type::new(BVec4), - vec![Type::new(Vec4), Type::new(Vec4)], - ); - declare_function( - state, "greaterThanEqual", None, Type::new(BVec2), @@ -3611,7 +3372,7 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio state, "greaterThanEqual", None, - Type::new(BVec4), + Type::new(BVec2), vec![Type::new(Vec4), Type::new(Vec4)], ); declare_function(state, "any", None, Type::new(Bool), vec![Type::new(BVec2)]); @@ -3631,22 +3392,22 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio state, "floor", None, - Type::new(Float), - vec![Type::new(Float)], + Type::new(Double), + vec![Type::new(Double)], ); declare_function( state, "ceil", None, - Type::new(Float), - vec![Type::new(Float)], + Type::new(Double), + vec![Type::new(Double)], ); declare_function( state, "round", None, - Type::new(Float), - vec![Type::new(Float)], + Type::new(Double), + vec![Type::new(Double)], ); declare_function( state, @@ -3655,13 +3416,6 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio Type::new(Float), vec![Type::new(Float)], ); - declare_function( - state, - "fract", - None, - Type::new(Vec2), - vec![Type::new(Vec2)], - ); declare_function(state, "mod", None, Type::new(Vec2), vec![Type::new(Vec2)]); declare_function(state, "mod", None, Type::new(Float), vec![Type::new(Float)]); @@ -3676,6 +3430,13 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio 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)], ); @@ -3719,6 +3480,27 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio ); 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), @@ -3765,417 +3547,6 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio "gl_Position", SymDecl::Global(StorageClass::Out, None, Type::new(Vec4), RunClass::Vector), ); - state.clip_dist_sym = state.declare( - "gl_ClipDistance", - SymDecl::Global(StorageClass::Out, None, Type::new_array(Float, 4), RunClass::Vector), - ); - - state.declare( - "swgl_SpanLength", - SymDecl::Global(StorageClass::In, None, Type::new(Int), RunClass::Scalar), - ); - state.declare( - "swgl_StepSize", - SymDecl::Global(StorageClass::Const, None, Type::new(Int), RunClass::Scalar), - ); - - for t in &[Float, Vec2, Vec3, Vec4, Int, IVec2, IVec3, IVec4, Mat3, Mat4] { - declare_function_ext( - state, - "swgl_forceScalar", - None, - Type::new(*t), - vec![Type::new(*t)], - RunClass::Scalar, - ); - } - - // GL_ARB_shader_group_vote - for (name, cxx_name) in &[("anyInvocations", "test_any"), - ("allInvocations", "test_all"), - ("allInvocationsEqual", "test_equal")] { - declare_function_ext( - state, - name, - Some(cxx_name), - Type::new(Bool), - vec![Type::new(Bool)], - RunClass::Scalar, - ); - } - - declare_function( - state, - "swgl_stepInterp", - None, - Type::new(Void), - vec![], - ); - - for t in &[Float, Vec2, Vec3, Vec4] { - declare_function_ext( - state, - "swgl_interpStep", - None, - Type::new(*t), - vec![Type::new(*t)], - RunClass::Scalar, - ); - } - - declare_function( - state, - "swgl_commitPartialSolidRGBA8", - None, - Type::new(Void), - vec![Type::new(Int), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitPartialSolidR8", - None, - Type::new(Void), - vec![Type::new(Int), Type::new(Float)], - ); - declare_function( - state, - "swgl_commitSolidRGBA8", - None, - Type::new(Void), - vec![Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitSolidR8", - None, - Type::new(Void), - vec![Type::new(Float)], - ); - declare_function( - state, - "swgl_commitColorRGBA8", - None, - Type::new(Void), - vec![Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitColorR8", - None, - Type::new(Void), - vec![Type::new(Float)], - ); - declare_function( - state, - "swgl_blendDropShadow", - None, - Type::new(Void), - vec![Type::new(Vec4)], - ); - declare_function( - state, - "swgl_blendSubpixelText", - None, - Type::new(Void), - vec![Type::new(Vec4)], - ); - declare_function( - state, - "swgl_clipMask", - None, - Type::new(Void), - vec![Type::new(Sampler2D), Type::new(Vec2), Type::new(Vec2), Type::new(Vec2)], - ); - declare_function( - state, - "swgl_antiAlias", - None, - Type::new(Void), - vec![Type::new(Int)], - ); - declare_function( - state, - "swgl_antiAlias", - None, - Type::new(Void), - vec![Type::new(BVec4)], - ); - declare_function_ext( - state, - "swgl_validateGradient", - None, - Type::new(Int), - vec![Type::new(Sampler2D), Type::new(IVec2), Type::new(Int)], - RunClass::Scalar, - ); - declare_function( - state, - "swgl_commitLinearGradientRGBA8", - None, - Type::new(Void), - vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float), Type::new(Bool), Type::new(Float)], - ); - declare_function( - state, - "swgl_commitRadialGradientRGBA8", - None, - Type::new(Void), - vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float), Type::new(Bool), Type::new(Vec2), - Type::new(Float)], - ); - declare_function( - state, - "swgl_commitGradientRGBA8", - None, - Type::new(Void), - vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float)], - ); - declare_function( - state, - "swgl_commitGradientColorRGBA8", - None, - Type::new(Void), - vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float), Type::new(Float)], - ); - for s in &[Sampler2D, Sampler2DRect] { - declare_function_ext( - state, - "swgl_isTextureLinear", - None, - Type::new(Bool), - vec![Type::new(*s)], - RunClass::Scalar, - ); - declare_function_ext( - state, - "swgl_isTextureRGBA8", - None, - Type::new(Bool), - vec![Type::new(*s)], - RunClass::Scalar, - ); - declare_function_ext( - state, - "swgl_isTextureR8", - None, - Type::new(Bool), - vec![Type::new(*s)], - RunClass::Scalar, - ); - declare_function( - state, - "swgl_commitTextureLinearRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureLinearR8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureLinearR8ToRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitPartialTextureLinearR8", - None, - Type::new(Void), - vec![Type::new(Int), Type::new(*s), Type::new(Vec2), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitPartialTextureLinearInvertR8", - None, - Type::new(Void), - vec![Type::new(Int), Type::new(*s), Type::new(Vec2), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureLinearColorRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureLinearColorRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Float)], - ); - declare_function( - state, - "swgl_commitTextureLinearColorR8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Float)], - ); - declare_function( - state, - "swgl_commitTextureLinearColorR8ToRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Vec4)], - ); - - declare_function( - state, - "swgl_commitTextureLinearRepeatRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), - Type::new(Vec4), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureLinearRepeatColorRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), - Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], - ); - - declare_function( - state, - "swgl_commitTextureNearestRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureNearestColorRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureNearestRepeatRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), - Type::new(Vec4), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureNearestRepeatColorRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), - Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], - ); - - declare_function( - state, - "swgl_commitTextureRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureColorRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureRepeatRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), - Type::new(Vec4), Type::new(Vec4)], - ); - declare_function( - state, - "swgl_commitTextureRepeatColorRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec2), - Type::new(Vec4), Type::new(Vec4), Type::new(Vec4)], - ); - - declare_function( - state, - "swgl_commitGaussianBlurRGBA8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Bool), - Type::new(Int), Type::new(Vec2)], - ); - declare_function( - state, - "swgl_commitGaussianBlurR8", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), Type::new(Bool), - Type::new(Int), Type::new(Vec2)], - ); - declare_function( - state, - "swgl_commitTextureLinearYUV", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(Int), Type::new(Int)], - ); - declare_function( - state, - "swgl_commitTextureLinearYUV", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(Int), Type::new(Int)], - ); - declare_function( - state, - "swgl_commitTextureLinearYUV", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(Int), Type::new(Int)], - ); - declare_function( - state, - "swgl_commitTextureLinearColorYUV", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(Int), Type::new(Int), Type::new(Float)], - ); - declare_function( - state, - "swgl_commitTextureLinearColorYUV", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(Int), Type::new(Int), Type::new(Float)], - ); - declare_function( - state, - "swgl_commitTextureLinearColorYUV", - None, - Type::new(Void), - vec![Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(*s), Type::new(Vec2), Type::new(Vec4), - Type::new(Int), Type::new(Int), Type::new(Float)], - ); - } TranslationUnit(tu.0.map(state, translate_external_declaration)) } @@ -4227,13 +3598,7 @@ fn infer_expr_inner(state: &mut State, expr: &Expr, assign: &mut SymRef) -> RunC }; match fun { FunIdentifier::Identifier(ref sym) => match &state.sym(*sym).decl { - SymDecl::NativeFunction(_, _, ref ret_class) => { - if *ret_class != RunClass::Unknown { - *ret_class - } else { - run_class - } - } + 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()) diff --git a/third_party/webrender/glsl-to-cxx/src/lib.rs b/third_party/webrender/glsl-to-cxx/src/lib.rs index a42f14a23b6..409af02d586 100644 --- a/third_party/webrender/glsl-to-cxx/src/lib.rs +++ b/third_party/webrender/glsl-to-cxx/src/lib.rs @@ -2,7 +2,7 @@ * 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; +use glsl; mod hir; @@ -61,6 +61,8 @@ pub fn translate(args: &mut dyn Iterator<Item = String>) -> String { .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); @@ -77,6 +79,7 @@ pub fn translate(args: &mut dyn Iterator<Item = String>) -> String { vs_hir, vs_is_frag, &uniform_indices, + None, ); result += "\n"; result += &translate_shader( @@ -85,6 +88,7 @@ pub fn translate(args: &mut dyn Iterator<Item = String>) -> String { fs_hir, fs_is_frag, &uniform_indices, + frag_include, ); result } @@ -116,6 +120,7 @@ fn translate_shader( hir: hir::TranslationUnit, is_frag: bool, uniform_indices: &UniformIndices, + include_file: Option<String>, ) -> String { //println!("{:#?}", state); @@ -180,6 +185,8 @@ fn translate_shader( uses_discard: false, used_fragcoord: Cell::new(0), use_perspective: false, + has_draw_span_rgba8: false, + has_draw_span_r8: false, used_globals: RefCell::new(Vec::new()), texel_fetches: RefCell::new(Vec::new()), }; @@ -208,6 +215,10 @@ fn translate_shader( show_translation_unit(&mut state, &hir); + if let Some(include_file) = include_file { + write_include_file(&mut state, include_file); + } + let pruned_inputs: Vec<_> = inputs .iter() .filter(|i| state.used_globals.borrow().contains(i)) @@ -239,7 +250,6 @@ fn translate_shader( write!(state, " return this;\n}}\n"); write!(state, "FragmentShaderImpl* get_fragment_shader() override {{\n"); write!(state, " return this;\n}}\n"); - write!(state, "const char* get_name() const override {{ return \"{}\"; }}\n", name); write!(state, "static ProgramImpl* loader() {{ return new {}_program; }}\n", name); write!(state, "}};\n\n"); } @@ -292,7 +302,8 @@ fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndi match tk { hir::TypeKind::Sampler2D | hir::TypeKind::Sampler2DRect - | hir::TypeKind::ISampler2D => { + | hir::TypeKind::ISampler2D + | hir::TypeKind::Sampler2DArray => { write!(state, " "); show_type_kind(state, &tk); let suffix = if let hir::StorageClass::Sampler(format) = storage { @@ -315,7 +326,8 @@ fn write_program_samplers(state: &mut OutputState, uniform_indices: &UniformIndi match tk { hir::TypeKind::Sampler2D | hir::TypeKind::Sampler2DRect - | hir::TypeKind::ISampler2D => { + | hir::TypeKind::ISampler2D + | hir::TypeKind::Sampler2DArray => { write!(state, " case {}:\n", index); write!(state, " {}_slot = value;\n", name); write!(state, " return true;\n"); @@ -343,6 +355,9 @@ fn write_bind_textures(state: &mut OutputState, uniforms: &UniformIndices) { hir::TypeKind::ISampler2D => write!(state, " {0} = lookup_isampler(&samplers.{0}_impl, samplers.{0}_slot);\n", name), + hir::TypeKind::Sampler2DArray => write!(state, + " {0} = lookup_sampler_array(&samplers.{0}_impl, samplers.{0}_slot);\n", + name), _ => {} }; } @@ -393,8 +408,9 @@ fn write_set_uniform_4fv( if float4_compatible(tk.clone()) { write!( state, - " self->{} = vec4_scalar::load_from_ptr(value);\n", - name + " self->{} = {}_scalar(value);\n", + name, + tk.glsl_primitive_type_name().unwrap(), ); } else { write!(state, " assert(0); // {}\n", name); @@ -535,9 +551,6 @@ fn write_load_attribs(state: &mut OutputState, attribs: &[hir::SymRef]) { fn write_store_outputs(state: &mut OutputState, outputs: &[hir::SymRef]) { let is_scalar = state.is_scalar.replace(true); write!(state, "public:\nstruct InterpOutputs {{\n"); - if state.hir.used_clip_dist != 0 { - state.write(" Float swgl_ClipDistance;\n"); - } for i in outputs { let sym = state.hir.sym(*i); match &sym.decl { @@ -563,15 +576,6 @@ fn write_store_outputs(state: &mut OutputState, outputs: &[hir::SymRef]) { state, " auto* dest = reinterpret_cast<InterpOutputs*>(dest_ptr);\n" ); - if state.hir.used_clip_dist != 0 { - for (i, comp) in "xyzw".chars().enumerate() { - if (state.hir.used_clip_dist & (1 << i)) != 0 { - write!(state, " dest->swgl_ClipDistance.{} = get_nth(gl_ClipDistance[{}], n);\n", comp, i); - } else { - write!(state, " dest->swgl_ClipDistance.{} = 0.0f;\n", comp); - } - } - } for i in outputs { let sym = state.hir.sym(*i); match &sym.decl { @@ -622,7 +626,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { write!(state, "static void read_interp_inputs(\ - Self *self, const InterpInputs *init, const InterpInputs *step) {{\n"); + 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 { @@ -636,7 +640,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { ); write!( state, - " self->interp_step.{0} = step->{0} * 4.0f;\n", + " self->interp_step.{0} = step->{0} * step_width;\n", name ); } @@ -653,7 +657,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { if state.use_perspective { write!(state, "static void read_perspective_inputs(\ - Self *self, const InterpInputs *init, const InterpInputs *step) {{\n"); + Self *self, const InterpInputs *init, const InterpInputs *step, float step_width) {{\n"); if has_varying { write!(state, " Float w = 1.0f / self->gl_FragCoord.w;\n"); } @@ -671,7 +675,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { write!(state, " self->{0} = self->interp_perspective.{0} * w;\n", name); write!( state, - " self->interp_step.{0} = step->{0} * 4.0f;\n", + " self->interp_step.{0} = step->{0} * step_width;\n", name ); } @@ -682,12 +686,9 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { write!(state, "}}\n"); } - write!(state, "ALWAYS_INLINE void step_interp_inputs(int steps = 4) {{\n"); + write!(state, "ALWAYS_INLINE void step_interp_inputs() {{\n"); if (used_fragcoord & 1) != 0 { - write!(state, " step_fragcoord(steps);\n"); - } - if !inputs.is_empty() { - write!(state, " float chunks = steps * 0.25f;\n"); + write!(state, " step_fragcoord();\n"); } for i in inputs { let sym = state.hir.sym(*i); @@ -695,7 +696,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { hir::SymDecl::Global(_, _, _, run_class) => { if *run_class != hir::RunClass::Scalar { let name = sym.name.as_str(); - write!(state, " {0} += interp_step.{0} * chunks;\n", name); + write!(state, " {0} += interp_step.{0};\n", name); } } _ => panic!(), @@ -704,14 +705,11 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { write!(state, "}}\n"); if state.use_perspective { - write!(state, "ALWAYS_INLINE void step_perspective_inputs(int steps = 4) {{\n"); + write!(state, "ALWAYS_INLINE void step_perspective_inputs() {{\n"); if (used_fragcoord & 1) != 0 { - write!(state, " step_fragcoord(steps);\n"); - } - write!(state, " step_perspective(steps);\n"); - if !inputs.is_empty() { - write!(state, " float chunks = steps * 0.25f;\n"); + write!(state, " step_fragcoord();\n"); } + write!(state, " step_perspective();\n"); if has_varying { write!(state, " Float w = 1.0f / gl_FragCoord.w;\n"); } @@ -721,7 +719,7 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { hir::SymDecl::Global(_, _, _, run_class) => { if *run_class != hir::RunClass::Scalar { let name = sym.name.as_str(); - write!(state, " interp_perspective.{0} += interp_step.{0} * chunks;\n", name); + write!(state, " interp_perspective.{0} += interp_step.{0};\n", name); write!(state, " {0} = w * interp_perspective.{0};\n", name); } } @@ -730,6 +728,58 @@ fn write_read_inputs(state: &mut OutputState, inputs: &[hir::SymRef]) { } 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 (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, " {0} += interp_step.{0} * chunks;\n", 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 { @@ -754,6 +804,8 @@ pub struct OutputState { uses_discard: bool, used_fragcoord: Cell<i32>, use_perspective: bool, + has_draw_span_rgba8: bool, + has_draw_span_r8: bool, used_globals: RefCell<Vec<hir::SymRef>>, texel_fetches: RefCell<Vec<(hir::SymRef, hir::SymRef, hir::TexelFetchOffsets)>>, } @@ -822,7 +874,7 @@ fn add_used_global(state: &OutputState, i: &hir::SymRef) { pub fn show_sym(state: &OutputState, i: &hir::SymRef) { let sym = state.hir.sym(*i); match &sym.decl { - hir::SymDecl::NativeFunction(_, ref cxx_name, _) => { + hir::SymDecl::NativeFunction(_, ref cxx_name) => { let mut name = sym.name.as_str(); if state.output_cxx { name = cxx_name.unwrap_or(name); @@ -1515,10 +1567,8 @@ fn expr_run_class(state: &OutputState, expr: &hir::Expr) -> hir::RunClass { }); match fun { hir::FunIdentifier::Identifier(ref sym) => match &state.hir.sym(*sym).decl { - hir::SymDecl::NativeFunction(_, _, ref ret_class) => { - if *ret_class != hir::RunClass::Unknown { - *ret_class - } else if arg_mask != 0 { + hir::SymDecl::NativeFunction(..) => { + if arg_mask != 0 { hir::RunClass::Vector } else { hir::RunClass::Scalar @@ -1846,21 +1896,33 @@ pub fn show_hir_expr_inner(state: &OutputState, expr: &hir::Expr, top_level: boo &state.hir, &args[0], &args[1], &args[3], ) { let base_sym = state.hir.sym(base); - 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 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; } - write!( - state, - "texelFetchUnchecked({}, {}_{}_fetch, {}, {})", - sampler_sym.name, - sampler_sym.name, - base_sym.name, - x, - y, - ); - return; } } show_sym(state, name) @@ -2296,12 +2358,15 @@ pub fn show_declaration(state: &mut OutputState, d: &hir::Declaration) { let base = list.head.name; let base_sym = state.hir.sym(base); if let hir::SymDecl::Local(..) = &base_sym.decl { - let mut texel_fetches = state.texel_fetches.borrow_mut(); - while let Some(idx) = texel_fetches.iter().position(|&(_, b, _)| b == base) + if symbol_run_class(&base_sym.decl, state.vector_mask) == hir::RunClass::Scalar { - let (sampler, _, offsets) = texel_fetches.remove(idx); - let sampler_sym = state.hir.sym(sampler); - define_texel_fetch_ptr(state, &base_sym, &sampler_sym, &offsets); + 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); + } } } } @@ -2319,22 +2384,19 @@ pub fn show_declaration(state: &mut OutputState, d: &hir::Declaration) { //state.write(";\n"); } hir::Declaration::Global(ref qual, ref identifiers) => { - // We only want to output GLSL layout qualifiers if not C++ - if !state.output_cxx { - show_type_qualifier(state, &qual); + show_type_qualifier(state, &qual); - if !identifiers.is_empty() { - let mut iter = identifiers.iter(); - let first = iter.next().unwrap(); - show_identifier(state, first); + 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); - } + for identifier in iter { + let _ = write!(state, ", {}", identifier); } - - state.write(";\n"); } + + state.write(";\n"); } hir::Declaration::StructDefinition(ref sym) => { show_sym_decl(state, sym); @@ -2610,32 +2672,32 @@ fn define_texel_fetch_ptr( offsets: &hir::TexelFetchOffsets, ) { show_indent(state); - let ptr_type = if let hir::SymDecl::Global(_, _, ty, _) = &sampler_sym.decl { - if symbol_run_class(&base_sym.decl, state.vector_mask) == hir::RunClass::Scalar { - match ty.kind { - hir::TypeKind::Sampler2D - | hir::TypeKind::Sampler2DRect => "vec4_scalar*", - hir::TypeKind::ISampler2D => "ivec4_scalar*", - _ => panic!(), + if let hir::SymDecl::Global(_, _, ty, _) = &sampler_sym.decl { + match ty.kind { + hir::TypeKind::Sampler2D + | hir::TypeKind::Sampler2DRect => { + write!( + state, + "vec4_scalar* {}_{}_fetch = ", + sampler_sym.name, base_sym.name + ); } - } else { - "I32" + hir::TypeKind::ISampler2D => { + write!( + state, + "ivec4_scalar* {}_{}_fetch = ", + sampler_sym.name, base_sym.name + ); + } + _ => panic!(), } } else { panic!(); - }; + } write!( state, - "{} {}_{}_fetch = texelFetchPtr({}, {}, {}, {}, {}, {});\n", - ptr_type, - sampler_sym.name, - base_sym.name, - sampler_sym.name, - base_sym.name, - offsets.min_x, - offsets.max_x, - offsets.min_y, - offsets.max_y, + "texelFetchPtr({}, {}, {}, {}, {}, {});\n", + sampler_sym.name, base_sym.name, offsets.min_x, offsets.max_x, offsets.min_y, offsets.max_y ); } @@ -2688,64 +2750,27 @@ pub fn show_function_definition( } if state.output_cxx { - match fd.prototype.name.as_str() { - "swgl_drawSpanRGBA8" | - "swgl_drawSpanR8" => { - // Partial spans are not drawn using span shaders, but rather drawn with a fragment shader - // where the span shader left off. We need to undo any changes to the interpolants made by - // the span shaders so that we can reset the interpolants to where the fragment shader - // expects them. We do this by saving them in an _Undo_ struct on entry to the span shader, - // and then restore them in the _Undo_ struct destructor. - let mut needs_undo = vec![]; - for global in &fd.globals { - let sym = state.hir.sym(*global); - match &sym.decl { - hir::SymDecl::Global(hir::StorageClass::In, _, ty, hir::RunClass::Vector) => { - if needs_undo.is_empty() { - state.write("struct _Undo_ {\nSelf* self;\n"); - } - show_type(state, ty); - write!(state, " {};\n", sym.name); - needs_undo.push(sym.name.clone()); - } - _ => {} - } - } - if !needs_undo.is_empty() { - state.write("explicit _Undo_(Self* self) : self(self)"); - for name in &needs_undo { - write!(state, ", {0}(self->{0})", name); - } - state.write(" {}\n"); - state.write("~_Undo_() {\n"); - for name in &needs_undo { - write!(state, "self->{0} = {0};\n", name); - } - state.write("}} _undo_(this);\n"); - } - } - _ => {} - } - let mut texel_fetches = state.texel_fetches.borrow_mut(); texel_fetches.clear(); for ((sampler, base), offsets) in fd.texel_fetches.iter() { - add_used_global(state, sampler); - let sampler_sym = state.hir.sym(*sampler); let base_sym = state.hir.sym(*base); - 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) { + 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); - } else { - texel_fetches.push((*sampler, *base, offsets.clone())); } + 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!(), } - _ => panic!(), } } } @@ -3170,7 +3195,7 @@ pub fn show_iteration_statement(state: &mut OutputState, ist: &hir::IterationSta show_statement(state, body); state.write(" while ("); show_hir_expr(state, cond); - state.write(");\n"); + state.write(")\n"); } hir::IterationStatement::For(ref init, ref rest, ref body) => { state.write("for ("); @@ -3241,7 +3266,7 @@ pub fn show_jump_statement(state: &mut OutputState, j: &hir::JumpStatement) { if state.output_cxx { state.uses_discard = true; if let Some(mask) = &state.mask { - state.write("swgl_IsPixelDiscarded |= ("); + state.write("isPixelDiscarded |= ("); show_hir_expr(state, mask); state.write(")"); if state.return_declared { @@ -3249,7 +3274,7 @@ pub fn show_jump_statement(state: &mut OutputState, j: &hir::JumpStatement) { } state.write(";\n"); } else { - state.write("swgl_IsPixelDiscarded = true;\n"); + state.write("isPixelDiscarded = true;\n"); } } else { state.write("discard;\n"); @@ -3554,11 +3579,9 @@ pub fn show_translation_unit(state: &mut OutputState, tu: &hir::TranslationUnit) state.flush_buffer(); } if state.output_cxx { - for name in &["main", "swgl_drawSpanRGBA8", "swgl_drawSpanR8"] { - if let Some(sym) = state.hir.lookup(name) { - show_cxx_function_definition(state, sym, 0); - state.flush_buffer(); - } + if let Some(name) = state.hir.lookup("main") { + show_cxx_function_definition(state, name, 0); + state.flush_buffer(); } } } @@ -3568,33 +3591,37 @@ fn write_abi(state: &mut OutputState) { ShaderKind::Fragment => { state.write("static void run(Self *self) {\n"); if state.uses_discard { - state.write(" self->swgl_IsPixelDiscarded = false;\n"); + 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 steps) {\n"); - state.write(" self->step_interp_inputs(steps);\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.use_perspective { state.write("static void run_perspective(Self *self) {\n"); if state.uses_discard { - state.write(" self->swgl_IsPixelDiscarded = false;\n"); + state.write(" self->isPixelDiscarded = false;\n"); } state.write(" self->main();\n"); state.write(" self->step_perspective_inputs();\n"); state.write("}\n"); - state.write("static void skip_perspective(Self* self, int steps) {\n"); - state.write(" self->step_perspective_inputs(steps);\n"); + state.write("static void skip_perspective(Self* self, int chunks) {\n"); + state.write(" self->step_perspective_inputs();\n"); + state.write(" while (--chunks > 0) self->step_perspective_inputs();\n"); state.write("}\n"); } - if state.hir.lookup("swgl_drawSpanRGBA8").is_some() { + if state.has_draw_span_rgba8 { state.write( - "static int draw_span_RGBA8(Self* self) { DISPATCH_DRAW_SPAN(self, RGBA8); }\n"); + "static void draw_span_RGBA8(Self* self, uint32_t* buf, int len) { \ + DISPATCH_DRAW_SPAN(self, buf, len); }\n"); } - if state.hir.lookup("swgl_drawSpanR8").is_some() { + if state.has_draw_span_r8 { state.write( - "static int draw_span_R8(Self* self) { DISPATCH_DRAW_SPAN(self, R8); }\n"); + "static void draw_span_R8(Self* self, uint8_t* buf, int len) { \ + DISPATCH_DRAW_SPAN(self, buf, len); }\n"); } write!(state, "public:\n{}_frag() {{\n", state.name); @@ -3616,10 +3643,10 @@ fn write_abi(state: &mut OutputState) { state.write(" init_span_func = (InitSpanFunc)&read_interp_inputs;\n"); state.write(" run_func = (RunFunc)&run;\n"); state.write(" skip_func = (SkipFunc)&skip;\n"); - if state.hir.lookup("swgl_drawSpanRGBA8").is_some() { + if state.has_draw_span_rgba8 { state.write(" draw_span_RGBA8_func = (DrawSpanRGBA8Func)&draw_span_RGBA8;\n"); } - if state.hir.lookup("swgl_drawSpanR8").is_some() { + if state.has_draw_span_r8 { state.write(" draw_span_R8_func = (DrawSpanR8Func)&draw_span_R8;\n"); } if state.uses_discard { @@ -3643,9 +3670,6 @@ fn write_abi(state: &mut OutputState) { state.write(" init_batch_func = (InitBatchFunc)&init_batch;\n"); state.write(" load_attribs_func = (LoadAttribsFunc)&load_attribs;\n"); state.write(" run_primitive_func = (RunPrimitiveFunc)&run;\n"); - if state.hir.used_clip_dist != 0 { - state.write(" enable_clip_distance();\n"); - } } } state.write("}\n"); diff --git a/third_party/webrender/patches/0001-Add-signal-handler-to-catch-segfault-in-build-script.patch b/third_party/webrender/patches/0001-Add-signal-handler-to-catch-segfault-in-build-script.patch new file mode 100644 index 00000000000..674e9801466 --- /dev/null +++ b/third_party/webrender/patches/0001-Add-signal-handler-to-catch-segfault-in-build-script.patch @@ -0,0 +1,226 @@ +From 34d968adeda2e06b057a13d14a88df5766b38eda Mon Sep 17 00:00:00 2001 +From: Josh Matthews <josh@joshmatthews.net> +Date: Mon, 6 Jul 2020 14:37:42 -0400 +Subject: [PATCH] Add signal handler to catch segfault in build script. + +--- + Cargo.lock | 11 +++++ + webrender/Cargo.toml | 5 ++ + webrender/backtrace.rs | 103 +++++++++++++++++++++++++++++++++++++++++ + webrender/build.rs | 30 ++++++++++++ + 4 files changed, 149 insertions(+) + create mode 100644 webrender/backtrace.rs + +diff --git a/Cargo.lock b/Cargo.lock +index afdd336ae..cf91162d5 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -1477,6 +1477,14 @@ dependencies = [ + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + ] + ++[[package]] ++name = "sig" ++version = "1.0.0" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++dependencies = [ ++ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ++] ++ + [[package]] + name = "slab" + version = "0.4.2" +@@ -1750,6 +1758,7 @@ dependencies = [ + name = "webrender" + version = "0.61.0" + dependencies = [ ++ "backtrace 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1780,6 +1789,7 @@ dependencies = [ + "ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", ++ "sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "svg_fmt 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -2178,6 +2188,7 @@ dependencies = [ + "checksum servo-freetype-sys 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4ccb6d0d32d277d3ef7dea86203d8210945eb7a45fba89dd445b3595dd0dfc" + "checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" + "checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" ++"checksum sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6567e29578f9bfade6a5d94a32b9a4256348358d2a3f448cab0021f9a02614a2" + "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" + "checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" + "checksum smallvec 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" +diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml +index dcf26d913..f7679da57 100644 +--- a/webrender/Cargo.toml ++++ b/webrender/Cargo.toml +@@ -59,6 +59,11 @@ tracy-rs = { version = "0.1" } + mozangle = "0.3.1" + rand = "0.4" + ++[target.'cfg(any(target_os = "macos", target_os = "linux"))'.build-dependencies] ++backtrace = "0.3" ++sig = "1.0" ++libc = "0.2" ++ + [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies] + freetype = { version = "0.4", default-features = false } + libc = "0.2" +diff --git a/webrender/backtrace.rs b/webrender/backtrace.rs +new file mode 100644 +index 000000000..aa6bb6b32 +--- /dev/null ++++ b/webrender/backtrace.rs +@@ -0,0 +1,103 @@ ++/* 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 https://mozilla.org/MPL/2.0/. */ ++ ++//! Similar to `println!("{:?}", Backtrace::new())`, but doesn’t allocate. ++//! ++//! Seems to fix some deadlocks: https://github.com/servo/servo/issues/24881 ++//! ++//! FIXME: if/when a future version of the `backtrace` crate has ++//! https://github.com/rust-lang/backtrace-rs/pull/265, use that instead. ++ ++use std::fmt::{self, Write}; ++use backtrace::{BytesOrWideString, PrintFmt}; ++ ++#[inline(never)] ++pub(crate) fn print(w: &mut dyn std::io::Write) -> Result<(), std::io::Error> { ++ write!(w, "{:?}", Print { ++ print_fn_address: print as usize, ++ }) ++} ++ ++struct Print { ++ print_fn_address: usize, ++} ++ ++impl fmt::Debug for Print { ++ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { ++ // Safety: we’re in a signal handler that is about to call `libc::_exit`. ++ // Potential data races from using `*_unsynchronized` functions are perhaps ++ // less bad than potential deadlocks? ++ unsafe { ++ let mut print_fn_frame = 0; ++ let mut frame_count = 0; ++ backtrace::trace_unsynchronized(|frame| { ++ let found = frame.symbol_address() as usize == self.print_fn_address; ++ if found { ++ print_fn_frame = frame_count; ++ } ++ frame_count += 1; ++ !found ++ }); ++ ++ let mode = PrintFmt::Short; ++ let mut p = print_path; ++ let mut f = backtrace::BacktraceFmt::new(fmt, mode, &mut p); ++ f.add_context()?; ++ let mut result = Ok(()); ++ let mut frame_count = 0; ++ backtrace::trace_unsynchronized(|frame| { ++ let skip = frame_count < print_fn_frame; ++ frame_count += 1; ++ if skip { ++ return true ++ } ++ ++ let mut frame_fmt = f.frame(); ++ let mut any_symbol = false; ++ backtrace::resolve_frame_unsynchronized(frame, |symbol| { ++ any_symbol = true; ++ if let Err(e) = frame_fmt.symbol(frame, symbol) { ++ result = Err(e) ++ } ++ }); ++ if !any_symbol { ++ if let Err(e) = frame_fmt.print_raw(frame.ip(), None, None, None) { ++ result = Err(e) ++ } ++ } ++ result.is_ok() ++ }); ++ result?; ++ f.finish() ++ } ++ } ++} ++ ++fn print_path(fmt: &mut fmt::Formatter, path: BytesOrWideString) -> fmt::Result { ++ match path { ++ BytesOrWideString::Bytes(mut bytes) => { ++ loop { ++ match std::str::from_utf8(bytes) { ++ Ok(s) => { ++ fmt.write_str(s)?; ++ break; ++ } ++ Err(err) => { ++ fmt.write_char(std::char::REPLACEMENT_CHARACTER)?; ++ match err.error_len() { ++ Some(len) => bytes = &bytes[err.valid_up_to() + len..], ++ None => break, ++ } ++ } ++ } ++ } ++ } ++ BytesOrWideString::Wide(wide) => { ++ for c in std::char::decode_utf16(wide.iter().cloned()) { ++ fmt.write_char(c.unwrap_or(std::char::REPLACEMENT_CHARACTER))? ++ } ++ } ++ } ++ Ok(()) ++} +diff --git a/webrender/build.rs b/webrender/build.rs +index 3521d1342..36a7f17a8 100644 +--- a/webrender/build.rs ++++ b/webrender/build.rs +@@ -244,7 +244,37 @@ fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: & + Ok(()) + } + ++#[cfg(any(target_os = "macos", target_os = "linux"))] ++mod backtrace; ++ ++#[cfg(any(target_os = "macos", target_os = "linux"))] ++extern "C" fn handler(sig: i32) { ++ use std::sync::atomic; ++ static BEEN_HERE_BEFORE: atomic::AtomicBool = atomic::AtomicBool::new(false); ++ if !BEEN_HERE_BEFORE.swap(true, atomic::Ordering::SeqCst) { ++ let stdout = std::io::stdout(); ++ let mut stdout = stdout.lock(); ++ let _ = write!(&mut stdout, "Stack trace"); ++ if let Some(name) = std::thread::current().name() { ++ let _ = write!(&mut stdout, " for thread \"{}\"", name); ++ } ++ let _ = write!(&mut stdout, "\n"); ++ let _ = backtrace::print(&mut stdout); ++ } ++ unsafe { ++ libc::_exit(sig); ++ } ++} ++ + fn main() -> Result<(), std::io::Error> { ++ #[cfg(any(target_os = "macos", target_os = "linux"))] ++ { ++ sig::signal!(sig::ffi::Sig::SEGV, handler); // handle segfaults ++ sig::signal!(sig::ffi::Sig::ILL, handler); // handle stack overflow and unsupported CPUs ++ sig::signal!(sig::ffi::Sig::IOT, handler); // handle double panics ++ sig::signal!(sig::ffi::Sig::BUS, handler); // handle invalid memory access ++ } ++ + let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned()); + + let shaders_file_path = Path::new(&out_dir).join("shaders.rs"); +-- +2.39.2 + diff --git a/third_party/webrender/patches/0002-Bug-1646741-Update-gleam-to-0.12.-r-kvark.patch b/third_party/webrender/patches/0002-Bug-1646741-Update-gleam-to-0.12.-r-kvark.patch new file mode 100644 index 00000000000..7bb98352203 --- /dev/null +++ b/third_party/webrender/patches/0002-Bug-1646741-Update-gleam-to-0.12.-r-kvark.patch @@ -0,0 +1,174 @@ +From 299c4db222eb9f0acd9623b66b8f3d0a8a8f77f2 Mon Sep 17 00:00:00 2001 +From: Jeff Muizelaar <jmuizelaar@mozilla.com> +Date: Fri, 19 Jun 2020 04:10:02 +0000 +Subject: [PATCH 2/6] Bug 1646741 - Update gleam to 0.12. r=kvark + +For stride calculation and SSBOs + +Differential Revision: https://phabricator.services.mozilla.com/D80191 + +[ghsync] From https://hg.mozilla.org/mozilla-central/rev/ef8485a16d099e24f4832178664c5a93a28396ec +--- + Cargo.lock | 16 ++++++++-------- + direct-composition/Cargo.toml | 2 +- + example-compositor/compositor/Cargo.toml | 2 +- + examples/Cargo.toml | 2 +- + swgl/Cargo.toml | 2 +- + webrender/Cargo.toml | 2 +- + wrench/Cargo.toml | 2 +- + 7 files changed, 14 insertions(+), 14 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index cf91162d5..3eb484f26 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -257,7 +257,7 @@ name = "compositor" + version = "0.1.0" + dependencies = [ + "compositor-windows 0.1.0", +- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", + ] + +@@ -435,7 +435,7 @@ name = "direct-composition" + version = "0.1.0" + dependencies = [ + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.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.8 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -617,7 +617,7 @@ dependencies = [ + + [[package]] + name = "gleam" +-version = "0.11.0" ++version = "0.12.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)", +@@ -1545,7 +1545,7 @@ name = "swgl" + version = "0.1.0" + dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-cxx 0.1.0", + "webrender_build 0.0.1", + ] +@@ -1773,7 +1773,7 @@ dependencies = [ + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1808,7 +1808,7 @@ dependencies = [ + "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", +@@ -1938,7 +1938,7 @@ dependencies = [ + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", + "font-loader 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -2087,7 +2087,7 @@ dependencies = [ + "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" + "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.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9a13b5bb12ab457c15400b43cbba5971df5c1898b6a9c30cc8c52cb01baa112" ++"checksum gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8d023b0b00c16960f0f82816f2f546dabe937e75b25c7d6ce09a63e6a52d71e" + "checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" + "checksum glsl 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "766443890761b3c4edcce86cafaac97971b200662fbdd0446eb7c6b99b4401ea" + "checksum glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f22b383fcf6f85c4a268af39a0758ec40970e5f9f8fe9809e4415d48409b8379" +diff --git a/direct-composition/Cargo.toml b/direct-composition/Cargo.toml +index d099402d8..3506aec02 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.11" ++gleam = "0.12" + 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 ce4d61928..d505e9ad2 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.11.0" ++gleam = "0.12.0" + + [target.'cfg(windows)'.dependencies] + compositor-windows = { path = "../compositor-windows" } +diff --git a/examples/Cargo.toml b/examples/Cargo.toml +index 31c695f98..09e658ca2 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.11" ++gleam = "0.12" + glutin = "0.21" + rayon = "1" + webrender = { path = "../webrender" } +diff --git a/swgl/Cargo.toml b/swgl/Cargo.toml +index 3d57edbab..bc5a04b0a 100644 +--- a/swgl/Cargo.toml ++++ b/swgl/Cargo.toml +@@ -12,4 +12,4 @@ glsl-to-cxx = { path = "../glsl-to-cxx" } + webrender_build = { path = "../webrender_build" } + + [dependencies] +-gleam = "0.11.0" ++gleam = "0.12.0" +diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml +index f7679da57..2b0ab14fb 100644 +--- a/webrender/Cargo.toml ++++ b/webrender/Cargo.toml +@@ -34,7 +34,7 @@ cfg-if = "0.1.2" + cstr = "0.1.2" + euclid = { version = "0.20.0", features = ["serde"] } + fxhash = "0.2.1" +-gleam = "0.11.0" ++gleam = "0.12.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 988e2537a..4ba95e4c4 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.11" ++gleam = "0.12" + glutin = "0.21" + app_units = "0.7" + clap = { version = "2", features = ["yaml"] } +-- +2.39.2 + diff --git a/third_party/webrender/patches/0003-Bug-1651889.-Update-to-gleam-0.12.1.-r-kvark.patch b/third_party/webrender/patches/0003-Bug-1651889.-Update-to-gleam-0.12.1.-r-kvark.patch new file mode 100644 index 00000000000..6ca3264d183 --- /dev/null +++ b/third_party/webrender/patches/0003-Bug-1651889.-Update-to-gleam-0.12.1.-r-kvark.patch @@ -0,0 +1,107 @@ +From 3767bd8938b5b849bc23bb7ac490cb0c27655560 Mon Sep 17 00:00:00 2001 +From: Jeff Muizelaar <jmuizelaar@mozilla.com> +Date: Sat, 11 Jul 2020 09:42:33 +0000 +Subject: [PATCH 3/6] Bug 1651889. Update to gleam 0.12.1. r=kvark + +This should fix a crash caused by an unexpected pixel type. + +Differential Revision: https://phabricator.services.mozilla.com/D83167 + +[ghsync] From https://hg.mozilla.org/mozilla-central/rev/b850773b54e129888b8fb2f1e3bc68f528aeccbf +--- + Cargo.lock | 16 ++++++++-------- + webrender/Cargo.toml | 2 +- + 2 files changed, 9 insertions(+), 9 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index 3eb484f26..24f92084c 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -257,7 +257,7 @@ name = "compositor" + version = "0.1.0" + dependencies = [ + "compositor-windows 0.1.0", +- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", + ] + +@@ -435,7 +435,7 @@ name = "direct-composition" + version = "0.1.0" + dependencies = [ + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.1 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -617,7 +617,7 @@ dependencies = [ + + [[package]] + name = "gleam" +-version = "0.12.0" ++version = "0.12.1" + 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)", +@@ -1545,7 +1545,7 @@ name = "swgl" + version = "0.1.0" + dependencies = [ + "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glsl-to-cxx 0.1.0", + "webrender_build 0.0.1", + ] +@@ -1773,7 +1773,7 @@ dependencies = [ + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1808,7 +1808,7 @@ dependencies = [ + "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", +@@ -1938,7 +1938,7 @@ dependencies = [ + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", + "font-loader 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "gleam 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -2087,7 +2087,7 @@ dependencies = [ + "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" + "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.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8d023b0b00c16960f0f82816f2f546dabe937e75b25c7d6ce09a63e6a52d71e" ++"checksum gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdef5b9df6d3a261b80a5ac55e13bf93945725df2463c1b0a2e5a527dce0d37" + "checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" + "checksum glsl 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "766443890761b3c4edcce86cafaac97971b200662fbdd0446eb7c6b99b4401ea" + "checksum glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f22b383fcf6f85c4a268af39a0758ec40970e5f9f8fe9809e4415d48409b8379" +diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml +index 2b0ab14fb..3fa6630bd 100644 +--- a/webrender/Cargo.toml ++++ b/webrender/Cargo.toml +@@ -34,7 +34,7 @@ cfg-if = "0.1.2" + cstr = "0.1.2" + euclid = { version = "0.20.0", features = ["serde"] } + fxhash = "0.2.1" +-gleam = "0.12.0" ++gleam = "0.12.1" + image_loader = { optional = true, version = "0.23", package = "image", default-features = false, features = ["png"] } + lazy_static = "1" + log = "0.4" +-- +2.39.2 + diff --git a/third_party/webrender/patches/0004-Bug-1654699.-Update-core-foundation-core-graphics.-r.patch b/third_party/webrender/patches/0004-Bug-1654699.-Update-core-foundation-core-graphics.-r.patch new file mode 100644 index 00000000000..4791f15a989 --- /dev/null +++ b/third_party/webrender/patches/0004-Bug-1654699.-Update-core-foundation-core-graphics.-r.patch @@ -0,0 +1,328 @@ +From 920168aff79a7cf52980b0c90965a591f2f4204a Mon Sep 17 00:00:00 2001 +From: Jeff Muizelaar <jmuizelaar@mozilla.com> +Date: Fri, 24 Jul 2020 09:54:10 +0000 +Subject: [PATCH 4/6] Bug 1654699. Update core-foundation/core-graphics. + r=kvark,keeler,chunmin + +This includes updates to authenticator, cubeb-coreaudio, +metal, gfx-backend-vulkan, gfx-backend-metal, freetype + +libloading is duplicated because of ash + +Differential Revision: https://phabricator.services.mozilla.com/D84688 + +[ghsync] From https://hg.mozilla.org/mozilla-central/rev/45fc4a780b2b4a9e047eceba73b39b988f719c58 +--- + Cargo.lock | 110 +++++++++++++++++++++++++-------------- + webrender/Cargo.toml | 10 ++-- + webrender_api/Cargo.toml | 4 +- + wrench/Cargo.toml | 6 +-- + 4 files changed, 80 insertions(+), 50 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index 24f92084c..617092292 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -286,6 +286,15 @@ dependencies = [ + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + ] + ++[[package]] ++name = "core-foundation" ++version = "0.9.0" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++dependencies = [ ++ "core-foundation-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ++] ++ + [[package]] + name = "core-foundation-sys" + version = "0.6.2" +@@ -296,6 +305,11 @@ name = "core-foundation-sys" + version = "0.7.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + ++[[package]] ++name = "core-foundation-sys" ++version = "0.8.0" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++ + [[package]] + name = "core-graphics" + version = "0.17.3" +@@ -309,22 +323,34 @@ dependencies = [ + + [[package]] + name = "core-graphics" +-version = "0.19.0" ++version = "0.22.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-graphics-types 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ++ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ++] ++ ++[[package]] ++name = "core-graphics-types" ++version = "0.1.0" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++dependencies = [ ++ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "core-text" +-version = "15.0.0" ++version = "19.0.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ +- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", + ] +@@ -510,13 +536,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" + + [[package]] + name = "font-loader" +-version = "0.9.0" ++version = "0.11.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ +- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", +- "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "servo-fontconfig 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + ] + +@@ -535,11 +561,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" + + [[package]] + name = "freetype" +-version = "0.4.1" ++version = "0.7.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ ++ "freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", +- "servo-freetype-sys 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ++] ++ ++[[package]] ++name = "freetype-sys" ++version = "0.13.1" ++source = "registry+https://github.com/rust-lang/crates.io-index" ++dependencies = [ ++ "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ++ "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", ++ "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] +@@ -1431,29 +1467,20 @@ dependencies = [ + + [[package]] + name = "servo-fontconfig" +-version = "0.4.0" ++version = "0.5.1" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", +- "servo-fontconfig-sys 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ++ "servo-fontconfig-sys 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "servo-fontconfig-sys" +-version = "4.0.9" ++version = "5.1.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +- "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", +- "servo-freetype-sys 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)", +-] +- +-[[package]] +-name = "servo-freetype-sys" +-version = "4.0.5" +-source = "registry+https://github.com/rust-lang/crates.io-index" +-dependencies = [ +- "cmake 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ++ "freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", + ] + +@@ -1765,13 +1792,13 @@ dependencies = [ + "build-parallel 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cstr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", +- "freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", ++ "freetype 0.7.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.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glslopt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1822,8 +1849,8 @@ dependencies = [ + "app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", + "malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1931,13 +1958,13 @@ dependencies = [ + "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", +- "font-loader 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "font-loader 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.23.3 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -2051,11 +2078,14 @@ dependencies = [ + "checksum cocoa 0.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1706996401131526e36b3b49f0c4d912639ce110996f3ca144d78946727bce54" + "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" + "checksum core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" ++"checksum core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b5ed8e7e76c45974e15e41bfa8d5b0483cd90191639e01d8f5f1e606299d3fb" + "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" + "checksum core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" ++"checksum core-foundation-sys 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6" + "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" +-"checksum core-graphics 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "59e78b2e0aaf43f08e7ae0d6bc96895ef72ff0921c7d4ff4762201b2dba376dd" +-"checksum core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "131b3fd1f8bd5db9f2b398fa4fdb6008c64afc04d447c306ac2c7e98fba2a61d" ++"checksum core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f6082396a349fa49674ba1bda4077332a18bf150e8fa75745ece07085e29a113" ++"checksum core-graphics-types 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e92f5d519093a4178296707dbaa3880eae85a5ef5386675f361a1cf25376e93c" ++"checksum core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "04dfae50af11e72657fe7174cddb1ecddc5398037f7f6f39533ad69207c9a4e2" + "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" + "checksum crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be" + "checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +@@ -2075,10 +2105,11 @@ dependencies = [ + "checksum euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c6a5b0c779cd0b744c73a1d2083faf181080d696903cdad99a3b03d015d7030" + "checksum expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" + "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" +-"checksum font-loader 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "933a61458662fbc8e3cd22cdb8331edbd78545fc044e1e2cd3d742f6ce06aa41" ++"checksum font-loader 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c49d6b4c11dca1a1dd931a34a9f397e2da91abe3de4110505f3530a80e560b52" + "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" + "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +-"checksum freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "11926b2b410b469d0e9399eca4cbbe237a9ef02176c485803b29216307e8e028" ++"checksum freetype 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" ++"checksum freetype-sys 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" + "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" + "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +@@ -2183,9 +2214,8 @@ dependencies = [ + "checksum serde_bytes 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "325a073952621257820e7a3469f55ba4726d8b28657e7e36653d1c36dc2c84ae" + "checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" + "checksum serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)" = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" +-"checksum servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a088f8d775a5c5314aae09bd77340bc9c67d72b9a45258be34c83548b4814cd9" +-"checksum servo-fontconfig-sys 4.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "62b3e166450f523f4db06c14f02a2d39e76d49b5d8cbd224338d93e3595c156c" +-"checksum servo-freetype-sys 4.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4ccb6d0d32d277d3ef7dea86203d8210945eb7a45fba89dd445b3595dd0dfc" ++"checksum servo-fontconfig 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" ++"checksum servo-fontconfig-sys 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" + "checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" + "checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" + "checksum sig 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6567e29578f9bfade6a5d94a32b9a4256348358d2a3f448cab0021f9a02614a2" +diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml +index 3fa6630bd..d05cf5979 100644 +--- a/webrender/Cargo.toml ++++ b/webrender/Cargo.toml +@@ -10,7 +10,7 @@ edition = "2018" + + [features] + default = ["freetype-lib"] +-freetype-lib = ["freetype/servo-freetype-sys"] ++freetype-lib = ["freetype/freetype-sys"] + profiler = ["tracy-rs/enable_profiler"] + debugger = ["ws", "serde_json", "serde", "image_loader", "base64"] + capture = ["api/serialize", "ron", "serde", "smallvec/serde"] +@@ -65,13 +65,13 @@ sig = "1.0" + libc = "0.2" + + [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies] +-freetype = { version = "0.4", default-features = false } ++freetype = { version = "0.7", default-features = false } + libc = "0.2" + + [target.'cfg(target_os = "windows")'.dependencies] + dwrote = "0.11" + + [target.'cfg(target_os = "macos")'.dependencies] +-core-foundation = "0.7" +-core-graphics = "0.19" +-core-text = { version = "15", default-features = false } ++core-foundation = "0.9" ++core-graphics = "0.22" ++core-text = { version = "19", default-features = false } +diff --git a/webrender_api/Cargo.toml b/webrender_api/Cargo.toml +index 6b8b1e5f2..4ed7ce8e2 100644 +--- a/webrender_api/Cargo.toml ++++ b/webrender_api/Cargo.toml +@@ -28,5 +28,5 @@ malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = " + peek-poke = { version = "0.2", path = "../peek-poke", features = ["extras"] } + + [target.'cfg(target_os = "macos")'.dependencies] +-core-foundation = "0.7" +-core-graphics = "0.19" ++core-foundation = "0.9" ++core-graphics = "0.22" +diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml +index 4ba95e4c4..33679485a 100644 +--- a/wrench/Cargo.toml ++++ b/wrench/Cargo.toml +@@ -38,8 +38,8 @@ default-features = false + features = ["png"] + + [target.'cfg(target_os = "macos")'.dependencies] +-core-graphics = "0.19" +-core-foundation = "0.7" ++core-graphics = "0.22" ++core-foundation = "0.9" + + [features] + default = [ "env_logger" ] +@@ -51,7 +51,7 @@ dwrote = "0.11" + mozangle = {version = "0.3.1", features = ["egl"]} + + [target.'cfg(all(unix, not(target_os = "android")))'.dependencies] +-font-loader = "0.9" ++font-loader = "0.11" + + # Configuration information used when building wrench as an APK. + [package.metadata.android] +-- +2.39.2 + diff --git a/third_party/webrender/patches/0005-Bug-1656236-Update-to-euclid-0.22.-r-kvark.patch b/third_party/webrender/patches/0005-Bug-1656236-Update-to-euclid-0.22.-r-kvark.patch new file mode 100644 index 00000000000..91af85c23ff --- /dev/null +++ b/third_party/webrender/patches/0005-Bug-1656236-Update-to-euclid-0.22.-r-kvark.patch @@ -0,0 +1,9059 @@ +From 97fb9976d408016b4f64dac6516ac2032dcc27b3 Mon Sep 17 00:00:00 2001 +From: Nicolas Silva <nsilva@mozilla.com> +Date: Wed, 19 Aug 2020 10:22:02 +0000 +Subject: [PATCH 1/2] Bug 1656236 - Update to euclid 0.22. r=kvark + +Differential Revision: https://phabricator.services.mozilla.com/D85549 + +[ghsync] From https://hg.mozilla.org/mozilla-central/rev/dcfa644ba0788e1ce1e5d8523652cea6f83c8ce9 +--- + Cargo.lock | 28 ++++---- + direct-composition/Cargo.toml | 2 +- + examples/Cargo.toml | 2 +- + examples/animation.rs | 6 +- + peek-poke/Cargo.toml | 2 +- + tileview/Cargo.toml | 2 +- + tileview/src/main.rs | 6 +- + webrender/Cargo.toml | 4 +- + webrender/src/batch.rs | 10 +-- + webrender/src/border.rs | 12 ++-- + webrender/src/device/gl.rs | 2 +- + webrender/src/picture.rs | 6 +- + webrender/src/prim_store/mod.rs | 4 +- + webrender/src/render_backend.rs | 2 +- + webrender/src/render_target.rs | 2 +- + webrender/src/render_task_graph.rs | 2 +- + webrender/src/spatial_node.rs | 14 ++-- + webrender/src/spatial_tree.rs | 34 +++++----- + webrender/src/texture_cache.rs | 2 +- + webrender/src/util.rs | 63 +++++++++--------- + webrender_api/Cargo.toml | 2 +- + webrender_api/src/image.rs | 12 ++-- + webrender_api/src/image_tiling.rs | 13 ++-- + webrender_api/src/resources.rs | 13 ++-- + wr_malloc_size_of/Cargo.toml | 2 +- + wrench/Cargo.toml | 2 +- + wrench/reftests/aa/aa-dist-bug.yaml | 2 +- + .../clip/clip-45-degree-rotation-ref.png | Bin 13247 -> 13358 bytes + .../clip/clip-45-degree-rotation.yaml | 4 +- + wrench/reftests/clip/clip-out-rotation.yaml | 2 +- + wrench/reftests/clip/custom-clip-chains.yaml | 2 +- + wrench/reftests/clip/reftest.list | 2 +- + .../filters/backdrop-filter-perspective.png | Bin 60137 -> 60142 bytes + .../filters/backdrop-filter-perspective.yaml | 2 +- + .../filters/filter-drop-shadow-clip-2.yaml | 2 +- + .../filters/filter-drop-shadow-clip-3.yaml | 4 +- + .../filters/svg-filter-blur-transforms.yaml | 2 +- + .../svg-filter-drop-shadow-perspective.png | Bin 13041 -> 13036 bytes + .../svg-filter-drop-shadow-perspective.yaml | 2 +- + wrench/reftests/split/near-plane.yaml | 2 +- + wrench/reftests/split/same-plane.png | Bin 4280 -> 4279 bytes + wrench/reftests/split/same-plane.yaml | 4 +- + wrench/reftests/split/simple.yaml | 4 +- + wrench/reftests/split/split-intersect1.yaml | 2 +- + wrench/reftests/text/alpha-transform.yaml | 2 +- + wrench/reftests/text/raster-space.yaml | 6 +- + wrench/reftests/text/shadow-transforms.yaml | 4 +- + wrench/reftests/text/subpixel-rotate.yaml | 2 +- + wrench/reftests/text/writing-modes-ref.yaml | 4 +- + wrench/reftests/transforms/border-zoom.png | Bin 27613 -> 27613 bytes + wrench/reftests/transforms/border-zoom.yaml | 2 +- + .../reftests/transforms/content-offset.yaml | 2 +- + .../transforms/large-raster-root.yaml | 2 +- + wrench/reftests/transforms/local-clip.png | Bin 2187 -> 2138 bytes + wrench/reftests/transforms/local-clip.yaml | 2 +- + .../reftests/transforms/near-plane-clip.yaml | 2 +- + .../transforms/perspective-border-radius.yaml | 2 +- + .../reftests/transforms/perspective-clip.png | Bin 16937 -> 16932 bytes + .../reftests/transforms/perspective-clip.yaml | 2 +- + .../reftests/transforms/perspective-mask.png | Bin 2281 -> 2285 bytes + .../reftests/transforms/perspective-mask.yaml | 2 +- + .../transforms/perspective-origin.yaml | 2 +- + wrench/reftests/transforms/prim-suite.yaml | 2 +- + .../transforms/rotated-clip-large.png | Bin 7421 -> 7420 bytes + .../transforms/rotated-clip-large.yaml | 2 +- + wrench/reftests/transforms/rotated-clip.yaml | 2 +- + wrench/reftests/transforms/rotated-image.png | Bin 7449 -> 7441 bytes + wrench/reftests/transforms/rotated-image.yaml | 2 +- + .../transforms/screen-space-blit-trivial.yaml | 4 +- + .../reftests/transforms/screen-space-blit.png | Bin 73335 -> 73453 bytes + .../transforms/screen-space-blit.yaml | 4 +- + .../transforms/screen-space-blur.yaml | 4 +- + wrench/src/yaml_helper.rs | 54 ++++++--------- + 73 files changed, 192 insertions(+), 203 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index 617092292153887184c585e34723e97bcc78acac..b6085604cae8e18de3273bcddac43fa0a7e1abd1 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -460,7 +460,7 @@ dependencies = [ + name = "direct-composition" + version = "0.1.0" + dependencies = [ +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.12.1 (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", +@@ -513,7 +513,7 @@ dependencies = [ + + [[package]] + name = "euclid" +-version = "0.20.10" ++version = "0.22.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1142,7 +1142,7 @@ dependencies = [ + name = "peek-poke" + version = "0.2.0" + dependencies = [ +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "peek-poke-derive 0.2.1", + ] + +@@ -1169,11 +1169,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" + + [[package]] + name = "plane-split" +-version = "0.15.0" ++version = "0.17.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + ] +@@ -1626,7 +1626,7 @@ dependencies = [ + name = "tileview" + version = "0.1.0" + dependencies = [ +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.61.0", +@@ -1797,7 +1797,7 @@ dependencies = [ + "core-text 19.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cstr 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype 0.7.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.12.1 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1809,7 +1809,7 @@ dependencies = [ + "malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mozangle 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +- "plane-split 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", ++ "plane-split 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "png 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1834,7 +1834,7 @@ dependencies = [ + "app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1852,7 +1852,7 @@ dependencies = [ + "core-foundation 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_more 0.99.5 (registry+https://github.com/rust-lang/crates.io-index)", +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "malloc_size_of_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "peek-poke 0.2.0", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -1945,7 +1945,7 @@ name = "wr_malloc_size_of" + version = "0.0.1" + dependencies = [ + "app_units 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] +@@ -1963,7 +1963,7 @@ dependencies = [ + "crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", +- "euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)", ++ "euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", + "font-loader 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", +@@ -2102,7 +2102,7 @@ dependencies = [ + "checksum dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" + "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" +-"checksum euclid 0.20.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c6a5b0c779cd0b744c73a1d2083faf181080d696903cdad99a3b03d015d7030" ++"checksum euclid 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ab0e07e345fb061928646949fdf5fb888e5d75a57385e7f5856e45be289e745" + "checksum expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" + "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + "checksum font-loader 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c49d6b4c11dca1a1dd931a34a9f397e2da91abe3de4110505f3530a80e560b52" +@@ -2177,7 +2177,7 @@ dependencies = [ + "checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" + "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +-"checksum plane-split 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffe16a646a08f4b4dd74035b9ff8e378eb1a4012a74f14f5889e7001cdbece33" ++"checksum plane-split 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2211e7ccc9b6260779dd9bad59f7b10889d6361974623b9e405afd7e7e764654" + "checksum png 0.16.2 (registry+https://github.com/rust-lang/crates.io-index)" = "910f09135b1ed14bb16be445a8c23ddf0777eca485fbfc7cee00d81fecab158a" + "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +diff --git a/direct-composition/Cargo.toml b/direct-composition/Cargo.toml +index 3506aec0284102b887d5d77a0dc94811944a9cee..59528f98ce2c0aa2c13315cb89d711031ffa953d 100644 +--- a/direct-composition/Cargo.toml ++++ b/direct-composition/Cargo.toml +@@ -6,7 +6,7 @@ license = "MPL-2.0" + edition = "2018" + + [target.'cfg(windows)'.dependencies] +-euclid = "0.20" ++euclid = "0.22" + gleam = "0.12" + mozangle = {version = "0.3.1", features = ["egl"]} + webrender = {path = "../webrender"} +diff --git a/examples/Cargo.toml b/examples/Cargo.toml +index 09e658ca2ebaf2d145e19f7fec189d0ae13b405c..4cf5d2232d63b5388b239c4325c0e023df36bab5 100644 +--- a/examples/Cargo.toml ++++ b/examples/Cargo.toml +@@ -60,7 +60,7 @@ debug = ["webrender/capture", "webrender/debugger", "webrender/profiler"] + [dependencies] + app_units = "0.7" + env_logger = "0.5" +-euclid = "0.20" ++euclid = "0.22" + gleam = "0.12" + glutin = "0.21" + rayon = "1" +diff --git a/examples/animation.rs b/examples/animation.rs +index 6aa95d0838088c80530e0550cd60ffd264d3bac5..612d891178d53a1eb6d3e752724a61c48a4966e7 100644 +--- a/examples/animation.rs ++++ b/examples/animation.rs +@@ -165,9 +165,9 @@ impl Example for App { + self.angle0 += delta_angle * 0.1; + self.angle1 += delta_angle * 0.2; + self.angle2 -= delta_angle * 0.15; +- let xf0 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle0)); +- let xf1 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle1)); +- let xf2 = LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::radians(self.angle2)); ++ let xf0 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle0)); ++ let xf1 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle1)); ++ let xf2 = LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::radians(self.angle2)); + let mut txn = Transaction::new(); + txn.update_dynamic_properties( + DynamicProperties { +diff --git a/peek-poke/Cargo.toml b/peek-poke/Cargo.toml +index 709c2f30b68df8f9fe40fc306fc9025fc2313a3b..45179aeda603fce3aa4cd7fcc4eb0cff9e38cf27 100644 +--- a/peek-poke/Cargo.toml ++++ b/peek-poke/Cargo.toml +@@ -8,7 +8,7 @@ license = "MIT/Apache-2.0" + edition = "2018" + + [dependencies] +-euclid = { version = "0.20.0", optional = true } ++euclid = { version = "0.22.0", optional = true } + peek-poke-derive = { version = "0.2", path = "./peek-poke-derive", optional = true } + + [features] +diff --git a/tileview/Cargo.toml b/tileview/Cargo.toml +index cb32309424d9829a8a966643fbf828c5ec39c0ae..66e955d5a34ca233796397def7a3302162242a68 100644 +--- a/tileview/Cargo.toml ++++ b/tileview/Cargo.toml +@@ -12,4 +12,4 @@ ron = "0.5" + serde = {version = "1.0.88", features = ["derive"] } + webrender = {path = "../webrender", features=["capture","replay","debugger","png","profiler","no_static_freetype", "leak_checks"]} + webrender_api = {path = "../webrender_api", features=["serialize","deserialize"]} +-euclid = { version = "0.20.0", features = ["serde"] } ++euclid = { version = "0.22.0", features = ["serde"] } +diff --git a/tileview/src/main.rs b/tileview/src/main.rs +index 1706197b0bc4e7c4cf5358f606ed2e058b5d4208..0e33d15e4264cd7b99da4187e165ac026ac3aab4 100644 +--- a/tileview/src/main.rs ++++ b/tileview/src/main.rs +@@ -73,7 +73,7 @@ fn tile_node_to_svg(node: &TileNode, + { + match &node.kind { + TileNodeKind::Leaf { .. } => { +- let rect_world = transform.transform_rect(&node.rect.to_rect()).unwrap(); ++ let rect_world = transform.outer_transformed_rect(&node.rect.to_rect()).unwrap(); + format!("<rect x=\"{:.2}\" y=\"{:.2}\" width=\"{:.2}\" height=\"{:.2}\" />\n", + rect_world.origin.x * svg_settings.scale + svg_settings.x, + rect_world.origin.y * svg_settings.scale + svg_settings.y, +@@ -296,7 +296,7 @@ fn tile_to_svg(key: TileOffset, + origin: tile.rect.origin, + size: PictureSize::new(1.0, 1.0) + }; +- let rect_visual_id_world = slice.transform.transform_rect(&rect_visual_id).unwrap(); ++ let rect_visual_id_world = slice.transform.outer_transformed_rect(&rect_visual_id).unwrap(); + svg += &format!("\n<text class=\"svg_tile_visual_id\" x=\"{}\" y=\"{}\">{},{} ({})</text>", + rect_visual_id_world.origin.x * svg_settings.scale + svg_settings.x, + (rect_visual_id_world.origin.y + 110.0) * svg_settings.scale + svg_settings.y, +@@ -312,7 +312,7 @@ fn tile_to_svg(key: TileOffset, + origin: PicturePoint::new(rect.min.x, rect.min.y), + size: PictureSize::new(rect.max.x - rect.min.x, rect.max.y - rect.min.y), + }; +- let rect_world = slice.transform.transform_rect(&rect_pixel).unwrap(); ++ let rect_world = slice.transform.outer_transformed_rect(&rect_pixel).unwrap(); + + let style = + if let Some(prev_tile) = prev_tile { +diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml +index d05cf5979d31b563d3b51d46c61f9e6ea8537fbe..6e3389a309b62f6578d3d481557ebbc43a6bcb37 100644 +--- a/webrender/Cargo.toml ++++ b/webrender/Cargo.toml +@@ -32,7 +32,7 @@ bitflags = "1.2" + byteorder = "1.0" + cfg-if = "0.1.2" + cstr = "0.1.2" +-euclid = { version = "0.20.0", features = ["serde"] } ++euclid = { version = "0.22.0", features = ["serde"] } + fxhash = "0.2.1" + gleam = "0.12.1" + image_loader = { optional = true, version = "0.23", package = "image", default-features = false, features = ["png"] } +@@ -40,7 +40,7 @@ lazy_static = "1" + log = "0.4" + malloc_size_of_derive = "0.1" + num-traits = "0.2" +-plane-split = "0.15" ++plane-split = "0.17" + png = { optional = true, version = "0.16" } + rayon = "1" + ron = { optional = true, version = "0.5" } +diff --git a/webrender/src/batch.rs b/webrender/src/batch.rs +index 15b1e6e968e7ed28bf490314f0b49c648f59afc0..1ee371c6531533d10e5d9a30115eb8c54fa98477 100644 +--- a/webrender/src/batch.rs ++++ b/webrender/src/batch.rs +@@ -2329,14 +2329,14 @@ impl BatchBuilder { + let specific_resource_address = cache_item.uv_rect_handle.as_int(gpu_cache); + prim_header.specific_prim_address = gpu_cache.get_address(&ctx.globals.default_image_handle); + +- let segment_local_clip_rect = prim_header.local_clip_rect.intersection(&segment.local_rect); +- if segment_local_clip_rect.is_none() { +- continue; +- } ++ let segment_local_clip_rect = match prim_header.local_clip_rect.intersection(&segment.local_rect) { ++ Some(rect) => rect, ++ None => { continue; } ++ }; + + let segment_prim_header = PrimitiveHeader { + local_rect: segment.local_rect, +- local_clip_rect: segment_local_clip_rect.unwrap(), ++ local_clip_rect: segment_local_clip_rect, + specific_prim_address: prim_header.specific_prim_address, + transform_id: prim_header.transform_id, + }; +diff --git a/webrender/src/border.rs b/webrender/src/border.rs +index 094c78866a2285663a97a74a0d70f9be4ae71a1a..3185acd42c55bbc0c2b553e82d5b0e23b7f8a37e 100644 +--- a/webrender/src/border.rs ++++ b/webrender/src/border.rs +@@ -1067,12 +1067,12 @@ fn add_corner_segment( + return; + } + +- let segment_rect = image_rect.intersection(&non_overlapping_rect) +- .unwrap_or_else(LayoutRect::zero); +- +- if segment_rect.size.width <= 0. || segment_rect.size.height <= 0. { +- return; +- } ++ let segment_rect = match image_rect.intersection(&non_overlapping_rect) { ++ Some(rect) => rect, ++ None => { ++ return; ++ } ++ }; + + let texture_rect = segment_rect + .translate(-image_rect.origin.to_vector()) +diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs +index 4bd4d0491c4f20b301a3af3e4d1631d9a0818f86..6ad0e98eef3179f363cdaf6f2c9e44df0c840eb5 100644 +--- a/webrender/src/device/gl.rs ++++ b/webrender/src/device/gl.rs +@@ -2747,7 +2747,7 @@ impl Device { + debug_assert!(self.shader_is_ready); + + self.gl +- .uniform_matrix_4fv(program.u_transform, false, &transform.to_row_major_array()); ++ .uniform_matrix_4fv(program.u_transform, false, &transform.to_array()); + } + + pub fn switch_mode(&self, mode: i32) { +diff --git a/webrender/src/picture.rs b/webrender/src/picture.rs +index f5e103f0042de94a92d0cebfa7363e2916a71b74..5b71479b0a55fac98b7853a0e23d3c8186814432 100644 +--- a/webrender/src/picture.rs ++++ b/webrender/src/picture.rs +@@ -246,7 +246,7 @@ impl<Src, Dst> From<CoordinateSpaceMapping<Src, Dst>> for TransformKey { + CoordinateSpaceMapping::Transform(ref m) => { + TransformKey::Transform { + m: MatrixKey { +- m: m.to_row_major_array(), ++ m: m.to_array(), + }, + } + } +@@ -1032,7 +1032,7 @@ impl Tile { + /// Print debug information about this tile to a tree printer. + fn print(&self, pt: &mut dyn PrintTreePrinter) { + pt.new_level(format!("Tile {:?}", self.id)); +- pt.add_item(format!("local_tile_rect: {}", self.local_tile_rect)); ++ pt.add_item(format!("local_tile_rect: {:?}", self.local_tile_rect)); + pt.add_item(format!("fract_offset: {:?}", self.fract_offset)); + pt.add_item(format!("background_color: {:?}", self.background_color)); + pt.add_item(format!("invalidation_reason: {:?}", self.invalidation_reason)); +@@ -3212,7 +3212,7 @@ impl TileCacheInstance { + }; + + // If the rect is invalid, no need to create dependencies. +- if prim_rect.size.is_empty_or_negative() { ++ if prim_rect.size.is_empty() { + return None; + } + +diff --git a/webrender/src/prim_store/mod.rs b/webrender/src/prim_store/mod.rs +index 96938add4527ab29791a51b25de230fc2f6e9f44..0a1514a9b35b994e226a424bc86a75a4ac8133d1 100644 +--- a/webrender/src/prim_store/mod.rs ++++ b/webrender/src/prim_store/mod.rs +@@ -2194,7 +2194,7 @@ impl PrimitiveStore { + prim_instance.local_clip_rect + }; + +- if combined_local_clip_rect.size.is_empty_or_negative() { ++ if combined_local_clip_rect.size.is_empty() { + debug_assert!(combined_local_clip_rect.size.width >= 0.0 && + combined_local_clip_rect.size.height >= 0.0); + if prim_instance.is_chased() { +@@ -4352,7 +4352,7 @@ fn get_clipped_device_rect( + ) -> Option<DeviceRect> { + let unclipped_raster_rect = { + let world_rect = *unclipped * Scale::new(1.0); +- let raster_rect = world_rect * device_pixel_scale.inv(); ++ let raster_rect = world_rect * device_pixel_scale.inverse(); + + raster_rect.cast_unit() + }; +diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs +index 66ad336bef76216fb9488221f32558b9b30575f4..e22596a1babc1d719491774f21ae800186a908c5 100644 +--- a/webrender/src/render_backend.rs ++++ b/webrender/src/render_backend.rs +@@ -487,7 +487,7 @@ impl Document { + } + + fn has_pixels(&self) -> bool { +- !self.view.scene.device_rect.size.is_empty_or_negative() ++ !self.view.scene.device_rect.size.is_empty() + } + + fn process_frame_msg( +diff --git a/webrender/src/render_target.rs b/webrender/src/render_target.rs +index 2bd6277e6eb46651158891bf2a688f1d75657b2e..9a3c953f42a9b38fe188597e232093203de3d9d6 100644 +--- a/webrender/src/render_target.rs ++++ b/webrender/src/render_target.rs +@@ -256,7 +256,7 @@ impl<T: RenderTarget> RenderTargetList<T> { + } + }; + +- if alloc_size.is_empty_or_negative() && self.targets.is_empty() { ++ if alloc_size.is_empty() && self.targets.is_empty() { + // push an unused target here, only if we don't have any + self.targets.push(T::new(self.screen_size, self.gpu_supports_fast_clears)); + } +diff --git a/webrender/src/render_task_graph.rs b/webrender/src/render_task_graph.rs +index 7a2bd8b7f81ab2d31a9b9673d0cb63fa84207a98..3058a98838692c40891b31ff0ac4453be3d7eb61 100644 +--- a/webrender/src/render_task_graph.rs ++++ b/webrender/src/render_task_graph.rs +@@ -573,7 +573,7 @@ pub fn dump_render_tasks_as_svg( + + let saved = if task.saved_index.is_some() { " (Saved)" } else { "" }; + let label = text(tx, ty, format!("{}{}", task.kind.as_str(), saved)); +- let size = text(tx, ty + 12.0, format!("{}", task.location.size())); ++ let size = text(tx, ty + 12.0, format!("{:?}", task.location.size())); + + nodes[task_index] = Some(Node { rect, label, size }); + +diff --git a/webrender/src/spatial_node.rs b/webrender/src/spatial_node.rs +index 68f99e4411dd4a966fd548886a20fafcd5e0eb6d..2283b0fe1a956f910d6fd504aed2a98c2c87ae5c 100644 +--- a/webrender/src/spatial_node.rs ++++ b/webrender/src/spatial_node.rs +@@ -337,7 +337,7 @@ impl SpatialNode { + // perspective matrix using the scroll offset. + source_transform + .pre_translate(scroll_offset) +- .post_translate(-scroll_offset) ++ .then_translate(-scroll_offset) + } + ReferenceFrameKind::Perspective { scrolling_relative_to: None } | + ReferenceFrameKind::Transform | ReferenceFrameKind::Zoom => source_transform, +@@ -352,7 +352,7 @@ impl SpatialNode { + // between our reference frame and this node. Finally, we also include + // whatever local transformation this reference frame provides. + let relative_transform = resolved_transform +- .post_translate(snap_offset(state.parent_accumulated_scroll_offset, state.coordinate_system_relative_scale_offset.scale, global_device_pixel_scale)) ++ .then_translate(snap_offset(state.parent_accumulated_scroll_offset, state.coordinate_system_relative_scale_offset.scale, global_device_pixel_scale)) + .to_transform() + .with_destination::<LayoutPixel>(); + +@@ -389,9 +389,9 @@ impl SpatialNode { + if reset_cs_id { + // If we break 2D axis alignment or have a perspective component, we need to start a + // new incompatible coordinate system with which we cannot share clips without masking. +- let transform = state.coordinate_system_relative_scale_offset +- .to_transform() +- .pre_transform(&relative_transform); ++ let transform = relative_transform.then( ++ &state.coordinate_system_relative_scale_offset.to_transform() ++ ); + + // Push that new coordinate system and record the new id. + let coord_system = { +@@ -400,7 +400,7 @@ impl SpatialNode { + if parent_system.should_flatten { + cur_transform.flatten_z_output(); + } +- let world_transform = cur_transform.post_transform(&parent_system.world_transform); ++ let world_transform = cur_transform.then(&parent_system.world_transform); + let determinant = world_transform.determinant(); + info.invertible = determinant != 0.0 && !determinant.is_nan(); + +@@ -902,7 +902,7 @@ fn test_cst_perspective_relative_scroll() { + let mut cst = SpatialTree::new(); + let pipeline_id = PipelineId::dummy(); + let ext_scroll_id = ExternalScrollId(1, pipeline_id); +- let transform = LayoutTransform::create_perspective(100.0); ++ let transform = LayoutTransform::perspective(100.0); + + let root = cst.add_reference_frame( + None, +diff --git a/webrender/src/spatial_tree.rs b/webrender/src/spatial_tree.rs +index e11c982dfdeafb61465cfd7f296e7f644d764921..d1eaca9d8fc4fa7b1802cdb154f58266661484ba 100644 +--- a/webrender/src/spatial_tree.rs ++++ b/webrender/src/spatial_tree.rs +@@ -284,16 +284,16 @@ impl SpatialTree { + let child_cs = &self.coord_systems[child.coordinate_system_id.0 as usize]; + let child_transform = child.content_transform + .to_transform::<LayoutPixel, LayoutPixel>() +- .post_transform(&child_cs.world_transform); ++ .then(&child_cs.world_transform); + let parent_cs = &self.coord_systems[parent.coordinate_system_id.0 as usize]; + let parent_transform = parent.content_transform + .to_transform() +- .post_transform(&parent_cs.world_transform); ++ .then(&parent_cs.world_transform); + + let result = parent_transform + .inverse() + .unwrap_or_default() +- .post_transform(&child_transform) ++ .then(&child_transform) + .with_source::<LayoutPixel>() + .with_destination::<LayoutPixel>(); + return CoordinateSpaceMapping::Transform(result); +@@ -314,10 +314,10 @@ impl SpatialTree { + } + + coordinate_system_id = coord_system.parent.expect("invalid parent!"); +- transform = transform.post_transform(&coord_system.transform); ++ transform = transform.then(&coord_system.transform); + } + +- transform = transform.post_transform( ++ transform = transform.then( + &parent.content_transform + .inverse() + .to_transform(), +@@ -347,7 +347,7 @@ impl SpatialTree { + }; + let transform = scale_offset + .to_transform() +- .post_transform(&system.world_transform); ++ .then(&system.world_transform); + + CoordinateSpaceMapping::Transform(transform) + } +@@ -815,21 +815,21 @@ fn test_cst_simple_translation() { + let child1 = add_reference_frame( + &mut cst, + Some(root), +- LayoutTransform::create_translation(100.0, 0.0, 0.0), ++ LayoutTransform::translation(100.0, 0.0, 0.0), + LayoutVector2D::zero(), + ); + + let child2 = add_reference_frame( + &mut cst, + Some(child1), +- LayoutTransform::create_translation(0.0, 50.0, 0.0), ++ LayoutTransform::translation(0.0, 50.0, 0.0), + LayoutVector2D::zero(), + ); + + let child3 = add_reference_frame( + &mut cst, + Some(child2), +- LayoutTransform::create_translation(200.0, 200.0, 0.0), ++ LayoutTransform::translation(200.0, 200.0, 0.0), + LayoutVector2D::zero(), + ); + +@@ -857,21 +857,21 @@ fn test_cst_simple_scale() { + let child1 = add_reference_frame( + &mut cst, + Some(root), +- LayoutTransform::create_scale(4.0, 1.0, 1.0), ++ LayoutTransform::scale(4.0, 1.0, 1.0), + LayoutVector2D::zero(), + ); + + let child2 = add_reference_frame( + &mut cst, + Some(child1), +- LayoutTransform::create_scale(1.0, 2.0, 1.0), ++ LayoutTransform::scale(1.0, 2.0, 1.0), + LayoutVector2D::zero(), + ); + + let child3 = add_reference_frame( + &mut cst, + Some(child2), +- LayoutTransform::create_scale(2.0, 2.0, 1.0), ++ LayoutTransform::scale(2.0, 2.0, 1.0), + LayoutVector2D::zero(), + ); + +@@ -900,28 +900,28 @@ fn test_cst_scale_translation() { + let child1 = add_reference_frame( + &mut cst, + Some(root), +- LayoutTransform::create_translation(100.0, 50.0, 0.0), ++ LayoutTransform::translation(100.0, 50.0, 0.0), + LayoutVector2D::zero(), + ); + + let child2 = add_reference_frame( + &mut cst, + Some(child1), +- LayoutTransform::create_scale(2.0, 4.0, 1.0), ++ LayoutTransform::scale(2.0, 4.0, 1.0), + LayoutVector2D::zero(), + ); + + let child3 = add_reference_frame( + &mut cst, + Some(child2), +- LayoutTransform::create_translation(200.0, -100.0, 0.0), ++ LayoutTransform::translation(200.0, -100.0, 0.0), + LayoutVector2D::zero(), + ); + + let child4 = add_reference_frame( + &mut cst, + Some(child3), +- LayoutTransform::create_scale(3.0, 2.0, 1.0), ++ LayoutTransform::scale(3.0, 2.0, 1.0), + LayoutVector2D::zero(), + ); + +@@ -955,7 +955,7 @@ fn test_cst_translation_rotate() { + let child1 = add_reference_frame( + &mut cst, + Some(root), +- LayoutTransform::create_rotation(0.0, 0.0, 1.0, Angle::degrees(90.0)), ++ LayoutTransform::rotation(0.0, 0.0, 1.0, Angle::degrees(-90.0)), + LayoutVector2D::zero(), + ); + +diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs +index 7c254c7a849d053da9c13e1dcace9898e3c16d1d..9b24465680575dfcce2e16dae1cfc780456c46be 100644 +--- a/webrender/src/texture_cache.rs ++++ b/webrender/src/texture_cache.rs +@@ -1202,7 +1202,7 @@ impl TextureCache { + &mut self, + params: &CacheAllocParams, + ) -> CacheEntry { +- assert!(!params.descriptor.size.is_empty_or_negative()); ++ assert!(!params.descriptor.size.is_empty()); + + // If this image doesn't qualify to go in the shared (batching) cache, + // allocate a standalone entry. +diff --git a/webrender/src/util.rs b/webrender/src/util.rs +index f18b80a45df523de3adb9b3364f8422b43822fa1..c6a1038643265cc7579d48ef35a06f268f497028 100644 +--- a/webrender/src/util.rs ++++ b/webrender/src/util.rs +@@ -275,7 +275,7 @@ impl ScaleOffset { + } + + pub fn to_transform<F, T>(&self) -> Transform3D<f32, F, T> { +- Transform3D::row_major( ++ Transform3D::new( + self.scale.x, + 0.0, + 0.0, +@@ -376,13 +376,10 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> { + + fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>> { + let m: Transform2D<f32, Src, Dst>; +- m = Transform2D::column_major( +- self.m11 - target.x * self.m14, +- self.m21 - target.x * self.m24, +- self.m41 - target.x * self.m44, +- self.m12 - target.y * self.m14, +- self.m22 - target.y * self.m24, +- self.m42 - target.y * self.m44, ++ m = Transform2D::new( ++ self.m11 - target.x * self.m14, self.m12 - target.y * self.m14, ++ self.m21 - target.x * self.m24, self.m22 - target.y * self.m24, ++ self.m41 - target.x * self.m44, self.m42 - target.y * self.m44, + ); + m.inverse().map(|inv| Point2D::new(inv.m31, inv.m32)) + } +@@ -588,6 +585,9 @@ pub fn extract_inner_rect_safe<U>( + extract_inner_rect_impl(rect, radii, 1.0) + } + ++#[cfg(test)] ++use euclid::vec3; ++ + #[cfg(test)] + pub mod test { + use super::*; +@@ -601,7 +601,7 @@ pub mod test { + let p0 = Point2D::new(1.0, 2.0); + // an identical transform doesn't need any inverse projection + assert_eq!(m0.inverse_project(&p0), Some(p0)); +- let m1 = Transform3D::create_rotation(0.0, 1.0, 0.0, Angle::radians(PI / 3.0)); ++ let m1 = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(-PI / 3.0)); + // rotation by 60 degrees would imply scaling of X component by a factor of 2 + assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0))); + } +@@ -614,18 +614,18 @@ pub mod test { + + #[test] + fn scale_offset_convert() { +- let xref = LayoutTransform::create_translation(130.0, 200.0, 0.0); ++ let xref = LayoutTransform::translation(130.0, 200.0, 0.0); + validate_convert(&xref); + +- let xref = LayoutTransform::create_scale(13.0, 8.0, 1.0); ++ let xref = LayoutTransform::scale(13.0, 8.0, 1.0); + validate_convert(&xref); + +- let xref = LayoutTransform::create_scale(0.5, 0.5, 1.0) ++ let xref = LayoutTransform::scale(0.5, 0.5, 1.0) + .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0)); + validate_convert(&xref); + +- let xref = LayoutTransform::create_translation(50.0, 240.0, 0.0) +- .pre_transform(&LayoutTransform::create_scale(30.0, 11.0, 1.0)); ++ let xref = LayoutTransform::scale(30.0, 11.0, 1.0) ++ .then_translate(vec3(50.0, 240.0, 0.0)); + validate_convert(&xref); + } + +@@ -642,23 +642,24 @@ pub mod test { + + #[test] + fn scale_offset_inverse() { +- let xref = LayoutTransform::create_translation(130.0, 200.0, 0.0); ++ let xref = LayoutTransform::translation(130.0, 200.0, 0.0); + validate_inverse(&xref); + +- let xref = LayoutTransform::create_scale(13.0, 8.0, 1.0); ++ let xref = LayoutTransform::scale(13.0, 8.0, 1.0); + validate_inverse(&xref); + +- let xref = LayoutTransform::create_scale(0.5, 0.5, 1.0) +- .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0)); ++ let xref = LayoutTransform::translation(124.0, 38.0, 0.0). ++ then_scale(0.5, 0.5, 1.0); ++ + validate_inverse(&xref); + +- let xref = LayoutTransform::create_translation(50.0, 240.0, 0.0) +- .pre_transform(&LayoutTransform::create_scale(30.0, 11.0, 1.0)); ++ let xref = LayoutTransform::scale(30.0, 11.0, 1.0) ++ .then_translate(vec3(50.0, 240.0, 0.0)); + validate_inverse(&xref); + } + + fn validate_accumulate(x0: &LayoutTransform, x1: &LayoutTransform) { +- let x = x0.pre_transform(x1); ++ let x = x1.then(&x0); + + let s0 = ScaleOffset::from_transform(x0).unwrap(); + let s1 = ScaleOffset::from_transform(x1).unwrap(); +@@ -670,8 +671,8 @@ pub mod test { + + #[test] + fn scale_offset_accumulate() { +- let x0 = LayoutTransform::create_translation(130.0, 200.0, 0.0); +- let x1 = LayoutTransform::create_scale(7.0, 3.0, 1.0); ++ let x0 = LayoutTransform::translation(130.0, 200.0, 0.0); ++ let x1 = LayoutTransform::scale(7.0, 3.0, 1.0); + + validate_accumulate(&x0, &x1); + } +@@ -780,7 +781,7 @@ impl<Src, Dst> FastTransform<Src, Dst> { + pub fn to_transform(&self) -> Cow<Transform3D<f32, Src, Dst>> { + match *self { + FastTransform::Offset(offset) => Cow::Owned( +- Transform3D::create_translation(offset.x, offset.y, 0.0) ++ Transform3D::translation(offset.x, offset.y, 0.0) + ), + FastTransform::Transform { ref transform, .. } => Cow::Borrowed(transform), + } +@@ -799,7 +800,7 @@ impl<Src, Dst> FastTransform<Src, Dst> { + } + } + +- pub fn post_transform<NewDst>(&self, other: &FastTransform<Dst, NewDst>) -> FastTransform<Src, NewDst> { ++ pub fn then<NewDst>(&self, other: &FastTransform<Dst, NewDst>) -> FastTransform<Src, NewDst> { + match *self { + FastTransform::Offset(offset) => match *other { + FastTransform::Offset(other_offset) => { +@@ -817,15 +818,15 @@ impl<Src, Dst> FastTransform<Src, Dst> { + FastTransform::Offset(other_offset) => { + FastTransform::with_transform( + transform +- .post_translate(other_offset.to_3d()) ++ .then_translate(other_offset.to_3d()) + .with_destination::<NewDst>() + ) + } + FastTransform::Transform { transform: ref other_transform, inverse: ref other_inverse, is_2d: other_is_2d } => { + FastTransform::Transform { +- transform: transform.post_transform(other_transform), ++ transform: transform.then(other_transform), + inverse: inverse.as_ref().and_then(|self_inv| +- other_inverse.as_ref().map(|other_inv| self_inv.pre_transform(other_inv)) ++ other_inverse.as_ref().map(|other_inv| other_inv.then(self_inv)) + ), + is_2d: is_2d & other_is_2d, + } +@@ -838,7 +839,7 @@ impl<Src, Dst> FastTransform<Src, Dst> { + &self, + other: &FastTransform<NewSrc, Src> + ) -> FastTransform<NewSrc, Dst> { +- other.post_transform(self) ++ other.then(self) + } + + pub fn pre_translate(&self, other_offset: Vector2D<f32, Src>) -> Self { +@@ -850,13 +851,13 @@ impl<Src, Dst> FastTransform<Src, Dst> { + } + } + +- pub fn post_translate(&self, other_offset: Vector2D<f32, Dst>) -> Self { ++ pub fn then_translate(&self, other_offset: Vector2D<f32, Dst>) -> Self { + match *self { + FastTransform::Offset(offset) => { + FastTransform::Offset(offset + other_offset * Scale::<_, _, Src>::new(1.0)) + } + FastTransform::Transform { ref transform, .. } => { +- let transform = transform.post_translate(other_offset.to_3d()); ++ let transform = transform.then_translate(other_offset.to_3d()); + FastTransform::with_transform(transform) + } + } +diff --git a/webrender_api/Cargo.toml b/webrender_api/Cargo.toml +index 4ed7ce8e2344ab3db9e6ab50331a2e813ae00fbb..1a48083a10b49e29a811d3cafad9753b8e363614 100644 +--- a/webrender_api/Cargo.toml ++++ b/webrender_api/Cargo.toml +@@ -18,7 +18,7 @@ app_units = "0.7" + bitflags = "1.2" + byteorder = "1.2.1" + derive_more = "0.99" +-euclid = { version = "0.20.0", features = ["serde"] } ++euclid = { version = "0.22.0", features = ["serde"] } + malloc_size_of_derive = "0.1" + serde = { version = "1.0", features = ["rc"] } + serde_derive = "1.0" +diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs +index 869cb05aaaad1c007014429e97590d30e9ff681b..deaeb92aeb1c1a7ef9403bcc171e2ee10877d120 100644 +--- a/webrender_api/src/image.rs ++++ b/webrender_api/src/image.rs +@@ -516,8 +516,9 @@ where + + match (*self, *other) { + (All, rect) | (rect, All) => rect, +- (Partial(rect1), Partial(rect2)) => Partial(rect1.intersection(&rect2) +- .unwrap_or_else(Rect::zero)) ++ (Partial(rect1), Partial(rect2)) => { ++ Partial(rect1.intersection(&rect2).unwrap_or_else(Rect::zero)) ++ } + } + } + +@@ -526,9 +527,10 @@ where + use crate::DirtyRect::*; + + match *self { +- All => *rect, +- Partial(dirty_rect) => dirty_rect.intersection(rect) +- .unwrap_or_else(Rect::zero), ++ All => *rect, ++ Partial(dirty_rect) => { ++ dirty_rect.intersection(rect).unwrap_or_else(Rect::zero) ++ } + } + } + } +diff --git a/webrender_api/src/image_tiling.rs b/webrender_api/src/image_tiling.rs +index 3534ded3640b8d73388d0c9b06f485dac0b6cb3d..8fdc82ef24d49d82169993fab2a880be858615fa 100644 +--- a/webrender_api/src/image_tiling.rs ++++ b/webrender_api/src/image_tiling.rs +@@ -592,13 +592,12 @@ pub fn compute_valid_tiles_if_bounds_change( + new_rect: &DeviceIntRect, + tile_size: u16, + ) -> Option<TileRange> { +- let intersection = prev_rect.intersection(new_rect); +- +- if intersection.is_none() { +- return Some(TileRange::zero()); +- } +- +- let intersection = intersection.unwrap_or_else(DeviceIntRect::zero); ++ let intersection = match prev_rect.intersection(new_rect) { ++ Some(rect) => rect, ++ None => { ++ return Some(TileRange::zero()); ++ } ++ }; + + let left = prev_rect.min_x() != new_rect.min_x(); + let right = prev_rect.max_x() != new_rect.max_x(); +diff --git a/webrender_api/src/resources.rs b/webrender_api/src/resources.rs +index 19b97df1cb40fbcc883d2411cb2f0cc631f86422..c41fdc3009e30806af89e809f3a999f30396b274 100644 +--- a/webrender_api/src/resources.rs ++++ b/webrender_api/src/resources.rs +@@ -291,13 +291,12 @@ fn compute_valid_tiles_if_bounds_change( + new_rect: &DeviceIntRect, + tile_size: u16, + ) -> Option<TileRange> { +- let intersection = prev_rect.intersection(new_rect); +- +- if intersection.is_none() { +- return Some(TileRange::zero()); +- } +- +- let intersection = intersection.unwrap_or_else(DeviceIntRect::zero); ++ let intersection = match prev_rect.intersection(new_rect) { ++ Some(rect) => rect, ++ None => { ++ return Some(TileRange::zero()); ++ } ++ }; + + let left = prev_rect.min_x() != new_rect.min_x(); + let right = prev_rect.max_x() != new_rect.max_x(); +diff --git a/wr_malloc_size_of/Cargo.toml b/wr_malloc_size_of/Cargo.toml +index 589e309ba1396eb84a6a371ec55d4313765ccd67..a1f92126fc4b7bb9e3e1b6852f0550ab563715c2 100644 +--- a/wr_malloc_size_of/Cargo.toml ++++ b/wr_malloc_size_of/Cargo.toml +@@ -11,4 +11,4 @@ path = "lib.rs" + + [dependencies] + app_units = "0.7" +-euclid = "0.20" ++euclid = "0.22" +diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml +index 33679485acf1f6fb885a3c7f0a5f6e813176360d..5595ced68e277f9a18a7a678edfe83bbdfb218ff 100644 +--- a/wrench/Cargo.toml ++++ b/wrench/Cargo.toml +@@ -11,7 +11,7 @@ base64 = "0.10" + bincode = "1.0" + byteorder = "1.0" + env_logger = { version = "0.5", optional = true } +-euclid = "0.20" ++euclid = "0.22" + gleam = "0.12" + glutin = "0.21" + app_units = "0.7" +diff --git a/wrench/reftests/aa/aa-dist-bug.yaml b/wrench/reftests/aa/aa-dist-bug.yaml +index 7d91d87ad6ae30e2017f066c8271b85e29736737..19b747aabce4adf42a94b97995100ecbbab2556f 100644 +--- a/wrench/reftests/aa/aa-dist-bug.yaml ++++ b/wrench/reftests/aa/aa-dist-bug.yaml +@@ -6,7 +6,7 @@ + root: + items: + - type: stacking-context +- transform: rotate-z(45) rotate-x(60) ++ transform: rotate-z(-45) rotate-x(-60) + transform-origin: 300 300 + items: + - type: box-shadow +diff --git a/wrench/reftests/clip/clip-45-degree-rotation-ref.png b/wrench/reftests/clip/clip-45-degree-rotation-ref.png +index cbe0e4d38ecdf6f1c165374050da84d1018bb76b..620eb49e43504e30b19f9c27a680603e2762e1c8 100644 +GIT binary patch +literal 13358 +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 +B|IPpa + +literal 13247 +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 +F@V{Kl3Mc>o + +diff --git a/wrench/reftests/clip/clip-45-degree-rotation.yaml b/wrench/reftests/clip/clip-45-degree-rotation.yaml +index 21b43586cdf794471483e93465628d818924d93f..316f249a3e052b40e40b8b9ae60e4f625b7c88b3 100644 +--- a/wrench/reftests/clip/clip-45-degree-rotation.yaml ++++ b/wrench/reftests/clip/clip-45-degree-rotation.yaml +@@ -6,7 +6,7 @@ root: + bounds: [0, 0, 0, 0] + "clip-rect": [0, 0, 0, 0] + type: "stacking-context" +- transform: rotate(45) translate(200, 0) ++ transform: rotate(-45) translate(200, 0) + items: + - + bounds: [0, 0, 300, 300] +@@ -23,7 +23,7 @@ root: + "clip-rect": [0, 0, 0, 0] + clip-and-scroll: 5 + type: "stacking-context" +- transform: rotate(-45) translate(-300, 0) ++ transform: rotate(45) translate(-300, 0) + items: + - + bounds: [0, 0, 1598, 1200] +diff --git a/wrench/reftests/clip/clip-out-rotation.yaml b/wrench/reftests/clip/clip-out-rotation.yaml +index 41d511ddc810190ef62b98606279c9b1ef19d1f2..43d4aa36978073fa29ff3ac3cac08801c1d57c26 100644 +--- a/wrench/reftests/clip/clip-out-rotation.yaml ++++ b/wrench/reftests/clip/clip-out-rotation.yaml +@@ -14,7 +14,7 @@ root: + - + bounds: [0, 0, 0, 0] + type: "stacking-context" +- transform: rotate(15) translate(200, 0) ++ transform: rotate(-15) translate(200, 0) + items: + - + bounds: [0, 0, 1000, 1000] +diff --git a/wrench/reftests/clip/custom-clip-chains.yaml b/wrench/reftests/clip/custom-clip-chains.yaml +index 0dfa9f41250ab51c82e89cc9b513de5cc3d2cb64..62e2fde8c513a35d12206bff3f7211aff5f03bb5 100644 +--- a/wrench/reftests/clip/custom-clip-chains.yaml ++++ b/wrench/reftests/clip/custom-clip-chains.yaml +@@ -30,7 +30,7 @@ root: + - + bounds: [0, 0, 200, 200] + type: stacking-context +- transform: rotate(90) ++ transform: rotate(-90) + items: + - type: clip + id: 3 +diff --git a/wrench/reftests/clip/reftest.list b/wrench/reftests/clip/reftest.list +index 625d62a7b43468e4ae8ce2d767d4a8d5a2a19390..8518911a9f4af9a750fc32481f1bdef00cfe4e68 100644 +--- a/wrench/reftests/clip/reftest.list ++++ b/wrench/reftests/clip/reftest.list +@@ -1,4 +1,4 @@ +-platform(linux,mac) == border-with-rounded-clip.yaml border-with-rounded-clip.png ++ platform(linux,mac) == border-with-rounded-clip.yaml border-with-rounded-clip.png + == clip-mode.yaml clip-mode.png + == clip-ellipse.yaml clip-ellipse.png + platform(linux,mac) == clip-45-degree-rotation.yaml clip-45-degree-rotation-ref.png +diff --git a/wrench/reftests/filters/backdrop-filter-perspective.png b/wrench/reftests/filters/backdrop-filter-perspective.png +index 5b0b9aa68cd8599f424118e74d3da1928bc2f14e..83e181e970b2a43a263f3f9e0523400e005483ec 100644 +GIT binary patch +delta 57754 +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�t#Te&kn$`d~ZACRs}B +zXR)t@!4Zwip9JY0guc0wQ++27KUY4o-1I8HpZv!qmY6Q@r`9|TuaazU`@7Y^7Zg;% +ztuiO@i7VuG(Ur{Es*5uTzd5;D&a<h2uN$-VKsT^5LvZ2t?$ZR#-lK%a7M3%zcO|Re +z{z=wNWcgO$a8DA|r14%>)B$lZ=2~TQKV<(GTNfYZG@A%2TPTVgg><lu+?14e2~lW( +z4aYAyW>w9eM^hGqdj+P01bJuGg352xIBHX&h@Y8?FfE1yJvWeaL!F*Zh6ic^kU+^W +z6-Rdw5nKNGVQ+B?#amQ1^irZ3Y!jOM7oU88SnZK9f<YT@d)jy)QDS>F`Sxu}f;u0b +z+VC|_rRX_PHuNpx<&=doY4Q$clOF*~p&{A(J)p8UT_0*YoHH!lo~286%5%1Shmq>d +z8q1O3yQ7Ozl&c<ZNng4Bi<O@z1WG~|O1n|S`(ke3yl38+{tAFs8_n34l$$;Hn$MZ5 +z52+3NPXJa+<~x=;0|S1pWxYKNe8^E7Gu%xSgruDqa8_!x;ZyPT$4L6wbqK{N8W4Cv +zFn-s8l-1lXdd1nSAMZ~CEuS?PZXV~8+2VV}EjvY(_lFd?1<>q~iW1FmJ_w9~u~T!3 +znrSe|8SUpu&6&`5(T@t=%POSMgJFVRZRM+bh4!oaUG!)01%f!4m|QUhIgrGjzCss4 +zvvdw<+4GC?ixPbWu5MsS#NWZce*NMSb>{8$Y2hnJXhEv?SyXs}h`!u=b_yFwAYjrx +zGw++Eb&eQ2Q_w0W>JFlc!uf;$oXpHL7BC9K_&F0SV#|9%LSi}GuHY$@Sr~lIZN8{Z +zi3U**kZ5-4;$hX5z$wx!&3H~WfqJO*Pe*{1Gc5_F#s1ogzG+7e@hsQ?7cWSXqQ1xd +zYF#2`G%=j$V`B;yUh_S_G?5rOMRMU78Mm(*mcUNLrKN!z2*<LET1!>AOy*<yFQH{C +z2Ca;VgBq^BgKk@%Ft6i&%e=(DP7)F&&}75lqB3q?hfeHRTIKZK2T4$0+yg6XHVBwu +z^RgpiCwrb>jYw|Y8erJ0B3<Riad<^D-kqon#S%n4%=5hEm!?INso_cj>1oYzeu?GE +z{2h~W)M_co80c2xu#2TEc{;3d-?n2C_9y2Xobl{tF*$yc&4;o<Ob)Lto4G23q}VXD +zj~5;A=|2K&du=o}&zK0<HxsZ-KLA;UL3N+UH#-S@%xxTtkgubH!6=6FrPx81h4kW! +zk|A7IpMAN|Y2dnjQs!$l4{d8dCIxw;!6}$sn#+}-DZSG|x+*rmOR7<e_iOX{9#^Z- +z<{ox6X|CCW4U1&wkwF;@_F45*&9NlCbH-28Hfdg=okE4NKRfvGvb5bN;Q$Wdo{L|2 +zdlUK~7ZT$%lLwWQ$9>VH;f=u6K7pP=m^wnWLCVB_Gd-`VS7DTtH2+(VL_Y3X89B&h +ze;nrS2V@a7N$ErrCc>d0p9n%z+{jA;MQ%D5M|g0&W3~AEFN<n&nte99Vp+-=x%8C$ +zs5H*orn1G&mr(Ba--bhnWPrR7>EgJnzt<EK2A53P5}82)nSm)Zyu1RVu6C?HbVGyD +zINmW)>wau}#wjl#?Sjp4NV%2HS{O&mu7ov$@_kS_Q~q*VP;7N2!Vw&O?AfIKNd5M& +zDyQ4d{kiUTGk%5NaA+qLDt6`7>J!MWZ8r<&cxE{3JJ?HT{xpy!Km@>M%zECHL^Rom +ze{ln!`ZkpqB)F-^>6(=g(Q@RgvFtZNb~fey@+o$gXbD9V`~*`7_g0ZW<JMe0OK=xe +z=cpRfX$OAz)z4U3FQ0WZIWC<oFy0mV9`3$Cqd!i6rks)rN$dcl$}F4Pk<c=o+(Lc` +zB|t>i>_brgWWQU{A6sC!pT4@<XyK*AMJNyp-X&8t%A_~X;Sim%QYr?!YseABM`q~X +zx!03=f&oGMHwm=t1?W~imrE6TGB0Nl5zpLZTC4*RtE9IQ84~{O@pI+`>c$4}0Q7B6 +ze?htS{%@(xV%4q#3kf(*aU&xmxkdv?rjf#qeL5Szd0)JNm0WZu#1#DR+kff%4{TE5 +zSSi-BnA^lY5@wWPYP`iuK?umTk<*1wkJLyqC?eSUJ*117m6bq~Y*8ZRQ5DFhEcb`= +z4}nd(qYcMadULn-&%su+Z0U>2FRDmy;%jM36bV&R{c{CWn)Q455!0lNdwr9f-X0MV +z7GD32^PNt!0hB{DH`$P;yWTLJ^cD_4oEP{`f)?ymjAv22k1SM}&O8l~56)F1-{+b- +z<84z}3NNSHwlZ`#LyPYh@&eAik7}2}k?M+L1WzbSX@=15nevx+(dAhCoa~WJ?AvGG +zx=`-P1N@lW$NEG_plv&;Lhgo}orA%?0g-(8-pF+<!0dehU$_9K$>=*F*3Chi@;3n( +zRvJ8C&brC-|EY`PE3?d3m}NJ0OLyib(6j_zl6Syn)R4zKmC}Mk1fFQ{uxz({GxLnz +z9b3!$r{o5*N4PO{q38z9=1Tc<kJo+aW7h1`LZ5E@rkMF6`Y?`kQH`lWJYKOWbGCp- +zRy-%F0CY<V?AZBFxSEL3TrxWSv1S_&Wqk@rXe7iXNR`BeXd$-_hU+F^%VKCI$9d|D +zNv+gy&@vmHXexVbo+sUOpJg_{_EvGBz{`+E3R&@8{%REaPSQt)-36NPO1o0Qi<y#= +zA1|vu)ZcH5+lEEyvy2RjSmyphdwkzY+Mlz01U`*R05tda;U33FjBja_nLBi=e$lfN +z#dPTw5md42bYd1&GaTAz_=h8J?V5g|{W3&p%B2%>*hZ;A9+z*d9#L*nv&UK9^ygb6 +zwKobmAFdnzgG^b&ElQQ2f#wcTHnG|#O+VeWH<M?oZz(X@^GwxTQKr94FZ-aM(I{?P +zgaQ~&B4%6Y-Rj>Ayg$x%x_|y-Rp{JO)})xO#9m&nq)BKx3GKB_Yqf9QzVM0V^M2(z +zZu85dm!Ye%SwaE=NzY29C6=$n_3zguW3B%!3A@yvC9QwSCW{~zeT1^JM>~?63D_8p +z<5z&E3Ro#CyPHlAq7Bog{+Pt%jawO$0QN7VB%0ZNaZ*n58oL-w`a+6CAFJg|tJ3*C +z#Z7H+(+2;xFP4)ji<Lq3MTYI>#qgi%v%;@g73t>X>01?5Uc&WBbpCGKP~w2!5GW#t +zCJ6a8r`)>$yRoL~Nb@;EPjp`(z6p<W^y$f-pQu+ZTnIoL$>s1r2_=+<vXVe_KwV%y +zytDSb%Rep{MDY&$XurIKG;*NaZl+<+RI<|HLVDt1A{|_Lc7|iBhq+yAm+Rzr`5nw3 +zZ^tJNH41>d;VoqzRsLF9u1k2)FDpvI`*PH0;$!y<mB|BG3eJ2JR?D*4FCh}WGn$$L +z_mD+$>J_hI82Bp?d5<k8NXCr>py4LU=BM$_DTz8&A9Ctel2-ArG|N6lC{Lgn;7hhK +z3?3G1*PscCGC;NFiD1_+v7u?cT(g$?G#&kB2-9ElQ#}2sX(zrb6O*;srkBjAn=(K4 +z?o#FFo%j%!axd31uYo)AVD;^si8pTQ<~2+u>TXlHF2(FYu4k;8t(-fffWZqV!SAA3 +zwvb#hJE6k$uI|e0mfMYb5h{1TK2*<`5~k6f(Hc=Bve!O1;_I5@PwPsNoENo=hP3wu +zFGZhuTK618dOt0hfX{Hghzk8R>C_vSHr$|OFW5Km(VH$8ybBUCM{wHMS9%*-XgrYD +zWL0dGrQBtp!I6am_jl?7P<1g^DUluH1Cy4@D#<|<c`nB*&<ct0h@3x~;o?#+oKv3c +z&bQuwp_`(}%NLvSaJ9mSR<i<}L3lIgl5+4Pnr@r1B6?Zm=~if?ncW6Eob~Wpiev%X +zCKPK+u*<g!^xBGv&-w*-beBn$k#=^!`VLb>zX#5+Zbn)+Jnr=Zwj<9<WP}m2GCVy2 +z@ogNhs>o>@i*<E;_=1q8lgZb|jKU=uUmF$RE}bPP2S%JRi^=udya34ReRh8e?Hg|X +zaI?oZeYk&H0=B-~O?~rrBQTx&y}vVcFsDwqvOvkSE&+I*6Hn=HRuLLMm}Y_?QI7Se +zE0<Q3m!3{0kp>5_UDD!=T#KXb6s?X93O4Qp9}v+L^lGdtXN27K$Ia=?%w#iLBt#Xs +z^o}RO9JQk8VV?&satZnx|7?+7+0O9j$%<sQky&c*l)Uj)=GY{_MUff*;3P{L#?St= +z4>6A?iBbxGtkSwjNg<6e?F)gOv|U+OZp~We?4n_r%xAFNXC&FFwM@S@f($A$5e#D% +zX&SIrOpXkA3TTPH7!|tPjR~LqxoPE<3Bfb9k$|YYrVRJ|rAbPJtTkl?HUzNEYL$W~ +zlX%VaTC~i;ncE=$Ew1;|MQ&W297A*&LaGVdUqevd#i2|N>jp`=Cu&TC`M9NKkZd*= +z@}6rBcL`|JqG(#+?o1>u7~@7#!F<l1eAmZwn4w0e!t6Zk6ZlJYAB$cjZJxtDUJJc% +zlE_Wnb!7HfMt6Co`Lm<uM+?JU=zW?iPh|uWjl9ZEKhUr)-a$Bq>EIqoAYX?`;C9O) +zUc>*dwkwZ^vTOfsLb8pWY-ubdlU<fXr0k5njLcXvLk!Xo?g~%F7LR@Glp++GNw$O; +z`@YLkc7>WLgm`}U^t|u;>EEZP-~2iEeSf~!xvuZ^J?C8KocopnQZ@95Y^FxS1JA7n +zcki9wu~x1sfumyf+!avpV5y$+k@=NW<}IXy6QXb$r5$ixI*#oDW%~7J0FU>2Yw41q +zaw&WSj#;eGQPvJnrDU?GJj{I=@iMDpoERX785w`&xzOxCK}!WhP%R6{4sLX8)uo1; +z>#$q?GBddO?e~eIT?W8IGfF4SHum7Xl7ot_rE~Yz2Kdd<Z;X%jmsvk&bx42;c8k36 +zeL$(`wbFW2wkw?RPSe-6tQveBk!yjP)+|rQC0y24_II^sqN0OwrZCFmoSn?HDjMzX +zy|-mVKl>WGHyqkdFM;a{`$zt&o%3QEzxS5@pGGPXvwC${qs)=Iw@D_m3r7{nG$Z*h +zUwn094&pOOgyjvZTNh);i__%wSLUF{x09=;E?GO-U2~*7_sN&9O}hCoQLI~9FwQ<F +zjiJ~k4%*V2!LeacvWw0yXD~I&chF8mcEilhQ8woyU+q2V`ra4V9oU?mr7_dTr>#vU +zdg%K^(+woq$O%~~g4;y8z-156pdVI~EbF)Ya`4{uvupf|Yu-rD@;x@;_4D?;iSN4k +zcyn4R3|Kzqp5Gc6)vCw!Xs<OkN+OvPS$i~q-CWV48g~2#bqsUjbv}J{$hy=Df3#tr +zkYg0}GI~z;^$j)c3+JbozR}d?WrJ5avEP|=yGKNwuBJs{;)J8+PiXDnxM_Iz32+MP +zc$?5m`rAT!Fgw9oxyq6e;)v=Y$k6dJ@^JhAyjbX<`T4x&;y?~iR&SdVzl7zB75gYV +z|EwZUq>39Y6MoTUKNbQ(UMz|#W@}ku`p_g?6p9YrKQtVCPnpB9`G|9B)knsUmjv!F +z4Zb~Jxole?;=7%8yv5oig49MhB&*PP^aJ6Nt!k&-<3~5pAJl|RL=2Q)n3>ibE@@Od +zTp?ZL=)hz9#pX%A18|THvDL~i8uQIZxx}xb{0oHq{W5|*tKXve>Txr^rg_V`zBQZ+ +z_QX^W@|LE@o{uJG*<u&JwS+Y#7Qqlfmyd<6Rl=<@hIuFdW^jokW|0hvfM@0VLv?A* +z*7bH8sXtN-GsL^#Haq+vM2RG#A&{s!Bdi|}eF#+Z7-Mav&E%F78mtZ!ifBsP<|ync +z-S&2|*p7QLaK?V>{Zsh6=;w>mdhUYj%hcycFZiqG5Ps}4l8NINMRsw9qe^k<9>oVH +z+xxfUpU;h=t$0#jeRVmng|>zzoe5FtxbB(Q-z7q0e|GUqd<0=!P~E7|@1geox3jgF +zq`gs~qfyr8ujwJHfwO|MPdT#=jxp<fZ|(F6cDvzsWGgNWFDf|2rIE6LOroM<1m44k +z%DYh1<;4*9m>W}>`QyBA1LUrlmC_T~ZS7MpsPgw%p-b~XYd`l0@&R$UJDRolB}QZI +zo2BM@HY06ktq41p2!AyF*i?R@<gyQx1SE?kocLPAmDB1hJIJ{q(%8Lzb-K;3>WH1= +zM`_AvO6W6(Y4YZ$&Vg?n4J2jd47m8G&-az%l8N4sIsL<}i8f!g!j`#?_9)0D^klMa +zNPc<9X7woX3Gyu3q9-TGJZx-3M#cslvo}d(3dP-Kk{@Aw7;(0K;e6*=F~Skyf;ELv +zgRf~>@&+8V9ru=*RVleR*q7^dEe4t1!lBajUQ2mtF241?>>+ZaO6{AUxFo1=yc7NV +zp@6vZ7^(;+taS|b?W;Qq=f-JaV9sH_oT8T{<i#M%bhLbJv@%hDCJo!p^UkiqB2~{( +z72U}jt!dbusv~@$C1zr5mh)yvZm(m}HSUn)`R@=--h`*{fg8^atuL<6yMFCes5}-M +z?&KtK{lfMn_9h&u7qL)~#4+5p{I%l{!4!N2cGlo?eP!kZ6``hyb04oR-9LC!<ix^~ +zuW}5^B6pp8!~A+kWROdc+70bj&^;G7zRelq1AJsEuDf@t$_FPW*(#+LZ*pvh=StAT +zBs-b$*-oKd^~N~&U?ER}*Eqc^+btiY)zNn!rh^e3=VA63I8jXV(ztSS3>~5XtUK)_ +z!^mAbqIga{<Ppw4_wtmO%x_|c>p!v)Am$hD+I_Y*ZqlYWAF;B@H(0t5lw{g1cy=1n +zGq}SSax9tqwbF+srgvwGUs_Es%ZIC*#3D<><P>BQNImryZm-j$0o_^uV`>+}1flV9 +znL}g4M|xjE2sPXL_WQO|x1H;pfwg8MInEy8p>vWeFmWRlm%xcHC7TWSKHMT+n0kxl +z%5n*R>{b<ad8$f*jeH0tc{bs#!5_2LMF>ylMKiGl(N}Bua@^{6bM2^Mh<m{eaNtcR +zZoXzf>g7xGZ%LMpB!?W&(!D}r3xzsM{SFHjr<iuP%q>~Qh?i_JA%NmYT<@O}sGG9X +z`J<RTAps~f|Kgnsr3&23muQR)VvDWu>qSz&gT)5hv`+l!BWqy+u7of`X1Mxkcm>Ql +z+x(h0Gn#wYkvI?^dQ}ao4fV*|I)A}e<H#Ke%R~-nI(oVy-lQ<-L;bB#&AsbEwi9nq +z>j!$ck(=Ny{KoH7-vNR9G9oBVELy27XN6oC!Fi7VG}$6xugNUP_p4l;OYU>F3L>R+ +z__d`|<yy-s*q1Sk+;UV=Wg8d9yO)!msCW_U3rlZJ1bJ;v-JTI(KlZNMS4PFJ=@I5d +zxZ@5Y+)n;6-&kJqxi+*V0^0teL59b0Cw^|+zT=OSE#{#DeZZ<r@NxCF{``q7y`eu@ +zCFU!fowYrf=w<rLN}?SebF~d3eJ4d@f`)8s%}Z?jq;0e|C6*bnX2z7i0GxZUt4EY) +zdX~}s+3~I0NQ}2!nNGvA4E|UzBhB{V*&xXbgHHCC7|Lo-uuaU3@g&`CyHXj-sWAr> +zVVAyqJ?eApo*d!UvdL}P#_x8+_HBfnM>*lZf^9vdhTUv2QEpfl=wnB3pZ&P-8lLb3 +zW4NoNJ@C|gOR(DN!%ne`w6m5&o_uCCu4SSO_ldgdG7-WKYaS|4q@FG?m|1v#0sH1N +zGUm(v;+Ic$z6P0Ok^&Fxd?&4ze=2%ecqjWbCW_`j>bIND5<+!Dg)Dm`^eRWkxaV7t +zCgR?N4^0D7Gl5QZfP{)k4~;4NlMkl^UZ}W>RkJa>7=h0Xzp5RXf*17F2^Wzs>}D>O +zQ}2JTKhN=c<wW}dlt-mPiRgG!qLrxZzQ^xnf}><S^eavt?w{8@sI0b!KPe$KBoUGf +z&-iQ|J7(d$tCRYj`=HhJ<=GrwSG5fmGchNfhU5Nh*O4h<X@F}lQy4|<ZWB(Jy{@E$ +zAJ*?D%k7rRJDZo6@NEvBEm+3_JGzTb)BF0-OMil>4Z0O95Zo)9{PTKycVVtk`20wP +z&Mc_v=R%jvYu{ySZ4G>$jhwU!*&QyxG2f2jJYD^eSG#$$;VY^dRj8Yl2K4LUYkXr9 +z!QwLHvw%6!N^Dck2|XDa{%KRjk4<Lq%)LL37kq^7%m?!I4w^3|z!dIoL?;RE9+K^d +zU%J1KZ!uj!{!@?jg=A8(HK&VtS9;BXlT^tPw4H7|&j;U#laJ1yPwS)+Td*qwsaoQ3 +z=css@Oh|7bYcbJF3nlXO@?b1D@NOa|J)J%LXba;BxHl0FOptU!Rvi>YxjUS`eZD|l +z*%67%8;eLge^tuD9<Qzt{`_gOmH3@-S*6Uq(0fhrHmg}vuFYz=X*M+P=}EVJM+ezQ +zv6UIed%Wa!OPn?<8PO*(x~~nt&I}E6<Y(YE@SlnUJtE5q6{cK6;d8d-Qy#JcwzA$& +zUtCu71e$eayvNg=MdRMT4(N-(f?p<Xyq`9&;k@-@N?_wjtZzZjX9|IX@o<cUEtxe& +z&k{NILWh{_Ni1KjAEN0{(V<Zq8}5_0oQ$*59>|w1ZpVf2J3lj3(f#9{4Re+(z7|ar +z6OZRVIpT9fsjAvWwL=MxOnG5wI(9BUP6Tl0%z_QL#zJG$sh)SmVz09D+;cT7yh%v% +z$~t1*PGC168EcQ2a>$g$@jNY>$`6ij==!X8jZm)<uo}sjjq`JAb=8lFh7EUSV9Rxs +z#|+m=r7GRi8A2!#m`ZIf3-iP93OfyNu%^DcjSj>|rzYd#Q(2>09*D%ce;VV00|S0} +zIoM&x9lIz#Dbf3b?>Q9^49qyuh{1lT^{N>32dWH703y?g8-pKjn^#J~=&f#ve{sum +zJ1^6R%iD7+v8g;Yd~PGJ!EKq-`U-wWL5hz9yYj8oCwPn2G1EFU@-UuH+_9r$n2Ya< +zcd19%_UVQHlzt<cEhNDMWiDacAbUe4sba7zGAYC_Q}{&eN5@+&Wy^`di?yu<-`?{Z +z$lPpe5qDLJ&+j^A*_;)hM&do2-1hlU6xvp&@z3X`JT_ijrN!1h9hrX)CJ_$5ly-ca +zcMu(8Ye#AK^B<y8%{bg1Iy8nBOv)Q6hGoqi?5%qXi>y<f;JyG)1Z0vRW@EWHEd<F@ +z_R6VL*5Wq9NhDaVtv%~~$WiGvJoQq$YN;b;0Yi45aXm%;Se~`B*^;;^9NCvR<anF& +z7PAmrLcv|bR${Mhvb3ex<Fq*F1=e1z=kXhhXLKB&=o>0tvj~?$T3FV&THP7X8fSb| +zf6PkSaalMy1l{hb2Bef2)w*vXzPrd$jxNr=S-pblQ69PIIR=4TP7k-N_vjkW#VNy1 +zVW|~vvKj-m+s|FWl>*peaxTki*&!=QY*MN+%3cu$UWYtu>9C2)0Q*Eyy}69S@!qy{ +z9L@0-nQRaju{To8b)hLz<G~z!=Hx?Gi2tqwuXBv0nm%wQ%GXH7UT;-erhh!1b+AeK +zexs$Sg2l}<V@>=i1J{Rrk3FGuFsvw+nir$8@=$SpXD&GJ-><E%EV8*WE^t5FC-A65 +z6r_!4JR>vx!mYHK++jHJwF2D#am7p)v+&>5se5`B2OLV~$Q_5tAE_0|GsrOXsr2S6 +z2o*p&1^h%>0<OP~y_vMkH3zvBE{x4NzmoEq-85$IQ*};S#^Q}owb#+2i(4vkWc#Iv +zPNsy^@P$)GccE=b4?iT^9vZWfYk6|l|D^6}Zd5Tz_sfgEXCJzpCvdZYb!rs*p|Q=e +zK7?j<pu*bZt5b2GCIsrXcYL%8Q&N>@73J|jad5dCVy0!r^(k2a6|)uOJ9VTwp2Ls> +zT1IcgDUg<b`yR9Skyrtc4bQdm!OvmmE=>e)W{-xh;5xgOH^=WIeIN4WOcAx##w(Pw +ztNzNX@()t~!+im62;CMeVBarcR~!&~e6p71L^hDm6cjNNICVHUuGQKe0>tK9W$a<Q +z1*BkC?2_EIj2lbOD!n<*C0n6g$+S+TCa9??^DqXi#JpmWi*ycu7D=ovX*PjjDCEOK +zE+)=i4mAkcj4KrJAT_z_8|`j8KdL?(+z)_=XX5SCf`j)}1Ry*g1#!+4Dr2A`bFN)g +zUJ?cmDC@T-x%h<7HI@Qv!AIBsnEjH9IJW5+V83A|a`u?&xvxGe>gvK;8)kpOA(LYd +z#DljHp-KF0I=+GBZ5h&V$2Xzb`#ME(#Pb^SC0i;Z8IW3yIR?!OX?`i}{FPe60z$C` +zv$I&-z(U&loi*`oXtl|JCQSjK8$4j>r92I)r;cQKvbRGe$p)w;q<Uz_$h#HvGVAcE +zA7w^yPLk+t`60igKQG(OM=O7UsMeJTJZtd}roneM$UK|ccb>4d-1XvXZmJ0&Lyi}O +zj<v2BU)^q~uL`gEZX)G|^x#b}$XuV?;eSwb8@CI)mfR6_Vs%<U<Wt?Y;Gvpr5xe8E +zbJ1V{1Og}p+T$}2!!eBMhb$2)(kZE>+&M*;Bk8C*^rY$lrT=1qBIwbj%d)N}76`gX +zDV8s6xImq{_%Mqm10Ba<n3f6%JNEuLZFvD{lLZqln$fXo21d4KeMWXlKz)H|R(iBJ +zsnjV4{u&gbvcGJ%sHfrBMIN)r0-y$#*C-TDh<9o`@tg+2br7fylI2N1bXs|qMBF*i +zKnp9G9O+~vtBasw#v=Vqds!fw%!MVVM^|e-R_hluy)YZBQS+cUJxxdO(whU$W%+1i +zJ!-yysy!jYNGLr$_C0h1{-7r?3313c^mJl;dj~hD6>lZWfh3p>NT4Y2>571Q0#CvX +zhZ-B-)f#z<r;GZjB?e){m4~kMWYvR%TI7Id476928!q$E{t*_swPIW}g}x1pMf1~T +zSKZKfa9HjNy%A)ui0C?zOQYvY2cf(qD+kZBA66KmLP`5_x0@wM=e`W57CaHx4)9?X +zpa{An!;E*N>G}3rgo{?8tB8m_jtu+mASs`mnrsKgCt4W;G2f!I_{ZJuXmBJ{Pi2)@ +zGC2_g86}ozm8GXvEz=cRzK$Doq-qHGCMP9v7?;;;krNdRWCd|2<5s@JJMDa@w#2}^ +zIF%j_&iNGq7<K^k1UUWWf#-mmdY=VfI09OS(6g=g0Lx3ZUqIQV2YKUv?RAi}p_v$( +z%#0Tf5)hJewu;OJ|4B)3DYJ@fSWf_z1-VL)r}*FXzOLh)KOja;zN|U#8a7i%*J=%? +zt{0AAlo;tF3`qc_VW54UKy-lIT`x<TPH3Iv`>P$QUgjo}eaT?2{<gdXlvF}FSfi=_ +zgCS&}I#fStgjV4;LF#{ecK+>f3E1dp;ujssIoJGEmepD@ozpK+>EMZ~EgeZJJ+?X^ +z89;7PXk<bl7`=4RECH5n_4qViXF1TV9ImUu@yq@lpjXg)#>ORci67oe0748b+XDFm +z(7fbQg9b)>zc>gxjXWOsHoO+RLtjxOyqRb|qf_spTb~wgRqyeLM1T!<Gup#nxI)2| +zU2iMdCHK2S;f{|%CqA;NH7@sjgggC%CJ_Ns7m*IvTNJ>+(4MC2+0@&$Wq@68ZHfRY +zrU*Q>c+lSvN&(0=?R2805dG5Qy$N=0(QdVZkU?EAQ6=X_Fc2rY`{+b0B!GVDye7N! +zKtewa<Q+YGpSkHGz!2pNJi7>2GtzA3;{x+XhEf5ld||4L3P>9_yI$3OLCjI5wyhaI +z{HCvnh%U?}{q47PJ5B(Tjs(w*xU;it2i`*z7J@>7gu)2ap!g+Tuy<1sp9YDgNZ)$q +ztfNZdN2%Xom;?`+Ebrs>2OlduXa^QpC~0ihe)S^o>yQ4HCxPGDm|w66_}3o%1<;=` +zf&Yh?{u7IU-$kT<0{Rms@UPhWi=aPY0>3$*{;5U4??~c*3i=Z!@XN~aFF=371b(BI +z{fb4vzZKA50sRRR)W6>?>Ho+g;5YRn{|NLaOyJ*iUcU<Z6Q<Yi?N0w|QNv${lo{-f +UUBmZZ+6VqF>znG8>0Z0{e+Jg#nE(I) + +delta 57683 +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 +R@qg@pG}LreOTZgY{sV^9x)A^X + +diff --git a/wrench/reftests/filters/backdrop-filter-perspective.yaml b/wrench/reftests/filters/backdrop-filter-perspective.yaml +index 07d64ebb9fbc08e557e8c725290e16735d221071..43d29ddf1d6223d02232805bc586b76506f76b41 100644 +--- a/wrench/reftests/filters/backdrop-filter-perspective.yaml ++++ b/wrench/reftests/filters/backdrop-filter-perspective.yaml +@@ -13,7 +13,7 @@ root: + bounds: 0 0 256 256 + - type: stacking-context + bounds: 50 50 0 0 +- transform: ["rotate-y(50)", "rotate-z(45)"] ++ transform: ["rotate-y(-50)", "rotate-z(-45)"] + items: + - type: clip + id: 2 +diff --git a/wrench/reftests/filters/filter-drop-shadow-clip-2.yaml b/wrench/reftests/filters/filter-drop-shadow-clip-2.yaml +index 232700c555ba5a4814fbbbc8b025cd294ff5a92c..6aafa7313725f5a61385af269203251cb5f986ee 100644 +--- a/wrench/reftests/filters/filter-drop-shadow-clip-2.yaml ++++ b/wrench/reftests/filters/filter-drop-shadow-clip-2.yaml +@@ -8,7 +8,7 @@ root: + clip-rect: 10 0 300 300 + - type: stacking-context + bounds: 30 30 0 0 +- transform: rotate-z(45) ++ transform: rotate-z(-45) + filters: drop-shadow([15, 0], 0, red) + clip-node: 2 + items: +diff --git a/wrench/reftests/filters/filter-drop-shadow-clip-3.yaml b/wrench/reftests/filters/filter-drop-shadow-clip-3.yaml +index 8f6552977f29d3ce80ad3358c730dbb82e781bd6..4b9ad3d36ebd1bc84986af42770ef90b0f6f3eed 100644 +--- a/wrench/reftests/filters/filter-drop-shadow-clip-3.yaml ++++ b/wrench/reftests/filters/filter-drop-shadow-clip-3.yaml +@@ -13,7 +13,7 @@ root: + items: + - type: stacking-context + bounds: [50, -10, 200, 100] +- transform: rotate-z(90) ++ transform: rotate-z(-90) + items: + - + bounds: [0, 0, 500, 150] +@@ -26,7 +26,7 @@ root: + - type: stacking-context + bounds: [150, 35, 200, 100] + filters: drop-shadow([200, 10], 5, red) +- transform: rotate-z(90) ++ transform: rotate-z(-90) + items: + - + bounds: [0, 0, 500, 150] +diff --git a/wrench/reftests/filters/svg-filter-blur-transforms.yaml b/wrench/reftests/filters/svg-filter-blur-transforms.yaml +index 23dbbcb9d456f2c6c46dafbfefd24bf9730acedd..4962ecbe130b1c787b26a4b1a7dd6667f7105adf 100644 +--- a/wrench/reftests/filters/svg-filter-blur-transforms.yaml ++++ b/wrench/reftests/filters/svg-filter-blur-transforms.yaml +@@ -3,7 +3,7 @@ root: + items: + - type: stacking-context + bounds: [0, 100, 300, 300] +- transform: scale-x(0.1) rotate-z(45) ++ transform: scale-x(0.1) rotate-z(-45) + filter-primitives: + - type: blur + radius: 10 +diff --git a/wrench/reftests/filters/svg-filter-drop-shadow-perspective.png b/wrench/reftests/filters/svg-filter-drop-shadow-perspective.png +index 1566a152ead9be48f0312db7d0e2699985b77914..7ae7393f87a6f97635febedef322dbdfe7ee589f 100644 +GIT binary patch +literal 13036 +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 +R;47B@U~HYyWrw}u{vYF~K@I=_ + +literal 13041 +zcmb`u`CpUA_deVT3aCJUDrFCZrLG8w81^MVkge6KpzKCaBa2}t1O!w-)`Y68p-oXr +zML_l~Ac7?*ghn(#*i<3`LO@JNgc$fvu<y_F4?I7pyzrj6XU@6Kb*^(}?#!P(oMm^b +z?*8t(?_^z&h|}QT4e=l8UEuGGr3+TyeP`V2g0TBFu9Pz+)5$^mj;@B}vHW&@pZ+U^ +z205mec6j&UW3I>dVV?D6ekYr_Q|@<C<^lZ!JO7Z`Ap8U^jd+Iqp~(0Z>L=HPof+O$ +zH&0h7OOh#3o?8NMA$EAhGWJQsFk_a1wVHXL)!n_tvqaYph1B9&q9$5TKFrC^E@khf +zIXlsz4m>>Glmfw_P_o7dEfw#y|KESTzE4z|<c%)L$yrFwQ9sT)!;Ac)m*b{VuXU2; +z$&2KF%#H#-{}kfSdrbcN*jEj1(N;WpzT*TYlG;4K_HYzl6zj3SObb5c<IdQ;U{%AV +zOqL#@vB+-{jz(TsEFH8k!)z28T(^1vKN9URS9XzG-8erhY|?Cto9(FFB0tzHAzqU8 +zenWs;VGe-Zo*8}-+r@qv9%JJWm3QS0^CWprg_KvCaCEar_Iy&q5OQ#AvNSs4Fym>3 +zUSvW}Lp`bc^FHN?N}GtpG`e=}msK^!OzRf5^6Ksb+UAJhLfWw1#(ncz?{jB(3wk(_ +z+4aUs8(gt76{op%Z{_+HFVoCz>uM?DR{nYQk~KR0V@yqdljZ_e7EGM{!OD!GHgj?a +z#<DJt7!@>{eI48?eslnZ=yP`t%Q5dYnk<ofEhxTx*>PsfXE>bFKKB8=HNq?ZX9258 +zxu6|6Hr-Y4Kps}LBKS2_6X&spAsJnMZC~!5_n6@=VCA0gdVTG5dRduw?~-PN95H3> +zAi?j?>fHH{mpeswvN&lrQ5*U>)$U<&n^_YYVZxLJ8+^enh2QF+H!jD<>Yp^WsGZWP +zmOx)MGKjWjd0WvclX~`?U??XqY$Pf<T@XHX-U`Cb`)>>t<)>6j$|3Y)S|bAK+JY-y +z5_E+I#3!CAO3yL*3+x1=J7M$N5R2qhSx&q>Zrsf&U+I>fA!To>f9%vfj^(6s^=v#q +zh|L+h*XaDW5G%vL+LgG<A%63gi7X6Vkk>iX!C0|gu6l4=_X_lgPE(Cfvgjihl`iC_ +z*<T!CK>7*WOEfsqTp2z{VSM~>sXIYmM|$^jMP+=^m{m*r+ofXc<ep)|q&a;r^G?NZ +z{5elvN!T?tixIP66bG~VO6!D@0#ni(W{6jK8jfRoOR;=oZ7eUzLZ;iHEgtIS+|sW3 +zaX-(md0RAwm3838qwH$mBvI`aIYuzi6{@cXSD@Ji*-Memm%=QN%PPYc`-`C`DY7o* +zv!%rO=!wCR%5{_EfCk*!sK-nOzUh_8@@6)F%wnsgD1)hcK-$A+M?uk>)}heg!f@=w +zxM{25@H5rAFsNC@N@U?%1zes~@^mnEb@<!hw6HB#PRR0VPJ^dke!*YXMRozFr7%jq +zz3}|9d16X*fIA^QV@d!YT*&gne%P3}-eMC!9A#M_(-t;M=1d8`?lIA}qh@&cKvd2~ +zB>pQkL~Yw;^TRuD_Ooyq$(9pULqQ&kiMjO`NS@v8;Y%Jf`S_-x7ad8HZiv&QyB<D@ +zX_L^n(0W+;V%;uUU}&mT6=rl@{bLlO_F^#If!8$rBCnn67GbZbgwTUayP-}Os6|sv +z1#Mcy7P)FQr0jyQ^l~(!TsL{`@r8~ViAUj|DU7+Zv0qAKX4so-H$+hCU$=IqWUBuW +z9@|m$?kdZNTeadh{3Yj^D!o4JQ>Nxqp>>G2EX^o$Q)8xWoX9I1F$+M2ax2(dH-!9) +zHlh3~ISk=V?Yj!SV|csk`Id+%h{o9{bSl&C?|0GG25?{6NC9)Md!uTCW7D<xl{^3& +zC3e?~iutZBqUv#jquK*5uYqwCl~kEdw??TQ{qj43H|VKNGBqw51oQHO>fh#P%ZHtr +zA3_bhU4EqDbkdj=dPYdu)bl<t3mP|@^<*bHZK;ADbe{)*IgsVoVv_E{&)GBLIfVzt +zx#&q!RItvp`}MhwPTClGiTbDVlzH7zY0Zk=eyqs+dkP}Yu+A#q<c@`sWA*%a!J5`& +z%|ybCn^XBSrtZPOUmOkK(wgclV#f0MWZ<#gbp5#egRy19cIO;;3nO1&*mTe|zpjgI +z!r_{9$X}Ib#4orZ^9$xtdQUtTGu##@W|HW5pN=_ALBc-`-u=Z2z6IvG(biIM9L->0 +zZPYrc0XJh(X@&#iO)t*OY>wYH7}V9(v$OO1@|(U<5iExC6uqq7F3j}6u)iGXVL&aa +znhN!B9+(7Dr2w(jXVWq@{RPcp4{5-?tOI998EQ2`W>Lo3sCq>Wg`zQNtOyl5WTl|N +zMMza)X%DF(mX(JR3Q=0g8ERE+aZ|b`GMOH3spn|0di|I@w3n`VwdW`3*=6(c)za$u +z=tGe|lJcV3;sOc9U`i4}T~gmT@ow6?PXo)AU!fFH($A<P?B|-g2GPjH#D5|;NS=i5 +zwIffa)@#HjK^caRi(2`=OoW$Y{s_0bP)>y!6}iX}r21>x`pKWFO{>%WqK*W+l&4kS +znjgk)I$`_HdxURLD1O8aQS3x-Y!XUe=YY)a@|P7mGwrTHEKlm2PwpWJpFB$#BW{{V +zr50Ssf>&`>s0$fHqOO_E()){?^rWa2cTJU|&mS+^Fo@m4dnU92<lvM?RClhDN}qn* +z3;FLyCs|1(?DU0g7`*glnUvmxh&*D)!@7md+|@g|>h8;}tId21<xO59O~%z{hf>kM +zean}JO_^e<Nub}hLV7IbzggeQVj^lM3VU^hUmPYWos(Z5#6k*wPg=`u%oyyeA(i0S +z@lN1*-SjnB`~HTC;kY~VutOy!Tc72E6n~qyzUQ(>5%5sT{4p^)NOx<VxX1i&M?(vD +ztz9<Tx9AM=)(76ByzHp;w^PDKY-soU`s_Z0p*d0mDQ7}^?+2NZ-$Uwchkirl6%Rii +zqhQ{1YkG*k=2_T<hrxU|7x-pP{VSH2p3YYc*;J;DLE(ecUfi;vl;tA#f4*~yOaQ(s +zv0+8FFowyfgfwTT_C)^Na7#J)(@ql^JIoh^{^1)kZp@7;kI(NCYdU5k5iN3nkPzDr +zd|#S9AH65?J-ub-s@e3(GA)$_%;T6#!dbR8N?)6zBCU1<W;|dQQTSd}?7%=fjMqe^ +zd5_DJ7p1_Cb5V}v)^Gc@J~-9fcTJ8r&{S}7!Q`v==^2gNEg^~2Xu<PHIwj#OZyT6B +zfaD99%Hi!}Cu&Qk=kfTRZ41xWLNB2e^RoMHd0r}iiN8;QnmmQYnU8)gUP&7E?!9m} +z${lmsVZ-`Q_*q)=d*u@h|E<9VSxpu1{NxU95oe@%tJlpwD7Ex#)DD0WIfbeC)?pEm +z+u4-5_OuP|ZS-xS*&k7kp1?|5JuTtQ2FU{6TyFMZN=um4LmX?_rqpHr%!A($f=oBB +z4~m5p>$Xgd1e$MbH!CAdgRRXD5A!(ZW{(zU_)G948vAn*2;W2UV`1H27aN3gIqJ#Q +zm$`2_Hp@wj%V7UMlM41D42&`~bj*Du2JE8HUh>}M@9h4b6c3N5{Rg@x=4&lxhV*Gg +z4PApLRD)*y6X2DvM8)&W=KD=U#mtI3Jlrl5gqHXEps4ow^Gf5uOjlHX(O98DyJxZh +zn>+8F$ArR>-`oI_O1bWqXr^P#ddXu%&C<BuGbvo)!EtDrF;Xha?vtds?AeY5r+rqV +z??4C<=e5~NrT<<SbSWGD*~3E&J)5=XIsDuHmgFiJOvp5d?W(Xs2^}9e>cSIs3igxd +zgEhuAdU3i^v?mT26z*F#+xs!jvDP(29Kn3_i3!2hJKjIJ*-Hie*F^!?x#igq9UY8Q +z)az7c#+W&&*R*vsvhGfkErtye0odL@n0k`(a(pd^f5(hCWERRqaWmDzuh({e8Zi0( +z7e_&rn@|3ax>9Yd%;hs>%uk(7wo&<$)<O3V#+3~tJ)C(9XK-J&!%#(&9}HJA=5Ohl +zgJd`mZP*^U%-w5QuBEP<ZYAw;<;YwGHAEMPS`+-muOBre^$rfWxCR)fjaAf;EMkUm +z@&9m>0w9yc3{6S-(;sWNDgy&qAcEQ5(~SPD@dbAcm9~F6uL=qnR?S<ZZkEObb={ms +zb7M5-d}*ZY^4i{N2aJ~*|NAO!T=^d$mKIzvT$^OR6P9=Bky#{)smiV9^fx+lEMxgs +zT@j}NeV}&6yX-w((9vJnh8B+xwDMBZZFx}m5aczJ{8=UP(UN#io*{1RoRFrxM2?5S +zM)M8F6YYffJ$a=r&R`A=@f4axnNzdold~#c)}|8bP9Vb!zd=+24yWzWT6;RdiA`c% +zC;tMgEU^gv8ir-x?x0Fa9*8v<04Y_m>={DeoZ>rRDu~}jY{@kZ#6c{P_d2K{Py@pa +zUX}GG#0-<~v+>Ge0f|Inp4U$68XQR(*c()EL7V~%happ{)&oyMa1CVJYsi@pU4ug# +zx^0@IJaygkUq6;*ROnj%BD3c+p@^bzQK}!2j<rgwe%N3=FtqKx^OpUF2^<&R`R+$e +z!l~p02WO}IgO6i0|6T47m4|#*!2ibNU3EK`|DX?QD($!G@GP36;?;Yh?!=Nq#P%lr +zR@L+7qX!0oK1u%iW{5xJSvvg;kJsb=#t9FdY2@;XZvI7Lov|Vn6f&W)FQQxfBZ)Nn +zZn{)Y+OKWw&JS=E+MaUJyC;0YgJtqn7|fN~aKfLF6ei#X??}x@N&O<@8Y8BY<;$^} +z%dbqn8K`_wQcCRQ6p&s=DmOxHP%R>wmOD$l_)vycQ>?Eb5b!Mm^G%oSC8WLU0Se$T +zNjP+qN!idf%kl=o%I|$?>-?<z4OsfXqzxsd0!TO*rtgkQ@TdYUP?pfk;D6U6BaZ6$ +z+mW55QKIhFpRvB3yi99j%)g!N=jqK8*U4CY4Hb}#32zNd#88qVNZCmGjK=>1gx}F| +z7dh-spftg79%=NWF(5tv&s;T5!xo*VpPQ|I%%+9g#%XBww^ll4pIU*C#}~eX#;Vq& +zy;~lo+tP<L#hbiZ#l1U+aNL?R2=hpeE}!ni<0a-wnu&B`y)e{d6+i;=#=zvN9R?^f +zyd|hioH4_3(+2HpKRb$dIT`IoQ}3R??HT_p2K)BQs70Q-j$k4&TKI9wvm{f>KJ|Bm +z-eI74^VKA4%di>55Ye+bO=Orn<QpTMKW<>~KMelR_9A)T{<BdUnWj|=JtJ*$!%T0s +zHVJJ!q4eRTc$Fx?e4`+`|D+t*{OoaxBs{KvDDjk1;UM%x4<0^h+GADu<X&`xRFB;? +z<b}n~RQLrN`*hKo?nX&+M^ndSXu_<MlU&ivH2I=%{>e<+;rCRkn@7GWA}B~IwK7s; +zS&AllMrmhQ>y85G#+IEw)^7-qzV4h9^(St;x6&*W#mrxS&v9-voLXNkii7Hk!z=X} +zYFC@xr$%l>`OJ5ptD~zkGvrd0wk6N*xj09|#J88484X0TU{_AIx=!A7LX5Cys<uqV +z+18$*gg>ivQpfqaY<;gO>+!rb?MY@v&A@ffjM)n6p+Y4k+*fv)bMC>GFQYaE`Ap~m +zZ#zG+7zrfKo0HH0KjUfdpr*P<qWzu%HX5m25o67{=nyatn7ct41pcn43bg2&j!_e6 +z`&u>HJydJ#%xj<<YrS$kc<pY%RxUN^^I!;&J02%>6bpVs7#s{75GNl{&oJUg->bw; +zo70;f-67j<lk1`TN(!RrS~O=`P&8eK$HQLME>BlZq1kID<1PRx#Ojo1(Bo|q15MnB +zJgvRDf=Npn$1#}Tpl$#bsa^{t!Cc7ZvtZ#O5Cs+D@g~h$qnaJu>Y_xbZic3}-E@ZA +z5`^|1)TSjNnPKDrd5C(∨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 +AWdHyG + +diff --git a/wrench/reftests/filters/svg-filter-drop-shadow-perspective.yaml b/wrench/reftests/filters/svg-filter-drop-shadow-perspective.yaml +index 10c4502c28de1b42af7352f761179ba573580932..744e2f655e55f4aa344e82e11d20f9dc6defd5a4 100644 +--- a/wrench/reftests/filters/svg-filter-drop-shadow-perspective.yaml ++++ b/wrench/reftests/filters/svg-filter-drop-shadow-perspective.yaml +@@ -8,7 +8,7 @@ root: + items: + - type: "stacking-context" + transform-origin: 0 250 +- transform: rotate-x(15) ++ transform: rotate-x(-15) + filter-primitives: + - type: drop-shadow + color: red +diff --git a/wrench/reftests/split/near-plane.yaml b/wrench/reftests/split/near-plane.yaml +index 15a02fdca6736b86cb254b4b1b1ef182f318116e..f1911674cafdc2db4bd93837af76a87f164acf20 100644 +--- a/wrench/reftests/split/near-plane.yaml ++++ b/wrench/reftests/split/near-plane.yaml +@@ -9,7 +9,7 @@ root: + items: + - type: stacking-context + bounds: [0, 0, 600, 600] +- transform: rotate-x(60.0) ++ transform: rotate-x(-60.0) + items: + - type: rect + bounds: [000, 0, 600, 600] +diff --git a/wrench/reftests/split/same-plane.png b/wrench/reftests/split/same-plane.png +index 76fb240d36953ad0941a28d4583f8813a9c2ce3e..414dc5cd84fc940b24d3b844c1a3b796212c6f59 100644 +GIT binary patch +delta 1925 +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 +YMEK6g+ZDNApkd&*b#t-)@}>&%e_YINzyJUM + +delta 1926 +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 + +diff --git a/wrench/reftests/split/same-plane.yaml b/wrench/reftests/split/same-plane.yaml +index d560f22351ddea1207cc0f969c1f9086c762f875..277709bad1f2346f8011e55bfee03aa55acde31e 100644 +--- a/wrench/reftests/split/same-plane.yaml ++++ b/wrench/reftests/split/same-plane.yaml +@@ -7,7 +7,7 @@ root: + items: + - type: "stacking-context" + transform-style: preserve-3d +- transform: rotate-y(30) rotate-x(75) translate(-100, 100, 0) ++ transform: rotate-y(-30) rotate-x(-75) translate(-100, 100, 0) + items: + - type: "stacking-context" + perspective: 400 +@@ -25,7 +25,7 @@ root: + color: 255 0 0 1.0000 + - type: stacking-context + bounds: [0, 0, 600, 600] +- transform: rotate-z(90) ++ transform: rotate-z(-90) + items: + - + bounds: [0, 200, 150, 200] +diff --git a/wrench/reftests/split/simple.yaml b/wrench/reftests/split/simple.yaml +index 7714690e0f0ae65e57b283b8c04f6fa503a4f340..b6445780f5f9cb61b866669548a8b9f9e42943e3 100644 +--- a/wrench/reftests/split/simple.yaml ++++ b/wrench/reftests/split/simple.yaml +@@ -10,14 +10,14 @@ root: + items: + - type: stacking-context + bounds: [0, 0, 600, 600] +- transform: rotate-y(60.0) ++ transform: rotate-y(-60.0) + items: + - type: rect + bounds: [0, 0, 600, 600] + color: [255, 0, 0, 0.5] + - type: stacking-context + bounds: [0, 0, 600, 600] +- transform: rotate-y(-60.0) ++ transform: rotate-y(60.0) + items: + - type: rect + bounds: [0, 0, 600, 600] +diff --git a/wrench/reftests/split/split-intersect1.yaml b/wrench/reftests/split/split-intersect1.yaml +index 65f9da4d5a9449008d72d6a9467849887dd39d5c..dff763870728c98c6bded01d9e25a2bac4c6ff8a 100644 +--- a/wrench/reftests/split/split-intersect1.yaml ++++ b/wrench/reftests/split/split-intersect1.yaml +@@ -10,7 +10,7 @@ root: + bounds: 0 0 100 100 + color: red + - type: stacking-context +- transform: rotate-y(0.1) ++ transform: rotate-y(-0.1) + bounds: 0 0 100 100 + items: + - type: rect +diff --git a/wrench/reftests/text/alpha-transform.yaml b/wrench/reftests/text/alpha-transform.yaml +index b2aa366c18986c425fc4c591f6613be0c8cf2836..8a623b80671321dda5b186bf5e356a36cd20a2cd 100644 +--- a/wrench/reftests/text/alpha-transform.yaml ++++ b/wrench/reftests/text/alpha-transform.yaml +@@ -2,7 +2,7 @@ root: + items: + - type: stacking-context + bounds: [0, 0, 660, 210] +- transform: scale(1.5, 2.5) rotate(10) ++ transform: scale(1.5, 2.5) rotate(-10) + items: + - text: "a Bcd Efgh Ijklm Nopqrs Tuvwxyz" + origin: 20 50 +diff --git a/wrench/reftests/text/raster-space.yaml b/wrench/reftests/text/raster-space.yaml +index 6bae473a7fb9a6b3f21a20e1b68f05b2a78e139a..e6534784123f7ce80f2b1063c03c5a5aa9204282 100644 +--- a/wrench/reftests/text/raster-space.yaml ++++ b/wrench/reftests/text/raster-space.yaml +@@ -1,7 +1,7 @@ + root: + items: + - type: stacking-context +- transform: scale(5.0) rotate(45) ++ transform: scale(5.0) rotate(-45) + transform-origin: 300 300 + raster-space: local(1.0) + items: +@@ -10,7 +10,7 @@ root: + size: 20 + font: "FreeSans.ttf" + - type: stacking-context +- transform: scale(5.0) rotate(45) ++ transform: scale(5.0) rotate(-45) + transform-origin: 0 400 + items: + - text: "Screen" +@@ -18,7 +18,7 @@ root: + size: 20 + font: "FreeSans.ttf" + - type: stacking-context +- transform: scale(5.0) rotate(45) ++ transform: scale(5.0) rotate(-45) + transform-origin: -80 240 + raster-space: local(5.0) + items: +diff --git a/wrench/reftests/text/shadow-transforms.yaml b/wrench/reftests/text/shadow-transforms.yaml +index 8e5d942458c78ce8affe16fb98dba367b5afdeb7..7a75133dab4c82c22f41399f9e7ea53240c5a469 100644 +--- a/wrench/reftests/text/shadow-transforms.yaml ++++ b/wrench/reftests/text/shadow-transforms.yaml +@@ -3,7 +3,7 @@ + root: + items: + - type: stacking-context +- transform: rotate(30) ++ transform: rotate(-30) + transform-origin: 80 80 + items: + - +@@ -38,7 +38,7 @@ root: + items: + - type: "stacking-context" + transform-origin: 235 235 +- transform: rotate-x(15) ++ transform: rotate-x(-15) + items: + - + type: "shadow" +diff --git a/wrench/reftests/text/subpixel-rotate.yaml b/wrench/reftests/text/subpixel-rotate.yaml +index 1edcdbd8d263ab0cb45b85bd75641230443945ff..296afb0d28dcf0c106613f11b560ddfbca97e83d 100644 +--- a/wrench/reftests/text/subpixel-rotate.yaml ++++ b/wrench/reftests/text/subpixel-rotate.yaml +@@ -2,7 +2,7 @@ root: + items: + - type: stacking-context + bounds: [0, 0, 430, 330] +- transform: rotate(30) ++ transform: rotate(-30) + items: + - text: "a Bcd Efgh Ijklm Nopqrs Tuvwxyz" + origin: 50 200 +diff --git a/wrench/reftests/text/writing-modes-ref.yaml b/wrench/reftests/text/writing-modes-ref.yaml +index a1d374b66d717b3b5a384dd0b2bb095699a53427..c392822e607d9cdb71488df6848eb60928eebabf 100644 +--- a/wrench/reftests/text/writing-modes-ref.yaml ++++ b/wrench/reftests/text/writing-modes-ref.yaml +@@ -2,7 +2,7 @@ root: + items: + - type: stacking-context + bounds: [0, 0, 300, 60] +- transform: rotate(90) translate(-120, 160) ++ transform: rotate(-90) translate(-120, 160) + items: + - text: "This is sideways-left" + origin: 0 40 +@@ -10,7 +10,7 @@ root: + font: "FreeSans.ttf" + - type: stacking-context + bounds: [0, 0, 300, 60] +- transform: rotate(-90) translate(-90, 120) ++ transform: rotate(90) translate(-90, 120) + items: + - text: "This is sideways-right" + origin: 0 40 +diff --git a/wrench/reftests/transforms/border-zoom.png b/wrench/reftests/transforms/border-zoom.png +index 627f69184418de1babbfd7f44a67a7a38db4ccf8..cb634ef97c4d92a3c388b716b3ee32e099e9070b 100644 +GIT binary patch +literal 27613 +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 + +literal 27613 +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%0Af|suGCi#I1*wEo`ge=| +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 +amcNNn4M|gB)cMtDoaQFX?N_(C{{C-F`aE|4 + +diff --git a/wrench/reftests/transforms/border-zoom.yaml b/wrench/reftests/transforms/border-zoom.yaml +index cbd9eb96d486c2b7307b8a1d65f44ab29889ce11..f3f472bf2114a21640a7b5ace941aa2a905312b7 100644 +--- a/wrench/reftests/transforms/border-zoom.yaml ++++ b/wrench/reftests/transforms/border-zoom.yaml +@@ -3,7 +3,7 @@ root: + items: + - type: stacking-context + bounds: [50, 50, 100, 100] +- transform: rotate(30) ++ transform: rotate(-30) + items: + - type: border + bounds: [ 10, 10, 100, 100 ] +diff --git a/wrench/reftests/transforms/content-offset.yaml b/wrench/reftests/transforms/content-offset.yaml +index d779d93053a7560b80911e52d9279f403838cebd..379b269fe6112104f1984d97755e4cdecc11f3b3 100644 +--- a/wrench/reftests/transforms/content-offset.yaml ++++ b/wrench/reftests/transforms/content-offset.yaml +@@ -9,7 +9,7 @@ root: + items: + - + type: "stacking-context" +- transform: rotate-x(45) translate(100, 100, 0) ++ transform: rotate-x(-45) translate(100, 100, 0) + "transform-style": "preserve-3d" + items: + - +diff --git a/wrench/reftests/transforms/large-raster-root.yaml b/wrench/reftests/transforms/large-raster-root.yaml +index 2383dcb6cafa7978bb07224b16006645933f3aa0..0c5176d06d98812979b6b3ed64f4c5da87182fe6 100644 +--- a/wrench/reftests/transforms/large-raster-root.yaml ++++ b/wrench/reftests/transforms/large-raster-root.yaml +@@ -6,7 +6,7 @@ root: + perspective: 20 + items: + - type: stacking-context +- transform: rotate-z(45) rotate-x(45) ++ transform: rotate-z(-45) rotate-x(-45) + filters: drop-shadow([0, 0], 10000, blue) + items: + - type: rect +diff --git a/wrench/reftests/transforms/local-clip.png b/wrench/reftests/transforms/local-clip.png +index 21fcf7de8fd7c64d050469eccd0fdc1f3159d237..92e25ec5e59cbdb0e6d8b6538f320927cfc3a333 100644 +GIT binary patch +delta 864 +zcmW-dYe*DP9L3G1x=Tg(?rKg@$;@@NBFa&#EjGj3b$qN=(rcmDrVvI!Zk8Eg3)50# +z4&|CqscrPdie9@!A%xV>L=<T+bhjtshr(EE%P<Sk?sz|*^E>B%ISuGNY)o?qMKo_y +zZQEeagIm3o3HfE~3zG8YH0RAwlLF#vQJDGSQK6jDadv$7G2i{j>F(>E@u45HYzZ_R +zK9LxRZ}J6)20!G~{mm*(%`JJf$`69SvdNFnrEep5-u@oz<n>}dm_L+BvL$6RK%)a; +z&}D$x88rT?8nx7;#l@<prpUjioNGYh$Uw`4E6^&JDIKwi-Gf0>`YzJ8h}8^yEKwa- +z?KsDVJL;h~cb|_|rpr~!g#PWl+N%<+ZxJz$?nhTH^7<3MS{=<Y4>thxbgRk5MWH|X +z+$;>*>D&k~3!O!(qdg$9!rEr&bz<4%QiOiDe8%isK@4w9Fk5!{)r#mw^IzjOarnSl +zGYk+vEj~P$Q#UqR5o_kX2Z=FOH=bLXy5{t`8cy;#h5iu(oJ^-}CwM*NSBD_xo1k|| +zIrkWe-36Lw0WntAqZw;e$5KfV0Q#CPH`+vq3oU3gO>RWug?g067PR7M8`LiwV7Z;f +zc}eL4$eFKsY=nD>u=^6!T?W`<r&Sh7=>ceavF1r8T*L>lo5k+2#hEu<Ql0?xk$H%4 +ze<1E?g?i`2wjoYZS}kaCzSfscIF}W7v_Soh0d9*lG?ndVK{uHt5^kRr*9M^eeqzAT +z6iLajp#97$BiuGC9%+DjuK|u@x*=Mv?R*&YwXeV2KG1U`b=vm~y5U`gM%841nun7x +zDz_8v-fE-J?`C~KJ`(`pEI}#LcxFu|9G<gK77f`C<ZQxHGc8EFrZGlhOtNGZ5*hL# +zOBN|rJ%_fj8el;&y@fgDggX|NS!NPIJ7Uk8m^4+Op%pF$@ha5kf3mR?nT$H@GAWjO +zx6Fj>G<4*=30;6Xa;7k-if{!#-3*!sMMu*q2GtO?uks7uc@&eFau+4yNr8CR?0>T| +V*A49b#(9Pl*el9kM{g64@*gzxO#lD@ + +delta 916 +zcmXw2e@qld6!z>!XHC=W+_m<&8pH11VF*}QPsm}c<i@#0@I<4PrX1Dth>AbLk6?^V +zYnH6rIOi>>MdhLdG%BQjaB{`OODwgUZis}M)>a}Kl?%q$TCgWZ!~|=Hg7e>d-}k=v +z-ZwLMxd=SwT(Pkjdp|wUIyN{^_rYg#d^h*Y@2R=qkhH;{+4p|tGN&bQd-Iqzo$XzJ +z?cmkn^7Hk=kn;L@(9%HS&(PfF2FnS>So@>0_0*-UEsuUaeE6NcUXm;j(LcjE16OJW +z#-E+H8?px4#yu!YQr7`E_2fIyWrA2KNyb}*%MRY*of|_W_~JC^l8Jn*A;Q9UJ8@MW +zTCOq`m?T?g^F1tlIri<_0Kk`jJ79Su*Qlghme_=+f8>cXVx<DU*S>UCdCNMI$O*_Z +zYCjy|>I$pjxj}FfSW`vFJeb(7m5Eqf^HW4453^=#+FW>1AZ?cA<VO-a$Zxx~Du$hG +z<m&ux-;CH4CZ4u5@v;?*)A?+7QI$(h+4vF91b6PF-FTys%ArkKS%`T9g^8M@usjR_ +zoS%An|NF0Bjpyz!(G&H^{P*=_JiC2WxVe~-lu!7PNfUHr;Cu5Wbr)TCwH_>?Q$jJ? +z#MLPjqN^>T5M6$)9$Y{nn|Pgt1YXd<JW16#qeM^VaVlyzZm@8wL{D~5>jk;ijhJ7d +zm|%Ieg}}Yk`t>Hrcj1&H#GHY`fu;Z7jM9qq<P!~)y5!o;h<VJ32?wv%hhfDxoZ< +z#~HXbQ&J-URvfY$cQg?5>B(%w+;+;f%XoE<P4t90W1$JC>A|cJb4kG6E=eu43PY#u +zMnnVEUOo9aVy*~s%E_w%>$Gu3R~S+&&vrkyE4^R#WVaXQ3=AuUixRDzcl%Mv%IBx! +z4R<%`m^1=-Yl0JDXf<No(E?d8`!ujas-)0IoDl8zQ*t#X!F;#5MoaL#ns#zJ<0ru~ +zALTS~#xK=rA`}Fido@5QfQ6&RDM|?;=BqfRY$Ggyqy2f6PUNQ#vL*&;Gl7EpifMg6 +z#Rdx~J;RB+cXm>N=n2kv(3?RAF*VSXyl4+|%{IYvq%-71qqN=el}*P%+=iH<p1wcF +m0x<Mp&o;~N7W!uvFT8avx8tu}H@=8j=Fn5@E5$JsRR05N)@X$Q + +diff --git a/wrench/reftests/transforms/local-clip.yaml b/wrench/reftests/transforms/local-clip.yaml +index 50f7b39788ef85b18d0dd665df58621c8244eca4..661b40bed733c439bcb2ac61fb9ca67bc2b98d5d 100644 +--- a/wrench/reftests/transforms/local-clip.yaml ++++ b/wrench/reftests/transforms/local-clip.yaml +@@ -11,7 +11,7 @@ root: + bounds: [0, 0, 0, 0] + "clip-and-scroll": 3 + type: "stacking-context" +- transform: rotate(45) translate(200, 200) ++ transform: rotate(-45) translate(200, 200) + items: + - + bounds: [0, 0, 100, 100] +diff --git a/wrench/reftests/transforms/near-plane-clip.yaml b/wrench/reftests/transforms/near-plane-clip.yaml +index 99cc121de71380dd9b6183e98876acf8f65def21..3bbafca974b064c6f320bc8708cd2faae45a946a 100644 +--- a/wrench/reftests/transforms/near-plane-clip.yaml ++++ b/wrench/reftests/transforms/near-plane-clip.yaml +@@ -10,7 +10,7 @@ root: + - + bounds: [0, 0, 1000, 1000] + type: "stacking-context" +- transform: rotate-x(-30) ++ transform: rotate-x(30) + items: + - + bounds: [350, 200, 260, 300] +diff --git a/wrench/reftests/transforms/perspective-border-radius.yaml b/wrench/reftests/transforms/perspective-border-radius.yaml +index fd9c5102877f264771149c89a3e7e547b319b79b..41b43cd48afb7e520aa43f1675121610cb45cd7e 100644 +--- a/wrench/reftests/transforms/perspective-border-radius.yaml ++++ b/wrench/reftests/transforms/perspective-border-radius.yaml +@@ -9,7 +9,7 @@ root: + - + bounds: [128, 128, 256, 256] + type: "stacking-context" +- transform: rotate-x(60) rotate-y(120) ++ transform: rotate-x(-60) rotate-y(-120) + items: + - + bounds: [128, 128, 256, 256] +diff --git a/wrench/reftests/transforms/perspective-clip.png b/wrench/reftests/transforms/perspective-clip.png +index f92856f1c5df54fb1deb7c0ee8e6903a8f45e8fd..97be10064a1ea6f97dd8c18877d15d69de0b36e1 100644 +GIT binary patch +delta 13569 +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òw@L%Z|S=vwo!p?w* +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 +U&dN#s2mDh<>Rftt(bE6_0raJEJOBUy + +delta 13540 +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 +zA$v)_&E6	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 + +diff --git a/wrench/reftests/transforms/perspective-clip.yaml b/wrench/reftests/transforms/perspective-clip.yaml +index 7c314dd22b6ae5b8a0bb27226463139992ab8dd4..ae905b8a441a4ee03ba927a606aa05468be44b45 100644 +--- a/wrench/reftests/transforms/perspective-clip.yaml ++++ b/wrench/reftests/transforms/perspective-clip.yaml +@@ -17,7 +17,7 @@ root: + items: + - + type: "stacking-context" +- transform: rotate-x(10) ++ transform: rotate-x(-10) + transform-origin: 300 300 + filters: identity + items: +diff --git a/wrench/reftests/transforms/perspective-mask.png b/wrench/reftests/transforms/perspective-mask.png +index 80c2dae951a8e884e5942fc5f22fa9209cdc35c2..41ef1283dbb146a533e378f536eec02ef97d9b52 100644 +GIT binary patch +literal 2285 +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 + +literal 2281 +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 + +diff --git a/wrench/reftests/transforms/perspective-mask.yaml b/wrench/reftests/transforms/perspective-mask.yaml +index 7df62461d27e6316e5d2bc513febfa079f32d9d4..eadc48e0bab09eda5a1fbbf8128ee0e00808344f 100644 +--- a/wrench/reftests/transforms/perspective-mask.yaml ++++ b/wrench/reftests/transforms/perspective-mask.yaml +@@ -14,7 +14,7 @@ root: + - + type: "stacking-context" + bounds: [0, 0, 250, 100] +- transform: rotate-y(-54) ++ transform: rotate-y(54) + items: + - + bounds: [0, 0, 128, 128] +diff --git a/wrench/reftests/transforms/perspective-origin.yaml b/wrench/reftests/transforms/perspective-origin.yaml +index d6ae149d44b7c7ec80346b063e1bc7c045710ecc..ca5eb5f95cf6db9625f220c8b24baea0007d7dc3 100644 +--- a/wrench/reftests/transforms/perspective-origin.yaml ++++ b/wrench/reftests/transforms/perspective-origin.yaml +@@ -9,7 +9,7 @@ root: + - + bounds: [0, 0, 1000, 1000] + type: "stacking-context" +- transform: rotate-x(45) ++ transform: rotate-x(-45) + items: + - + bounds: [350, 400, 260, 260] +diff --git a/wrench/reftests/transforms/prim-suite.yaml b/wrench/reftests/transforms/prim-suite.yaml +index 2a170f8fc90dcdaf604e706078502818cc3fee13..241a2e1c0a5b13c4316abf9f1549643715710ca5 100644 +--- a/wrench/reftests/transforms/prim-suite.yaml ++++ b/wrench/reftests/transforms/prim-suite.yaml +@@ -3,7 +3,7 @@ root: + items: + - type: stacking-context + bounds: [50, 50, 100, 100] +- transform: rotate(30) ++ transform: rotate(-30) + items: + - type: rect + bounds: [ 10, 10, 80, 80 ] +diff --git a/wrench/reftests/transforms/rotated-clip-large.png b/wrench/reftests/transforms/rotated-clip-large.png +index 88d2eda08523205e54163e5b89ea5e3f941c3b4d..c4e484037098077d8a101925d0724fa40f87e7f2 100644 +GIT binary patch +literal 7420 +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 +LIgQ-Y*B<^qGmY-K + +literal 7421 +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 +QfrAkP-4i-_M;)&G2R|Rc#{d8T + +diff --git a/wrench/reftests/transforms/rotated-clip-large.yaml b/wrench/reftests/transforms/rotated-clip-large.yaml +index 18aa7e3dd440f13910370e44f054b36974aac657..ec57e6c69731283b8acf9656c20096fe3e300371 100644 +--- a/wrench/reftests/transforms/rotated-clip-large.yaml ++++ b/wrench/reftests/transforms/rotated-clip-large.yaml +@@ -14,4 +14,4 @@ root: + bounds: 20 20 200 200 + color: blue + type: stacking-context +- transform: rotate(33) ++ transform: rotate(-33) +diff --git a/wrench/reftests/transforms/rotated-clip.yaml b/wrench/reftests/transforms/rotated-clip.yaml +index 9b17a43dbff18459b3193d5cc52b6d1d004d6a83..a4305e644995ee8cbf1be8bec647fc7f267b9b24 100644 +--- a/wrench/reftests/transforms/rotated-clip.yaml ++++ b/wrench/reftests/transforms/rotated-clip.yaml +@@ -14,4 +14,4 @@ root: + bounds: 20 20 100 100 + color: blue + type: stacking-context +- transform: rotate(30) ++ transform: rotate(-30) +diff --git a/wrench/reftests/transforms/rotated-image.png b/wrench/reftests/transforms/rotated-image.png +index f74fdbdc6e6a82c066132924164959c46c8cba27..2bb0d45e48de86ae676ffbc0d13ceda9fe8818be 100644 +GIT binary patch +literal 7441 +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 + +literal 7449 +zcmdUU`9D-``2U$1`<hggttd+rQH-4@OG?p#vBW5vdTb+MEHRUcvL!v12v3p<*|!YF +z7DXn>z71kL*=B4r80K?krtkOl`4>L(!<o6yx$f(JU)%e-?=w$qFIfr;91;KkK-lV> +z*%bhQfIlGs9}oEDa`)XW0FWkGnVDY0WG;^J<1ft&^H980UY&j1b#w6H6{>klPmSnB +z`9r7goV?R-lJ^?-^nBFpUzdB%!G4%no<C-FoZme;L^eZ+80_X%xsp{s9@t=`8@-Aj +zVAAR$=N)Z)md6(Yi^B}(9f<-#V|}&q=Ga$3=RbGrTP6)9<lgT2m69|xKCH~BOyBm_ +z(j1BFT8>}zZpto3oQgSj1VYO6skg_|Q_KiHw_2Y>jd+VGWp?@8@|fRdTFef7_VGQK +zzSy;#$eIyvoZr#UCr<FKgAk3jk7)ZqwSDhRrj+_CRO8GmcGd^J(KpHQh|0eWQ%*QP +z>MIzyxj39%<R`A}jUL3;#_ZfNUrhCv*v?~kNt(wVVCD~okZ<rkxW2#~Q;EBn@f5+e +z;W(;E4;j@Qlv@svGXE)IxmIs3R;lARtNrv_M($2_L|JzK%|i6l|H7KlbwZ)MzVb5N +zYkH^4$B7c=*}B-3ZDr*<A|P&3d$$BOym5uGHD4SA>^wZ<)sJj@x<5?|CDO37;T+D~ +zp`{syX&*S@<l)vRG~Zv;qTa~3!HdB*GG^43uYWpy{MtM0Snz~Q;Cz2u!}e0F;>DLa +zPL+Reh&qn$nx+HVLy%9KG-W(ddDH=ueYr+GfSKwRRQ9H+^)vEDY*k#dm2YQ+^yQT& +z&qYA52T2cYRhfl8h^@M8wm4U=sjwP$BSY+%c2)YXn#xG|;!|JRz8oI^ET$yr+be&` +zAbq4csb8-B%)p?|RFVedS|OjBt9O_qyzVbVnFs${n`~LxwTbXm;RyO<GzIHc`L|u# +zzW!o2KRW`^{WS^K&P9mIutE-84>o^)CC|3I%zEbQKP?`%`IlsV7C4B8goM0vt=BvM +zLfJk0m&9&8b@>6mvX%gC|2S&jk}At0TBo|TCc~OX+_}Qj+v@MpyV;A82_Et{JgS(1 +zW|6mkj_<uJ$8@Gn<rSX)g_@?ZT<_89vrTP2G2Je=s2;4Za+N0tTJhMjnaeg$pah9q +z+`p2$<Fg8on~WnEnDC7uRGNnSUuk=$qQX%Z(#_Rm0<s&|;^?W#Bk&p`Gyh#)PyNQQ +z;O6Ege4(V}d&Qlr*FIcRVfd{~JYx1Ug5wo-gS&+`#8jJ%WA`WQByFI4hcu_mnvnV@ +zrdnIToVX6xWM;e?Ko49u*<2y~$lHj=v?l107mj+P0!OxmneGS6Z+#OG-FhOfG}Woz +zD0CrGmSGSR^1bW@`CvVM=Ud4rbKWgvOzxlcLcacw*JJ-tb$Fc0w@M~fH{$bBOdWDO +zzC52CYIJXT3@2e>%@<&<@)V-2{4*Khf}5(Yi@~xRb>1?vrMcf~ohp$7EA;E(^~_Cj +z_RYZ(cXudh-RMGi%De%^v3b?Hfi@nNd+XcFcQ-t_M3Jr|S`Z461R=sAfB$?>pNds) +z?&Pg<D0Y>RojddQPm^4W`0x0T0O?2ub<$}2puE0-efx%L;s-nTMq>Y@nEsE<@(XwM +z2|mj@0p#vB2e<o={HH&=ug&$?%P{u1icn0*2kT6ucm{7K8mg(<TMU(Xws?iR&M%jB +zo}4@eDTt1@KTGvWI|p|yK~FZ9yrwVJEjO2PDMC|X)PzM1C?*xRd*loG=m)Lk!KJs? +ztCPhGhi6LNWn}rqO?I|QV95iv8O^X%)O?bH<y5kb5&y^n8Mjs4xFdYRxr(u4GGp3! +z2BV^J^RrOgp)&$!9}Ag+AB5dPEjbH56{GMhJ3FyO)YyQbmL$VnFg;v}!y61*b?F)G +z)qo)O`^|)$o@<}q06iv3G^Dv;-p&ryHF(A_7!1ln<3z;vBUD7zdPvp8A^gRf@Xfp7 +zASu!3aub^&ZC1&OkAej;C{l+)qS;85&!W2CGL6cWv>4x4Dty_oPf#D{7!4*XGIdhB +zBN~lOoMFa@DQF-GSade;)C6-kI5T86eKI|JfVr@(bztP309jN>&PbYQ%*0>6d4ezb +zi+{UJ^o5hPN2T}u%H!shGOyCHZ`U66{O9<>k3JB;B3AJt5*ERjeEZmwxxL(&7(8)b +z#{lqhUTo1?wNiSu7B<905xX*V2z4+<W~ytbO5y3WeF*mwD10s_s@b1M1pd_9uP|XT +z^WpssoLzDMBuEQ`)W<f)=I}WhNO93LM#xAaFXKqG^j>?wQYP>k#(vx$ZrkCpI-BA5 +z*x6NpauP5ub}YX&!dNxiRo36P@I!*0&BX0wIk7B9&;3h?mXnvJhi(0r-=YuT_mOfY +zz9?l&XQk6h*V@aORjvBaOcdc%boF?ss=;)CNr_#7J$$jE*EZ?uKJzLq`}QC{By-1o +z+Ma`rF1t$=CC#L|S!5gCZ%&WQob7}q4g6AT*QG{RZ&n>kIHl1eB%^oQ+5oRWAnLCF +zyxKyX8HKV!;@|tWL|nemsZu2IAwS`RPu(n897lW&mceA4arZC%={T+LwE<+bG#14) +z4qJ4ST!y37kQ7pr`_yD6q<gQyd|~<b_g9sU0YgVh^s4H5;R_nb1;jk%TpHk1f&GVM +z(ipMxJ=Zc3eJzW&t6oEb-dh|#BfUvh-l@v^w_TT4O559nxx3>}eii&!i&vL|nS3NM +z1-j85`b-8|a8oL!tW;x=P#K6hiiF-CJA|1QCcV$k5)cQJ7BnCgeiKI4fWaQ73_muz +z=_r91FAfyM#i}>J87{6OF{~&i>hi$1Zj)XG7q%z_ndzf~=^7q<->d!H))t1e3IU8^ +zx0ZkE$?LY2fA>+wnT@It18S>y&2)l(b0<U*aAj&iZd>Fm@F8TXz4@hsr8RCm3<UY@ +zHrbmXqu#(LB&h*_#R|Y_*`f;-UIQWyF8=0ltwiGLDrqzY&O;zh0;DKWBgq7PiY_+x +z<R`uXw@AaU6o_}yVq71UUkUITCio^hvkT&;FJ?*iVe(xBH@ab$vANo!v<8Pgs|GeH +zqUx}!W0!U($wG%tD>GaaB^6(;5{=~?TthtJV+fD1uys4qi>ufwADn}0DLax0I{Jc^ +zs{s9(h#WALTNi#lu^gyHxBnY0d#HP8B86@Z7{>+x^ubfh+pFooIM1(Fz<9`>p>c%F +zi14Y*2t7)(AedWN{+zSCq%TTh-GH<gO&4xXlha;vEU+&^ivX%P@j{*{vsbUpV~10H +zq{5Fs<25!;73J~w!R^o{jKfc-*~s|LKU7$;EYUL}OOqDLLkdRO^x_hsOX*NBF*SwU +z>dw4#aJj8SjZBa$ZqYT)4uHZXJ8-qe`jQ>nJNS*rmXjY{E4Mxnj8apfnY*1HEsxGh +z>G<Nmsq32U@_|(^>s#ICUR_uO#`dUw@OocKQ{x=rmypRNzNz|<gs-b&u37P$8heP} +zGe2}{t!BUHf_HR$vB(5k+Y>_Caj{XE!Q&xw$QeEfff<GNNbQ~&Z$Yr|Kff5udOTKI +zCXzTPHai+P0HKYxaPQH1$g3dNJ2R9%4TN7&2R|;*IMM$!;Obb~T=fPp6N(k;W-`}$ +zZJLg?BeqtF(;m53z2D5-tNT7hl2g|)3*stF@Ub3Vih+n4vpSZ-U~PskbP3<P5zzum +z=2dBjO5)v!`834q)vZiBKpd(rMh^!pmk(O=3GFtvo>2IFtO@gkI_y>aXq8zD@JFe_ +z<iaUUlfzi)d%v>n-kcNL7D!r5?4wo=SS)kZFeBJS=v%+PW>K4wptVBJ5mAMl6w&k> +z$7|4QPRfbhf+Bx@uu~l+!y!>$uuaDw^l=rhhB%fYn;xKpj`*qH^}qa4U&Hh33yPPd +zwVKdJp{`c>G6na*@D^dh`T#6=!Ut?dX~0qqtY-5T*y@SHct!Gq93i6u;yMIVHS6&T +zkH=GjWXQa&1ze{vBMD8_uv0aAltm+qT)T?NS0DGjy;yD&gSN&+T4ZSZeE$?Uk9NTw +zVJ$IQ>2)uTf}mQZ0cm|{W16QR6w(aX66WzoNf;n|l5E<Yzd<(wct(UoM9QF~ft50x +zDpLb-5&c7ySMp%tA@Em4wwI8mm<DE(uxBV~G)M})z1Wk|S7rkR%X)yar6?uaQ3$}@ +zqg81d2hjrRqTVT6nVm#tG_PT4=waE8IBSDCEmJf%^3utvN@FD{>Pxq3hKFUFmhps8 +z>v`<7r%|cGPu&FXyAQRbWCvZoq@w)CKdCCm%nG1Q5>bIX;!r<{l~39HI9@H$poCxL +zPNX$g9<mta3a<BQNImg5wwfWJB#<P-UoaiE9RC@LY41`B`lZ_*mVs~IET(jIM`gmo +z3VARW<wSq`e}C3AA7Rgp-=}O}h=AXTsTK?=87OZuOxX?>-y=tNzi~Kzqcb6)j~XRv +z5e$Q)J}bFH=O&C_&a6eczb7I$<+9gnXGn(?E&+_lGz|oQ$uBY;SY8bHXdKlC**4#m +zD$$Er>$z<NpuTM#wFY3k76Gr!k3Q+6UbQjTg6iKZ5lQ5-2BLT^ZS<d<+gmV=%6gK; +zPf9K~0^?7Q3mB=YQj+Hn7ocpCbMv7@y-rpe20q^X@iu<($vJ4jw1it+KNP-mkDZc_ +zMHBlC#FLKNx1W7%rV4u%UIwzeHQ@5!3)QTrEbTfIas52rW5eub-jj>F_08MHY;VmW +zN=H)9Q-b$4D15Ws!p~5QrDM5XqF#>lE*Io?cPO1cJvWAuf8MGCjXx+}e?-|>&J~1c +zw5=Ap{XXVssow5f?wkxRTJ~?&K8o<hs;7>~wlhE5!jETG>cM?*`%v7-voX}ZVRqD= +zxr2Kmj^h`9U1ihEr4e|%gq+1Pv@k)*hNpqC_x8^YHpKsWai0QxK!^w#n{+66R!A)^ +zXR$D{U+u6u5mbchJC$0tR(eGw4Vdlb4w@Ka-=oqdU|P3;um$#}j=<P7>HEM^9};3> +z3egDDwk<e-2X|v?PF&YMc!bG6x6S2oL-EoPD$)GN|E}bOhm43PaoM*EN<ak<0Cpb` +ztR_-&gWL9P9LMR$E@0bYaZmSy?C1_f)E`o|`^s5&Wrk4Z2_D+myNAN-g5co<R8RPg +z={t^`IghL!1AS>qo$5YSzF+kg`$?M8mor}2=f^-*reB_SQ+dUc77#MUZlZ<Gyo?Pa +zDL<~f3;OLV7G}>eB!_nd9R#&<bJy;x=}=j>KlZHIbC3`BV)Ggk|G8`X?}Gq*+Yx!I +z!USEujwWLf=$7I;mm%FtclWTOi5Kn|iIi5OU9>HF8L&F*c!A~A0Qo&ifWQ1RzXvbR +zyi!%h#&|USPP>ZJ<pwYJ#eaTW9PLW)-^=nvIPflXeGYecf@}S^ZhM=E+0pkoheM@n +z?2G37krQ0Phv4lq31(nopOS!pyB4Q?tvaSHkGuVw&Wc&x2<-AC{2t(K*zVRd5I^m^ +zAf0M(fQ89r--}q>-OIKUT)eV#qjV}~DVGD8owxu;G<a8TVYJ5o4}gQ`k`)Q$Z0Bgy +z1BC>%Z<l1uxdVj>n|dWtB2QgKXESm_0>~EeWhpU7DS0{Jx^qXO&3q=f+GXcQJCgpW +zFvLVetc8M#i=1?IywfUK`9r;I39*#r$J4R`F^X`i8TqkDC8z^ztxND6b3sy3bzzHE +z)BAiq+K&sMnuj_QT*DO*r)r*YObcjDX2{zBqOsozF)L(X_{tZ8l@jFaV+4pXfEegY +z0N5C9jAh`9r%7!RMJ-yU+Z)~Mi(VZ0jUj>~3PLnQ$z<`Ltp8ctE?W*RZLlhYZsI0v +zl3_N+KHw9*pnD%}<h-7oYrbj&k#G~bHwPGOz5&tB{rplpUSD)fo<Gq@z{Q^%J>+?L +zM8`DsWcT{Q9dHE<#gz2Y3tRB<>2L%8r@(-ZVZkgd^)%>!o9MkI>B{@!_m-Djs(o2@ +z)h$7N8xGb`Kfhbd$U)hVKiw+@v5ZFOqepxQC#qS#tPj&4c%KY<?O!hkQnx-%*6?+e +z-vgujne&4^PV&#uVD~bl0rjEgplqVy>}X!96PRTm>knW^5K_Z7OSs|wE3b)hP?+!= +zJoSTg0sV;o0lHC0k~CUNW{D$YjHz4{`ZVEKD$9+gQJG!?-Ruc9k&!uW#-wIJ(2Y%< +z>iWT7RXA}a8VL$X^JNAkfIgAb`&lb)Q^Ag4#3VP8f+WRhu_9RF-d*{(C)@9g-vtEK +zV!&s_30$ChB4{b?W+CWArA#dP1Yy867W9@y{N0Zs0id@)iWO79PPX7uPOz5cm4gS< +zCppV5HwfZaMbK+K{gdg)Q8-z|Xpm#r4h_fVR@Sm~YPSp~hm&d1a!`<6m-*H%YWTkn +zh?jC^n;W^pPocJ(^uA@OS@9qr(&nJg(tywxw7`=&OU(<F?lZTMZ1e_q_EOp;2oc+k +zAo@GKnFS6+aM_aO27=B6;C6pM7}c)K|6CS_IVi8@Lnk5x7b;Uh=cH-1*B&~<vb8V) +z)djq@;r-oTDMI90oz-<HddUH~FhnSHG;w8B1ES`8?bm-;`E|w%8bF2y%mPOCUz?sE +z>$ZEx!GcFZG$M^`D?hNL8%Ar(JXz9(2@>i&2Tnkb+m(UJ3+#0rK2>m(mwAHkF~GMO +zEK?Vq$md^_umj~<yBARkRlfQe%!ER>Wf$rc2fJ54P~ZT|I}ch4MR}{rJHAlk2YC@B +zR*<VzAu(_0)b_bg#(b~+u{YWZd$rC3@+z2F6gNT3SQU^{0R>2bOy4^5X`sN~F*^DM +zV0H!ltI?%)?h;{TrhoFtF(7Flp5|71<Fo|w%iej)v%T1J0^Pd>SB||Du0j7Bh&jh^ +za+I6QbSRcJ6xHwpeZszx4HVj`hRT=Z0IpY{d#swy0Kb1e>E}w*?bS1Q!*%%ig94zh +zSguImw9m^wGX@}|VavC#6I8Aiwwz_5tl5jswE!kEPKAIinyqw#`}-TKHDBx#@gj~0 +zT{{P4wsDcu5%~Wn!i(uR(z6X|k1rPyWm!ij!44bC!Ke6;U6L3p5Nq394<X4C`Yi*N +zYw^cm{Eqhb*WBoZQUXYW9Ftq3VeN9F6@POJN!^l>Wtjl(eOb(<l*K0`12%B>t(8u~ +zoqPy*BE+vf=KyFUH4)NL(8Xa$6we>+thx)9MK9G7un@{~(0xxMcEHmZbhw&#Yon5b +z62tH(m33k-sB_DPfv-VW9-8_$8l)wtUJ%#TS}8}WX4xnw0F`r9DJR&E6N#s;^;od~ +zeYyt;-KgLQ&Go=)W)9>y>*Rz5{|T2Y!y0NpPv;6}dw#}P9fsIl)g)N5ybK3Aa3&(a +z-NWD6LyX|FYG?@Sa-S`njD;VJVO=^n(!y!II9=9-{dfw$`kMCvk5G&(-%1iIF^mMd +zEFbU+Kb7U*V2@AoG@F~&Nb*T=Q#oVa&z?;+(vqp%tDG_4*GHzchm|FG);VMT?!2bj +zqwrK78hhNZ@1Z*H<ONgu-97AF?I0$)ifZ4oUJKRW<Zi5~@h&{{l(O5N-wLc$4L<{S +z6YF+}THQZs#0pW*8-5lOv_3JJ{`@Dj@rNL8_qnaJgid?k=ESj-HcqIXfL?tI2p>Xl +zYT#$FsCBl8bq3GR0u)E+a=wU$*8z<HMccY6POQy~oCuk^;C9KjToKRm*6Ywf<Zd`} +z+#tv4%%Auix5JvpVjl_OK$76(>~VXpl#|>kYLMgCN;nZyb`Swe8_^)G?A*_Z{f+7| +zY+nJ+t^oTZ@6WXJDO~+Uu>OzSAR(})c~E<8K$-v&6FfVyQ*A}PtfFzGbP)xxs0e$Y +z0kr<hjc+gKJ~du@e_NX1z<OHfaxk8esv<tdPx%K7*8%(zxjr%hhCYcc1`EFofrr1` +zad>~_wA6RDxEf;5H)gTZnNV5I^sZM@h7d%q#hO_TTRzQD-wAeDUEN9i&+{~~BIvbh +z--_eUIM4OYIPs#3>t6>#?&pF2X&Z2}RD~?p`Bi9^a0E`PgtBPbe3^#>NL<m(52i=L +z&Mlg-4FKOQzNSI6eGb*jaUg}$kD%$31ovpj&332gd=_d9^FFw*$#K*|uHBxeykOY{ +zwjJpJA+YTb^p_DFDaljN+kFDJhxSSufiD8sXy%JyU^Q3V5wGN+D=3EMXF@A{1o10R +z12N}8u85Uz1%@kO-Ef$s!Qh`+<wPhsOE5D)L<61kM{0Kmx3P#5ROi}SFBh=^S;qr> +zjYG&wg{Hbd4R&k(NJY!>fW`a~zZWxi<PT2Tgy_BBTrOcJ&=dFjCsn`%R$FS2n)|_B +zGs%<Vp#DcHcocGPp6A}8z}lR<OqRg;)F9vdS6Fz^xNhyYxYy>r)saWb?)!|U`eYoW +z(%Mfb_)4ghdCt#YkeQj7?{~&6jp0W8cztWz^bGc!EgP?ki$*Bo;0Mv2G2-U4Rp?fJ +zr2r|*$(^G3uHwXzQBBQ}^a<zDS?vTmYKo{b(tlA0Vi*Pu9^)Rx<OX7nBPzevUd+J4 +zwSBF9rw2=p3~NqlSI2d2^jl08e;Y+*fd57*?Cxq-$9>DF_KsxqM4V6DuUd6Xtku~f +zQ%$q_k%HDx$+r$~KOc|nVer3{j005K7awUVvGqh`Kl8=>M_4G0XQ^IdZgfb`U}jws +z`2S#&&1bc>G?Oo~pfzthOQpNXRo8lYRB1I<*L-7Z$nFiUTRL<Mq8QaIrX=iJZk~zJ +ztoGkGo*0FS3<bAWzYB^cRf@$;PSXF&Kk)?H6m;u#%*g101o)80$Zn4^W1Si?t1YsG +zitzK#7=hDQTb*|cnRgNmpe_-$XILr2+}~fE_#8+!os3E5sUCXp<Sj*8t8BfkbbHAp +zT4Y0*oiov3{C{bHp_G0LrhE&Sp?A~3oD{hV)}uREgu2#XV*Q^KT8M-g{PLQ*yyZRw +R{^bc+nO`#d_!siQ{{Rvp=p6t6 + +diff --git a/wrench/reftests/transforms/rotated-image.yaml b/wrench/reftests/transforms/rotated-image.yaml +index 23630c5f36320172a78eef141ae3e62da5306f67..5ab5d3b75705a46a3ee323541b5d846673ec8ffc 100644 +--- a/wrench/reftests/transforms/rotated-image.yaml ++++ b/wrench/reftests/transforms/rotated-image.yaml +@@ -44,7 +44,7 @@ root: + "backface-visible": true + type: "stacking-context" + "scroll-policy": scrollable +- transform: rotate-z(1) ++ transform: rotate-z(-1) + "transform-style": flat + items: + - +diff --git a/wrench/reftests/transforms/screen-space-blit-trivial.yaml b/wrench/reftests/transforms/screen-space-blit-trivial.yaml +index 47202568585519ca36eba12e6f63b9312b36b59b..76b2578faa564a5547288cc52769482e77b43d53 100644 +--- a/wrench/reftests/transforms/screen-space-blit-trivial.yaml ++++ b/wrench/reftests/transforms/screen-space-blit-trivial.yaml +@@ -10,13 +10,13 @@ root: + items: + - type: "stacking-context" + transform-origin: 235 235 +- transform: rotate-x(15) ++ transform: rotate-x(-15) + items: + - image: checkerboard(2, 16, 16) + bounds: [100, 100, 260, 260] + - type: "stacking-context" + transform-origin: 635 235 +- transform: rotate-z(45) ++ transform: rotate-z(-45) + items: + - image: checkerboard(2, 16, 16) + bounds: [500, 100, 260, 260] +diff --git a/wrench/reftests/transforms/screen-space-blit.png b/wrench/reftests/transforms/screen-space-blit.png +index b3cc0b05ab7fb7866bc97c1f73bb51f5d7940c7a..6479438fe1592a09b4429d755e64ceeee5c5b8e7 100644 +GIT binary patch +literal 73453 +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 + +literal 73335 +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⪼crgFMi37 +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 +S`+>9xe$0%l44>$`hWrnV>B(~d + +diff --git a/wrench/reftests/transforms/screen-space-blit.yaml b/wrench/reftests/transforms/screen-space-blit.yaml +index 5b5a914f376251512b821abf5912b2d4c829b7f7..0bae3a37361bc9adb52ac2f379874f13a1eb1fa7 100644 +--- a/wrench/reftests/transforms/screen-space-blit.yaml ++++ b/wrench/reftests/transforms/screen-space-blit.yaml +@@ -9,14 +9,14 @@ root: + items: + - type: "stacking-context" + transform-origin: 235 235 +- transform: rotate-x(15) ++ transform: rotate-x(-15) + filters: identity + items: + - image: checkerboard(2, 16, 16) + bounds: [100, 100, 260, 260] + - type: "stacking-context" + transform-origin: 635 235 +- transform: rotate-z(45) ++ transform: rotate-z(-45) + items: + - image: checkerboard(2, 16, 16) + bounds: [500, 100, 260, 260] +diff --git a/wrench/reftests/transforms/screen-space-blur.yaml b/wrench/reftests/transforms/screen-space-blur.yaml +index 14c791eb8dd47c497ebbe2dc5db7a9f55d8c50f2..e9c5f5d32dc4e8d1f8196173c1c19a011d4ec264 100644 +--- a/wrench/reftests/transforms/screen-space-blur.yaml ++++ b/wrench/reftests/transforms/screen-space-blur.yaml +@@ -6,14 +6,14 @@ root: + items: + - type: "stacking-context" + transform-origin: 235 235 +- transform: rotate-x(15) ++ transform: rotate-x(-15) + filters: blur(3) + items: + - image: checkerboard(2, 16, 16) + bounds: [100, 100, 260, 260] + - type: "stacking-context" + transform-origin: 635 235 +- transform: rotate-z(45) ++ transform: rotate-z(-45) + filters: blur(3) + items: + - image: checkerboard(2, 16, 16) +diff --git a/wrench/src/yaml_helper.rs b/wrench/src/yaml_helper.rs +index 1915b9f531184246a200e6ab1cc6870f6547af37..d82f863e9cce87be6273e353e92685743cd18ab6 100644 +--- a/wrench/src/yaml_helper.rs ++++ b/wrench/src/yaml_helper.rs +@@ -172,24 +172,24 @@ fn make_rotation( + axis_y: f32, + axis_z: f32, + ) -> LayoutTransform { +- let pre_transform = LayoutTransform::create_translation(origin.x, origin.y, 0.0); +- let post_transform = LayoutTransform::create_translation(-origin.x, -origin.y, -0.0); ++ let pre_transform = LayoutTransform::translation(-origin.x, -origin.y, -0.0); ++ let post_transform = LayoutTransform::translation(origin.x, origin.y, 0.0); + + let theta = 2.0f32 * f32::consts::PI - degrees.to_radians(); + let transform = + LayoutTransform::identity().pre_rotate(axis_x, axis_y, axis_z, Angle::radians(theta)); + +- pre_transform.pre_transform(&transform).pre_transform(&post_transform) ++ pre_transform.then(&transform).then(&post_transform) + } + + pub fn make_perspective( + origin: LayoutPoint, + perspective: f32, + ) -> LayoutTransform { +- let pre_transform = LayoutTransform::create_translation(origin.x, origin.y, 0.0); +- let post_transform = LayoutTransform::create_translation(-origin.x, -origin.y, -0.0); +- let transform = LayoutTransform::create_perspective(perspective); +- pre_transform.pre_transform(&transform).pre_transform(&post_transform) ++ let pre_transform = LayoutTransform::translation(-origin.x, -origin.y, -0.0); ++ let post_transform = LayoutTransform::translation(origin.x, origin.y, 0.0); ++ let transform = LayoutTransform::perspective(perspective); ++ pre_transform.then(&transform).then(&post_transform) + } + + // Create a skew matrix, specified in degrees. +@@ -199,7 +199,7 @@ fn make_skew( + ) -> LayoutTransform { + let alpha = Angle::radians(skew_x.to_radians()); + let beta = Angle::radians(skew_y.to_radians()); +- LayoutTransform::create_skew(alpha, beta) ++ LayoutTransform::skew(alpha, beta) + } + + impl YamlHelper for Yaml { +@@ -327,23 +327,11 @@ impl YamlHelper for Yaml { + fn as_matrix4d(&self) -> Option<LayoutTransform> { + if let Some(nums) = self.as_vec_f32() { + assert_eq!(nums.len(), 16, "expected 16 floats, got '{:?}'", self); +- Some(LayoutTransform::row_major( +- nums[0], +- nums[1], +- nums[2], +- nums[3], +- nums[4], +- nums[5], +- nums[6], +- nums[7], +- nums[8], +- nums[9], +- nums[10], +- nums[11], +- nums[12], +- nums[13], +- nums[14], +- nums[15], ++ Some(LayoutTransform::new( ++ nums[0], nums[1], nums[2], nums[3], ++ nums[4], nums[5], nums[6], nums[7], ++ nums[8], nums[9], nums[10], nums[11], ++ nums[12], nums[13], nums[14], nums[15], + )) + } else { + None +@@ -365,7 +353,7 @@ impl YamlHelper for Yaml { + let mx = match function { + "translate" if args.len() >= 2 => { + let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(0.); +- LayoutTransform::create_translation( ++ LayoutTransform::translation( + args[0].parse().unwrap(), + args[1].parse().unwrap(), + z, +@@ -386,16 +374,16 @@ impl YamlHelper for Yaml { + let y = args.get(1).and_then(|a| a.parse().ok()).unwrap_or(x); + // Default to no Z scale if unspecified. + let z = args.get(2).and_then(|a| a.parse().ok()).unwrap_or(1.0); +- LayoutTransform::create_scale(x, y, z) ++ LayoutTransform::scale(x, y, z) + } + "scale-x" if args.len() == 1 => { +- LayoutTransform::create_scale(args[0].parse().unwrap(), 1.0, 1.0) ++ LayoutTransform::scale(args[0].parse().unwrap(), 1.0, 1.0) + } + "scale-y" if args.len() == 1 => { +- LayoutTransform::create_scale(1.0, args[0].parse().unwrap(), 1.0) ++ LayoutTransform::scale(1.0, args[0].parse().unwrap(), 1.0) + } + "scale-z" if args.len() == 1 => { +- LayoutTransform::create_scale(1.0, 1.0, args[0].parse().unwrap()) ++ LayoutTransform::scale(1.0, 1.0, args[0].parse().unwrap()) + } + "skew" if args.len() >= 1 => { + // Default to no Y skew if unspecified. +@@ -409,14 +397,14 @@ impl YamlHelper for Yaml { + make_skew(0.0, args[0].parse().unwrap()) + } + "perspective" if args.len() == 1 => { +- LayoutTransform::create_perspective(args[0].parse().unwrap()) ++ LayoutTransform::perspective(args[0].parse().unwrap()) + } + _ => { + println!("unknown function {}", function); + break; + } + }; +- transform = transform.post_transform(&mx); ++ transform = transform.then(&mx); + } + Some(transform) + } +@@ -424,7 +412,7 @@ impl YamlHelper for Yaml { + let transform = array.iter().fold( + LayoutTransform::identity(), + |u, yaml| match yaml.as_transform(transform_origin) { +- Some(ref transform) => u.pre_transform(transform), ++ Some(ref transform) => transform.then(&u), + None => u, + }, + ); +-- +2.39.2 + diff --git a/third_party/webrender/patches/0006-Bump-procedural-masquerade-to-0.1.7.patch b/third_party/webrender/patches/0006-Bump-procedural-masquerade-to-0.1.7.patch new file mode 100644 index 00000000000..f8e985bbc03 --- /dev/null +++ b/third_party/webrender/patches/0006-Bump-procedural-masquerade-to-0.1.7.patch @@ -0,0 +1,53 @@ +From 415b9ba32648667313f6f4ce7965752285bf0b26 Mon Sep 17 00:00:00 2001 +From: Martin Robinson <mrobinson@igalia.com> +Date: Thu, 12 Jan 2023 12:35:31 +0100 +Subject: [PATCH 2/2] Bump procedural-masquerade to 0.1.7 + +This fixes a build issue in this branch. +--- + Cargo.lock | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/Cargo.lock b/Cargo.lock +index b6085604cae8e18de3273bcddac43fa0a7e1abd1..b7055733e57fcd0acff07881ef72369b560c2abe 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -417,7 +417,7 @@ version = "0.1.7" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "cstr-macros 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +- "procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ++ "procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] +@@ -425,7 +425,7 @@ name = "cstr-macros" + version = "0.1.6" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ +- "procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ++ "procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", + ] + +@@ -1212,7 +1212,7 @@ dependencies = [ + + [[package]] + name = "procedural-masquerade" +-version = "0.1.6" ++version = "0.1.7" + source = "registry+https://github.com/rust-lang/crates.io-index" + + [[package]] +@@ -2182,7 +2182,7 @@ dependencies = [ + "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" + "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" + "checksum proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +-"checksum procedural-masquerade 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9a1574a51c3fd37b26d2c0032b649d08a7d51d4cca9c41bbc5bf7118fa4509d0" ++"checksum procedural-masquerade 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8f1383dff4092fe903ac180e391a8d4121cc48f08ccf850614b0290c6673b69d" + "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" + "checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +-- +2.39.2 + diff --git a/third_party/webrender/patches/0007-Fix-warnings.patch b/third_party/webrender/patches/0007-Fix-warnings.patch new file mode 100644 index 00000000000..e56e63b2ce9 --- /dev/null +++ b/third_party/webrender/patches/0007-Fix-warnings.patch @@ -0,0 +1,601 @@ +diff --git a/direct-composition/src/lib.rs b/direct-composition/src/lib.rs +index fadb8f2b72..fa94b4b0e3 100644 +--- a/direct-composition/src/lib.rs ++++ b/direct-composition/src/lib.rs +@@ -4,9 +4,9 @@ + + #![cfg(windows)] + +-extern crate gleam; +-extern crate mozangle; +-extern crate winapi; ++use gleam; ++use mozangle; ++use winapi; + + use com::{ComPtr, CheckHResult, as_ptr}; + use std::ptr; +diff --git a/direct-composition/src/main_windows.rs b/direct-composition/src/main_windows.rs +index 18f1300a51..ff6608b4f0 100644 +--- a/direct-composition/src/main_windows.rs ++++ b/direct-composition/src/main_windows.rs +@@ -2,11 +2,11 @@ + * 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 direct_composition; +-extern crate euclid; +-extern crate gleam; +-extern crate webrender; +-extern crate winit; ++use direct_composition; ++use euclid; ++use gleam; ++use webrender; ++use winit; + + use euclid::size2; + use direct_composition::DirectComposition; +diff --git a/examples/alpha_perf.rs b/examples/alpha_perf.rs +index 56dc5b1abe..9200e65c8d 100644 +--- a/examples/alpha_perf.rs ++++ b/examples/alpha_perf.rs +@@ -2,11 +2,11 @@ + * 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 euclid; +-extern crate gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use euclid; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/animation.rs b/examples/animation.rs +index 612d891178..2e87e78b17 100644 +--- a/examples/animation.rs ++++ b/examples/animation.rs +@@ -10,11 +10,11 @@ + //! rounded cornered rectangle, which is done automatically during the + //! scene building for render optimization. + +-extern crate euclid; +-extern crate gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use euclid; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/basic.rs b/examples/basic.rs +index 79e0a87c46..0844994400 100644 +--- a/examples/basic.rs ++++ b/examples/basic.rs +@@ -2,11 +2,11 @@ + * 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 euclid; +-extern crate gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use euclid; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/blob.rs b/examples/blob.rs +index dd58b17f38..ea6536cea8 100644 +--- a/examples/blob.rs ++++ b/examples/blob.rs +@@ -2,11 +2,11 @@ + * 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 gleam; +-extern crate glutin; +-extern crate rayon; +-extern crate webrender; +-extern crate winit; ++use gleam; ++use glutin; ++use rayon; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/document.rs b/examples/document.rs +index 56f5eedc8f..e33eff4665 100644 +--- a/examples/document.rs ++++ b/examples/document.rs +@@ -2,11 +2,11 @@ + * 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 euclid; +-extern crate gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use euclid; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/frame_output.rs b/examples/frame_output.rs +index dc1c1d83f0..2cd612c9b4 100644 +--- a/examples/frame_output.rs ++++ b/examples/frame_output.rs +@@ -2,11 +2,11 @@ + * 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 euclid; +-extern crate gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use euclid; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/iframe.rs b/examples/iframe.rs +index 50e8b46f30..32c0b3a8fe 100644 +--- a/examples/iframe.rs ++++ b/examples/iframe.rs +@@ -2,10 +2,10 @@ + * 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 gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/image_resize.rs b/examples/image_resize.rs +index f45add1e7a..e28dd8e03d 100644 +--- a/examples/image_resize.rs ++++ b/examples/image_resize.rs +@@ -2,10 +2,10 @@ + * 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 gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/multiwindow.rs b/examples/multiwindow.rs +index 9b20960a94..2183b94144 100644 +--- a/examples/multiwindow.rs ++++ b/examples/multiwindow.rs +@@ -2,11 +2,11 @@ + * 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 euclid; +-extern crate gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use euclid; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + use gleam::gl; + use glutin::NotCurrent; +diff --git a/examples/scrolling.rs b/examples/scrolling.rs +index 34cd14304f..745c4f9bd3 100644 +--- a/examples/scrolling.rs ++++ b/examples/scrolling.rs +@@ -2,11 +2,11 @@ + * 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 euclid; +-extern crate gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use euclid; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/texture_cache_stress.rs b/examples/texture_cache_stress.rs +index d2e6818302..a065649b73 100644 +--- a/examples/texture_cache_stress.rs ++++ b/examples/texture_cache_stress.rs +@@ -2,10 +2,10 @@ + * 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 gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/examples/yuv.rs b/examples/yuv.rs +index a8f36b33d7..3943bd23a6 100644 +--- a/examples/yuv.rs ++++ b/examples/yuv.rs +@@ -2,10 +2,10 @@ + * 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 gleam; +-extern crate glutin; +-extern crate webrender; +-extern crate winit; ++use gleam; ++use glutin; ++use webrender; ++use winit; + + #[path = "common/boilerplate.rs"] + mod boilerplate; +diff --git a/glsl-to-cxx/src/lib.rs b/glsl-to-cxx/src/lib.rs +index e40418aaab..409af02d58 100644 +--- a/glsl-to-cxx/src/lib.rs ++++ b/glsl-to-cxx/src/lib.rs +@@ -2,7 +2,7 @@ + * 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; ++use glsl; + + mod hir; + +diff --git a/peek-poke/peek-poke-derive/src/lib.rs b/peek-poke/peek-poke-derive/src/lib.rs +index 7000f28bf1..92d538e516 100644 +--- a/peek-poke/peek-poke-derive/src/lib.rs ++++ b/peek-poke/peek-poke-derive/src/lib.rs +@@ -223,7 +223,7 @@ fn peek_poke_derive(mut s: Structure) -> TokenStream { + }; + + let poke_impl = s.gen_impl(quote! { +- extern crate peek_poke; ++ use peek_poke; + + gen unsafe impl peek_poke::Poke for @Self { + #max_size_fn +@@ -249,7 +249,7 @@ fn peek_poke_derive(mut s: Structure) -> TokenStream { + let peek_impl = quote! { + #[allow(non_upper_case_globals)] + const #dummy_const: () = { +- extern crate peek_poke; ++ use peek_poke; + + impl #impl_generics peek_poke::Peek for #name #ty_generics #where_clause { + #peek_from_fn +diff --git a/swgl/build.rs b/swgl/build.rs +index 1de3568aa0..5133e9f397 100644 +--- a/swgl/build.rs ++++ b/swgl/build.rs +@@ -2,9 +2,9 @@ + * 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; +-extern crate webrender_build; ++use cc; ++use glsl_to_cxx; ++use webrender_build; + + use std::collections::HashSet; + use std::fmt::Write; +diff --git a/swgl/src/lib.rs b/swgl/src/lib.rs +index e8fc030e0c..e19e85fd51 100644 +--- a/swgl/src/lib.rs ++++ b/swgl/src/lib.rs +@@ -5,7 +5,7 @@ + #![crate_name = "swgl"] + #![crate_type = "lib"] + +-extern crate gleam; ++use gleam; + + mod swgl_fns; + +diff --git a/webrender/build.rs b/webrender/build.rs +index 36a7f17a8e..caea4dd5c2 100644 +--- a/webrender/build.rs ++++ b/webrender/build.rs +@@ -2,7 +2,7 @@ + * 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 webrender_build; ++use webrender_build; + + use std::borrow::Cow; + use std::env; +diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs +index 6ad0e98eef..3eac572081 100644 +--- a/webrender/src/device/gl.rs ++++ b/webrender/src/device/gl.rs +@@ -373,7 +373,7 @@ impl<T> Drop for VBO<T> { + pub struct ExternalTexture { + id: gl::GLuint, + target: gl::GLuint, +- swizzle: Swizzle, ++ _swizzle: Swizzle, + uv_rect: TexelRect, + } + +@@ -387,7 +387,7 @@ impl ExternalTexture { + ExternalTexture { + id, + target: get_gl_target(target), +- swizzle, ++ _swizzle: swizzle, + uv_rect, + } + } +@@ -520,7 +520,7 @@ impl Texture { + let ext = ExternalTexture { + id: self.id, + target: self.target, +- swizzle: Swizzle::default(), ++ _swizzle: Swizzle::default(), + // TODO(gw): Support custom UV rect for external textures during captures + uv_rect: TexelRect::new( + 0.0, +diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs +index bae74efcfa..321afd2221 100644 +--- a/webrender/src/internal_types.rs ++++ b/webrender/src/internal_types.rs +@@ -568,13 +568,13 @@ pub enum ResultMsg { + + #[derive(Clone, Debug)] + pub struct ResourceCacheError { +- description: String, ++ _description: String, + } + + impl ResourceCacheError { + pub fn new(description: String) -> ResourceCacheError { + ResourceCacheError { +- description, ++ _description: description, + } + } + } +diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs +index 965f7dbc89..0db5feae9e 100644 +--- a/webrender/src/lib.rs ++++ b/webrender/src/lib.rs +@@ -72,8 +72,7 @@ extern crate serde; + #[macro_use] + extern crate tracy_rs; + +-extern crate malloc_size_of; +-extern crate svg_fmt; ++use malloc_size_of; + + #[macro_use] + mod profiler; +@@ -155,49 +154,34 @@ mod platform { + } + + #[cfg(target_os = "macos")] +-extern crate core_foundation; ++use core_foundation; + #[cfg(target_os = "macos")] +-extern crate core_graphics; ++use core_graphics; + #[cfg(target_os = "macos")] +-extern crate core_text; +- +-#[cfg(all(unix, not(target_os = "macos")))] +-extern crate freetype; +-#[cfg(all(unix, not(target_os = "macos")))] +-extern crate libc; ++use core_text; + + #[cfg(target_os = "windows")] +-extern crate dwrote; +- +-extern crate bincode; +-extern crate byteorder; +-pub extern crate euclid; +-extern crate fxhash; +-extern crate gleam; +-extern crate num_traits; +-extern crate plane_split; +-extern crate rayon; +-#[cfg(feature = "ron")] +-extern crate ron; ++use dwrote; ++ ++pub use euclid; + #[cfg(feature = "debugger")] +-extern crate serde_json; ++use serde_json; + #[macro_use] + extern crate smallvec; +-extern crate time; + #[cfg(feature = "debugger")] +-extern crate ws; ++use ws; + #[cfg(feature = "debugger")] +-extern crate image_loader; ++use image_loader; + #[cfg(feature = "debugger")] +-extern crate base64; ++use base64; + #[cfg(all(feature = "capture", feature = "png"))] +-extern crate png; ++use png; + #[cfg(test)] +-extern crate rand; ++use rand; + + #[macro_use] + pub extern crate api; +-extern crate webrender_build; ++use webrender_build; + + #[doc(hidden)] + pub use crate::composite::{CompositorConfig, Compositor, CompositorCapabilities}; +diff --git a/webrender/src/render_target.rs b/webrender/src/render_target.rs +index 9a3c953f42..9c62297c92 100644 +--- a/webrender/src/render_target.rs ++++ b/webrender/src/render_target.rs +@@ -1003,10 +1003,10 @@ fn add_svg_filter_instances( + let generic_int = match filter { + SvgFilterInfo::Blend(mode) => *mode as u16, + SvgFilterInfo::ComponentTransfer(data) => +- ((data.r_func.to_int() << 12 | ++ (data.r_func.to_int() << 12 | + data.g_func.to_int() << 8 | + data.b_func.to_int() << 4 | +- data.a_func.to_int()) as u16), ++ data.a_func.to_int()) as u16, + SvgFilterInfo::Composite(operator) => + operator.as_int() as u16, + SvgFilterInfo::LinearToSrgb | +diff --git a/webrender/tests/angle_shader_validation.rs b/webrender/tests/angle_shader_validation.rs +index 0a099d0b04..dda275dfda 100644 +--- a/webrender/tests/angle_shader_validation.rs ++++ b/webrender/tests/angle_shader_validation.rs +@@ -2,9 +2,9 @@ + * 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 mozangle; +-extern crate webrender; +-extern crate webrender_build; ++use mozangle; ++use webrender; ++use webrender_build; + + use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator}; + use webrender_build::shader::{ShaderFeatureFlags, ShaderVersion, build_shader_strings, get_shader_features}; +diff --git a/webrender_api/src/api.rs b/webrender_api/src/api.rs +index 3e8a99e921..94962ad712 100644 +--- a/webrender_api/src/api.rs ++++ b/webrender_api/src/api.rs +@@ -4,8 +4,6 @@ + + #![deny(missing_docs)] + +-extern crate serde_bytes; +- + use peek_poke::PeekPoke; + use std::cell::Cell; + use std::fmt; +diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs +index deaeb92aeb..4a664bddcf 100644 +--- a/webrender_api/src/image.rs ++++ b/webrender_api/src/image.rs +@@ -340,7 +340,7 @@ pub enum ImageData { + } + + mod serde_image_data_raw { +- extern crate serde_bytes; ++ use serde_bytes; + + use std::sync::Arc; + use serde::{Deserializer, Serializer}; +diff --git a/webrender_api/src/lib.rs b/webrender_api/src/lib.rs +index 5f274753e8..848f4740c9 100644 +--- a/webrender_api/src/lib.rs ++++ b/webrender_api/src/lib.rs +@@ -15,28 +15,23 @@ + #![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp, clippy::too_many_arguments))] + #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal, clippy::new_without_default))] + +-extern crate app_units; + #[macro_use] + extern crate bitflags; +-extern crate byteorder; + #[cfg(feature = "nightly")] +-extern crate core; ++use core; + #[cfg(target_os = "macos")] +-extern crate core_foundation; ++use core_foundation; + #[cfg(target_os = "macos")] +-extern crate core_graphics; ++use core_graphics; + #[macro_use] + extern crate derive_more; +-pub extern crate euclid; ++pub use euclid; + #[macro_use] + extern crate malloc_size_of_derive; +-extern crate serde; + #[macro_use] + extern crate serde_derive; +-extern crate time; + +-extern crate malloc_size_of; +-extern crate peek_poke; ++use malloc_size_of; + + mod api; + pub mod channel; +diff --git a/wr_malloc_size_of/lib.rs b/wr_malloc_size_of/lib.rs +index 49a9666342..2a34508974 100644 +--- a/wr_malloc_size_of/lib.rs ++++ b/wr_malloc_size_of/lib.rs +@@ -10,8 +10,8 @@ + + //! A reduced fork of Firefox's malloc_size_of crate, for bundling with WebRender. + +-extern crate app_units; +-extern crate euclid; ++use app_units; ++use euclid; + + use std::hash::{BuildHasher, Hash}; + use std::mem::size_of; diff --git a/third_party/webrender/patches/001-Restore-hit-testing-api.diff b/third_party/webrender/patches/001-Restore-hit-testing-api.diff deleted file mode 100644 index 0a51a9a70ea..00000000000 --- a/third_party/webrender/patches/001-Restore-hit-testing-api.diff +++ /dev/null @@ -1,250 +0,0 @@ -diff --git a/webrender/src/hit_test.rs b/webrender/src/hit_test.rs -index 4a73e2158d..e095d8db0c 100644 ---- a/webrender/src/hit_test.rs -+++ b/webrender/src/hit_test.rs -@@ -2,13 +2,14 @@ - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - --use api::{BorderRadius, ClipMode, HitTestItem, HitTestResult, ItemTag, PrimitiveFlags}; -+use api::{BorderRadius, ClipMode, HitTestItem, HitTestResult, ItemTag, PrimitiveFlags, HitTestFlags}; - use api::{PipelineId, ApiHitTester, ClipId}; - use api::units::*; - use crate::clip::{ClipItemKind, ClipStore, ClipNode, rounded_rectangle_contains_point}; - use crate::clip::{polygon_contains_point}; - use crate::prim_store::PolygonKey; - use crate::scene_builder_thread::Interners; -+use crate::spatial_node::SpatialNodeType; - use crate::spatial_tree::{SpatialNodeIndex, SpatialTree}; - use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo}; - use std::ops; -@@ -45,8 +46,9 @@ impl ApiHitTester for SharedHitTester { - fn hit_test(&self, - pipeline_id: Option<PipelineId>, - point: WorldPoint, -+ flags: HitTestFlags, - ) -> HitTestResult { -- self.get_ref().hit_test(HitTest::new(pipeline_id, point)) -+ self.get_ref().hit_test(HitTest::new(pipeline_id, point, flags)) - } - } - -@@ -355,6 +357,7 @@ impl HitTester { - self.spatial_nodes.clear(); - - self.spatial_nodes.reserve(spatial_tree.spatial_nodes.len()); -+ self.pipeline_root_nodes.clear(); - for (index, node) in spatial_tree.spatial_nodes.iter().enumerate() { - let index = SpatialNodeIndex::new(index); - -@@ -380,6 +383,8 @@ impl HitTester { - } - - pub fn hit_test(&self, test: HitTest) -> HitTestResult { -+ let point = test.get_absolute_point(self); -+ - let mut result = HitTestResult::default(); - - let mut current_spatial_node_index = SpatialNodeIndex::INVALID; -@@ -402,7 +407,7 @@ impl HitTester { - point_in_layer = scroll_node - .world_content_transform - .inverse() -- .and_then(|inverted| inverted.transform_point2d(test.point)); -+ .and_then(|inverted| inverted.transform_point2d(point)); - current_spatial_node_index = item.spatial_node_index; - } - -@@ -426,7 +431,7 @@ impl HitTester { - .world_content_transform; - let transformed_point = match transform - .inverse() -- .and_then(|inverted| inverted.transform_point2d(test.point)) -+ .and_then(|inverted| inverted.transform_point2d(point)) - { - Some(point) => point, - None => { -@@ -457,7 +462,7 @@ impl HitTester { - point_in_viewport = root_node - .world_viewport_transform - .inverse() -- .and_then(|inverted| inverted.transform_point2d(test.point)) -+ .and_then(|inverted| inverted.transform_point2d(point)) - .map(|pt| pt - scroll_node.external_scroll_offset); - - current_root_spatial_node_index = root_spatial_node_index; -@@ -470,6 +475,10 @@ impl HitTester { - point_in_viewport, - point_relative_to_item: point_in_layer - item.rect.origin.to_vector(), - }); -+ -+ if !test.flags.contains(HitTestFlags::FIND_ALL) { -+ return result; -+ } - } - } - } -@@ -477,24 +486,51 @@ impl HitTester { - result.items.dedup(); - result - } -+ -+ fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode { -+ &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0 as usize] -+ } -+ - } - - #[derive(MallocSizeOf)] - pub struct HitTest { - pipeline_id: Option<PipelineId>, - point: WorldPoint, -+ flags: HitTestFlags, - } - - impl HitTest { - pub fn new( - pipeline_id: Option<PipelineId>, - point: WorldPoint, -+ flags: HitTestFlags, - ) -> HitTest { - HitTest { - pipeline_id, - point, -+ flags - } - } -+ -+ fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint { -+ if !self.flags.contains(HitTestFlags::POINT_RELATIVE_TO_PIPELINE_VIEWPORT) { -+ return self.point; -+ } -+ -+ let point = LayoutPoint::new(self.point.x, self.point.y); -+ self.pipeline_id -+ .and_then(|id| -+ hit_tester -+ .get_pipeline_root(id) -+ .world_viewport_transform -+ .transform_point2d(point) -+ ) -+ .unwrap_or_else(|| { -+ WorldPoint::new(self.point.x, self.point.y) -+ }) -+ } -+ - } - - /// Collect clips for a given ClipId, convert and add them to the hit testing -diff --git a/webrender/src/render_api.rs b/webrender/src/render_api.rs -index b84fa2c63a..b2c8a64e88 100644 ---- a/webrender/src/render_api.rs -+++ b/webrender/src/render_api.rs -@@ -10,6 +10,7 @@ use std::marker::PhantomData; - use std::path::PathBuf; - use std::sync::Arc; - use std::u32; -+use api::HitTestFlags; - use time::precise_time_ns; - //use crate::api::peek_poke::PeekPoke; - use crate::api::channel::{Sender, single_msg_channel, unbounded_channel}; -@@ -812,7 +813,7 @@ pub enum FrameMsg { - /// - UpdateEpoch(PipelineId, Epoch), - /// -- HitTest(Option<PipelineId>, WorldPoint, Sender<HitTestResult>), -+ HitTest(Option<PipelineId>, WorldPoint, HitTestFlags, Sender<HitTestResult>), - /// - RequestHitTester(Sender<Arc<dyn ApiHitTester>>), - /// -@@ -1297,12 +1298,13 @@ impl RenderApi { - document_id: DocumentId, - pipeline_id: Option<PipelineId>, - point: WorldPoint, -+ flags: HitTestFlags, - ) -> HitTestResult { - let (tx, rx) = single_msg_channel(); - - self.send_frame_msg( - document_id, -- FrameMsg::HitTest(pipeline_id, point, tx) -+ FrameMsg::HitTest(pipeline_id, point, flags, tx) - ); - rx.recv().unwrap() - } -diff --git a/webrender/src/render_backend.rs b/webrender/src/render_backend.rs -index 96bc600484..825e981b5c 100644 ---- a/webrender/src/render_backend.rs -+++ b/webrender/src/render_backend.rs -@@ -535,14 +535,14 @@ impl Document { - FrameMsg::UpdateEpoch(pipeline_id, epoch) => { - self.scene.pipeline_epochs.insert(pipeline_id, epoch); - } -- FrameMsg::HitTest(pipeline_id, point, tx) => { -+ FrameMsg::HitTest(pipeline_id, point, flags, tx) => { - if !self.hit_tester_is_valid { - self.rebuild_hit_tester(); - } - - let result = match self.hit_tester { - Some(ref hit_tester) => { -- hit_tester.hit_test(HitTest::new(pipeline_id, point)) -+ hit_tester.hit_test(HitTest::new(pipeline_id, point, flags)) - } - None => HitTestResult { items: Vec::new() }, - }; -diff --git a/webrender_api/src/lib.rs b/webrender_api/src/lib.rs -index 7dc887ade9..b0446c1d0b 100644 ---- a/webrender_api/src/lib.rs -+++ b/webrender_api/src/lib.rs -@@ -278,7 +278,7 @@ pub trait ApiHitTester: Send + Sync { - /// hit results so that only items inside that pipeline are matched. The vector - /// of hit results will contain all display items that match, ordered from - /// front to back. -- fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint) -> HitTestResult; -+ fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint, flags: HitTestFlags) -> HitTestResult; - } - - /// A hit tester requested to the render backend thread but not necessarily ready yet. -@@ -322,6 +322,17 @@ pub struct HitTestResult { - pub items: Vec<HitTestItem>, - } - -+bitflags! { -+ #[derive(Deserialize, MallocSizeOf, Serialize)] -+ /// -+ pub struct HitTestFlags: u8 { -+ /// -+ const FIND_ALL = 0b00000001; -+ /// -+ const POINT_RELATIVE_TO_PIPELINE_VIEWPORT = 0b00000010; -+ } -+} -+ - impl Drop for NotificationRequest { - fn drop(&mut self) { - if let Some(ref mut handler) = self.handler { -diff --git a/wrench/src/main.rs b/wrench/src/main.rs -index 7dc037ebb2..843512f595 100644 ---- a/wrench/src/main.rs -+++ b/wrench/src/main.rs -@@ -965,6 +965,7 @@ fn render<'a>( - wrench.document_id, - None, - cursor_position, -+ HitTestFlags::empty(), - ); - - println!("Hit test results:"); -diff --git a/wrench/src/rawtest.rs b/wrench/src/rawtest.rs -index 580f1cb015..b86b583617 100644 ---- a/wrench/src/rawtest.rs -+++ b/wrench/src/rawtest.rs -@@ -1393,6 +1393,7 @@ impl<'a> RawtestHarness<'a> { - self.wrench.document_id, - None, - point, -+ HitTetFlags::empty(), - ) - }; - diff --git a/third_party/webrender/patches/002-Upgrade-version-of-gleam.diff b/third_party/webrender/patches/002-Upgrade-version-of-gleam.diff deleted file mode 100644 index 288a1b57457..00000000000 --- a/third_party/webrender/patches/002-Upgrade-version-of-gleam.diff +++ /dev/null @@ -1,62 +0,0 @@ -diff --git a/example-compositor/compositor/Cargo.toml b/example-compositor/compositor/Cargo.toml -index 4202332c41..02c6ebe0ce 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.13.1" -+gleam = "0.15" - - [target.'cfg(windows)'.dependencies] - compositor-windows = { path = "../compositor-windows" } -diff --git a/examples/Cargo.toml b/examples/Cargo.toml -index 556b67d1ed..f1a4718b04 100644 ---- a/examples/Cargo.toml -+++ b/examples/Cargo.toml -@@ -57,7 +57,7 @@ debug = ["webrender/capture", "webrender/profiler"] - app_units = "0.7" - env_logger = "0.5" - euclid = "0.22" --gleam = "0.13" -+gleam = "0.15" - glutin = "0.21" - rayon = "1" - webrender = { path = "../webrender" } -diff --git a/swgl/Cargo.toml b/swgl/Cargo.toml -index 9b7624b13e..2e84e2267c 100644 ---- a/swgl/Cargo.toml -+++ b/swgl/Cargo.toml -@@ -12,4 +12,4 @@ glsl-to-cxx = { path = "../glsl-to-cxx" } - webrender_build = { path = "../webrender_build" } - - [dependencies] --gleam = "0.13.1" -+gleam = "0.15" -diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml -index 40064a1573..e82ac85718 100644 ---- a/webrender/Cargo.toml -+++ b/webrender/Cargo.toml -@@ -33,7 +33,7 @@ byteorder = "1.0" - cstr = "0.2" - euclid = { version = "0.22.0", features = ["serde"] } - fxhash = "0.2.1" --gleam = "0.13.1" -+gleam = "0.15" - lazy_static = "1" - log = "0.4" - malloc_size_of_derive = "0.1" -diff --git a/wrench/Cargo.toml b/wrench/Cargo.toml -index 7d2345be43..f51e4202ff 100644 ---- a/wrench/Cargo.toml -+++ b/wrench/Cargo.toml -@@ -9,7 +9,7 @@ edition = "2018" - [dependencies] - base64 = "0.12" - env_logger = { version = "0.5", optional = true } --gleam = "0.13" -+gleam = "0.15" - glutin = "0.21" - clap = { version = "2", features = ["yaml"] } - log = "0.4" diff --git a/third_party/webrender/patches/003-Fix-WebRender-warnings.diff b/third_party/webrender/patches/003-Fix-WebRender-warnings.diff deleted file mode 100644 index 835174ed538..00000000000 --- a/third_party/webrender/patches/003-Fix-WebRender-warnings.diff +++ /dev/null @@ -1,221 +0,0 @@ -diff --git a/webrender/build.rs b/webrender/build.rs -index 60b4a96c23..adc5dbf2e8 100644 ---- a/webrender/build.rs -+++ b/webrender/build.rs -@@ -2,8 +2,6 @@ - * 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 webrender_build; -- - use std::borrow::Cow; - use std::env; - use std::fs::{canonicalize, read_dir, File}; -diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs -index eb272e56d6..5e0c050378 100644 ---- a/webrender/src/device/gl.rs -+++ b/webrender/src/device/gl.rs -@@ -386,6 +386,7 @@ impl<T> Drop for VBO<T> { - pub struct ExternalTexture { - id: gl::GLuint, - target: gl::GLuint, -+ #[allow(dead_code)] - swizzle: Swizzle, - uv_rect: TexelRect, - } -diff --git a/webrender/src/hit_test.rs b/webrender/src/hit_test.rs -index e095d8db0c..0bd02cd426 100644 ---- a/webrender/src/hit_test.rs -+++ b/webrender/src/hit_test.rs -@@ -6,10 +6,9 @@ use api::{BorderRadius, ClipMode, HitTestItem, HitTestResult, ItemTag, Primitive - use api::{PipelineId, ApiHitTester, ClipId}; - use api::units::*; - use crate::clip::{ClipItemKind, ClipStore, ClipNode, rounded_rectangle_contains_point}; --use crate::clip::{polygon_contains_point}; -+use crate::clip::polygon_contains_point; - use crate::prim_store::PolygonKey; - use crate::scene_builder_thread::Interners; --use crate::spatial_node::SpatialNodeType; - use crate::spatial_tree::{SpatialNodeIndex, SpatialTree}; - use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo}; - use std::ops; -diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs -index 07f937c6a0..9819850db0 100644 ---- a/webrender/src/internal_types.rs -+++ b/webrender/src/internal_types.rs -@@ -536,6 +536,7 @@ pub enum ResultMsg { - - #[derive(Clone, Debug)] - pub struct ResourceCacheError { -+ #[allow(dead_code)] - description: String, - } - -diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs -index 34bc9fb49b..cc200e30eb 100644 ---- a/webrender/src/lib.rs -+++ b/webrender/src/lib.rs -@@ -71,8 +71,7 @@ extern crate serde; - extern crate tracy_rs; - #[macro_use] - extern crate derive_more; --extern crate malloc_size_of; --extern crate svg_fmt; -+use malloc_size_of; - - #[macro_use] - mod profiler; -@@ -163,41 +162,14 @@ mod platform { - } - } - --#[cfg(target_os = "macos")] --extern crate core_foundation; --#[cfg(target_os = "macos")] --extern crate core_graphics; --#[cfg(target_os = "macos")] --extern crate core_text; -- --#[cfg(all(unix, not(target_os = "macos")))] --extern crate freetype; --#[cfg(all(unix, not(target_os = "macos")))] --extern crate libc; -- --#[cfg(target_os = "windows")] --extern crate dwrote; -- --extern crate bincode; --extern crate byteorder; --pub extern crate euclid; --extern crate fxhash; --extern crate gleam; --extern crate num_traits; --extern crate plane_split; --extern crate rayon; --#[cfg(feature = "ron")] --extern crate ron; -+pub use euclid; - #[macro_use] - extern crate smallvec; --extern crate time; --#[cfg(all(feature = "capture", feature = "png"))] --extern crate png; - #[cfg(test)] --extern crate rand; -+use rand; - --pub extern crate api; --extern crate webrender_build; -+pub use api; -+use webrender_build; - - #[doc(hidden)] - pub use crate::composite::{CompositorConfig, Compositor, CompositorCapabilities, CompositorSurfaceTransform}; -diff --git a/webrender/src/profiler.rs b/webrender/src/profiler.rs -index 40c091d617..702fa634ff 100644 ---- a/webrender/src/profiler.rs -+++ b/webrender/src/profiler.rs -@@ -1362,6 +1362,7 @@ pub struct Counter { - change_indicator: u8, - - /// Only used to check that the constants match the real index. -+ #[allow(dead_code)] - index: usize, - - graph: Option<Graph>, -diff --git a/webrender/src/render_target.rs b/webrender/src/render_target.rs -index d31176047b..301ce4ec4d 100644 ---- a/webrender/src/render_target.rs -+++ b/webrender/src/render_target.rs -@@ -10,7 +10,7 @@ use crate::batch::{ClipBatcher, BatchBuilder}; - use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX}; - use crate::clip::ClipStore; - use crate::composite::CompositeState; --use crate::frame_builder::{FrameGlobalResources}; -+use crate::frame_builder::FrameGlobalResources; - use crate::gpu_cache::{GpuCache, GpuCacheAddress}; - use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance}; - use crate::gpu_types::{TransformPalette, ZBufferIdGenerator}; -@@ -819,10 +819,10 @@ fn add_svg_filter_instances( - let generic_int = match filter { - SvgFilterInfo::Blend(mode) => *mode as u16, - SvgFilterInfo::ComponentTransfer(data) => -- ((data.r_func.to_int() << 12 | -+ (data.r_func.to_int() << 12 | - data.g_func.to_int() << 8 | - data.b_func.to_int() << 4 | -- data.a_func.to_int()) as u16), -+ data.a_func.to_int()) as u16, - SvgFilterInfo::Composite(operator) => - operator.as_int() as u16, - SvgFilterInfo::LinearToSrgb | -diff --git a/webrender_api/src/image.rs b/webrender_api/src/image.rs -index 238d004814..23c660b647 100644 ---- a/webrender_api/src/image.rs -+++ b/webrender_api/src/image.rs -@@ -326,10 +326,9 @@ pub enum ImageData { - } - - mod serde_image_data_raw { -- extern crate serde_bytes; -- -- use std::sync::Arc; - use serde::{Deserializer, Serializer}; -+ use serde_bytes; -+ use std::sync::Arc; - - pub fn serialize<S: Serializer>(bytes: &Arc<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error> { - serde_bytes::serialize(bytes.as_slice(), serializer) -diff --git a/webrender_api/src/lib.rs b/webrender_api/src/lib.rs -index b0446c1d0b..f5cda1fbf6 100644 ---- a/webrender_api/src/lib.rs -+++ b/webrender_api/src/lib.rs -@@ -15,29 +15,20 @@ - #![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp, clippy::too_many_arguments))] - #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal, clippy::new_without_default))] - --pub extern crate crossbeam_channel; --pub extern crate euclid; -+pub use crossbeam_channel; -+pub use euclid; - --extern crate app_units; - #[macro_use] - extern crate bitflags; --extern crate byteorder; - #[cfg(feature = "nightly")] - extern crate core; --#[cfg(target_os = "macos")] --extern crate core_foundation; --#[cfg(target_os = "macos")] --extern crate core_graphics; --extern crate derive_more; - #[macro_use] - extern crate malloc_size_of_derive; --extern crate serde; - #[macro_use] - extern crate serde_derive; --extern crate time; - --extern crate malloc_size_of; --extern crate peek_poke; -+use malloc_size_of; -+use peek_poke; - - pub mod channel; - mod color; -diff --git a/wr_malloc_size_of/lib.rs b/wr_malloc_size_of/lib.rs -index 38b049095e..abd982ffe8 100644 ---- a/wr_malloc_size_of/lib.rs -+++ b/wr_malloc_size_of/lib.rs -@@ -10,8 +10,8 @@ - - //! A reduced fork of Firefox's malloc_size_of crate, for bundling with WebRender. - --extern crate app_units; --extern crate euclid; -+use app_units; -+use euclid; - - use std::hash::{BuildHasher, Hash}; - use std::mem::size_of; diff --git a/third_party/webrender/patches/head b/third_party/webrender/patches/head index d85686ccf42..1e8d57b32a9 100644 --- a/third_party/webrender/patches/head +++ b/third_party/webrender/patches/head @@ -1 +1 @@ -e491e1ae637b2eed1e7195855d88357e5eb3ddf9 +1175acad2d4f49fa712e105c84149ac7f394261d diff --git a/third_party/webrender/patches/series b/third_party/webrender/patches/series index ea98b9a1f26..e53e49c34d6 100644 --- a/third_party/webrender/patches/series +++ b/third_party/webrender/patches/series @@ -1,3 +1,7 @@ -001-Restore-hit-testing-api.diff -002-Upgrade-version-of-gleam.diff -003-Fix-WebRender-warnings.diff +0001-Add-signal-handler-to-catch-segfault-in-build-script.patch +0002-Bug-1646741-Update-gleam-to-0.12.-r-kvark.patch +0003-Bug-1651889.-Update-to-gleam-0.12.1.-r-kvark.patch +0004-Bug-1654699.-Update-core-foundation-core-graphics.-r.patch +0005-Bug-1656236-Update-to-euclid-0.22.-r-kvark.patch +0006-Bump-procedural-masquerade-to-0.1.7.patch +0007-Fix-warnings.patch diff --git a/third_party/webrender/peek-poke/peek-poke-derive/src/lib.rs b/third_party/webrender/peek-poke/peek-poke-derive/src/lib.rs index ac20b2b922b..92d538e5164 100644 --- a/third_party/webrender/peek-poke/peek-poke-derive/src/lib.rs +++ b/third_party/webrender/peek-poke/peek-poke-derive/src/lib.rs @@ -43,7 +43,10 @@ fn get_discriminant_size_type(len: usize) -> TokenStream { fn is_struct(s: &Structure) -> bool { // a single variant with no prefix is 'struct' - matches!(&s.variants()[..], [v] if v.prefix.is_none()) + match &s.variants()[..] { + [v] if v.prefix.is_none() => true, + _ => false, + } } fn derive_max_size(s: &Structure) -> TokenStream { @@ -220,7 +223,7 @@ fn peek_poke_derive(mut s: Structure) -> TokenStream { }; let poke_impl = s.gen_impl(quote! { - extern crate peek_poke; + use peek_poke; gen unsafe impl peek_poke::Poke for @Self { #max_size_fn @@ -246,7 +249,7 @@ fn peek_poke_derive(mut s: Structure) -> TokenStream { let peek_impl = quote! { #[allow(non_upper_case_globals)] const #dummy_const: () = { - extern crate peek_poke; + use peek_poke; impl #impl_generics peek_poke::Peek for #name #ty_generics #where_clause { #peek_from_fn diff --git a/third_party/webrender/peek-poke/src/euclid.rs b/third_party/webrender/peek-poke/src/euclid.rs index dc4dd7774b4..44b0ed27e73 100644 --- a/third_party/webrender/peek-poke/src/euclid.rs +++ b/third_party/webrender/peek-poke/src/euclid.rs @@ -9,7 +9,7 @@ // except according to those terms. use crate::{Peek, Poke}; -use euclid::{Point2D, Rect, Box2D, SideOffsets2D, Size2D, Transform3D, Vector2D}; +use euclid::{Point2D, Rect, SideOffsets2D, Size2D, Transform3D, Vector2D}; unsafe impl<T: Poke, U> Poke for Point2D<T, U> { #[inline(always)] @@ -53,27 +53,6 @@ impl<T: Peek, U> Peek for Rect<T, U> { } } -unsafe impl<T: Poke, U> Poke for Box2D<T, U> { - #[inline(always)] - fn max_size() -> usize { - Point2D::<T, U>::max_size() * 2 - } - #[inline(always)] - unsafe fn poke_into(&self, bytes: *mut u8) -> *mut u8 { - let bytes = self.min.poke_into(bytes); - let bytes = self.max.poke_into(bytes); - bytes - } -} -impl<T: Peek, U> Peek for Box2D<T, U> { - #[inline(always)] - unsafe fn peek_from(bytes: *const u8, output: *mut Self) -> *const u8 { - let bytes = Point2D::<T, U>::peek_from(bytes, &mut (*output).min); - let bytes = Point2D::<T, U>::peek_from(bytes, &mut (*output).max); - bytes - } -} - unsafe impl<T: Poke, U> Poke for SideOffsets2D<T, U> { #[inline(always)] fn max_size() -> usize { diff --git a/third_party/webrender/peek-poke/src/lib.rs b/third_party/webrender/peek-poke/src/lib.rs index 034799c594d..55864599bbd 100644 --- a/third_party/webrender/peek-poke/src/lib.rs +++ b/third_party/webrender/peek-poke/src/lib.rs @@ -113,20 +113,6 @@ pub fn ensure_red_zone<T: Poke>(bytes: &mut Vec<u8>) { } } -/// Remove the "red zone" (padding of zeroes) from the end of the vec of `bytes`. -/// This is effectively the inverse of `ensure_red_zone`, with the caveat that -/// space reserved for the red zone is not un-reserved. Callers are repsonsible -/// for making sure the vec actually has a red zone, otherwise data bytes can -/// get stripped instead. -pub fn strip_red_zone<T: Poke>(bytes: &mut Vec<u8>) { - assert!(bytes.len() >= T::max_size()); - unsafe { - let end_ptr = bytes.as_end_mut_ptr(); - end_ptr.write_bytes(0, T::max_size()); - bytes.set_end_ptr(end_ptr.sub(T::max_size())); - } -} - #[inline] unsafe fn read_verbatim<T>(src: *const u8, dst: *mut T) -> *const u8 { *dst = (src as *const T).read_unaligned(); diff --git a/third_party/webrender/servo-tidy.toml b/third_party/webrender/servo-tidy.toml index bee447cba64..15c2b7bae39 100644 --- a/third_party/webrender/servo-tidy.toml +++ b/third_party/webrender/servo-tidy.toml @@ -6,17 +6,18 @@ check-alphabetical-order = false [ignore] # Ignored packages with duplicated versions packages = [ - "cfg-if", "core-foundation", "core-foundation-sys", "core-graphics", "gl_generator", "gleam", + "rand", "rand_core", # https://github.com/trimental/andrew/issues/5 "rusttype", # https://bugzilla.mozilla.org/show_bug.cgi?id=1615148 "smallvec", + "winapi", "yaml-rust", # These are tracked in bug 1587468, see there for pending work. "proc-macro2", diff --git a/third_party/webrender/swgl/Cargo.toml b/third_party/webrender/swgl/Cargo.toml index 2e84e2267c1..bc5a04b0a34 100644 --- a/third_party/webrender/swgl/Cargo.toml +++ b/third_party/webrender/swgl/Cargo.toml @@ -12,4 +12,4 @@ glsl-to-cxx = { path = "../glsl-to-cxx" } webrender_build = { path = "../webrender_build" } [dependencies] -gleam = "0.15" +gleam = "0.12.0" diff --git a/third_party/webrender/swgl/README.md b/third_party/webrender/swgl/README.md index 2c43ed88195..158a4157930 100644 --- a/third_party/webrender/swgl/README.md +++ b/third_party/webrender/swgl/README.md @@ -1,220 +1,4 @@ -# swgl +swgl +======== Software OpenGL implementation for WebRender - -## Overview -This is a relatively simple single threaded software rasterizer designed -for use by WebRender. It will shade one quad at a time using a 4xf32 vector -with one vertex per lane. It rasterizes quads usings spans and shades that -span 4 pixels at a time. - -## Building -clang-cl is required to build on Windows. This can be done by installing -the llvm binaries from https://releases.llvm.org/ and adding the installation -to the path with something like `set PATH=%PATH%;C:\Program Files\LLVM\bin`. -Then `set CC=clang-cl` and `set CXX=clang-cl`. That should be sufficient -for `cc-rs` to use `clang-cl` instead of `cl`. - -## Extensions -SWGL contains a number of OpenGL and GLSL extensions designed to both ease -integration with WebRender and to help accelerate span rasterization. - -GLSL extension intrinsics are generally prefixed with `swgl_` to distinguish -them from other items in the GLSL namespace. - -Inside GLSL, the `SWGL` preprocessor token is defined so that usage of SWGL -extensions may be conditionally compiled. - -``` -void swgl_drawSpanRGBA8(); -void swgl_drawSpanR8(); - -int swgl_SpanLength; -int swgl_StepSize; - -mixed swgl_interpStep(mixed varying_input); -void swgl_stepInterp(); -``` - -SWGL's default fragment processing calls a fragment shader's `main` function -on groups of fragments in units of `swgl_StepSize`. On return, the value of -gl_FragColor is read, packed to an appropriate pixel format, and sent to the -blend stage for output to the destination framebuffer. This can be inefficient -for some types of fragment shaders, such as those that must lookup from a -texture and immediately output it, unpacking the texels only to subsequently -repack them at cost. Also, various per-fragment conditions in the shader might -need to be repeatedly checked, even though they are actually constant over -the entire primitive. - -To work around this inefficiency, SWGL allows fragments to optionally be -processed over entire spans. This can both side-step the packing inefficiency -as well as more efficiently deal with conditions that remain constant over an -entire span. SWGL also introduces various specialized intrinsics for more -efficiently dealing with certain types of primitive spans with optimal -fixed-function processing. - -Inside a fragment shader, a `swgl_drawSpan` function may be defined to override -the normal fragment processing for that fragment shader. The function must then -call some form of `swgl_commit` intrinsic to actually output to the destination -framebuffer via the blend stage, as normal fragment processing does not take -place otherwise as would have happened in `main`. This function is used by the -rasterizer to process an entire span of fragments that have passed the depth -test (if applicable) and clipping, but have not yet been output to the blend -stage. - -The amount of fragments within the span to be processed is designated by -`swgl_SpanLength` and is always aligned to units of `swgl_StepSize`. -The size of a group of fragments in terms of which `swgl_commit` intrinsics -process and output fragments is designated by `swgl_StepSize`. The -`swgl_commit` intrinsics will deduct accordingly from `swgl_SpanLength` in -units of `swgl_StepSize` to reflect the fragments actually processed, which -may be less than the entire span or up to the entire span. - -Fragments should be output until `swgl_SpanLength` becomes zero to process the -entire span. If `swgl_drawSpan` returns while leaving any fragments unprocessed, -the remaining fragments will be processed as normal by the fragment shader's -`main` function. This can be used to conditionally handle certain fast-paths -in a fragment shader by otherwise defaulting to the `main` function if -`swgl_drawSpan` can't appropriately process some or all of the fragments. - -The values of any varying inputs to the fragment shader will be set to their -values for the start of the span, but do not automatically update over the -the course of a span within a given call to `swgl_drawSpan`. The -`swgl_interpStep` intrinsic may be used to get the derivative per `swgl_StepSize` -group of fragments of a varying input so that the caller may update such -variables manually if desired or otherwise use that information for processing. -The `swgl_stepInterp` intrinsic forces all such varying inputs to advance by -a single step. - -The RGBA8 version will be used when the destination framebuffer is RGBA8 format, -and the R8 version will be used when the destination framebuffer is R8. Various -other intrinsics described below may have restrictions on whether they can be -used only with a certain destination framebuffer format and are noted as such if -so. - -``` -void swgl_clipMask(sampler2D mask, vec2 offset, vec2 bb_origin, vec2 bb_size); -``` - -When called from the the vertex shader, this specifies a clip mask texture to -be used to mask the currently drawn primitive while blending is enabled. This -mask will only apply to the current primitive. - -The mask must be an R8 texture that will be interpreted as alpha weighting -applied to the source pixel prior to the blend stage. It is sampled 1:1 with -nearest filtering without any applied transform. The given offset specifies -the positioning of the clip mask relative to the framebuffer's viewport. - -The supplied bounding box constrains sampling of the clip mask to only fall -within the given rectangle, specified relative to the clip mask offset. -Anything falling outside this rectangle will be clipped entirely. If the -rectangle is empty, then the clip mask will be ignored. - -``` -void swgl_antiAlias(int edgeMask); -``` - -When called from the vertex shader, this enables anti-aliasing for the -currently drawn primitive while blending is enabled. This setting will only -apply to the current primitive. Anti-aliasing will be applied only to the -edges corresponding to bits supplied in the mask. For simple use-cases, -the edge mask can be set to all 1 bits to enable AA for the entire quad. - -The order of the bits in the edge mask must match the winding order in which -the vertices are output in the vertex shader if processed as a quad, so that -the edge ends on that vertex. The easiest way to understand this ordering -is that for a rectangle (x0,y0,x1,y1) then the edge Nth edge bit corresponds -to the edge where Nth coordinate in the rectangle is constant. - -SWGL tries to use an anti-aliasing method that is reasonably close to WR's -signed-distance field approximation. WR would normally try to discern the -2D local-space coordinates of a given destination pixel relative to the -2D local-space bounding rectangle of a primitive. It then uses the screen- -space derivative to try to determine the how many local-space units equate -to a distance of around one screen-space pixel. A distance approximation -of coverage is then used based on the distance in local-space from the -the current pixel's center, roughly at half-intensity at pixel center -and ranging to zero or full intensity within a radius of half a pixel -away from the center. To account for AAing going outside the normal geometry -boundaries of the primitive, WR has to extrude the primitive by a local-space -estimate to allow some AA to happen within the extruded region. - -SWGL can ultimately do this approximation more simply and get around the -extrusion limitations by just ensuring spans encompass any pixel that is -partially covered when computing span boundaries. Further, since SWGL already -knows the slope of an edge and the coordinate of the span relative to the span -boundaries, finding the partial coverage of a given span becomes easy to do -without requiring any extra interpolants to track against local-space bounds. -Essentially, SWGL just performs anti-aliasing on the actual geometry bounds, -but when the pixels on a span's edge are determined to be partially covered -during span rasterization, it uses the same distance field method as WR on -those span boundary pixels to estimate the coverage based on edge slope. - -``` -void swgl_commitTextureLinearRGBA8(sampler, vec2 uv, vec4 uv_bounds); -void swgl_commitTextureLinearR8(sampler, vec2 uv, vec4 uv_bounds); -void swgl_commitTextureLinearR8ToRGBA8(sampler, vec2 uv, vec4 uv_bounds); - -void swgl_commitTextureLinearColorRGBA8(sampler, vec2 uv, vec4 uv_bounds, vec4|float color); -void swgl_commitTextureLinearColorR8(sampler, vec2 uv, vec4 uv_bounds, vec4|float color); -void swgl_commitTextureLinearColorR8ToRGBA8(sampler, vec2 uv, vec4 uv_bounds, vec4|float color); - -void swgl_commitTextureLinearRepeatRGBA8(sampler, vec2 uv, vec2 tile_repeat, vec4 uv_repeat, vec4 uv_bounds); -void swgl_commitTextureLinearRepeatColorRGBA8(sampler, vec2 uv, vec2 tile_repeat, vec4 uv_repeat, vec4 uv_bounds, vec4|float color); - -void swgl_commitTextureNearestRGBA8(sampler, vec2 uv, vec4 uv_bounds); -void swgl_commitTextureNearestColorRGBA8(sampler, vec2 uv, vec4 uv_bounds, vec4|float color); - -void swgl_commitTextureNearestRepeatRGBA8(sampler, vec2 uv, vec2 tile_repeat, vec4 uv_repeat, vec4 uv_bounds); -void swgl_commitTextureNearestRepeatColorRGBA8(sampler, vec2 uv, vec2 tile_repeat, vec4 uv_repeat, vec4 uv_bounds, vec4|float color); - -void swgl_commitTextureRGBA8(sampler, vec2 uv, vec4 uv_bounds); -void swgl_commitTextureColorRGBA8(sampler, vec2 uv, vec4 uv_bounds, vec4|float color); - -void swgl_commitTextureRepeatRGBA8(sampler, vec2 uv, vec2 tile_repeat, vec4 uv_repeat, vec4 uv_bounds); -void swgl_commitTextureRepeatColorRGBA8(sampler, vec2 uv, vec2 tile_repeat, vec4 uv_repeat, vec4 uv_bounds, vec4|float color); - -void swgl_commitPartialTextureLinearR8(int len, sampler, vec2 uv, vec4 uv_bounds); -void swgl_commitPartialTextureLinearInvertR8(int len, sampler, vec2 uv, vec4 uv_bounds); -``` - -Samples and commits an entire span of texture starting at the given uv and -within the supplied uv bounds. The color variations also accept a supplied color -that modulates the result. - -The RGBA8 versions may only be used to commit within `swgl_drawSpanRGBA8`, and -the R8 versions may only be used to commit within `swgl_drawSpanR8`. The R8ToRGBA8 -versions may be used to sample from an R8 source while committing to an RGBA8 -framebuffer. - -The Linear variations use a linear filter that bilinearly interpolates between -the four samples near the pixel. The Nearest variations use a nearest filter -that chooses the closest aliased sample to the center of the pixel. If neither -Linear nor Nearest is specified in the `swgl_commitTexture` variation name, then -it will automatically select either the Linear or Nearest variation depending -on the sampler's specified filter. - -The Repeat variations require an optional repeat rect that specifies how to -scale and offset the UVs, assuming the UVs are normalized to repeat in the -range 0 to 1. For NearestRepeat variations, it is assumed the repeat rect is -always within the bounds. The tile repeat limit, if non-zero, specifies the -maximum number of repetitions allowed. - -The Partial variations allow committing only a sub-span rather the entire -remaining span. These are currently only implemented in linear R8 variants -for optimizing clip shaders in WebRender. The Invert variant of these is -useful for implementing clip-out modes by inverting the source texture value. - -``` -// Premultiplied alpha over blend, but with source color set to source alpha modulated with a constant color. -void swgl_blendDropShadow(vec4 color); -// Premultiplied alpha over blend, but treats the source as a subpixel mask modulated with a constant color. -void swgl_blendSubpixelText(vec4 color); -``` - -SWGL allows overriding the blend mode per-primitive by calling `swgl_blend` -intrinsics in the vertex shader. The existing blend mode set by the GL is -replaced with the one specified by the intrinsic for the current primitive. -The blend mode will be reset to the blend mode set by the GL for the next -primitive after the current one, even within the same draw call. - diff --git a/third_party/webrender/swgl/build.rs b/third_party/webrender/swgl/build.rs index 300ff9f282a..5133e9f397a 100644 --- a/third_party/webrender/swgl/build.rs +++ b/third_party/webrender/swgl/build.rs @@ -2,9 +2,9 @@ * 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; -extern crate webrender_build; +use cc; +use glsl_to_cxx; +use webrender_build; use std::collections::HashSet; use std::fmt::Write; @@ -52,7 +52,7 @@ fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String } fn translate_shader(shader_key: &str, shader_dir: &str) { - let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n"); + let mut imported = String::from("#define SWGL 1\n"); let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n", webrender_build::MAX_VERTEX_TEXTURE_WIDTH); @@ -73,21 +73,11 @@ fn translate_shader(shader_key: &str, shader_dir: &str) { std::fs::write(&imp_name, imported).unwrap(); let mut build = cc::Build::new(); - build.no_default_flags(true); - if let Ok(tool) = build.try_get_compiler() { - if tool.is_like_msvc() { - build.flag("/EP"); - if tool.path().to_str().map_or(false, |p| p.contains("clang")) { - build.flag("/clang:-undef"); - } else { - build.flag("/u"); - } - } else { - build.flag("-xc").flag("-P").flag("-undef"); - } + if build.get_compiler().is_like_msvc() { + build.flag("/EP"); + } else { + build.flag("-xc").flag("-P"); } - // Use SWGLPP target to avoid pulling CFLAGS/CXXFLAGS. - build.target("SWGLPP"); build.file(&imp_name); let vs = build.clone() .define("WR_VERTEX_SHADER", Some("1")) @@ -100,11 +90,16 @@ fn translate_shader(shader_key: &str, shader_dir: &str) { std::fs::write(&vs_name, vs).unwrap(); std::fs::write(&fs_name, fs).unwrap(); - let args = vec![ + 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(); } @@ -117,9 +112,7 @@ fn main() { let shader_flags = ShaderFeatureFlags::GL | - ShaderFeatureFlags::DUAL_SOURCE_BLENDING | - ShaderFeatureFlags::ADVANCED_BLEND_EQUATION | - ShaderFeatureFlags::DEBUG; + ShaderFeatureFlags::DUAL_SOURCE_BLENDING; let mut shaders: Vec<String> = Vec::new(); for (name, features) in get_shader_features(shader_flags) { shaders.extend(features.iter().map(|f| { @@ -129,74 +122,26 @@ fn main() { shaders.sort(); - // We need to ensure that the C preprocessor does not pull compiler flags from - // the host or target environment. Set up a SWGLPP target with empty flags to - // work around this. - if let Ok(target) = std::env::var("TARGET") { - if let Ok(cc) = std::env::var(format!("CC_{}", target)) - .or(std::env::var(format!("CC_{}", target.replace("-", "_")))) { - std::env::set_var("CC_SWGLPP", cc); - } - } - std::env::set_var("CFLAGS_SWGLPP", ""); - for shader in &shaders { translate_shader(shader, &shader_dir); } write_load_shader(&shaders); - println!("cargo:rerun-if-changed=src/blend.h"); - println!("cargo:rerun-if-changed=src/composite.h"); 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/rasterize.h"); - println!("cargo:rerun-if-changed=src/swgl_ext.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"); - let mut build = cc::Build::new(); - build.cpp(true); - - if let Ok(tool) = build.try_get_compiler() { - if tool.is_like_msvc() { - build.flag("/std:c++17") - .flag("/EHs-") - .flag("/GR-") - .flag("/UMOZILLA_CONFIG_H"); - } else { - build.flag("-std=c++17") - .flag("-fno-exceptions") - .flag("-fno-rtti") - .flag("-fno-math-errno") - .flag("-UMOZILLA_CONFIG_H"); - } - // SWGL relies heavily on inlining for performance so override -Oz with -O2 - if tool.args().contains(&"-Oz".into()) { - build.flag("-O2"); - } - - // Most GLSL compilers assume something like fast-math so we turn it on. - // However, reciprocal division makes it so 1/1 = 0.999994 which can produce a lot of fuzz - // in reftests and the use of reciprocal instructions usually involves a refinement step - // which bloats our already bloated code. Further, our shader code is sufficiently parallel - // that we're more likely to be throughput bound vs latency bound. Having fewer - // instructions makes things easier on the processor and in places where it matters we can - // probably explicitly use reciprocal instructions and avoid the refinement step. - if tool.is_like_msvc() { - build.flag("/fp:fast") - .flag("-Xclang") - .flag("-mrecip=none"); - } else if tool.is_like_clang() { - // gcc only supports -mrecip=none on some targets so to keep - // things simple we don't use -ffast-math with gcc at all - build.flag("-ffast-math") - .flag("-mrecip=none"); - } - } - - build.file("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") diff --git a/third_party/webrender/swgl/src/blend.h b/third_party/webrender/swgl/src/blend.h deleted file mode 100644 index 8bc1c93994e..00000000000 --- a/third_party/webrender/swgl/src/blend.h +++ /dev/null @@ -1,864 +0,0 @@ -/* 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/. */ - -static ALWAYS_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 -} - -static ALWAYS_INLINE WideRGBA8 pack_pixels_RGBA8(const vec4& v, - float scale = 255.0f) { - ivec4 i = round_pixel(v, scale); - HalfRGBA8 xz = packRGBA8(i.z, i.x); - HalfRGBA8 yw = packRGBA8(i.y, i.w); - HalfRGBA8 xyzwl = zipLow(xz, yw); - HalfRGBA8 xyzwh = zipHigh(xz, yw); - HalfRGBA8 lo = zip2Low(xyzwl, xyzwh); - HalfRGBA8 hi = zip2High(xyzwl, xyzwh); - return combine(lo, hi); -} - -static ALWAYS_INLINE WideRGBA8 pack_pixels_RGBA8(Float alpha, - float scale = 255.0f) { - I32 i = round_pixel(alpha, scale); - HalfRGBA8 c = packRGBA8(i, i); - c = zipLow(c, c); - return zip(c, c); -} - -static ALWAYS_INLINE WideRGBA8 pack_pixels_RGBA8(float alpha, - float scale = 255.0f) { - I32 i = round_pixel(alpha, scale); - return repeat2(packRGBA8(i, i)); -} - -UNUSED static ALWAYS_INLINE WideRGBA8 pack_pixels_RGBA8(const vec4_scalar& v, - float scale = 255.0f) { - I32 i = round_pixel((Float){v.z, v.y, v.x, v.w}, scale); - return repeat2(packRGBA8(i, i)); -} - -static ALWAYS_INLINE WideRGBA8 pack_pixels_RGBA8() { - return pack_pixels_RGBA8(fragment_shader->gl_FragColor); -} - -static ALWAYS_INLINE WideRGBA8 pack_pixels_RGBA8(WideRGBA32F v, - float scale = 255.0f) { - ivec4 i = round_pixel(bit_cast<vec4>(v), scale); - return combine(packRGBA8(i.x, i.y), packRGBA8(i.z, i.w)); -} - -static ALWAYS_INLINE WideR8 packR8(I32 a) { -#if USE_SSE2 - return lowHalf(bit_cast<V8<uint16_t>>(_mm_packs_epi32(a, a))); -#elif USE_NEON - return vqmovun_s32(a); -#else - return CONVERT(a, WideR8); -#endif -} - -static ALWAYS_INLINE WideR8 pack_pixels_R8(Float c, float scale = 255.0f) { - return packR8(round_pixel(c, scale)); -} - -static ALWAYS_INLINE WideR8 pack_pixels_R8() { - return pack_pixels_R8(fragment_shader->gl_FragColor.x); -} - -// Load a partial span > 0 and < 4 pixels. -template <typename V, typename P> -static ALWAYS_INLINE V partial_load_span(const P* src, int span) { - return bit_cast<V>( - (span >= 2 - ? combine(unaligned_load<V2<P>>(src), - V2<P>{span > 2 ? unaligned_load<P>(src + 2) : P(0), 0}) - : V4<P>{unaligned_load<P>(src), 0, 0, 0})); -} - -// Store a partial span > 0 and < 4 pixels. -template <typename V, typename P> -static ALWAYS_INLINE void partial_store_span(P* dst, V src, int span) { - auto pixels = bit_cast<V4<P>>(src); - if (span >= 2) { - unaligned_store(dst, lowHalf(pixels)); - if (span > 2) { - unaligned_store(dst + 2, pixels.z); - } - } else { - unaligned_store(dst, pixels.x); - } -} - -// Dispatcher that chooses when to load a full or partial span -template <typename V, typename P> -static ALWAYS_INLINE V load_span(const P* src, int span) { - if (span >= 4) { - return unaligned_load<V, P>(src); - } else { - return partial_load_span<V, P>(src, span); - } -} - -// Dispatcher that chooses when to store a full or partial span -template <typename V, typename P> -static ALWAYS_INLINE void store_span(P* dst, V src, int span) { - if (span >= 4) { - unaligned_store<V, P>(dst, src); - } else { - partial_store_span<V, P>(dst, src, span); - } -} - -template <typename T> -static ALWAYS_INLINE T muldiv256(T x, T y) { - return (x * y) >> 8; -} - -// (x*y + x) >> 8, cheap approximation of (x*y) / 255 -template <typename T> -static ALWAYS_INLINE T muldiv255(T x, T y) { - return (x * y + x) >> 8; -} - -template <typename V> -static ALWAYS_INLINE WideRGBA8 pack_span(uint32_t*, const V& v, - float scale = 255.0f) { - return pack_pixels_RGBA8(v, scale); -} - -template <typename C> -static ALWAYS_INLINE WideR8 pack_span(uint8_t*, C c, float scale = 255.0f) { - return pack_pixels_R8(c, scale); -} - -// Helper functions to apply a color modulus when available. -struct NoColor {}; - -template <typename P> -static ALWAYS_INLINE P applyColor(P src, NoColor) { - return src; -} - -struct InvertColor {}; - -template <typename P> -static ALWAYS_INLINE P applyColor(P src, InvertColor) { - return 255 - src; -} - -template <typename P> -static ALWAYS_INLINE P applyColor(P src, P color) { - return muldiv255(color, src); -} - -static ALWAYS_INLINE WideRGBA8 applyColor(PackedRGBA8 src, WideRGBA8 color) { - return applyColor(unpack(src), color); -} - -template <typename P, typename C> -static ALWAYS_INLINE auto packColor(P* buf, C color) { - return pack_span(buf, color, 255.0f); -} - -template <typename P> -static ALWAYS_INLINE NoColor packColor(UNUSED P* buf, NoColor noColor) { - return noColor; -} - -template <typename P> -static ALWAYS_INLINE InvertColor packColor(UNUSED P* buf, - InvertColor invertColor) { - return invertColor; -} - -// Single argument variation that takes an explicit destination buffer type. -template <typename P, typename C> -static ALWAYS_INLINE auto packColor(C color) { - // Just pass in a typed null pointer, as the pack routines never use the - // pointer's value, just its type. - return packColor((P*)0, color); -} - -// Byte-wise addition for when x or y is a signed 8-bit value stored in the -// low byte of a larger type T only with zeroed-out high bits, where T is -// greater than 8 bits, i.e. uint16_t. This can result when muldiv255 is used -// upon signed operands, using up all the precision in a 16 bit integer, and -// potentially losing the sign bit in the last >> 8 shift. Due to the -// properties of two's complement arithmetic, even though we've discarded the -// sign bit, we can still represent a negative number under addition (without -// requiring any extra sign bits), just that any negative number will behave -// like a large unsigned number under addition, generating a single carry bit -// on overflow that we need to discard. Thus, just doing a byte-wise add will -// overflow without the troublesome carry, giving us only the remaining 8 low -// bits we actually need while keeping the high bits at zero. -template <typename T> -static ALWAYS_INLINE T addlow(T x, T y) { - typedef VectorType<uint8_t, sizeof(T)> bytes; - return bit_cast<T>(bit_cast<bytes>(x) + bit_cast<bytes>(y)); -} - -// Replace color components of each pixel with the pixel's alpha values. -template <typename T> -static ALWAYS_INLINE T alphas(T c) { - return SHUFFLE(c, c, 3, 3, 3, 3, 7, 7, 7, 7, 11, 11, 11, 11, 15, 15, 15, 15); -} - -// Replace the alpha values of the first vector with alpha values from the -// second, while leaving the color components unmodified. -template <typename T> -static ALWAYS_INLINE T set_alphas(T c, T a) { - return SHUFFLE(c, a, 0, 1, 2, 19, 4, 5, 6, 23, 8, 9, 10, 27, 12, 13, 14, 31); -} - -// Miscellaneous helper functions for working with packed RGBA8 data. -static ALWAYS_INLINE HalfRGBA8 if_then_else(V8<int16_t> c, HalfRGBA8 t, - HalfRGBA8 e) { - return bit_cast<HalfRGBA8>((c & t) | (~c & e)); -} - -template <typename T, typename C, int N> -static ALWAYS_INLINE VectorType<T, N> if_then_else(VectorType<C, N> c, - VectorType<T, N> t, - VectorType<T, N> e) { - return combine(if_then_else(lowHalf(c), lowHalf(t), lowHalf(e)), - if_then_else(highHalf(c), highHalf(t), highHalf(e))); -} - -static ALWAYS_INLINE HalfRGBA8 min(HalfRGBA8 x, HalfRGBA8 y) { -#if USE_SSE2 - return bit_cast<HalfRGBA8>( - _mm_min_epi16(bit_cast<V8<int16_t>>(x), bit_cast<V8<int16_t>>(y))); -#elif USE_NEON - return vminq_u16(x, y); -#else - return if_then_else(x < y, x, y); -#endif -} - -template <typename T, int N> -static ALWAYS_INLINE VectorType<T, N> min(VectorType<T, N> x, - VectorType<T, N> y) { - return combine(min(lowHalf(x), lowHalf(y)), min(highHalf(x), highHalf(y))); -} - -static ALWAYS_INLINE HalfRGBA8 max(HalfRGBA8 x, HalfRGBA8 y) { -#if USE_SSE2 - return bit_cast<HalfRGBA8>( - _mm_max_epi16(bit_cast<V8<int16_t>>(x), bit_cast<V8<int16_t>>(y))); -#elif USE_NEON - return vmaxq_u16(x, y); -#else - return if_then_else(x > y, x, y); -#endif -} - -template <typename T, int N> -static ALWAYS_INLINE VectorType<T, N> max(VectorType<T, N> x, - VectorType<T, N> y) { - return combine(max(lowHalf(x), lowHalf(y)), max(highHalf(x), highHalf(y))); -} - -template <typename T, int N> -static ALWAYS_INLINE VectorType<T, N> recip(VectorType<T, N> v) { - return combine(recip(lowHalf(v)), recip(highHalf(v))); -} - -// Helper to get the reciprocal if the value is non-zero, or otherwise default -// to the supplied fallback value. -template <typename V> -static ALWAYS_INLINE V recip_or(V v, float f) { - return if_then_else(v != V(0.0f), recip(v), V(f)); -} - -template <typename T, int N> -static ALWAYS_INLINE VectorType<T, N> inversesqrt(VectorType<T, N> v) { - return combine(inversesqrt(lowHalf(v)), inversesqrt(highHalf(v))); -} - -// Extract the alpha components so that we can cheaply calculate the reciprocal -// on a single SIMD register. Then multiply the duplicated alpha reciprocal with -// the pixel data. 0 alpha is treated as transparent black. -static ALWAYS_INLINE WideRGBA32F unpremultiply(WideRGBA32F v) { - Float a = recip_or((Float){v[3], v[7], v[11], v[15]}, 0.0f); - return v * a.xxxxyyyyzzzzwwww; -} - -// Packed RGBA32F data is AoS in BGRA order. Transpose it to SoA and swizzle to -// RGBA to unpack. -static ALWAYS_INLINE vec4 unpack(PackedRGBA32F c) { - return bit_cast<vec4>( - SHUFFLE(c, c, 2, 6, 10, 14, 1, 5, 9, 13, 0, 4, 8, 12, 3, 7, 11, 15)); -} - -// The following lum/sat functions mostly follow the KHR_blend_equation_advanced -// specification but are rearranged to work on premultiplied data. -static ALWAYS_INLINE Float lumv3(vec3 v) { - return v.x * 0.30f + v.y * 0.59f + v.z * 0.11f; -} - -static ALWAYS_INLINE Float minv3(vec3 v) { return min(min(v.x, v.y), v.z); } - -static ALWAYS_INLINE Float maxv3(vec3 v) { return max(max(v.x, v.y), v.z); } - -static inline vec3 clip_color(vec3 v, Float lum, Float alpha) { - Float mincol = max(-minv3(v), lum); - Float maxcol = max(maxv3(v), alpha - lum); - return lum + v * (lum * (alpha - lum) * recip_or(mincol * maxcol, 0.0f)); -} - -static inline vec3 set_lum(vec3 base, vec3 ref, Float alpha) { - return clip_color(base - lumv3(base), lumv3(ref), alpha); -} - -static inline vec3 set_lum_sat(vec3 base, vec3 sref, vec3 lref, Float alpha) { - vec3 diff = base - minv3(base); - Float sbase = maxv3(diff); - Float ssat = maxv3(sref) - minv3(sref); - // The sbase range is rescaled to ssat. If sbase has 0 extent, then rescale - // to black, as per specification. - return set_lum(diff * ssat * recip_or(sbase, 0.0f), lref, alpha); -} - -// Flags the reflect the current blend-stage clipping to be applied. -enum SWGLClipFlag { - SWGL_CLIP_FLAG_MASK = 1 << 0, - SWGL_CLIP_FLAG_AA = 1 << 1, - SWGL_CLIP_FLAG_BLEND_OVERRIDE = 1 << 2, -}; -static int swgl_ClipFlags = 0; -static BlendKey swgl_BlendOverride = BLEND_KEY_NONE; -static WideRGBA8 swgl_BlendColorRGBA8 = {0}; -static WideRGBA8 swgl_BlendAlphaRGBA8 = {0}; - -// A pointer into the color buffer for the start of the span. -static void* swgl_SpanBuf = nullptr; -// A pointer into the clip mask for the start of the span. -static uint8_t* swgl_ClipMaskBuf = nullptr; - -static ALWAYS_INLINE WideR8 expand_mask(UNUSED uint8_t* buf, WideR8 mask) { - return mask; -} -static ALWAYS_INLINE WideRGBA8 expand_mask(UNUSED uint32_t* buf, WideR8 mask) { - WideRG8 maskRG = zip(mask, mask); - return zip(maskRG, maskRG); -} - -// Loads a chunk of clip masks. The current pointer into the color buffer is -// used to reconstruct the relative position within the span. From there, the -// pointer into the clip mask can be generated from the start of the clip mask -// span. -template <typename P> -static ALWAYS_INLINE uint8_t* get_clip_mask(P* buf) { - return &swgl_ClipMaskBuf[buf - (P*)swgl_SpanBuf]; -} - -template <typename P> -static ALWAYS_INLINE auto load_clip_mask(P* buf, int span) - -> decltype(expand_mask(buf, 0)) { - return expand_mask(buf, - unpack(load_span<PackedR8>(get_clip_mask(buf), span))); -} - -// Temporarily removes masking from the blend stage, assuming the caller will -// handle it. -static ALWAYS_INLINE void override_clip_mask() { - blend_key = BlendKey(blend_key - MASK_BLEND_KEY_NONE); -} - -// Restores masking to the blend stage, assuming it was previously overridden. -static ALWAYS_INLINE void restore_clip_mask() { - blend_key = BlendKey(MASK_BLEND_KEY_NONE + blend_key); -} - -// A pointer to the start of the opaque destination region of the span for AA. -static const uint8_t* swgl_OpaqueStart = nullptr; -// The size, in bytes, of the opaque region. -static uint32_t swgl_OpaqueSize = 0; -// AA coverage distance offsets for the left and right edges. -static Float swgl_LeftAADist = 0.0f; -static Float swgl_RightAADist = 0.0f; -// AA coverage slope values used for accumulating coverage for each step. -static Float swgl_AASlope = 0.0f; - -// Get the amount of pixels we need to process before the start of the opaque -// region. -template <typename P> -static ALWAYS_INLINE int get_aa_opaque_start(P* buf) { - return max(int((P*)swgl_OpaqueStart - buf), 0); -} - -// Assuming we are already in the opaque part of the span, return the remaining -// size of the opaque part. -template <typename P> -static ALWAYS_INLINE int get_aa_opaque_size(P* buf) { - return max(int((P*)&swgl_OpaqueStart[swgl_OpaqueSize] - buf), 0); -} - -// Temporarily removes anti-aliasing from the blend stage, assuming the caller -// will handle it. -static ALWAYS_INLINE void override_aa() { - blend_key = BlendKey(blend_key - AA_BLEND_KEY_NONE); -} - -// Restores anti-aliasing to the blend stage, assuming it was previously -// overridden. -static ALWAYS_INLINE void restore_aa() { - blend_key = BlendKey(AA_BLEND_KEY_NONE + blend_key); -} - -static PREFER_INLINE WideRGBA8 blend_pixels(uint32_t* buf, PackedRGBA8 pdst, - WideRGBA8 src, int span = 4) { - 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}; - const WideRGBA8 ALPHA_OPAQUE = {0, 0, 0, 255, 0, 0, 0, 255, - 0, 0, 0, 255, 0, 0, 0, 255}; - -// clang-format off - // Computes AA for the given pixel based on the offset of the pixel within - // destination row. Given the initial coverage offsets for the left and right - // edges, the offset is scaled by the slope and accumulated to find the - // minimum coverage value for the pixel. A final weight is generated that - // can be used to scale the source pixel. -#define DO_AA(format, body) \ - do { \ - int offset = int((const uint8_t*)buf - swgl_OpaqueStart); \ - if (uint32_t(offset) >= swgl_OpaqueSize) { \ - Float delta = swgl_AASlope * float(offset); \ - Float dist = clamp(min(swgl_LeftAADist + delta.x, \ - swgl_RightAADist + delta.y), \ - 0.0f, 256.0f); \ - auto aa = pack_pixels_##format(dist, 1.0f); \ - body; \ - } \ - } while (0) - - // Each blend case is preceded by the MASK_ variant. The MASK_ case first - // loads the mask values and multiplies the source value by them. After, it - // falls through to the normal blending case using the masked source. The - // AA_ variations may further precede the blend cases, in which case the - // source value is further modified before use. -#define BLEND_CASE_KEY(key) \ - case AA_##key: \ - DO_AA(RGBA8, src = muldiv256(src, aa)); \ - goto key; \ - case AA_MASK_##key: \ - DO_AA(RGBA8, src = muldiv256(src, aa)); \ - FALLTHROUGH; \ - case MASK_##key: \ - src = muldiv255(src, load_clip_mask(buf, span)); \ - FALLTHROUGH; \ - case key: key - -#define BLEND_CASE(...) BLEND_CASE_KEY(BLEND_KEY(__VA_ARGS__)) - - switch (blend_key) { - BLEND_CASE(GL_ONE, GL_ZERO): - return src; - BLEND_CASE(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, - GL_ONE_MINUS_SRC_ALPHA): - // dst + src.a*(src.rgb1 - dst) - // use addlow for signed overflow - return addlow(dst, muldiv255(alphas(src), (src | ALPHA_OPAQUE) - dst)); - BLEND_CASE(GL_ONE, GL_ONE_MINUS_SRC_ALPHA): - return src + dst - muldiv255(dst, alphas(src)); - BLEND_CASE(GL_ZERO, GL_ONE_MINUS_SRC_COLOR): - return dst - muldiv255(dst, src); - BLEND_CASE(GL_ZERO, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE): - return dst - (muldiv255(dst, src) & RGB_MASK); - BLEND_CASE(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA): - return dst - muldiv255(dst, alphas(src)); - BLEND_CASE(GL_ZERO, GL_SRC_COLOR): - return muldiv255(src, dst); - BLEND_CASE(GL_ONE, GL_ONE): - return src + dst; - BLEND_CASE(GL_ONE, GL_ONE, GL_ONE, GL_ONE_MINUS_SRC_ALPHA): - return src + dst - (muldiv255(dst, src) & ALPHA_MASK); - BLEND_CASE(GL_ONE_MINUS_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE): - // src*(1-dst.a) + dst*1 = src - src*dst.a + dst - return dst + ((src - muldiv255(src, alphas(dst))) & RGB_MASK); - BLEND_CASE(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR): - // src*k + (1-src)*dst = src*k + dst - - // src*dst = dst + src*(k - dst) use addlow - // for signed overflow - return addlow( - dst, muldiv255(src, repeat2(ctx->blendcolor) - dst)); - - // We must explicitly handle the masked/anti-aliased secondary blend case. - // The secondary color as well as the source must be multiplied by the - // weights. - case BLEND_KEY(GL_ONE, GL_ONE_MINUS_SRC1_COLOR): { - WideRGBA8 secondary = - applyColor(dst, - packColor<uint32_t>(fragment_shader->gl_SecondaryFragColor)); - return src + dst - secondary; - } - case MASK_BLEND_KEY(GL_ONE, GL_ONE_MINUS_SRC1_COLOR): { - WideRGBA8 secondary = - applyColor(dst, - packColor<uint32_t>(fragment_shader->gl_SecondaryFragColor)); - WideRGBA8 mask = load_clip_mask(buf, span); - return muldiv255(src, mask) + dst - muldiv255(secondary, mask); - } - case AA_BLEND_KEY(GL_ONE, GL_ONE_MINUS_SRC1_COLOR): { - WideRGBA8 secondary = - applyColor(dst, - packColor<uint32_t>(fragment_shader->gl_SecondaryFragColor)); - DO_AA(RGBA8, { - src = muldiv256(src, aa); - secondary = muldiv256(secondary, aa); - }); - return src + dst - secondary; - } - case AA_MASK_BLEND_KEY(GL_ONE, GL_ONE_MINUS_SRC1_COLOR): { - WideRGBA8 secondary = - applyColor(dst, - packColor<uint32_t>(fragment_shader->gl_SecondaryFragColor)); - WideRGBA8 mask = load_clip_mask(buf, span); - DO_AA(RGBA8, mask = muldiv256(mask, aa)); - return muldiv255(src, mask) + dst - muldiv255(secondary, mask); - } - - BLEND_CASE(GL_MIN): - return min(src, dst); - BLEND_CASE(GL_MAX): - return max(src, dst); - - // The KHR_blend_equation_advanced spec describes the blend equations such - // that the unpremultiplied values Cs, Cd, As, Ad and function f combine to - // the result: - // Cr = f(Cs,Cd)*As*Ad + Cs*As*(1-Ad) + Cd*AD*(1-As) - // Ar = As*Ad + As*(1-Ad) + Ad*(1-As) - // However, working with unpremultiplied values requires expensive math to - // unpremultiply and premultiply again during blending. We can use the fact - // that premultiplied value P = C*A and simplify the equations such that no - // unpremultiplied colors are necessary, allowing us to stay with integer - // math that avoids floating-point conversions in the common case. Some of - // the blend modes require division or sqrt, in which case we do convert - // to (possibly transposed/unpacked) floating-point to implement the mode. - // However, most common modes can still use cheaper premultiplied integer - // math. As an example, the multiply mode f(Cs,Cd) = Cs*Cd is simplified - // to: - // Cr = Cs*Cd*As*Ad + Cs*As*(1-Ad) + Cd*Ad*(1-As) - // .. Pr = Ps*Pd + Ps - Ps*Ad + Pd - Pd*As - // Ar = As*Ad + As - As*Ad + Ad - Ad*As - // .. Ar = As + Ad - As*Ad - // Note that the alpha equation is the same for all blend equations, such - // that so long as the implementation results in As + Ad - As*Ad, we can - // avoid using separate instructions to compute the alpha result, which is - // dependent on the math used to implement each blend mode. The exact - // reductions used to get the final math for every blend mode are too - // involved to show here in comments, but mostly follows from replacing - // Cs*As and Cd*Ad with Ps and Ps while factoring out as many common terms - // as possible. - - BLEND_CASE(GL_MULTIPLY_KHR): { - WideRGBA8 diff = muldiv255(alphas(src) - (src & RGB_MASK), - alphas(dst) - (dst & RGB_MASK)); - return src + dst + (diff & RGB_MASK) - alphas(diff); - } - BLEND_CASE(GL_SCREEN_KHR): - return src + dst - muldiv255(src, dst); - BLEND_CASE(GL_OVERLAY_KHR): { - WideRGBA8 srcA = alphas(src); - WideRGBA8 dstA = alphas(dst); - WideRGBA8 diff = muldiv255(src, dst) + muldiv255(srcA - src, dstA - dst); - return src + dst + - if_then_else(dst * 2 <= dstA, (diff & RGB_MASK) - alphas(diff), - -diff); - } - BLEND_CASE(GL_DARKEN_KHR): - return src + dst - - max(muldiv255(src, alphas(dst)), muldiv255(dst, alphas(src))); - BLEND_CASE(GL_LIGHTEN_KHR): - return src + dst - - min(muldiv255(src, alphas(dst)), muldiv255(dst, alphas(src))); - - BLEND_CASE(GL_COLORDODGE_KHR): { - // Color-dodge and color-burn require division, so we convert to FP math - // here, but avoid transposing to a vec4. - WideRGBA32F srcF = CONVERT(src, WideRGBA32F); - WideRGBA32F srcA = alphas(srcF); - WideRGBA32F dstF = CONVERT(dst, WideRGBA32F); - WideRGBA32F dstA = alphas(dstF); - return pack_pixels_RGBA8( - srcA * set_alphas( - min(dstA, dstF * srcA * recip_or(srcA - srcF, 255.0f)), - dstF) + - srcF * (255.0f - dstA) + dstF * (255.0f - srcA), - 1.0f / 255.0f); - } - BLEND_CASE(GL_COLORBURN_KHR): { - WideRGBA32F srcF = CONVERT(src, WideRGBA32F); - WideRGBA32F srcA = alphas(srcF); - WideRGBA32F dstF = CONVERT(dst, WideRGBA32F); - WideRGBA32F dstA = alphas(dstF); - return pack_pixels_RGBA8( - srcA * set_alphas((dstA - min(dstA, (dstA - dstF) * srcA * - recip_or(srcF, 255.0f))), - dstF) + - srcF * (255.0f - dstA) + dstF * (255.0f - srcA), - 1.0f / 255.0f); - } - BLEND_CASE(GL_HARDLIGHT_KHR): { - WideRGBA8 srcA = alphas(src); - WideRGBA8 dstA = alphas(dst); - WideRGBA8 diff = muldiv255(src, dst) + muldiv255(srcA - src, dstA - dst); - return src + dst + - if_then_else(src * 2 <= srcA, (diff & RGB_MASK) - alphas(diff), - -diff); - } - - BLEND_CASE(GL_SOFTLIGHT_KHR): { - // Soft-light requires an unpremultiply that can't be factored out as - // well as a sqrt, so we convert to FP math here, but avoid transposing - // to a vec4. - WideRGBA32F srcF = CONVERT(src, WideRGBA32F); - WideRGBA32F srcA = alphas(srcF); - WideRGBA32F dstF = CONVERT(dst, WideRGBA32F); - WideRGBA32F dstA = alphas(dstF); - WideRGBA32F dstU = unpremultiply(dstF); - WideRGBA32F scale = srcF + srcF - srcA; - return pack_pixels_RGBA8( - dstF * (255.0f + - set_alphas( - scale * - if_then_else(scale < 0.0f, 1.0f - dstU, - min((16.0f * dstU - 12.0f) * dstU + 3.0f, - inversesqrt(dstU) - 1.0f)), - WideRGBA32F(0.0f))) + - srcF * (255.0f - dstA), - 1.0f / 255.0f); - } - BLEND_CASE(GL_DIFFERENCE_KHR): { - WideRGBA8 diff = - min(muldiv255(dst, alphas(src)), muldiv255(src, alphas(dst))); - return src + dst - diff - (diff & RGB_MASK); - } - BLEND_CASE(GL_EXCLUSION_KHR): { - WideRGBA8 diff = muldiv255(src, dst); - return src + dst - diff - (diff & RGB_MASK); - } - - // The HSL blend modes are non-separable and require complicated use of - // division. It is advantageous to convert to FP and transpose to vec4 - // math to more easily manipulate the individual color components. -#define DO_HSL(rgb) \ - do { \ - vec4 srcV = unpack(CONVERT(src, PackedRGBA32F)); \ - vec4 dstV = unpack(CONVERT(dst, PackedRGBA32F)); \ - Float srcA = srcV.w * (1.0f / 255.0f); \ - Float dstA = dstV.w * (1.0f / 255.0f); \ - Float srcDstA = srcV.w * dstA; \ - vec3 srcC = vec3(srcV) * dstA; \ - vec3 dstC = vec3(dstV) * srcA; \ - return pack_pixels_RGBA8(vec4(rgb + vec3(srcV) - srcC + vec3(dstV) - dstC, \ - srcV.w + dstV.w - srcDstA), \ - 1.0f); \ - } while (0) - - BLEND_CASE(GL_HSL_HUE_KHR): - DO_HSL(set_lum_sat(srcC, dstC, dstC, srcDstA)); - BLEND_CASE(GL_HSL_SATURATION_KHR): - DO_HSL(set_lum_sat(dstC, srcC, dstC, srcDstA)); - BLEND_CASE(GL_HSL_COLOR_KHR): - DO_HSL(set_lum(srcC, dstC, srcDstA)); - BLEND_CASE(GL_HSL_LUMINOSITY_KHR): - DO_HSL(set_lum(dstC, srcC, srcDstA)); - - // SWGL-specific extended blend modes. - BLEND_CASE(SWGL_BLEND_DROP_SHADOW): { - // Premultiplied alpha over blend, but with source color set to source alpha - // modulated with a constant color. - WideRGBA8 color = applyColor(alphas(src), swgl_BlendColorRGBA8); - return color + dst - muldiv255(dst, alphas(color)); - } - - BLEND_CASE(SWGL_BLEND_SUBPIXEL_TEXT): - // Premultiplied alpha over blend, but treats the source as a subpixel mask - // modulated with a constant color. - return applyColor(src, swgl_BlendColorRGBA8) + dst - - muldiv255(dst, applyColor(src, swgl_BlendAlphaRGBA8)); - - default: - UNREACHABLE; - // return src; - } - -#undef BLEND_CASE -#undef BLEND_CASE_KEY - // clang-format on -} - -static PREFER_INLINE WideR8 blend_pixels(uint8_t* buf, WideR8 dst, WideR8 src, - int span = 4) { -// clang-format off -#define BLEND_CASE_KEY(key) \ - case AA_##key: \ - DO_AA(R8, src = muldiv256(src, aa)); \ - goto key; \ - case AA_MASK_##key: \ - DO_AA(R8, src = muldiv256(src, aa)); \ - FALLTHROUGH; \ - case MASK_##key: \ - src = muldiv255(src, load_clip_mask(buf, span)); \ - FALLTHROUGH; \ - case key: key - -#define BLEND_CASE(...) BLEND_CASE_KEY(BLEND_KEY(__VA_ARGS__)) - - switch (blend_key) { - BLEND_CASE(GL_ONE, GL_ZERO): - return src; - BLEND_CASE(GL_ZERO, GL_SRC_COLOR): - return muldiv255(src, dst); - BLEND_CASE(GL_ONE, GL_ONE): - return src + dst; - default: - UNREACHABLE; - // return src; - } - -#undef BLEND_CASE -#undef BLEND_CASE_KEY - // clang-format on -} - -static ALWAYS_INLINE void commit_span(uint32_t* buf, WideRGBA8 r) { - unaligned_store(buf, pack(r)); -} - -static ALWAYS_INLINE void commit_span(uint32_t* buf, WideRGBA8 r, int len) { - partial_store_span(buf, pack(r), len); -} - -static ALWAYS_INLINE WideRGBA8 blend_span(uint32_t* buf, WideRGBA8 r) { - return blend_pixels(buf, unaligned_load<PackedRGBA8>(buf), r); -} - -static ALWAYS_INLINE WideRGBA8 blend_span(uint32_t* buf, WideRGBA8 r, int len) { - return blend_pixels(buf, partial_load_span<PackedRGBA8>(buf, len), r, len); -} - -static ALWAYS_INLINE void commit_span(uint32_t* buf, PackedRGBA8 r) { - unaligned_store(buf, r); -} - -static ALWAYS_INLINE void commit_span(uint32_t* buf, PackedRGBA8 r, int len) { - partial_store_span(buf, r, len); -} - -static ALWAYS_INLINE PackedRGBA8 blend_span(uint32_t* buf, PackedRGBA8 r) { - return pack(blend_span(buf, unpack(r))); -} - -static ALWAYS_INLINE PackedRGBA8 blend_span(uint32_t* buf, PackedRGBA8 r, - int len) { - return pack(blend_span(buf, unpack(r), len)); -} - -static ALWAYS_INLINE void commit_span(uint8_t* buf, WideR8 r) { - unaligned_store(buf, pack(r)); -} - -static ALWAYS_INLINE void commit_span(uint8_t* buf, WideR8 r, int len) { - partial_store_span(buf, pack(r), len); -} - -static ALWAYS_INLINE WideR8 blend_span(uint8_t* buf, WideR8 r) { - return blend_pixels(buf, unpack(unaligned_load<PackedR8>(buf)), r); -} - -static ALWAYS_INLINE WideR8 blend_span(uint8_t* buf, WideR8 r, int len) { - return blend_pixels(buf, unpack(partial_load_span<PackedR8>(buf, len)), r, - len); -} - -static ALWAYS_INLINE void commit_span(uint8_t* buf, PackedR8 r) { - unaligned_store(buf, r); -} - -static ALWAYS_INLINE void commit_span(uint8_t* buf, PackedR8 r, int len) { - partial_store_span(buf, r, len); -} - -static ALWAYS_INLINE PackedR8 blend_span(uint8_t* buf, PackedR8 r) { - return pack(blend_span(buf, unpack(r))); -} - -static ALWAYS_INLINE PackedR8 blend_span(uint8_t* buf, PackedR8 r, int len) { - return pack(blend_span(buf, unpack(r), len)); -} - -template <bool BLEND, typename P, typename R> -static ALWAYS_INLINE void commit_blend_span(P* buf, R r) { - if (BLEND) { - commit_span(buf, blend_span(buf, r)); - } else { - commit_span(buf, r); - } -} - -template <bool BLEND, typename P, typename R> -static ALWAYS_INLINE void commit_blend_span(P* buf, R r, int len) { - if (BLEND) { - commit_span(buf, blend_span(buf, r, len), len); - } else { - commit_span(buf, r, len); - } -} - -template <typename P, typename R> -static ALWAYS_INLINE void commit_blend_solid_span(P* buf, R r, int len) { - for (P* end = &buf[len & ~3]; buf < end; buf += 4) { - commit_span(buf, blend_span(buf, r)); - } - len &= 3; - if (len > 0) { - partial_store_span(buf, pack(blend_span(buf, r, len)), len); - } -} - -template <bool BLEND> -static void commit_solid_span(uint32_t* buf, WideRGBA8 r, int len) { - commit_blend_solid_span(buf, r, len); -} - -template <> -ALWAYS_INLINE void commit_solid_span<false>(uint32_t* buf, WideRGBA8 r, - int len) { - fill_n(buf, len, bit_cast<U32>(pack(r)).x); -} - -template <bool BLEND> -static void commit_solid_span(uint8_t* buf, WideR8 r, int len) { - commit_blend_solid_span(buf, r, len); -} - -template <> -ALWAYS_INLINE void commit_solid_span<false>(uint8_t* buf, WideR8 r, int len) { - PackedR8 p = pack(r); - if (uintptr_t(buf) & 3) { - int align = 4 - (uintptr_t(buf) & 3); - align = min(align, len); - partial_store_span(buf, p, align); - buf += align; - len -= align; - } - fill_n((uint32_t*)buf, len / 4, bit_cast<uint32_t>(p)); - buf += len & ~3; - len &= 3; - if (len > 0) { - partial_store_span(buf, p, len); - } -} diff --git a/third_party/webrender/swgl/src/composite.h b/third_party/webrender/swgl/src/composite.h deleted file mode 100644 index f88de485fdd..00000000000 --- a/third_party/webrender/swgl/src/composite.h +++ /dev/null @@ -1,1069 +0,0 @@ -/* 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 <bool COMPOSITE, typename P> -static inline void copy_row(P* dst, const P* src, int span) { - // No scaling, so just do a fast copy. - memcpy(dst, src, span * sizeof(P)); -} - -template <> -void copy_row<true, uint32_t>(uint32_t* dst, const uint32_t* src, int span) { - // No scaling, so just do a fast composite. - auto* end = dst + span; - while (dst + 4 <= end) { - WideRGBA8 srcpx = unpack(unaligned_load<PackedRGBA8>(src)); - WideRGBA8 dstpx = unpack(unaligned_load<PackedRGBA8>(dst)); - PackedRGBA8 r = pack(srcpx + dstpx - muldiv255(dstpx, alphas(srcpx))); - unaligned_store(dst, r); - src += 4; - dst += 4; - } - if (dst < end) { - WideRGBA8 srcpx = unpack(partial_load_span<PackedRGBA8>(src, end - dst)); - WideRGBA8 dstpx = unpack(partial_load_span<PackedRGBA8>(dst, end - dst)); - auto r = pack(srcpx + dstpx - muldiv255(dstpx, alphas(srcpx))); - partial_store_span(dst, r, end - dst); - } -} - -template <bool COMPOSITE, typename P> -static inline void scale_row(P* dst, int dstWidth, const P* src, int srcWidth, - int span, int frac) { - // Do scaling with different source and dest widths. - for (P* end = dst + span; dst < end; dst++) { - *dst = *src; - // Step source according to width ratio. - for (frac += srcWidth; frac >= dstWidth; frac -= dstWidth) { - src++; - } - } -} - -template <> -void scale_row<true, uint32_t>(uint32_t* dst, int dstWidth, const uint32_t* src, - int srcWidth, int span, int frac) { - // Do scaling with different source and dest widths. - // Gather source pixels four at a time for better packing. - auto* end = dst + span; - for (; dst + 4 <= end; dst += 4) { - U32 srcn; - srcn.x = *src; - for (frac += srcWidth; frac >= dstWidth; frac -= dstWidth) { - src++; - } - srcn.y = *src; - for (frac += srcWidth; frac >= dstWidth; frac -= dstWidth) { - src++; - } - srcn.z = *src; - for (frac += srcWidth; frac >= dstWidth; frac -= dstWidth) { - src++; - } - srcn.w = *src; - for (frac += srcWidth; frac >= dstWidth; frac -= dstWidth) { - src++; - } - WideRGBA8 srcpx = unpack(bit_cast<PackedRGBA8>(srcn)); - WideRGBA8 dstpx = unpack(unaligned_load<PackedRGBA8>(dst)); - PackedRGBA8 r = pack(srcpx + dstpx - muldiv255(dstpx, alphas(srcpx))); - unaligned_store(dst, r); - } - if (dst < end) { - // Process any remaining pixels. Try to gather as many pixels as possible - // into a single source chunk for compositing. - U32 srcn = {*src, 0, 0, 0}; - if (end - dst > 1) { - for (frac += srcWidth; frac >= dstWidth; frac -= dstWidth) { - src++; - } - srcn.y = *src; - if (end - dst > 2) { - for (frac += srcWidth; frac >= dstWidth; frac -= dstWidth) { - src++; - } - srcn.z = *src; - } - } - WideRGBA8 srcpx = unpack(bit_cast<PackedRGBA8>(srcn)); - WideRGBA8 dstpx = unpack(partial_load_span<PackedRGBA8>(dst, end - dst)); - auto r = pack(srcpx + dstpx - muldiv255(dstpx, alphas(srcpx))); - partial_store_span(dst, r, end - dst); - } -} - -template <bool COMPOSITE = false> -static NO_INLINE void scale_blit(Texture& srctex, const IntRect& srcReq, - Texture& dsttex, const IntRect& dstReq, - bool invertY, const IntRect& clipRect) { - assert(!COMPOSITE || (srctex.internal_format == GL_RGBA8 && - dsttex.internal_format == GL_RGBA8)); - // Cache scaling ratios - int srcWidth = srcReq.width(); - int srcHeight = srcReq.height(); - int dstWidth = dstReq.width(); - int dstHeight = dstReq.height(); - // Compute valid dest bounds - IntRect dstBounds = dsttex.sample_bounds(dstReq).intersect(clipRect); - // Compute valid source bounds - IntRect srcBounds = srctex.sample_bounds(srcReq, invertY); - // If srcReq is outside the source texture, we need to clip the sampling - // bounds so that we never sample outside valid source bounds. Get texture - // bounds relative to srcReq and scale to dest-space rounding inward, using - // this rect to limit the dest bounds further. - IntRect srcClip = srctex.bounds() - srcReq.origin(); - if (invertY) { - srcClip.invert_y(srcReq.height()); - } - srcClip.scale(srcWidth, srcHeight, dstWidth, dstHeight, true); - dstBounds.intersect(srcClip); - // Check if clipped sampling bounds are empty - if (dstBounds.is_empty()) { - return; - } - - // Calculate source and dest pointers from clamped offsets - int bpp = srctex.bpp(); - int srcStride = srctex.stride(); - int destStride = dsttex.stride(); - char* dest = dsttex.sample_ptr(dstReq, dstBounds); - // Clip the source bounds by the destination offset. - int fracX = srcWidth * dstBounds.x0; - int fracY = srcHeight * dstBounds.y0; - srcBounds.x0 = max(fracX / dstWidth, srcBounds.x0); - srcBounds.y0 = max(fracY / dstHeight, srcBounds.y0); - fracX %= dstWidth; - fracY %= dstHeight; - char* src = srctex.sample_ptr(srcReq, srcBounds, invertY); - // Inverted Y must step downward along source rows - if (invertY) { - srcStride = -srcStride; - } - int span = dstBounds.width(); - for (int rows = dstBounds.height(); rows > 0; rows--) { - switch (bpp) { - case 1: - if (srcWidth == dstWidth) - copy_row<COMPOSITE>((uint8_t*)dest, (uint8_t*)src, span); - else - scale_row<COMPOSITE>((uint8_t*)dest, dstWidth, (uint8_t*)src, - srcWidth, span, fracX); - break; - case 2: - if (srcWidth == dstWidth) - copy_row<COMPOSITE>((uint16_t*)dest, (uint16_t*)src, span); - else - scale_row<COMPOSITE>((uint16_t*)dest, dstWidth, (uint16_t*)src, - srcWidth, span, fracX); - break; - case 4: - if (srcWidth == dstWidth) - copy_row<COMPOSITE>((uint32_t*)dest, (uint32_t*)src, span); - else - scale_row<COMPOSITE>((uint32_t*)dest, dstWidth, (uint32_t*)src, - srcWidth, span, fracX); - break; - default: - assert(false); - break; - } - dest += destStride; - // Step source according to height ratio. - for (fracY += srcHeight; fracY >= dstHeight; fracY -= dstHeight) { - src += srcStride; - } - } -} - -template <bool COMPOSITE> -static void linear_row_blit(uint32_t* dest, int span, const vec2_scalar& srcUV, - float srcDU, sampler2D sampler) { - vec2 uv = init_interp(srcUV, vec2_scalar(srcDU, 0.0f)); - for (; span >= 4; span -= 4) { - auto srcpx = textureLinearPackedRGBA8(sampler, ivec2(uv)); - unaligned_store(dest, srcpx); - dest += 4; - uv.x += 4 * srcDU; - } - if (span > 0) { - auto srcpx = textureLinearPackedRGBA8(sampler, ivec2(uv)); - partial_store_span(dest, srcpx, span); - } -} - -template <> -void linear_row_blit<true>(uint32_t* dest, int span, const vec2_scalar& srcUV, - float srcDU, sampler2D sampler) { - vec2 uv = init_interp(srcUV, vec2_scalar(srcDU, 0.0f)); - for (; span >= 4; span -= 4) { - WideRGBA8 srcpx = textureLinearUnpackedRGBA8(sampler, ivec2(uv)); - WideRGBA8 dstpx = unpack(unaligned_load<PackedRGBA8>(dest)); - PackedRGBA8 r = pack(srcpx + dstpx - muldiv255(dstpx, alphas(srcpx))); - unaligned_store(dest, r); - - dest += 4; - uv.x += 4 * srcDU; - } - if (span > 0) { - WideRGBA8 srcpx = textureLinearUnpackedRGBA8(sampler, ivec2(uv)); - WideRGBA8 dstpx = unpack(partial_load_span<PackedRGBA8>(dest, span)); - PackedRGBA8 r = pack(srcpx + dstpx - muldiv255(dstpx, alphas(srcpx))); - partial_store_span(dest, r, span); - } -} - -template <bool COMPOSITE> -static void linear_row_blit(uint8_t* dest, int span, const vec2_scalar& srcUV, - float srcDU, sampler2D sampler) { - vec2 uv = init_interp(srcUV, vec2_scalar(srcDU, 0.0f)); - for (; span >= 4; span -= 4) { - auto srcpx = textureLinearPackedR8(sampler, ivec2(uv)); - unaligned_store(dest, srcpx); - dest += 4; - uv.x += 4 * srcDU; - } - if (span > 0) { - auto srcpx = textureLinearPackedR8(sampler, ivec2(uv)); - partial_store_span(dest, srcpx, span); - } -} - -template <bool COMPOSITE> -static void linear_row_blit(uint16_t* dest, int span, const vec2_scalar& srcUV, - float srcDU, sampler2D sampler) { - vec2 uv = init_interp(srcUV, vec2_scalar(srcDU, 0.0f)); - for (; span >= 4; span -= 4) { - auto srcpx = textureLinearPackedRG8(sampler, ivec2(uv)); - unaligned_store(dest, srcpx); - dest += 4; - uv.x += 4 * srcDU; - } - if (span > 0) { - auto srcpx = textureLinearPackedRG8(sampler, ivec2(uv)); - partial_store_span(dest, srcpx, span); - } -} - -template <bool COMPOSITE = false> -static NO_INLINE void linear_blit(Texture& srctex, const IntRect& srcReq, - Texture& dsttex, const IntRect& dstReq, - bool invertY, const IntRect& clipRect) { - assert(srctex.internal_format == GL_RGBA8 || - srctex.internal_format == GL_R8 || srctex.internal_format == GL_RG8); - assert(!COMPOSITE || (srctex.internal_format == GL_RGBA8 && - dsttex.internal_format == GL_RGBA8)); - // Compute valid dest bounds - IntRect dstBounds = dsttex.sample_bounds(dstReq); - dstBounds.intersect(clipRect); - // Check if sampling bounds are empty - if (dstBounds.is_empty()) { - return; - } - // Initialize sampler for source texture - sampler2D_impl sampler; - init_sampler(&sampler, srctex); - sampler.filter = TextureFilter::LINEAR; - // Compute source UVs - vec2_scalar srcUV(srcReq.x0, srcReq.y0); - vec2_scalar srcDUV(float(srcReq.width()) / dstReq.width(), - float(srcReq.height()) / dstReq.height()); - // Inverted Y must step downward along source rows - if (invertY) { - srcUV.y += srcReq.height(); - srcDUV.y = -srcDUV.y; - } - // Skip to clamped source start - srcUV += srcDUV * (vec2_scalar(dstBounds.x0, dstBounds.y0) + 0.5f); - // Scale UVs by lerp precision - srcUV = linearQuantize(srcUV, 128); - srcDUV *= 128.0f; - // Calculate dest pointer from clamped offsets - int bpp = dsttex.bpp(); - int destStride = dsttex.stride(); - char* dest = dsttex.sample_ptr(dstReq, dstBounds); - int span = dstBounds.width(); - for (int rows = dstBounds.height(); rows > 0; rows--) { - switch (bpp) { - case 1: - linear_row_blit<COMPOSITE>((uint8_t*)dest, span, srcUV, srcDUV.x, - &sampler); - break; - case 2: - linear_row_blit<COMPOSITE>((uint16_t*)dest, span, srcUV, srcDUV.x, - &sampler); - break; - case 4: - linear_row_blit<COMPOSITE>((uint32_t*)dest, span, srcUV, srcDUV.x, - &sampler); - break; - default: - assert(false); - break; - } - dest += destStride; - srcUV.y += srcDUV.y; - } -} - -extern "C" { - -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; - Texture& srctex = ctx->textures[srcfb->color_attachment]; - if (!srctex.buf) return; - Texture& dsttex = ctx->textures[dstfb->color_attachment]; - if (!dsttex.buf) return; - assert(!dsttex.locked); - if (srctex.internal_format != dsttex.internal_format) { - assert(false); - return; - } - // Force flipped Y onto dest coordinates - if (srcY1 < srcY0) { - swap(srcY0, srcY1); - swap(dstY0, dstY1); - } - bool invertY = dstY1 < dstY0; - if (invertY) { - swap(dstY0, dstY1); - } - IntRect srcReq = IntRect{srcX0, srcY0, srcX1, srcY1} - srctex.offset; - IntRect dstReq = IntRect{dstX0, dstY0, dstX1, dstY1} - dsttex.offset; - if (srcReq.is_empty() || dstReq.is_empty()) { - return; - } - IntRect clipRect = {0, 0, dstReq.width(), dstReq.height()}; - prepare_texture(srctex); - prepare_texture(dsttex, &dstReq); - if (!srcReq.same_size(dstReq) && srctex.width >= 2 && filter == GL_LINEAR && - (srctex.internal_format == GL_RGBA8 || srctex.internal_format == GL_R8 || - srctex.internal_format == GL_RG8)) { - linear_blit(srctex, srcReq, dsttex, dstReq, invertY, dstReq); - } else { - scale_blit(srctex, srcReq, dsttex, dstReq, invertY, clipRect); - } -} - -typedef Texture LockedTexture; - -// Lock the given texture to prevent modification. -LockedTexture* LockTexture(GLuint texId) { - Texture& tex = ctx->textures[texId]; - if (!tex.buf) { - assert(tex.buf != nullptr); - return nullptr; - } - if (__sync_fetch_and_add(&tex.locked, 1) == 0) { - // If this is the first time locking the texture, flush any delayed clears. - prepare_texture(tex); - } - return (LockedTexture*)&tex; -} - -// Lock the given framebuffer's color attachment to prevent modification. -LockedTexture* LockFramebuffer(GLuint fboId) { - Framebuffer& fb = ctx->framebuffers[fboId]; - // Only allow locking a framebuffer if it has a valid color attachment. - if (!fb.color_attachment) { - assert(fb.color_attachment != 0); - return nullptr; - } - return LockTexture(fb.color_attachment); -} - -// Reference an already locked resource -void LockResource(LockedTexture* resource) { - if (!resource) { - return; - } - __sync_fetch_and_add(&resource->locked, 1); -} - -// Remove a lock on a texture that has been previously locked -void UnlockResource(LockedTexture* resource) { - if (!resource) { - return; - } - if (__sync_fetch_and_add(&resource->locked, -1) <= 0) { - // The lock should always be non-zero before unlocking. - assert(0); - } -} - -// Get the underlying buffer for a locked resource -void* GetResourceBuffer(LockedTexture* resource, int32_t* width, - int32_t* height, int32_t* stride) { - *width = resource->width; - *height = resource->height; - *stride = resource->stride(); - return resource->buf; -} - -// Extension for optimized compositing of textures or framebuffers that may be -// safely used across threads. The source and destination must be locked to -// ensure that they can be safely accessed while the SWGL context might be used -// by another thread. Band extents along the Y axis may be used to clip the -// destination rectangle without effecting the integer scaling ratios. -void Composite(LockedTexture* lockedDst, LockedTexture* lockedSrc, GLint srcX, - GLint srcY, GLsizei srcWidth, GLsizei srcHeight, GLint dstX, - GLint dstY, GLsizei dstWidth, GLsizei dstHeight, - GLboolean opaque, GLboolean flip, GLenum filter, GLint clipX, - GLint clipY, GLsizei clipWidth, GLsizei clipHeight) { - if (!lockedDst || !lockedSrc) { - return; - } - Texture& srctex = *lockedSrc; - Texture& dsttex = *lockedDst; - assert(srctex.bpp() == 4); - assert(dsttex.bpp() == 4); - - IntRect srcReq = - IntRect{srcX, srcY, srcX + srcWidth, srcY + srcHeight} - srctex.offset; - IntRect dstReq = - IntRect{dstX, dstY, dstX + dstWidth, dstY + dstHeight} - dsttex.offset; - // Compute clip rect as relative to the dstReq, as that's the same coords - // as used for the sampling bounds. - IntRect clipRect = {clipX - dstX, clipY - dstY, clipX - dstX + clipWidth, - clipY - dstY + clipHeight}; - - if (opaque) { - // Ensure we have rows of at least 2 pixels when using the linear filter - // to avoid overreading the row. - if (!srcReq.same_size(dstReq) && srctex.width >= 2 && filter == GL_LINEAR) { - linear_blit<false>(srctex, srcReq, dsttex, dstReq, flip, clipRect); - } else { - scale_blit<false>(srctex, srcReq, dsttex, dstReq, flip, clipRect); - } - } else { - if (!srcReq.same_size(dstReq) && srctex.width >= 2 && filter == GL_LINEAR) { - linear_blit<true>(srctex, srcReq, dsttex, dstReq, flip, clipRect); - } else { - scale_blit<true>(srctex, srcReq, dsttex, dstReq, flip, clipRect); - } - } -} - -} // extern "C" - -// Saturated add helper for YUV conversion. Supported platforms have intrinsics -// to do this natively, but support a slower generic fallback just in case. -static inline V8<int16_t> addsat(V8<int16_t> x, V8<int16_t> y) { -#if USE_SSE2 - return _mm_adds_epi16(x, y); -#elif USE_NEON - return vqaddq_s16(x, y); -#else - auto r = x + y; - // An overflow occurred if the signs of both inputs x and y did not differ - // but yet the sign of the result did differ. - auto overflow = (~(x ^ y) & (r ^ x)) >> 15; - // If there was an overflow, we need to choose the appropriate limit to clamp - // to depending on whether or not the inputs are negative. - auto limit = (x >> 15) ^ 0x7FFF; - // If we didn't overflow, just use the result, and otherwise, use the limit. - return (~overflow & r) | (overflow & limit); -#endif -} - -// Interleave and packing helper for YUV conversion. During transform by the -// color matrix, the color components are de-interleaved as this format is -// usually what comes out of the planar YUV textures. The components thus need -// to be interleaved before finally getting packed to BGRA format. Alpha is -// forced to be opaque. -static inline PackedRGBA8 packYUV(V8<int16_t> gg, V8<int16_t> br) { - return pack(bit_cast<WideRGBA8>(zip(br, gg))) | - PackedRGBA8{0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255}; -} - -// clang-format off -// Supports YUV color matrixes of the form: -// [R] [1.1643835616438356, 0.0, rv ] [Y - 16] -// [G] = [1.1643835616438358, -gu, -gv ] x [U - 128] -// [B] [1.1643835616438356, bu, 0.0 ] [V - 128] -// We must be able to multiply a YUV input by a matrix coefficient ranging as -// high as ~2.2 in the U/V cases, where U/V can be signed values between -128 -// and 127. The largest fixed-point representation we can thus support without -// overflowing 16 bit integers leaves us 6 bits of fractional precision while -// also supporting a sign bit. The closest representation of the Y coefficient -// ~1.164 in this precision is 74.5/2^6 which is common to all color spaces -// we support. Conversions can still sometimes overflow the precision and -// require clamping back into range, so we use saturated additions to do this -// efficiently at no extra cost. -// clang-format on -struct YUVMatrix { - // These constants are loaded off the "this" pointer via relative addressing - // modes and should be about as quick to load as directly addressed SIMD - // constant memory. - V8<int16_t> rbCoeffs; - V8<int16_t> gCoeffs; - V8<uint16_t> yScale; - V8<int16_t> yBias; - V8<int16_t> uvBias; - V8<int16_t> brMask; - - // Set the coefficients to cancel out and pass through YUV as GBR. All biases - // are set to zero and the BR-mask is set to remove the contribution of Y to - // the BR channels. Scales are set such that the shift by 6 in convert is - // balanced. - YUVMatrix() - : rbCoeffs(1 << 6), - gCoeffs(0), - yScale(1 << (6 + 1)), - yBias(0), - uvBias(0), - brMask(0) {} - - // Convert matrix coefficients to fixed-point representation. - YUVMatrix(double rv, double gu, double gv, double bu) - : rbCoeffs( - zip(I16(int16_t(bu * 64.0 + 0.5)), I16(int16_t(rv * 64.0 + 0.5)))), - gCoeffs(zip(I16(-int16_t(gu * -64.0 + 0.5)), - I16(-int16_t(gv * -64.0 + 0.5)))), - yScale(2 * 74 + 1), - yBias(int16_t(-16 * 74.5) + (1 << 5)), - uvBias(-128), - brMask(-1) {} - - ALWAYS_INLINE PackedRGBA8 convert(V8<int16_t> yy, V8<int16_t> uv) const { - // Bias Y values by -16 and multiply by 74.5. Add 2^5 offset to round to - // nearest 2^6. Note that we have to use an unsigned multiply with a 2x - // scale to represent a fractional scale and to avoid shifting with the sign - // bit. - yy = bit_cast<V8<int16_t>>((bit_cast<V8<uint16_t>>(yy) * yScale) >> 1) + - yBias; - - // Bias U/V values by -128. - uv += uvBias; - - // Compute (R, B) = (74.5*Y + rv*V, 74.5*Y + bu*U) - auto br = rbCoeffs * uv; - br = addsat(yy & brMask, br); - br >>= 6; - - // Compute G = 74.5*Y + -gu*U + -gv*V - auto gg = gCoeffs * uv; - gg = addsat( - yy, - addsat(gg, bit_cast<V8<int16_t>>(bit_cast<V4<uint32_t>>(gg) >> 16))); - gg >>= 6; - - // Interleave B/R and G values. Force alpha to opaque. - return packYUV(gg, br); - } -}; - -enum YUVColorSpace { REC_601 = 0, REC_709, REC_2020, IDENTITY }; - -static const YUVMatrix yuvMatrix[IDENTITY + 1] = { - // clang-format off -// From Rec601: -// [R] [1.1643835616438356, 0.0, 1.5960267857142858 ] [Y - 16] -// [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708 ] x [U - 128] -// [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [V - 128] - {1.5960267857142858, -0.3917622900949137, -0.8129676472377708, 2.017232142857143}, - -// From Rec709: -// [R] [1.1643835616438356, 0.0, 1.7927410714285714] [Y - 16] -// [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444 ] x [U - 128] -// [B] [1.1643835616438356, 2.1124017857142854, 0.0 ] [V - 128] - {1.7927410714285714, -0.21324861427372963, -0.532909328559444, 2.1124017857142854}, - -// From Re2020: -// [R] [1.16438356164384, 0.0, 1.678674107142860 ] [Y - 16] -// [G] = [1.16438356164384, -0.187326104219343, -0.650424318505057 ] x [U - 128] -// [B] [1.16438356164384, 2.14177232142857, 0.0 ] [V - 128] - {1.678674107142860, -0.187326104219343, -0.650424318505057, 2.14177232142857}, - -// Identity -// [R] [V] -// [G] = [Y] -// [B] [U] - {}, - // clang-format on -}; - -// Helper function for textureLinearRowR8 that samples horizontal taps and -// combines them based on Y fraction with next row. -template <typename S> -static ALWAYS_INLINE V8<int16_t> linearRowTapsR8(S sampler, I32 ix, - int32_t offsety, - int32_t stridey, - int16_t fracy) { - uint8_t* buf = (uint8_t*)sampler->buf + offsety; - auto a0 = unaligned_load<V2<uint8_t>>(&buf[ix.x]); - auto b0 = unaligned_load<V2<uint8_t>>(&buf[ix.y]); - auto c0 = unaligned_load<V2<uint8_t>>(&buf[ix.z]); - auto d0 = unaligned_load<V2<uint8_t>>(&buf[ix.w]); - auto abcd0 = CONVERT(combine(a0, b0, c0, d0), V8<int16_t>); - buf += stridey; - auto a1 = unaligned_load<V2<uint8_t>>(&buf[ix.x]); - auto b1 = unaligned_load<V2<uint8_t>>(&buf[ix.y]); - auto c1 = unaligned_load<V2<uint8_t>>(&buf[ix.z]); - auto d1 = unaligned_load<V2<uint8_t>>(&buf[ix.w]); - auto abcd1 = CONVERT(combine(a1, b1, c1, d1), V8<int16_t>); - abcd0 += ((abcd1 - abcd0) * fracy) >> 7; - return abcd0; -} - -// Optimized version of textureLinearPackedR8 for Y R8 texture. This assumes -// constant Y and returns a duplicate of the result interleaved with itself -// to aid in later YUV transformation. -template <typename S> -static inline V8<int16_t> textureLinearRowR8(S sampler, I32 ix, int32_t offsety, - int32_t stridey, int16_t fracy) { - assert(sampler->format == TextureFormat::R8); - - // Calculate X fraction and clamp X offset into range. - I32 fracx = ix; - ix >>= 7; - fracx = ((fracx & (ix >= 0)) | (ix > int32_t(sampler->width) - 2)) & 0x7F; - ix = clampCoord(ix, sampler->width - 1); - - // Load the sample taps and combine rows. - auto abcd = linearRowTapsR8(sampler, ix, offsety, stridey, fracy); - - // Unzip the result and do final horizontal multiply-add base on X fraction. - auto abcdl = SHUFFLE(abcd, abcd, 0, 0, 2, 2, 4, 4, 6, 6); - auto abcdh = SHUFFLE(abcd, abcd, 1, 1, 3, 3, 5, 5, 7, 7); - abcdl += ((abcdh - abcdl) * CONVERT(fracx, I16).xxyyzzww) >> 7; - - // The final result is the packed values interleaved with a duplicate of - // themselves. - return abcdl; -} - -// Optimized version of textureLinearPackedR8 for paired U/V R8 textures. -// Since the two textures have the same dimensions and stride, the addressing -// math can be shared between both samplers. This also allows a coalesced -// multiply in the final stage by packing both U/V results into a single -// operation. -template <typename S> -static inline V8<int16_t> textureLinearRowPairedR8(S sampler, S sampler2, - I32 ix, int32_t offsety, - int32_t stridey, - int16_t fracy) { - assert(sampler->format == TextureFormat::R8 && - sampler2->format == TextureFormat::R8); - assert(sampler->width == sampler2->width && - sampler->height == sampler2->height); - assert(sampler->stride == sampler2->stride); - - // Calculate X fraction and clamp X offset into range. - I32 fracx = ix; - ix >>= 7; - fracx = ((fracx & (ix >= 0)) | (ix > int32_t(sampler->width) - 2)) & 0x7F; - ix = clampCoord(ix, sampler->width - 1); - - // Load the sample taps for the first sampler and combine rows. - auto abcd = linearRowTapsR8(sampler, ix, offsety, stridey, fracy); - - // Load the sample taps for the second sampler and combine rows. - auto xyzw = linearRowTapsR8(sampler2, ix, offsety, stridey, fracy); - - // We are left with a result vector for each sampler with values for adjacent - // pixels interleaved together in each. We need to unzip these values so that - // we can do the final horizontal multiply-add based on the X fraction. - auto abcdxyzwl = SHUFFLE(abcd, xyzw, 0, 8, 2, 10, 4, 12, 6, 14); - auto abcdxyzwh = SHUFFLE(abcd, xyzw, 1, 9, 3, 11, 5, 13, 7, 15); - abcdxyzwl += ((abcdxyzwh - abcdxyzwl) * CONVERT(fracx, I16).xxyyzzww) >> 7; - - // The final result is the packed values for the first sampler interleaved - // with the packed values for the second sampler. - return abcdxyzwl; -} - -// Casting to int loses some precision while stepping that can offset the -// image, so shift the values by some extra bits of precision to minimize -// this. We support up to 16 bits of image size, 7 bits of quantization, -// and 1 bit for sign, which leaves 8 bits left for extra precision. -const int STEP_BITS = 8; - -// Optimized version of textureLinearPackedR8 for Y R8 texture with -// half-resolution paired U/V R8 textures. This allows us to more efficiently -// pack YUV samples into vectors to substantially reduce math operations even -// further. -template <bool BLEND> -static inline void upscaleYUV42R8(uint32_t* dest, int span, uint8_t* yRow, - I32 yU, int32_t yDU, int32_t yStrideV, - int16_t yFracV, uint8_t* cRow1, - uint8_t* cRow2, I32 cU, int32_t cDU, - int32_t cStrideV, int16_t cFracV, - const YUVMatrix& colorSpace) { - // As much as possible try to utilize the fact that we're only using half - // the UV samples to combine Y and UV samples into single vectors. Here we - // need to initialize several useful vector quantities for stepping fractional - // offsets. For the UV samples, we take the average of the first+second and - // third+fourth samples in a chunk which conceptually correspond to offsets - // 0.5 and 1.5 (in 0..2 range). This allows us to reconstruct intermediate - // samples 0.25, 0.75, 1.25, and 1.75 later. X fraction is shifted over into - // the top 7 bits of an unsigned short so that we can mask off the exact - // fractional bits we need to blend merely by right shifting them into - // position. - cU = (cU.xzxz + cU.ywyw) >> 1; - auto ycFracX = CONVERT(combine(yU, cU), V8<uint16_t>) - << (16 - (STEP_BITS + 7)); - auto ycFracDX = combine(I16(yDU), I16(cDU)) << (16 - (STEP_BITS + 7)); - auto ycFracV = combine(I16(yFracV), I16(cFracV)); - I32 yI = yU >> (STEP_BITS + 7); - I32 cI = cU >> (STEP_BITS + 7); - // Load initial combined YUV samples for each row and blend them. - auto ycSrc0 = - CONVERT(combine(unaligned_load<V4<uint8_t>>(&yRow[yI.x]), - combine(unaligned_load<V2<uint8_t>>(&cRow1[cI.x]), - unaligned_load<V2<uint8_t>>(&cRow2[cI.x]))), - V8<int16_t>); - auto ycSrc1 = CONVERT( - combine(unaligned_load<V4<uint8_t>>(&yRow[yI.x + yStrideV]), - combine(unaligned_load<V2<uint8_t>>(&cRow1[cI.x + cStrideV]), - unaligned_load<V2<uint8_t>>(&cRow2[cI.x + cStrideV]))), - V8<int16_t>); - auto ycSrc = ycSrc0 + (((ycSrc1 - ycSrc0) * ycFracV) >> 7); - - // Here we shift in results from the next sample while caching results from - // the previous sample. This allows us to reduce the multiplications in the - // inner loop down to only two since we just need to blend the new samples - // horizontally and then vertically once each. - for (uint32_t* end = dest + span; dest < end; dest += 4) { - yU += yDU; - I32 yIn = yU >> (STEP_BITS + 7); - cU += cDU; - I32 cIn = cU >> (STEP_BITS + 7); - // Load combined YUV samples for the next chunk on each row and blend them. - auto ycSrc0n = - CONVERT(combine(unaligned_load<V4<uint8_t>>(&yRow[yIn.x]), - combine(unaligned_load<V2<uint8_t>>(&cRow1[cIn.x]), - unaligned_load<V2<uint8_t>>(&cRow2[cIn.x]))), - V8<int16_t>); - auto ycSrc1n = CONVERT( - combine(unaligned_load<V4<uint8_t>>(&yRow[yIn.x + yStrideV]), - combine(unaligned_load<V2<uint8_t>>(&cRow1[cIn.x + cStrideV]), - unaligned_load<V2<uint8_t>>(&cRow2[cIn.x + cStrideV]))), - V8<int16_t>); - auto ycSrcn = ycSrc0n + (((ycSrc1n - ycSrc0n) * ycFracV) >> 7); - - // The source samples for the chunk may not match the actual tap offsets. - // Since we're upscaling, we know the tap offsets fall within all the - // samples in a 4-wide chunk. Since we can't rely on PSHUFB or similar, - // instead we do laborious shuffling here for the Y samples and then the UV - // samples. - auto yshuf = lowHalf(ycSrc); - auto yshufn = - SHUFFLE(yshuf, yIn.x == yI.w ? lowHalf(ycSrcn).yyyy : lowHalf(ycSrcn), - 1, 2, 3, 4); - if (yI.y == yI.x) { - yshuf = yshuf.xxyz; - yshufn = yshufn.xxyz; - } - if (yI.z == yI.y) { - yshuf = yshuf.xyyz; - yshufn = yshufn.xyyz; - } - if (yI.w == yI.z) { - yshuf = yshuf.xyzz; - yshufn = yshufn.xyzz; - } - - auto cshuf = highHalf(ycSrc); - auto cshufn = - SHUFFLE(cshuf, cIn.x == cI.y ? highHalf(ycSrcn).yyww : highHalf(ycSrcn), - 1, 4, 3, 6); - if (cI.y == cI.x) { - cshuf = cshuf.xxzz; - cshufn = cshufn.xxzz; - } - - // After shuffling, combine the Y and UV samples back into a single vector - // for blending. Shift X fraction into position as unsigned to mask off top - // bits and get rid of low bits to avoid multiplication overflow. - auto yuvPx = combine(yshuf, cshuf); - yuvPx += ((combine(yshufn, cshufn) - yuvPx) * - bit_cast<V8<int16_t>>(ycFracX >> (16 - 7))) >> - 7; - - // Cache the new samples as the current samples on the next iteration. - ycSrc = ycSrcn; - ycFracX += ycFracDX; - yI = yIn; - cI = cIn; - - // De-interleave the Y and UV results. We need to average the UV results - // to produce values for intermediate samples. Taps for UV were collected at - // offsets 0.5 and 1.5, such that if we take a quarter of the difference - // (1.5-0.5)/4, subtract it from even samples, and add it to odd samples, - // we can estimate samples 0.25, 0.75, 1.25, and 1.75. - auto yPx = SHUFFLE(yuvPx, yuvPx, 0, 0, 1, 1, 2, 2, 3, 3); - auto uvPx = SHUFFLE(yuvPx, yuvPx, 4, 6, 4, 6, 5, 7, 5, 7) + - ((SHUFFLE(yuvPx, yuvPx, 4, 6, 5, 7, 4, 6, 5, 7) - - SHUFFLE(yuvPx, yuvPx, 5, 7, 4, 6, 5, 7, 4, 6)) >> - 2); - - commit_blend_span<BLEND>(dest, colorSpace.convert(yPx, uvPx)); - } -} - -// This is the inner loop driver of CompositeYUV that processes an axis-aligned -// YUV span, dispatching based on appropriate format and scaling. This is also -// reused by blendYUV to accelerate some cases of texture sampling in the -// shader. -template <bool BLEND = false> -static void linear_row_yuv(uint32_t* dest, int span, sampler2DRect samplerY, - const vec2_scalar& srcUV, float srcDU, - sampler2DRect samplerU, sampler2DRect samplerV, - const vec2_scalar& chromaUV, float chromaDU, - int colorDepth, const YUVMatrix& colorSpace) { - // Calculate varying and constant interp data for Y plane. - I32 yU = cast(init_interp(srcUV.x, srcDU) * (1 << STEP_BITS)); - int32_t yV = int32_t(srcUV.y); - - // Calculate varying and constant interp data for chroma planes. - I32 cU = cast(init_interp(chromaUV.x, chromaDU) * (1 << STEP_BITS)); - int32_t cV = int32_t(chromaUV.y); - - // We need to skip 4 pixels per chunk. - int32_t yDU = int32_t((4 << STEP_BITS) * srcDU); - int32_t cDU = int32_t((4 << STEP_BITS) * chromaDU); - - if (samplerY->width < 2 || samplerU->width < 2) { - // If the source row has less than 2 pixels, it's not safe to use a linear - // filter because it may overread the row. Just convert the single pixel - // with nearest filtering and fill the row with it. - I16 yuv = CONVERT( - round_pixel((Float){texelFetch(samplerY, ivec2(srcUV)).x.x, - texelFetch(samplerU, ivec2(chromaUV)).x.x, - texelFetch(samplerV, ivec2(chromaUV)).x.x, 1.0f}), - I16); - commit_solid_span<BLEND>( - dest, - unpack(colorSpace.convert(V8<int16_t>(yuv.x), - zip(I16(yuv.y), I16(yuv.z)))), - span); - } else if (samplerY->format == TextureFormat::R16) { - // Sample each YUV plane, rescale it to fit in low 8 bits of word, and - // then transform them by the appropriate color space. - assert(colorDepth > 8); - // Need to right shift the sample by the amount of bits over 8 it - // occupies. On output from textureLinearUnpackedR16, we have lost 1 bit - // of precision at the low end already, hence 1 is subtracted from the - // color depth. - int rescaleBits = (colorDepth - 1) - 8; - for (; span >= 4; span -= 4) { - auto yPx = - textureLinearUnpackedR16(samplerY, ivec2(yU >> STEP_BITS, yV)) >> - rescaleBits; - auto uPx = - textureLinearUnpackedR16(samplerU, ivec2(cU >> STEP_BITS, cV)) >> - rescaleBits; - auto vPx = - textureLinearUnpackedR16(samplerV, ivec2(cU >> STEP_BITS, cV)) >> - rescaleBits; - commit_blend_span<BLEND>( - dest, colorSpace.convert(zip(yPx, yPx), zip(uPx, vPx))); - dest += 4; - yU += yDU; - cU += cDU; - } - if (span > 0) { - // Handle any remaining pixels... - auto yPx = - textureLinearUnpackedR16(samplerY, ivec2(yU >> STEP_BITS, yV)) >> - rescaleBits; - auto uPx = - textureLinearUnpackedR16(samplerU, ivec2(cU >> STEP_BITS, cV)) >> - rescaleBits; - auto vPx = - textureLinearUnpackedR16(samplerV, ivec2(cU >> STEP_BITS, cV)) >> - rescaleBits; - commit_blend_span<BLEND>( - dest, colorSpace.convert(zip(yPx, yPx), zip(uPx, vPx)), span); - } - } else { - assert(samplerY->format == TextureFormat::R8); - assert(colorDepth == 8); - - // Calculate varying and constant interp data for Y plane. - int16_t yFracV = yV & 0x7F; - yV >>= 7; - int32_t yOffsetV = clampCoord(yV, samplerY->height) * samplerY->stride; - int32_t yStrideV = - yV >= 0 && yV < int32_t(samplerY->height) - 1 ? samplerY->stride : 0; - - // Calculate varying and constant interp data for chroma planes. - int16_t cFracV = cV & 0x7F; - cV >>= 7; - int32_t cOffsetV = clampCoord(cV, samplerU->height) * samplerU->stride; - int32_t cStrideV = - cV >= 0 && cV < int32_t(samplerU->height) - 1 ? samplerU->stride : 0; - - // If we're sampling the UV planes at half the resolution of the Y plane, - // then try to use half resolution fast-path. - if (yDU >= cDU && cDU > 0 && yDU <= (4 << (STEP_BITS + 7)) && - cDU <= (2 << (STEP_BITS + 7))) { - // Ensure that samples don't fall outside of the valid bounds of each - // planar texture. Step until the initial X coordinates are positive. - for (; (yU.x < 0 || cU.x < 0) && span >= 4; span -= 4) { - auto yPx = textureLinearRowR8(samplerY, yU >> STEP_BITS, yOffsetV, - yStrideV, yFracV); - auto uvPx = textureLinearRowPairedR8( - samplerU, samplerV, cU >> STEP_BITS, cOffsetV, cStrideV, cFracV); - commit_blend_span<BLEND>(dest, colorSpace.convert(yPx, uvPx)); - dest += 4; - yU += yDU; - cU += cDU; - } - // Calculate the number of aligned chunks that we can step inside the - // bounds of each planar texture without overreading. - int inside = min( - min((((int(samplerY->width) - 4) << (STEP_BITS + 7)) - yU.x) / yDU, - (((int(samplerU->width) - 4) << (STEP_BITS + 7)) - cU.x) / cDU) * - 4, - span & ~3); - if (inside > 0) { - uint8_t* yRow = (uint8_t*)samplerY->buf + yOffsetV; - uint8_t* cRow1 = (uint8_t*)samplerU->buf + cOffsetV; - uint8_t* cRow2 = (uint8_t*)samplerV->buf + cOffsetV; - upscaleYUV42R8<BLEND>(dest, inside, yRow, yU, yDU, yStrideV, yFracV, - cRow1, cRow2, cU, cDU, cStrideV, cFracV, - colorSpace); - span -= inside; - dest += inside; - yU += (inside / 4) * yDU; - cU += (inside / 4) * cDU; - } - // If there are any remaining chunks that weren't inside, handle them - // below. - } - for (; span >= 4; span -= 4) { - // Sample each YUV plane and then transform them by the appropriate - // color space. - auto yPx = textureLinearRowR8(samplerY, yU >> STEP_BITS, yOffsetV, - yStrideV, yFracV); - auto uvPx = textureLinearRowPairedR8(samplerU, samplerV, cU >> STEP_BITS, - cOffsetV, cStrideV, cFracV); - commit_blend_span<BLEND>(dest, colorSpace.convert(yPx, uvPx)); - dest += 4; - yU += yDU; - cU += cDU; - } - if (span > 0) { - // Handle any remaining pixels... - auto yPx = textureLinearRowR8(samplerY, yU >> STEP_BITS, yOffsetV, - yStrideV, yFracV); - auto uvPx = textureLinearRowPairedR8(samplerU, samplerV, cU >> STEP_BITS, - cOffsetV, cStrideV, cFracV); - commit_blend_span<BLEND>(dest, colorSpace.convert(yPx, uvPx), span); - } - } -} - -static void linear_convert_yuv(Texture& ytex, Texture& utex, Texture& vtex, - YUVColorSpace colorSpace, int colorDepth, - const IntRect& srcReq, Texture& dsttex, - const IntRect& dstReq, bool invertY, - const IntRect& clipRect) { - // Compute valid dest bounds - IntRect dstBounds = dsttex.sample_bounds(dstReq, invertY); - dstBounds.intersect(clipRect); - // Check if sampling bounds are empty - if (dstBounds.is_empty()) { - return; - } - // Initialize samplers for source textures - sampler2DRect_impl sampler[3]; - init_sampler(&sampler[0], ytex); - init_sampler(&sampler[1], utex); - init_sampler(&sampler[2], vtex); - - // Compute source UVs - vec2_scalar srcUV(srcReq.x0, srcReq.y0); - vec2_scalar srcDUV(float(srcReq.width()) / dstReq.width(), - float(srcReq.height()) / dstReq.height()); - // Inverted Y must step downward along source rows - if (invertY) { - srcUV.y += srcReq.height(); - srcDUV.y = -srcDUV.y; - } - // Skip to clamped source start - srcUV += srcDUV * (vec2_scalar(dstBounds.x0, dstBounds.y0) + 0.5f); - // Calculate separate chroma UVs for chroma planes with different scale - vec2_scalar chromaScale(float(utex.width) / ytex.width, - float(utex.height) / ytex.height); - vec2_scalar chromaUV = srcUV * chromaScale; - vec2_scalar chromaDUV = srcDUV * chromaScale; - // Scale UVs by lerp precision. If the row has only 1 pixel, then don't - // quantize so that we can use nearest filtering instead to avoid overreads. - if (ytex.width >= 2 && utex.width >= 2) { - srcUV = linearQuantize(srcUV, 128); - srcDUV *= 128.0f; - chromaUV = linearQuantize(chromaUV, 128); - chromaDUV *= 128.0f; - } - // Calculate dest pointer from clamped offsets - int destStride = dsttex.stride(); - char* dest = dsttex.sample_ptr(dstReq, dstBounds); - int span = dstBounds.width(); - for (int rows = dstBounds.height(); rows > 0; rows--) { - linear_row_yuv((uint32_t*)dest, span, &sampler[0], srcUV, srcDUV.x, - &sampler[1], &sampler[2], chromaUV, chromaDUV.x, colorDepth, - yuvMatrix[colorSpace]); - dest += destStride; - srcUV.y += srcDUV.y; - chromaUV.y += chromaDUV.y; - } -} - -extern "C" { - -// Extension for compositing a YUV surface represented by separate YUV planes -// to a BGRA destination. The supplied color space is used to determine the -// transform from YUV to BGRA after sampling. -void CompositeYUV(LockedTexture* lockedDst, LockedTexture* lockedY, - LockedTexture* lockedU, LockedTexture* lockedV, - YUVColorSpace colorSpace, GLuint colorDepth, GLint srcX, - GLint srcY, GLsizei srcWidth, GLsizei srcHeight, GLint dstX, - GLint dstY, GLsizei dstWidth, GLsizei dstHeight, - GLboolean flip, GLint clipX, GLint clipY, GLsizei clipWidth, - GLsizei clipHeight) { - if (!lockedDst || !lockedY || !lockedU || !lockedV) { - return; - } - if (colorSpace > IDENTITY) { - assert(false); - return; - } - Texture& ytex = *lockedY; - Texture& utex = *lockedU; - Texture& vtex = *lockedV; - Texture& dsttex = *lockedDst; - // All YUV planes must currently be represented by R8 or R16 textures. - // The chroma (U/V) planes must have matching dimensions. - assert(ytex.bpp() == utex.bpp() && ytex.bpp() == vtex.bpp()); - assert((ytex.bpp() == 1 && colorDepth == 8) || - (ytex.bpp() == 2 && colorDepth > 8)); - // assert(ytex.width == utex.width && ytex.height == utex.height); - assert(utex.width == vtex.width && utex.height == vtex.height); - assert(ytex.offset == utex.offset && ytex.offset == vtex.offset); - assert(dsttex.bpp() == 4); - - IntRect srcReq = - IntRect{srcX, srcY, srcX + srcWidth, srcY + srcHeight} - ytex.offset; - IntRect dstReq = - IntRect{dstX, dstY, dstX + dstWidth, dstY + dstHeight} - dsttex.offset; - // Compute clip rect as relative to the dstReq, as that's the same coords - // as used for the sampling bounds. - IntRect clipRect = {clipX - dstX, clipY - dstY, clipX - dstX + clipWidth, - clipY - dstY + clipHeight}; - // For now, always use a linear filter path that would be required for - // scaling. Further fast-paths for non-scaled video might be desirable in the - // future. - linear_convert_yuv(ytex, utex, vtex, colorSpace, colorDepth, srcReq, dsttex, - dstReq, flip, clipRect); -} - -} // extern "C" diff --git a/third_party/webrender/swgl/src/gl.cc b/third_party/webrender/swgl/src/gl.cc index 6e214547421..f4a69752dde 100644 --- a/third_party/webrender/swgl/src/gl.cc +++ b/third_party/webrender/swgl/src/gl.cc @@ -22,65 +22,15 @@ # define debugf(...) printf(__VA_ARGS__) #endif -// #define PRINT_TIMINGS - #ifdef _WIN32 # define ALWAYS_INLINE __forceinline -# define NO_INLINE __declspec(noinline) - -// Including Windows.h brings a huge amount of namespace polution so just -// define a couple of things manually -typedef int BOOL; -# define WINAPI __stdcall -# define DECLSPEC_IMPORT __declspec(dllimport) -# define WINBASEAPI DECLSPEC_IMPORT -typedef unsigned long DWORD; -typedef long LONG; -typedef __int64 LONGLONG; -# define DUMMYSTRUCTNAME - -typedef union _LARGE_INTEGER { - struct { - DWORD LowPart; - LONG HighPart; - } DUMMYSTRUCTNAME; - struct { - DWORD LowPart; - LONG HighPart; - } u; - LONGLONG QuadPart; -} LARGE_INTEGER; -extern "C" { -WINBASEAPI BOOL WINAPI -QueryPerformanceCounter(LARGE_INTEGER* lpPerformanceCount); - -WINBASEAPI BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency); -} - #else -// GCC is slower when dealing with always_inline, especially in debug builds. -// When using Clang, use always_inline more aggressively. -# if defined(__clang__) || defined(NDEBUG) -# define ALWAYS_INLINE __attribute__((always_inline)) inline -# else -# define ALWAYS_INLINE inline -# endif -# define NO_INLINE __attribute__((noinline)) -#endif - -// Some functions may cause excessive binary bloat if inlined in debug or with -// GCC builds, so use PREFER_INLINE on these instead of ALWAYS_INLINE. -#if defined(__clang__) && defined(NDEBUG) -# define PREFER_INLINE ALWAYS_INLINE -#else -# define PREFER_INLINE inline +# define ALWAYS_INLINE __attribute__((always_inline)) inline #endif #define UNREACHABLE __builtin_unreachable() -#define UNUSED [[maybe_unused]] - -#define FALLTHROUGH [[fallthrough]] +#define UNUSED __attribute__((unused)) #ifdef MOZILLA_CLIENT # define IMPLICIT __attribute__((annotate("moz_implicit"))) @@ -91,32 +41,19 @@ WINBASEAPI BOOL WINAPI QueryPerformanceFrequency(LARGE_INTEGER* lpFrequency); #include "gl_defs.h" #include "glsl.h" #include "program.h" -#include "texture.h" using namespace glsl; -typedef ivec2_scalar IntPoint; - struct IntRect { int x0; int y0; int x1; int y1; - IntRect() : x0(0), y0(0), x1(0), y1(0) {} - IntRect(int x0, int y0, int x1, int y1) : x0(x0), y0(y0), x1(x1), y1(y1) {} - IntRect(IntPoint origin, IntPoint size) - : x0(origin.x), - y0(origin.y), - x1(origin.x + size.x), - y1(origin.y + size.y) {} - int width() const { return x1 - x0; } int height() const { return y1 - y0; } bool is_empty() const { return width() <= 0 || height() <= 0; } - IntPoint origin() const { return IntPoint(x0, y0); } - bool same_size(const IntRect& o) const { return width() == o.width() && height() == o.height(); } @@ -133,12 +70,6 @@ struct IntRect { return *this; } - IntRect intersection(const IntRect& o) { - IntRect result = *this; - result.intersect(o); - return result; - } - // Scale from source-space to dest-space, optionally rounding inward IntRect& scale(int srcWidth, int srcHeight, int dstWidth, int dstHeight, bool roundIn = false) { @@ -156,60 +87,15 @@ struct IntRect { swap(y0, y1); } - IntRect& offset(const IntPoint& o) { - x0 += o.x; - y0 += o.y; - x1 += o.x; - y1 += o.y; + IntRect& offset(int dx, int dy) { + x0 += dx; + y0 += dy; + x1 += dx; + y1 += dy; return *this; } - - IntRect operator+(const IntPoint& o) const { - return IntRect(*this).offset(o); - } - IntRect operator-(const IntPoint& o) const { - return IntRect(*this).offset(-o); - } }; -typedef vec2_scalar Point2D; -typedef vec4_scalar Point3D; - -struct IntRange { - int start; - int end; - - int len() const { return end - start; } - - IntRange intersect(IntRange r) const { - return {max(start, r.start), min(end, r.end)}; - } -}; - -struct FloatRange { - float start; - float end; - - float clip(float x) const { return clamp(x, start, end); } - - FloatRange clip(FloatRange r) const { return {clip(r.start), clip(r.end)}; } - - FloatRange merge(FloatRange r) const { - return {min(start, r.start), max(end, r.end)}; - } - - IntRange round() const { - return {int(floor(start + 0.5f)), int(floor(end + 0.5f))}; - } - - IntRange round_out() const { return {int(floor(start)), int(ceil(end))}; } -}; - -template <typename P> -static inline FloatRange x_range(P p0, P p1) { - return {min(p0.x, p1.x), max(p0.x, p1.x)}; -} - struct VertexAttrib { size_t size = 0; // in bytes GLenum type = 0; @@ -237,18 +123,12 @@ static int bytes_for_internal_format(GLenum internal_format) { case GL_R8: case GL_RED: return 1; - case GL_RG8: - case GL_RG: - return 2; case GL_DEPTH_COMPONENT: case GL_DEPTH_COMPONENT16: + return 2; case GL_DEPTH_COMPONENT24: case GL_DEPTH_COMPONENT32: return 4; - case GL_RGB_RAW_422_APPLE: - return 2; - case GL_R16: - return 2; default: debugf("internal format: %x\n", internal_format); assert(0); @@ -268,12 +148,6 @@ static TextureFormat gl_format_to_texture_format(int type) { return TextureFormat::RGBA8; case GL_R8: return TextureFormat::R8; - case GL_RG8: - return TextureFormat::RG8; - case GL_R16: - return TextureFormat::R16; - case GL_RGB_RAW_422_APPLE: - return TextureFormat::YUV422; default: assert(0); return TextureFormat::RGBA8; @@ -287,34 +161,19 @@ struct Query { struct Buffer { char* buf = nullptr; size_t size = 0; - size_t capacity = 0; bool allocate(size_t new_size) { - // If the size remains unchanged, don't allocate anything. - if (new_size == size) { - return false; - } - // If the new size is within the existing capacity of the buffer, just - // reuse the existing buffer. - if (new_size <= capacity) { - size = new_size; - return true; - } - // Otherwise we need to reallocate the buffer to hold up to the requested - // larger size. - char* new_buf = (char*)realloc(buf, new_size); - assert(new_buf); - if (!new_buf) { - // If we fail, null out the buffer rather than leave around the old - // allocation state. + 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; } - // The reallocation succeeded, so install the buffer. - buf = new_buf; - size = new_size; - capacity = new_size; - return true; + return false; } void cleanup() { @@ -322,7 +181,6 @@ struct Buffer { free(buf); buf = nullptr; size = 0; - capacity = 0; } } @@ -331,6 +189,7 @@ struct Buffer { struct Framebuffer { GLuint color_attachment = 0; + GLint layer = 0; GLuint depth_attachment = 0; }; @@ -364,32 +223,17 @@ struct Texture { GLenum internal_format = 0; int width = 0; int height = 0; + int depth = 0; char* buf = nullptr; size_t buf_size = 0; - uint32_t buf_stride = 0; - uint8_t buf_bpp = 0; GLenum min_filter = GL_NEAREST; GLenum mag_filter = GL_LINEAR; - // The number of active locks on this texture. If this texture has any active - // locks, we need to disallow modifying or destroying the texture as it may - // be accessed by other threads where modifications could lead to races. - int32_t locked = 0; - // When used as an attachment of a framebuffer, rendering to the texture - // behaves as if it is located at the given offset such that the offset is - // subtracted from all transformed vertexes after the viewport is applied. - IntPoint offset; enum FLAGS { - // If the buffer is internally-allocated by SWGL SHOULD_FREE = 1 << 1, - // If the buffer has been cleared to initialize it. Currently this is only - // utilized by depth buffers which need to know when depth runs have reset - // to a valid row state. When unset, the depth runs may contain garbage. - CLEARED = 1 << 2, }; int flags = SHOULD_FREE; bool should_free() const { return bool(flags & SHOULD_FREE); } - bool cleared() const { return bool(flags & CLEARED); } void set_flag(int flag, bool val) { if (val) { @@ -398,14 +242,7 @@ struct Texture { flags &= ~flag; } } - void set_should_free(bool val) { - // buf must be null before SHOULD_FREE can be safely toggled. Otherwise, we - // might accidentally mistakenly realloc an externally allocated buffer as - // if it were an internally allocated one. - assert(!buf); - set_flag(SHOULD_FREE, val); - } - void set_cleared(bool val) { set_flag(CLEARED, val); } + void set_should_free(bool val) { set_flag(SHOULD_FREE, val); } // Delayed-clearing state. When a clear of an FB is requested, we don't // immediately clear each row, as the rows may be subsequently overwritten @@ -418,9 +255,6 @@ struct Texture { uint32_t clear_val = 0; uint32_t* cleared_rows = nullptr; - void init_depth_runs(uint32_t z); - void fill_depth_runs(uint32_t z, const IntRect& scissor); - void enable_delayed_clear(uint32_t val) { delay_clear = height; clear_val = val; @@ -441,88 +275,40 @@ struct Texture { } } - int bpp() const { return buf_bpp; } - void set_bpp() { buf_bpp = bytes_for_internal_format(internal_format); } + int bpp() const { return bytes_for_internal_format(internal_format); } - size_t stride() const { return buf_stride; } - void set_stride() { buf_stride = aligned_stride(buf_bpp * width); } - - // Set an external backing buffer of this texture. - void set_buffer(void* new_buf, size_t new_stride) { - assert(!should_free()); - // Ensure that the supplied stride is at least as big as the row data and - // is aligned to the smaller of either the BPP or word-size. We need to at - // least be able to sample data from within a row and sample whole pixels - // of smaller formats without risking unaligned access. - set_bpp(); - set_stride(); - assert(new_stride >= size_t(bpp() * width) && - new_stride % min(bpp(), sizeof(uint32_t)) == 0); + size_t stride(int b = 0, int min_width = 0) const { + return aligned_stride((b ? b : bpp()) * max(width, min_width)); + } - buf = (char*)new_buf; - buf_size = 0; - buf_stride = new_stride; + 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) { - assert(!locked); // Locked textures shouldn't be reallocated - // If we get here, some GL API call that invalidates the texture was used. - // Mark the buffer as not-cleared to signal this. - set_cleared(false); - // Check if there is either no buffer currently or if we forced validation - // of the buffer size because some dimension might have changed. if ((!buf || force) && should_free()) { - // Initialize the buffer's BPP and stride, since they may have changed. - set_bpp(); - set_stride(); - // Compute new size based on the maximum potential stride, rather than - // the current stride, to hopefully avoid reallocations when size would - // otherwise change too much... - size_t max_stride = max(buf_stride, aligned_stride(buf_bpp * min_width)); - size_t size = max_stride * max(height, min_height); - if ((!buf && size > 0) || size > buf_size) { + size_t size = layer_stride(bpp(), min_width, min_height) * max(depth, 1); + if (!buf || size > buf_size) { // Allocate with a SIMD register-sized tail of padding at the end so we // can safely read or write past the end of the texture with SIMD ops. - // Currently only the flat Z-buffer texture needs this padding due to - // full-register loads and stores in check_depth and discard_depth. In - // case some code in the future accidentally uses a linear filter on a - // texture with less than 2 pixels per row, we also add this padding - // just to be safe. All other texture types and use-cases should be - // safe to omit padding. - size_t padding = - internal_format == GL_DEPTH_COMPONENT24 || max(width, min_width) < 2 - ? sizeof(Float) - : 0; - char* new_buf = (char*)realloc(buf, size + padding); + char* new_buf = (char*)realloc(buf, size + sizeof(Float)); assert(new_buf); if (new_buf) { - // Successfully reallocated the buffer, so go ahead and set it. buf = new_buf; buf_size = size; return true; } - // Allocation failed, so ensure we don't leave stale buffer state. cleanup(); } } - // Nothing changed... return false; } void cleanup() { - assert(!locked); // Locked textures shouldn't be destroyed - if (buf) { - // If we need to toggle SHOULD_FREE state, ensure that buf is nulled out, - // regardless of whether we internally allocated it. This will prevent us - // from wrongly treating buf as having been internally allocated for when - // we go to realloc if it actually was externally allocted. - if (should_free()) { - free(buf); - } + if (buf && should_free()) { + free(buf); buf = nullptr; buf_size = 0; - buf_bpp = 0; - buf_stride = 0; } disable_delayed_clear(); } @@ -530,41 +316,44 @@ struct Texture { ~Texture() { cleanup(); } IntRect bounds() const { return IntRect{0, 0, width, height}; } - IntRect offset_bounds() const { return bounds() + offset; } // Find the valid sampling bounds relative to the requested region IntRect sample_bounds(const IntRect& req, bool invertY = false) const { - IntRect bb = bounds().intersect(req) - req.origin(); + IntRect bb = bounds().intersect(req).offset(-req.x0, -req.y0); if (invertY) bb.invert_y(req.height()); return bb; } // Get a pointer for sampling at the given offset - char* sample_ptr(int x, int y) const { - return buf + y * stride() + x * bpp(); + char* sample_ptr(int x, int y, int z, int bpp, size_t stride) const { + return buf + (height * z + y) * stride + x * bpp; + } + + char* sample_ptr(int x, int y, int z, int bpp) const { + return sample_ptr(x, y, z, bpp, stride(bpp)); + } + + char* sample_ptr(int x, int y, int z) const { + return sample_ptr(x, y, z, bpp()); } // Get a pointer for sampling the requested region and limit to the provided // sampling bounds - char* sample_ptr(const IntRect& req, const IntRect& bounds, + char* sample_ptr(const IntRect& req, const IntRect& bounds, int z, bool invertY = false) const { // Offset the sample pointer by the clamped bounds int x = req.x0 + bounds.x0; // Invert the Y offset if necessary int y = invertY ? req.y1 - 1 - bounds.y0 : req.y0 + bounds.y0; - return sample_ptr(x, y); + return sample_ptr(x, y, z); } }; -// The last vertex attribute is reserved as a null attribute in case a vertex -// attribute is used without being set. -#define MAX_ATTRIBS 17 -#define NULL_ATTRIB 16 +#define MAX_ATTRIBS 16 +#define NULL_ATTRIB 15 struct VertexArray { VertexAttrib attribs[MAX_ATTRIBS]; int max_attrib = -1; - // The GL spec defines element array buffer binding to be part of VAO state. - GLuint element_array_buffer_binding = 0; void validate(); }; @@ -580,67 +369,33 @@ struct Program { FragmentShaderImpl* frag_impl = nullptr; bool deleted = false; - ~Program() { delete impl; } + ~Program() { + delete impl; + } }; -// clang-format off -// Fully-expand GL defines while ignoring more than 4 suffixes +// for GL defines to fully expand #define CONCAT_KEY(prefix, x, y, z, w, ...) prefix##x##y##z##w -// Generate a blend key enum symbol -#define BLEND_KEY(...) CONCAT_KEY(BLEND_, __VA_ARGS__, 0, 0, 0) -#define MASK_BLEND_KEY(...) CONCAT_KEY(MASK_BLEND_, __VA_ARGS__, 0, 0, 0) -#define AA_BLEND_KEY(...) CONCAT_KEY(AA_BLEND_, __VA_ARGS__, 0, 0, 0) -#define AA_MASK_BLEND_KEY(...) CONCAT_KEY(AA_MASK_BLEND_, __VA_ARGS__, 0, 0, 0) - -// Utility macro to easily generate similar code for all implemented blend modes +#define BLEND_KEY(...) CONCAT_KEY(BLEND_, __VA_ARGS__, 0, 0) #define FOR_EACH_BLEND_KEY(macro) \ - macro(GL_ONE, GL_ZERO, 0, 0) \ - macro(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA) \ - 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_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) \ - macro(GL_MIN, 0, 0, 0) \ - macro(GL_MAX, 0, 0, 0) \ - macro(GL_MULTIPLY_KHR, 0, 0, 0) \ - macro(GL_SCREEN_KHR, 0, 0, 0) \ - macro(GL_OVERLAY_KHR, 0, 0, 0) \ - macro(GL_DARKEN_KHR, 0, 0, 0) \ - macro(GL_LIGHTEN_KHR, 0, 0, 0) \ - macro(GL_COLORDODGE_KHR, 0, 0, 0) \ - macro(GL_COLORBURN_KHR, 0, 0, 0) \ - macro(GL_HARDLIGHT_KHR, 0, 0, 0) \ - macro(GL_SOFTLIGHT_KHR, 0, 0, 0) \ - macro(GL_DIFFERENCE_KHR, 0, 0, 0) \ - macro(GL_EXCLUSION_KHR, 0, 0, 0) \ - macro(GL_HSL_HUE_KHR, 0, 0, 0) \ - macro(GL_HSL_SATURATION_KHR, 0, 0, 0) \ - macro(GL_HSL_COLOR_KHR, 0, 0, 0) \ - macro(GL_HSL_LUMINOSITY_KHR, 0, 0, 0) \ - macro(SWGL_BLEND_DROP_SHADOW, 0, 0, 0) \ - macro(SWGL_BLEND_SUBPIXEL_TEXT, 0, 0, 0) + 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__), -#define DEFINE_MASK_BLEND_KEY(...) MASK_BLEND_KEY(__VA_ARGS__), -#define DEFINE_AA_BLEND_KEY(...) AA_BLEND_KEY(__VA_ARGS__), -#define DEFINE_AA_MASK_BLEND_KEY(...) AA_MASK_BLEND_KEY(__VA_ARGS__), enum BlendKey : uint8_t { + BLEND_KEY_NONE = 0, FOR_EACH_BLEND_KEY(DEFINE_BLEND_KEY) - FOR_EACH_BLEND_KEY(DEFINE_MASK_BLEND_KEY) - FOR_EACH_BLEND_KEY(DEFINE_AA_BLEND_KEY) - FOR_EACH_BLEND_KEY(DEFINE_AA_MASK_BLEND_KEY) - BLEND_KEY_NONE = BLEND_KEY(GL_ONE, GL_ZERO), - MASK_BLEND_KEY_NONE = MASK_BLEND_KEY(GL_ONE, GL_ZERO), - AA_BLEND_KEY_NONE = AA_BLEND_KEY(GL_ONE, GL_ZERO), - AA_MASK_BLEND_KEY_NONE = AA_MASK_BLEND_KEY(GL_ONE, GL_ZERO), }; -// clang-format on const size_t MAX_TEXTURE_UNITS = 16; @@ -704,10 +459,8 @@ struct ObjectStore { O* find(size_t i) const { return i < size ? objects[i] : nullptr; } - template <typename T> - void on_erase(T*, ...) {} - template <typename T> - void on_erase(T* o, decltype(&T::on_erase)) { + template <typename T> void on_erase(T*, ...) {} + template <typename T> void on_erase(T* o, decltype(&T::on_erase)) { o->on_erase(); } @@ -727,8 +480,6 @@ struct ObjectStore { }; struct Context { - int32_t references = 1; - ObjectStore<Query> queries; ObjectStore<Buffer> buffers; ObjectStore<Texture> textures; @@ -756,7 +507,7 @@ struct Context { bool scissortest = false; IntRect scissor = {0, 0, 0, 0}; - GLfloat clearcolor[4] = {0, 0, 0, 0}; + uint32_t clearcolor = 0; GLdouble cleardepth = 1; int unpack_row_length = 0; @@ -766,10 +517,14 @@ struct Context { struct TextureUnit { GLuint texture_2d_binding = 0; + GLuint texture_3d_binding = 0; + GLuint texture_2d_array_binding = 0; GLuint texture_rectangle_binding = 0; void unlink(GLuint n) { ::unlink(texture_2d_binding, n); + ::unlink(texture_3d_binding, n); + ::unlink(texture_2d_array_binding, n); ::unlink(texture_rectangle_binding, n); } }; @@ -784,6 +539,7 @@ struct Context { 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; @@ -800,9 +556,13 @@ struct Context { case GL_ARRAY_BUFFER: return array_buffer_binding; case GL_ELEMENT_ARRAY_BUFFER: - return vertex_arrays[current_vertex_array].element_array_buffer_binding; + 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_TEXTURE_RECTANGLE: return texture_units[active_texture_unit].texture_rectangle_binding; case GL_TIME_ELAPSED: @@ -830,17 +590,16 @@ struct Context { return textures[texture_units[unit].texture_2d_binding]; } - Texture& get_texture(sampler2DRect, int unit) { - return textures[texture_units[unit].texture_rectangle_binding]; + Texture& get_texture(sampler2DArray, int unit) { + return textures[texture_units[unit].texture_2d_array_binding]; } - IntRect apply_scissor(IntRect bb, - const IntPoint& origin = IntPoint(0, 0)) const { - return scissortest ? bb.intersect(scissor - origin) : bb; + Texture& get_texture(sampler2DRect, int unit) { + return textures[texture_units[unit].texture_rectangle_binding]; } - IntRect apply_scissor(const Texture& t) const { - return apply_scissor(t.bounds(), t.offset); + IntRect apply_scissor(IntRect bb) const { + return scissortest ? bb.intersect(scissor) : bb; } }; static Context* ctx = nullptr; @@ -851,12 +610,14 @@ static BlendKey blend_key = BLEND_KEY_NONE; static void prepare_texture(Texture& t, const IntRect* skip = nullptr); template <typename S> +static inline void init_depth(S* s, Texture& t) { + s->depth = max(t.depth, 1); + s->height_stride = s->stride * t.height; +} + +template <typename S> static inline void init_filter(S* s, Texture& t) { - // If the width is not at least 2 pixels, then we can't safely sample the end - // of the row with a linear filter. In that case, just punt to using nearest - // filtering instead. - s->filter = t.width >= 2 ? gl_filter_to_texture_filter(t.mag_filter) - : TextureFilter::NEAREST; + s->filter = gl_filter_to_texture_filter(t.mag_filter); } template <typename S> @@ -864,44 +625,20 @@ static inline void init_sampler(S* s, Texture& t) { prepare_texture(t); s->width = t.width; s->height = t.height; - s->stride = t.stride(); int bpp = t.bpp(); - if (bpp >= 4) - s->stride /= 4; - else if (bpp == 2) - s->stride /= 2; - else - assert(bpp == 1); - // Use uint32_t* for easier sampling, but need to cast to uint8_t* or - // uint16_t* for formats with bpp < 4. + 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 <typename S> -static inline void null_sampler(S* s) { - // For null texture data, just make the sampler provide a 1x1 buffer that is - // transparent black. Ensure buffer holds at least a SIMD vector of zero data - // for SIMD padding of unaligned loads. - static const uint32_t zeroBuf[sizeof(Float) / sizeof(uint32_t)] = {0}; - s->width = 1; - s->height = 1; - s->stride = s->width; - s->buf = (uint32_t*)zeroBuf; - s->format = TextureFormat::RGBA8; -} - -template <typename S> -static inline void null_filter(S* s) { - s->filter = TextureFilter::NEAREST; -} - -template <typename S> S* lookup_sampler(S* s, int texture) { Texture& t = ctx->get_texture(s, texture); if (!t.buf) { - null_sampler(s); - null_filter(s); + *s = S(); } else { init_sampler(s, t); init_filter(s, t); @@ -913,13 +650,26 @@ template <typename S> S* lookup_isampler(S* s, int texture) { Texture& t = ctx->get_texture(s, texture); if (!t.buf) { - null_sampler(s); + *s = S(); } else { init_sampler(s, t); } return s; } +template <typename S> +S* lookup_sampler_array(S* s, int texture) { + Texture& t = ctx->get_texture(s, texture); + 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: @@ -983,40 +733,21 @@ void load_attrib(T& attrib, VertexAttrib& va, uint32_t start, int instance, attrib = T(load_attrib_scalar<scalar_type>(va, src)); } else { // Specialized for WR's primitive vertex order/winding. + // Triangles must be indexed at offsets 0, 1, 2. + // Quads must be successive triangles indexed at offsets 0, 1, 2, 2, 1, 3. + // Triangle vertexes fill vertex shader SIMD lanes as 0, 1, 2, 2. + // Quad vertexes fill vertex shader SIMD lanes as 0, 1, 3, 2, so that the + // points form a convex path that can be traversed by the rasterizer. if (!count) return; - assert(count >= 2 && count <= 4); + assert(count == 3 || count == 4); char* src = (char*)va.buf + va.stride * start + va.offset; - switch (count) { - case 2: { - // Lines must be indexed at offsets 0, 1. - // Line vertexes fill vertex shader SIMD lanes as 0, 1, 1, 0. - scalar_type lanes[2] = { - load_attrib_scalar<scalar_type>(va, src), - load_attrib_scalar<scalar_type>(va, src + va.stride)}; - attrib = (T){lanes[0], lanes[1], lanes[1], lanes[0]}; - break; - } - case 3: { - // Triangles must be indexed at offsets 0, 1, 2. - // Triangle vertexes fill vertex shader SIMD lanes as 0, 1, 2, 2. - scalar_type lanes[3] = { - load_attrib_scalar<scalar_type>(va, src), - load_attrib_scalar<scalar_type>(va, src + va.stride), - load_attrib_scalar<scalar_type>(va, src + va.stride * 2)}; - attrib = (T){lanes[0], lanes[1], lanes[2], lanes[2]}; - break; - } - default: - // Quads must be successive triangles indexed at offsets 0, 1, 2, 2, - // 1, 3. Quad vertexes fill vertex shader SIMD lanes as 0, 1, 3, 2, so - // that the points form a convex path that can be traversed by the - // rasterizer. - attrib = (T){load_attrib_scalar<scalar_type>(va, src), - load_attrib_scalar<scalar_type>(va, src + va.stride), - load_attrib_scalar<scalar_type>(va, src + va.stride * 3), - load_attrib_scalar<scalar_type>(va, src + va.stride * 2)}; - break; - } + attrib = (T){ + load_attrib_scalar<scalar_type>(va, src), + load_attrib_scalar<scalar_type>(va, src + va.stride), + load_attrib_scalar<scalar_type>(va, src + va.stride * 2 + + (count > 3 ? va.stride : 0)), + load_attrib_scalar<scalar_type>(va, src + va.stride * 2) + }; } } @@ -1076,6 +807,7 @@ 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; @@ -1090,6 +822,7 @@ 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; @@ -1103,18 +836,10 @@ void Disable(GLenum cap) { GLenum GetError() { return GL_NO_ERROR; } static const char* const extensions[] = { - "GL_ARB_blend_func_extended", - "GL_ARB_clear_texture", - "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", - "GL_KHR_blend_equation_advanced", - "GL_KHR_blend_equation_advanced_coherent", - "GL_APPLE_rgb_422", + "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) { @@ -1128,7 +853,7 @@ void GetIntegerv(GLenum pname, GLint* params) { params[0] = 1 << 15; break; case GL_MAX_ARRAY_TEXTURE_LAYERS: - params[0] = 0; + params[0] = 1 << 15; break; case GL_READ_FRAMEBUFFER_BINDING: params[0] = ctx->read_framebuffer_binding; @@ -1145,12 +870,6 @@ void GetIntegerv(GLenum pname, GLint* params) { case GL_NUM_EXTENSIONS: params[0] = sizeof(extensions) / sizeof(extensions[0]); break; - case GL_MAJOR_VERSION: - params[0] = 3; - break; - case GL_MINOR_VERSION: - params[0] = 2; - break; default: debugf("unhandled glGetIntegerv parameter %x\n", pname); assert(false); @@ -1177,8 +896,6 @@ const char* GetString(GLenum name) { return "Software WebRender"; case GL_VERSION: return "3.2"; - case GL_SHADING_LANGUAGE_VERSION: - return "1.50"; default: debugf("unhandled glGetString parameter %x\n", name); assert(false); @@ -1254,23 +971,17 @@ GLenum remap_blendfunc(GLenum rgb, GLenum a) { return a; } -// Generate a hashed blend key based on blend func and equation state. This -// allows all the blend state to be processed down to a blend key that can be -// dealt with inside a single switch statement. -static void hash_blend_key() { - GLenum srgb = ctx->blendfunc_srgb; - GLenum drgb = ctx->blendfunc_drgb; - GLenum sa = ctx->blendfunc_sa; - GLenum da = ctx->blendfunc_da; - GLenum equation = ctx->blend_equation; +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)) - // Basic non-separate blend funcs used the two argument form int hash = HASH_BLEND_KEY(srgb, drgb, 0, 0); - // Separate alpha blend funcs use the 4 argument hash if (srgb != sa || drgb != da) hash |= HASH_BLEND_KEY(0, 0, sa, da); - // Any other blend equation than the default func_add ignores the func and - // instead generates a one-argument hash based on the equation - if (equation != GL_FUNC_ADD) hash = HASH_BLEND_KEY(equation, 0, 0, 0); switch (hash) { #define MAP_BLEND_KEY(...) \ case HASH_BLEND_KEY(__VA_ARGS__): \ @@ -1278,22 +989,14 @@ static void hash_blend_key() { break; FOR_EACH_BLEND_KEY(MAP_BLEND_KEY) default: - debugf("blendfunc: %x, %x, separate: %x, %x, equation: %x\n", srgb, drgb, - sa, da, equation); + debugf("blendfunc: %x, %x, separate: %x, %x\n", srgb, drgb, sa, da); assert(false); break; } -} -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; - - hash_blend_key(); + if (ctx->blend) { + blend_key = ctx->blend_key; + } } void BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { @@ -1302,12 +1005,8 @@ void BlendColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { } void BlendEquation(GLenum mode) { - assert(mode == GL_FUNC_ADD || mode == GL_MIN || mode == GL_MAX || - (mode >= GL_MULTIPLY_KHR && mode <= GL_HSL_LUMINOSITY_KHR)); - if (mode != ctx->blend_equation) { - ctx->blend_equation = mode; - hash_blend_key(); - } + assert(mode == GL_FUNC_ADD); + ctx->blend_equation = mode; } void DepthMask(GLboolean flag) { ctx->depthmask = flag; } @@ -1328,10 +1027,8 @@ void SetScissor(GLint x, GLint y, GLsizei width, GLsizei height) { } void ClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { - ctx->clearcolor[0] = r; - ctx->clearcolor[1] = g; - ctx->clearcolor[2] = b; - ctx->clearcolor[3] = a; + I32 c = round_pixel((Float){b, g, r, a}); + ctx->clearcolor = bit_cast<uint32_t>(CONVERT(c, U8)); } void ClearDepth(GLdouble depth) { ctx->cleardepth = depth; } @@ -1369,6 +1066,7 @@ void DeleteBuffer(GLuint 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); } } @@ -1434,45 +1132,26 @@ void DeleteProgram(GLuint n) { void LinkProgram(GLuint program) { Program& p = ctx->programs[program]; assert(p.impl); - if (!p.impl) { - return; - } assert(p.impl->interpolants_size() <= sizeof(Interpolants)); if (!p.vert_impl) p.vert_impl = p.impl->get_vertex_shader(); if (!p.frag_impl) p.frag_impl = p.impl->get_fragment_shader(); } -GLint GetLinkStatus(GLuint program) { - if (auto* p = ctx->programs.find(program)) { - return p->impl ? 1 : 0; - } - return 0; -} - void BindAttribLocation(GLuint program, GLuint index, char* name) { Program& p = ctx->programs[program]; assert(p.impl); - if (!p.impl) { - return; - } p.impl->bind_attrib(name, index); } GLint GetAttribLocation(GLuint program, char* name) { Program& p = ctx->programs[program]; assert(p.impl); - if (!p.impl) { - return -1; - } return p.impl->get_attrib(name); } GLint GetUniformLocation(GLuint program, char* name) { Program& p = ctx->programs[program]; assert(p.impl); - if (!p.impl) { - return -1; - } GLint loc = p.impl->get_uniform(name); // debugf("location: %d\n", loc); return loc; @@ -1482,15 +1161,7 @@ static uint64_t get_time_value() { #ifdef __MACH__ return mach_absolute_time(); #elif defined(_WIN32) - LARGE_INTEGER time; - static bool have_frequency = false; - static LARGE_INTEGER frequency; - if (!have_frequency) { - QueryPerformanceFrequency(&frequency); - have_frequency = true; - } - QueryPerformanceCounter(&time); - return time.QuadPart * 1000000000ULL / frequency.QuadPart; + return uint64_t(clock()) * (1000000000ULL / CLOCKS_PER_SEC); #else return ({ struct timespec tp; @@ -1583,113 +1254,60 @@ void PixelStorei(GLenum name, GLint param) { static GLenum remap_internal_format(GLenum format) { switch (format) { case GL_DEPTH_COMPONENT: - return GL_DEPTH_COMPONENT24; + return GL_DEPTH_COMPONENT16; case GL_RGBA: return GL_RGBA8; case GL_RED: return GL_R8; - case GL_RG: - return GL_RG8; - case GL_RGB_422_APPLE: - return GL_RGB_RAW_422_APPLE; default: return format; } } -} // extern "C" - -static bool format_requires_conversion(GLenum external_format, - GLenum internal_format) { - switch (external_format) { - case GL_RGBA: - return internal_format == GL_RGBA8; - default: - return false; - } -} - -static inline void copy_bgra8_to_rgba8(uint32_t* dest, const uint32_t* src, - int width) { - for (; width >= 4; width -= 4, dest += 4, src += 4) { - U32 p = unaligned_load<U32>(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 void convert_copy(GLenum external_format, GLenum internal_format, - uint8_t* dst_buf, size_t dst_stride, - const uint8_t* src_buf, size_t src_stride, - size_t width, size_t height) { - switch (external_format) { - case GL_RGBA: - if (internal_format == GL_RGBA8) { - for (; height; height--) { - copy_bgra8_to_rgba8((uint32_t*)dst_buf, (const uint32_t*)src_buf, - width); - dst_buf += dst_stride; - src_buf += src_stride; - } - return; - } - break; - default: - break; - } - size_t row_bytes = width * bytes_for_internal_format(internal_format); - for (; height; height--) { - memcpy(dst_buf, src_buf, row_bytes); - dst_buf += dst_stride; - src_buf += src_stride; +void TexStorage3D(GLenum target, GLint levels, GLenum internal_format, + GLsizei width, GLsizei height, GLsizei depth) { + assert(levels == 1); + 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.internal_format != internal_format) { + changed = true; + 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, GLenum external_format, GLsizei width, - GLsizei height, void* buf = nullptr, - GLsizei stride = 0, GLsizei min_width = 0, - GLsizei min_height = 0) { - GLenum internal_format = remap_internal_format(external_format); +static void set_tex_storage(Texture& t, 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 || + if (t.width != width || t.height != height || t.depth != 0 || t.internal_format != internal_format) { changed = true; t.internal_format = internal_format; t.width = width; t.height = height; + t.depth = 0; } - // If we are changed from an internally managed buffer to an externally - // supplied one or vice versa, ensure that we clean up old buffer state. - // However, if we have to convert the data from a non-native format, then - // always treat it as internally managed since we will need to copy to an - // internally managed native format buffer. - bool should_free = buf == nullptr || format_requires_conversion( - external_format, internal_format); - if (t.should_free() != should_free) { - changed = true; - t.cleanup(); + if (t.should_free() != should_free || buf != nullptr) { + if (t.should_free()) { + t.cleanup(); + } t.set_should_free(should_free); - } - // If now an external buffer, explicitly set it... - if (!should_free) { - t.set_buffer(buf, stride); + t.buf = (char*)buf; + t.buf_size = 0; } t.disable_delayed_clear(); t.allocate(changed, min_width, min_height); - // If we have a buffer that needs format conversion, then do that now. - if (buf && should_free) { - convert_copy(external_format, internal_format, (uint8_t*)t.buf, t.stride(), - (const uint8_t*)buf, stride, width, height); - } } -extern "C" { - void TexStorage2D(GLenum target, GLint levels, GLenum internal_format, GLsizei width, GLsizei height) { assert(levels == 1); @@ -1701,19 +1319,12 @@ 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 || ty == GL_UNSIGNED_INT_8_8_8_8_REV)) { + 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 if (format == GL_RG && ty == GL_UNSIGNED_BYTE) { - return GL_RG8; - } else if (format == GL_RGB_422_APPLE && - ty == GL_UNSIGNED_SHORT_8_8_REV_APPLE) { - return GL_RGB_RAW_422_APPLE; - } else if (format == GL_RED && ty == GL_UNSIGNED_SHORT) { - return GL_R16; } else { debugf("unknown internal format for format %x, type %x\n", format, ty); assert(false); @@ -1721,6 +1332,20 @@ GLenum internal_format_for_data(GLenum format, GLenum ty) { } } +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<U32>(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] @@ -1750,10 +1375,7 @@ static void* get_pixel_unpack_buffer_data(void* data) { void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum ty, void* data) { - if (level != 0) { - assert(false); - return; - } + if (level != 0) { assert(false); return; } data = get_pixel_unpack_buffer_data(data); if (!data) return; Texture& t = ctx->textures[ctx->get_binding(target)]; @@ -1765,33 +1387,84 @@ void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei row_length = ctx->unpack_row_length != 0 ? ctx->unpack_row_length : width; assert(t.internal_format == internal_format_for_data(format, ty)); - int src_bpp = format_requires_conversion(format, t.internal_format) - ? bytes_for_internal_format(format) - : t.bpp(); - if (!src_bpp || !t.buf) return; - convert_copy(format, t.internal_format, - (uint8_t*)t.sample_ptr(xoffset, yoffset), t.stride(), - (const uint8_t*)data, row_length * src_bpp, width, height); + int bpp = t.bpp(); + if (!bpp || !t.buf) return; + size_t dest_stride = t.stride(bpp); + char* dest = t.sample_ptr(xoffset, yoffset, 0, bpp, dest_stride); + 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) { - if (level != 0) { - assert(false); - return; - } + if (level != 0) { assert(false); return; } 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) { + if (level != 0) { assert(false); return; } + 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.sample_ptr(xoffset, yoffset, zoffset + z, bpp, dest_stride); + 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) { + if (level != 0) { assert(false); return; } + assert(border == 0); + TexStorage3D(target, 1, internal_format, width, height, depth); + TexSubImage3D(target, 0, 0, 0, 0, width, height, depth, format, ty, data); +} + void GenerateMipmap(UNUSED GLenum target) { // TODO: support mipmaps } -void SetTextureParameter(GLuint texid, GLenum pname, GLint param) { - Texture& t = ctx->textures[texid]; +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); @@ -1810,10 +1483,6 @@ void SetTextureParameter(GLuint texid, GLenum pname, GLint param) { } } -void TexParameteri(GLenum target, GLenum pname, GLint param) { - SetTextureParameter(ctx->get_binding(target), pname, param); -} - void GenTextures(int n, GLuint* result) { for (int i = 0; i < n; i++) { Texture t; @@ -1839,7 +1508,9 @@ void GenRenderbuffers(int n, GLuint* result) { void Renderbuffer::on_erase() { for (auto* fb : ctx->framebuffers) { if (fb) { - unlink(fb->color_attachment, texture); + if (unlink(fb->color_attachment, texture)) { + fb->layer = 0; + } unlink(fb->depth_attachment, texture); } } @@ -1875,11 +1546,10 @@ void RenderbufferStorage(GLenum target, GLenum internal_format, GLsizei width, } switch (internal_format) { case GL_DEPTH_COMPONENT: - case GL_DEPTH_COMPONENT16: case GL_DEPTH_COMPONENT24: case GL_DEPTH_COMPONENT32: - // Force depth format to 24 bits... - internal_format = GL_DEPTH_COMPONENT24; + // Force depth format to 16 bits... + internal_format = GL_DEPTH_COMPONENT16; break; } set_tex_storage(ctx->textures[r.texture], internal_format, width, height); @@ -1963,8 +1633,7 @@ void VertexAttribDivisor(GLuint index, GLuint divisor) { va.divisor = divisor; } -void BufferData(GLenum target, GLsizeiptr size, void* data, - UNUSED GLenum usage) { +void BufferData(GLenum target, GLsizeiptr size, void* data, UNUSED GLenum usage) { Buffer& b = ctx->buffers[ctx->get_binding(target)]; if (b.allocate(size)) { ctx->validate_vertex_array = true; @@ -2004,23 +1673,17 @@ GLboolean UnmapBuffer(GLenum target) { void Uniform1i(GLint location, GLint V0) { // debugf("tex: %d\n", (int)ctx->textures.size); - if (vertex_shader) { - vertex_shader->set_uniform_1i(location, V0); - } + vertex_shader->set_uniform_1i(location, V0); } void Uniform4fv(GLint location, GLsizei count, const GLfloat* v) { assert(count == 1); - if (vertex_shader) { - vertex_shader->set_uniform_4fv(location, v); - } + vertex_shader->set_uniform_4fv(location, v); } void UniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) { assert(count == 1); assert(!transpose); - if (vertex_shader) { - vertex_shader->set_uniform_matrix4fv(location, value); - } + vertex_shader->set_uniform_matrix4fv(location, value); } void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, @@ -2031,7 +1694,24 @@ void FramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, 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(target == GL_READ_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER); + assert(level == 0); + 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); @@ -2046,6 +1726,7 @@ void FramebufferRenderbuffer(GLenum target, GLenum attachment, 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 { @@ -2055,18 +1736,11 @@ void FramebufferRenderbuffer(GLenum target, GLenum attachment, } // extern "C" -static inline Framebuffer* get_framebuffer(GLenum target, - bool fallback = false) { +static inline Framebuffer* get_framebuffer(GLenum target) { if (target == GL_FRAMEBUFFER) { target = GL_DRAW_FRAMEBUFFER; } - Framebuffer* fb = ctx->framebuffers.find(ctx->get_binding(target)); - if (fallback && !fb) { - // If the specified framebuffer isn't found and a fallback is requested, - // use the default framebuffer. - fb = &ctx->framebuffers[0]; - } - return fb; + return ctx->framebuffers.find(ctx->get_binding(target)); } template <typename T> @@ -2092,7 +1766,9 @@ static inline uint32_t clear_chunk(uint16_t value) { return uint32_t(value) | (uint32_t(value) << 16); } -static inline uint32_t clear_chunk(uint32_t value) { return value; } +static inline uint32_t clear_chunk(uint32_t value) { + return value; +} template <typename T> static inline void clear_row(T* buf, size_t len, T value, uint32_t chunk) { @@ -2115,22 +1791,20 @@ static inline void clear_row(T* buf, size_t len, T value, uint32_t chunk) { } template <typename T> -static void clear_buffer(Texture& t, T value, IntRect bb, int skip_start = 0, - int skip_end = 0) { +static void clear_buffer(Texture& t, T value, int layer, IntRect bb, + int skip_start = 0, int skip_end = 0) { if (!t.buf) return; skip_start = max(skip_start, bb.x0); skip_end = max(skip_end, skip_start); assert(sizeof(T) == t.bpp()); - size_t stride = t.stride(); - // When clearing multiple full-width rows, collapse them into a single large - // "row" to avoid redundant setup from clearing each row individually. Note - // that we can only safely do this if the stride is tightly packed. - if (bb.width() == t.width && bb.height() > 1 && skip_start >= skip_end && - (t.should_free() || stride == t.width * sizeof(T))) { + size_t stride = t.stride(sizeof(T)); + // When clearing multiple full-width rows, collapse them into a single + // large "row" to avoid redundant setup from clearing each row individually. + if (bb.width() == t.width && bb.height() > 1 && skip_start >= skip_end) { bb.x1 += (stride / sizeof(T)) * (bb.height() - 1); bb.y1 = bb.y0 + 1; } - T* buf = (T*)t.sample_ptr(bb.x0, bb.y0); + T* buf = (T*)t.sample_ptr(bb.x0, bb.y0, layer, sizeof(T), stride); uint32_t chunk = clear_chunk(value); for (int rows = bb.height(); rows > 0; rows--) { if (bb.x0 < skip_start) { @@ -2144,12 +1818,20 @@ static void clear_buffer(Texture& t, T value, IntRect bb, int skip_start = 0, } template <typename T> +static inline void clear_buffer(Texture& t, T value, int layer = 0) { + IntRect bb = ctx->apply_scissor(t.bounds()); + if (bb.width() > 0) { + clear_buffer<T>(t, value, layer, bb); + } +} + +template <typename T> static inline void force_clear_row(Texture& t, int y, int skip_start = 0, int skip_end = 0) { assert(t.buf != nullptr); assert(sizeof(T) == t.bpp()); assert(skip_start <= skip_end); - T* buf = (T*)t.sample_ptr(0, y); + T* buf = (T*)t.sample_ptr(0, y, 0, sizeof(T)); uint32_t chunk = clear_chunk((T)t.clear_val); if (skip_start > 0) { clear_row<T>(buf, skip_start, t.clear_val, chunk); @@ -2188,9 +1870,9 @@ static void force_clear(Texture& t, const IntRect* skip = nullptr) { while (mask) { int count = __builtin_ctz(mask); if (count > 0) { - clear_buffer<T>(t, t.clear_val, - IntRect{0, start, t.width, start + count}, skip_start, - skip_end); + clear_buffer<T>(t, t.clear_val, 0, + IntRect{0, start, t.width, start + count}, + skip_start, skip_end); t.delay_clear -= count; start += count; mask >>= count; @@ -2201,9 +1883,9 @@ static void force_clear(Texture& t, const IntRect* skip = nullptr) { } int count = (i + 1) * 32 - start; if (count > 0) { - clear_buffer<T>(t, t.clear_val, - IntRect{0, start, t.width, start + count}, skip_start, - skip_end); + clear_buffer<T>(t, t.clear_val, 0, + IntRect{0, start, t.width, start + count}, + skip_start, skip_end); t.delay_clear -= count; } } @@ -2220,7 +1902,7 @@ static void prepare_texture(Texture& t, const IntRect* skip) { case GL_R8: force_clear<uint8_t>(t, skip); break; - case GL_RG8: + case GL_DEPTH_COMPONENT16: force_clear<uint16_t>(t, skip); break; default: @@ -2230,53 +1912,31 @@ static void prepare_texture(Texture& t, const IntRect* skip) { } } -// Setup a clear on a texture. This may either force an immediate clear or -// potentially punt to a delayed clear, if applicable. -template <typename T> -static void request_clear(Texture& t, T value, const IntRect& scissor) { - // If the clear would require a scissor, force clear anything outside - // the scissor, and then immediately clear anything inside the scissor. - if (!scissor.contains(t.offset_bounds())) { - IntRect skip = scissor - t.offset; - force_clear<T>(t, &skip); - clear_buffer<T>(t, value, skip.intersection(t.bounds())); - } else { - // Do delayed clear for 2D texture without scissor. - t.enable_delayed_clear(value); - } -} - -template <typename T> -static inline void request_clear(Texture& t, T value) { - // If scissoring is enabled, use the scissor rect. Otherwise, just scissor to - // the entire texture bounds. - request_clear(t, value, ctx->scissortest ? ctx->scissor : t.offset_bounds()); -} - extern "C" { -void InitDefaultFramebuffer(int x, int y, int width, int height, int stride, - void* buf) { +void InitDefaultFramebuffer(int width, int height) { Framebuffer& fb = ctx->framebuffers[0]; if (!fb.color_attachment) { GenTextures(1, &fb.color_attachment); + fb.layer = 0; } - // If the dimensions or buffer properties changed, we need to reallocate - // the underlying storage for the color buffer texture. Texture& colortex = ctx->textures[fb.color_attachment]; - set_tex_storage(colortex, GL_RGBA8, width, height, buf, stride); - colortex.offset = IntPoint(x, y); + if (colortex.width != width || colortex.height != height) { + colortex.cleanup(); + set_tex_storage(colortex, GL_RGBA8, width, height); + } if (!fb.depth_attachment) { GenTextures(1, &fb.depth_attachment); } - // Ensure dimensions of the depth buffer match the color buffer. Texture& depthtex = ctx->textures[fb.depth_attachment]; - set_tex_storage(depthtex, GL_DEPTH_COMPONENT24, width, height); - depthtex.offset = IntPoint(x, y); + if (depthtex.width != width || depthtex.height != height) { + depthtex.cleanup(); + set_tex_storage(depthtex, GL_DEPTH_COMPONENT16, width, height); + } } void* GetColorBuffer(GLuint fbo, GLboolean flush, int32_t* width, - int32_t* height, int32_t* stride) { + int32_t* height) { Framebuffer* fb = ctx->framebuffers.find(fbo); if (!fb || !fb->color_attachment) { return nullptr; @@ -2285,33 +1945,16 @@ void* GetColorBuffer(GLuint fbo, GLboolean flush, int32_t* width, if (flush) { prepare_texture(colortex); } - assert(colortex.offset == IntPoint(0, 0)); - if (width) { - *width = colortex.width; - } - if (height) { - *height = colortex.height; - } - if (stride) { - *stride = colortex.stride(); - } - return colortex.buf ? colortex.sample_ptr(0, 0) : nullptr; -} - -void ResolveFramebuffer(GLuint fbo) { - Framebuffer* fb = ctx->framebuffers.find(fbo); - if (!fb || !fb->color_attachment) { - return; - } - Texture& colortex = ctx->textures[fb->color_attachment]; - prepare_texture(colortex); + *width = colortex.width; + *height = colortex.height; + return colortex.buf ? colortex.sample_ptr(0, 0, fb->layer) : nullptr; } void SetTextureBuffer(GLuint texid, GLenum internal_format, GLsizei width, - GLsizei height, GLsizei stride, void* buf, - GLsizei min_width, GLsizei min_height) { + GLsizei height, void* buf, GLsizei min_width, + GLsizei min_height) { Texture& t = ctx->textures[texid]; - set_tex_storage(t, internal_format, width, height, buf, stride, min_width, + set_tex_storage(t, internal_format, width, height, !buf, buf, min_width, min_height); } @@ -2323,170 +1966,57 @@ GLenum CheckFramebufferStatus(GLenum target) { return GL_FRAMEBUFFER_COMPLETE; } -void ClearTexSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, - GLint zoffset, GLsizei width, GLsizei height, - GLsizei depth, GLenum format, GLenum type, - const void* data) { - if (level != 0) { - assert(false); - return; - } - Texture& t = ctx->textures[texture]; - assert(!t.locked); - if (width <= 0 || height <= 0 || depth <= 0) { - return; - } - assert(zoffset == 0 && depth == 1); - IntRect scissor = {xoffset, yoffset, xoffset + width, yoffset + height}; - if (t.internal_format == GL_DEPTH_COMPONENT24) { - uint32_t value = 0xFFFFFF; - switch (format) { - case GL_DEPTH_COMPONENT: - switch (type) { - case GL_DOUBLE: - value = uint32_t(*(const GLdouble*)data * 0xFFFFFF); - break; - case GL_FLOAT: - value = uint32_t(*(const GLfloat*)data * 0xFFFFFF); - break; - default: - assert(false); - break; - } - break; - default: - assert(false); - break; - } - if (t.cleared() && !scissor.contains(t.offset_bounds())) { - // If we need to scissor the clear and the depth buffer was already - // initialized, then just fill runs for that scissor area. - t.fill_depth_runs(value, scissor); - } else { - // Otherwise, the buffer is either uninitialized or the clear would - // encompass the entire buffer. If uninitialized, we can safely fill - // the entire buffer with any value and thus ignore any scissoring. - t.init_depth_runs(value); - } - return; - } - - uint32_t color = 0xFF000000; - switch (type) { - case GL_FLOAT: { - const GLfloat* f = (const GLfloat*)data; - Float v = {0.0f, 0.0f, 0.0f, 1.0f}; - switch (format) { - case GL_RGBA: - v.w = f[3]; // alpha - FALLTHROUGH; - case GL_RGB: - v.z = f[2]; // blue - FALLTHROUGH; - case GL_RG: - v.y = f[1]; // green - FALLTHROUGH; - case GL_RED: - v.x = f[0]; // red - break; - default: - assert(false); - break; - } - color = bit_cast<uint32_t>(CONVERT(round_pixel(v), U8)); - break; - } - case GL_UNSIGNED_BYTE: { - const GLubyte* b = (const GLubyte*)data; - switch (format) { - case GL_RGBA: - color = (color & ~0xFF000000) | (uint32_t(b[3]) << 24); // alpha - FALLTHROUGH; - case GL_RGB: - color = (color & ~0x00FF0000) | (uint32_t(b[2]) << 16); // blue - FALLTHROUGH; - case GL_RG: - color = (color & ~0x0000FF00) | (uint32_t(b[1]) << 8); // green - FALLTHROUGH; - case GL_RED: - color = (color & ~0x000000FF) | uint32_t(b[0]); // red - break; - default: - assert(false); - break; - } - break; - } - default: - assert(false); - break; - } - - switch (t.internal_format) { - case GL_RGBA8: - // Clear color needs to swizzle to BGRA. - request_clear<uint32_t>(t, - (color & 0xFF00FF00) | - ((color << 16) & 0xFF0000) | - ((color >> 16) & 0xFF), - scissor); - break; - case GL_R8: - request_clear<uint8_t>(t, uint8_t(color & 0xFF), scissor); - break; - case GL_RG8: - request_clear<uint16_t>(t, uint16_t(color & 0xFFFF), scissor); - break; - default: - assert(false); - break; - } -} - -void ClearTexImage(GLuint texture, GLint level, GLenum format, GLenum type, - const void* data) { - Texture& t = ctx->textures[texture]; - IntRect scissor = t.offset_bounds(); - ClearTexSubImage(texture, level, scissor.x0, scissor.y0, 0, scissor.width(), - scissor.height(), 1, format, type, data); +static inline bool clear_requires_scissor(Texture& t) { + return ctx->scissortest && !ctx->scissor.contains(t.bounds()); } void Clear(GLbitfield mask) { - Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER, true); + Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER); if ((mask & GL_COLOR_BUFFER_BIT) && fb.color_attachment) { Texture& t = ctx->textures[fb.color_attachment]; - IntRect scissor = ctx->scissortest - ? ctx->scissor.intersection(t.offset_bounds()) - : t.offset_bounds(); - ClearTexSubImage(fb.color_attachment, 0, scissor.x0, scissor.y0, 0, - scissor.width(), scissor.height(), 1, GL_RGBA, GL_FLOAT, - ctx->clearcolor); + if (t.internal_format == GL_RGBA8) { + uint32_t color = ctx->clearcolor; + // If the clear would require a scissor, force clear anything outside + // the scissor, and then immediately clear anything inside the scissor. + if (clear_requires_scissor(t)) { + force_clear<uint32_t>(t, &ctx->scissor); + clear_buffer<uint32_t>(t, color, fb.layer); + } else if (t.depth > 1) { + // Delayed clear is not supported on texture arrays. + t.disable_delayed_clear(); + clear_buffer<uint32_t>(t, color, fb.layer); + } else { + // Do delayed clear for 2D texture without scissor. + 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<uint8_t>(t, &ctx->scissor); + clear_buffer<uint8_t>(t, color, fb.layer); + } else if (t.depth > 1) { + t.disable_delayed_clear(); + clear_buffer<uint8_t>(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]; - IntRect scissor = ctx->scissortest - ? ctx->scissor.intersection(t.offset_bounds()) - : t.offset_bounds(); - ClearTexSubImage(fb.depth_attachment, 0, scissor.x0, scissor.y0, 0, - scissor.width(), scissor.height(), 1, GL_DEPTH_COMPONENT, - GL_DOUBLE, &ctx->cleardepth); + assert(t.internal_format == GL_DEPTH_COMPONENT16); + uint16_t depth = uint16_t(0xFFFF * ctx->cleardepth) - 0x8000; + if (clear_requires_scissor(t)) { + force_clear<uint16_t>(t, &ctx->scissor); + clear_buffer<uint16_t>(t, depth); + } else { + t.enable_delayed_clear(depth); + } } } -void ClearColorRect(GLuint fbo, GLint xoffset, GLint yoffset, GLsizei width, - GLsizei height, GLfloat r, GLfloat g, GLfloat b, - GLfloat a) { - GLfloat color[] = {r, g, b, a}; - Framebuffer& fb = ctx->framebuffers[fbo]; - Texture& t = ctx->textures[fb.color_attachment]; - IntRect scissor = - IntRect{xoffset, yoffset, xoffset + width, yoffset + height}.intersection( - t.offset_bounds()); - ClearTexSubImage(fb.color_attachment, 0, scissor.x0, scissor.y0, 0, - scissor.width(), scissor.height(), 1, GL_RGBA, GL_FLOAT, - color); -} - void InvalidateFramebuffer(GLenum target, GLsizei num_attachments, const GLenum* attachments) { Framebuffer* fb = get_framebuffer(target); @@ -2497,7 +2027,7 @@ void InvalidateFramebuffer(GLenum target, GLsizei num_attachments, switch (attachments[i]) { case GL_DEPTH_ATTACHMENT: { Texture& t = ctx->textures[fb->depth_attachment]; - t.set_cleared(false); + t.disable_delayed_clear(); break; } case GL_COLOR_ATTACHMENT0: { @@ -2516,58 +2046,40 @@ void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, Framebuffer* fb = get_framebuffer(GL_READ_FRAMEBUFFER); if (!fb) return; assert(format == GL_RED || format == GL_RGBA || format == GL_RGBA_INTEGER || - format == GL_BGRA || format == GL_RG); + 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); - x -= t.offset.x; - y -= t.offset.y; - assert(x >= 0 && y >= 0); 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); - return; - } - // Only support readback conversions that are reversible - assert(!format_requires_conversion(format, t.internal_format) || - bytes_for_internal_format(format) == t.bpp()); - uint8_t* dest = (uint8_t*)data; - size_t destStride = width * t.bpp(); - if (y < 0) { - dest += -y * destStride; - height += y; - y = 0; - } - if (y + height > t.height) { - height = t.height - y; - } - if (x < 0) { - dest += -x * t.bpp(); - width += x; - x = 0; } - if (x + width > t.width) { - width = t.width - x; - } - if (width <= 0 || height <= 0) { - return; + int bpp = t.bpp(); + char* dest = (char*)data; + size_t src_stride = t.stride(bpp); + char* src = t.sample_ptr(x, y, fb->layer, bpp, src_stride); + 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; } - convert_copy(format, t.internal_format, dest, destStride, - (const uint8_t*)t.sample_ptr(x, y), t.stride(), width, height); } void CopyImageSubData(GLuint srcName, GLenum srcTarget, UNUSED GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, - GLenum dstTarget, UNUSED GLint dstLevel, GLint dstX, - GLint dstY, GLint dstZ, GLsizei srcWidth, - GLsizei srcHeight, GLsizei srcDepth) { + GLenum dstTarget, UNUSED GLint dstLevel, GLint dstX, GLint dstY, + GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, + GLsizei srcDepth) { assert(srcLevel == 0 && dstLevel == 0); - assert(srcZ == 0 && srcDepth == 1 && dstZ == 0); if (srcTarget == GL_RENDERBUFFER) { Renderbuffer& rb = ctx->renderbuffers[srcName]; srcName = rb.texture; @@ -2581,44 +2093,532 @@ void CopyImageSubData(GLuint srcName, GLenum srcTarget, UNUSED GLint srcLevel, prepare_texture(srctex); Texture& dsttex = ctx->textures[dstName]; if (!dsttex.buf) return; - assert(!dsttex.locked); IntRect skip = {dstX, dstY, dstX + srcWidth, dstY + srcHeight}; prepare_texture(dsttex, &skip); assert(srctex.internal_format == dsttex.internal_format); assert(srcWidth >= 0); assert(srcHeight >= 0); + assert(srcDepth >= 0); assert(srcX + srcWidth <= srctex.width); assert(srcY + srcHeight <= srctex.height); + assert(srcZ + srcDepth <= max(srctex.depth, 1)); assert(dstX + srcWidth <= dsttex.width); assert(dstY + srcHeight <= dsttex.height); + assert(dstZ + srcDepth <= max(dsttex.depth, 1)); int bpp = srctex.bpp(); - int src_stride = srctex.stride(); - int dest_stride = dsttex.stride(); - char* dest = dsttex.sample_ptr(dstX, dstY); - char* src = srctex.sample_ptr(srcX, srcY); - for (int y = 0; y < srcHeight; y++) { - memcpy(dest, src, srcWidth * bpp); - dest += dest_stride; - src += src_stride; + int src_stride = srctex.stride(bpp); + int dest_stride = dsttex.stride(bpp); + for (int z = 0; z < srcDepth; z++) { + char* dest = dsttex.sample_ptr(dstX, dstY, dstZ + z, bpp, dest_stride); + char* src = srctex.sample_ptr(srcX, srcY, srcZ + z, bpp, src_stride); + for (int y = 0; y < srcHeight; y++) { + memcpy(dest, src, srcWidth * bpp); + dest += dest_stride; + src += src_stride; + } } } -void CopyTexSubImage2D(GLenum target, UNUSED GLint level, GLint xoffset, - GLint yoffset, GLint x, GLint y, GLsizei width, +void CopyTexSubImage3D(GLenum target, UNUSED GLint level, GLint xoffset, GLint yoffset, + GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) { assert(level == 0); Framebuffer* fb = get_framebuffer(GL_READ_FRAMEBUFFER); if (!fb) return; - CopyImageSubData(fb->color_attachment, GL_TEXTURE_2D, 0, x, y, 0, - ctx->get_binding(target), GL_TEXTURE_2D, 0, xoffset, yoffset, - 0, width, height, 1); + 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, UNUSED GLint level, GLint xoffset, GLint yoffset, + GLint x, GLint y, GLsizei width, GLsizei height) { + assert(level == 0); + 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); } } // extern "C" -#include "blend.h" -#include "composite.h" -#include "swgl_ext.h" +using PackedRGBA8 = V16<uint8_t>; +using WideRGBA8 = V16<uint16_t>; +using HalfRGBA8 = V8<uint16_t>; + +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<uint8_t>; +using WideR8 = V4<uint16_t>; + +static inline WideR8 unpack(PackedR8 p) { return CONVERT(p, WideR8); } + +static inline WideR8 packR8(I32 a) { +#if USE_SSE2 + return lowHalf(bit_cast<V8<uint16_t>>(_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<V16<uint8_t>>(_mm_packus_epi16(m, m)); + return SHUFFLE(r, r, 0, 1, 2, 3); +#elif USE_NEON + return lowHalf(bit_cast<V8<uint8_t>>(vqmovn_u16(expand(p)))); +#else + return CONVERT(p, PackedR8); +#endif +} + +using ZMask4 = V4<int16_t>; +using ZMask8 = V8<int16_t>; + +static inline PackedRGBA8 unpack(ZMask4 mask, uint32_t*) { + return bit_cast<PackedRGBA8>(mask.xxyyzzww); +} + +static inline WideR8 unpack(ZMask4 mask, uint8_t*) { + return bit_cast<WideR8>(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<uint8_t>; +using ZMask8Code = V8<uint8_t>; +# define ZMASK_NONE_PASSED 0xFFFFFFFFU +# define ZMASK_ALL_PASSED 0 +static inline uint32_t zmask_code(ZMask4 mask) { + return bit_cast<uint32_t>(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 <int FUNC, bool MASK> +static ALWAYS_INLINE int check_depth8(uint16_t z, uint16_t* zbuf, + ZMask8& outmask) { + ZMask8 dest = unaligned_load<ZMask8>(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 <bool FULL_SPANS, bool DISCARD> +static ALWAYS_INLINE bool check_depth4(ZMask4 src, uint16_t* zbuf, + ZMask4& outmask, int span = 0) { + ZMask4 dest = unaligned_load<ZMask4>(zbuf); + // 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; +} + +template <bool FULL_SPANS, bool DISCARD> +static ALWAYS_INLINE bool check_depth4(uint16_t z, uint16_t* zbuf, + ZMask4& outmask, int span = 0) { + return check_depth4<FULL_SPANS, DISCARD>(ZMask4(int16_t(z)), zbuf, outmask, + span); +} + +template <typename T> +static inline ZMask4 packZMask4(T a) { +#if USE_SSE2 + return lowHalf(bit_cast<ZMask8>(_mm_packs_epi32(a, a))); +#elif USE_NEON + return vqmovn_s32(a); +#else + return CONVERT(a, ZMask4); +#endif +} + +static ALWAYS_INLINE ZMask4 packDepth() { + return packZMask4(cast(fragment_shader->gl_FragCoord.z * 0xFFFF) - 0x8000); +} + +static ALWAYS_INLINE void discard_depth(ZMask4 src, uint16_t* zbuf, + ZMask4 mask) { + if (ctx->depthmask) { + ZMask4 dest = unaligned_load<ZMask4>(zbuf); + mask |= packZMask4(fragment_shader->isPixelDiscarded); + unaligned_store(zbuf, (mask & dest) | (~mask & src)); + } +} + +static ALWAYS_INLINE void discard_depth(uint16_t z, uint16_t* zbuf, + ZMask4 mask) { + discard_depth(ZMask4(int16_t(z)), zbuf, mask); +} + +static inline WideRGBA8 pack_pixels_RGBA8(const vec4& v) { + ivec4 i = round_pixel(v); + 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 = round_pixel((Float){v.z, v.y, v.x, v.w}); + HalfRGBA8 c = packRGBA8(i, i); + return combine(c, c); +} + +static inline WideRGBA8 pack_pixels_RGBA8() { + return pack_pixels_RGBA8(fragment_shader->gl_FragColor); +} + +template <typename V> +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 <typename T> +static inline T muldiv255(T x, T y) { + return (x * y + x) >> 8; +} + +// Byte-wise addition for when x or y is a signed 8-bit value stored in the +// low byte of a larger type T only with zeroed-out high bits, where T is +// greater than 8 bits, i.e. uint16_t. This can result when muldiv255 is used +// upon signed operands, using up all the precision in a 16 bit integer, and +// potentially losing the sign bit in the last >> 8 shift. Due to the +// properties of two's complement arithmetic, even though we've discarded the +// sign bit, we can still represent a negative number under addition (without +// requiring any extra sign bits), just that any negative number will behave +// like a large unsigned number under addition, generating a single carry bit +// on overflow that we need to discard. Thus, just doing a byte-wise add will +// overflow without the troublesome carry, giving us only the remaining 8 low +// bits we actually need while keeping the high bits at zero. +template <typename T> +static inline T addlow(T x, T y) { + typedef VectorType<uint8_t, sizeof(T)> bytes; + return bit_cast<T>(bit_cast<bytes>(x) + bit_cast<bytes>(y)); +} + +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}; + const WideRGBA8 ALPHA_OPAQUE = {0, 0, 0, 255, 0, 0, 0, 255, + 0, 0, 0, 255, 0, 0, 0, 255}; + switch (blend_key) { + case BLEND_KEY_NONE: + return src; + case BLEND_KEY(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE): + // dst + src.a*(src.rgb1 - dst.rgb0) + // use addlow for signed overflow + return addlow(dst, + muldiv255(alphas(src), (src | ALPHA_OPAQUE) - (dst & RGB_MASK))); + 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): + // src*(1-dst.a) + dst*1 = src - src*dst.a + dst + return dst + ((src - muldiv255(src, alphas(dst))) & RGB_MASK); + case BLEND_KEY(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR): + // src*k + (1-src)*dst = src*k + dst - src*dst = dst + src*(k - dst) + // use addlow for signed overflow + return addlow(dst, + muldiv255(src, combine(ctx->blendcolor, ctx->blendcolor) - dst)); + 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 <bool DISCARD> +static inline void discard_output(uint32_t* buf, PackedRGBA8 mask) { + PackedRGBA8 dst = unaligned_load<PackedRGBA8>(buf); + WideRGBA8 r = pack_pixels_RGBA8(); + if (blend_key) r = blend_pixels_RGBA8(dst, r); + if (DISCARD) mask |= bit_cast<PackedRGBA8>(fragment_shader->isPixelDiscarded); + unaligned_store(buf, (mask & dst) | (~mask & pack(r))); +} + +template <bool DISCARD> +static inline void discard_output(uint32_t* buf) { + discard_output<DISCARD>(buf, 0); +} + +template <> +inline void discard_output<false>(uint32_t* buf) { + WideRGBA8 r = pack_pixels_RGBA8(); + if (blend_key) r = blend_pixels_RGBA8(unaligned_load<PackedRGBA8>(buf), r); + unaligned_store(buf, pack(r)); +} + +static inline PackedRGBA8 span_mask_RGBA8(int span) { + return bit_cast<PackedRGBA8>(I32(span) < I32{1, 2, 3, 4}); +} + +static inline PackedRGBA8 span_mask(uint32_t*, int span) { + return span_mask_RGBA8(span); +} + +static inline WideR8 pack_pixels_R8(Float c) { + return packR8(round_pixel(c)); +} + +static inline WideR8 pack_pixels_R8() { + return pack_pixels_R8(fragment_shader->gl_FragColor.x); +} + +template <typename C> +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 <bool DISCARD> +static inline void discard_output(uint8_t* buf, WideR8 mask) { + WideR8 dst = unpack(unaligned_load<PackedR8>(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 <bool DISCARD> +static inline void discard_output(uint8_t* buf) { + discard_output<DISCARD>(buf, 0); +} + +template <> +inline void discard_output<false>(uint8_t* buf) { + WideR8 r = pack_pixels_R8(); + if (blend_key) r = blend_pixels_R8(unpack(unaligned_load<PackedR8>(buf)), r); + unaligned_store(buf, pack(r)); +} + +static inline WideR8 span_mask_R8(int span) { + return bit_cast<WideR8>(WideR8(span) < WideR8{1, 2, 3, 4}); +} + +static inline WideR8 span_mask(uint8_t*, int span) { + return span_mask_R8(span); +} + +template <bool DISCARD, bool W, typename P, typename M> +static inline void commit_output(P* buf, M mask) { + fragment_shader->run<W>(); + discard_output<DISCARD>(buf, mask); +} + +template <bool DISCARD, bool W, typename P> +static inline void commit_output(P* buf) { + fragment_shader->run<W>(); + discard_output<DISCARD>(buf); +} + +template <bool DISCARD, bool W, typename P> +static inline void commit_output(P* buf, int span) { + commit_output<DISCARD, W>(buf, span_mask(buf, span)); +} + +template <bool DISCARD, bool W, typename P, typename Z> +static inline void commit_output(P* buf, Z z, uint16_t* zbuf) { + ZMask4 zmask; + if (check_depth4<true, DISCARD>(z, zbuf, zmask)) { + commit_output<DISCARD, W>(buf, unpack(zmask, buf)); + if (DISCARD) { + discard_depth(z, zbuf, zmask); + } + } else { + fragment_shader->skip<W>(); + } +} + +template <bool DISCARD, bool W, typename P, typename Z> +static inline void commit_output(P* buf, Z z, uint16_t* zbuf, int span) { + ZMask4 zmask; + if (check_depth4<false, DISCARD>(z, zbuf, zmask, span)) { + commit_output<DISCARD, W>(buf, unpack(zmask, buf)); + if (DISCARD) { + discard_depth(z, zbuf, zmask); + } + } +} + +static inline void commit_span(uint32_t* buf, PackedRGBA8 r) { + if (blend_key) + r = pack(blend_pixels_RGBA8(unaligned_load<PackedRGBA8>(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<PackedRGBA8>(buf), src))); + } + } else { + fill_n(buf, len, bit_cast<U32>(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<PackedRGBA8>(src); + unaligned_store(buf, pack(blend_pixels_RGBA8( + unaligned_load<PackedRGBA8>(buf), unpack(r)))); + } + } else { + memcpy(buf, src, len * sizeof(uint32_t)); + } +} + +static inline void commit_span(uint8_t* buf, PackedR8 r) { + if (blend_key) + r = pack(blend_pixels_R8(unpack(unaligned_load<PackedR8>(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<PackedR8>(buf)), src))); + } + } else { + fill_n((uint32_t*)buf, len / 4, bit_cast<uint32_t>(r)); + } +} + +#define DISPATCH_DRAW_SPAN(self, buf, len) do { \ + int drawn = self->draw_span(buf, len); \ + if (drawn) self->step_interp_inputs(drawn >> 2); \ + for (buf += drawn; drawn < len; drawn += 4, buf += 4) { \ + run(self); \ + commit_span(buf, pack_span(buf)); \ + } \ +} while (0) + +#include "texture.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wuninitialized" @@ -2627,14 +2627,942 @@ void CopyTexSubImage2D(GLenum target, UNUSED GLint level, GLint xoffset, #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #ifdef __clang__ -# pragma GCC diagnostic ignored "-Wunused-private-field" +#pragma GCC diagnostic ignored "-Wunused-private-field" #else -# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #endif #include "load_shader.h" #pragma GCC diagnostic pop -#include "rasterize.h" +typedef vec2_scalar Point2D; +typedef vec4_scalar Point3D; + +struct ClipRect { + float x0; + float y0; + float x1; + float y1; + + ClipRect(const IntRect& i) : x0(i.x0), y0(i.y0), x1(i.x1), y1(i.y1) {} + ClipRect(Texture& t) : ClipRect(ctx->apply_scissor(t.bounds())) {} + + template <typename P> + bool overlaps(int nump, const P* p) const { + // Generate a mask of which side of the clip rect all of a polygon's points + // fall inside of. This is a cheap conservative estimate of whether the + // bounding box of the polygon might overlap the clip rect, rather than an + // exact test that would require multiple slower line intersections. + int sides = 0; + for (int i = 0; i < nump; i++) { + sides |= p[i].x < x1 ? (p[i].x > x0 ? 1 | 2 : 1) : 2; + sides |= p[i].y < y1 ? (p[i].y > y0 ? 4 | 8 : 4) : 8; + } + return sides == 0xF; + } +}; + +// Helper function for drawing 8-pixel wide chunks of a span with depth buffer. +// Using 8-pixel chunks maximizes use of 16-bit depth values in 128-bit wide +// SIMD register. However, since fragment shaders process only 4 pixels per +// invocation, we need to run fragment shader twice for every 8 pixel batch +// of results we get from the depth test. Perspective is not supported. +template <int FUNC, bool MASK, typename P> +static inline void draw_depth_span(uint16_t z, P* buf, uint16_t* depth, + int span) { + int skip = 0; + // Check if the fragment shader has an optimized draw specialization. + if (fragment_shader->has_draw_span(buf)) { + // The loop tries to accumulate runs of pixels that passed (len) and + // runs of pixels that failed (skip). This allows it to pass the largest + // possible span in between changes in depth pass or fail status to the + // fragment shader's draw specialer. + int len = 0; + do { + ZMask8 zmask; + // Process depth in 8-pixel chunks. + switch (check_depth8<FUNC, MASK>(z, depth, zmask)) { + case 0: // All pixels failed the depth test. + if (len) { + // Flush out passed pixels. + fragment_shader->draw_span(buf - len, len); + len = 0; + } + // Accumulate 2 skipped chunks. + skip += 2; + break; + case -1: // All pixels passed the depth test. + if (skip) { + // Flushed out any skipped chunks. + fragment_shader->skip(skip); + skip = 0; + } + // Accumulate 8 passed pixels. + len += 8; + break; + default: // Mixture of pass and fail results. + if (len) { + // Flush out any passed pixels. + fragment_shader->draw_span(buf - len, len); + len = 0; + } else if (skip) { + // Flush out any skipped chunks. + fragment_shader->skip(skip); + skip = 0; + } + // Run fragment shader on first 4 depth results. + commit_output<false, false>(buf, unpack(lowHalf(zmask), buf)); + // Run fragment shader on next 4 depth results. + commit_output<false, false>(buf + 4, unpack(highHalf(zmask), buf)); + break; + } + // Advance to next 8 pixels... + buf += 8; + depth += 8; + span -= 8; + } while (span >= 8); + // Flush out any remaining passed pixels. + if (len) { + fragment_shader->draw_span(buf - len, len); + } + } else { + // No draw specialization, so we can use a simpler loop here that just + // accumulates depth failures, but otherwise invokes fragment shader + // immediately on depth pass. + do { + ZMask8 zmask; + // Process depth in 8-pixel chunks. + switch (check_depth8<FUNC, MASK>(z, depth, zmask)) { + case 0: // All pixels failed the depth test. + // Accumulate 2 skipped chunks. + skip += 2; + break; + case -1: // All pixels passed the depth test. + if (skip) { + // Flush out any skipped chunks. + fragment_shader->skip(skip); + skip = 0; + } + // Run the fragment shader for two 4-pixel chunks. + commit_output<false, false>(buf); + commit_output<false, false>(buf + 4); + break; + default: // Mixture of pass and fail results. + if (skip) { + // Flush out any skipped chunks. + fragment_shader->skip(skip); + skip = 0; + } + // Run fragment shader on first 4 depth results. + commit_output<false, false>(buf, unpack(lowHalf(zmask), buf)); + // Run fragment shader on next 4 depth results. + commit_output<false, false>(buf + 4, unpack(highHalf(zmask), buf)); + break; + } + // Advance to next 8 pixels... + buf += 8; + depth += 8; + span -= 8; + } while (span >= 8); + } + // Flush out any remaining skipped chunks. + if (skip) { + fragment_shader->skip(skip); + } +} + +// Draw a simple span in 4-pixel wide chunks, optionally using depth. +template <bool DISCARD, bool W, typename P, typename Z> +static ALWAYS_INLINE void draw_span(P* buf, uint16_t* depth, int span, Z z) { + if (depth) { + // Depth testing is enabled. If perspective is used, Z values will vary + // across the span, we use packDepth to generate 16-bit Z values suitable + // for depth testing based on current values from gl_FragCoord.z. + // Otherwise, for the no-perspective case, we just use the provided Z. + // Process 4-pixel chunks first. + for (; span >= 4; span -= 4, buf += 4, depth += 4) { + commit_output<DISCARD, W>(buf, z(), depth); + } + // If there are any remaining pixels, do a partial chunk. + if (span > 0) { + commit_output<DISCARD, W>(buf, z(), depth, span); + } + } else { + // Process 4-pixel chunks first. + for (; span >= 4; span -= 4, buf += 4) { + commit_output<DISCARD, W>(buf); + } + // If there are any remaining pixels, do a partial chunk. + if (span > 0) { + commit_output<DISCARD, W>(buf, span); + } + } +} + +// Draw spans for each row of a given quad (or triangle) with a constant Z +// value. The quad is assumed convex. It is clipped to fall within the given +// clip rect. In short, this function rasterizes a quad by first finding a +// top most starting point and then from there tracing down the left and right +// sides of this quad until it hits the bottom, outputting a span between the +// current left and right positions at each row along the way. Points are +// assumed to be ordered in either CW or CCW to support this, but currently +// both orders (CW and CCW) are supported and equivalent. +template <typename P> +static inline void draw_quad_spans(int nump, Point2D p[4], uint16_t z, + Interpolants interp_outs[4], + Texture& colortex, int layer, + Texture& depthtex, + const ClipRect& clipRect) { + // Only triangles and convex quads supported. + assert(nump == 3 || nump == 4); + Point2D l0, r0, l1, r1; + int l0i, r0i, l1i, r1i; + { + // Find the index of the top-most (smallest Y) point from which + // rasterization can start. + 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)); + // Helper to find next index in the points array, walking forward. +#define NEXT_POINT(idx) \ + ({ \ + int cur = (idx) + 1; \ + cur < nump ? cur : 0; \ + }) + // Helper to find the previous index in the points array, walking backward. +#define PREV_POINT(idx) \ + ({ \ + int cur = (idx)-1; \ + cur >= 0 ? cur : nump - 1; \ + }) + // Start looking for "left"-side and "right"-side descending edges starting + // from the determined top point. + int next = NEXT_POINT(top); + int prev = PREV_POINT(top); + if (p[top].y == p[next].y) { + // If the next point is on the same row as the top, then advance one more + // time to the next point and use that as the "left" descending edge. + l0i = next; + l1i = NEXT_POINT(next); + // Assume top and prev form a descending "right" edge, as otherwise this + // will be a collapsed polygon and harmlessly bail out down below. + r0i = top; + r1i = prev; + } else if (p[top].y == p[prev].y) { + // If the prev point is on the same row as the top, then advance to the + // prev again and use that as the "right" descending edge. + // Assume top and next form a non-empty descending "left" edge. + l0i = top; + l1i = next; + r0i = prev; + r1i = PREV_POINT(prev); + } else { + // Both next and prev are on distinct rows from top, so both "left" and + // "right" edges are non-empty/descending. + l0i = r0i = top; + l1i = next; + r1i = prev; + } + // Load the points from the indices. + l0 = p[l0i]; // Start of left edge + r0 = p[r0i]; // End of left edge + l1 = p[l1i]; // Start of right edge + r1 = p[r1i]; // End of right edge + // 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); + } + + struct Edge + { + float yScale; + float xSlope; + float x; + Interpolants interpSlope; + Interpolants interp; + + Edge(float y, const Point2D& p0, const Point2D& p1, + const Interpolants& i0, const Interpolants& i1) : + // Inverse Y scale for slope calculations. Avoid divide on 0-length edge. + // Later checks below ensure that Y <= p1.y, or otherwise we don't use + // this edge. We just need to guard against Y == p1.y == p0.y. In that + // case, Y - p0.y == 0 and will cancel out the slopes below, except if + // yScale is Inf for some reason (or worse, NaN), which 1/(p1.y-p0.y) + // might produce if we don't bound it. + yScale(1.0f / max(p1.y - p0.y, 1.0f / 256)), + // Calculate dX/dY slope + xSlope((p1.x - p0.x) * yScale), + // Initialize current X based on Y and slope + x(p0.x + (y - p0.y) * xSlope), + // Calculate change in interpolants per change in Y + interpSlope((i1 - i0) * yScale), + // Initialize current interpolants based on Y and slope + interp(i0 + (y - p0.y) * interpSlope) + {} + + void nextRow() { + // step current X and interpolants to next row from slope + x += xSlope; + interp += interpSlope; + } + }; + + // Vertex selection above should result in equal left and right start rows + assert(l0.y == r0.y); + // Find the start y, clip to within the clip rect, and round to row center. + float y = floor(max(l0.y, clipRect.y0) + 0.5f) + 0.5f; + // Initialize left and right edges from end points and start Y + Edge left(y, l0, l1, interp_outs[l0i], interp_outs[l1i]); + Edge right(y, r0, r1, interp_outs[r0i], interp_outs[r1i]); + // Get pointer to color buffer and depth buffer at current Y + P* fbuf = (P*)colortex.sample_ptr(0, int(y), layer, sizeof(P)); + uint16_t* fdepth = + (uint16_t*)depthtex.sample_ptr(0, int(y), 0, sizeof(uint16_t)); + // Loop along advancing Ys, rasterizing spans at each row + float checkY = min(min(l1.y, r1.y), clipRect.y1); + for (;;) { + // Check if we maybe passed edge ends or outside clip rect... + if (y > checkY) { + // If we're outside the clip rect, we're done. + if (y > clipRect.y1) break; + // Helper to find the next non-duplicate vertex that doesn't loop back. +#define STEP_EDGE(e0i, e0, e1i, e1, STEP_POINT, end) \ + for (;;) { \ + /* Set new start of edge to be end of old edge */ \ + e0i = e1i; \ + e0 = e1; \ + /* Set new end of edge to next point */ \ + e1i = STEP_POINT(e1i); \ + e1 = p[e1i]; \ + /* If the edge is descending, use it. */ \ + if (e1.y > e0.y) break; \ + /* If the edge is ascending or crossed the end, we're done. */ \ + if (e1.y < e0.y || e0i == end) return; \ + /* Otherwise, it's a duplicate, so keep searching. */ \ + } + // Check if Y advanced past the end of the left edge + if (y > l1.y) { + // Step to next left edge past Y and reset edge interpolants. + do { STEP_EDGE(l0i, l0, l1i, l1, NEXT_POINT, r1i); } while (y > l1.y); + left = Edge(y, l0, l1, interp_outs[l0i], interp_outs[l1i]); + } + // Check if Y advanced past the end of the right edge + if (y > r1.y) { + // Step to next right edge past Y and reset edge interpolants. + do { STEP_EDGE(r0i, r0, r1i, r1, PREV_POINT, l1i); } while (y > r1.y); + right = Edge(y, r0, r1, interp_outs[r0i], interp_outs[r1i]); + } + // Reset check condition for next time around. + checkY = min(min(l1.y, r1.y), clipRect.y1); + } + // lx..rx form the bounds of the span. WR does not use backface culling, + // so we need to use min/max to support the span in either orientation. + // Clip the span to fall within the clip rect and then round to nearest + // column. + int startx = int(max(min(left.x, right.x), clipRect.x0) + 0.5f); + int endx = int(min(max(left.x, right.x), clipRect.x1) + 0.5f); + // Check if span is non-empty. + int span = endx - startx; + if (span > 0) { + ctx->shaded_rows++; + ctx->shaded_pixels += span; + // Advance color/depth buffer pointers to the start of the span. + P* buf = fbuf + startx; + // Check if the we will need to use depth-buffer or discard on this span. + uint16_t* depth = depthtex.buf != nullptr ? fdepth + startx : nullptr; + bool use_discard = fragment_shader->use_discard(); + if (depthtex.delay_clear) { + // Delayed clear is enabled for the depth buffer. Check if this row + // needs to be cleared. + int yi = int(y); + uint32_t& mask = depthtex.cleared_rows[yi / 32]; + if ((mask & (1 << (yi & 31))) == 0) { + // The depth buffer is unitialized on this row, but we know it will + // thus be cleared entirely to the clear value. This lets us quickly + // check the constant Z value of the quad against the clear Z to know + // if the entire span passes or fails the depth test all at once. + 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 we got here, we passed the depth test. + if (ctx->depthmask) { + // Depth writes are enabled, so we need to initialize depth. + mask |= 1 << (yi & 31); + depthtex.delay_clear--; + if (use_discard) { + // if discard is enabled, we don't know what pixels may be + // written to, so we have to clear the entire row. + force_clear_row<uint16_t>(depthtex, yi); + } else { + // Otherwise, we only need to clear the pixels that fall outside + // the current span on this row. + if (startx > 0 || endx < depthtex.width) { + force_clear_row<uint16_t>(depthtex, yi, startx, endx); + } + // Fill in the span's Z values with constant Z. + clear_buffer<uint16_t>(depthtex, z, 0, + IntRect{startx, yi, endx, yi + 1}); + // We already passed the depth test, so no need to test depth + // any more. + depth = nullptr; + } + } else { + // No depth writes, so don't clear anything, and no need to test. + depth = nullptr; + } + } + } + if (colortex.delay_clear) { + // Delayed clear is enabled for the color buffer. Check if needs 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 (depth || blend_key || use_discard) { + // If depth test, blending, or discard is used, old color values + // might be sampled, so we need to clear the entire row to fill it. + force_clear_row<P>(colortex, yi); + } else if (startx > 0 || endx < colortex.width) { + // Otherwise, we only need to clear the row outside of the span. + // The fragment shader will fill the row within the span itself. + force_clear_row<P>(colortex, yi, startx, endx); + } + } + } + // Initialize fragment shader interpolants to current span position. + fragment_shader->gl_FragCoord.x = init_interp(startx + 0.5f, 1); + fragment_shader->gl_FragCoord.y = y; + { + // Change in interpolants is difference between current right and left + // edges per the change in right and left X. + Interpolants step = + (right.interp - left.interp) * (1.0f / (right.x - left.x)); + // Advance current interpolants to X at start of span. + Interpolants o = left.interp + step * (startx + 0.5f - left.x); + fragment_shader->init_span(&o, &step, 4.0f); + } + if (!use_discard) { + // Fast paths for the case where fragment discard is not used. + if (depth) { + // If depth is used, we want to process spans in 8-pixel chunks to + // maximize sampling and testing 16-bit depth values within the 128- + // bit width of a SIMD register. + if (span >= 8) { + // Specializations for supported depth functions depending on + // whether depth writes are enabled. + if (ctx->depthfunc == GL_LEQUAL) { + if (ctx->depthmask) + draw_depth_span<GL_LEQUAL, true>(z, buf, depth, span); + else + draw_depth_span<GL_LEQUAL, false>(z, buf, depth, span); + } else { + if (ctx->depthmask) + draw_depth_span<GL_LESS, true>(z, buf, depth, span); + else + draw_depth_span<GL_LESS, false>(z, buf, depth, span); + } + // Advance buffers past processed chunks. + buf += span & ~7; + depth += span & ~7; + span &= 7; + } + } else { + // Check if the fragment shader has an optimized draw specialization. + if (span >= 4 && fragment_shader->has_draw_span(buf)) { + // Draw specialization expects 4-pixel chunks. + int len = span & ~3; + fragment_shader->draw_span(buf, len); + buf += len; + span &= 3; + } + } + draw_span<false, false>(buf, depth, span, [=]{ return z; }); + } else { + // If discard is used, then use slower fallbacks. This should be rare. + // Just needs to work, doesn't need to be too fast yet... + draw_span<true, false>(buf, depth, span, [=]{ return z; }); + } + } + next_span: + // Advance Y and edge interpolants to next row. + y++; + left.nextRow(); + right.nextRow(); + // Advance buffers to next row. + fbuf += colortex.stride(sizeof(P)) / sizeof(P); + fdepth += depthtex.stride(sizeof(uint16_t)) / sizeof(uint16_t); + } +} + +// Draw perspective-correct spans for a convex quad that has been clipped to +// the near and far Z planes, possibly producing a clipped convex polygon with +// more than 4 sides. This assumes the Z value will vary across the spans and +// requires interpolants to factor in W values. This tends to be slower than +// the simpler 2D draw_quad_spans above, especially since we can't optimize the +// depth test easily when Z values, and should be used only rarely if possible. +template <typename P> +static inline void draw_perspective_spans(int nump, Point3D* p, + Interpolants* interp_outs, + Texture& colortex, int layer, + Texture& depthtex, + const ClipRect& clipRect) { + Point3D l0, r0, l1, r1; + int l0i, r0i, l1i, r1i; + { + // Find the index of the top-most point (smallest Y) from which + // rasterization can start. + int top = 0; + for (int i = 1; i < nump; i++) { + if (p[i].y < p[top].y) { + top = i; + } + } + // Find left-most top point, the start of the left descending edge. + // Advance forward in the points array, searching at most nump points + // in case the polygon is flat. + l0i = top; + for (int i = top + 1; i < nump && p[i].y == p[top].y; i++) { + l0i = i; + } + if (l0i == nump - 1) { + for (int i = 0; i <= top && p[i].y == p[top].y; i++) { + l0i = i; + } + } + // Find right-most top point, the start of the right descending edge. + // Advance backward in the points array, searching at most nump points. + r0i = top; + for (int i = top - 1; i >= 0 && p[i].y == p[top].y; i--) { + r0i = i; + } + if (r0i == 0) { + for (int i = nump - 1; i >= top && p[i].y == p[top].y; i--) { + r0i = i; + } + } + // End of left edge is next point after left edge start. + l1i = NEXT_POINT(l0i); + // End of right edge is prev point after right edge start. + r1i = PREV_POINT(r0i); + l0 = p[l0i]; // Start of left edge + r0 = p[r0i]; // End of left edge + l1 = p[l1i]; // Start of right edge + r1 = p[r1i]; // End of right edge + } + + struct Edge + { + float yScale; + // Current coordinates for edge. Where in the 2D case of draw_quad_spans, + // it is enough to just track the X coordinate as we advance along the rows, + // for the perspective case we also need to keep track of Z and W. For + // simplicity, we just use the full 3D point to track all these coordinates. + Point3D pSlope; + Point3D p; + Interpolants interpSlope; + Interpolants interp; + + Edge(float y, const Point3D& p0, const Point3D& p1, + const Interpolants& i0, const Interpolants& i1) : + // Inverse Y scale for slope calculations. Avoid divide on 0-length edge. + yScale(1.0f / max(p1.y - p0.y, 1.0f / 256)), + // Calculate dX/dY slope + pSlope((p1 - p0) * yScale), + // Initialize current coords based on Y and slope + p(p0 + (y - p0.y) * pSlope), + // Crucially, these interpolants must be scaled by the point's 1/w value, + // which allows linear interpolation in a perspective-correct manner. + // This will be canceled out inside the fragment shader later. + // Calculate change in interpolants per change in Y + interpSlope((i1 * p1.w - i0 * p0.w) * yScale), + // Initialize current interpolants based on Y and slope + interp(i0 * p0.w + (y - p0.y) * interpSlope) + {} + + float x() const { return p.x; } + vec2_scalar zw() const { return {p.z, p.w}; } + + void nextRow() { + // step current coords and interpolants to next row from slope + p += pSlope; + interp += interpSlope; + } + }; + + // Vertex selection above should result in equal left and right start rows + assert(l0.y == r0.y); + // Find the start y, clip to within the clip rect, and round to row center. + float y = floor(max(l0.y, clipRect.y0) + 0.5f) + 0.5f; + // Initialize left and right edges from end points and start Y + Edge left(y, l0, l1, interp_outs[l0i], interp_outs[l1i]); + Edge right(y, r0, r1, interp_outs[r0i], interp_outs[r1i]); + // Get pointer to color buffer and depth buffer at current Y + P* fbuf = (P*)colortex.sample_ptr(0, int(y), layer, sizeof(P)); + uint16_t* fdepth = + (uint16_t*)depthtex.sample_ptr(0, int(y), 0, sizeof(uint16_t)); + // Loop along advancing Ys, rasterizing spans at each row + float checkY = min(min(l1.y, r1.y), clipRect.y1); + for (;;) { + // Check if we maybe passed edge ends or outside clip rect... + if (y > checkY) { + // If we're outside the clip rect, we're done. + if (y > clipRect.y1) break; + // Check if Y advanced past the end of the left edge + if (y > l1.y) { + // Step to next left edge past Y and reset edge interpolants. + do { STEP_EDGE(l0i, l0, l1i, l1, NEXT_POINT, r1i); } while (y > l1.y); + left = Edge(y, l0, l1, interp_outs[l0i], interp_outs[l1i]); + } + // Check if Y advanced past the end of the right edge + if (y > r1.y) { + // Step to next right edge past Y and reset edge interpolants. + do { STEP_EDGE(r0i, r0, r1i, r1, PREV_POINT, l1i); } while (y > r1.y); + right = Edge(y, r0, r1, interp_outs[r0i], interp_outs[r1i]); + } + // Reset check condition for next time around. + checkY = min(min(l1.y, r1.y), clipRect.y1); + } + // lx..rx form the bounds of the span. WR does not use backface culling, + // so we need to use min/max to support the span in either orientation. + // Clip the span to fall within the clip rect and then round to nearest + // column. + int startx = int(max(min(left.x(), right.x()), clipRect.x0) + 0.5f); + int endx = int(min(max(left.x(), right.x()), clipRect.x1) + 0.5f); + // Check if span is non-empty. + int span = endx - startx; + if (span > 0) { + ctx->shaded_rows++; + ctx->shaded_pixels += span; + // Advance color/depth buffer pointers to the start of the span. + P* buf = fbuf + startx; + // Check if the we will need to use depth-buffer or discard on this span. + uint16_t* depth = depthtex.buf != nullptr ? fdepth + startx : nullptr; + bool use_discard = fragment_shader->use_discard(); + if (depthtex.delay_clear) { + // Delayed clear is enabled for the depth buffer. Check if this row + // needs to be cleared. + int yi = int(y); + uint32_t& mask = depthtex.cleared_rows[yi / 32]; + if ((mask & (1 << (yi & 31))) == 0) { + mask |= 1 << (yi & 31); + depthtex.delay_clear--; + // Since Z varies across the span, it's easier to just clear the + // row and rely on later depth testing. If necessary, this could be + // optimized to test against the start and end Z values of the span + // here. + force_clear_row<uint16_t>(depthtex, yi); + } + } + if (colortex.delay_clear) { + // Delayed clear is enabled for the color buffer. Check if needs 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 (depth || blend_key || use_discard) { + // If depth test, blending, or discard is used, old color values + // might be sampled, so we need to clear the entire row to fill it. + force_clear_row<P>(colortex, yi); + } else if (startx > 0 || endx < colortex.width) { + // Otherwise, we only need to clear the row outside of the span. + // The fragment shader will fill the row within the span itself. + force_clear_row<P>(colortex, yi, startx, endx); + } + } + } + // Initialize fragment shader interpolants to current span position. + fragment_shader->gl_FragCoord.x = init_interp(startx + 0.5f, 1); + fragment_shader->gl_FragCoord.y = y; + { + // Calculate the fragment Z and W change per change in fragment X step. + vec2_scalar stepZW = + (right.zw() - left.zw()) * (1.0f / (right.x() - left.x())); + // Calculate initial Z and W values for span start. + vec2_scalar zw = left.zw() + stepZW * (startx + 0.5f - left.x()); + // Set fragment shader's Z and W values so that it can use them to + // cancel out the 1/w baked into the interpolants. + fragment_shader->gl_FragCoord.z = init_interp(zw.x, stepZW.x); + fragment_shader->gl_FragCoord.w = init_interp(zw.y, stepZW.y); + fragment_shader->stepZW = stepZW * 4.0f; + // Change in interpolants is difference between current right and left + // edges per the change in right and left X. The left and right + // interpolant values were previously multipled by 1/w, so the step and + // initial span values take this into account. + Interpolants step = + (right.interp - left.interp) * (1.0f / (right.x() - left.x())); + // Advance current interpolants to X at start of span. + Interpolants o = left.interp + step * (startx + 0.5f - left.x()); + fragment_shader->init_span<true>(&o, &step, 4.0f); + } + if (!use_discard) { + // No discard is used. Common case. + draw_span<false, true>(buf, depth, span, packDepth); + } else { + // Discard is used. Rare. + draw_span<true, true>(buf, depth, span, packDepth); + } + } + // Advance Y and edge interpolants to next row. + y++; + left.nextRow(); + right.nextRow(); + // Advance buffers to next row. + fbuf += colortex.stride(sizeof(P)) / sizeof(P); + fdepth += depthtex.stride(sizeof(uint16_t)) / sizeof(uint16_t); + } +} + +// Clip a primitive against both sides of a view-frustum axis, producing +// intermediate vertexes with interpolated attributes that will no longer +// intersect the selected axis planes. This assumes the primitive is convex +// and should produce at most N+2 vertexes for each invocation (only in the +// worst case where one point falls outside on each of the opposite sides +// with the rest of the points inside). +template <XYZW AXIS> +static int clip_side(int nump, Point3D* p, Interpolants* interp, Point3D* outP, + Interpolants* outInterp) { + int numClip = 0; + Point3D prev = p[nump - 1]; + Interpolants prevInterp = interp[nump - 1]; + float prevCoord = prev.select(AXIS); + // Coordinate must satisfy -W <= C <= W. Determine if it is outside, and + // if so, remember which side it is outside of. + int prevSide = prevCoord < -prev.w ? -1 : (prevCoord > prev.w ? 1 : 0); + // Loop through points, finding edges that cross the planes by evaluating + // the side at each point. + for (int i = 0; i < nump; i++) { + Point3D cur = p[i]; + Interpolants curInterp = interp[i]; + float curCoord = cur.select(AXIS); + int curSide = curCoord < -cur.w ? -1 : (curCoord > cur.w ? 1 : 0); + // Check if the previous and current end points are on different sides. + if (curSide != prevSide) { + // One of the edge's end points is outside the plane with the other + // inside the plane. Find the offset where it crosses the plane and + // adjust the point and interpolants to there. + if (prevSide) { + // Edge that was previously outside crosses inside. + // Evaluate plane equation for previous and current end-point + // based on previous side and calculate relative offset. + assert(numClip < nump + 2); + float prevDist = prevCoord - prevSide * prev.w; + float curDist = curCoord - prevSide * cur.w; + float k = prevDist / (prevDist - curDist); + outP[numClip] = prev + (cur - prev) * k; + outInterp[numClip] = prevInterp + (curInterp - prevInterp) * k; + numClip++; + } + if (curSide) { + // Edge that was previously inside crosses outside. + // Evaluate plane equation for previous and current end-point + // based on current side and calculate relative offset. + assert(numClip < nump + 2); + float prevDist = prevCoord - curSide * prev.w; + float curDist = curCoord - curSide * cur.w; + float k = prevDist / (prevDist - curDist); + outP[numClip] = prev + (cur - prev) * k; + outInterp[numClip] = prevInterp + (curInterp - prevInterp) * k; + numClip++; + } + } + if (!curSide) { + // The current end point is inside the plane, so output point unmodified. + assert(numClip < nump + 2); + outP[numClip] = cur; + outInterp[numClip] = curInterp; + numClip++; + } + prev = cur; + prevInterp = curInterp; + prevCoord = curCoord; + prevSide = curSide; + } + return numClip; +} + +// Helper function to dispatch to perspective span drawing with points that +// have already been transformed and clipped. +static inline void draw_perspective_clipped(int nump, Point3D* p_clip, + Interpolants* interp_clip, + Texture& colortex, int layer, + Texture& depthtex) { + // If polygon is ouside clip rect, nothing to draw. + ClipRect clipRect(colortex); + if (!clipRect.overlaps(nump, p_clip)) { + return; + } + + // Finally draw perspective-correct spans for the polygon. + if (colortex.internal_format == GL_RGBA8) { + draw_perspective_spans<uint32_t>(nump, p_clip, interp_clip, colortex, + layer, depthtex, clipRect); + } else if (colortex.internal_format == GL_R8) { + draw_perspective_spans<uint8_t>(nump, p_clip, interp_clip, colortex, + layer, depthtex, clipRect); + } else { + assert(false); + } +} + +// Draws a perspective-correct 3D primitive with varying Z value, as opposed +// to a simple 2D planar primitive with a constant Z value that could be +// trivially Z rejected. This requires clipping the primitive against the near +// and far planes to ensure it stays within the valid Z-buffer range. The Z +// and W of each fragment of the primitives are interpolated across the +// generated spans and then depth-tested as appropriate. +// Additionally, vertex attributes must be interpolated with perspective- +// correction by dividing by W before interpolation, and then later multiplied +// by W again to produce the final correct attribute value for each fragment. +// This process is expensive and should be avoided if possible for primitive +// batches that are known ahead of time to not need perspective-correction. +static void draw_perspective(int nump, + Interpolants interp_outs[4], + Texture& colortex, int layer, + Texture& depthtex) { + // Convert output of vertex shader to screen space. + vec4 pos = vertex_shader->gl_Position; + vec3_scalar scale = + vec3_scalar(ctx->viewport.width(), ctx->viewport.height(), 1) * 0.5f; + vec3_scalar offset = + vec3_scalar(ctx->viewport.x0, ctx->viewport.y0, 0.0f) + scale; + if (test_none(pos.z <= -pos.w || pos.z >= pos.w)) { + // No points cross the near or far planes, so no clipping required. + // Just divide coords by W and convert to viewport. + Float w = 1.0f / pos.w; + vec3 screen = pos.sel(X, Y, Z) * w * scale + offset; + Point3D p[4] = { + {screen.x.x, screen.y.x, screen.z.x, w.x}, + {screen.x.y, screen.y.y, screen.z.y, w.y}, + {screen.x.z, screen.y.z, screen.z.z, w.z}, + {screen.x.w, screen.y.w, screen.z.w, w.w} + }; + draw_perspective_clipped(nump, p, interp_outs, colortex, layer, depthtex); + } else { + // Points cross the near or far planes, so we need to clip. + // Start with the original 3 or 4 points... + Point3D p[4] = { + {pos.x.x, pos.y.x, pos.z.x, pos.w.x}, + {pos.x.y, pos.y.y, pos.z.y, pos.w.y}, + {pos.x.z, pos.y.z, pos.z.z, pos.w.z}, + {pos.x.w, pos.y.w, pos.z.w, pos.w.w} + }; + // Clipping can expand the points by 1 for each of 6 view frustum planes. + Point3D p_clip[4 + 6]; + Interpolants interp_clip[4 + 6]; + // Clip against near and far Z planes. + nump = clip_side<Z>(nump, p, interp_outs, p_clip, interp_clip); + // If no points are left inside the view frustum, there's nothing to draw. + if (nump < 3) { + return; + } + // After clipping against only the near and far planes, we might still + // produce points where W = 0, exactly at the camera plane. OpenGL specifies + // that for clip coordinates, points must satisfy: + // -W <= X <= W + // -W <= Y <= W + // -W <= Z <= W + // When Z = W = 0, this is trivially satisfied, but when we transform and + // divide by W below it will produce a divide by 0. Usually we want to only + // clip Z to avoid the extra work of clipping X and Y. We can still project + // points that fall outside the view frustum X and Y so long as Z is valid. + // The span drawing code will then ensure X and Y are clamped to viewport + // boundaries. However, in the Z = W = 0 case, sometimes clipping X and Y, + // will push W further inside the view frustum so that it is no longer 0, + // allowing us to finally proceed to projecting the points to the screen. + for (int i = 0; i < nump; i++) { + // Found an invalid W, so need to clip against X and Y... + if (p_clip[i].w <= 0.0f) { + // Ping-pong p_clip -> p_tmp -> p_clip. + Point3D p_tmp[4 + 6]; + Interpolants interp_tmp[4 + 6]; + nump = clip_side<X>(nump, p_clip, interp_clip, p_tmp, interp_tmp); + if (nump < 3) return; + nump = clip_side<Y>(nump, p_tmp, interp_tmp, p_clip, interp_clip); + if (nump < 3) return; + // After clipping against X and Y planes, there's still points left + // to draw, so proceed to trying projection now... + break; + } + } + // Divide coords by W and convert to viewport. + for (int i = 0; i < nump; i++) { + float w = 1.0f / p_clip[i].w; + p_clip[i] = Point3D(p_clip[i].sel(X, Y, Z) * w * scale + offset, w); + } + draw_perspective_clipped(nump, p_clip, interp_clip, colortex, layer, + depthtex); + } +} + +static void draw_quad(int nump, Texture& colortex, int layer, + Texture& depthtex) { + // Run vertex shader once for the primitive's vertices. + // Reserve space for 6 sets of interpolants, in case we need to clip against + // near and far planes in the perspective case. + Interpolants interp_outs[4]; + vertex_shader->run_primitive((char*)interp_outs, sizeof(Interpolants)); + vec4 pos = vertex_shader->gl_Position; + // Check if any vertex W is different from another. If so, use perspective. + if (test_any(pos.w != pos.w.x)) { + draw_perspective(nump, interp_outs, colortex, layer, depthtex); + return; + } + + // Convert output of vertex shader to screen space. + // Divide coords by W and convert to viewport. + float w = 1.0f / pos.w.x; + vec2 screen = + (pos.sel(X, Y) * w + 1) * 0.5f * + vec2_scalar(ctx->viewport.width(), ctx->viewport.height()) + + vec2_scalar(ctx->viewport.x0, ctx->viewport.y0); + Point2D 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}}; + + // If quad is ouside clip rect, nothing to draw. + ClipRect clipRect(colortex); + if (!clipRect.overlaps(nump, p)) { + return; + } + + // Since the quad is assumed 2D, Z is constant across the quad. + float screenZ = (pos.z.x * w + 1) * 0.5f; + if (screenZ < 0 || screenZ > 1) { + // Z values would cross the near or far plane, so just bail. + return; + } + // Since Z doesn't need to be interpolated, just set the fragment shader's + // Z and W values here, once and for all fragment shader invocations. + // SSE2 does not support unsigned comparison, so bias Z to be negative. + uint16_t z = uint16_t(0xFFFF * screenZ) - 0x8000; + fragment_shader->gl_FragCoord.z = screenZ; + fragment_shader->gl_FragCoord.w = w; + + // Finally draw 2D spans for the quad. Currently only supports drawing to + // RGBA8 and R8 color buffers. + if (colortex.internal_format == GL_RGBA8) { + draw_quad_spans<uint32_t>(nump, p, z, interp_outs, colortex, layer, + depthtex, clipRect); + } else if (colortex.internal_format == GL_R8) { + draw_quad_spans<uint8_t>(nump, p, z, interp_outs, colortex, layer, depthtex, + clipRect); + } else { + assert(false); + } +} void VertexArray::validate() { int last_enabled = -1; @@ -2653,32 +3581,78 @@ void VertexArray::validate() { max_attrib = last_enabled; } +template <typename INDEX> +static inline void draw_elements(GLsizei count, GLsizei instancecount, + Buffer& indices_buf, size_t offset, + VertexArray& v, Texture& colortex, int layer, + Texture& depthtex) { + assert((offset & (sizeof(INDEX) - 1)) == 0); + INDEX* indices = (INDEX*)(indices_buf.buf + offset); + count = min(count, + (GLsizei)((indices_buf.size - offset) / sizeof(INDEX))); + // Triangles must be indexed at offsets 0, 1, 2. + // Quads must be successive triangles indexed at offsets 0, 1, 2, 2, 1, 3. + if (count == 6 && indices[1] == indices[0] + 1 && + indices[2] == indices[0] + 2 && indices[5] == indices[0] + 3) { + assert(indices[3] == indices[0] + 2 && indices[4] == indices[0] + 1); + // Fast path - since there is only a single quad, we only load per-vertex + // attribs once for all instances, as they won't change across instances + // or within an instance. + vertex_shader->load_attribs(v.attribs, indices[0], 0, 4); + draw_quad(4, colortex, layer, depthtex); + for (GLsizei instance = 1; instance < instancecount; instance++) { + vertex_shader->load_attribs(v.attribs, indices[0], instance, 0); + draw_quad(4, colortex, layer, depthtex); + } + } else { + for (GLsizei instance = 0; instance < instancecount; instance++) { + for (GLsizei i = 0; i + 3 <= count; i += 3) { + if (indices[i + 1] != indices[i] + 1 || + indices[i + 2] != indices[i] + 2) { + continue; + } + int nump = 3; + if (i + 6 <= count && indices[i + 5] == indices[i] + 3) { + assert(indices[i + 3] == indices[i] + 2 && + indices[i + 4] == indices[i] + 1); + nump = 4; + i += 3; + } + vertex_shader->load_attribs(v.attribs, indices[i], instance, nump); + draw_quad(nump, colortex, layer, depthtex); + } + } + } +} + extern "C" { void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, - GLintptr offset, GLsizei instancecount) { - if (offset < 0 || count <= 0 || instancecount <= 0 || !vertex_shader || - !fragment_shader) { + void* indicesptr, GLsizei instancecount) { + assert(mode == GL_TRIANGLES); + assert(type == GL_UNSIGNED_SHORT || type == GL_UNSIGNED_INT); + if (count <= 0 || instancecount <= 0) { return; } - Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER, true); - if (!fb.color_attachment) { - return; - } + Framebuffer& fb = *get_framebuffer(GL_DRAW_FRAMEBUFFER); Texture& colortex = ctx->textures[fb.color_attachment]; if (!colortex.buf) { return; } - assert(!colortex.locked); 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_COMPONENT24); + assert(depthtex.internal_format == GL_DEPTH_COMPONENT16); assert(colortex.width == depthtex.width && colortex.height == depthtex.height); - assert(colortex.offset == depthtex.offset); + } + + Buffer& indices_buf = ctx->buffers[ctx->element_array_buffer_binding]; + size_t offset = (size_t)indicesptr; + if (!indices_buf.buf || offset >= indices_buf.size) { + return; } // debugf("current_vertex_array %d\n", ctx->current_vertex_array); @@ -2689,8 +3663,8 @@ void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, v.validate(); } -#ifdef PRINT_TIMINGS - uint64_t start = get_time_value(); +#ifndef NDEBUG + // uint64_t start = get_time_value(); #endif ctx->shaded_rows = 0; @@ -2698,43 +3672,14 @@ void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, vertex_shader->init_batch(); - switch (type) { - case GL_UNSIGNED_SHORT: - assert(mode == GL_TRIANGLES); - draw_elements<uint16_t>(count, instancecount, offset, v, colortex, - depthtex); - break; - case GL_UNSIGNED_INT: - assert(mode == GL_TRIANGLES); - draw_elements<uint32_t>(count, instancecount, offset, v, colortex, - depthtex); - break; - case GL_NONE: - // Non-standard GL extension - if element type is GL_NONE, then we don't - // use any element buffer and behave as if DrawArrays was called instead. - for (GLsizei instance = 0; instance < instancecount; instance++) { - switch (mode) { - case GL_LINES: - for (GLsizei i = 0; i + 2 <= count; i += 2) { - vertex_shader->load_attribs(v.attribs, offset + i, instance, 2); - draw_quad(2, colortex, depthtex); - } - break; - case GL_TRIANGLES: - for (GLsizei i = 0; i + 3 <= count; i += 3) { - vertex_shader->load_attribs(v.attribs, offset + i, instance, 3); - draw_quad(3, colortex, depthtex); - } - break; - default: - assert(false); - break; - } - } - break; - default: - assert(false); - break; + if (type == GL_UNSIGNED_SHORT) { + draw_elements<uint16_t>(count, instancecount, indices_buf, offset, v, + colortex, fb.layer, depthtex); + } else if (type == GL_UNSIGNED_INT) { + draw_elements<uint32_t>(count, instancecount, indices_buf, offset, v, + colortex, fb.layer, depthtex); + } else { + assert(false); } if (ctx->samples_passed_query) { @@ -2742,66 +3687,329 @@ void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, q.value += ctx->shaded_pixels; } -#ifdef PRINT_TIMINGS - uint64_t end = get_time_value(); - printf( - "%7.3fms draw(%s, %d): %d pixels in %d rows (avg %f pixels/row, " - "%fns/pixel)\n", - double(end - start) / (1000. * 1000.), - ctx->programs[ctx->current_program].impl->get_name(), instancecount, - ctx->shaded_pixels, ctx->shaded_rows, - double(ctx->shaded_pixels) / ctx->shaded_rows, - double(end - start) / max(ctx->shaded_pixels, 1)); +#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() { -#ifdef PRINT_TIMINGS - printf("Finish\n"); -#endif +} // extern "C" + +template <typename P> +static inline void scale_row(P* dst, int dstWidth, const P* src, int srcWidth, + int span) { + int frac = 0; + for (P* end = dst + span; dst < end; dst++) { + *dst = *src; + // Step source according to width ratio. + for (frac += srcWidth; frac >= dstWidth; frac -= dstWidth) { + src++; + } + } } -void MakeCurrent(Context* c) { - if (ctx == c) { +static void scale_blit(Texture& srctex, const IntRect& srcReq, int srcZ, + Texture& dsttex, const IntRect& dstReq, int dstZ, + bool invertY) { + // Cache scaling ratios + int srcWidth = srcReq.width(); + int srcHeight = srcReq.height(); + int dstWidth = dstReq.width(); + int dstHeight = dstReq.height(); + // Compute valid dest bounds + IntRect dstBounds = dsttex.sample_bounds(dstReq, invertY); + // Compute valid source bounds + // Scale source to dest, rounding inward to avoid sampling outside source + IntRect srcBounds = srctex.sample_bounds(srcReq) + .scale(srcWidth, srcHeight, dstWidth, dstHeight, true); + // Limit dest sampling bounds to overlap source bounds + dstBounds.intersect(srcBounds); + // Check if sampling bounds are empty + if (dstBounds.is_empty()) { return; } - ctx = c; - setup_program(ctx ? ctx->current_program : 0); + // Compute final source bounds from clamped dest sampling bounds + srcBounds = IntRect(dstBounds) + .scale(dstWidth, dstHeight, srcWidth, srcHeight); + // Calculate source and dest pointers from clamped offsets + int bpp = srctex.bpp(); + int srcStride = srctex.stride(bpp); + int destStride = dsttex.stride(bpp); + char* dest = dsttex.sample_ptr(dstReq, dstBounds, dstZ, invertY); + char* src = srctex.sample_ptr(srcReq, srcBounds, srcZ); + // Inverted Y must step downward along dest rows + if (invertY) { + destStride = -destStride; + } + int span = dstBounds.width(); + int frac = 0; + for (int rows = dstBounds.height(); rows > 0; rows--) { + if (srcWidth == dstWidth) { + // No scaling, so just do a fast copy. + memcpy(dest, src, span * bpp); + } else { + // Do scaling with different source and dest widths. + switch (bpp) { + case 1: + scale_row((uint8_t*)dest, dstWidth, (uint8_t*)src, srcWidth, span); + break; + case 2: + scale_row((uint16_t*)dest, dstWidth, (uint16_t*)src, srcWidth, span); + break; + case 4: + scale_row((uint32_t*)dest, dstWidth, (uint32_t*)src, srcWidth, span); + break; + default: + assert(false); + break; + } + } + dest += destStride; + // Step source according to height ratio. + for (frac += srcHeight; frac >= dstHeight; frac -= dstHeight) { + src += srcStride; + } + } +} + +static void linear_row(uint32_t* dest, int span, const vec2_scalar& srcUV, + float srcDU, int srcZOffset, sampler2DArray sampler) { + vec2 uv = init_interp(srcUV, vec2_scalar(srcDU, 0.0f)); + for (; span >= 4; span -= 4) { + auto srcpx = textureLinearPackedRGBA8(sampler, ivec2(uv), srcZOffset); + unaligned_store(dest, srcpx); + dest += 4; + uv.x += 4 * srcDU; + } + if (span > 0) { + auto srcpx = textureLinearPackedRGBA8(sampler, ivec2(uv), srcZOffset); + auto mask = span_mask_RGBA8(span); + auto dstpx = unaligned_load<PackedRGBA8>(dest); + unaligned_store(dest, (mask & dstpx) | (~mask & srcpx)); + } } -Context* CreateContext() { return new Context; } +static void linear_row(uint8_t* dest, int span, const vec2_scalar& srcUV, + float srcDU, int srcZOffset, sampler2DArray sampler) { + vec2 uv = init_interp(srcUV, vec2_scalar(srcDU, 0.0f)); + for (; span >= 4; span -= 4) { + auto srcpx = textureLinearPackedR8(sampler, ivec2(uv), srcZOffset); + unaligned_store(dest, pack(srcpx)); + dest += 4; + uv.x += 4 * srcDU; + } + if (span > 0) { + auto srcpx = textureLinearPackedR8(sampler, ivec2(uv), srcZOffset); + auto mask = span_mask_R8(span); + auto dstpx = unpack(unaligned_load<PackedR8>(dest)); + unaligned_store(dest, pack((mask & dstpx) | (~mask & srcpx))); + } +} -void ReferenceContext(Context* c) { - if (!c) { +static void linear_blit(Texture& srctex, const IntRect& srcReq, int srcZ, + Texture& dsttex, const IntRect& dstReq, int dstZ, + bool invertY) { + assert(srctex.internal_format == GL_RGBA8 || + srctex.internal_format == GL_R8); + // Compute valid dest bounds + IntRect dstBounds = dsttex.sample_bounds(dstReq, invertY); + // Check if sampling bounds are empty + if (dstBounds.is_empty()) { return; } - ++c->references; + // Initialize sampler for source texture + sampler2DArray_impl sampler; + init_sampler(&sampler, srctex); + init_depth(&sampler, srctex); + sampler.filter = TextureFilter::LINEAR; + // Compute source UVs + int srcZOffset = srcZ * sampler.height_stride; + vec2_scalar srcUV(srcReq.x0, srcReq.y0); + vec2_scalar srcDUV(float(srcReq.width()) / dstReq.width(), + float(srcReq.height()) / dstReq.height()); + // Skip to clamped source start + srcUV += srcDUV * vec2_scalar(dstBounds.x0, dstBounds.y0); + // Offset source UVs to texel centers and scale by lerp precision + srcUV = linearQuantize(srcUV + 0.5f, 128); + srcDUV *= 128.0f; + // Calculate dest pointer from clamped offsets + int bpp = dsttex.bpp(); + int destStride = dsttex.stride(bpp); + char* dest = dsttex.sample_ptr(dstReq, dstBounds, dstZ, invertY); + // Inverted Y must step downward along dest rows + if (invertY) { + destStride = -destStride; + } + int span = dstBounds.width(); + for (int rows = dstBounds.height(); rows > 0; rows--) { + switch (bpp) { + case 1: + linear_row((uint8_t*)dest, span, srcUV, srcDUV.x, srcZOffset, + &sampler); + break; + case 4: + linear_row((uint32_t*)dest, span, srcUV, srcDUV.x, srcZOffset, + &sampler); + break; + default: + assert(false); + break; + } + dest += destStride; + srcUV.y += srcDUV.y; + } } -void DestroyContext(Context* c) { - if (!c) { +extern "C" { + +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 || srcfb->layer < 0) return; + Framebuffer* dstfb = get_framebuffer(GL_DRAW_FRAMEBUFFER); + if (!dstfb || dstfb->layer < 0) return; + Texture& srctex = ctx->textures[srcfb->color_attachment]; + if (!srctex.buf || srcfb->layer >= max(srctex.depth, 1)) return; + Texture& dsttex = ctx->textures[dstfb->color_attachment]; + if (!dsttex.buf || dstfb->layer >= max(dsttex.depth, 1)) return; + if (srctex.internal_format != dsttex.internal_format) { + assert(false); return; } - assert(c->references > 0); - --c->references; - if (c->references > 0) { + // Force flipped Y onto dest coordinates + if (srcY1 < srcY0) { + swap(srcY0, srcY1); + swap(dstY0, dstY1); + } + bool invertY = dstY1 < dstY0; + if (invertY) { + swap(dstY0, dstY1); + } + IntRect srcReq = {srcX0, srcY0, srcX1, srcY1}; + IntRect dstReq = {dstX0, dstY0, dstX1, dstY1}; + if (srcReq.is_empty() || dstReq.is_empty()) { return; } - if (ctx == c) { - MakeCurrent(nullptr); + prepare_texture(srctex); + prepare_texture(dsttex, &dstReq); + if (!srcReq.same_size(dstReq) && filter == GL_LINEAR && + (srctex.internal_format == GL_RGBA8 || + srctex.internal_format == GL_R8)) { + linear_blit(srctex, srcReq, srcfb->layer, dsttex, dstReq, dstfb->layer, + invertY); + } else { + scale_blit(srctex, srcReq, srcfb->layer, dsttex, dstReq, dstfb->layer, + invertY); } - delete c; } -size_t ReportMemory(size_t (*size_of_op)(void*)) { - size_t size = 0; +void Finish() {} + +void MakeCurrent(void* ctx_ptr) { + ctx = (Context*)ctx_ptr; if (ctx) { - for (auto& t : ctx->textures) { - if (t && t->should_free()) { - size += size_of_op(t->buf); + 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, dstX + srcWidth, dstY + srcHeight}; + prepare_texture(dsttex, &skip); + char* dest = dsttex.sample_ptr(dstX, flip ? dsttex.height - 1 - dstY : dstY, + fb.layer, bpp, dest_stride); + char* src = srctex.sample_ptr(srcX, srcY, 0, bpp, src_stride); + 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<PackedRGBA8>(src)); + WideRGBA8 dstpx = unpack(unaligned_load<PackedRGBA8>(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<PackedRGBA8>(src)); + WideRGBA8 dstpx = unpack(unaligned_load<PackedRGBA8>(dest)); + U32 r = bit_cast<U32>( + 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; } } - return size; } + } // extern "C" diff --git a/third_party/webrender/swgl/src/gl_defs.h b/third_party/webrender/swgl/src/gl_defs.h index 22219366ecf..c7e87230a3d 100644 --- a/third_party/webrender/swgl/src/gl_defs.h +++ b/third_party/webrender/swgl/src/gl_defs.h @@ -15,27 +15,20 @@ typedef float GLfloat; typedef double GLdouble; typedef uint32_t GLenum; -typedef uint8_t GLboolean; +typedef int32_t GLboolean; typedef uint32_t GLbitfield; typedef int32_t GLsizei; typedef size_t GLsizeiptr; typedef intptr_t GLintptr; -#define GL_FALSE 0 -#define GL_TRUE 1 - -#define GL_NONE 0 - #define GL_NO_ERROR 0 #define GL_RGBA32F 0x8814 #define GL_RGBA8 0x8058 #define GL_R8 0x8229 -#define GL_R16 0x822A #define GL_RGBA32I 0x8D82 #define GL_BGRA8 0x93A1 -#define GL_RG8 0x822B #define GL_BYTE 0x1400 #define GL_UNSIGNED_BYTE 0x1401 @@ -44,7 +37,6 @@ typedef intptr_t GLintptr; #define GL_INT 0x1404 #define GL_UNSIGNED_INT 0x1405 #define GL_FLOAT 0x1406 -#define GL_DOUBLE 0x1408 #define GL_RED 0x1903 #define GL_GREEN 0x1904 @@ -54,7 +46,6 @@ typedef intptr_t GLintptr; #define GL_RGBA 0x1908 #define GL_RGBA_INTEGER 0x8D99 #define GL_BGRA 0x80E1 -#define GL_RG 0x8227 #define GL_DEPTH_COMPONENT 0x1902 #define GL_DEPTH_COMPONENT16 0x81A5 @@ -155,8 +146,6 @@ typedef intptr_t GLintptr; #define GL_ONE_MINUS_SRC1_ALPHA 0x88FB #define GL_FUNC_ADD 0x8006 -#define GL_MIN 0x8007 -#define GL_MAX 0x8008 #define GL_NEVER 0x0200 #define GL_LESS 0x0201 @@ -176,9 +165,6 @@ typedef intptr_t GLintptr; #define GL_VERSION 0x1F02 #define GL_EXTENSIONS 0x1F03 #define GL_NUM_EXTENSIONS 0x821D -#define GL_MINOR_VERSION 0x821C -#define GL_MAJOR_VERSION 0x821B -#define GL_SHADING_LANGUAGE_VERSION 0x8B8C #define GL_POINTS 0x0000 #define GL_LINES 0x0001 @@ -188,29 +174,3 @@ typedef intptr_t GLintptr; #define GL_TRIANGLE_STRIP 0x0005 #define GL_TRIANGLE_FAN 0x0006 #define GL_QUADS 0x0007 - -#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 - -#define GL_RGB_422_APPLE 0x8A1F -#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA -#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB -#define GL_RGB_RAW_422_APPLE 0x8A51 - -#define GL_MULTIPLY_KHR 0x9294 -#define GL_SCREEN_KHR 0x9295 -#define GL_OVERLAY_KHR 0x9296 -#define GL_DARKEN_KHR 0x9297 -#define GL_LIGHTEN_KHR 0x9298 -#define GL_COLORDODGE_KHR 0x9299 -#define GL_COLORBURN_KHR 0x929A -#define GL_HARDLIGHT_KHR 0x929B -#define GL_SOFTLIGHT_KHR 0x929C -#define GL_DIFFERENCE_KHR 0x929E -#define GL_EXCLUSION_KHR 0x92A0 -#define GL_HSL_HUE_KHR 0x92AD -#define GL_HSL_SATURATION_KHR 0x92AE -#define GL_HSL_COLOR_KHR 0x92AF -#define GL_HSL_LUMINOSITY_KHR 0x92B0 - -#define SWGL_BLEND_DROP_SHADOW 0xB001 -#define SWGL_BLEND_SUBPIXEL_TEXT 0xB002 diff --git a/third_party/webrender/swgl/src/glsl.h b/third_party/webrender/swgl/src/glsl.h index bec63858b0d..cdedb43d567 100644 --- a/third_party/webrender/swgl/src/glsl.h +++ b/third_party/webrender/swgl/src/glsl.h @@ -2,45 +2,14 @@ * 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 { -enum TextureFormat { RGBA32F, RGBA32I, RGBA8, R8, RG8, R16, YUV422 }; - -enum TextureFilter { NEAREST, LINEAR }; - -struct samplerCommon { - uint32_t* buf = nullptr; - uint32_t stride = 0; // in units of BPP if < 4, or dwords if BPP >= 4 - uint32_t height = 0; - uint32_t width = 0; - TextureFormat format = TextureFormat::RGBA8; -}; - -struct samplerFilter { - TextureFilter filter = TextureFilter::NEAREST; -}; - -struct sampler2D_impl : samplerCommon, samplerFilter {}; -typedef sampler2D_impl* sampler2D; - -typedef struct sampler2DR8_impl : sampler2D_impl{} * sampler2DR8; -typedef struct sampler2DRG8_impl : sampler2D_impl{} * sampler2DRG8; -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 sampler2DRect_impl : samplerCommon, samplerFilter {}; -typedef sampler2DRect_impl* sampler2DRect; - #if USE_SSE2 SI bool test_all(Bool cond) { return _mm_movemask_ps(cond) == 0xF; } SI bool test_any(Bool cond) { return _mm_movemask_ps(cond) != 0; } @@ -49,14 +18,9 @@ SI bool test_none(Bool cond) { return _mm_movemask_ps(cond) == 0; } SI bool test_all(Bool cond) { return bit_cast<uint32_t>(CONVERT(cond, U8)) == 0xFFFFFFFFU; } -SI bool test_any(Bool cond) { - return bit_cast<uint32_t>(CONVERT(cond, U8)) != 0; -} -SI bool test_none(Bool cond) { - return bit_cast<uint32_t>(CONVERT(cond, U8)) == 0; -} +SI bool test_any(Bool cond) { return bit_cast<uint32_t>(CONVERT(cond, U8)) != 0; } +SI bool test_none(Bool cond) { return bit_cast<uint32_t>(CONVERT(cond, U8)) == 0; } #endif -SI bool test_equal(Bool cond) { return test_none(cond != cond.x); } float make_float(float n) { return n; } @@ -110,23 +74,17 @@ struct vec4; struct ivec2; SI int32_t if_then_else(int32_t c, int32_t t, int32_t e) { return c ? t : e; } -SI int32_t if_then_else(bool 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 bit_cast<Float>((c & bit_cast<I32>(Float(t))) | - (~c & bit_cast<I32>(Float(e)))); + return bit_cast<Float>((c & bit_cast<I32>(Float(t))) | (~c & bit_cast<I32>(Float(e)))); } SI I32 if_then_else(I32 c, int32_t t, int32_t e) { return (c & I32(t)) | (~c & I32(e)); } -SI U32 if_then_else(I32 c, U32 t, U32 e) { - return bit_cast<U32>((c & bit_cast<I32>(t)) | (~c & bit_cast<I32>(e))); -} - SI Float if_then_else(I32 c, Float t, Float e) { return bit_cast<Float>((c & bit_cast<I32>(t)) | (~c & bit_cast<I32>(e))); } @@ -137,10 +95,7 @@ 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 I16 if_then_else(I16 c, I16 t, I16 e) { return (c & t) | (~c & e); } - -template <typename T> -SI void swap(T& a, T& b) { +template <typename T> SI void swap(T& a, T& b) { T t(a); a = b; b = t; @@ -201,37 +156,7 @@ SI Float sqrt(Float v) { #endif } -SI float recip(float x) { -#if USE_SSE2 - return _mm_cvtss_f32(_mm_rcp_ss(_mm_set_ss(x))); -#else - return 1.0f / x; -#endif -} - -// Use a fast vector reciprocal approximation when available. This should only -// be used in cases where it is okay that the approximation is imprecise - -// essentially visually correct but numerically wrong. Otherwise just rely on -// however the compiler would implement slower division if the platform doesn't -// provide a convenient intrinsic. -SI Float recip(Float v) { -#if USE_SSE2 - return _mm_rcp_ps(v); -#elif USE_NEON - Float e = vrecpeq_f32(v); - return vrecpsq_f32(v, e) * e; -#else - return 1.0f / v; -#endif -} - -SI float inversesqrt(float x) { -#if USE_SSE2 - return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); -#else - return 1.0f / sqrtf(x); -#endif -} +SI float inversesqrt(float x) { return 1.0f / sqrtf(x); } SI Float inversesqrt(Float v) { #if USE_SSE2 @@ -269,45 +194,18 @@ enum XYZW { A = 3, }; -struct bvec4_scalar; - struct bvec2_scalar { bool x; bool y; bvec2_scalar() : bvec2_scalar(false) {} - IMPLICIT constexpr bvec2_scalar(bool a) : x(a), y(a) {} + constexpr bvec2_scalar(bool a) : x(a), y(a) {} constexpr bvec2_scalar(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_scalar sel(XYZW c1, XYZW c2) { - return bvec2_scalar(select(c1), select(c2)); - } - bvec4_scalar sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4); -}; - -struct bvec2_scalar1 { - bool x; - - IMPLICIT constexpr bvec2_scalar1(bool a) : x(a) {} - - operator bvec2_scalar() const { return bvec2_scalar(x); } }; struct bvec2 { bvec2() : bvec2(0) {} - IMPLICIT bvec2(Bool a) : x(a), y(a) {} + bvec2(Bool a) : x(a), y(a) {} bvec2(Bool x, Bool y) : x(x), y(y) {} Bool& select(XYZW c) { switch (c) { @@ -321,15 +219,13 @@ struct bvec2 { } Bool sel(XYZW c1) { return select(c1); } - bvec2 sel(XYZW c1, XYZW c2) { return bvec2(select(c1), select(c2)); } - bvec2 operator~() { return bvec2(~x, ~y); } Bool x; Bool y; }; -bvec2_scalar1 make_bvec2(bool n) { return bvec2_scalar1(n); } +bvec2_scalar make_bvec2(bool n) { return bvec2_scalar{n, n}; } bvec2_scalar make_bvec2(bool x, bool y) { return bvec2_scalar{x, y}; } @@ -353,8 +249,8 @@ struct vec2_scalar { float y; constexpr vec2_scalar() : vec2_scalar(0.0f) {} - IMPLICIT constexpr vec2_scalar(float a) : x(a), y(a) {} - IMPLICIT constexpr vec2_scalar(int a) : x(a), y(a) {} + 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) { @@ -390,9 +286,6 @@ struct vec2_scalar { 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); - } friend vec2_scalar operator/(vec2_scalar a, vec2_scalar b) { return vec2_scalar(a.x / b.x, a.y / b.y); } @@ -415,12 +308,6 @@ struct vec2_scalar { return *this; } - 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; @@ -469,12 +356,12 @@ struct vec2 { typedef float element_type; constexpr vec2() : vec2(Float(0.0f)) {} - IMPLICIT constexpr vec2(Float a) : x(a), y(a) {} + constexpr vec2(Float a) : x(a), y(a) {} vec2(Float x, Float y) : x(x), y(y) {} - IMPLICIT constexpr vec2(vec2_scalar s) : x(s.x), y(s.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}) {} - explicit vec2(ivec2 a); + vec2(ivec2 a); Float x; Float y; @@ -583,7 +470,6 @@ 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 min(vec2 a, Float b) { return vec2(min(a.x, b), min(a.y, b)); } SI vec2_scalar min(vec2_scalar a, vec2_scalar b) { return vec2_scalar{min(a.x, b.x), min(a.y, b.y)}; @@ -599,12 +485,8 @@ vec2 step(vec2 edge, vec2 x) { return vec2(step(edge.x, x.x), step(edge.y, x.y)); } -vec2_scalar step(vec2_scalar edge, vec2_scalar x) { - return vec2_scalar(step(edge.x, x.x), step(edge.y, x.y)); -} - -SI vec2 max(vec2 a, vec2 b) { return vec2(max(a.x, b.x), max(a.y, b.y)); } -SI vec2 max(vec2 a, Float b) { return vec2(max(a.x, b), max(a.y, b)); } +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)}; @@ -617,31 +499,9 @@ 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); } -template <typename A, typename B> -SI auto distance(A a, B b) { - return length(a - b); -} +SI Float distance(vec2 a, vec2 b) { return length(a - b); } -template <typename T> -SI T normalize(T a) { - return a / length(a); -} - -SI vec2 sqrt(vec2 a) { return vec2(sqrt(a.x), sqrt(a.y)); } - -SI vec2_scalar sqrt(vec2_scalar a) { return vec2_scalar(sqrt(a.x), sqrt(a.y)); } - -SI vec2 recip(vec2 a) { return vec2(recip(a.x), recip(a.y)); } - -SI vec2_scalar recip(vec2_scalar a) { - return vec2_scalar(recip(a.x), recip(a.y)); -} - -SI vec2 inversesqrt(vec2 a) { return vec2(inversesqrt(a.x), inversesqrt(a.y)); } - -SI vec2_scalar inversesqrt(vec2_scalar a) { - return vec2_scalar(inversesqrt(a.x), inversesqrt(a.y)); -} +SI vec2 normalize(vec2 a) { return a / length(a); } #define abs __glsl_abs @@ -657,13 +517,6 @@ Float abs(Float v) { #endif } -float sign(float a) { return copysignf(1.0f, a); } - -Float sign(Float v) { - return bit_cast<Float>((bit_cast<I32>(v) & 0x80000000) | - bit_cast<I32>(Float(1.0f))); -} - 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); } @@ -725,22 +578,17 @@ SI I32 roundfast(Float v, Float scale) { #endif } -template <typename T> -SI auto round_pixel(T v, float scale = 255.0f) { - return roundfast(v, scale); -} +template <typename T> SI auto round_pixel(T v) { return roundfast(v, 255.0f); } #define round __glsl_round float round(float a) { return roundf(a); } -Float round(Float v) { return floor(v + 0.5f); } - float fract(float a) { return a - floor(a); } -Float fract(Float v) { return v - floor(v); } +Float round(Float v) { return floor(v + 0.5f); } -vec2 fract(vec2 v) { return vec2(fract(v.x), fract(v.y)); } +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 @@ -748,15 +596,11 @@ vec2 fract(vec2 v) { return vec2(fract(v.x), fract(v.y)); } // 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_scalar fwidth(vec2 p) { +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_scalar(d.x + d.z); + return vec2(d.xyxy + d.zwzw); } -float dFdx(Float x) { return x.y - x.x; } - -vec2_scalar dFdx(vec2 p) { return vec2_scalar(dFdx(p.x), dFdx(p.y)); } - // See // http://www.machinedlearnings.com/2011/06/fast-approximate-logarithm-exponential.html. Float approx_log2(Float x) { @@ -768,7 +612,6 @@ Float approx_log2(Float x) { return e - 124.225514990f - 1.498030302f * m - 1.725879990f / (0.3520887068f + m); } - Float approx_pow2(Float x) { Float f = fract(x); return bit_cast<Float>( @@ -776,41 +619,16 @@ Float approx_pow2(Float x) { 27.728023300f / (4.84252568f - f))); } -#define pow __glsl_pow - -SI float pow(float x, float y) { return powf(x, y); } - +// From skia Float pow(Float x, Float y) { return if_then_else((x == 0) | (x == 1), x, approx_pow2(approx_log2(x) * y)); } -#define exp __glsl_exp - -SI float exp(float x) { return expf(x); } - Float exp(Float y) { - float l2e = 1.4426950408889634074f; - return approx_pow2(l2e * y); + float x = 2.718281828459045235360287471352; + return approx_pow2(log2f(x) * y); } -#define exp2 __glsl_exp2 - -SI float exp2(float x) { return exp2f(x); } - -Float exp2(Float x) { return approx_pow2(x); } - -#define log __glsl_log - -SI float log(float x) { return logf(x); } - -Float log(Float x) { return approx_log2(x) * 0.69314718f; } - -#define log2 __glsl_log2 - -SI float log2(float x) { return log2f(x); } - -Float log2(Float x) { return approx_log2(x); } - struct ivec4; struct ivec2_scalar { @@ -820,7 +638,7 @@ struct ivec2_scalar { int32_t y; ivec2_scalar() : ivec2_scalar(0) {} - IMPLICIT constexpr ivec2_scalar(int32_t a) : x(a), y(a) {} + 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) { @@ -838,8 +656,6 @@ struct ivec2_scalar { return ivec2_scalar{select(c1), select(c2)}; } - ivec2_scalar operator-() const { return ivec2_scalar{-x, -y}; } - ivec2_scalar& operator+=(ivec2_scalar a) { x += a.x; y += a.y; @@ -864,25 +680,17 @@ struct ivec2_scalar { friend ivec2_scalar operator+(ivec2_scalar a, ivec2_scalar b) { return ivec2_scalar{a.x + b.x, a.y + b.y}; } - - friend ivec2_scalar operator-(ivec2_scalar a, ivec2_scalar b) { - return ivec2_scalar{a.x - b.x, a.y - b.y}; - } - - friend bool operator==(const ivec2_scalar& l, const ivec2_scalar& r) { - return l.x == r.x && l.y == r.y; - } }; struct ivec2 { typedef int32_t element_type; ivec2() : ivec2(I32(0)) {} - IMPLICIT ivec2(I32 a) : x(a), y(a) {} + ivec2(I32 a) : x(a), y(a) {} ivec2(I32 x, I32 y) : x(x), y(y) {} - IMPLICIT ivec2(vec2 a) : x(cast(a.x)), y(cast(a.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)) {} - IMPLICIT constexpr ivec2(ivec2_scalar s) : x(s.x), y(s.y) {} + 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}) {} @@ -973,7 +781,7 @@ struct ivec3_scalar { int32_t z; ivec3_scalar() : ivec3_scalar(0) {} - IMPLICIT constexpr ivec3_scalar(int32_t a) : x(a), y(a), z(a) {} + 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) { @@ -996,7 +804,7 @@ struct ivec3_scalar { struct ivec3 { ivec3() : ivec3(0) {} - IMPLICIT ivec3(I32 a) : x(a), y(a), z(a) {} + 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)) {} @@ -1047,7 +855,7 @@ struct ivec4_scalar { int32_t w; ivec4_scalar() : ivec4_scalar(0) {} - IMPLICIT constexpr ivec4_scalar(int32_t a) : x(a), y(a), z(a), w(a) {} + 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) {} @@ -1073,31 +881,16 @@ struct ivec4_scalar { 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}; } - - int32_t& operator[](int index) { - switch (index) { - case 0: - return x; - case 1: - return y; - case 2: - return z; - case 3: - return w; - default: - UNREACHABLE; - } - } }; struct ivec4 { typedef int32_t element_type; ivec4() : ivec4(I32(0)) {} - IMPLICIT ivec4(I32 a) : x(a), y(a), z(a), w(a) {} + 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) {} - IMPLICIT constexpr ivec4(ivec4_scalar s) : x(s.x), y(s.y), z(s.z), w(s.w) {} + 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}), @@ -1190,21 +983,13 @@ struct bvec3_scalar { bool z; bvec3_scalar() : bvec3_scalar(false) {} - IMPLICIT constexpr bvec3_scalar(bool a) : x(a), y(a), z(a) {} + 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_scalar1 { - bool x; - - IMPLICIT constexpr bvec3_scalar1(bool a) : x(a) {} - - operator bvec3_scalar() const { return bvec3_scalar(x); } -}; - struct bvec3 { bvec3() : bvec3(0) {} - IMPLICIT bvec3(Bool a) : x(a), y(a), z(a) {} + 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) { @@ -1225,8 +1010,6 @@ struct bvec3 { Bool z; }; -bvec3_scalar1 make_bvec3(bool n) { return bvec3_scalar1(n); } - struct bvec4_scalar { bool x; bool y; @@ -1234,45 +1017,14 @@ struct bvec4_scalar { bool w; bvec4_scalar() : bvec4_scalar(false) {} - IMPLICIT constexpr bvec4_scalar(bool a) : x(a), y(a), z(a), w(a) {} + 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) {} - - bool& select(XYZW c) { - switch (c) { - case X: - return x; - case Y: - return y; - case Z: - return z; - case W: - return w; - default: - UNREACHABLE; - } - } - bool sel(XYZW c1) { return select(c1); } - bvec2_scalar sel(XYZW c1, XYZW c2) { - return bvec2_scalar(select(c1), select(c2)); - } -}; - -bvec4_scalar bvec2_scalar::sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { - return bvec4_scalar{select(c1), select(c2), select(c3), select(c4)}; -} - -struct bvec4_scalar1 { - bool x; - - IMPLICIT constexpr bvec4_scalar1(bool a) : x(a) {} - - operator bvec4_scalar() const { return bvec4_scalar(x); } }; struct bvec4 { bvec4() : bvec4(0) {} - IMPLICIT bvec4(Bool a) : x(a), y(a), z(a), w(a) {} + 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) { @@ -1285,8 +1037,6 @@ struct bvec4 { return z; case W: return w; - default: - UNREACHABLE; } } Bool sel(XYZW c1) { return select(c1); } @@ -1297,16 +1047,12 @@ struct bvec4 { Bool w; }; -bvec4_scalar1 make_bvec4(bool n) { return bvec4_scalar1(n); } +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}; } -bvec4_scalar make_bvec4(bvec2_scalar a, bvec2_scalar b) { - return bvec4_scalar{a.x, a.y, b.x, b.y}; -} - template <typename N> bvec4 make_bvec4(const N& n) { return bvec4(n); @@ -1383,7 +1129,7 @@ struct vec3_scalar { float z; constexpr vec3_scalar() : vec3_scalar(0.0f) {} - IMPLICIT constexpr vec3_scalar(float a) : x(a), y(a), z(a) {} + 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) { @@ -1474,11 +1220,10 @@ struct vec3 { typedef float element_type; constexpr vec3() : vec3(Float(0.0f)) {} - IMPLICIT constexpr vec3(Float a) : x(a), y(a), z(a) {} + 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) {} - explicit vec3(vec4); - IMPLICIT constexpr vec3(vec3_scalar s) : x(s.x), y(s.y), z(s.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}), @@ -1507,8 +1252,6 @@ struct vec3 { return vec3(select(c1), select(c2), select(c3)); } - vec4 sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4); - vec2_ref lsel(XYZW c1, XYZW c2) { return vec2_ref(select(c1), select(c2)); } friend vec3 operator*(vec3 a, Float b) { @@ -1605,26 +1348,13 @@ vec3 step(vec3 edge, vec3 x) { return vec3(step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)); } -vec3_scalar step(vec3_scalar edge, vec3_scalar x) { - return vec3_scalar(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 min(vec3 a, Float b) { - return vec3(min(a.x, b), min(a.y, b), min(a.z, b)); -} -SI vec3_scalar min(vec3_scalar a, vec3_scalar b) { - return vec3_scalar{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 max(vec3 a, Float b) { - return vec3(max(a.x, b), max(a.y, b), max(a.z, b)); -} + 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)}; } @@ -1670,15 +1400,11 @@ struct vec4_scalar { float w; constexpr vec4_scalar() : vec4_scalar(0.0f) {} - IMPLICIT constexpr vec4_scalar(float a) : x(a), y(a), z(a), w(a) {} + 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) {} vec4_scalar(vec3_scalar xyz, float w) : x(xyz.x), y(xyz.y), z(xyz.z), w(w) {} - static vec4_scalar load_from_ptr(const float* f) { - return vec4_scalar(f[0], f[1], f[2], f[3]); - } - ALWAYS_INLINE float& select(XYZW c) { switch (c) { case X: @@ -1700,9 +1426,6 @@ struct vec4_scalar { vec3_scalar sel(XYZW c1, XYZW c2, XYZW c3) { return vec3_scalar{select(c1), select(c2), select(c3)}; } - vec4_scalar sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { - return vec4_scalar{select(c1), select(c2), select(c3), select(c4)}; - } vec2_scalar_ref lsel(XYZW c1, XYZW c2) { return vec2_scalar_ref(select(c1), select(c2)); } @@ -1750,56 +1473,30 @@ struct vec4_scalar { w /= a.w; return *this; } - - vec4_scalar& operator*=(vec4_scalar a) { - x *= a.x; - y *= a.y; - z *= a.z; - w *= a.w; - return *this; - } - - friend bool operator==(const vec4_scalar& l, const vec4_scalar& r) { - return l.x == r.x && l.y == r.y && l.z == r.z && l.w == r.w; - } - - friend bool operator!=(const vec4_scalar& l, const vec4_scalar& r) { - return l.x != r.x || l.y != r.y || l.z != r.z || l.w != r.w; - } }; 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_ref { - vec4_ref(Float& x, Float& y, Float& z, Float& w) : x(x), y(y), z(z), w(w) {} - Float& x; - Float& y; - Float& z; - Float& w; - - vec4_ref& operator=(const vec4& a); -}; - struct vec4 { typedef struct vec4 vector_type; typedef float element_type; constexpr vec4() : vec4(Float(0.0f)) {} - IMPLICIT constexpr vec4(Float a) : x(a), y(a), z(a), w(a) {} + 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) {} - IMPLICIT constexpr vec4(vec4_scalar s) : x(s.x), y(s.y), z(s.z), w(s.w) {} + 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}) {} - ALWAYS_INLINE Float& select(XYZW c) { + Float& select(XYZW c) { switch (c) { case X: return x; @@ -1813,29 +1510,18 @@ struct vec4 { UNREACHABLE; } } - ALWAYS_INLINE Float& sel(XYZW c1) { return select(c1); } + Float& sel(XYZW c1) { return select(c1); } - ALWAYS_INLINE vec2 sel(XYZW c1, XYZW c2) { - return vec2(select(c1), select(c2)); - } + vec2 sel(XYZW c1, XYZW c2) { return vec2(select(c1), select(c2)); } - ALWAYS_INLINE vec3 sel(XYZW c1, XYZW c2, XYZW c3) { + vec3 sel(XYZW c1, XYZW c2, XYZW c3) { return vec3(select(c1), select(c2), select(c3)); } - ALWAYS_INLINE vec3_ref lsel(XYZW c1, XYZW c2, XYZW c3) { + vec3_ref lsel(XYZW c1, XYZW c2, XYZW c3) { return vec3_ref(select(c1), select(c2), select(c3)); } - ALWAYS_INLINE vec2_ref lsel(XYZW c1, XYZW c2) { - return vec2_ref(select(c1), select(c2)); - } - - ALWAYS_INLINE vec4 sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { - return vec4(select(c1), select(c2), select(c3), select(c4)); - } - ALWAYS_INLINE vec4_ref lsel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { - return vec4_ref(select(c1), select(c2), select(c3), select(c4)); - } + vec2_ref lsel(XYZW c1, XYZW c2) { return vec2_ref(select(c1), select(c2)); } Float& operator[](int index) { switch (index) { @@ -1957,13 +1643,6 @@ struct vec4 { 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; @@ -1978,18 +1657,6 @@ struct vec4 { Float w; }; -inline vec4_ref& vec4_ref::operator=(const vec4& a) { - x = a.x; - y = a.y; - z = a.z; - w = a.w; - return *this; -} - -inline vec4 vec3::sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { - return vec4(select(c1), select(c2), select(c3), select(c4)); -} - 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)}; @@ -2017,10 +1684,6 @@ vec4_scalar make_vec4(float x, float y, const vec2_scalar& v) { return vec4_scalar{x, y, v.x, v.y}; } -ivec4_scalar make_ivec4(const vec4_scalar& v) { - return ivec4_scalar{int32_t(v.x), int32_t(v.y), int32_t(v.z), int32_t(v.w)}; -} - template <typename N> vec4 make_vec4(const N& n) { return vec4(n); @@ -2041,8 +1704,6 @@ vec4 make_vec4(const X& x, const Y& y, const Z& z, const W& w) { return vec4(x, y, z, w); } -ALWAYS_INLINE vec3::vec3(vec4 v) : x(v.x), y(v.y), z(v.z) {} - SI ivec4 roundfast(vec4 v, Float scale) { return ivec4(roundfast(v.x, scale), roundfast(v.y, scale), roundfast(v.z, scale), roundfast(v.w, scale)); @@ -2059,14 +1720,6 @@ SI vec4 if_then_else(I32 c, vec4 t, vec4 e) { SI vec4 if_then_else(int32_t c, vec4 t, vec4 e) { return c ? t : e; } -SI vec4_scalar if_then_else(int32_t c, vec4_scalar t, vec4_scalar e) { - return c ? t : e; -} - -SI vec2 clamp(vec2 a, Float minVal, Float maxVal) { - return vec2(clamp(a.x, minVal, maxVal), clamp(a.y, minVal, maxVal)); -} - 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)); } @@ -2076,56 +1729,20 @@ SI vec2_scalar clamp(vec2_scalar a, vec2_scalar minVal, vec2_scalar maxVal) { clamp(a.y, minVal.y, maxVal.y)}; } -SI vec2_scalar clamp(vec2_scalar a, float minVal, float maxVal) { - return vec2_scalar{clamp(a.x, minVal, maxVal), clamp(a.y, minVal, maxVal)}; -} - 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, Float minVal, Float maxVal) { - return vec3(clamp(a.x, minVal, maxVal), clamp(a.y, minVal, maxVal), - clamp(a.z, minVal, maxVal)); -} - 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, Float minVal, Float maxVal) { - return vec4(clamp(a.x, minVal, maxVal), clamp(a.y, minVal, maxVal), - clamp(a.z, minVal, maxVal), clamp(a.w, minVal, maxVal)); -} - 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)); } - -SI vec4_scalar clamp(vec4_scalar a, vec4_scalar minVal, vec4_scalar maxVal) { - return vec4_scalar{ - 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)}; -} - -SI vec4_scalar clamp(vec4_scalar a, float minVal, float maxVal) { - return vec4_scalar{clamp(a.x, minVal, maxVal), clamp(a.y, minVal, maxVal), - clamp(a.z, minVal, maxVal), clamp(a.w, minVal, maxVal)}; -} - -vec4 step(vec4 edge, vec4 x) { - return vec4(step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), - step(edge.w, x.w)); -} - -vec4_scalar step(vec4_scalar edge, vec4_scalar x) { - return vec4_scalar(step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), - step(edge.w, x.w)); -} - template <typename T> auto lessThanEqual(T x, T y) -> decltype(x <= y) { return x <= y; @@ -2163,20 +1780,6 @@ SI bvec2 lessThan(vec2 x, vec2 y) { return bvec2(lessThan(x.x, y.x), lessThan(x.y, y.y)); } -SI bvec2_scalar lessThan(vec2_scalar x, vec2_scalar y) { - return bvec2_scalar(lessThan(x.x, y.x), lessThan(x.y, y.y)); -} - -SI bvec4 lessThan(vec4 x, vec4 y) { - return bvec4(lessThan(x.x, y.x), lessThan(x.y, y.y), lessThan(x.z, y.z), - lessThan(x.w, y.w)); -} - -SI bvec4_scalar lessThan(vec4_scalar x, vec4_scalar y) { - return bvec4_scalar{lessThan(x.x, y.x), lessThan(x.y, y.y), - lessThan(x.z, y.z), lessThan(x.w, y.w)}; -} - template <typename T> auto greaterThan(T x, T y) -> decltype(x > y) { return x > y; @@ -2186,20 +1789,6 @@ bvec2 greaterThan(vec2 x, vec2 y) { return bvec2(greaterThan(x.x, y.x), greaterThan(x.y, y.y)); } -bvec2_scalar greaterThan(vec2_scalar x, vec2_scalar y) { - return bvec2_scalar(greaterThan(x.x, y.x), greaterThan(x.y, y.y)); -} - -SI bvec4 greaterThan(vec4 x, vec4 y) { - return bvec4(greaterThan(x.x, y.x), greaterThan(x.y, y.y), - greaterThan(x.z, y.z), greaterThan(x.w, y.w)); -} - -SI bvec4_scalar greaterThan(vec4_scalar x, vec4_scalar y) { - return bvec4_scalar{greaterThan(x.x, y.x), greaterThan(x.y, y.y), - greaterThan(x.z, y.z), greaterThan(x.w, y.w)}; -} - template <typename T> auto greaterThanEqual(T x, T y) -> decltype(x >= y) { return x >= y; @@ -2210,29 +1799,51 @@ bvec4 greaterThanEqual(vec4 x, vec4 y) { greaterThanEqual(x.z, y.z), greaterThanEqual(x.w, y.w)); } -template <typename T> -auto equal(T x, T y) -> decltype(x > y) { - return x == y; -} +enum TextureFormat { RGBA32F, RGBA32I, RGBA8, R8 }; -bvec2 equal(vec2 x, vec2 y) { return bvec2(equal(x.x, y.x), equal(x.y, y.y)); } +enum TextureFilter { NEAREST, LINEAR }; -bvec2_scalar equal(vec2_scalar x, vec2_scalar y) { - return bvec2_scalar(equal(x.x, y.x), equal(x.y, y.y)); -} +struct samplerCommon { + uint32_t* buf = nullptr; + uint32_t stride = 0; // in dwords + uint32_t height = 0; + uint32_t width = 0; + TextureFormat format = TextureFormat::RGBA8; +}; -template <typename T> -auto notEqual(T x, T y) -> decltype(x > y) { - return x != y; -} +struct samplerDepth { + int depth = 0; + uint32_t height_stride = 0; // in dwords +}; -bvec2 notEqual(vec2 x, vec2 y) { - return bvec2(notEqual(x.x, y.x), notEqual(x.y, y.y)); -} +struct samplerFilter { + TextureFilter filter = TextureFilter::NEAREST; +}; -bvec2_scalar notEqual(vec2_scalar x, vec2_scalar y) { - return bvec2_scalar(notEqual(x.x, y.x), notEqual(x.y, y.y)); -} +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 sampler2DRect_impl : samplerCommon, samplerFilter {}; +typedef sampler2DRect_impl* sampler2DRect; struct mat4_scalar; @@ -2240,7 +1851,7 @@ struct mat2_scalar { vec2_scalar data[2]; mat2_scalar() = default; - IMPLICIT constexpr mat2_scalar(float a) { + constexpr mat2_scalar(float a) { data[0] = vec2_scalar(a); data[1] = vec2_scalar(a); } @@ -2248,7 +1859,7 @@ struct mat2_scalar { data[0] = a; data[1] = b; } - IMPLICIT mat2_scalar(const mat4_scalar& mat); + mat2_scalar(const mat4_scalar& mat); vec2_scalar& operator[](int index) { return data[index]; } const vec2_scalar& operator[](int index) const { return data[index]; } @@ -2286,7 +1897,7 @@ struct mat2 { const vec2& operator[](int index) const { return data[index]; } mat2() = default; - IMPLICIT mat2(Float a) { + mat2(Float a) { data[0] = vec2(a); data[1] = vec2(a); } @@ -2295,8 +1906,8 @@ struct mat2 { data[0] = a; data[1] = b; } - IMPLICIT mat2(const mat4& mat); - IMPLICIT constexpr mat2(mat2_scalar s) { + mat2(const mat4& mat); + constexpr mat2(mat2_scalar s) { data[0] = vec2(s.data[0]); data[1] = vec2(s.data[1]); } @@ -2350,7 +1961,7 @@ struct mat3_scalar { data[1] = b; data[2] = c; } - IMPLICIT mat3_scalar(const mat4_scalar& mat); + mat3_scalar(const mat4_scalar& mat); vec3_scalar& operator[](int index) { return data[index]; } const vec3_scalar& operator[](int index) const { return data[index]; } @@ -2384,7 +1995,7 @@ struct mat3 { data[2] = c; } - IMPLICIT constexpr mat3(mat3_scalar s) { + constexpr mat3(mat3_scalar s) { data[0] = vec3(s.data[0]); data[1] = vec3(s.data[1]); data[2] = vec3(s.data[2]); @@ -2403,7 +2014,7 @@ struct mat3 { data[2] = vec3(d7, d8, d9); } - IMPLICIT mat3(const mat4& mat); + mat3(const mat4& mat); friend vec3 operator*(mat3 m, vec3 v) { vec3 u; @@ -2490,7 +2101,7 @@ struct mat4 { vec4 data[4]; mat4() = default; - IMPLICIT constexpr mat4(mat4_scalar s) { + constexpr mat4(mat4_scalar s) { data[0] = vec4(s.data[0]); data[1] = vec4(s.data[1]); data[2] = vec4(s.data[2]); @@ -2522,15 +2133,15 @@ mat3::mat3(const mat4& mat) vec3(mat[1].x, mat[1].y, mat[1].z), vec3(mat[2].x, mat[2].y, mat[2].z)) {} -IMPLICIT mat3_scalar::mat3_scalar(const mat4_scalar& mat) +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)) {} -IMPLICIT mat2::mat2(const mat4& mat) +mat2::mat2(const mat4& mat) : mat2(vec2(mat[0].x, mat[0].y), vec2(mat[1].x, mat[1].y)) {} -IMPLICIT mat2_scalar::mat2_scalar(const mat4_scalar& mat) +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)) {} @@ -2584,6 +2195,256 @@ SI mat4 if_then_else(I32 c, mat4 t, mat4 e) { 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 <typename T, typename S> +SI T clamp2D(T P, S sampler) { + return T{clampCoord(P.x, sampler->width), clampCoord(P.y, sampler->height)}; +} +template <typename T> +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 <typename S> +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) { + I32 offset = P.x + P.y * sampler->stride; + return fetchOffsetsRGBA8(sampler, offset); +} + +vec4 texelFetchRGBA8(sampler2DArray sampler, ivec3 P) { + assert(test_all(P.z == P.z.x)); + I32 offset = P.x + P.y * sampler->stride + P.z.x * sampler->height_stride; + return fetchOffsetsRGBA8(sampler, offset); +} + +template <typename S> +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) { + 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) { + assert(test_all(P.z == P.z.x)); + I32 offset = P.x + P.y * sampler->stride + P.z.x * sampler->height_stride; + return vec4(fetchOffsetsR8(sampler, offset), 0.0f, 0.0f, 1.0f); +} + +template <typename S> +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) { + I32 offset = P.x * 4 + P.y * sampler->stride; + return fetchOffsetsFloat(sampler, offset); +} + +SI vec4 texelFetchFloat(sampler2DArray sampler, ivec3 P) { + assert(test_all(P.z == P.z.x)); + I32 offset = P.x * 4 + P.y * sampler->stride + P.z.x * sampler->height_stride; + return fetchOffsetsFloat(sampler, offset); +} + +vec4 texelFetch(sampler2D sampler, ivec2 P, int lod) { + assert(lod == 0); + P = clamp2D(P, sampler); + if (sampler->format == TextureFormat::RGBA32F) { + return texelFetchFloat(sampler, P); + } else if (sampler->format == TextureFormat::RGBA8) { + return texelFetchRGBA8(sampler, P); + } else { + assert(sampler->format == TextureFormat::R8); + return texelFetchR8(sampler, P); + } +} + +vec4 texelFetch(sampler2DRGBA32F sampler, ivec2 P, int lod) { + assert(lod == 0); + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA32F); + return texelFetchFloat(sampler, P); +} + +vec4 texelFetch(sampler2DRGBA8 sampler, ivec2 P, int lod) { + assert(lod == 0); + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA8); + return texelFetchRGBA8(sampler, P); +} + +vec4 texelFetch(sampler2DR8 sampler, ivec2 P, int lod) { + assert(lod == 0); + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::R8); + return texelFetchR8(sampler, P); +} + +vec4_scalar texelFetch(sampler2D sampler, ivec2_scalar P, int lod) { + assert(lod == 0); + 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) { + assert(lod == 0); + 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) { + assert(lod == 0); + 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) { + assert(lod == 0); + 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(sampler2DRect sampler, ivec2 P) { + P = clamp2D(P, sampler); + assert(sampler->format == TextureFormat::RGBA8); + I32 offset = P.x + P.y * sampler->stride; + return fetchOffsetsRGBA8(sampler, offset); +} + +SI vec4 texelFetch(sampler2DArray sampler, ivec3 P, int lod) { + assert(lod == 0); + P = clamp2DArray(P, sampler); + if (sampler->format == TextureFormat::RGBA32F) { + return texelFetchFloat(sampler, P); + } else if (sampler->format == TextureFormat::R8) { + return texelFetchR8(sampler, P); + } else { + assert(sampler->format == TextureFormat::RGBA8); + return texelFetchRGBA8(sampler, P); + } +} + +vec4 texelFetch(sampler2DArrayRGBA32F sampler, ivec3 P, int lod) { + assert(lod == 0); + P = clamp2DArray(P, sampler); + assert(sampler->format == TextureFormat::RGBA32F); + return texelFetchFloat(sampler, P); +} + +vec4 texelFetch(sampler2DArrayRGBA8 sampler, ivec3 P, int lod) { + assert(lod == 0); + P = clamp2DArray(P, sampler); + assert(sampler->format == TextureFormat::RGBA8); + return texelFetchRGBA8(sampler, P); +} + +vec4 texelFetch(sampler2DArrayR8 sampler, ivec3 P, int lod) { + assert(lod == 0); + P = clamp2DArray(P, sampler); + assert(sampler->format == TextureFormat::R8); + return texelFetchR8(sampler, P); +} + +template <typename S> +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) { + assert(lod == 0); + 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) { + assert(lod == 0); + 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 <typename T, typename U, typename A, typename R = typename T::vector_type> SI R mix(T x, U y, A a) { @@ -2598,19 +2459,416 @@ SI T mix(T x, T y, float a) { } template <typename T> -SI T mix(T x, T y, vec2_scalar a) { - return T{mix(x.x, y.x, a.x), mix(x.y, y.y, a.y)}; +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)}; } +// Scale texture coords for quantization, subtract offset for filtering +// (assuming coords already offset to texel centers), and round to nearest +// 1/scale increment template <typename T> -SI T mix(T x, T y, vec3_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)}; +SI T linearQuantize(T P, float scale) { + return P * scale + (0.5f - 0.5f * scale); } -template <typename T> -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)}; +// Helper version that also scales normalized texture coords for sampler +template <typename T, typename S> +SI T linearQuantize(T P, float scale, S sampler) { + P.x *= sampler->width; + P.y *= sampler->height; + return linearQuantize(P, scale); +} + +template <typename S> +vec4 textureLinearRGBA8(S sampler, vec2 P, int32_t zoffset = 0) { + assert(sampler->format == TextureFormat::RGBA8); + +#if USE_SSE2 + ivec2 i(linearQuantize(P, 256, sampler)); + ivec2 frac = i & (I32)0xFF; + i >>= 8; + + // Pack coords so they get clamped into range, and also for later bounding + // of fractional coords. Store Y as low-bits for easier access, X as high. + __m128i yx = _mm_packs_epi32(i.y, i.x); + __m128i hw = _mm_packs_epi32(_mm_set1_epi32(sampler->height - 1), + _mm_set1_epi32(sampler->width - 1)); + // Clamp coords to valid range to prevent sampling outside texture. + __m128i clampyx = _mm_min_epi16(_mm_max_epi16(yx, _mm_setzero_si128()), hw); + // Multiply clamped Y by stride and add X offset. + __m128i row0 = _mm_madd_epi16( + _mm_unpacklo_epi16(clampyx, _mm_setzero_si128()), + _mm_set1_epi16(sampler->stride)); + row0 = _mm_add_epi32(row0, _mm_unpackhi_epi16(clampyx, _mm_setzero_si128())); + // Add in layer offset if available + row0 = _mm_add_epi32(row0, _mm_set1_epi32(zoffset)); + + // Check if fractional coords are all zero, in which case skip filtering. + __m128i fracyx = _mm_packs_epi32(frac.y, frac.x); + if (!_mm_movemask_epi8(_mm_cmpgt_epi16(fracyx, _mm_setzero_si128()))) { + return fetchOffsetsRGBA8(sampler, row0); + } + + // Check if coords were clamped at all above. If so, need to adjust fractions + // to avoid sampling outside the texture on the edges. + __m128i yxinside = _mm_andnot_si128( + _mm_cmplt_epi16(yx, _mm_setzero_si128()), + _mm_cmplt_epi16(yx, hw)); + // Set fraction to zero when outside. + fracyx = _mm_and_si128(fracyx, yxinside); + // Store two side-by-side copies of X fraction, as below each pixel value + // will be interleaved to be next to the pixel value for the next row. + __m128i fracx = _mm_unpackhi_epi16(fracyx, fracyx); + // For Y fraction, we need to store 1-fraction before each fraction, as a + // madd will be used to weight and collapse all results as last step. + __m128i fracy = _mm_unpacklo_epi16( + _mm_sub_epi16(_mm_set1_epi16(256), fracyx), fracyx); + + // Ensure we don't sample row off end of texture from added stride. + __m128i row1 = _mm_and_si128(yxinside, _mm_set1_epi16(sampler->stride)); + + // Load two adjacent pixels on each row and interleave them. + // 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 +# define LOAD_LANE(out, idx) \ + { \ + uint32_t* buf = &sampler->buf[_mm_cvtsi128_si32( \ + _mm_shuffle_epi32(row0, _MM_SHUFFLE(idx, idx, idx, idx)))]; \ + out = _mm_unpacklo_epi8( \ + _mm_loadl_epi64((__m128i*)buf), \ + _mm_loadl_epi64((__m128i*)(buf + _mm_extract_epi16(row1, idx)))); \ + } + __m128i x, y, z, w; + LOAD_LANE(x, 0) + LOAD_LANE(y, 1) + LOAD_LANE(z, 2) + LOAD_LANE(w, 3) +# undef LOAD_LANE + + // Need to transpose the data from AoS to SoA format. Best to do this here + // while the data is still packed into 8-bit components, requiring fewer + // insns. + // r0,R0,g0,G0,b0,B0,a0,A0,r1,R1,g1,G1,b1,B1,a1,A1 \/ + // r2,R2,g2,G2,b2,B2,a2,A2,r3,R3,g3,G3,b3,B3,a3,A3 + // ... r0,R0,r2,R2,g0,G0,g2,G2,b0,B0,b2,B2,a0,A0,a2,A2 + // ... r1,R1,r3,R3,g1,G1,g3,G3,b1,B1,b3,B3,a1,A1,a3,A3 + __m128i xy0 = _mm_unpacklo_epi16(x, y); + __m128i xy1 = _mm_unpackhi_epi16(x, y); + __m128i zw0 = _mm_unpacklo_epi16(z, w); + __m128i zw1 = _mm_unpackhi_epi16(z, w); + // r0,R0,r2,R2,g0,G0,g2,G2,b0,B0,b2,B2,a0,A0,a2,A2 \/ + // r4,R4,r6,R6,g4,G4,g6,G6,b4,B4,b6,B6,a4,A4,a6,A6 + // ... r0,R0,r2,R2,r4,R4,r6,R6,g0,G0,g2,G2,g4,G4,g6,G6 + // ... b0,B0,b2,B2,b4,B4,b6,B6,a0,A0,a2,A2,a4,A4,a6,A6 + __m128i rg0 = _mm_unpacklo_epi32(xy0, zw0); + __m128i ba0 = _mm_unpackhi_epi32(xy0, zw0); + __m128i rg1 = _mm_unpacklo_epi32(xy1, zw1); + __m128i ba1 = _mm_unpackhi_epi32(xy1, zw1); + + // Expand packed SoA pixels for each column. Multiply then add columns with + // 8-bit precision so we don't carry to high byte of word accidentally. Use + // final madd insn to blend interleaved rows and expand result to 32 bits. +# define FILTER_COMPONENT(out, unpack, src0, src1) \ + { \ + __m128i cc0 = unpack(src0, _mm_setzero_si128()); \ + __m128i cc1 = unpack(src1, _mm_setzero_si128()); \ + cc0 = _mm_add_epi8( \ + cc0, _mm_srli_epi16(_mm_mullo_epi16(_mm_sub_epi16(cc1, cc0), fracx), \ + 8)); \ + out = _mm_cvtepi32_ps(_mm_madd_epi16(cc0, fracy)); \ + } + __m128 fr, fg, fb, fa; + FILTER_COMPONENT(fr, _mm_unpacklo_epi8, rg0, rg1); + FILTER_COMPONENT(fg, _mm_unpackhi_epi8, rg0, rg1); + FILTER_COMPONENT(fb, _mm_unpacklo_epi8, ba0, ba1); + FILTER_COMPONENT(fa, _mm_unpackhi_epi8, ba0, ba1); +# undef FILTER_COMPONENT + + return vec4(fb, fg, fr, fa) * (1.0f / 0xFF00); +#else + ivec2 i(linearQuantize(P, 128, sampler)); + 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<V8<uint8_t> >(&sampler->buf[row0.x]), V8<int16_t>); + auto a1 = + CONVERT(unaligned_load<V8<uint8_t> >(&sampler->buf[row1.x]), V8<int16_t>); + a0 += ((a1 - a0) * fracy.x) >> 7; + + auto b0 = + CONVERT(unaligned_load<V8<uint8_t> >(&sampler->buf[row0.y]), V8<int16_t>); + auto b1 = + CONVERT(unaligned_load<V8<uint8_t> >(&sampler->buf[row1.y]), V8<int16_t>); + 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<V8<uint8_t> >(&sampler->buf[row0.z]), V8<int16_t>); + auto c1 = + CONVERT(unaligned_load<V8<uint8_t> >(&sampler->buf[row1.z]), V8<int16_t>); + c0 += ((c1 - c0) * fracy.z) >> 7; + + auto d0 = + CONVERT(unaligned_load<V8<uint8_t> >(&sampler->buf[row0.w]), V8<int16_t>); + auto d1 = + CONVERT(unaligned_load<V8<uint8_t> >(&sampler->buf[row1.w]), V8<int16_t>); + 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<uint16_t>(zip2Low(abl, cdl)), V8<float>); + auto ba = CONVERT(V8<uint16_t>(zip2High(abl, cdl)), V8<float>); + + 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 <typename S> +static U16 textureLinearPackedR8(S sampler, ivec2 i, int32_t zoffset) { + assert(sampler->format == TextureFormat::R8); + 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<V2<uint8_t> >(&buf[row0.x]); + auto b0 = unaligned_load<V2<uint8_t> >(&buf[row0.y]); + auto c0 = unaligned_load<V2<uint8_t> >(&buf[row0.z]); + auto d0 = unaligned_load<V2<uint8_t> >(&buf[row0.w]); + auto abcd0 = CONVERT(combine(combine(a0, b0), combine(c0, d0)), V8<int16_t>); + + auto a1 = unaligned_load<V2<uint8_t> >(&buf[row1.x]); + auto b1 = unaligned_load<V2<uint8_t> >(&buf[row1.y]); + auto c1 = unaligned_load<V2<uint8_t> >(&buf[row1.z]); + auto d1 = unaligned_load<V2<uint8_t> >(&buf[row1.w]); + auto abcd1 = CONVERT(combine(combine(a1, b1), combine(c1, d1)), V8<int16_t>); + + 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; + + return U16(abcdl); +} + +template <typename S> +vec4 textureLinearR8(S sampler, vec2 P, int32_t zoffset = 0) { + assert(sampler->format == TextureFormat::R8); + +#if USE_SSE2 + ivec2 i(linearQuantize(P, 256, sampler)); + ivec2 frac = i & (I32)0xFF; + i >>= 8; + + // Pack coords so they get clamped into range, and also for later bounding + // of fractional coords. Store Y as low-bits for easier access, X as high. + __m128i yx = _mm_packs_epi32(i.y, i.x); + __m128i hw = _mm_packs_epi32(_mm_set1_epi32(sampler->height - 1), + _mm_set1_epi32(sampler->width - 1)); + // Clamp coords to valid range to prevent sampling outside texture. + __m128i clampyx = _mm_min_epi16(_mm_max_epi16(yx, _mm_setzero_si128()), hw); + // Multiply clamped Y by stride and add X offset. + __m128i row0 = _mm_madd_epi16( + _mm_unpacklo_epi16(clampyx, _mm_setzero_si128()), + _mm_set1_epi16(sampler->stride)); + row0 = _mm_add_epi32(row0, _mm_unpackhi_epi16(clampyx, _mm_setzero_si128())); + // Add in layer offset if available + row0 = _mm_add_epi32(row0, _mm_set1_epi32(zoffset)); + + __m128i fracyx = _mm_packs_epi32(frac.y, frac.x); + + // Check if coords were clamped at all above. If so, need to adjust fractions + // to avoid sampling outside the texture on the edges. + __m128i yxinside = _mm_andnot_si128( + _mm_cmplt_epi16(yx, _mm_setzero_si128()), + _mm_cmplt_epi16(yx, hw)); + // Set fraction to zero when outside. + fracyx = _mm_and_si128(fracyx, yxinside); + // For X fraction, we need to store 1-fraction before each fraction, as a + // madd will be used to weight and collapse all results as last step. + __m128i fracx = _mm_unpackhi_epi16( + _mm_sub_epi16(_mm_set1_epi16(256), fracyx), fracyx); + // Store two side-by-side copies of Y fraction, as below each pixel value + // will be interleaved to be next to the pixel value for the next column. + __m128i fracy = _mm_unpacklo_epi16(fracyx, fracyx); + + // Ensure we don't sample row off end of texture from added stride. + __m128i row1 = _mm_and_si128(yxinside, _mm_set1_epi16(sampler->stride)); + + // Calculate pointers for first row in each lane + 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))); + // Load adjacent columns from first row, pack into register, then expand. + __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()); + // Load adjacent columns from next row, pack into register, then expand. + __m128i cc1 = _mm_unpacklo_epi8( + _mm_setr_epi16(*(uint16_t*)(buf0 + _mm_extract_epi16(row1, 0)), + *(uint16_t*)(buf1 + _mm_extract_epi16(row1, 1)), + *(uint16_t*)(buf2 + _mm_extract_epi16(row1, 2)), + *(uint16_t*)(buf3 + _mm_extract_epi16(row1, 3)), + 0, 0, 0, 0), + _mm_setzero_si128()); + // Multiply then add rows with 8-bit precision so we don't carry to high byte + // of word accidentally. Use final madd insn to blend interleaved columns and + // expand result to 32 bits. + __m128i cc = _mm_add_epi8( + cc0, _mm_srli_epi16(_mm_mullo_epi16(_mm_sub_epi16(cc1, cc0), fracy), 8)); + __m128 r = _mm_cvtepi32_ps(_mm_madd_epi16(cc, fracx)); + return vec4((Float)r * (1.0f / 0xFF00), 0.0f, 0.0f, 1.0f); +#else + ivec2 i(linearQuantize(P, 128, sampler)); + Float r = CONVERT(textureLinearPackedR8(sampler, i, zoffset), Float); + return vec4(r * (1.0f / 255.0f), 0.0f, 0.0f, 1.0f); +#endif +} + +template <typename S> +vec4 textureLinearRGBA32F(S sampler, vec2 P, int32_t 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 - 1, r.x, 0.0f); + I32 offset0 = c.x * 4 + c.y * sampler->stride + zoffset; + I32 offset1 = offset0 + ((i.y >= 0 && i.y < int32_t(sampler->height) - 1) & + I32(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); +} + +SI vec4 texture(sampler2D sampler, vec2 P) { + if (sampler->filter == TextureFilter::LINEAR) { + if (sampler->format == TextureFormat::RGBA8) { + return textureLinearRGBA8(sampler, P); + } else if (sampler->format == TextureFormat::R8) { + return textureLinearR8(sampler, P); + } else { + assert(sampler->format == TextureFormat::RGBA32F); + return textureLinearRGBA32F(sampler, P); + } + } else { + ivec2 coord(roundzero(P.x, sampler->width), roundzero(P.y, sampler->height)); + return texelFetch(sampler, coord, 0); + } +} + +vec4 texture(sampler2DRect sampler, vec2 P) { + assert(sampler->format == TextureFormat::RGBA8); + if (sampler->filter == TextureFilter::LINEAR) { + return textureLinearRGBA8(sampler, + P * vec2_scalar{1.0f / sampler->width, 1.0f / sampler->height}); + } else { + ivec2 coord(roundzero(P.x, 1.0f), roundzero(P.y, 1.0f)); + return texelFetch(sampler, coord); + } +} + +SI vec4 texture(sampler2DArray sampler, vec3 P) { + if (sampler->filter == TextureFilter::LINEAR) { + // SSE2 can generate slow code for 32-bit multiply, and we never actually sample + // from different layers in one chunk, so do cheaper scalar multiplication instead. + assert(test_all(P.z == P.z.x)); + int32_t zoffset = + clampCoord(roundeven(P.z.x, 1.0f), sampler->depth) * sampler->height_stride; + if (sampler->format == TextureFormat::RGBA8) { + return textureLinearRGBA8(sampler, vec2(P.x, P.y), zoffset); + } else if (sampler->format == TextureFormat::R8) { + return textureLinearR8(sampler, vec2(P.x, P.y), zoffset); + } else { + assert(sampler->format == TextureFormat::RGBA32F); + return textureLinearRGBA32F(sampler, vec2(P.x, P.y), zoffset); + } + } else { + // just do nearest for now + ivec3 coord(roundzero(P.x, sampler->width), roundzero(P.y, sampler->height), + roundeven(P.z, 1.0f)); + return texelFetch(sampler, coord, 0); + } +} + +vec4 texture(sampler2DArray sampler, vec3 P, float bias) { + assert(bias == 0.0f); + return texture(sampler, P); +} + +vec4 textureLod(sampler2DArray sampler, vec3 P, float lod) { + assert(lod == 0.0f); + 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)}; +} + +ivec2_scalar textureSize(sampler2DRect sampler) { + return ivec2_scalar{int32_t(sampler->width), int32_t(sampler->height)}; } ivec4 ivec2::sel(XYZW c1, XYZW c2, XYZW c3, XYZW c4) { @@ -2675,30 +2933,15 @@ SI T mix(T x, T y, bvec4_scalar a) { } template <typename T> -SI T mix(T x, T y, bvec4_scalar1 a) { - return a.x ? y : x; -} - -template <typename T> 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 <typename T> -SI T mix(T x, T y, bvec3_scalar1 a) { - return a.x ? y : x; -} - -template <typename T> SI T mix(T x, T y, bvec2_scalar a) { return T{a.x ? y.x : x.x, a.y ? y.y : x.y}; } -template <typename T> -SI T mix(T x, T y, bvec2_scalar1 a) { - return a.x ? y : x; -} - float dot(vec3_scalar a, vec3_scalar b) { return a.x * b.x + a.y * b.y + a.z * b.z; } @@ -2736,28 +2979,7 @@ Float atan(Float v) { return {atanf(v.x), atanf(v.y), atanf(v.z), atanf(v.w)}; } float atan(float a, float b) { return atan2f(a, b); } Float atan(Float a, Float b) { - return {atan2f(a.x, b.x), atan2f(a.y, b.y), atan2f(a.z, b.z), - atan2f(a.w, b.w)}; -} - -bvec4 equal(vec4 x, vec4 y) { - return bvec4(equal(x.x, y.x), equal(x.y, y.y), equal(x.z, y.z), - equal(x.w, y.w)); -} - -bvec4_scalar equal(vec4_scalar x, vec4_scalar y) { - return bvec4_scalar(equal(x.x, y.x), equal(x.y, y.y), equal(x.z, y.z), - equal(x.w, y.w)); -} - -bvec4 notEqual(vec4 x, vec4 y) { - return bvec4(notEqual(x.x, y.x), notEqual(x.y, y.y), notEqual(x.z, y.z), - notEqual(x.w, y.w)); -} - -bvec4_scalar notEqual(vec4_scalar x, vec4_scalar y) { - return bvec4_scalar(notEqual(x.x, y.x), notEqual(x.y, y.y), - notEqual(x.z, y.z), notEqual(x.w, y.w)); + return {atan2f(a.x, b.x), atan2f(a.y, b.y), atan2f(a.z, b.z), atan2f(a.w, b.w)}; } bvec4 notEqual(ivec4 a, ivec4 b) { @@ -2783,18 +3005,12 @@ 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)}; } -vec2 sign(vec2 v) { return vec2(sign(v.x), sign(v.y)); } - -vec2_scalar sign(vec2_scalar v) { return vec2_scalar{sign(v.x), sign(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)); } -vec3 sign(vec3 v) { return vec3(sign(v.x), sign(v.y), sign(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); diff --git a/third_party/webrender/swgl/src/lib.rs b/third_party/webrender/swgl/src/lib.rs index e8fc030e0c9..e19e85fd512 100644 --- a/third_party/webrender/swgl/src/lib.rs +++ b/third_party/webrender/swgl/src/lib.rs @@ -5,7 +5,7 @@ #![crate_name = "swgl"] #![crate_type = "lib"] -extern crate gleam; +use gleam; mod swgl_fns; diff --git a/third_party/webrender/swgl/src/program.h b/third_party/webrender/swgl/src/program.h index 9ea7c6dd6eb..80e5a5b68f7 100644 --- a/third_party/webrender/swgl/src/program.h +++ b/third_party/webrender/swgl/src/program.h @@ -12,12 +12,6 @@ namespace glsl { // to operate in Float-sized chunks. typedef vec3 Interpolants; -// Clip distances, if enabled, are always stored in the first SIMD chunk of the -// interpolants. -static ALWAYS_INLINE Float get_clip_distances(const Interpolants& interp) { - return interp.x; -} - struct VertexShaderImpl; struct FragmentShaderImpl; @@ -29,14 +23,10 @@ struct ProgramImpl { virtual size_t interpolants_size() const = 0; virtual VertexShaderImpl* get_vertex_shader() = 0; virtual FragmentShaderImpl* get_fragment_shader() = 0; - virtual const char* get_name() const = 0; }; typedef ProgramImpl* (*ProgramLoader)(); -// The maximum size of the gl_ClipDistance array. -constexpr int32_t gl_MaxClipDistances = 4; - struct VertexShaderImpl { typedef void (*SetUniform1iFunc)(VertexShaderImpl*, int index, int value); typedef void (*SetUniform4fvFunc)(VertexShaderImpl*, int index, @@ -56,17 +46,7 @@ struct VertexShaderImpl { LoadAttribsFunc load_attribs_func = nullptr; RunPrimitiveFunc run_primitive_func = nullptr; - enum FLAGS { - CLIP_DISTANCE = 1 << 0, - }; - int flags = 0; - void enable_clip_distance() { flags |= CLIP_DISTANCE; } - ALWAYS_INLINE bool use_clip_distance() const { - return (flags & CLIP_DISTANCE) != 0; - } - vec4 gl_Position; - Float gl_ClipDistance[gl_MaxClipDistances]; void set_uniform_1i(int index, int value) { (*set_uniform_1i_func)(this, index, value); @@ -92,20 +72,18 @@ struct VertexShaderImpl { } }; -// The number of pixels in a step. -constexpr int32_t swgl_StepSize = 4; - struct FragmentShaderImpl { typedef void (*InitSpanFunc)(FragmentShaderImpl*, const void* interps, - const void* step); + const void* step, float step_width); typedef void (*RunFunc)(FragmentShaderImpl*); - typedef void (*SkipFunc)(FragmentShaderImpl*, int steps); + typedef void (*SkipFunc)(FragmentShaderImpl*, int chunks); typedef void (*InitSpanWFunc)(FragmentShaderImpl*, const void* interps, - const void* step); + const void* step, float step_width); typedef void (*RunWFunc)(FragmentShaderImpl*); - typedef void (*SkipWFunc)(FragmentShaderImpl*, int steps); - typedef int (*DrawSpanRGBA8Func)(FragmentShaderImpl*); - typedef int (*DrawSpanR8Func)(FragmentShaderImpl*); + typedef void (*SkipWFunc)(FragmentShaderImpl*, int chunks); + typedef void (*DrawSpanRGBA8Func)(FragmentShaderImpl*, uint32_t* buf, + int len); + typedef void (*DrawSpanR8Func)(FragmentShaderImpl*, uint8_t* buf, int len); InitSpanFunc init_span_func = nullptr; RunFunc run_func = nullptr; @@ -129,27 +107,31 @@ struct FragmentShaderImpl { } vec4 gl_FragCoord; + vec2_scalar stepZW; + Bool isPixelDiscarded = false; vec4 gl_FragColor; vec4 gl_SecondaryFragColor; - vec2_scalar swgl_StepZW; - Bool swgl_IsPixelDiscarded = false; - // The current buffer position for committing span output. - uint32_t* swgl_OutRGBA8 = nullptr; - uint8_t* swgl_OutR8 = nullptr; - // The remaining number of pixels in the span. - int32_t swgl_SpanLength = 0; + ALWAYS_INLINE void step_fragcoord() { gl_FragCoord.x += 4; } - ALWAYS_INLINE void step_fragcoord(int steps = 4) { gl_FragCoord.x += steps; } + ALWAYS_INLINE void step_fragcoord(int chunks) { + gl_FragCoord.x += 4 * chunks; + } + + ALWAYS_INLINE void step_perspective() { + gl_FragCoord.z += stepZW.x; + gl_FragCoord.w += stepZW.y; + } - ALWAYS_INLINE void step_perspective(int steps = 4) { - gl_FragCoord.z += swgl_StepZW.x * steps; - gl_FragCoord.w += swgl_StepZW.y * steps; + ALWAYS_INLINE void step_perspective(int chunks) { + gl_FragCoord.z += stepZW.x * chunks; + gl_FragCoord.w += stepZW.y * chunks; } template <bool W = false> - ALWAYS_INLINE void init_span(const void* interps, const void* step) { - (*(W ? init_span_w_func : init_span_func))(this, interps, step); + ALWAYS_INLINE void init_span(const void* interps, const void* step, + float step_width) { + (*(W ? init_span_w_func : init_span_func))(this, interps, step, step_width); } template <bool W = false> @@ -158,24 +140,20 @@ struct FragmentShaderImpl { } template <bool W = false> - ALWAYS_INLINE void skip(int steps = 4) { - (*(W ? skip_w_func : skip_func))(this, steps); + ALWAYS_INLINE void skip(int chunks = 1) { + (*(W ? skip_w_func : skip_func))(this, chunks); } - ALWAYS_INLINE int draw_span(uint32_t* buf, int len) { - swgl_OutRGBA8 = buf; - swgl_SpanLength = len; - return (*draw_span_RGBA8_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 int draw_span(uint8_t* buf, int len) { - swgl_OutR8 = buf; - swgl_SpanLength = len; - return (*draw_span_R8_func)(this); + 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*) { diff --git a/third_party/webrender/swgl/src/rasterize.h b/third_party/webrender/swgl/src/rasterize.h deleted file mode 100644 index 48f3b9e5898..00000000000 --- a/third_party/webrender/swgl/src/rasterize.h +++ /dev/null @@ -1,1670 +0,0 @@ -/* 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/. */ - -// The SWGL depth buffer is roughly organized as a span buffer where each row -// of the depth buffer is a list of spans, and each span has a constant depth -// and a run length (represented by DepthRun). The span from start..start+count -// is placed directly at that start index in the row's array of runs, so that -// there is no need to explicitly record the start index at all. This also -// avoids the need to move items around in the run array to manage insertions -// since space is implicitly always available for a run between any two -// pre-existing runs. Linkage from one run to the next is implicitly defined by -// the count, so if a run exists from start..start+count, the next run will -// implicitly pick up right at index start+count where that preceding run left -// off. All of the DepthRun items that are after the head of the run can remain -// uninitialized until the run needs to be split and a new run needs to start -// somewhere in between. -// For uses like perspective-correct rasterization or with a discard mask, a -// run is not an efficient representation, and it is more beneficial to have -// a flattened array of individual depth samples that can be masked off easily. -// To support this case, the first run in a given row's run array may have a -// zero count, signaling that this entire row is flattened. Critically, the -// depth and count fields in DepthRun are ordered (endian-dependently) so that -// the DepthRun struct can be interpreted as a sign-extended int32_t depth. It -// is then possible to just treat the entire row as an array of int32_t depth -// samples that can be processed with SIMD comparisons, since the count field -// behaves as just the sign-extension of the depth field. The count field is -// limited to 8 bits so that we can support depth values up to 24 bits. -// When a depth buffer is cleared, each row is initialized to a maximal runs -// spanning the entire row. In the normal case, the depth buffer will continue -// to manage itself as a list of runs. If perspective or discard is used for -// a given row, the row will be converted to the flattened representation to -// support it, after which it will only ever revert back to runs if the depth -// buffer is cleared. - -// The largest 24-bit depth value supported. -constexpr uint32_t MAX_DEPTH_VALUE = 0xFFFFFF; -// The longest 8-bit depth run that is supported, aligned to SIMD chunk size. -constexpr uint32_t MAX_DEPTH_RUN = 255 & ~3; - -struct DepthRun { - // Ensure that depth always occupies the LSB and count the MSB so that we - // can sign-extend depth just by setting count to zero, marking it flat. - // When count is non-zero, then this is interpreted as an actual run and - // depth is read in isolation. -#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - uint32_t depth : 24; - uint32_t count : 8; -#else - uint32_t count : 8; - uint32_t depth : 24; -#endif - - DepthRun() = default; - DepthRun(uint32_t depth, uint8_t count) : depth(depth), count(count) {} - - // If count is zero, this is actually a flat depth sample rather than a run. - bool is_flat() const { return !count; } - - // Compare a source depth from rasterization with a stored depth value. - template <int FUNC> - ALWAYS_INLINE bool compare(uint32_t src) const { - switch (FUNC) { - case GL_LEQUAL: - return src <= depth; - case GL_LESS: - return src < depth; - case GL_ALWAYS: - return true; - default: - assert(false); - return false; - } - } -}; - -// Fills runs at the given position with the given depth up to the span width. -static ALWAYS_INLINE void set_depth_runs(DepthRun* runs, uint32_t depth, - uint32_t width) { - // If the width exceeds the maximum run size, then we need to output clamped - // runs first. - for (; width >= MAX_DEPTH_RUN; - runs += MAX_DEPTH_RUN, width -= MAX_DEPTH_RUN) { - *runs = DepthRun(depth, MAX_DEPTH_RUN); - } - // If there are still any left over samples to fill under the maximum run - // size, then output one last run for them. - if (width > 0) { - *runs = DepthRun(depth, width); - } -} - -// A cursor for reading and modifying a row's depth run array. It locates -// and iterates through a desired span within all the runs, testing if -// the depth of this span passes or fails the depth test against existing -// runs. If desired, new runs may be inserted to represent depth occlusion -// from this span in the run array. -struct DepthCursor { - // Current position of run the cursor has advanced to. - DepthRun* cur = nullptr; - // The start of the remaining potential samples in the desired span. - DepthRun* start = nullptr; - // The end of the potential samples in the desired span. - DepthRun* end = nullptr; - - DepthCursor() = default; - - // Construct a cursor with runs for a given row's run array and the bounds - // of the span we wish to iterate within it. - DepthCursor(DepthRun* runs, int num_runs, int span_offset, int span_count) - : cur(runs), start(&runs[span_offset]), end(start + span_count) { - // This cursor should never iterate over flat runs - assert(!runs->is_flat()); - DepthRun* end_runs = &runs[num_runs]; - // Clamp end of span to end of row - if (end > end_runs) { - end = end_runs; - } - // If the span starts past the end of the row, just advance immediately - // to it to signal that we're done. - if (start >= end_runs) { - cur = end_runs; - start = end_runs; - return; - } - // Otherwise, find the first depth run that contains the start of the span. - // If the span starts after the given run, then we need to keep searching - // through the row to find an appropriate run. The check above already - // guaranteed that the span starts within the row's runs, and the search - // won't fall off the end. - for (;;) { - assert(cur < end); - DepthRun* next = cur + cur->count; - if (start < next) { - break; - } - cur = next; - } - } - - // The cursor is valid if the current position is at the end or if the run - // contains the start position. - bool valid() const { - return cur >= end || (cur <= start && start < cur + cur->count); - } - - // Skip past any initial runs that fail the depth test. If we find a run that - // would pass, then return the accumulated length between where we started - // and that position. Otherwise, if we fall off the end, return -1 to signal - // that there are no more passed runs at the end of this failed region and - // so it is safe for the caller to stop processing any more regions in this - // row. - template <int FUNC> - int skip_failed(uint32_t val) { - assert(valid()); - DepthRun* prev = start; - while (cur < end) { - if (cur->compare<FUNC>(val)) { - return start - prev; - } - cur += cur->count; - start = cur; - } - return -1; - } - - // Helper to convert function parameters into template parameters to hoist - // some checks out of inner loops. - ALWAYS_INLINE int skip_failed(uint32_t val, GLenum func) { - switch (func) { - case GL_LEQUAL: - return skip_failed<GL_LEQUAL>(val); - case GL_LESS: - return skip_failed<GL_LESS>(val); - default: - assert(false); - return -1; - } - } - - // Find a region of runs that passes the depth test. It is assumed the caller - // has called skip_failed first to skip past any runs that failed the depth - // test. This stops when it finds a run that fails the depth test or we fall - // off the end of the row. If the write mask is enabled, this will insert runs - // to represent this new region that passed the depth test. The length of the - // region is returned. - template <int FUNC, bool MASK> - int check_passed(uint32_t val) { - assert(valid()); - DepthRun* prev = cur; - while (cur < end) { - if (!cur->compare<FUNC>(val)) { - break; - } - DepthRun* next = cur + cur->count; - if (next > end) { - if (MASK) { - // Chop the current run where the end of the span falls, making a new - // run from the end of the span till the next run. The beginning of - // the current run will be folded into the run from the start of the - // passed region before returning below. - *end = DepthRun(cur->depth, next - end); - } - // If the next run starts past the end, then just advance the current - // run to the end to signal that we're now at the end of the row. - next = end; - } - cur = next; - } - // If we haven't advanced past the start of the span region, then we found - // nothing that passed. - if (cur <= start) { - return 0; - } - // If 'end' fell within the middle of a passing run, then 'cur' will end up - // pointing at the new partial run created at 'end' where the passing run - // was split to accommodate starting in the middle. The preceding runs will - // be fixed below to properly join with this new split. - int passed = cur - start; - if (MASK) { - // If the search started from a run before the start of the span, then - // edit that run to meet up with the start. - if (prev < start) { - prev->count = start - prev; - } - // Create a new run for the entirety of the passed samples. - set_depth_runs(start, val, passed); - } - start = cur; - return passed; - } - - // Helper to convert function parameters into template parameters to hoist - // some checks out of inner loops. - template <bool MASK> - ALWAYS_INLINE int check_passed(uint32_t val, GLenum func) { - switch (func) { - case GL_LEQUAL: - return check_passed<GL_LEQUAL, MASK>(val); - case GL_LESS: - return check_passed<GL_LESS, MASK>(val); - default: - assert(false); - return 0; - } - } - - ALWAYS_INLINE int check_passed(uint32_t val, GLenum func, bool mask) { - return mask ? check_passed<true>(val, func) - : check_passed<false>(val, func); - } - - // Fill a region of runs with a given depth value, bypassing any depth test. - ALWAYS_INLINE void fill(uint32_t depth) { - check_passed<GL_ALWAYS, true>(depth); - } -}; - -// Initialize a depth texture by setting the first run in each row to encompass -// the entire row. -void Texture::init_depth_runs(uint32_t depth) { - if (!buf) return; - DepthRun* runs = (DepthRun*)buf; - for (int y = 0; y < height; y++) { - set_depth_runs(runs, depth, width); - runs += stride() / sizeof(DepthRun); - } - set_cleared(true); -} - -// Fill a portion of the run array with flattened depth samples. -static ALWAYS_INLINE void fill_flat_depth(DepthRun* dst, size_t n, - uint32_t depth) { - fill_n((uint32_t*)dst, n, depth); -} - -// Fills a scissored region of a depth texture with a given depth. -void Texture::fill_depth_runs(uint32_t depth, const IntRect& scissor) { - if (!buf) return; - assert(cleared()); - IntRect bb = bounds().intersection(scissor - offset); - DepthRun* runs = (DepthRun*)sample_ptr(0, bb.y0); - for (int rows = bb.height(); rows > 0; rows--) { - if (bb.width() >= width) { - // If the scissor region encompasses the entire row, reset the row to a - // single run encompassing the entire row. - set_depth_runs(runs, depth, width); - } else if (runs->is_flat()) { - // If the row is flattened, just directly fill the portion of the row. - fill_flat_depth(&runs[bb.x0], bb.width(), depth); - } else { - // Otherwise, if we are still using runs, then set up a cursor to fill - // it with depth runs. - DepthCursor(runs, width, bb.x0, bb.width()).fill(depth); - } - runs += stride() / sizeof(DepthRun); - } -} - -using ZMask = I32; - -#if USE_SSE2 -# define ZMASK_NONE_PASSED 0xFFFF -# define ZMASK_ALL_PASSED 0 -static inline uint32_t zmask_code(ZMask mask) { - return _mm_movemask_epi8(mask); -} -#else -# define ZMASK_NONE_PASSED 0xFFFFFFFFU -# define ZMASK_ALL_PASSED 0 -static inline uint32_t zmask_code(ZMask mask) { - return bit_cast<uint32_t>(CONVERT(mask, U8)); -} -#endif - -// Interprets items in the depth buffer as sign-extended 32-bit depth values -// instead of as runs. Returns a mask that signals which samples in the given -// chunk passed or failed the depth test with given Z value. -template <bool DISCARD> -static ALWAYS_INLINE bool check_depth(I32 src, DepthRun* zbuf, ZMask& outmask, - int span = 4) { - // SSE2 does not support unsigned comparison. So ensure Z value is - // sign-extended to int32_t. - I32 dest = unaligned_load<I32>(zbuf); - // Invert the depth test to check which pixels failed and should be discarded. - ZMask mask = ctx->depthfunc == GL_LEQUAL - ? - // GL_LEQUAL: Not(LessEqual) = Greater - ZMask(src > dest) - : - // GL_LESS: Not(Less) = GreaterEqual - ZMask(src >= dest); - // Mask off any unused lanes in the span. - mask |= ZMask(span) < ZMask{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 ALWAYS_INLINE I32 packDepth() { - return cast(fragment_shader->gl_FragCoord.z * MAX_DEPTH_VALUE); -} - -static ALWAYS_INLINE void discard_depth(I32 src, DepthRun* zbuf, I32 mask) { - if (ctx->depthmask) { - I32 dest = unaligned_load<I32>(zbuf); - mask |= fragment_shader->swgl_IsPixelDiscarded; - unaligned_store(zbuf, (mask & dest) | (~mask & src)); - } -} - -static ALWAYS_INLINE void mask_output(uint32_t* buf, ZMask zmask, - int span = 4) { - WideRGBA8 r = pack_pixels_RGBA8(); - PackedRGBA8 dst = load_span<PackedRGBA8>(buf, span); - if (blend_key) r = blend_pixels(buf, dst, r, span); - PackedRGBA8 mask = bit_cast<PackedRGBA8>(zmask); - store_span(buf, (mask & dst) | (~mask & pack(r)), span); -} - -template <bool DISCARD> -static ALWAYS_INLINE void discard_output(uint32_t* buf, int span = 4) { - mask_output(buf, fragment_shader->swgl_IsPixelDiscarded, span); -} - -template <> -ALWAYS_INLINE void discard_output<false>(uint32_t* buf, int span) { - WideRGBA8 r = pack_pixels_RGBA8(); - if (blend_key) - r = blend_pixels(buf, load_span<PackedRGBA8>(buf, span), r, span); - store_span(buf, pack(r), span); -} - -static ALWAYS_INLINE void mask_output(uint8_t* buf, ZMask zmask, int span = 4) { - WideR8 r = pack_pixels_R8(); - WideR8 dst = unpack(load_span<PackedR8>(buf, span)); - if (blend_key) r = blend_pixels(buf, dst, r, span); - WideR8 mask = packR8(zmask); - store_span(buf, pack((mask & dst) | (~mask & r)), span); -} - -template <bool DISCARD> -static ALWAYS_INLINE void discard_output(uint8_t* buf, int span = 4) { - mask_output(buf, fragment_shader->swgl_IsPixelDiscarded, span); -} - -template <> -ALWAYS_INLINE void discard_output<false>(uint8_t* buf, int span) { - WideR8 r = pack_pixels_R8(); - if (blend_key) - r = blend_pixels(buf, unpack(load_span<PackedR8>(buf, span)), r, span); - store_span(buf, pack(r), span); -} - -struct ClipRect { - float x0; - float y0; - float x1; - float y1; - - explicit ClipRect(const IntRect& i) - : x0(i.x0), y0(i.y0), x1(i.x1), y1(i.y1) {} - explicit ClipRect(const Texture& t) : ClipRect(ctx->apply_scissor(t)) { - // If blending is enabled, set blend_key to reflect the resolved blend - // state for the currently drawn primitive. - if (ctx->blend) { - blend_key = ctx->blend_key; - if (swgl_ClipFlags) { - // If there is a blend override set, replace the blend key with it. - if (swgl_ClipFlags & SWGL_CLIP_FLAG_BLEND_OVERRIDE) { - blend_key = swgl_BlendOverride; - } - // If a clip mask is available, set up blending state to use the clip - // mask. - if (swgl_ClipFlags & SWGL_CLIP_FLAG_MASK) { - assert(swgl_ClipMask->format == TextureFormat::R8); - // Constrain the clip mask bounds to always fall within the clip mask. - swgl_ClipMaskBounds.intersect(IntRect{0, 0, int(swgl_ClipMask->width), - int(swgl_ClipMask->height)}); - // The clip mask offset is relative to the viewport. - swgl_ClipMaskOffset += ctx->viewport.origin() - t.offset; - // The clip mask bounds are relative to the clip mask offset. - swgl_ClipMaskBounds.offset(swgl_ClipMaskOffset); - // Finally, constrain the clip rectangle by the clip mask bounds. - intersect(swgl_ClipMaskBounds); - // Modify the blend key so that it will use the clip mask while - // blending. - restore_clip_mask(); - } - if (swgl_ClipFlags & SWGL_CLIP_FLAG_AA) { - // Modify the blend key so that it will use AA while blending. - restore_aa(); - } - } - } else { - blend_key = BLEND_KEY_NONE; - swgl_ClipFlags = 0; - } - } - - FloatRange x_range() const { return {x0, x1}; } - - void intersect(const IntRect& c) { - x0 = max(x0, float(c.x0)); - y0 = max(y0, float(c.y0)); - x1 = min(x1, float(c.x1)); - y1 = min(y1, float(c.y1)); - } - - template <typename P> - void set_clip_mask(int x, int y, P* buf) const { - if (swgl_ClipFlags & SWGL_CLIP_FLAG_MASK) { - swgl_SpanBuf = buf; - swgl_ClipMaskBuf = (uint8_t*)swgl_ClipMask->buf + - (y - swgl_ClipMaskOffset.y) * swgl_ClipMask->stride + - (x - swgl_ClipMaskOffset.x); - } - } - - template <typename P> - bool overlaps(int nump, const P* p) const { - // Generate a mask of which side of the clip rect all of a polygon's points - // fall inside of. This is a cheap conservative estimate of whether the - // bounding box of the polygon might overlap the clip rect, rather than an - // exact test that would require multiple slower line intersections. - int sides = 0; - for (int i = 0; i < nump; i++) { - sides |= p[i].x < x1 ? (p[i].x > x0 ? 1 | 2 : 1) : 2; - sides |= p[i].y < y1 ? (p[i].y > y0 ? 4 | 8 : 4) : 8; - } - return sides == 0xF; - } -}; - -// Given a current X position at the center Y position of a row, return the X -// position of the left and right intercepts of the row top and bottom. -template <typename E> -static ALWAYS_INLINE FloatRange x_intercepts(const E& e) { - float rad = 0.5f * abs(e.x_slope()); - return {e.cur_x() - rad, e.cur_x() + rad}; -} - -// Return the AA sub-span corresponding to a given edge. If AA is requested, -// then this finds the X intercepts with the row clipped into range of the -// edge and finally conservatively rounds them out. If there is no AA, then -// it just returns the current rounded X position clipped within bounds. -template <typename E> -static ALWAYS_INLINE IntRange aa_edge(const E& e, const FloatRange& bounds) { - return e.edgeMask ? bounds.clip(x_intercepts(e)).round_out() - : bounds.clip({e.cur_x(), e.cur_x()}).round(); -} - -// Calculate the initial AA coverage as an approximation of the distance from -// the center of the pixel in the direction of the edge slope. Given an edge -// (x,y)..(x+dx,y+dy), then the normalized tangent vector along the edge is -// (dx,dy)/sqrt(dx^2+dy^2). We know that for dy=1 then dx=e.x_slope. We rotate -// the tangent vector either -90 or +90 degrees to get the edge normal vector, -// where 'dx=-dy and 'dy=dx. Once normalized by 1/sqrt(dx^2+dy^2), scale into -// the range of 0..256 so that we can cheaply convert to a fixed-point scale -// factor. It is assumed that at exactly the pixel center the opacity is half -// (128) and linearly decreases along the normal vector at 1:1 scale with the -// slope. While not entirely accurate, this gives a reasonably agreeable looking -// approximation of AA. For edges on which there is no AA, just force the -// opacity to maximum (256) with no slope, relying on the span clipping to trim -// pixels outside the span. -template <typename E> -static ALWAYS_INLINE FloatRange aa_dist(const E& e, float dir) { - if (e.edgeMask) { - float dx = (dir * 256.0f) * inversesqrt(1.0f + e.x_slope() * e.x_slope()); - return {128.0f + dx * (e.cur_x() - 0.5f), -dx}; - } else { - return {256.0f, 0.0f}; - } -} - -template <typename P, typename E> -static ALWAYS_INLINE IntRange aa_span(P* buf, const E& left, const E& right, - const FloatRange& bounds) { - // If there is no AA, just return the span from the rounded left edge X - // position to the rounded right edge X position. Clip the span to be within - // the valid bounds. - if (!(swgl_ClipFlags & SWGL_CLIP_FLAG_AA)) { - return bounds.clip({left.cur_x(), right.cur_x()}).round(); - } - - // Calculate the left and right AA spans along with the coverage distances - // and slopes necessary to do blending. - IntRange leftAA = aa_edge(left, bounds); - FloatRange leftDist = aa_dist(left, -1.0f); - IntRange rightAA = aa_edge(right, bounds); - FloatRange rightDist = aa_dist(right, 1.0f); - - // Use the pointer into the destination buffer as a status indicator of the - // coverage offset. The pointer is calculated so that subtracting it with - // the current destination pointer will yield a negative value if the span - // is outside the opaque area and otherwise will yield a positive value - // above the opaque size. This pointer is stored as a uint8 pointer so that - // there are no hidden multiplication instructions and will just return a - // 1:1 linear memory address. Thus the size of the opaque region must also - // be scaled by the pixel size in bytes. - swgl_OpaqueStart = (const uint8_t*)(buf + leftAA.end); - swgl_OpaqueSize = max(rightAA.start - leftAA.end - 3, 0) * sizeof(P); - - // Offset the coverage distances by the end of the left AA span, which - // corresponds to the opaque start pointer, so that pixels become opaque - // immediately after. The distances are also offset for each lane in the - // chunk. - Float offset = cast(leftAA.end + (I32){0, 1, 2, 3}); - swgl_LeftAADist = leftDist.start + offset * leftDist.end; - swgl_RightAADist = rightDist.start + offset * rightDist.end; - swgl_AASlope = - (Float){leftDist.end, rightDist.end, 0.0f, 0.0f} / float(sizeof(P)); - - // Return the full span width from the start of the left span to the end of - // the right span. - return {leftAA.start, rightAA.end}; -} - -// Calculate the span the user clip distances occupy from the left and right -// edges at the current row. -template <typename E> -static ALWAYS_INLINE IntRange clip_distance_range(const E& left, - const E& right) { - Float leftClip = get_clip_distances(left.interp); - Float rightClip = get_clip_distances(right.interp); - // Get the change in clip dist per X step. - Float clipStep = (rightClip - leftClip) / (right.cur_x() - left.cur_x()); - // Find the zero intercepts starting from the left edge. - Float clipDist = left.cur_x() - leftClip * recip(clipStep); - // Find the distance to the start of the span for any clip distances that - // are increasing in value. If the clip distance is constant or decreasing - // in value, then check if it starts outside the clip volume. - Float start = if_then_else(clipStep > 0.0f, clipDist, - if_then_else(leftClip < 0.0f, 1.0e6f, 0.0f)); - // Find the distance to the end of the span for any clip distances that are - // decreasing in value. If the clip distance is constant or increasing in - // value, then check if it ends inside the clip volume. - Float end = if_then_else(clipStep < 0.0f, clipDist, - if_then_else(rightClip >= 0.0f, 1.0e6f, 0.0f)); - // Find the furthest start offset. - start = max(start, start.zwxy); - // Find the closest end offset. - end = min(end, end.zwxy); - // Finally, round the offsets to an integer span that can be used to bound - // the current span. - return FloatRange{max(start.x, start.y), min(end.x, end.y)}.round(); -} - -// Converts a run array into a flattened array of depth samples. This just -// walks through every run and fills the samples with the depth value from -// the run. -static void flatten_depth_runs(DepthRun* runs, size_t width) { - if (runs->is_flat()) { - return; - } - while (width > 0) { - size_t n = runs->count; - fill_flat_depth(runs, n, runs->depth); - runs += n; - width -= n; - } -} - -// Helper function for drawing passed depth runs within the depth buffer. -// Flattened depth (perspective or discard) is not supported. -template <typename P> -static ALWAYS_INLINE void draw_depth_span(uint32_t z, P* buf, - DepthCursor& cursor) { - for (;;) { - // Get the span that passes the depth test. Assume on entry that - // any failed runs have already been skipped. - int span = cursor.check_passed(z, ctx->depthfunc, ctx->depthmask); - // If nothing passed, since we already skipped passed failed runs - // previously, we must have hit the end of the row. Bail out. - if (span <= 0) { - break; - } - if (span >= 4) { - // If we have a draw specialization, try to process as many 4-pixel - // chunks as possible using it. - if (fragment_shader->has_draw_span(buf)) { - int drawn = fragment_shader->draw_span(buf, span & ~3); - buf += drawn; - span -= drawn; - } - // Otherwise, just process each chunk individually. - while (span >= 4) { - fragment_shader->run(); - discard_output<false>(buf); - buf += 4; - span -= 4; - } - } - // If we have a partial chunk left over, we still have to process it as if - // it were a full chunk. Mask off only the part of the chunk we want to - // use. - if (span > 0) { - fragment_shader->run(); - discard_output<false>(buf, span); - buf += span; - } - // Skip past any runs that fail the depth test. - int skip = cursor.skip_failed(z, ctx->depthfunc); - // If there aren't any, that means we won't encounter any more passing runs - // and so it's safe to bail out. - if (skip <= 0) { - break; - } - // Advance interpolants for the fragment shader past the skipped region. - // If we processed a partial chunk above, we actually advanced the - // interpolants a full chunk in the fragment shader's run function. Thus, - // we need to first subtract off that 4-pixel chunk and only partially - // advance them to that partial chunk before we can add on the rest of the - // skips. This is combined with the skip here for efficiency's sake. - fragment_shader->skip(skip - (span > 0 ? 4 - span : 0)); - buf += skip; - } -} - -// Draw a simple span in 4-pixel wide chunks, optionally using depth. -template <bool DISCARD, bool W, typename P, typename Z> -static ALWAYS_INLINE void draw_span(P* buf, DepthRun* depth, int span, Z z) { - if (depth) { - // Depth testing is enabled. If perspective is used, Z values will vary - // across the span, we use packDepth to generate packed Z values suitable - // for depth testing based on current values from gl_FragCoord.z. - // Otherwise, for the no-perspective case, we just use the provided Z. - // Process 4-pixel chunks first. - for (; span >= 4; span -= 4, buf += 4, depth += 4) { - I32 zsrc = z(); - ZMask zmask; - if (check_depth<DISCARD>(zsrc, depth, zmask)) { - fragment_shader->run<W>(); - mask_output(buf, zmask); - if (DISCARD) discard_depth(zsrc, depth, zmask); - } else { - fragment_shader->skip<W>(); - } - } - // If there are any remaining pixels, do a partial chunk. - if (span > 0) { - I32 zsrc = z(); - ZMask zmask; - if (check_depth<DISCARD>(zsrc, depth, zmask, span)) { - fragment_shader->run<W>(); - mask_output(buf, zmask, span); - if (DISCARD) discard_depth(zsrc, depth, zmask); - } - } - } else { - // Process 4-pixel chunks first. - for (; span >= 4; span -= 4, buf += 4) { - fragment_shader->run<W>(); - discard_output<DISCARD>(buf); - } - // If there are any remaining pixels, do a partial chunk. - if (span > 0) { - fragment_shader->run<W>(); - discard_output<DISCARD>(buf, span); - } - } -} - -// Called during rasterization to forcefully clear a row on which delayed clear -// has been enabled. If we know that we are going to completely overwrite a part -// of the row, then we only need to clear the row outside of that part. However, -// if blending or discard is enabled, the values of that underlying part of the -// row may be used regardless to produce the final rasterization result, so we -// have to then clear the entire underlying row to prepare it. -template <typename P> -static inline void prepare_row(Texture& colortex, int y, int startx, int endx, - bool use_discard, DepthRun* depth, - uint32_t z = 0, DepthCursor* cursor = nullptr) { - assert(colortex.delay_clear > 0); - // Delayed clear is enabled for the color buffer. Check if needs clear. - uint32_t& mask = colortex.cleared_rows[y / 32]; - if ((mask & (1 << (y & 31))) == 0) { - mask |= 1 << (y & 31); - colortex.delay_clear--; - if (blend_key || use_discard) { - // If depth test, blending, or discard is used, old color values - // might be sampled, so we need to clear the entire row to fill it. - force_clear_row<P>(colortex, y); - } else if (depth) { - if (depth->is_flat() || !cursor) { - // If flat depth is used, we can't cheaply predict if which samples will - // pass. - force_clear_row<P>(colortex, y); - } else { - // Otherwise if depth runs are used, see how many samples initially pass - // the depth test and only fill the row outside those. The fragment - // shader will fill the row within the passed samples. - int passed = - DepthCursor(*cursor).check_passed<false>(z, ctx->depthfunc); - if (startx > 0 || startx + passed < colortex.width) { - force_clear_row<P>(colortex, y, startx, startx + passed); - } - } - } else if (startx > 0 || endx < colortex.width) { - // Otherwise, we only need to clear the row outside of the span. - // The fragment shader will fill the row within the span itself. - force_clear_row<P>(colortex, y, startx, endx); - } - } -} - -// Perpendicular dot-product is the dot-product of a vector with the -// perpendicular vector of the other, i.e. dot(a, {-b.y, b.x}) -template <typename T> -static ALWAYS_INLINE auto perpDot(T a, T b) { - return a.x * b.y - a.y * b.x; -} - -// Check if the winding of the initial edges is flipped, requiring us to swap -// the edges to avoid spans having negative lengths. Assume that l0.y == r0.y -// due to the initial edge scan in draw_quad/perspective_spans. -template <typename T> -static ALWAYS_INLINE bool checkIfEdgesFlipped(T l0, T l1, T r0, T r1) { - // If the starting point of the left edge is to the right of the starting - // point of the right edge, then just assume the edges are flipped. If the - // left and right starting points are the same, then check the sign of the - // cross-product of the edges to see if the edges are flipped. Otherwise, - // if the left starting point is actually just to the left of the right - // starting point, then assume no edge flip. - return l0.x > r0.x || (l0.x == r0.x && perpDot(l1 - l0, r1 - r0) > 0.0f); -} - -// Draw spans for each row of a given quad (or triangle) with a constant Z -// value. The quad is assumed convex. It is clipped to fall within the given -// clip rect. In short, this function rasterizes a quad by first finding a -// top most starting point and then from there tracing down the left and right -// sides of this quad until it hits the bottom, outputting a span between the -// current left and right positions at each row along the way. Points are -// assumed to be ordered in either CW or CCW to support this, but currently -// both orders (CW and CCW) are supported and equivalent. -template <typename P> -static inline void draw_quad_spans(int nump, Point2D p[4], uint32_t z, - Interpolants interp_outs[4], - Texture& colortex, Texture& depthtex, - const ClipRect& clipRect) { - // Only triangles and convex quads supported. - assert(nump == 3 || nump == 4); - - Point2D l0, r0, l1, r1; - int l0i, r0i, l1i, r1i; - { - // Find the index of the top-most (smallest Y) point from which - // rasterization can start. - 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)); - // Helper to find next index in the points array, walking forward. -#define NEXT_POINT(idx) \ - ({ \ - int cur = (idx) + 1; \ - cur < nump ? cur : 0; \ - }) - // Helper to find the previous index in the points array, walking backward. -#define PREV_POINT(idx) \ - ({ \ - int cur = (idx)-1; \ - cur >= 0 ? cur : nump - 1; \ - }) - // Start looking for "left"-side and "right"-side descending edges starting - // from the determined top point. - int next = NEXT_POINT(top); - int prev = PREV_POINT(top); - if (p[top].y == p[next].y) { - // If the next point is on the same row as the top, then advance one more - // time to the next point and use that as the "left" descending edge. - l0i = next; - l1i = NEXT_POINT(next); - // Assume top and prev form a descending "right" edge, as otherwise this - // will be a collapsed polygon and harmlessly bail out down below. - r0i = top; - r1i = prev; - } else if (p[top].y == p[prev].y) { - // If the prev point is on the same row as the top, then advance to the - // prev again and use that as the "right" descending edge. - // Assume top and next form a non-empty descending "left" edge. - l0i = top; - l1i = next; - r0i = prev; - r1i = PREV_POINT(prev); - } else { - // Both next and prev are on distinct rows from top, so both "left" and - // "right" edges are non-empty/descending. - l0i = r0i = top; - l1i = next; - r1i = prev; - } - // Load the points from the indices. - l0 = p[l0i]; // Start of left edge - r0 = p[r0i]; // End of left edge - l1 = p[l1i]; // Start of right edge - r1 = p[r1i]; // End of right edge - // 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); - } - - struct Edge { - float yScale; - float xSlope; - float x; - Interpolants interpSlope; - Interpolants interp; - bool edgeMask; - - Edge(float y, const Point2D& p0, const Point2D& p1, const Interpolants& i0, - const Interpolants& i1, int edgeIndex) - : // Inverse Y scale for slope calculations. Avoid divide on 0-length - // edge. Later checks below ensure that Y <= p1.y, or otherwise we - // don't use this edge. We just need to guard against Y == p1.y == - // p0.y. In that case, Y - p0.y == 0 and will cancel out the slopes - // below, except if yScale is Inf for some reason (or worse, NaN), - // which 1/(p1.y-p0.y) might produce if we don't bound it. - yScale(1.0f / max(p1.y - p0.y, 1.0f / 256)), - // Calculate dX/dY slope - xSlope((p1.x - p0.x) * yScale), - // Initialize current X based on Y and slope - x(p0.x + (y - p0.y) * xSlope), - // Calculate change in interpolants per change in Y - interpSlope((i1 - i0) * yScale), - // Initialize current interpolants based on Y and slope - interp(i0 + (y - p0.y) * interpSlope), - // Extract the edge mask status for this edge - edgeMask((swgl_AAEdgeMask >> edgeIndex) & 1) {} - - void nextRow() { - // step current X and interpolants to next row from slope - x += xSlope; - interp += interpSlope; - } - - float cur_x() const { return x; } - float x_slope() const { return xSlope; } - }; - - // Vertex selection above should result in equal left and right start rows - assert(l0.y == r0.y); - // Find the start y, clip to within the clip rect, and round to row center. - // If AA is enabled, round out conservatively rather than round to nearest. - float aaRound = swgl_ClipFlags & SWGL_CLIP_FLAG_AA ? 0.0f : 0.5f; - float y = floor(max(l0.y, clipRect.y0) + aaRound) + 0.5f; - // Initialize left and right edges from end points and start Y - Edge left(y, l0, l1, interp_outs[l0i], interp_outs[l1i], l1i); - Edge right(y, r0, r1, interp_outs[r0i], interp_outs[r1i], r0i); - // WR does not use backface culling, so check if edges are flipped. - bool flipped = checkIfEdgesFlipped(l0, l1, r0, r1); - if (flipped) swap(left, right); - // Get pointer to color buffer and depth buffer at current Y - P* fbuf = (P*)colortex.sample_ptr(0, int(y)); - DepthRun* fdepth = (DepthRun*)depthtex.sample_ptr(0, int(y)); - // Loop along advancing Ys, rasterizing spans at each row - float checkY = min(min(l1.y, r1.y), clipRect.y1); - // Ensure we don't rasterize out edge bounds - FloatRange clipSpan = - clipRect.x_range().clip(x_range(l0, l1).merge(x_range(r0, r1))); - for (;;) { - // Check if we maybe passed edge ends or outside clip rect... - if (y > checkY) { - // If we're outside the clip rect, we're done. - if (y > clipRect.y1) break; - // Helper to find the next non-duplicate vertex that doesn't loop back. -#define STEP_EDGE(y, e0i, e0, e1i, e1, STEP_POINT, end) \ - do { \ - /* Set new start of edge to be end of old edge */ \ - e0i = e1i; \ - e0 = e1; \ - /* Set new end of edge to next point */ \ - e1i = STEP_POINT(e1i); \ - e1 = p[e1i]; \ - /* If the edge crossed the end, we're done. */ \ - if (e0i == end) return; \ - /* Otherwise, it doesn't advance, so keep searching. */ \ - } while (y > e1.y) - // Check if Y advanced past the end of the left edge - if (y > l1.y) { - // Step to next left edge past Y and reset edge interpolants. - STEP_EDGE(y, l0i, l0, l1i, l1, NEXT_POINT, r1i); - (flipped ? right : left) = - Edge(y, l0, l1, interp_outs[l0i], interp_outs[l1i], l1i); - } - // Check if Y advanced past the end of the right edge - if (y > r1.y) { - // Step to next right edge past Y and reset edge interpolants. - STEP_EDGE(y, r0i, r0, r1i, r1, PREV_POINT, l1i); - (flipped ? left : right) = - Edge(y, r0, r1, interp_outs[r0i], interp_outs[r1i], r0i); - } - // Reset the clip bounds for the new edges - clipSpan = - clipRect.x_range().clip(x_range(l0, l1).merge(x_range(r0, r1))); - // Reset check condition for next time around. - checkY = min(ceil(min(l1.y, r1.y) - aaRound), clipRect.y1); - } - - // Calculate a potentially AA'd span and check if it is non-empty. - IntRange span = aa_span(fbuf, left, right, clipSpan); - if (span.len() > 0) { - // If user clip planes are enabled, use them to bound the current span. - if (vertex_shader->use_clip_distance()) { - span = span.intersect(clip_distance_range(left, right)); - if (span.len() <= 0) goto next_span; - } - ctx->shaded_rows++; - ctx->shaded_pixels += span.len(); - // Advance color/depth buffer pointers to the start of the span. - P* buf = fbuf + span.start; - // Check if we will need to use depth-buffer or discard on this span. - DepthRun* depth = - depthtex.buf != nullptr && depthtex.cleared() ? fdepth : nullptr; - DepthCursor cursor; - bool use_discard = fragment_shader->use_discard(); - if (use_discard) { - if (depth) { - // If we're using discard, we may have to unpredictably drop out some - // samples. Flatten the depth run array here to allow this. - if (!depth->is_flat()) { - flatten_depth_runs(depth, depthtex.width); - } - // Advance to the depth sample at the start of the span. - depth += span.start; - } - } else if (depth) { - if (!depth->is_flat()) { - // We're not using discard and the depth row is still organized into - // runs. Skip past any runs that would fail the depth test so we - // don't have to do any extra work to process them with the rest of - // the span. - cursor = DepthCursor(depth, depthtex.width, span.start, span.len()); - int skipped = cursor.skip_failed(z, ctx->depthfunc); - // If we fell off the row, that means we couldn't find any passing - // runs. We can just skip the entire span. - if (skipped < 0) { - goto next_span; - } - buf += skipped; - span.start += skipped; - } else { - // The row is already flattened, so just advance to the span start. - depth += span.start; - } - } - - if (colortex.delay_clear) { - // Delayed clear is enabled for the color buffer. Check if needs clear. - prepare_row<P>(colortex, int(y), span.start, span.end, use_discard, - depth, z, &cursor); - } - - // Initialize fragment shader interpolants to current span position. - fragment_shader->gl_FragCoord.x = init_interp(span.start + 0.5f, 1); - fragment_shader->gl_FragCoord.y = y; - { - // Change in interpolants is difference between current right and left - // edges per the change in right and left X. - Interpolants step = - (right.interp - left.interp) * (1.0f / (right.x - left.x)); - // Advance current interpolants to X at start of span. - Interpolants o = left.interp + step * (span.start + 0.5f - left.x); - fragment_shader->init_span(&o, &step); - } - clipRect.set_clip_mask(span.start, y, buf); - if (!use_discard) { - // Fast paths for the case where fragment discard is not used. - if (depth) { - // If depth is used, we want to process entire depth runs if depth is - // not flattened. - if (!depth->is_flat()) { - draw_depth_span(z, buf, cursor); - goto next_span; - } - // Otherwise, flattened depth must fall back to the slightly slower - // per-chunk depth test path in draw_span below. - } else { - // Check if the fragment shader has an optimized draw specialization. - if (span.len() >= 4 && fragment_shader->has_draw_span(buf)) { - // Draw specialization expects 4-pixel chunks. - int drawn = fragment_shader->draw_span(buf, span.len() & ~3); - buf += drawn; - span.start += drawn; - } - } - draw_span<false, false>(buf, depth, span.len(), [=] { return z; }); - } else { - // If discard is used, then use slower fallbacks. This should be rare. - // Just needs to work, doesn't need to be too fast yet... - draw_span<true, false>(buf, depth, span.len(), [=] { return z; }); - } - } - next_span: - // Advance Y and edge interpolants to next row. - y++; - left.nextRow(); - right.nextRow(); - // Advance buffers to next row. - fbuf += colortex.stride() / sizeof(P); - fdepth += depthtex.stride() / sizeof(DepthRun); - } -} - -// Draw perspective-correct spans for a convex quad that has been clipped to -// the near and far Z planes, possibly producing a clipped convex polygon with -// more than 4 sides. This assumes the Z value will vary across the spans and -// requires interpolants to factor in W values. This tends to be slower than -// the simpler 2D draw_quad_spans above, especially since we can't optimize the -// depth test easily when Z values, and should be used only rarely if possible. -template <typename P> -static inline void draw_perspective_spans(int nump, Point3D* p, - Interpolants* interp_outs, - Texture& colortex, Texture& depthtex, - const ClipRect& clipRect) { - Point3D l0, r0, l1, r1; - int l0i, r0i, l1i, r1i; - { - // Find the index of the top-most point (smallest Y) from which - // rasterization can start. - int top = 0; - for (int i = 1; i < nump; i++) { - if (p[i].y < p[top].y) { - top = i; - } - } - // Find left-most top point, the start of the left descending edge. - // Advance forward in the points array, searching at most nump points - // in case the polygon is flat. - l0i = top; - for (int i = top + 1; i < nump && p[i].y == p[top].y; i++) { - l0i = i; - } - if (l0i == nump - 1) { - for (int i = 0; i <= top && p[i].y == p[top].y; i++) { - l0i = i; - } - } - // Find right-most top point, the start of the right descending edge. - // Advance backward in the points array, searching at most nump points. - r0i = top; - for (int i = top - 1; i >= 0 && p[i].y == p[top].y; i--) { - r0i = i; - } - if (r0i == 0) { - for (int i = nump - 1; i >= top && p[i].y == p[top].y; i--) { - r0i = i; - } - } - // End of left edge is next point after left edge start. - l1i = NEXT_POINT(l0i); - // End of right edge is prev point after right edge start. - r1i = PREV_POINT(r0i); - l0 = p[l0i]; // Start of left edge - r0 = p[r0i]; // End of left edge - l1 = p[l1i]; // Start of right edge - r1 = p[r1i]; // End of right edge - } - - struct Edge { - float yScale; - // Current coordinates for edge. Where in the 2D case of draw_quad_spans, - // it is enough to just track the X coordinate as we advance along the rows, - // for the perspective case we also need to keep track of Z and W. For - // simplicity, we just use the full 3D point to track all these coordinates. - Point3D pSlope; - Point3D p; - Interpolants interpSlope; - Interpolants interp; - bool edgeMask; - - Edge(float y, const Point3D& p0, const Point3D& p1, const Interpolants& i0, - const Interpolants& i1, int edgeIndex) - : // Inverse Y scale for slope calculations. Avoid divide on 0-length - // edge. - yScale(1.0f / max(p1.y - p0.y, 1.0f / 256)), - // Calculate dX/dY slope - pSlope((p1 - p0) * yScale), - // Initialize current coords based on Y and slope - p(p0 + (y - p0.y) * pSlope), - // Crucially, these interpolants must be scaled by the point's 1/w - // value, which allows linear interpolation in a perspective-correct - // manner. This will be canceled out inside the fragment shader later. - // Calculate change in interpolants per change in Y - interpSlope((i1 * p1.w - i0 * p0.w) * yScale), - // Initialize current interpolants based on Y and slope - interp(i0 * p0.w + (y - p0.y) * interpSlope), - // Extract the edge mask status for this edge - edgeMask((swgl_AAEdgeMask >> edgeIndex) & 1) {} - - float x() const { return p.x; } - vec2_scalar zw() const { return {p.z, p.w}; } - - void nextRow() { - // step current coords and interpolants to next row from slope - p += pSlope; - interp += interpSlope; - } - - float cur_x() const { return p.x; } - float x_slope() const { return pSlope.x; } - }; - - // Vertex selection above should result in equal left and right start rows - assert(l0.y == r0.y); - // Find the start y, clip to within the clip rect, and round to row center. - // If AA is enabled, round out conservatively rather than round to nearest. - float aaRound = swgl_ClipFlags & SWGL_CLIP_FLAG_AA ? 0.0f : 0.5f; - float y = floor(max(l0.y, clipRect.y0) + aaRound) + 0.5f; - // Initialize left and right edges from end points and start Y - Edge left(y, l0, l1, interp_outs[l0i], interp_outs[l1i], l1i); - Edge right(y, r0, r1, interp_outs[r0i], interp_outs[r1i], r0i); - // WR does not use backface culling, so check if edges are flipped. - bool flipped = checkIfEdgesFlipped(l0, l1, r0, r1); - if (flipped) swap(left, right); - // Get pointer to color buffer and depth buffer at current Y - P* fbuf = (P*)colortex.sample_ptr(0, int(y)); - DepthRun* fdepth = (DepthRun*)depthtex.sample_ptr(0, int(y)); - // Loop along advancing Ys, rasterizing spans at each row - float checkY = min(min(l1.y, r1.y), clipRect.y1); - // Ensure we don't rasterize out edge bounds - FloatRange clipSpan = - clipRect.x_range().clip(x_range(l0, l1).merge(x_range(r0, r1))); - for (;;) { - // Check if we maybe passed edge ends or outside clip rect... - if (y > checkY) { - // If we're outside the clip rect, we're done. - if (y > clipRect.y1) break; - // Check if Y advanced past the end of the left edge - if (y > l1.y) { - // Step to next left edge past Y and reset edge interpolants. - STEP_EDGE(y, l0i, l0, l1i, l1, NEXT_POINT, r1i); - (flipped ? right : left) = - Edge(y, l0, l1, interp_outs[l0i], interp_outs[l1i], l1i); - } - // Check if Y advanced past the end of the right edge - if (y > r1.y) { - // Step to next right edge past Y and reset edge interpolants. - STEP_EDGE(y, r0i, r0, r1i, r1, PREV_POINT, l1i); - (flipped ? left : right) = - Edge(y, r0, r1, interp_outs[r0i], interp_outs[r1i], r0i); - } - // Reset the clip bounds for the new edges - clipSpan = - clipRect.x_range().clip(x_range(l0, l1).merge(x_range(r0, r1))); - // Reset check condition for next time around. - checkY = min(ceil(min(l1.y, r1.y) - aaRound), clipRect.y1); - } - - // Calculate a potentially AA'd span and check if it is non-empty. - IntRange span = aa_span(fbuf, left, right, clipSpan); - if (span.len() > 0) { - // If user clip planes are enabled, use them to bound the current span. - if (vertex_shader->use_clip_distance()) { - span = span.intersect(clip_distance_range(left, right)); - if (span.len() <= 0) goto next_span; - } - ctx->shaded_rows++; - ctx->shaded_pixels += span.len(); - // Advance color/depth buffer pointers to the start of the span. - P* buf = fbuf + span.start; - // Check if the we will need to use depth-buffer or discard on this span. - DepthRun* depth = - depthtex.buf != nullptr && depthtex.cleared() ? fdepth : nullptr; - bool use_discard = fragment_shader->use_discard(); - if (depth) { - // Perspective may cause the depth value to vary on a per sample basis. - // Ensure the depth row is flattened to allow testing of individual - // samples - if (!depth->is_flat()) { - flatten_depth_runs(depth, depthtex.width); - } - // Advance to the depth sample at the start of the span. - depth += span.start; - } - if (colortex.delay_clear) { - // Delayed clear is enabled for the color buffer. Check if needs clear. - prepare_row<P>(colortex, int(y), span.start, span.end, use_discard, - depth); - } - // Initialize fragment shader interpolants to current span position. - fragment_shader->gl_FragCoord.x = init_interp(span.start + 0.5f, 1); - fragment_shader->gl_FragCoord.y = y; - { - // Calculate the fragment Z and W change per change in fragment X step. - vec2_scalar stepZW = - (right.zw() - left.zw()) * (1.0f / (right.x() - left.x())); - // Calculate initial Z and W values for span start. - vec2_scalar zw = left.zw() + stepZW * (span.start + 0.5f - left.x()); - // Set fragment shader's Z and W values so that it can use them to - // cancel out the 1/w baked into the interpolants. - fragment_shader->gl_FragCoord.z = init_interp(zw.x, stepZW.x); - fragment_shader->gl_FragCoord.w = init_interp(zw.y, stepZW.y); - fragment_shader->swgl_StepZW = stepZW; - // Change in interpolants is difference between current right and left - // edges per the change in right and left X. The left and right - // interpolant values were previously multipled by 1/w, so the step and - // initial span values take this into account. - Interpolants step = - (right.interp - left.interp) * (1.0f / (right.x() - left.x())); - // Advance current interpolants to X at start of span. - Interpolants o = left.interp + step * (span.start + 0.5f - left.x()); - fragment_shader->init_span<true>(&o, &step); - } - clipRect.set_clip_mask(span.start, y, buf); - if (!use_discard) { - // No discard is used. Common case. - draw_span<false, true>(buf, depth, span.len(), packDepth); - } else { - // Discard is used. Rare. - draw_span<true, true>(buf, depth, span.len(), packDepth); - } - } - next_span: - // Advance Y and edge interpolants to next row. - y++; - left.nextRow(); - right.nextRow(); - // Advance buffers to next row. - fbuf += colortex.stride() / sizeof(P); - fdepth += depthtex.stride() / sizeof(DepthRun); - } -} - -// Clip a primitive against both sides of a view-frustum axis, producing -// intermediate vertexes with interpolated attributes that will no longer -// intersect the selected axis planes. This assumes the primitive is convex -// and should produce at most N+2 vertexes for each invocation (only in the -// worst case where one point falls outside on each of the opposite sides -// with the rest of the points inside). The supplied AA edge mask will be -// modified such that it corresponds to the clipped polygon edges. -template <XYZW AXIS> -static int clip_side(int nump, Point3D* p, Interpolants* interp, Point3D* outP, - Interpolants* outInterp, int& outEdgeMask) { - // Potential mask bits of which side of a plane a coordinate falls on. - enum SIDE { POSITIVE = 1, NEGATIVE = 2 }; - int numClip = 0; - int edgeMask = outEdgeMask; - Point3D prev = p[nump - 1]; - Interpolants prevInterp = interp[nump - 1]; - float prevCoord = prev.select(AXIS); - // Coordinate must satisfy -W <= C <= W. Determine if it is outside, and - // if so, remember which side it is outside of. In the special case that W is - // negative and |C| < |W|, both -W <= C and C <= W will be false, such that - // we must consider the coordinate as falling outside of both plane sides - // simultaneously. We test each condition separately and combine them to form - // a mask of which plane sides we exceeded. If we neglect to consider both - // sides simultaneously, points can erroneously oscillate from one plane side - // to the other and exceed the supported maximum number of clip outputs. - int prevMask = (prevCoord < -prev.w ? NEGATIVE : 0) | - (prevCoord > prev.w ? POSITIVE : 0); - // Loop through points, finding edges that cross the planes by evaluating - // the side at each point. - outEdgeMask = 0; - for (int i = 0; i < nump; i++, edgeMask >>= 1) { - Point3D cur = p[i]; - Interpolants curInterp = interp[i]; - float curCoord = cur.select(AXIS); - int curMask = - (curCoord < -cur.w ? NEGATIVE : 0) | (curCoord > cur.w ? POSITIVE : 0); - // Check if the previous and current end points are on different sides. If - // the masks of sides intersect, then we consider them to be on the same - // side. So in the case the masks do not intersect, we then consider them - // to fall on different sides. - if (!(curMask & prevMask)) { - // One of the edge's end points is outside the plane with the other - // inside the plane. Find the offset where it crosses the plane and - // adjust the point and interpolants to there. - if (prevMask) { - // Edge that was previously outside crosses inside. - // Evaluate plane equation for previous and current end-point - // based on previous side and calculate relative offset. - if (numClip >= nump + 2) { - // If for some reason we produced more vertexes than we support, just - // bail out. - assert(false); - return 0; - } - // The positive plane is assigned the sign 1, and the negative plane is - // assigned -1. If the point falls outside both planes, that means W is - // negative. To compensate for this, we must interpolate the coordinate - // till W=0, at which point we can choose a single plane side for the - // coordinate to fall on since W will no longer be negative. To compute - // the coordinate where W=0, we compute K = prev.w / (prev.w-cur.w) and - // interpolate C = prev.C + K*(cur.C - prev.C). The sign of C will be - // the side of the plane we need to consider. Substituting K into the - // comparison C < 0, we can then avoid the division in K with a - // cross-multiplication. - float prevSide = - (prevMask & NEGATIVE) && (!(prevMask & POSITIVE) || - prevCoord * (cur.w - prev.w) < - prev.w * (curCoord - prevCoord)) - ? -1 - : 1; - float prevDist = prevCoord - prevSide * prev.w; - float curDist = curCoord - prevSide * cur.w; - // It may happen that after we interpolate by the weight k that due to - // floating point rounding we've underestimated the value necessary to - // push it over the clipping boundary. Just in case, nudge the mantissa - // by a single increment so that we essentially round it up and move it - // further inside the clipping boundary. We use nextafter to do this in - // a portable fashion. - float k = prevDist / (prevDist - curDist); - Point3D clipped = prev + (cur - prev) * k; - if (prevSide * clipped.select(AXIS) > clipped.w) { - k = nextafterf(k, 1.0f); - clipped = prev + (cur - prev) * k; - } - outP[numClip] = clipped; - outInterp[numClip] = prevInterp + (curInterp - prevInterp) * k; - // Don't output the current edge mask since start point was outside. - numClip++; - } - if (curMask) { - // Edge that was previously inside crosses outside. - // Evaluate plane equation for previous and current end-point - // based on current side and calculate relative offset. - if (numClip >= nump + 2) { - assert(false); - return 0; - } - // In the case the coordinate falls on both plane sides, the computation - // here is much the same as for prevSide, but since we are going from a - // previous W that is positive to current W that is negative, then the - // sign of cur.w - prev.w will flip in the equation. The resulting sign - // is negated to compensate for this. - float curSide = - (curMask & POSITIVE) && (!(curMask & NEGATIVE) || - prevCoord * (cur.w - prev.w) < - prev.w * (curCoord - prevCoord)) - ? 1 - : -1; - float prevDist = prevCoord - curSide * prev.w; - float curDist = curCoord - curSide * cur.w; - // Calculate interpolation weight k and the nudge it inside clipping - // boundary with nextafter. Note that since we were previously inside - // and now crossing outside, we have to flip the nudge direction for - // the weight towards 0 instead of 1. - float k = prevDist / (prevDist - curDist); - Point3D clipped = prev + (cur - prev) * k; - if (curSide * clipped.select(AXIS) > clipped.w) { - k = nextafterf(k, 0.0f); - clipped = prev + (cur - prev) * k; - } - outP[numClip] = clipped; - outInterp[numClip] = prevInterp + (curInterp - prevInterp) * k; - // Output the current edge mask since the end point is inside. - outEdgeMask |= (edgeMask & 1) << numClip; - numClip++; - } - } - if (!curMask) { - // The current end point is inside the plane, so output point unmodified. - if (numClip >= nump + 2) { - assert(false); - return 0; - } - outP[numClip] = cur; - outInterp[numClip] = curInterp; - // Output the current edge mask since the end point is inside. - outEdgeMask |= (edgeMask & 1) << numClip; - numClip++; - } - prev = cur; - prevInterp = curInterp; - prevCoord = curCoord; - prevMask = curMask; - } - return numClip; -} - -// Helper function to dispatch to perspective span drawing with points that -// have already been transformed and clipped. -static inline void draw_perspective_clipped(int nump, Point3D* p_clip, - Interpolants* interp_clip, - Texture& colortex, - Texture& depthtex) { - // If polygon is ouside clip rect, nothing to draw. - ClipRect clipRect(colortex); - if (!clipRect.overlaps(nump, p_clip)) { - return; - } - - // Finally draw perspective-correct spans for the polygon. - if (colortex.internal_format == GL_RGBA8) { - draw_perspective_spans<uint32_t>(nump, p_clip, interp_clip, colortex, - depthtex, clipRect); - } else if (colortex.internal_format == GL_R8) { - draw_perspective_spans<uint8_t>(nump, p_clip, interp_clip, colortex, - depthtex, clipRect); - } else { - assert(false); - } -} - -// Draws a perspective-correct 3D primitive with varying Z value, as opposed -// to a simple 2D planar primitive with a constant Z value that could be -// trivially Z rejected. This requires clipping the primitive against the near -// and far planes to ensure it stays within the valid Z-buffer range. The Z -// and W of each fragment of the primitives are interpolated across the -// generated spans and then depth-tested as appropriate. -// Additionally, vertex attributes must be interpolated with perspective- -// correction by dividing by W before interpolation, and then later multiplied -// by W again to produce the final correct attribute value for each fragment. -// This process is expensive and should be avoided if possible for primitive -// batches that are known ahead of time to not need perspective-correction. -static void draw_perspective(int nump, Interpolants interp_outs[4], - Texture& colortex, Texture& depthtex) { - // Lines are not supported with perspective. - assert(nump >= 3); - // Convert output of vertex shader to screen space. - vec4 pos = vertex_shader->gl_Position; - vec3_scalar scale = - vec3_scalar(ctx->viewport.width(), ctx->viewport.height(), 1) * 0.5f; - vec3_scalar offset = - make_vec3(make_vec2(ctx->viewport.origin() - colortex.offset), 0.0f) + - scale; - // Verify if point is between near and far planes, rejecting NaN. - if (test_all(pos.z > -pos.w && pos.z < pos.w)) { - // No points cross the near or far planes, so no clipping required. - // Just divide coords by W and convert to viewport. We assume the W - // coordinate is non-zero and the reciprocal is finite since it would - // otherwise fail the test_none condition. - Float w = 1.0f / pos.w; - vec3 screen = pos.sel(X, Y, Z) * w * scale + offset; - Point3D p[4] = {{screen.x.x, screen.y.x, screen.z.x, w.x}, - {screen.x.y, screen.y.y, screen.z.y, w.y}, - {screen.x.z, screen.y.z, screen.z.z, w.z}, - {screen.x.w, screen.y.w, screen.z.w, w.w}}; - draw_perspective_clipped(nump, p, interp_outs, colortex, depthtex); - } else { - // Points cross the near or far planes, so we need to clip. - // Start with the original 3 or 4 points... - Point3D p[4] = {{pos.x.x, pos.y.x, pos.z.x, pos.w.x}, - {pos.x.y, pos.y.y, pos.z.y, pos.w.y}, - {pos.x.z, pos.y.z, pos.z.z, pos.w.z}, - {pos.x.w, pos.y.w, pos.z.w, pos.w.w}}; - // Clipping can expand the points by 1 for each of 6 view frustum planes. - Point3D p_clip[4 + 6]; - Interpolants interp_clip[4 + 6]; - // Clip against near and far Z planes. - nump = clip_side<Z>(nump, p, interp_outs, p_clip, interp_clip, - swgl_AAEdgeMask); - // If no points are left inside the view frustum, there's nothing to draw. - if (nump < 3) { - return; - } - // After clipping against only the near and far planes, we might still - // produce points where W = 0, exactly at the camera plane. OpenGL specifies - // that for clip coordinates, points must satisfy: - // -W <= X <= W - // -W <= Y <= W - // -W <= Z <= W - // When Z = W = 0, this is trivially satisfied, but when we transform and - // divide by W below it will produce a divide by 0. Usually we want to only - // clip Z to avoid the extra work of clipping X and Y. We can still project - // points that fall outside the view frustum X and Y so long as Z is valid. - // The span drawing code will then ensure X and Y are clamped to viewport - // boundaries. However, in the Z = W = 0 case, sometimes clipping X and Y, - // will push W further inside the view frustum so that it is no longer 0, - // allowing us to finally proceed to projecting the points to the screen. - for (int i = 0; i < nump; i++) { - // Found an invalid W, so need to clip against X and Y... - if (p_clip[i].w <= 0.0f) { - // Ping-pong p_clip -> p_tmp -> p_clip. - Point3D p_tmp[4 + 6]; - Interpolants interp_tmp[4 + 6]; - nump = clip_side<X>(nump, p_clip, interp_clip, p_tmp, interp_tmp, - swgl_AAEdgeMask); - if (nump < 3) return; - nump = clip_side<Y>(nump, p_tmp, interp_tmp, p_clip, interp_clip, - swgl_AAEdgeMask); - if (nump < 3) return; - // After clipping against X and Y planes, there's still points left - // to draw, so proceed to trying projection now... - break; - } - } - // Divide coords by W and convert to viewport. - for (int i = 0; i < nump; i++) { - float w = 1.0f / p_clip[i].w; - // If the W coord is essentially zero, small enough that division would - // result in Inf/NaN, then just set the reciprocal itself to zero so that - // the coordinates becomes zeroed out, as the only valid point that - // satisfies -W <= X/Y/Z <= W is all zeroes. - if (!isfinite(w)) w = 0.0f; - p_clip[i] = Point3D(p_clip[i].sel(X, Y, Z) * w * scale + offset, w); - } - draw_perspective_clipped(nump, p_clip, interp_clip, colortex, depthtex); - } -} - -static void draw_quad(int nump, Texture& colortex, Texture& depthtex) { - // Run vertex shader once for the primitive's vertices. - // Reserve space for 6 sets of interpolants, in case we need to clip against - // near and far planes in the perspective case. - Interpolants interp_outs[4]; - swgl_ClipFlags = 0; - vertex_shader->run_primitive((char*)interp_outs, sizeof(Interpolants)); - vec4 pos = vertex_shader->gl_Position; - // Check if any vertex W is different from another. If so, use perspective. - if (test_any(pos.w != pos.w.x)) { - draw_perspective(nump, interp_outs, colortex, depthtex); - return; - } - - // Convert output of vertex shader to screen space. - // Divide coords by W and convert to viewport. - float w = 1.0f / pos.w.x; - // If the W coord is essentially zero, small enough that division would - // result in Inf/NaN, then just set the reciprocal itself to zero so that - // the coordinates becomes zeroed out, as the only valid point that - // satisfies -W <= X/Y/Z <= W is all zeroes. - if (!isfinite(w)) w = 0.0f; - vec2 screen = (pos.sel(X, Y) * w + 1) * 0.5f * - vec2_scalar(ctx->viewport.width(), ctx->viewport.height()) + - make_vec2(ctx->viewport.origin() - colortex.offset); - Point2D 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}}; - - // If quad is ouside clip rect, nothing to draw. - ClipRect clipRect(colortex); - if (!clipRect.overlaps(nump, p)) { - return; - } - - // Since the quad is assumed 2D, Z is constant across the quad. - float screenZ = (pos.z.x * w + 1) * 0.5f; - if (screenZ < 0 || screenZ > 1) { - // Z values would cross the near or far plane, so just bail. - return; - } - // Since Z doesn't need to be interpolated, just set the fragment shader's - // Z and W values here, once and for all fragment shader invocations. - uint32_t z = uint32_t(MAX_DEPTH_VALUE * screenZ); - fragment_shader->gl_FragCoord.z = screenZ; - fragment_shader->gl_FragCoord.w = w; - - // If supplied a line, adjust it so that it is a quad at least 1 pixel thick. - // Assume that for a line that all 4 SIMD lanes were actually filled with - // vertexes 0, 1, 1, 0. - if (nump == 2) { - // Nudge Y height to span at least 1 pixel by advancing to next pixel - // boundary so that we step at least 1 row when drawing spans. - if (int(p[0].y + 0.5f) == int(p[1].y + 0.5f)) { - p[2].y = 1 + int(p[1].y + 0.5f); - p[3].y = p[2].y; - // Nudge X width to span at least 1 pixel so that rounded coords fall on - // separate pixels. - if (int(p[0].x + 0.5f) == int(p[1].x + 0.5f)) { - p[1].x += 1.0f; - p[2].x += 1.0f; - } - } else { - // If the line already spans at least 1 row, then assume line is vertical - // or diagonal and just needs to be dilated horizontally. - p[2].x += 1.0f; - p[3].x += 1.0f; - } - // Pretend that it's a quad now... - nump = 4; - } - - // Finally draw 2D spans for the quad. Currently only supports drawing to - // RGBA8 and R8 color buffers. - if (colortex.internal_format == GL_RGBA8) { - draw_quad_spans<uint32_t>(nump, p, z, interp_outs, colortex, depthtex, - clipRect); - } else if (colortex.internal_format == GL_R8) { - draw_quad_spans<uint8_t>(nump, p, z, interp_outs, colortex, depthtex, - clipRect); - } else { - assert(false); - } -} - -template <typename INDEX> -static inline void draw_elements(GLsizei count, GLsizei instancecount, - size_t offset, VertexArray& v, - Texture& colortex, Texture& depthtex) { - Buffer& indices_buf = ctx->buffers[v.element_array_buffer_binding]; - if (!indices_buf.buf || offset >= indices_buf.size) { - return; - } - assert((offset & (sizeof(INDEX) - 1)) == 0); - INDEX* indices = (INDEX*)(indices_buf.buf + offset); - count = min(count, (GLsizei)((indices_buf.size - offset) / sizeof(INDEX))); - // Triangles must be indexed at offsets 0, 1, 2. - // Quads must be successive triangles indexed at offsets 0, 1, 2, 2, 1, 3. - if (count == 6 && indices[1] == indices[0] + 1 && - indices[2] == indices[0] + 2 && indices[5] == indices[0] + 3) { - assert(indices[3] == indices[0] + 2 && indices[4] == indices[0] + 1); - // Fast path - since there is only a single quad, we only load per-vertex - // attribs once for all instances, as they won't change across instances - // or within an instance. - vertex_shader->load_attribs(v.attribs, indices[0], 0, 4); - draw_quad(4, colortex, depthtex); - for (GLsizei instance = 1; instance < instancecount; instance++) { - vertex_shader->load_attribs(v.attribs, indices[0], instance, 0); - draw_quad(4, colortex, depthtex); - } - } else { - for (GLsizei instance = 0; instance < instancecount; instance++) { - for (GLsizei i = 0; i + 3 <= count; i += 3) { - if (indices[i + 1] != indices[i] + 1 || - indices[i + 2] != indices[i] + 2) { - continue; - } - if (i + 6 <= count && indices[i + 5] == indices[i] + 3) { - assert(indices[i + 3] == indices[i] + 2 && - indices[i + 4] == indices[i] + 1); - vertex_shader->load_attribs(v.attribs, indices[i], instance, 4); - draw_quad(4, colortex, depthtex); - i += 3; - } else { - vertex_shader->load_attribs(v.attribs, indices[i], instance, 3); - draw_quad(3, colortex, depthtex); - } - } - } - } -} diff --git a/third_party/webrender/swgl/src/swgl_ext.h b/third_party/webrender/swgl/src/swgl_ext.h deleted file mode 100644 index 52d240e0818..00000000000 --- a/third_party/webrender/swgl/src/swgl_ext.h +++ /dev/null @@ -1,1826 +0,0 @@ -/* 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/. */ - -// When using a solid color with clip masking, the cost of loading the clip mask -// in the blend stage exceeds the cost of processing the color. Here we handle -// the entire span of clip mask texture before the blend stage to more -// efficiently process it and modulate it with color without incurring blend -// stage overheads. -template <typename P, typename C> -static void commit_masked_solid_span(P* buf, C color, int len) { - override_clip_mask(); - uint8_t* mask = get_clip_mask(buf); - for (P* end = &buf[len]; buf < end; buf += 4, mask += 4) { - commit_span( - buf, - blend_span( - buf, - applyColor(expand_mask(buf, unpack(unaligned_load<PackedR8>(mask))), - color))); - } - restore_clip_mask(); -} - -// When using a solid color with anti-aliasing, most of the solid span will not -// benefit from anti-aliasing in the opaque region. We only want to apply the AA -// blend stage in the non-opaque start and end of the span where AA is needed. -template <typename P, typename R> -static ALWAYS_INLINE void commit_aa_solid_span(P* buf, R r, int len) { - if (int start = min((get_aa_opaque_start(buf) + 3) & ~3, len)) { - commit_solid_span<true>(buf, r, start); - buf += start; - len -= start; - } - if (int opaque = min((get_aa_opaque_size(buf) + 3) & ~3, len)) { - override_aa(); - commit_solid_span<true>(buf, r, opaque); - restore_aa(); - buf += opaque; - len -= opaque; - } - if (len > 0) { - commit_solid_span<true>(buf, r, len); - } -} - -// Forces a value with vector run-class to have scalar run-class. -template <typename T> -static ALWAYS_INLINE auto swgl_forceScalar(T v) -> decltype(force_scalar(v)) { - return force_scalar(v); -} - -// Advance all varying inperpolants by a single chunk -#define swgl_stepInterp() step_interp_inputs() - -// Pseudo-intrinsic that accesses the interpolation step for a given varying -#define swgl_interpStep(v) (interp_step.v) - -// Commit an entire span of a solid color. This dispatches to clip-masked and -// anti-aliased fast-paths as appropriate. -#define swgl_commitSolid(format, v, n) \ - do { \ - int len = (n); \ - if (blend_key) { \ - if (swgl_ClipFlags & SWGL_CLIP_FLAG_MASK) { \ - commit_masked_solid_span(swgl_Out##format, \ - packColor(swgl_Out##format, (v)), len); \ - } else if (swgl_ClipFlags & SWGL_CLIP_FLAG_AA) { \ - commit_aa_solid_span(swgl_Out##format, \ - pack_span(swgl_Out##format, (v)), len); \ - } else { \ - commit_solid_span<true>(swgl_Out##format, \ - pack_span(swgl_Out##format, (v)), len); \ - } \ - } else { \ - commit_solid_span<false>(swgl_Out##format, \ - pack_span(swgl_Out##format, (v)), len); \ - } \ - swgl_Out##format += len; \ - swgl_SpanLength -= len; \ - } while (0) -#define swgl_commitSolidRGBA8(v) swgl_commitSolid(RGBA8, v, swgl_SpanLength) -#define swgl_commitSolidR8(v) swgl_commitSolid(R8, v, swgl_SpanLength) -#define swgl_commitPartialSolidRGBA8(len, v) \ - swgl_commitSolid(RGBA8, v, min(int(len), swgl_SpanLength)) -#define swgl_commitPartialSolidR8(len, v) \ - swgl_commitSolid(R8, v, min(int(len), swgl_SpanLength)) - -#define swgl_commitChunk(format, chunk) \ - do { \ - auto r = chunk; \ - if (blend_key) r = blend_span(swgl_Out##format, r); \ - commit_span(swgl_Out##format, r); \ - swgl_Out##format += swgl_StepSize; \ - swgl_SpanLength -= swgl_StepSize; \ - } while (0) - -// Commit a single chunk of a color -#define swgl_commitColor(format, color) \ - swgl_commitChunk(format, pack_pixels_##format(color)) -#define swgl_commitColorRGBA8(color) swgl_commitColor(RGBA8, color) -#define swgl_commitColorR8(color) swgl_commitColor(R8, color) - -template <typename S> -static ALWAYS_INLINE bool swgl_isTextureLinear(S s) { - return s->filter == TextureFilter::LINEAR; -} - -template <typename S> -static ALWAYS_INLINE bool swgl_isTextureRGBA8(S s) { - return s->format == TextureFormat::RGBA8; -} - -template <typename S> -static ALWAYS_INLINE bool swgl_isTextureR8(S s) { - return s->format == TextureFormat::R8; -} - -// Use the default linear quantization scale of 128. This gives 7 bits of -// fractional precision, which when multiplied with a signed 9 bit value -// still fits in a 16 bit integer. -const int swgl_LinearQuantizeScale = 128; - -// Quantizes UVs for access into a linear texture. -template <typename S, typename T> -static ALWAYS_INLINE T swgl_linearQuantize(S s, T p) { - return linearQuantize(p, swgl_LinearQuantizeScale, s); -} - -// Quantizes an interpolation step for UVs for access into a linear texture. -template <typename S, typename T> -static ALWAYS_INLINE T swgl_linearQuantizeStep(S s, T p) { - return samplerScale(s, p) * swgl_LinearQuantizeScale; -} - -template <typename S> -static ALWAYS_INLINE WideRGBA8 textureLinearUnpacked(UNUSED uint32_t* buf, - S sampler, ivec2 i) { - return textureLinearUnpackedRGBA8(sampler, i); -} - -template <typename S> -static ALWAYS_INLINE WideR8 textureLinearUnpacked(UNUSED uint8_t* buf, - S sampler, ivec2 i) { - return textureLinearUnpackedR8(sampler, i); -} - -template <typename S> -static ALWAYS_INLINE bool matchTextureFormat(S s, UNUSED uint32_t* buf) { - return swgl_isTextureRGBA8(s); -} - -template <typename S> -static ALWAYS_INLINE bool matchTextureFormat(S s, UNUSED uint8_t* buf) { - return swgl_isTextureR8(s); -} - -// Quantizes the UVs to the 2^7 scale needed for calculating fractional offsets -// for linear sampling. -#define LINEAR_QUANTIZE_UV(sampler, uv, uv_step, uv_rect, min_uv, max_uv) \ - uv = swgl_linearQuantize(sampler, uv); \ - vec2_scalar uv_step = \ - float(swgl_StepSize) * vec2_scalar{uv.x.y - uv.x.x, uv.y.y - uv.y.x}; \ - vec2_scalar min_uv = max( \ - swgl_linearQuantize(sampler, vec2_scalar{uv_rect.x, uv_rect.y}), 0.0f); \ - vec2_scalar max_uv = \ - max(swgl_linearQuantize(sampler, vec2_scalar{uv_rect.z, uv_rect.w}), \ - min_uv); - -// Implements the fallback linear filter that can deal with clamping and -// arbitrary scales. -template <bool BLEND, typename S, typename C, typename P> -static P* blendTextureLinearFallback(S sampler, vec2 uv, int span, - vec2_scalar uv_step, vec2_scalar min_uv, - vec2_scalar max_uv, C color, P* buf) { - for (P* end = buf + span; buf < end; buf += swgl_StepSize, uv += uv_step) { - commit_blend_span<BLEND>( - buf, applyColor(textureLinearUnpacked(buf, sampler, - ivec2(clamp(uv, min_uv, max_uv))), - color)); - } - return buf; -} - -static ALWAYS_INLINE U64 castForShuffle(V16<int16_t> r) { - return bit_cast<U64>(r); -} -static ALWAYS_INLINE U16 castForShuffle(V4<int16_t> r) { - return bit_cast<U16>(r); -} - -static ALWAYS_INLINE V16<int16_t> applyFracX(V16<int16_t> r, I16 fracx) { - return r * fracx.xxxxyyyyzzzzwwww; -} -static ALWAYS_INLINE V4<int16_t> applyFracX(V4<int16_t> r, I16 fracx) { - return r * fracx; -} - -// Implements a faster linear filter that works with axis-aligned constant Y but -// scales less than 1, i.e. upscaling. In this case we can optimize for the -// constant Y fraction as well as load all chunks from memory in a single tap -// for each row. -template <bool BLEND, typename S, typename C, typename P> -static void blendTextureLinearUpscale(S sampler, vec2 uv, int span, - vec2_scalar uv_step, vec2_scalar min_uv, - vec2_scalar max_uv, C color, P* buf) { - typedef VectorType<uint8_t, 4 * sizeof(P)> packed_type; - typedef VectorType<uint16_t, 4 * sizeof(P)> unpacked_type; - typedef VectorType<int16_t, 4 * sizeof(P)> signed_unpacked_type; - - ivec2 i(clamp(uv, min_uv, max_uv)); - ivec2 frac = i; - i >>= 7; - P* row0 = (P*)sampler->buf + computeRow(sampler, ivec2_scalar(0, i.y.x)); - P* row1 = row0 + computeNextRowOffset(sampler, ivec2_scalar(0, i.y.x)); - I16 fracx = computeFracX(sampler, i, frac); - int16_t fracy = computeFracY(frac).x; - auto src0 = - CONVERT(unaligned_load<packed_type>(&row0[i.x.x]), signed_unpacked_type); - auto src1 = - CONVERT(unaligned_load<packed_type>(&row1[i.x.x]), signed_unpacked_type); - auto src = castForShuffle(src0 + (((src1 - src0) * fracy) >> 7)); - - // We attempt to sample ahead by one chunk and interpolate it with the current - // one. However, due to the complication of upscaling, we may not necessarily - // shift in all the next set of samples. - for (P* end = buf + span; buf < end; buf += 4) { - uv.x += uv_step.x; - I32 ixn = cast(uv.x); - I16 fracn = computeFracNoClamp(ixn); - ixn >>= 7; - auto src0n = CONVERT(unaligned_load<packed_type>(&row0[ixn.x]), - signed_unpacked_type); - auto src1n = CONVERT(unaligned_load<packed_type>(&row1[ixn.x]), - signed_unpacked_type); - auto srcn = castForShuffle(src0n + (((src1n - src0n) * fracy) >> 7)); - - // Since we're upscaling, we know that a source pixel has a larger footprint - // than the destination pixel, and thus all the source pixels needed for - // this chunk will fall within a single chunk of texture data. However, - // since the source pixels don't map 1:1 with destination pixels, we need to - // shift the source pixels over based on their offset from the start of the - // chunk. This could conceivably be optimized better with usage of PSHUFB or - // VTBL instructions However, since PSHUFB requires SSSE3, instead we resort - // to masking in the correct pixels to avoid having to index into memory. - // For the last sample to interpolate with, we need to potentially shift in - // a sample from the next chunk over in the case the samples fill out an - // entire chunk. - auto shuf = src; - auto shufn = SHUFFLE(src, ixn.x == i.x.w ? srcn.yyyy : srcn, 1, 2, 3, 4); - if (i.x.y == i.x.x) { - shuf = shuf.xxyz; - shufn = shufn.xxyz; - } - if (i.x.z == i.x.y) { - shuf = shuf.xyyz; - shufn = shufn.xyyz; - } - if (i.x.w == i.x.z) { - shuf = shuf.xyzz; - shufn = shufn.xyzz; - } - - // Convert back to a signed unpacked type so that we can interpolate the - // final result. - auto interp = bit_cast<signed_unpacked_type>(shuf); - auto interpn = bit_cast<signed_unpacked_type>(shufn); - interp += applyFracX(interpn - interp, fracx) >> 7; - - commit_blend_span<BLEND>( - buf, applyColor(bit_cast<unpacked_type>(interp), color)); - - i.x = ixn; - fracx = fracn; - src = srcn; - } -} - -// This is the fastest variant of the linear filter that still provides -// filtering. In cases where there is no scaling required, but we have a -// subpixel offset that forces us to blend in neighboring pixels, we can -// optimize away most of the memory loads and shuffling that is required by the -// fallback filter. -template <bool BLEND, typename S, typename C, typename P> -static void blendTextureLinearFast(S sampler, vec2 uv, int span, - vec2_scalar min_uv, vec2_scalar max_uv, - C color, P* buf) { - typedef VectorType<uint8_t, 4 * sizeof(P)> packed_type; - typedef VectorType<uint16_t, 4 * sizeof(P)> unpacked_type; - typedef VectorType<int16_t, 4 * sizeof(P)> signed_unpacked_type; - - ivec2 i(clamp(uv, min_uv, max_uv)); - ivec2 frac = i; - i >>= 7; - P* row0 = (P*)sampler->buf + computeRow(sampler, force_scalar(i)); - P* row1 = row0 + computeNextRowOffset(sampler, force_scalar(i)); - int16_t fracx = computeFracX(sampler, i, frac).x; - int16_t fracy = computeFracY(frac).x; - auto src0 = CONVERT(unaligned_load<packed_type>(row0), signed_unpacked_type); - auto src1 = CONVERT(unaligned_load<packed_type>(row1), signed_unpacked_type); - auto src = castForShuffle(src0 + (((src1 - src0) * fracy) >> 7)); - - // Since there is no scaling, we sample ahead by one chunk and interpolate it - // with the current one. We can then reuse this value on the next iteration. - for (P* end = buf + span; buf < end; buf += 4) { - row0 += 4; - row1 += 4; - auto src0n = - CONVERT(unaligned_load<packed_type>(row0), signed_unpacked_type); - auto src1n = - CONVERT(unaligned_load<packed_type>(row1), signed_unpacked_type); - auto srcn = castForShuffle(src0n + (((src1n - src0n) * fracy) >> 7)); - - // For the last sample to interpolate with, we need to potentially shift in - // a sample from the next chunk over since the samples fill out an entire - // chunk. - auto interp = bit_cast<signed_unpacked_type>(src); - auto interpn = - bit_cast<signed_unpacked_type>(SHUFFLE(src, srcn, 1, 2, 3, 4)); - interp += ((interpn - interp) * fracx) >> 7; - - commit_blend_span<BLEND>( - buf, applyColor(bit_cast<unpacked_type>(interp), color)); - - src = srcn; - } -} - -// Implements a faster linear filter that works with axis-aligned constant Y but -// downscaling the texture by half. In this case we can optimize for the -// constant X/Y fractions and reduction factor while minimizing shuffling. -template <bool BLEND, typename S, typename C, typename P> -static NO_INLINE void blendTextureLinearDownscale(S sampler, vec2 uv, int span, - vec2_scalar min_uv, - vec2_scalar max_uv, C color, - P* buf) { - typedef VectorType<uint8_t, 4 * sizeof(P)> packed_type; - typedef VectorType<uint16_t, 4 * sizeof(P)> unpacked_type; - typedef VectorType<int16_t, 4 * sizeof(P)> signed_unpacked_type; - - ivec2 i(clamp(uv, min_uv, max_uv)); - ivec2 frac = i; - i >>= 7; - P* row0 = (P*)sampler->buf + computeRow(sampler, force_scalar(i)); - P* row1 = row0 + computeNextRowOffset(sampler, force_scalar(i)); - int16_t fracx = computeFracX(sampler, i, frac).x; - int16_t fracy = computeFracY(frac).x; - - for (P* end = buf + span; buf < end; buf += 4) { - auto src0 = - CONVERT(unaligned_load<packed_type>(row0), signed_unpacked_type); - auto src1 = - CONVERT(unaligned_load<packed_type>(row1), signed_unpacked_type); - auto src = castForShuffle(src0 + (((src1 - src0) * fracy) >> 7)); - row0 += 4; - row1 += 4; - auto src0n = - CONVERT(unaligned_load<packed_type>(row0), signed_unpacked_type); - auto src1n = - CONVERT(unaligned_load<packed_type>(row1), signed_unpacked_type); - auto srcn = castForShuffle(src0n + (((src1n - src0n) * fracy) >> 7)); - row0 += 4; - row1 += 4; - - auto interp = - bit_cast<signed_unpacked_type>(SHUFFLE(src, srcn, 0, 2, 4, 6)); - auto interpn = - bit_cast<signed_unpacked_type>(SHUFFLE(src, srcn, 1, 3, 5, 7)); - interp += ((interpn - interp) * fracx) >> 7; - - commit_blend_span<BLEND>( - buf, applyColor(bit_cast<unpacked_type>(interp), color)); - } -} - -enum LinearFilter { - // No linear filter is needed. - LINEAR_FILTER_NEAREST = 0, - // The most general linear filter that handles clamping and varying scales. - LINEAR_FILTER_FALLBACK, - // A linear filter optimized for axis-aligned upscaling. - LINEAR_FILTER_UPSCALE, - // A linear filter with no scaling but with subpixel offset. - LINEAR_FILTER_FAST, - // A linear filter optimized for 2x axis-aligned downscaling. - LINEAR_FILTER_DOWNSCALE -}; - -// Dispatches to an appropriate linear filter depending on the selected filter. -template <bool BLEND, typename S, typename C, typename P> -static P* blendTextureLinearDispatch(S sampler, vec2 uv, int span, - vec2_scalar uv_step, vec2_scalar min_uv, - vec2_scalar max_uv, C color, P* buf, - LinearFilter filter) { - P* end = buf + span; - if (filter != LINEAR_FILTER_FALLBACK) { - // If we're not using the fallback, then Y is constant across the entire - // row. We just need to ensure that we handle any samples that might pull - // data from before the start of the row and require clamping. - float beforeDist = max(0.0f, min_uv.x) - uv.x.x; - if (beforeDist > 0) { - int before = clamp(int(ceil(beforeDist / uv_step.x)) * swgl_StepSize, 0, - int(end - buf)); - buf = blendTextureLinearFallback<BLEND>(sampler, uv, before, uv_step, - min_uv, max_uv, color, buf); - uv.x += (before / swgl_StepSize) * uv_step.x; - } - // We need to check how many samples we can take from inside the row without - // requiring clamping. In case the filter oversamples the row by a step, we - // subtract off a step from the width to leave some room. - float insideDist = - min(max_uv.x, float((int(sampler->width) - swgl_StepSize) * - swgl_LinearQuantizeScale)) - - uv.x.x; - if (uv_step.x > 0.0f && insideDist >= uv_step.x) { - int inside = int(end - buf); - if (filter == LINEAR_FILTER_DOWNSCALE) { - inside = clamp(int(insideDist * (0.5f / swgl_LinearQuantizeScale)) & - ~(swgl_StepSize - 1), - 0, inside); - blendTextureLinearDownscale<BLEND>(sampler, uv, inside, min_uv, max_uv, - color, buf); - } else if (filter == LINEAR_FILTER_UPSCALE) { - inside = clamp(int(insideDist / uv_step.x) * swgl_StepSize, 0, inside); - blendTextureLinearUpscale<BLEND>(sampler, uv, inside, uv_step, min_uv, - max_uv, color, buf); - } else { - inside = clamp(int(insideDist * (1.0f / swgl_LinearQuantizeScale)) & - ~(swgl_StepSize - 1), - 0, inside); - blendTextureLinearFast<BLEND>(sampler, uv, inside, min_uv, max_uv, - color, buf); - } - buf += inside; - uv.x += (inside / swgl_StepSize) * uv_step.x; - } - } - // If the fallback filter was requested, or if there are any samples left that - // may be outside the row and require clamping, then handle that with here. - if (buf < end) { - buf = blendTextureLinearFallback<BLEND>( - sampler, uv, int(end - buf), uv_step, min_uv, max_uv, color, buf); - } - return buf; -} - -// Helper function to quantize UVs for linear filtering before dispatch -template <bool BLEND, typename S, typename C, typename P> -static inline int blendTextureLinear(S sampler, vec2 uv, int span, - const vec4_scalar& uv_rect, C color, - P* buf, LinearFilter filter) { - if (!matchTextureFormat(sampler, buf)) { - return 0; - } - LINEAR_QUANTIZE_UV(sampler, uv, uv_step, uv_rect, min_uv, max_uv); - blendTextureLinearDispatch<BLEND>(sampler, uv, span, uv_step, min_uv, max_uv, - color, buf, filter); - return span; -} - -// Samples an axis-aligned span of on a single row of a texture using 1:1 -// nearest filtering. Sampling is constrained to only fall within the given UV -// bounds. This requires a pointer to the destination buffer. An optional color -// modulus can be supplied. -template <bool BLEND, typename S, typename C, typename P> -static int blendTextureNearestFast(S sampler, vec2 uv, int span, - const vec4_scalar& uv_rect, C color, - P* buf) { - if (!matchTextureFormat(sampler, buf)) { - return 0; - } - - typedef VectorType<uint8_t, 4 * sizeof(P)> packed_type; - - ivec2_scalar i = make_ivec2(samplerScale(sampler, force_scalar(uv))); - ivec2_scalar minUV = - make_ivec2(samplerScale(sampler, vec2_scalar{uv_rect.x, uv_rect.y})); - ivec2_scalar maxUV = - make_ivec2(samplerScale(sampler, vec2_scalar{uv_rect.z, uv_rect.w})); - - // Calculate the row pointer within the buffer, clamping to within valid row - // bounds. - P* row = - &((P*)sampler - ->buf)[clamp(clampCoord(i.y, sampler->height), minUV.y, maxUV.y) * - sampler->stride]; - // Find clamped X bounds within the row. - int minX = clamp(minUV.x, 0, sampler->width - 1); - int maxX = clamp(maxUV.x, minX, sampler->width - 1); - int curX = i.x; - int endX = i.x + span; - // If we need to start sampling below the valid sample bounds, then we need to - // fill this section with a constant clamped sample. - if (curX < minX) { - int n = min(minX, endX) - curX; - auto src = - applyColor(unpack(bit_cast<packed_type>(V4<P>(row[minX]))), color); - commit_solid_span<BLEND>(buf, src, n); - buf += n; - curX += n; - } - // Here we only deal with valid samples within the sample bounds. No clamping - // should occur here within these inner loops. - int n = max(min(maxX + 1, endX) - curX, 0); - // Try to process as many chunks as possible with full loads and stores. - for (int end = curX + (n & ~3); curX < end; curX += 4, buf += 4) { - auto src = applyColor(unaligned_load<packed_type>(&row[curX]), color); - commit_blend_span<BLEND>(buf, src); - } - n &= 3; - // If we have any leftover samples after processing chunks, use partial loads - // and stores. - if (n > 0) { - auto src = applyColor(partial_load_span<packed_type>(&row[curX], n), color); - commit_blend_span<BLEND>(buf, src, n); - buf += n; - curX += n; - } - // If we still have samples left above the valid sample bounds, then we again - // need to fill this section with a constant clamped sample. - if (curX < endX) { - auto src = - applyColor(unpack(bit_cast<packed_type>(V4<P>(row[maxX]))), color); - commit_solid_span<BLEND>(buf, src, endX - curX); - } - return span; -} - -// We need to verify that the pixel step reasonably approximates stepping by a -// single texel for every pixel we need to reproduce. Try to ensure that the -// margin of error is no more than approximately 2^-7. Also, we check here if -// the scaling can be quantized for acceleration. -template <typename T> -static ALWAYS_INLINE int spanNeedsScale(int span, T P) { - span &= ~(128 - 1); - span += 128; - int scaled = round((P.x.y - P.x.x) * span); - return scaled != span ? (scaled == span * 2 ? 2 : 1) : 0; -} - -// Helper function to decide whether we can safely apply 1:1 nearest filtering -// without diverging too much from the linear filter. -template <typename S, typename T> -static inline LinearFilter needsTextureLinear(S sampler, T P, int span) { - // First verify if the row Y doesn't change across samples - if (P.y.x != P.y.y) { - return LINEAR_FILTER_FALLBACK; - } - P = samplerScale(sampler, P); - if (int scale = spanNeedsScale(span, P)) { - // If the source region is not flipped and smaller than the destination, - // then we can use the upscaling filter since row Y is constant. - return P.x.x < P.x.y && P.x.y - P.x.x <= 1 - ? LINEAR_FILTER_UPSCALE - : (scale == 2 ? LINEAR_FILTER_DOWNSCALE - : LINEAR_FILTER_FALLBACK); - } - // Also verify that we're reasonably close to the center of a texel - // so that it doesn't look that much different than if a linear filter - // was used. - if ((int(P.x.x * 4.0f + 0.5f) & 3) != 2 || - (int(P.y.x * 4.0f + 0.5f) & 3) != 2) { - // The source and destination regions are the same, but there is a - // significant subpixel offset. We can use a faster linear filter to deal - // with the offset in this case. - return LINEAR_FILTER_FAST; - } - // Otherwise, we have a constant 1:1 step and we're stepping reasonably close - // to the center of each pixel, so it's safe to disable the linear filter and - // use nearest. - return LINEAR_FILTER_NEAREST; -} - -// Commit an entire span with linear filtering -#define swgl_commitTextureLinear(format, s, p, uv_rect, color, n) \ - do { \ - auto packed_color = packColor(swgl_Out##format, color); \ - int len = (n); \ - int drawn = 0; \ - if (LinearFilter filter = needsTextureLinear(s, p, len)) { \ - if (blend_key) { \ - drawn = blendTextureLinear<true>(s, p, len, uv_rect, packed_color, \ - swgl_Out##format, filter); \ - } else { \ - drawn = blendTextureLinear<false>(s, p, len, uv_rect, packed_color, \ - swgl_Out##format, filter); \ - } \ - } else if (blend_key) { \ - drawn = blendTextureNearestFast<true>(s, p, len, uv_rect, packed_color, \ - swgl_Out##format); \ - } else { \ - drawn = blendTextureNearestFast<false>(s, p, len, uv_rect, packed_color, \ - swgl_Out##format); \ - } \ - swgl_Out##format += drawn; \ - swgl_SpanLength -= drawn; \ - } while (0) -#define swgl_commitTextureLinearRGBA8(s, p, uv_rect) \ - swgl_commitTextureLinear(RGBA8, s, p, uv_rect, NoColor(), swgl_SpanLength) -#define swgl_commitTextureLinearR8(s, p, uv_rect) \ - swgl_commitTextureLinear(R8, s, p, uv_rect, NoColor(), swgl_SpanLength) - -// Commit a partial span with linear filtering, optionally inverting the color -#define swgl_commitPartialTextureLinearR8(len, s, p, uv_rect) \ - swgl_commitTextureLinear(R8, s, p, uv_rect, NoColor(), \ - min(int(len), swgl_SpanLength)) -#define swgl_commitPartialTextureLinearInvertR8(len, s, p, uv_rect) \ - swgl_commitTextureLinear(R8, s, p, uv_rect, InvertColor(), \ - min(int(len), swgl_SpanLength)) - -// Commit an entire span with linear filtering that is scaled by a color -#define swgl_commitTextureLinearColorRGBA8(s, p, uv_rect, color) \ - swgl_commitTextureLinear(RGBA8, s, p, uv_rect, color, swgl_SpanLength) -#define swgl_commitTextureLinearColorR8(s, p, uv_rect, color) \ - swgl_commitTextureLinear(R8, s, p, uv_rect, color, swgl_SpanLength) - -// Helper function that samples from an R8 texture while expanding it to support -// a differing framebuffer format. -template <bool BLEND, typename S, typename C, typename P> -static inline int blendTextureLinearR8(S sampler, vec2 uv, int span, - const vec4_scalar& uv_rect, C color, - P* buf) { - if (!swgl_isTextureR8(sampler)) { - return 0; - } - LINEAR_QUANTIZE_UV(sampler, uv, uv_step, uv_rect, min_uv, max_uv); - for (P* end = buf + span; buf < end; buf += swgl_StepSize, uv += uv_step) { - commit_blend_span<BLEND>( - buf, applyColor(expand_mask(buf, textureLinearUnpackedR8( - sampler, - ivec2(clamp(uv, min_uv, max_uv)))), - color)); - } - return span; -} - -// Commit an entire span with linear filtering while expanding from R8 to RGBA8 -#define swgl_commitTextureLinearColorR8ToRGBA8(s, p, uv_rect, color) \ - do { \ - auto packed_color = packColor(swgl_OutRGBA8, color); \ - int drawn = 0; \ - if (blend_key) { \ - drawn = blendTextureLinearR8<true>(s, p, swgl_SpanLength, uv_rect, \ - packed_color, swgl_OutRGBA8); \ - } else { \ - drawn = blendTextureLinearR8<false>(s, p, swgl_SpanLength, uv_rect, \ - packed_color, swgl_OutRGBA8); \ - } \ - swgl_OutRGBA8 += drawn; \ - swgl_SpanLength -= drawn; \ - } while (0) -#define swgl_commitTextureLinearR8ToRGBA8(s, p, uv_rect) \ - swgl_commitTextureLinearColorR8ToRGBA8(s, p, uv_rect, NoColor()) - -// Compute repeating UVs, possibly constrained by tile repeat limits -static inline vec2 tileRepeatUV(vec2 uv, const vec2_scalar& tile_repeat) { - if (tile_repeat.x > 0.0f) { - // Clamp to a number slightly less than the tile repeat limit so that - // it results in a number close to but not equal to 1 after fract(). - // This avoids fract() yielding 0 if the limit was left as whole integer. - uv = clamp(uv, vec2_scalar(0.0f), tile_repeat - 1.0e-6f); - } - return fract(uv); -} - -// Compute the number of non-repeating steps before we need to potentially -// repeat the UVs. -static inline int computeNoRepeatSteps(Float uv, float uv_step, - float tile_repeat, int steps) { - if (uv.w < uv.x) { - // Ensure the UV taps are ordered low to high. - uv = uv.wzyx; - } - // Check if the samples cross the boundary of the next whole integer or the - // tile repeat limit, whichever is lower. - float limit = floor(uv.x) + 1.0f; - if (tile_repeat > 0.0f) { - limit = min(limit, tile_repeat); - } - return uv.x >= 0.0f && uv.w < limit - ? (uv_step != 0.0f - ? int(min(float(steps), (limit - uv.x) / uv_step)) - : steps) - : 0; -} - -// Blends an entire span of texture with linear filtering and repeating UVs. -template <bool BLEND, typename S, typename C, typename P> -static int blendTextureLinearRepeat(S sampler, vec2 uv, int span, - const vec2_scalar& tile_repeat, - const vec4_scalar& uv_repeat, - const vec4_scalar& uv_rect, C color, - P* buf) { - if (!matchTextureFormat(sampler, buf)) { - return 0; - } - vec2_scalar uv_scale = {uv_repeat.z - uv_repeat.x, uv_repeat.w - uv_repeat.y}; - vec2_scalar uv_offset = {uv_repeat.x, uv_repeat.y}; - // Choose a linear filter to use for no-repeat sub-spans - LinearFilter filter = - needsTextureLinear(sampler, uv * uv_scale + uv_offset, span); - // We need to step UVs unscaled and unquantized so that we can modulo them - // with fract. We use uv_scale and uv_offset to map them into the correct - // range. - vec2_scalar uv_step = - float(swgl_StepSize) * vec2_scalar{uv.x.y - uv.x.x, uv.y.y - uv.y.x}; - uv_scale = swgl_linearQuantizeStep(sampler, uv_scale); - uv_offset = swgl_linearQuantize(sampler, uv_offset); - vec2_scalar min_uv = max( - swgl_linearQuantize(sampler, vec2_scalar{uv_rect.x, uv_rect.y}), 0.0f); - vec2_scalar max_uv = max( - swgl_linearQuantize(sampler, vec2_scalar{uv_rect.z, uv_rect.w}), min_uv); - for (P* end = buf + span; buf < end; buf += swgl_StepSize, uv += uv_step) { - int steps = int(end - buf) / swgl_StepSize; - // Find the sub-span before UVs repeat to avoid expensive repeat math - steps = computeNoRepeatSteps(uv.x, uv_step.x, tile_repeat.x, steps); - if (steps > 0) { - steps = computeNoRepeatSteps(uv.y, uv_step.y, tile_repeat.y, steps); - if (steps > 0) { - buf = blendTextureLinearDispatch<BLEND>( - sampler, fract(uv) * uv_scale + uv_offset, steps * swgl_StepSize, - uv_step * uv_scale, min_uv, max_uv, color, buf, filter); - if (buf >= end) { - break; - } - uv += steps * uv_step; - } - } - // UVs might repeat within this step, so explicitly compute repeated UVs - vec2 repeated_uv = clamp( - tileRepeatUV(uv, tile_repeat) * uv_scale + uv_offset, min_uv, max_uv); - commit_blend_span<BLEND>( - buf, applyColor(textureLinearUnpacked(buf, sampler, ivec2(repeated_uv)), - color)); - } - return span; -} - -// Commit an entire span with linear filtering and repeating UVs -#define swgl_commitTextureLinearRepeat(format, s, p, tile_repeat, uv_repeat, \ - uv_rect, color) \ - do { \ - auto packed_color = packColor(swgl_Out##format, color); \ - int drawn = 0; \ - if (blend_key) { \ - drawn = blendTextureLinearRepeat<true>(s, p, swgl_SpanLength, \ - tile_repeat, uv_repeat, uv_rect, \ - packed_color, swgl_Out##format); \ - } else { \ - drawn = blendTextureLinearRepeat<false>(s, p, swgl_SpanLength, \ - tile_repeat, uv_repeat, uv_rect, \ - packed_color, swgl_Out##format); \ - } \ - swgl_Out##format += drawn; \ - swgl_SpanLength -= drawn; \ - } while (0) -#define swgl_commitTextureLinearRepeatRGBA8(s, p, tile_repeat, uv_repeat, \ - uv_rect) \ - swgl_commitTextureLinearRepeat(RGBA8, s, p, tile_repeat, uv_repeat, uv_rect, \ - NoColor()) -#define swgl_commitTextureLinearRepeatColorRGBA8(s, p, tile_repeat, uv_repeat, \ - uv_rect, color) \ - swgl_commitTextureLinearRepeat(RGBA8, s, p, tile_repeat, uv_repeat, uv_rect, \ - color) - -template <typename S> -static ALWAYS_INLINE PackedRGBA8 textureNearestPacked(UNUSED uint32_t* buf, - S sampler, ivec2 i) { - return textureNearestPackedRGBA8(sampler, i); -} - -// Blends an entire span of texture with nearest filtering and either -// repeated or clamped UVs. -template <bool BLEND, bool REPEAT, typename S, typename C, typename P> -static int blendTextureNearestRepeat(S sampler, vec2 uv, int span, - const vec2_scalar& tile_repeat, - const vec4_scalar& uv_rect, C color, - P* buf) { - if (!matchTextureFormat(sampler, buf)) { - return 0; - } - if (!REPEAT) { - // If clamping, then we step pre-scaled to the sampler. For repeat modes, - // this will be accomplished via uv_scale instead. - uv = samplerScale(sampler, uv); - } - vec2_scalar uv_step = - float(swgl_StepSize) * vec2_scalar{uv.x.y - uv.x.x, uv.y.y - uv.y.x}; - vec2_scalar min_uv = samplerScale(sampler, vec2_scalar{uv_rect.x, uv_rect.y}); - vec2_scalar max_uv = samplerScale(sampler, vec2_scalar{uv_rect.z, uv_rect.w}); - vec2_scalar uv_scale = max_uv - min_uv; - // If the effective sampling area of this texture is only a single pixel, then - // treat it as a solid span. For repeat modes, the bounds are specified on - // pixel boundaries, whereas for clamp modes, bounds are on pixel centers, so - // the test varies depending on which. If the sample range on an axis is - // greater than one pixel, we can still check if we don't move far enough from - // the pixel center on that axis to hit the next pixel. - if ((int(min_uv.x) + (REPEAT ? 1 : 0) >= int(max_uv.x) || - (uv_step.x * span * (REPEAT ? uv_scale.x : 1.0f) < 0.5f)) && - (int(min_uv.y) + (REPEAT ? 1 : 0) >= int(max_uv.y) || - (uv_step.y * span * (REPEAT ? uv_scale.y : 1.0f) < 0.5f))) { - vec2 repeated_uv = REPEAT - ? tileRepeatUV(uv, tile_repeat) * uv_scale + min_uv - : clamp(uv, min_uv, max_uv); - commit_solid_span<BLEND>(buf, - applyColor(unpack(textureNearestPacked( - buf, sampler, ivec2(repeated_uv))), - color), - span); - } else { - for (P* end = buf + span; buf < end; buf += swgl_StepSize, uv += uv_step) { - if (REPEAT) { - int steps = int(end - buf) / swgl_StepSize; - // Find the sub-span before UVs repeat to avoid expensive repeat math - steps = computeNoRepeatSteps(uv.x, uv_step.x, tile_repeat.x, steps); - if (steps > 0) { - steps = computeNoRepeatSteps(uv.y, uv_step.y, tile_repeat.y, steps); - if (steps > 0) { - vec2 inside_uv = fract(uv) * uv_scale + min_uv; - vec2 inside_step = uv_step * uv_scale; - for (P* outside = &buf[steps * swgl_StepSize]; buf < outside; - buf += swgl_StepSize, inside_uv += inside_step) { - commit_blend_span<BLEND>( - buf, applyColor( - textureNearestPacked(buf, sampler, ivec2(inside_uv)), - color)); - } - if (buf >= end) { - break; - } - uv += steps * uv_step; - } - } - } - - // UVs might repeat within this step, so explicitly compute repeated UVs - vec2 repeated_uv = REPEAT - ? tileRepeatUV(uv, tile_repeat) * uv_scale + min_uv - : clamp(uv, min_uv, max_uv); - commit_blend_span<BLEND>( - buf, - applyColor(textureNearestPacked(buf, sampler, ivec2(repeated_uv)), - color)); - } - } - return span; -} - -// Determine if we can use the fast nearest filter for the given nearest mode. -// If the Y coordinate varies more than half a pixel over -// the span (which might cause the texel to alias to the next one), or the span -// needs X scaling, then we have to use the fallback. -template <typename S, typename T> -static ALWAYS_INLINE bool needsNearestFallback(S sampler, T P, int span) { - P = samplerScale(sampler, P); - return (P.y.y - P.y.x) * span >= 0.5f || spanNeedsScale(span, P); -} - -// Commit an entire span with nearest filtering and either clamped or repeating -// UVs -#define swgl_commitTextureNearest(format, s, p, uv_rect, color) \ - do { \ - auto packed_color = packColor(swgl_Out##format, color); \ - int drawn = 0; \ - if (needsNearestFallback(s, p, swgl_SpanLength)) { \ - if (blend_key) { \ - drawn = blendTextureNearestRepeat<true, false>( \ - s, p, swgl_SpanLength, 0.0f, uv_rect, packed_color, \ - swgl_Out##format); \ - } else { \ - drawn = blendTextureNearestRepeat<false, false>( \ - s, p, swgl_SpanLength, 0.0f, uv_rect, packed_color, \ - swgl_Out##format); \ - } \ - } else if (blend_key) { \ - drawn = blendTextureNearestFast<true>(s, p, swgl_SpanLength, uv_rect, \ - packed_color, swgl_Out##format); \ - } else { \ - drawn = blendTextureNearestFast<false>(s, p, swgl_SpanLength, uv_rect, \ - packed_color, swgl_Out##format); \ - } \ - swgl_Out##format += drawn; \ - swgl_SpanLength -= drawn; \ - } while (0) -#define swgl_commitTextureNearestRGBA8(s, p, uv_rect) \ - swgl_commitTextureNearest(RGBA8, s, p, uv_rect, NoColor()) -#define swgl_commitTextureNearestColorRGBA8(s, p, uv_rect, color) \ - swgl_commitTextureNearest(RGBA8, s, p, uv_rect, color) - -#define swgl_commitTextureNearestRepeat(format, s, p, tile_repeat, uv_rect, \ - color) \ - do { \ - auto packed_color = packColor(swgl_Out##format, color); \ - int drawn = 0; \ - if (blend_key) { \ - drawn = blendTextureNearestRepeat<true, true>( \ - s, p, swgl_SpanLength, tile_repeat, uv_rect, packed_color, \ - swgl_Out##format); \ - } else { \ - drawn = blendTextureNearestRepeat<false, true>( \ - s, p, swgl_SpanLength, tile_repeat, uv_rect, packed_color, \ - swgl_Out##format); \ - } \ - swgl_Out##format += drawn; \ - swgl_SpanLength -= drawn; \ - } while (0) -#define swgl_commitTextureNearestRepeatRGBA8(s, p, tile_repeat, uv_repeat, \ - uv_rect) \ - swgl_commitTextureNearestRepeat(RGBA8, s, p, tile_repeat, uv_repeat, \ - NoColor()) -#define swgl_commitTextureNearestRepeatColorRGBA8(s, p, tile_repeat, \ - uv_repeat, uv_rect, color) \ - swgl_commitTextureNearestRepeat(RGBA8, s, p, tile_repeat, uv_repeat, color) - -// Commit an entire span of texture with filtering determined by sampler state. -#define swgl_commitTexture(format, s, ...) \ - do { \ - if (s->filter == TextureFilter::LINEAR) { \ - swgl_commitTextureLinear##format(s, __VA_ARGS__); \ - } else { \ - swgl_commitTextureNearest##format(s, __VA_ARGS__); \ - } \ - } while (0) -#define swgl_commitTextureRGBA8(...) swgl_commitTexture(RGBA8, __VA_ARGS__) -#define swgl_commitTextureColorRGBA8(...) \ - swgl_commitTexture(ColorRGBA8, __VA_ARGS__) -#define swgl_commitTextureRepeatRGBA8(...) \ - swgl_commitTexture(RepeatRGBA8, __VA_ARGS__) -#define swgl_commitTextureRepeatColorRGBA8(...) \ - swgl_commitTexture(RepeatColorRGBA8, __VA_ARGS__) - -// Commit an entire span of a separable pass of a Gaussian blur that falls -// within the given radius scaled by supplied coefficients, clamped to uv_rect -// bounds. -template <bool BLEND, typename S, typename P> -static int blendGaussianBlur(S sampler, vec2 uv, const vec4_scalar& uv_rect, - P* buf, int span, bool hori, int radius, - vec2_scalar coeffs) { - if (!matchTextureFormat(sampler, buf)) { - return 0; - } - vec2_scalar size = {float(sampler->width), float(sampler->height)}; - ivec2_scalar curUV = make_ivec2(force_scalar(uv) * size); - ivec4_scalar bounds = make_ivec4(uv_rect * make_vec4(size, size)); - int startX = curUV.x; - int endX = min(bounds.z, curUV.x + span); - if (hori) { - for (; curUV.x + swgl_StepSize <= endX; - buf += swgl_StepSize, curUV.x += swgl_StepSize) { - commit_blend_span<BLEND>( - buf, gaussianBlurHorizontal<P>(sampler, curUV, bounds.x, bounds.z, - radius, coeffs.x, coeffs.y)); - } - } else { - for (; curUV.x + swgl_StepSize <= endX; - buf += swgl_StepSize, curUV.x += swgl_StepSize) { - commit_blend_span<BLEND>( - buf, gaussianBlurVertical<P>(sampler, curUV, bounds.y, bounds.w, - radius, coeffs.x, coeffs.y)); - } - } - return curUV.x - startX; -} - -#define swgl_commitGaussianBlur(format, s, p, uv_rect, hori, radius, coeffs) \ - do { \ - int drawn = 0; \ - if (blend_key) { \ - drawn = blendGaussianBlur<true>(s, p, uv_rect, swgl_Out##format, \ - swgl_SpanLength, hori, radius, coeffs); \ - } else { \ - drawn = blendGaussianBlur<false>(s, p, uv_rect, swgl_Out##format, \ - swgl_SpanLength, hori, radius, coeffs); \ - } \ - swgl_Out##format += drawn; \ - swgl_SpanLength -= drawn; \ - } while (0) -#define swgl_commitGaussianBlurRGBA8(s, p, uv_rect, hori, radius, coeffs) \ - swgl_commitGaussianBlur(RGBA8, s, p, uv_rect, hori, radius, coeffs) -#define swgl_commitGaussianBlurR8(s, p, uv_rect, hori, radius, coeffs) \ - swgl_commitGaussianBlur(R8, s, p, uv_rect, hori, radius, coeffs) - -// Convert and pack planar YUV samples to RGB output using a color space -static ALWAYS_INLINE PackedRGBA8 convertYUV(int colorSpace, U16 y, U16 u, - U16 v) { - auto yy = V8<int16_t>(zip(y, y)); - auto uv = V8<int16_t>(zip(u, v)); - return yuvMatrix[colorSpace].convert(yy, uv); -} - -// Helper functions to sample from planar YUV textures before converting to RGB -template <typename S0> -static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0, - int colorSpace, - UNUSED int rescaleFactor) { - switch (sampler0->format) { - case TextureFormat::RGBA8: { - auto planar = textureLinearPlanarRGBA8(sampler0, uv0); - return convertYUV(colorSpace, highHalf(planar.rg), lowHalf(planar.rg), - lowHalf(planar.ba)); - } - case TextureFormat::YUV422: { - auto planar = textureLinearPlanarYUV422(sampler0, uv0); - return convertYUV(colorSpace, planar.y, planar.u, planar.v); - } - default: - assert(false); - return PackedRGBA8(0); - } -} - -template <bool BLEND, typename S0, typename P, typename C = NoColor> -static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0, - const vec4_scalar& uv_rect0, int colorSpace, - int rescaleFactor, C color = C()) { - if (!swgl_isTextureLinear(sampler0)) { - return 0; - } - LINEAR_QUANTIZE_UV(sampler0, uv0, uv_step0, uv_rect0, min_uv0, max_uv0); - auto c = packColor(buf, color); - auto* end = buf + span; - for (; buf < end; buf += swgl_StepSize, uv0 += uv_step0) { - commit_blend_span<BLEND>( - buf, applyColor(sampleYUV(sampler0, ivec2(clamp(uv0, min_uv0, max_uv0)), - colorSpace, rescaleFactor), - c)); - } - return span; -} - -template <typename S0, typename S1> -static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0, S1 sampler1, - ivec2 uv1, int colorSpace, - UNUSED int rescaleFactor) { - switch (sampler1->format) { - case TextureFormat::RG8: { - assert(sampler0->format == TextureFormat::R8); - auto y = textureLinearUnpackedR8(sampler0, uv0); - auto planar = textureLinearPlanarRG8(sampler1, uv1); - return convertYUV(colorSpace, y, lowHalf(planar.rg), highHalf(planar.rg)); - } - case TextureFormat::RGBA8: { - assert(sampler0->format == TextureFormat::R8); - auto y = textureLinearUnpackedR8(sampler0, uv0); - auto planar = textureLinearPlanarRGBA8(sampler1, uv1); - return convertYUV(colorSpace, y, lowHalf(planar.ba), highHalf(planar.rg)); - } - default: - assert(false); - return PackedRGBA8(0); - } -} - -template <bool BLEND, typename S0, typename S1, typename P, - typename C = NoColor> -static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0, - const vec4_scalar& uv_rect0, S1 sampler1, vec2 uv1, - const vec4_scalar& uv_rect1, int colorSpace, - int rescaleFactor, C color = C()) { - if (!swgl_isTextureLinear(sampler0) || !swgl_isTextureLinear(sampler1)) { - return 0; - } - LINEAR_QUANTIZE_UV(sampler0, uv0, uv_step0, uv_rect0, min_uv0, max_uv0); - LINEAR_QUANTIZE_UV(sampler1, uv1, uv_step1, uv_rect1, min_uv1, max_uv1); - auto c = packColor(buf, color); - auto* end = buf + span; - for (; buf < end; buf += swgl_StepSize, uv0 += uv_step0, uv1 += uv_step1) { - commit_blend_span<BLEND>( - buf, applyColor(sampleYUV(sampler0, ivec2(clamp(uv0, min_uv0, max_uv0)), - sampler1, ivec2(clamp(uv1, min_uv1, max_uv1)), - colorSpace, rescaleFactor), - c)); - } - return span; -} - -template <typename S0, typename S1, typename S2> -static ALWAYS_INLINE PackedRGBA8 sampleYUV(S0 sampler0, ivec2 uv0, S1 sampler1, - ivec2 uv1, S2 sampler2, ivec2 uv2, - int colorSpace, int rescaleFactor) { - assert(sampler0->format == sampler1->format && - sampler0->format == sampler2->format); - switch (sampler0->format) { - case TextureFormat::R8: { - auto y = textureLinearUnpackedR8(sampler0, uv0); - auto u = textureLinearUnpackedR8(sampler1, uv1); - auto v = textureLinearUnpackedR8(sampler2, uv2); - return convertYUV(colorSpace, y, u, v); - } - case TextureFormat::R16: { - // The rescaling factor represents how many bits to add to renormalize the - // texture to 16 bits, and so the color depth is actually 16 minus the - // rescaling factor. - // Need to right shift the sample by the amount of bits over 8 it - // occupies. On output from textureLinearUnpackedR16, we have lost 1 bit - // of precision at the low end already, hence 1 is subtracted from the - // color depth. - int colorDepth = 16 - rescaleFactor; - int rescaleBits = (colorDepth - 1) - 8; - auto y = textureLinearUnpackedR16(sampler0, uv0) >> rescaleBits; - auto u = textureLinearUnpackedR16(sampler1, uv1) >> rescaleBits; - auto v = textureLinearUnpackedR16(sampler2, uv2) >> rescaleBits; - return convertYUV(colorSpace, U16(y), U16(u), U16(v)); - } - default: - assert(false); - return PackedRGBA8(0); - } -} - -// Fallback helper for when we can't specifically accelerate YUV with -// composition. -template <bool BLEND, typename S0, typename S1, typename S2, typename P, - typename C> -static void blendYUVFallback(P* buf, int span, S0 sampler0, vec2 uv0, - vec2_scalar uv_step0, vec2_scalar min_uv0, - vec2_scalar max_uv0, S1 sampler1, vec2 uv1, - vec2_scalar uv_step1, vec2_scalar min_uv1, - vec2_scalar max_uv1, S2 sampler2, vec2 uv2, - vec2_scalar uv_step2, vec2_scalar min_uv2, - vec2_scalar max_uv2, int colorSpace, - int rescaleFactor, C color) { - for (auto* end = buf + span; buf < end; buf += swgl_StepSize, uv0 += uv_step0, - uv1 += uv_step1, uv2 += uv_step2) { - commit_blend_span<BLEND>( - buf, applyColor(sampleYUV(sampler0, ivec2(clamp(uv0, min_uv0, max_uv0)), - sampler1, ivec2(clamp(uv1, min_uv1, max_uv1)), - sampler2, ivec2(clamp(uv2, min_uv2, max_uv2)), - colorSpace, rescaleFactor), - color)); - } -} - -template <bool BLEND, typename S0, typename S1, typename S2, typename P, - typename C = NoColor> -static int blendYUV(P* buf, int span, S0 sampler0, vec2 uv0, - const vec4_scalar& uv_rect0, S1 sampler1, vec2 uv1, - const vec4_scalar& uv_rect1, S2 sampler2, vec2 uv2, - const vec4_scalar& uv_rect2, int colorSpace, - int rescaleFactor, C color = C()) { - if (!swgl_isTextureLinear(sampler0) || !swgl_isTextureLinear(sampler1) || - !swgl_isTextureLinear(sampler2)) { - return 0; - } - LINEAR_QUANTIZE_UV(sampler0, uv0, uv_step0, uv_rect0, min_uv0, max_uv0); - LINEAR_QUANTIZE_UV(sampler1, uv1, uv_step1, uv_rect1, min_uv1, max_uv1); - LINEAR_QUANTIZE_UV(sampler2, uv2, uv_step2, uv_rect2, min_uv2, max_uv2); - auto c = packColor(buf, color); - blendYUVFallback<BLEND>(buf, span, sampler0, uv0, uv_step0, min_uv0, max_uv0, - sampler1, uv1, uv_step1, min_uv1, max_uv1, sampler2, - uv2, uv_step2, min_uv2, max_uv2, colorSpace, - rescaleFactor, c); - return span; -} - -// A variant of the blendYUV that attempts to reuse the inner loops from the -// CompositeYUV infrastructure. CompositeYUV imposes stricter requirements on -// the source data, which in turn allows it to be much faster than blendYUV. -// At a minimum, we need to ensure that we are outputting to a BGRA8 framebuffer -// and that no color scaling is applied, which we can accomplish via template -// specialization. We need to further validate inside that texture formats -// and dimensions are sane for video and that the video is axis-aligned before -// acceleration can proceed. -template <bool BLEND> -static int blendYUV(uint32_t* buf, int span, sampler2DRect sampler0, vec2 uv0, - const vec4_scalar& uv_rect0, sampler2DRect sampler1, - vec2 uv1, const vec4_scalar& uv_rect1, - sampler2DRect sampler2, vec2 uv2, - const vec4_scalar& uv_rect2, int colorSpace, - int rescaleFactor, NoColor noColor = NoColor()) { - if (!swgl_isTextureLinear(sampler0) || !swgl_isTextureLinear(sampler1) || - !swgl_isTextureLinear(sampler2)) { - return 0; - } - LINEAR_QUANTIZE_UV(sampler0, uv0, uv_step0, uv_rect0, min_uv0, max_uv0); - LINEAR_QUANTIZE_UV(sampler1, uv1, uv_step1, uv_rect1, min_uv1, max_uv1); - LINEAR_QUANTIZE_UV(sampler2, uv2, uv_step2, uv_rect2, min_uv2, max_uv2); - auto* end = buf + span; - // CompositeYUV imposes further restrictions on the source textures, such that - // the the Y/U/V samplers must all have a matching format, the U/V samplers - // must have matching sizes and sample coordinates, and there must be no - // change in row across the entire span. - if (sampler0->format == sampler1->format && - sampler1->format == sampler2->format && - sampler1->width == sampler2->width && - sampler1->height == sampler2->height && uv_step0.y == 0 && - uv_step0.x > 0 && uv_step1.y == 0 && uv_step1.x > 0 && - uv_step1 == uv_step2 && uv1.x.x == uv2.x.x && uv1.y.x == uv2.y.x) { - // CompositeYUV does not support a clamp rect, so we must take care to - // advance till we're inside the bounds of the clamp rect. - int outside = min(int(ceil(max((min_uv0.x - uv0.x.x) / uv_step0.x, - (min_uv1.x - uv1.x.x) / uv_step1.x))), - (end - buf) / swgl_StepSize); - if (outside > 0) { - blendYUVFallback<BLEND>( - buf, outside * swgl_StepSize, sampler0, uv0, uv_step0, min_uv0, - max_uv0, sampler1, uv1, uv_step1, min_uv1, max_uv1, sampler2, uv2, - uv_step2, min_uv2, max_uv2, colorSpace, rescaleFactor, noColor); - buf += outside * swgl_StepSize; - uv0.x += outside * uv_step0.x; - uv1.x += outside * uv_step1.x; - uv2.x += outside * uv_step2.x; - } - // Find the amount of chunks inside the clamp rect before we hit the - // maximum. If there are any chunks inside, we can finally dispatch to - // CompositeYUV. - int inside = min(int(min((max_uv0.x - uv0.x.x) / uv_step0.x, - (max_uv1.x - uv1.x.x) / uv_step1.x)), - (end - buf) / swgl_StepSize); - if (inside > 0) { - // We need the color depth, which is relative to the texture format and - // rescale factor. - int colorDepth = - (sampler0->format == TextureFormat::R16 ? 16 : 8) - rescaleFactor; - // Finally, call the inner loop of CompositeYUV. - linear_row_yuv<BLEND>( - buf, inside * swgl_StepSize, sampler0, force_scalar(uv0), - uv_step0.x / swgl_StepSize, sampler1, sampler2, force_scalar(uv1), - uv_step1.x / swgl_StepSize, colorDepth, yuvMatrix[colorSpace]); - // Now that we're done, advance past the processed inside portion. - buf += inside * swgl_StepSize; - uv0.x += inside * uv_step0.x; - uv1.x += inside * uv_step1.x; - uv2.x += inside * uv_step2.x; - } - } - // We either got here because we have some samples outside the clamp rect, or - // because some of the preconditions were not satisfied. Process whatever is - // left of the span. - blendYUVFallback<BLEND>(buf, end - buf, sampler0, uv0, uv_step0, min_uv0, - max_uv0, sampler1, uv1, uv_step1, min_uv1, max_uv1, - sampler2, uv2, uv_step2, min_uv2, max_uv2, colorSpace, - rescaleFactor, noColor); - return span; -} - -// Commit a single chunk of a YUV surface represented by multiple planar -// textures. This requires a color space specifier selecting how to convert -// from YUV to RGB output. In the case of HDR formats, a rescaling factor -// selects how many bits of precision must be utilized on conversion. See the -// sampleYUV dispatcher functions for the various supported plane -// configurations this intrinsic accepts. -#define swgl_commitTextureLinearYUV(...) \ - do { \ - int drawn = 0; \ - if (blend_key) { \ - drawn = blendYUV<true>(swgl_OutRGBA8, swgl_SpanLength, __VA_ARGS__); \ - } else { \ - drawn = blendYUV<false>(swgl_OutRGBA8, swgl_SpanLength, __VA_ARGS__); \ - } \ - swgl_OutRGBA8 += drawn; \ - swgl_SpanLength -= drawn; \ - } while (0) - -// Commit a single chunk of a YUV surface scaled by a color. -#define swgl_commitTextureLinearColorYUV(...) \ - swgl_commitTextureLinearYUV(__VA_ARGS__) - -// Each gradient stops entry is a pair of RGBA32F start color and end step. -struct GradientStops { - Float startColor; - union { - Float stepColor; - vec4_scalar stepData; - }; - - // Whether this gradient entry can be merged with an adjacent entry. The - // step will be equal with the adjacent step if and only if they can be - // merged, or rather, that the stops are actually part of a single larger - // gradient. - bool can_merge(const GradientStops& next) const { - return stepData == next.stepData; - } - - // Get the interpolated color within the entry based on the offset from its - // start. - Float interpolate(float offset) const { - return startColor + stepColor * offset; - } - - // Get the end color of the entry where interpolation stops. - Float end_color() const { return startColor + stepColor; } -}; - -// Checks if a gradient table of the specified size exists at the UV coords of -// the address within an RGBA32F texture. If so, a linear address within the -// texture is returned that may be used to sample the gradient table later. If -// the address doesn't describe a valid gradient, then a negative value is -// returned. -static inline int swgl_validateGradient(sampler2D sampler, ivec2_scalar address, - int entries) { - return sampler->format == TextureFormat::RGBA32F && address.y >= 0 && - address.y < int(sampler->height) && address.x >= 0 && - address.x < int(sampler->width) && entries > 0 && - address.x + - int(sizeof(GradientStops) / sizeof(Float)) * entries <= - int(sampler->width) - ? address.y * sampler->stride + address.x * 4 - : -1; -} - -static inline WideRGBA8 sampleGradient(sampler2D sampler, int address, - Float entry) { - assert(sampler->format == TextureFormat::RGBA32F); - assert(address >= 0 && address < int(sampler->height * sampler->stride)); - // Get the integer portion of the entry index to find the entry colors. - I32 index = cast(entry); - // Use the fractional portion of the entry index to control blending between - // entry colors. - Float offset = entry - cast(index); - // Every entry is a pair of colors blended by the fractional offset. - assert(test_all(index >= 0 && - index * int(sizeof(GradientStops) / sizeof(Float)) < - int(sampler->width))); - GradientStops* stops = (GradientStops*)&sampler->buf[address]; - // Blend between the colors for each SIMD lane, then pack them to RGBA8 - // result. Since the layout of the RGBA8 framebuffer is actually BGRA while - // the gradient table has RGBA colors, swizzling is required. - return combine( - packRGBA8(round_pixel(stops[index.x].interpolate(offset.x).zyxw), - round_pixel(stops[index.y].interpolate(offset.y).zyxw)), - packRGBA8(round_pixel(stops[index.z].interpolate(offset.z).zyxw), - round_pixel(stops[index.w].interpolate(offset.w).zyxw))); -} - -// Samples a gradient entry from the gradient at the provided linearized -// address. The integer portion of the entry index is used to find the entry -// within the table whereas the fractional portion is used to blend between -// adjacent table entries. -#define swgl_commitGradientRGBA8(sampler, address, entry) \ - swgl_commitChunk(RGBA8, sampleGradient(sampler, address, entry)) - -// Variant that allows specifying a color multiplier of the gradient result. -#define swgl_commitGradientColorRGBA8(sampler, address, entry, color) \ - swgl_commitChunk(RGBA8, applyColor(sampleGradient(sampler, address, entry), \ - packColor(swgl_OutRGBA, color))) - -// Samples an entire span of a linear gradient by crawling the gradient table -// and looking for consecutive stops that can be merged into a single larger -// gradient, then interpolating between those larger gradients within the span. -template <bool BLEND> -static bool commitLinearGradient(sampler2D sampler, int address, float size, - bool repeat, Float offset, uint32_t* buf, - int span) { - assert(sampler->format == TextureFormat::RGBA32F); - assert(address >= 0 && address < int(sampler->height * sampler->stride)); - GradientStops* stops = (GradientStops*)&sampler->buf[address]; - // Get the chunk delta from the difference in offset steps. This represents - // how far within the gradient table we advance for every step in output, - // normalized to gradient table size. - float delta = (offset.y - offset.x) * 4.0f; - if (!isfinite(delta)) { - return false; - } - for (; span > 0;) { - // If repeat is desired, we need to limit the offset to a fractional value. - if (repeat) { - offset = fract(offset); - } - // Try to process as many chunks as are within the span if possible. - float chunks = 0.25f * span; - // To properly handle both clamping and repeating of the table offset, we - // need to ensure we don't run past the 0 and 1 points. Here we compute the - // intercept points depending on whether advancing forwards or backwards in - // the gradient table to ensure the chunk count is limited by the amount - // before intersection. If there is no delta, then we compute no intercept. - float startEntry; - int minIndex, maxIndex; - if (offset.x < 0) { - // If we're below the gradient table, use the first color stop. We can - // only intercept the table if walking forward. - startEntry = 0; - minIndex = int(startEntry); - maxIndex = minIndex; - if (delta > 0) { - chunks = min(chunks, -offset.x / delta); - } - } else if (offset.x < 1) { - // Otherwise, we're inside the gradient table. Depending on the direction - // we're walking the the table, we may intersect either the 0 or 1 offset. - // Compute the start entry based on our initial offset, and compute the - // end entry based on the available chunks limited by intercepts. Clamp - // them into the valid range of the table. - startEntry = 1.0f + offset.x * size; - if (delta < 0) { - chunks = min(chunks, -offset.x / delta); - } else if (delta > 0) { - chunks = min(chunks, (1 - offset.x) / delta); - } - float endEntry = clamp(1.0f + (offset.x + delta * int(chunks)) * size, - 0.0f, 1.0f + size); - // Now that we know the range of entries we need to sample, we want to - // find the largest possible merged gradient within that range. Depending - // on which direction we are advancing in the table, we either walk up or - // down the table trying to merge the current entry with the adjacent - // entry. We finally limit the chunks to only sample from this merged - // gradient. - minIndex = int(startEntry); - maxIndex = minIndex; - if (delta > 0) { - while (maxIndex + 1 < endEntry && - stops[maxIndex].can_merge(stops[maxIndex + 1])) { - maxIndex++; - } - chunks = min(chunks, (maxIndex + 1 - startEntry) / (delta * size)); - } else if (delta < 0) { - while (minIndex - 1 > endEntry && - stops[minIndex - 1].can_merge(stops[minIndex])) { - minIndex--; - } - chunks = min(chunks, (minIndex - startEntry) / (delta * size)); - } - } else { - // If we're above the gradient table, use the last color stop. We can - // only intercept the table if walking backward. - startEntry = 1.0f + size; - minIndex = int(startEntry); - maxIndex = minIndex; - if (delta < 0) { - chunks = min(chunks, (1 - offset.x) / delta); - } - } - // If there are any amount of whole chunks of a merged gradient found, - // then we want to process that as a single gradient span with the start - // and end colors from the min and max entries. - if (chunks >= 1.0f) { - int inside = int(chunks); - // Sample the start color from the min entry and the end color from the - // max entry of the merged gradient. These are scaled to a range of - // 0..0xFF00, as that is the largest shifted value that can fit in a U16. - // Since we are only doing addition with the step value, we can still - // represent negative step values without having to use an explicit sign - // bit, as the result will still come out the same, allowing us to gain an - // extra bit of precision. We will later shift these into 8 bit output - // range while committing the span, but stepping with higher precision to - // avoid banding. We convert from RGBA to BGRA here to avoid doing this in - // the inner loop. - auto minColorF = stops[minIndex].startColor.zyxw * float(0xFF00); - auto maxColorF = stops[maxIndex].end_color().zyxw * float(0xFF00); - // Get the color range of the merged gradient, normalized to its size. - auto colorRangeF = - (maxColorF - minColorF) * (1.0f / (maxIndex + 1 - minIndex)); - // Compute the actual starting color of the current start offset within - // the merged gradient. The value 0.5 is added to the low bits (0x80) so - // that the color will effective round to the nearest increment below. - auto colorF = - minColorF + colorRangeF * (startEntry - minIndex) + float(0x80); - // Compute the portion of the color range that we advance on each chunk. - Float deltaColorF = colorRangeF * (delta * size); - // Quantize the color delta and current color. These have already been - // scaled to the 0..0xFF00 range, so we just need to round them to U16. - auto deltaColor = repeat4(CONVERT(round_pixel(deltaColorF, 1), U16)); - auto color = - combine(CONVERT(round_pixel(colorF, 1), U16), - CONVERT(round_pixel(colorF + deltaColorF * 0.25f, 1), U16), - CONVERT(round_pixel(colorF + deltaColorF * 0.5f, 1), U16), - CONVERT(round_pixel(colorF + deltaColorF * 0.75f, 1), U16)); - // Finally, step the current color through the output chunks, shifting - // it into 8 bit range and outputting as we go. - for (auto* end = buf + inside * 4; buf < end; buf += 4) { - commit_blend_span<BLEND>(buf, bit_cast<WideRGBA8>(color >> 8)); - color += deltaColor; - } - // Deduct the number of chunks inside the gradient from the remaining - // overall span. If we exhausted the span, bail out. - span -= inside * 4; - if (span <= 0) { - break; - } - // Otherwise, assume we're in a transitional section of the gradient that - // will probably require per-sample table lookups, so fall through below. - offset += inside * delta; - if (repeat) { - offset = fract(offset); - } - } - // If we get here, there were no whole chunks of a merged gradient found - // that we could process, but we still have a non-zero amount of span left. - // That means we have segments of gradient that begin or end at the current - // entry we're on. For this case, we just fall back to sampleGradient which - // will calculate a table entry for each sample, assuming the samples may - // have different table entries. - Float entry = clamp(offset * size + 1.0f, 0.0f, 1.0f + size); - commit_blend_span<BLEND>(buf, sampleGradient(sampler, address, entry)); - span -= 4; - buf += 4; - offset += delta; - } - return true; -} - -// Commits an entire span of a linear gradient, given the address of a table -// previously resolved with swgl_validateGradient. The size of the inner portion -// of the table is given, assuming the table start and ends with a single entry -// each to deal with clamping. Repeating will be handled if necessary. The -// initial offset within the table is used to designate where to start the span -// and how to step through the gradient table. -#define swgl_commitLinearGradientRGBA8(sampler, address, size, repeat, offset) \ - do { \ - bool drawn = false; \ - if (blend_key) { \ - drawn = \ - commitLinearGradient<true>(sampler, address, size, repeat, offset, \ - swgl_OutRGBA8, swgl_SpanLength); \ - } else { \ - drawn = \ - commitLinearGradient<false>(sampler, address, size, repeat, offset, \ - swgl_OutRGBA8, swgl_SpanLength); \ - } \ - if (drawn) { \ - swgl_OutRGBA8 += swgl_SpanLength; \ - swgl_SpanLength = 0; \ - } \ - } while (0) - -template <bool CLAMP, typename V> -static ALWAYS_INLINE V fastSqrt(V v) { -#if USE_SSE2 || USE_NEON - // Clamp to avoid zero in inversesqrt. - return v * inversesqrt(CLAMP ? max(v, V(1.0e-10f)) : v); -#else - return sqrt(v); -#endif -} - -template <bool CLAMP, typename V> -static ALWAYS_INLINE auto fastLength(V v) { - return fastSqrt<CLAMP>(dot(v, v)); -} - -// Samples an entire span of a radial gradient by crawling the gradient table -// and looking for consecutive stops that can be merged into a single larger -// gradient, then interpolating between those larger gradients within the span -// based on the computed position relative to a radius. -template <bool BLEND> -static bool commitRadialGradient(sampler2D sampler, int address, float size, - bool repeat, vec2 pos, float radius, - uint32_t* buf, int span) { - assert(sampler->format == TextureFormat::RGBA32F); - assert(address >= 0 && address < int(sampler->height * sampler->stride)); - GradientStops* stops = (GradientStops*)&sampler->buf[address]; - // clang-format off - // Given position p, delta d, and radius r, we need to repeatedly solve the - // following quadratic for the pixel offset t: - // length(p + t*d) = r - // (px + t*dx)^2 + (py + t*dy)^2 = r^2 - // Rearranged into quadratic equation form (t^2*a + t*b + c = 0) this is: - // t^2*(dx^2+dy^2) + t*2*(dx*px+dy*py) + (px^2+py^2-r^2) = 0 - // t^2*d.d + t*2*d.p + (p.p-r^2) = 0 - // The solution of the quadratic formula t=(-b+-sqrt(b^2-4ac))/2a reduces to: - // t = -d.p/d.d +- sqrt((d.p/d.d)^2 - (p.p-r^2)/d.d) - // Note that d.p, d.d, p.p, and r^2 are constant across the gradient, and so - // we cache them below for faster computation. - // - // The quadratic has two solutions, representing the span intersecting the - // given radius of gradient, which can occur at two offsets. If there is only - // one solution (where b^2-4ac = 0), this represents the point at which the - // span runs tangent to the radius. This middle point is significant in that - // before it, we walk down the gradient ramp, and after it, we walk up the - // ramp. - // clang-format on - vec2_scalar pos0 = {pos.x.x, pos.y.x}; - vec2_scalar delta = {pos.x.y - pos.x.x, pos.y.y - pos.y.x}; - float deltaDelta = dot(delta, delta); - if (!isfinite(deltaDelta) || !isfinite(radius)) { - return false; - } - float invDelta, middleT, middleB; - if (deltaDelta > 0) { - invDelta = 1.0f / deltaDelta; - middleT = -dot(delta, pos0) * invDelta; - middleB = middleT * middleT - dot(pos0, pos0) * invDelta; - } else { - // If position is invariant, just set the coefficients so the quadratic - // always reduces to the end of the span. - invDelta = 0.0f; - middleT = float(span); - middleB = 0.0f; - } - // We only want search for merged gradients up to the minimum of either the - // mid-point or the span length. Cache those offsets here as they don't vary - // in the inner loop. - Float middleEndRadius = fastLength<true>( - pos0 + delta * (Float){middleT, float(span), 0.0f, 0.0f}); - float middleRadius = span < middleT ? middleEndRadius.y : middleEndRadius.x; - float endRadius = middleEndRadius.y; - // Convert delta to change in position per chunk. - delta *= 4; - deltaDelta *= 4 * 4; - // clang-format off - // Given current position p and delta d, we reduce: - // length(p) = sqrt(dot(p,p)) = dot(p,p) * invsqrt(dot(p,p)) - // where dot(p+d,p+d) can be accumulated as: - // (x+dx)^2+(y+dy)^2 = (x^2+y^2) + 2(x*dx+y*dy) + (dx^2+dy^2) - // = p.p + 2p.d + d.d - // Since p increases by d every loop iteration, p.d increases by d.d, and thus - // we can accumulate d.d to calculate 2p.d, then allowing us to get the next - // dot-product by adding it to dot-product p.p of the prior iteration. This - // saves us some multiplications and an expensive sqrt inside the inner loop. - // clang-format on - Float dotPos = dot(pos, pos); - Float dotPosDelta = 2.0f * dot(pos, delta) + deltaDelta; - float deltaDelta2 = 2.0f * deltaDelta; - for (int t = 0; t < span;) { - // Compute the gradient table offset from the current position. - Float offset = fastSqrt<true>(dotPos) - radius; - float startRadius = radius; - // If repeat is desired, we need to limit the offset to a fractional value. - if (repeat) { - // The non-repeating radius at which the gradient table actually starts, - // radius + floor(offset) = radius + (offset - fract(offset)). - startRadius += offset.x; - offset = fract(offset); - startRadius -= offset.x; - } - // We need to find the min/max index in the table of the gradient we want to - // use as well as the intercept point where we leave this gradient. - float intercept = -1; - int minIndex = 0; - int maxIndex = int(1.0f + size); - if (offset.x < 0) { - // If inside the inner radius of the gradient table, then use the first - // stop. Set the intercept to advance forward to the start of the gradient - // table. - maxIndex = minIndex; - if (t >= middleT) { - intercept = radius; - } - } else if (offset.x < 1) { - // Otherwise, we're inside the valid part of the gradient table. - minIndex = int(1.0f + offset.x * size); - maxIndex = minIndex; - // Find the offset in the gradient that corresponds to the search limit. - // We only search up to the minimum of either the mid-point or the span - // length. Get the table index that corresponds to this offset, clamped so - // that we avoid hitting the beginning (0) or end (1 + size) of the table. - float searchOffset = - (t >= middleT ? endRadius : middleRadius) - startRadius; - int searchIndex = int(clamp(1.0f + size * searchOffset, 1.0f, size)); - // If we are past the mid-point, walk up the gradient table trying to - // merge stops. If we're below the mid-point, we need to walk down the - // table. We note the table index at which we need to look for an - // intercept to determine a valid span. - if (t >= middleT) { - while (maxIndex + 1 <= searchIndex && - stops[maxIndex].can_merge(stops[maxIndex + 1])) { - maxIndex++; - } - intercept = maxIndex + 1; - } else { - while (minIndex - 1 >= searchIndex && - stops[minIndex - 1].can_merge(stops[minIndex])) { - minIndex--; - } - intercept = minIndex; - } - // Convert from a table index into units of radius from the center of the - // gradient. - intercept = clamp((intercept - 1.0f) / size, 0.0f, 1.0f) + startRadius; - } else { - // If outside the outer radius of the gradient table, then use the last - // stop. Set the intercept to advance toward the valid part of the - // gradient table if going in, or just run to the end of the span if going - // away from the gradient. - minIndex = maxIndex; - if (t < middleT) { - intercept = radius + 1; - } - } - // Solve the quadratic for t to find where the merged gradient ends. If no - // intercept is found, just go to the middle or end of the span. - float endT = t >= middleT ? span : min(span, int(middleT)); - if (intercept >= 0) { - float b = middleB + intercept * intercept * invDelta; - if (b > 0) { - b = fastSqrt<false>(b); - endT = min(endT, t >= middleT ? middleT + b : middleT - b); - } - } - // Figure out how many chunks are actually inside the merged gradient. - if (t + 4.0f <= endT) { - int inside = int(endT - t) & ~3; - // Convert start and end colors to BGRA and scale to 0..255 range later. - auto minColorF = stops[minIndex].startColor.zyxw * 255.0f; - auto maxColorF = stops[maxIndex].end_color().zyxw * 255.0f; - // Compute the change in color per change in gradient offset. - auto deltaColorF = - (maxColorF - minColorF) * (size / (maxIndex + 1 - minIndex)); - // Subtract off the color difference of the beginning of the current span - // from the beginning of the gradient. - Float colorF = - minColorF - deltaColorF * (startRadius + (minIndex - 1) / size); - // Finally, walk over the span accumulating the position dot product and - // getting its sqrt as an offset into the color ramp. Since we're already - // in BGRA format and scaled to 255, we just need to round to an integer - // and pack down to pixel format. - for (auto* end = buf + inside; buf < end; buf += 4) { - Float offsetG = fastSqrt<false>(dotPos); - commit_blend_span<BLEND>( - buf, - combine( - packRGBA8(round_pixel(colorF + deltaColorF * offsetG.x, 1), - round_pixel(colorF + deltaColorF * offsetG.y, 1)), - packRGBA8(round_pixel(colorF + deltaColorF * offsetG.z, 1), - round_pixel(colorF + deltaColorF * offsetG.w, 1)))); - dotPos += dotPosDelta; - dotPosDelta += deltaDelta2; - } - // Advance past the portion of gradient we just processed. - t += inside; - // If we hit the end of the span, exit out now. - if (t >= span) { - break; - } - // Otherwise, we are most likely in a transitional section of the gradient - // between stops that will likely require doing per-sample table lookups. - // Rather than having to redo all the searching above to figure that out, - // just assume that to be the case and fall through below to doing the - // table lookups to hopefully avoid an iteration. - offset = fastSqrt<true>(dotPos) - radius; - if (repeat) { - offset = fract(offset); - } - } - // If we got here, that means we still have span left to process but did not - // have any whole chunks that fell within a merged gradient. Just fall back - // to doing a table lookup for each sample. - Float entry = clamp(offset * size + 1.0f, 0.0f, 1.0f + size); - commit_blend_span<BLEND>(buf, sampleGradient(sampler, address, entry)); - buf += 4; - t += 4; - dotPos += dotPosDelta; - dotPosDelta += deltaDelta2; - } - return true; -} - -// Commits an entire span of a radial gradient similar to -// swglcommitLinearGradient, but given a varying 2D position scaled to -// gradient-space and a radius at which the distance from the origin maps to the -// start of the gradient table. -#define swgl_commitRadialGradientRGBA8(sampler, address, size, repeat, pos, \ - radius) \ - do { \ - bool drawn = false; \ - if (blend_key) { \ - drawn = \ - commitRadialGradient<true>(sampler, address, size, repeat, pos, \ - radius, swgl_OutRGBA8, swgl_SpanLength); \ - } else { \ - drawn = \ - commitRadialGradient<false>(sampler, address, size, repeat, pos, \ - radius, swgl_OutRGBA8, swgl_SpanLength); \ - } \ - if (drawn) { \ - swgl_OutRGBA8 += swgl_SpanLength; \ - swgl_SpanLength = 0; \ - } \ - } while (0) - -// Extension to set a clip mask image to be sampled during blending. The offset -// specifies the positioning of the clip mask image relative to the viewport -// origin. The bounding box specifies the rectangle relative to the clip mask's -// origin that constrains sampling within the clip mask. Blending must be -// enabled for this to work. -static sampler2D swgl_ClipMask = nullptr; -static IntPoint swgl_ClipMaskOffset = {0, 0}; -static IntRect swgl_ClipMaskBounds = {0, 0, 0, 0}; -#define swgl_clipMask(mask, offset, bb_origin, bb_size) \ - do { \ - if (bb_size != vec2_scalar(0.0f, 0.0f)) { \ - swgl_ClipFlags |= SWGL_CLIP_FLAG_MASK; \ - swgl_ClipMask = mask; \ - swgl_ClipMaskOffset = make_ivec2(offset); \ - swgl_ClipMaskBounds = \ - IntRect(make_ivec2(bb_origin), make_ivec2(bb_size)); \ - } \ - } while (0) - -// Extension to enable anti-aliasing for the given edges of a quad. -// Blending must be enable for this to work. -static int swgl_AAEdgeMask = 0; - -static ALWAYS_INLINE int calcAAEdgeMask(bool on) { return on ? 0xF : 0; } -static ALWAYS_INLINE int calcAAEdgeMask(int mask) { return mask; } -static ALWAYS_INLINE int calcAAEdgeMask(bvec4_scalar mask) { - return (mask.x ? 1 : 0) | (mask.y ? 2 : 0) | (mask.z ? 4 : 0) | - (mask.w ? 8 : 0); -} - -#define swgl_antiAlias(edges) \ - do { \ - swgl_AAEdgeMask = calcAAEdgeMask(edges); \ - if (swgl_AAEdgeMask) { \ - swgl_ClipFlags |= SWGL_CLIP_FLAG_AA; \ - } \ - } while (0) - -#define swgl_blendDropShadow(color) \ - do { \ - swgl_ClipFlags |= SWGL_CLIP_FLAG_BLEND_OVERRIDE; \ - swgl_BlendOverride = BLEND_KEY(SWGL_BLEND_DROP_SHADOW); \ - swgl_BlendColorRGBA8 = packColor<uint32_t>(color); \ - } while (0) - -#define swgl_blendSubpixelText(color) \ - do { \ - swgl_ClipFlags |= SWGL_CLIP_FLAG_BLEND_OVERRIDE; \ - swgl_BlendOverride = BLEND_KEY(SWGL_BLEND_SUBPIXEL_TEXT); \ - swgl_BlendColorRGBA8 = packColor<uint32_t>(color); \ - swgl_BlendAlphaRGBA8 = alphas(swgl_BlendColorRGBA8); \ - } while (0) - -// Dispatch helper used by the GLSL translator to swgl_drawSpan functions. -// The number of pixels committed is tracked by checking for the difference in -// swgl_SpanLength. Any varying interpolants used will be advanced past the -// committed part of the span in case the fragment shader must be executed for -// any remaining pixels that were not committed by the span shader. -#define DISPATCH_DRAW_SPAN(self, format) \ - do { \ - int total = self->swgl_SpanLength; \ - self->swgl_drawSpan##format(); \ - int drawn = total - self->swgl_SpanLength; \ - if (drawn) self->step_interp_inputs(drawn); \ - return drawn; \ - } while (0) diff --git a/third_party/webrender/swgl/src/swgl_fns.rs b/third_party/webrender/swgl/src/swgl_fns.rs index fdb55058afe..0cb60c6d4c8 100644 --- a/third_party/webrender/swgl/src/swgl_fns.rs +++ b/third_party/webrender/swgl/src/swgl_fns.rs @@ -14,12 +14,8 @@ macro_rules! debug { ($($x:tt)*) => {}; } -#[repr(C)] -struct LockedTexture { - _private: [u8; 0], -} +extern "C" {} -#[allow(dead_code)] extern "C" { fn ActiveTexture(texture: GLenum); fn BindTexture(target: GLenum, texture: GLuint); @@ -65,7 +61,19 @@ extern "C" { level: GLint, ); fn CheckFramebufferStatus(target: GLenum) -> GLenum; - fn InvalidateFramebuffer(target: GLenum, num_attachments: GLsizei, attachments: *const 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, @@ -77,6 +85,18 @@ extern "C" { 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, @@ -88,6 +108,19 @@ extern "C" { 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 GenerateMipmap(target: GLenum); fn GetUniformLocation(program: GLuint, name: *const GLchar) -> GLint; fn BindAttribLocation(program: GLuint, index: GLuint, name: *const GLchar); @@ -119,19 +152,26 @@ extern "C" { transpose: GLboolean, value: *const GLfloat, ); + fn DrawElementsInstanced( mode: GLenum, count: GLsizei, type_: GLenum, - indices: GLintptr, + indices: *const c_void, instancecount: GLsizei, ); fn EnableVertexAttribArray(index: GLuint); fn VertexAttribDivisor(index: GLuint, divisor: GLuint); fn LinkProgram(program: GLuint); - fn GetLinkStatus(program: GLuint) -> GLint; 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, @@ -145,31 +185,6 @@ extern "C" { fn ClearColor(r: GLfloat, g: GLfloat, b: GLfloat, a: GLfloat); fn ClearDepth(depth: GLdouble); fn Clear(mask: GLbitfield); - fn ClearTexSubImage( - 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 ClearTexImage(target: GLenum, level: GLint, format: GLenum, ty: GLenum, data: *const c_void); - fn ClearColorRect( - fbo: GLuint, - xoffset: GLint, - yoffset: GLint, - width: GLsizei, - height: GLsizei, - r: GLfloat, - g: GLfloat, - b: GLfloat, - a: GLfloat, - ); fn PixelStorei(name: GLenum, param: GLint); fn ReadPixels( x: GLint, @@ -210,6 +225,17 @@ extern "C" { 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, @@ -227,33 +253,22 @@ extern "C" { fn GetString(name: GLenum) -> *const c_char; fn GetStringi(name: GLenum, index: GLuint) -> *const c_char; fn GetError() -> GLenum; - fn InitDefaultFramebuffer( - x: i32, - y: i32, - width: i32, - height: i32, - stride: i32, - buf: *mut c_void, - ); + fn InitDefaultFramebuffer(width: i32, height: i32); fn GetColorBuffer( fbo: GLuint, flush: GLboolean, width: *mut i32, height: *mut i32, - stride: *mut i32, ) -> *mut c_void; - fn ResolveFramebuffer(fbo: GLuint); fn SetTextureBuffer( tex: GLuint, internal_format: GLenum, width: GLsizei, height: GLsizei, - stride: GLsizei, buf: *mut c_void, min_width: GLsizei, min_height: GLsizei, ); - fn SetTextureParameter(tex: GLuint, pname: GLenum, param: GLint); fn DeleteTexture(n: GLuint); fn DeleteRenderbuffer(n: GLuint); fn DeleteFramebuffer(n: GLuint); @@ -262,64 +277,23 @@ extern "C" { fn DeleteQuery(n: GLuint); fn DeleteShader(shader: GLuint); fn DeleteProgram(program: GLuint); - fn LockFramebuffer(fbo: GLuint) -> *mut LockedTexture; - fn LockTexture(tex: GLuint) -> *mut LockedTexture; - fn LockResource(resource: *mut LockedTexture); - fn UnlockResource(resource: *mut LockedTexture); - fn GetResourceBuffer( - resource: *mut LockedTexture, - width: *mut i32, - height: *mut i32, - stride: *mut i32, - ) -> *mut c_void; fn Composite( - locked_dst: *mut LockedTexture, - locked_src: *mut LockedTexture, + src_id: GLuint, src_x: GLint, src_y: GLint, src_width: GLsizei, src_height: GLsizei, dst_x: GLint, dst_y: GLint, - dst_width: GLsizei, - dst_height: GLsizei, opaque: GLboolean, flip: GLboolean, - filter: GLenum, - clip_x: GLint, - clip_y: GLint, - clip_width: GLsizei, - clip_height: GLsizei, - ); - fn CompositeYUV( - locked_dst: *mut LockedTexture, - locked_y: *mut LockedTexture, - locked_u: *mut LockedTexture, - locked_v: *mut LockedTexture, - color_space: YUVColorSpace, - color_depth: GLuint, - src_x: GLint, - src_y: GLint, - src_width: GLsizei, - src_height: GLsizei, - dst_x: GLint, - dst_y: GLint, - dst_width: GLsizei, - dst_height: GLsizei, - flip: GLboolean, - clip_x: GLint, - clip_y: GLint, - clip_width: GLsizei, - clip_height: GLsizei, ); fn CreateContext() -> *mut c_void; - fn ReferenceContext(ctx: *mut c_void); fn DestroyContext(ctx: *mut c_void); fn MakeCurrent(ctx: *mut c_void); - fn ReportMemory(size_of_op: unsafe extern "C" fn(ptr: *const c_void) -> usize) -> usize; } -#[derive(Clone, Copy)] +#[derive(Clone)] pub struct Context(*mut c_void); impl Context { @@ -327,12 +301,6 @@ impl Context { Context(unsafe { CreateContext() }) } - pub fn reference(&self) { - unsafe { - ReferenceContext(self.0); - } - } - pub fn destroy(&self) { unsafe { DestroyContext(self.0); @@ -345,56 +313,18 @@ impl Context { } } - pub fn init_default_framebuffer( - &self, - x: i32, - y: i32, - width: i32, - height: i32, - stride: i32, - buf: *mut c_void, - ) { + pub fn init_default_framebuffer(&self, width: i32, height: i32) { unsafe { - InitDefaultFramebuffer(x, y, width, height, stride, buf); + InitDefaultFramebuffer(width, height); } } - pub fn get_color_buffer(&self, fbo: GLuint, flush: bool) -> (*mut c_void, i32, i32, i32) { + 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 mut stride: i32 = 0; - let data_ptr = GetColorBuffer( - fbo, - flush as GLboolean, - &mut width, - &mut height, - &mut stride, - ); - (data_ptr, width, height, stride) - } - } - - pub fn resolve_framebuffer(&self, fbo: GLuint) { - unsafe { - ResolveFramebuffer(fbo); - } - } - - pub fn clear_color_rect( - &self, - fbo: GLuint, - xoffset: GLint, - yoffset: GLint, - width: GLsizei, - height: GLsizei, - r: f32, - g: f32, - b: f32, - a: f32, - ) { - unsafe { - ClearColorRect(fbo, xoffset, yoffset, width, height, r, g, b, a); + let data_ptr = GetColorBuffer(fbo, flush as GLboolean, &mut width, &mut height); + (data_ptr, width, height) } } @@ -404,7 +334,6 @@ impl Context { internal_format: GLenum, width: GLsizei, height: GLsizei, - stride: GLsizei, buf: *mut c_void, min_width: GLsizei, min_height: GLsizei, @@ -415,7 +344,6 @@ impl Context { internal_format, width, height, - stride, buf, min_width, min_height, @@ -423,37 +351,32 @@ impl Context { } } - pub fn set_texture_parameter(&self, tex: GLuint, pname: GLenum, param: GLint) { - unsafe { - SetTextureParameter(tex, pname, param); - } - } - - pub fn lock_framebuffer(&self, fbo: GLuint) -> Option<LockedResource> { - unsafe { - let resource = LockFramebuffer(fbo); - if resource != ptr::null_mut() { - Some(LockedResource(resource)) - } else { - None - } - } - } - - pub fn lock_texture(&self, tex: GLuint) -> Option<LockedResource> { + 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 { - let resource = LockTexture(tex); - if resource != ptr::null_mut() { - Some(LockedResource(resource)) - } else { - None - } + Composite( + src_id, + src_x, + src_y, + src_width, + src_height, + dst_x, + dst_y, + opaque as GLboolean, + flip as GLboolean, + ); } } - - pub fn report_memory(size_of_op: unsafe extern "C" fn(ptr: *const c_void) -> usize) -> usize { - unsafe { ReportMemory(size_of_op) } - } } impl From<*mut c_void> for Context { @@ -488,7 +411,6 @@ fn calculate_length(width: GLsizei, height: GLsizei, format: GLenum, pixel_type: UNSIGNED_SHORT => 2, SHORT => 2, FLOAT => 4, - UNSIGNED_INT_8_8_8_8_REV => 1, _ => panic!("unsupported pixel_type for read_pixels: {:?}", pixel_type), }; @@ -563,8 +485,8 @@ impl Gl for Context { 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') { - let name = u[start + PREFIX.len()..start + end].trim(); + if let Some(end) = u[start ..].find('\n') { + let name = u[start + PREFIX.len() .. start + end].trim(); debug!("shader name: {}", name); unsafe { let c_string = CString::new(name).unwrap(); @@ -1033,6 +955,7 @@ impl Gl for Context { panic!(); } + // FIXME: Does not verify buffer size -- unsafe! fn tex_image_3d( &self, target: GLenum, @@ -1046,7 +969,24 @@ impl Gl for Context { ty: GLenum, opt_data: Option<&[u8]>, ) { - panic!(); + 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( @@ -1091,7 +1031,11 @@ impl Gl for Context { width: GLsizei, height: GLsizei, ) { - panic!(); + unsafe { + CopyTexSubImage3D( + target, level, xoffset, yoffset, zoffset, x, y, width, height, + ); + } } fn tex_sub_image_2d( @@ -1173,7 +1117,22 @@ impl Gl for Context { data: &[u8], ) { debug!("tex_sub_image_3d"); - panic!(); + //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( @@ -1190,7 +1149,21 @@ impl Gl for Context { ty: GLenum, offset: usize, ) { - panic!(); + unsafe { + TexSubImage3D( + target, + level, + xoffset, + yoffset, + zoffset, + width, + height, + depth, + format, + ty, + offset as *const c_void, + ); + } } fn tex_storage_2d( @@ -1216,7 +1189,10 @@ impl Gl for Context { height: GLsizei, depth: GLsizei, ) { - panic!(); + //panic!(); + unsafe { + TexStorage3D(target, levels, internal_format, width, height, depth); + } } fn get_tex_image_into_buffer( @@ -1376,7 +1352,10 @@ impl Gl for Context { "framebuffer_texture_layer {} {} {} {} {}", target, attachment, texture, level, layer ); - panic!(); + //panic!(); + unsafe { + FramebufferTextureLayer(target, attachment, texture, level, layer); + } } fn blit_framebuffer( @@ -1498,9 +1477,7 @@ impl Gl for Context { } fn draw_arrays(&self, mode: GLenum, first: GLint, count: GLsizei) { - unsafe { - DrawElementsInstanced(mode, count, NONE, first as GLintptr, 1); - } + panic!(); } fn draw_arrays_instanced( @@ -1510,9 +1487,7 @@ impl Gl for Context { count: GLsizei, primcount: GLsizei, ) { - unsafe { - DrawElementsInstanced(mode, count, NONE, first as GLintptr, primcount); - } + panic!(); } fn draw_elements( @@ -1528,7 +1503,13 @@ impl Gl for Context { ); //panic!(); unsafe { - DrawElementsInstanced(mode, count, element_type, indices_offset as GLintptr, 1); + DrawElementsInstanced( + mode, + count, + element_type, + indices_offset as *const c_void, + 1, + ); } } @@ -1550,7 +1531,7 @@ impl Gl for Context { mode, count, element_type, - indices_offset as GLintptr, + indices_offset as *const c_void, primcount, ); } @@ -1843,8 +1824,8 @@ impl Gl for Context { } fn get_program_info_log(&self, program: GLuint) -> String { - debug!("get_program_info_log {}", program); - String::new() + panic!(); + //String::new() } #[inline] @@ -1854,7 +1835,7 @@ impl Gl for Context { assert!(!result.is_empty()); //#define GL_LINK_STATUS 0x8B82 if pname == 0x8b82 { - result[0] = GetLinkStatus(program); + result[0] = 1; } } @@ -2118,7 +2099,7 @@ impl Gl for Context { //ptr::null() } - fn client_wait_sync(&self, sync: GLsync, flags: GLbitfield, timeout: GLuint64) -> GLenum { + fn client_wait_sync(&self, sync: GLsync, flags: GLbitfield, timeout: GLuint64) { panic!(); } @@ -2191,7 +2172,7 @@ impl Gl for Context { // GL_KHR_blend_equation_advanced fn blend_barrier_khr(&self) { - // No barrier required, so nothing to do + panic!(); } // GL_CHROMIUM_copy_texture @@ -2269,158 +2250,4 @@ impl Gl for Context { ) { unimplemented!("Not supported by SWGL"); } - - fn buffer_storage( - &self, - target: GLenum, - size: GLsizeiptr, - data: *const GLvoid, - flags: GLbitfield, - ) { - unimplemented!("Not supported by SWGL"); - } - - fn flush_mapped_buffer_range(&self, target: GLenum, offset: GLintptr, length: GLsizeiptr) { - unimplemented!("Not supported by SWGL"); - } -} - -/// A resource that is intended for sharing between threads. -/// Locked resources such as textures or framebuffers will -/// not allow any further modifications while it remains -/// locked. The resource will be unlocked when LockedResource -/// is dropped. -pub struct LockedResource(*mut LockedTexture); - -unsafe impl Send for LockedResource {} -unsafe impl Sync for LockedResource {} - -#[repr(C)] -pub enum YUVColorSpace { - Rec601 = 0, - Rec709, - Rec2020, - Identity, -} - -impl LockedResource { - /// Composites from a locked resource to another locked resource. The band - /// offset and height are relative to the destination rectangle and specify - /// how to clip the composition into appropriate range for this band. - pub fn composite( - &self, - locked_src: &LockedResource, - src_x: GLint, - src_y: GLint, - src_width: GLsizei, - src_height: GLsizei, - dst_x: GLint, - dst_y: GLint, - dst_width: GLsizei, - dst_height: GLsizei, - opaque: bool, - flip: bool, - filter: GLenum, - clip_x: GLint, - clip_y: GLint, - clip_width: GLsizei, - clip_height: GLsizei, - ) { - unsafe { - Composite( - self.0, - locked_src.0, - src_x, - src_y, - src_width, - src_height, - dst_x, - dst_y, - dst_width, - dst_height, - opaque as GLboolean, - flip as GLboolean, - filter, - clip_x, - clip_y, - clip_width, - clip_height, - ); - } - } - - /// Composites from locked resources representing YUV planes - pub fn composite_yuv( - &self, - locked_y: &LockedResource, - locked_u: &LockedResource, - locked_v: &LockedResource, - color_space: YUVColorSpace, - color_depth: GLuint, - src_x: GLint, - src_y: GLint, - src_width: GLsizei, - src_height: GLsizei, - dst_x: GLint, - dst_y: GLint, - dst_width: GLsizei, - dst_height: GLsizei, - flip: bool, - clip_x: GLint, - clip_y: GLint, - clip_width: GLsizei, - clip_height: GLsizei, - ) { - unsafe { - CompositeYUV( - self.0, - locked_y.0, - locked_u.0, - locked_v.0, - color_space, - color_depth, - src_x, - src_y, - src_width, - src_height, - dst_x, - dst_y, - dst_width, - dst_height, - flip as GLboolean, - clip_x, - clip_y, - clip_width, - clip_height, - ); - } - } - - /// Get the underlying buffer for a locked resource - pub fn get_buffer(&self) -> (*mut c_void, i32, i32, i32) { - unsafe { - let mut width: i32 = 0; - let mut height: i32 = 0; - let mut stride: i32 = 0; - let data_ptr = GetResourceBuffer(self.0, &mut width, &mut height, &mut stride); - (data_ptr, width, height, stride) - } - } -} - -impl Clone for LockedResource { - fn clone(&self) -> Self { - unsafe { - LockResource(self.0); - } - LockedResource(self.0) - } -} - -impl Drop for LockedResource { - fn drop(&mut self) { - unsafe { - UnlockResource(self.0); - } - } } diff --git a/third_party/webrender/swgl/src/texture.h b/third_party/webrender/swgl/src/texture.h index fdace241eb5..0219d078bcf 100644 --- a/third_party/webrender/swgl/src/texture.h +++ b/third_party/webrender/swgl/src/texture.h @@ -2,884 +2,19 @@ * 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/. */ -namespace glsl { - -using PackedRGBA8 = V16<uint8_t>; -using WideRGBA8 = V16<uint16_t>; -using HalfRGBA8 = V8<uint16_t>; - -SI WideRGBA8 unpack(PackedRGBA8 p) { return CONVERT(p, WideRGBA8); } - -template <int N> -UNUSED SI VectorType<uint8_t, N> genericPackWide(VectorType<uint16_t, N> p) { - typedef VectorType<uint8_t, N> packed_type; - // Generic conversions only mask off the low byte without actually clamping - // like a real pack. First force the word to all 1s if it overflows, and then - // add on the sign bit to cause it to roll over to 0 if it was negative. - p = (p | (p > 255)) + (p >> 15); - return CONVERT(p, packed_type); -} - -SI 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 genericPackWide(p); -#endif -} - -using PackedR8 = V4<uint8_t>; -using WideR8 = V4<uint16_t>; - -SI WideR8 unpack(PackedR8 p) { return CONVERT(p, WideR8); } - -SI PackedR8 pack(WideR8 p) { -#if USE_SSE2 - auto m = expand(p); - auto r = bit_cast<V16<uint8_t>>(_mm_packus_epi16(m, m)); - return SHUFFLE(r, r, 0, 1, 2, 3); -#elif USE_NEON - return lowHalf(bit_cast<V8<uint8_t>>(vqmovn_u16(expand(p)))); -#else - return genericPackWide(p); -#endif -} - -using PackedRG8 = V8<uint8_t>; -using WideRG8 = V8<uint16_t>; - -SI PackedRG8 pack(WideRG8 p) { -#if USE_SSE2 - return lowHalf(bit_cast<V16<uint8_t>>(_mm_packus_epi16(p, p))); -#elif USE_NEON - return bit_cast<V8<uint8_t>>(vqmovn_u16(p)); -#else - return genericPackWide(p); -#endif -} - -SI I32 clampCoord(I32 coord, int limit, int base = 0) { -#if USE_SSE2 - return _mm_min_epi16(_mm_max_epi16(coord, _mm_set1_epi32(base)), - _mm_set1_epi32(limit - 1)); -#else - return clamp(coord, base, limit - 1); -#endif -} - -SI int clampCoord(int coord, int limit, int base = 0) { - return min(max(coord, base), limit - 1); -} - -template <typename T, typename S> -SI T clamp2D(T P, S sampler) { - return T{clampCoord(P.x, sampler->width), clampCoord(P.y, sampler->height)}; -} - -SI float to_float(uint32_t x) { return x * (1.f / 255.f); } - -SI 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); -} - -SI 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}); -} - -SI 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}); -} - -SI 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 <typename S> -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]); -} - -template <typename S> -vec4 texelFetchRGBA8(S sampler, ivec2 P) { - I32 offset = P.x + P.y * sampler->stride; - return fetchOffsetsRGBA8(sampler, offset); -} - -template <typename S> -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); -} - -template <typename S> -vec4 texelFetchR8(S sampler, ivec2 P) { - I32 offset = P.x + P.y * sampler->stride; - return vec4(fetchOffsetsR8(sampler, offset), 0.0f, 0.0f, 1.0f); -} - -template <typename S> -SI vec4 fetchOffsetsRG8(S sampler, I32 offset) { - uint16_t* buf = (uint16_t*)sampler->buf; - U16 pixels = {buf[offset.x], buf[offset.y], buf[offset.z], buf[offset.w]}; - Float r = CONVERT(pixels & 0xFF, Float) * (1.0f / 255.0f); - Float g = CONVERT(pixels >> 8, Float) * (1.0f / 255.0f); - return vec4(r, g, 0.0f, 1.0f); -} - -template <typename S> -vec4 texelFetchRG8(S sampler, ivec2 P) { - I32 offset = P.x + P.y * sampler->stride; - return fetchOffsetsRG8(sampler, offset); -} - template <typename S> -SI Float fetchOffsetsR16(S sampler, I32 offset) { - U32 i = { - ((uint16_t*)sampler->buf)[offset.x], ((uint16_t*)sampler->buf)[offset.y], - ((uint16_t*)sampler->buf)[offset.z], ((uint16_t*)sampler->buf)[offset.w]}; - return cast(i) * (1.0f / 65535.0f); -} - -template <typename S> -vec4 texelFetchR16(S sampler, ivec2 P) { - I32 offset = P.x + P.y * sampler->stride; - return vec4(fetchOffsetsR16(sampler, offset), 0.0f, 0.0f, 1.0f); -} - -template <typename S> -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) { - I32 offset = P.x * 4 + P.y * sampler->stride; - return fetchOffsetsFloat(sampler, offset); -} - -template <typename S> -SI vec4 fetchOffsetsYUV422(S sampler, I32 offset) { - // Layout is 2 pixel chunks (occupying 4 bytes) organized as: G0, B, G1, R. - // Offset is aligned to a chunk rather than a pixel, and selector specifies - // pixel within the chunk. - I32 selector = offset & 1; - offset &= ~1; - uint16_t* buf = (uint16_t*)sampler->buf; - U32 pixels = {*(uint32_t*)&buf[offset.x], *(uint32_t*)&buf[offset.y], - *(uint32_t*)&buf[offset.z], *(uint32_t*)&buf[offset.w]}; - Float b = CONVERT((pixels >> 8) & 0xFF, Float) * (1.0f / 255.0f); - Float r = CONVERT((pixels >> 24), Float) * (1.0f / 255.0f); - Float g = - CONVERT(if_then_else(-selector, pixels >> 16, pixels) & 0xFF, Float) * - (1.0f / 255.0f); - return vec4(r, g, b, 1.0f); -} - -template <typename S> -vec4 texelFetchYUV422(S sampler, ivec2 P) { - I32 offset = P.x + P.y * sampler->stride; - return fetchOffsetsYUV422(sampler, offset); -} - -vec4 texelFetch(sampler2D sampler, ivec2 P, int lod) { - assert(lod == 0); - P = clamp2D(P, sampler); - switch (sampler->format) { - case TextureFormat::RGBA32F: - return texelFetchFloat(sampler, P); - case TextureFormat::RGBA8: - return texelFetchRGBA8(sampler, P); - case TextureFormat::R8: - return texelFetchR8(sampler, P); - case TextureFormat::RG8: - return texelFetchRG8(sampler, P); - case TextureFormat::R16: - return texelFetchR16(sampler, P); - case TextureFormat::YUV422: - return texelFetchYUV422(sampler, P); - default: - assert(false); - return vec4(); - } -} - -vec4 texelFetch(sampler2DRGBA32F sampler, ivec2 P, int lod) { - assert(lod == 0); - P = clamp2D(P, sampler); - assert(sampler->format == TextureFormat::RGBA32F); - return texelFetchFloat(sampler, P); -} - -vec4 texelFetch(sampler2DRGBA8 sampler, ivec2 P, int lod) { - assert(lod == 0); - P = clamp2D(P, sampler); +static PackedRGBA8 textureLinearPackedRGBA8(S sampler, ivec2 i, int zoffset) { assert(sampler->format == TextureFormat::RGBA8); - return texelFetchRGBA8(sampler, P); -} - -vec4 texelFetch(sampler2DR8 sampler, ivec2 P, int lod) { - assert(lod == 0); - P = clamp2D(P, sampler); - assert(sampler->format == TextureFormat::R8); - return texelFetchR8(sampler, P); -} - -vec4 texelFetch(sampler2DRG8 sampler, ivec2 P, int lod) { - assert(lod == 0); - P = clamp2D(P, sampler); - assert(sampler->format == TextureFormat::RG8); - return texelFetchRG8(sampler, P); -} - -vec4_scalar texelFetch(sampler2D sampler, ivec2_scalar P, int lod) { - assert(lod == 0); - 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) { - assert(lod == 0); - 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) { - assert(lod == 0); - 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) { - assert(lod == 0); - 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, 1.0f}; -} - -vec4_scalar texelFetch(sampler2DRG8 sampler, ivec2_scalar P, int lod) { - assert(lod == 0); - P = clamp2D(P, sampler); - assert(sampler->format == TextureFormat::RG8); - uint16_t pixel = ((uint16_t*)sampler->buf)[P.x + P.y * sampler->stride]; - return vec4_scalar{to_float(pixel & 0xFF), to_float(pixel >> 8), 0.0f, 1.0f}; -} - -vec4 texelFetch(sampler2DRect sampler, ivec2 P) { - P = clamp2D(P, sampler); - switch (sampler->format) { - case TextureFormat::RGBA8: - return texelFetchRGBA8(sampler, P); - case TextureFormat::R8: - return texelFetchR8(sampler, P); - case TextureFormat::RG8: - return texelFetchRG8(sampler, P); - case TextureFormat::R16: - return texelFetchR16(sampler, P); - case TextureFormat::YUV422: - return texelFetchYUV422(sampler, P); - default: - assert(false); - return vec4(); - } -} - -template <typename S> -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) { - assert(lod == 0); - 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) { - assert(lod == 0); - 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]; -} - -template <typename S> -SI I32 texelFetchPtr(S sampler, ivec2 P, int min_x, int max_x, int min_y, - int max_y) { - P.x = clampCoord(P.x, int(sampler->width) - max_x, -min_x); - P.y = clampCoord(P.y, int(sampler->height) - max_y, -min_y); - return P.x * 4 + P.y * sampler->stride; -} - -template <typename S, typename P> -SI P texelFetchUnchecked(S sampler, P* ptr, int x, int y = 0) { - return ptr[x + y * (sampler->stride >> 2)]; -} - -SI vec4 texelFetchUnchecked(sampler2D sampler, I32 offset, int x, int y = 0) { - assert(sampler->format == TextureFormat::RGBA32F); - return fetchOffsetsFloat(sampler, offset + (x * 4 + y * sampler->stride)); -} - -SI ivec4 texelFetchUnchecked(isampler2D sampler, I32 offset, int x, int y = 0) { - assert(sampler->format == TextureFormat::RGBA32I); - return fetchOffsetsInt(sampler, offset + (x * 4 + y * sampler->stride)); -} - -#define texelFetchOffset(sampler, P, lod, offset) \ - texelFetch(sampler, (P) + (offset), lod) - -// Scale texture coords for quantization, subtract offset for filtering -// (assuming coords already offset to texel centers), and round to nearest -// 1/scale increment -template <typename T> -SI T linearQuantize(T P, float scale) { - return P * scale + (0.5f - 0.5f * scale); -} - -// Helper version that also scales normalized texture coords for sampler -template <typename T, typename S> -SI T samplerScale(S sampler, T P) { - P.x *= sampler->width; - P.y *= sampler->height; - return P; -} - -template <typename T> -SI T samplerScale(UNUSED sampler2DRect sampler, T P) { - return P; -} - -template <typename T, typename S> -SI T linearQuantize(T P, float scale, S sampler) { - return linearQuantize(samplerScale(sampler, P), scale); -} - -// Compute clamped offset of first row for linear interpolation -template <typename S, typename I> -SI auto computeRow(S sampler, I i, size_t margin = 1) -> decltype(i.x) { - return clampCoord(i.x, sampler->width - margin) + - clampCoord(i.y, sampler->height) * sampler->stride; -} - -// Compute clamped offset of second row for linear interpolation from first row -template <typename S, typename I> -SI auto computeNextRowOffset(S sampler, I i) -> decltype(i.x) { - return if_then_else(i.y >= 0 && i.y < int32_t(sampler->height) - 1, - sampler->stride, 0); -} - -// Convert X coordinate to a 2^7 scale fraction for interpolation -template <typename S> -SI I16 computeFracX(S sampler, ivec2 i, ivec2 frac) { - auto overread = i.x > int32_t(sampler->width) - 2; - return CONVERT((((frac.x & (i.x >= 0)) | overread) & 0x7F) - overread, I16); -} - -// Convert Y coordinate to a 2^7 scale fraction for interpolation -SI I16 computeFracNoClamp(I32 frac) { return CONVERT(frac & 0x7F, I16); } -SI I16 computeFracY(ivec2 frac) { return computeFracNoClamp(frac.y); } - -struct WidePlanarRGBA8 { - V8<uint16_t> rg; - V8<uint16_t> ba; -}; - -template <typename S> -SI WidePlanarRGBA8 textureLinearPlanarRGBA8(S sampler, ivec2 i) { - assert(sampler->format == TextureFormat::RGBA8); - - ivec2 frac = i; - i >>= 7; - - I32 row0 = computeRow(sampler, i); - I32 row1 = row0 + computeNextRowOffset(sampler, i); - I16 fracx = computeFracX(sampler, i, frac); - I16 fracy = computeFracY(frac); - - auto a0 = - CONVERT(unaligned_load<V8<uint8_t>>(&sampler->buf[row0.x]), V8<int16_t>); - auto a1 = - CONVERT(unaligned_load<V8<uint8_t>>(&sampler->buf[row1.x]), V8<int16_t>); - a0 += ((a1 - a0) * fracy.x) >> 7; - - auto b0 = - CONVERT(unaligned_load<V8<uint8_t>>(&sampler->buf[row0.y]), V8<int16_t>); - auto b1 = - CONVERT(unaligned_load<V8<uint8_t>>(&sampler->buf[row1.y]), V8<int16_t>); - 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<V8<uint8_t>>(&sampler->buf[row0.z]), V8<int16_t>); - auto c1 = - CONVERT(unaligned_load<V8<uint8_t>>(&sampler->buf[row1.z]), V8<int16_t>); - c0 += ((c1 - c0) * fracy.z) >> 7; - - auto d0 = - CONVERT(unaligned_load<V8<uint8_t>>(&sampler->buf[row0.w]), V8<int16_t>); - auto d1 = - CONVERT(unaligned_load<V8<uint8_t>>(&sampler->buf[row1.w]), V8<int16_t>); - d0 += ((d1 - d0) * fracy.w) >> 7; - - auto cdl = zipLow(c0, d0); - auto cdh = zipHigh(c0, d0); - cdl += ((cdh - cdl) * fracx.zwzwzwzw) >> 7; - - auto rg = V8<uint16_t>(zip2Low(abl, cdl)); - auto ba = V8<uint16_t>(zip2High(abl, cdl)); - return WidePlanarRGBA8{rg, ba}; -} - -template <typename S> -vec4 textureLinearRGBA8(S sampler, vec2 P) { - ivec2 i(linearQuantize(P, 128, sampler)); - auto planar = textureLinearPlanarRGBA8(sampler, i); - auto rg = CONVERT(planar.rg, V8<float>); - auto ba = CONVERT(planar.ba, V8<float>); - 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); -} - -template <typename S> -static inline U16 textureLinearUnpackedR8(S sampler, ivec2 i) { - assert(sampler->format == TextureFormat::R8); - ivec2 frac = i; - i >>= 7; - - I32 row0 = computeRow(sampler, i); - I32 row1 = row0 + computeNextRowOffset(sampler, i); - I16 fracx = computeFracX(sampler, i, frac); - I16 fracy = computeFracY(frac); - - uint8_t* buf = (uint8_t*)sampler->buf; - auto a0 = unaligned_load<V2<uint8_t>>(&buf[row0.x]); - auto b0 = unaligned_load<V2<uint8_t>>(&buf[row0.y]); - auto c0 = unaligned_load<V2<uint8_t>>(&buf[row0.z]); - auto d0 = unaligned_load<V2<uint8_t>>(&buf[row0.w]); - auto abcd0 = CONVERT(combine(a0, b0, c0, d0), V8<int16_t>); - - auto a1 = unaligned_load<V2<uint8_t>>(&buf[row1.x]); - auto b1 = unaligned_load<V2<uint8_t>>(&buf[row1.y]); - auto c1 = unaligned_load<V2<uint8_t>>(&buf[row1.z]); - auto d1 = unaligned_load<V2<uint8_t>>(&buf[row1.w]); - auto abcd1 = CONVERT(combine(a1, b1, c1, d1), V8<int16_t>); - - 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; - - return U16(abcdl); -} - -template <typename S> -vec4 textureLinearR8(S sampler, vec2 P) { - assert(sampler->format == TextureFormat::R8); - - ivec2 i(linearQuantize(P, 128, sampler)); - Float r = CONVERT(textureLinearUnpackedR8(sampler, i), Float); - return vec4(r * (1.0f / 255.0f), 0.0f, 0.0f, 1.0f); -} - -struct WidePlanarRG8 { - V8<uint16_t> rg; -}; - -template <typename S> -SI WidePlanarRG8 textureLinearPlanarRG8(S sampler, ivec2 i) { - assert(sampler->format == TextureFormat::RG8); - - ivec2 frac = i; - i >>= 7; - - I32 row0 = computeRow(sampler, i); - I32 row1 = row0 + computeNextRowOffset(sampler, i); - I16 fracx = computeFracX(sampler, i, frac); - I16 fracy = computeFracY(frac); - - uint16_t* buf = (uint16_t*)sampler->buf; - - // Load RG bytes for two adjacent pixels - rgRG - auto a0 = unaligned_load<V4<uint8_t>>(&buf[row0.x]); - auto b0 = unaligned_load<V4<uint8_t>>(&buf[row0.y]); - auto ab0 = CONVERT(combine(a0, b0), V8<int16_t>); - // Load two pixels for next row - auto a1 = unaligned_load<V4<uint8_t>>(&buf[row1.x]); - auto b1 = unaligned_load<V4<uint8_t>>(&buf[row1.y]); - auto ab1 = CONVERT(combine(a1, b1), V8<int16_t>); - // Blend rows - ab0 += ((ab1 - ab0) * fracy.xxxxyyyy) >> 7; - - auto c0 = unaligned_load<V4<uint8_t>>(&buf[row0.z]); - auto d0 = unaligned_load<V4<uint8_t>>(&buf[row0.w]); - auto cd0 = CONVERT(combine(c0, d0), V8<int16_t>); - auto c1 = unaligned_load<V4<uint8_t>>(&buf[row1.z]); - auto d1 = unaligned_load<V4<uint8_t>>(&buf[row1.w]); - auto cd1 = CONVERT(combine(c1, d1), V8<int16_t>); - // Blend rows - cd0 += ((cd1 - cd0) * fracy.zzzzwwww) >> 7; - - // ab = a.rgRG,b.rgRG - // cd = c.rgRG,d.rgRG - // ... ac = ar,cr,ag,cg,aR,cR,aG,cG - // ... bd = br,dr,bg,dg,bR,dR,bG,dG - auto ac = zipLow(ab0, cd0); - auto bd = zipHigh(ab0, cd0); - // ar,br,cr,dr,ag,bg,cg,dg - // aR,bR,cR,dR,aG,bG,cG,dG - auto abcdl = zipLow(ac, bd); - auto abcdh = zipHigh(ac, bd); - // Blend columns - abcdl += ((abcdh - abcdl) * fracx.xyzwxyzw) >> 7; - - auto rg = V8<uint16_t>(abcdl); - return WidePlanarRG8{rg}; -} - -template <typename S> -vec4 textureLinearRG8(S sampler, vec2 P) { - ivec2 i(linearQuantize(P, 128, sampler)); - auto planar = textureLinearPlanarRG8(sampler, i); - auto rg = CONVERT(planar.rg, V8<float>) * (1.0f / 255.0f); - auto r = lowHalf(rg); - auto g = highHalf(rg); - return vec4(r, g, 0.0f, 1.0f); -} - -// Samples R16 texture with linear filtering and returns results packed as -// signed I16. One bit of precision is shifted away from the bottom end to -// accommodate the sign bit, so only 15 bits of precision is left. -template <typename S> -static inline I16 textureLinearUnpackedR16(S sampler, ivec2 i) { - assert(sampler->format == TextureFormat::R16); - - ivec2 frac = i; + ivec2 frac = i & 0x7F; i >>= 7; - I32 row0 = computeRow(sampler, i); - I32 row1 = row0 + computeNextRowOffset(sampler, i); - + 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) - 2)) & 0x7F, - I16) - << 8; - I16 fracy = computeFracY(frac) << 8; - - // Sample the 16 bit data for both rows - uint16_t* buf = (uint16_t*)sampler->buf; - auto a0 = unaligned_load<V2<uint16_t>>(&buf[row0.x]); - auto b0 = unaligned_load<V2<uint16_t>>(&buf[row0.y]); - auto c0 = unaligned_load<V2<uint16_t>>(&buf[row0.z]); - auto d0 = unaligned_load<V2<uint16_t>>(&buf[row0.w]); - auto abcd0 = CONVERT(combine(a0, b0, c0, d0) >> 1, V8<int16_t>); - - auto a1 = unaligned_load<V2<uint16_t>>(&buf[row1.x]); - auto b1 = unaligned_load<V2<uint16_t>>(&buf[row1.y]); - auto c1 = unaligned_load<V2<uint16_t>>(&buf[row1.z]); - auto d1 = unaligned_load<V2<uint16_t>>(&buf[row1.w]); - auto abcd1 = CONVERT(combine(a1, b1, c1, d1) >> 1, V8<int16_t>); - - // The samples occupy 15 bits and the fraction occupies 15 bits, so that when - // they are multiplied together, the new scaled sample will fit in the high - // 14 bits of the result. It is left shifted once to make it 15 bits again - // for the final multiply. -#if USE_SSE2 - abcd0 += bit_cast<V8<int16_t>>(_mm_mulhi_epi16(abcd1 - abcd0, fracy.xxyyzzww)) - << 1; -#elif USE_NEON - // NEON has a convenient instruction that does both the multiply and the - // doubling, so doesn't need an extra shift. - abcd0 += bit_cast<V8<int16_t>>(vqrdmulhq_s16(abcd1 - abcd0, fracy.xxyyzzww)); -#else - abcd0 += CONVERT((CONVERT(abcd1 - abcd0, V8<int32_t>) * - CONVERT(fracy.xxyyzzww, V8<int32_t>)) >> - 16, - V8<int16_t>) - << 1; -#endif - - abcd0 = SHUFFLE(abcd0, abcd0, 0, 2, 4, 6, 1, 3, 5, 7); - auto abcdl = lowHalf(abcd0); - auto abcdh = highHalf(abcd0); -#if USE_SSE2 - abcdl += lowHalf(bit_cast<V8<int16_t>>( - _mm_mulhi_epi16(expand(abcdh - abcdl), expand(fracx)))) - << 1; -#elif USE_NEON - abcdl += bit_cast<V4<int16_t>>(vqrdmulh_s16(abcdh - abcdl, fracx)); -#else - abcdl += CONVERT((CONVERT(abcdh - abcdl, V4<int32_t>) * - CONVERT(fracx, V4<int32_t>)) >> - 16, - V4<int16_t>) - << 1; -#endif - - return abcdl; -} - -template <typename S> -vec4 textureLinearR16(S sampler, vec2 P) { - assert(sampler->format == TextureFormat::R16); - - ivec2 i(linearQuantize(P, 128, sampler)); - Float r = CONVERT(textureLinearUnpackedR16(sampler, i), Float); - return vec4(r * (1.0f / 32767.0f), 0.0f, 0.0f, 1.0f); -} - -using PackedRGBA32F = V16<float>; -using WideRGBA32F = V16<float>; - -template <typename S> -vec4 textureLinearRGBA32F(S sampler, vec2 P) { - assert(sampler->format == TextureFormat::RGBA32F); - P = samplerScale(sampler, P); - P -= 0.5f; - vec2 f = floor(P); - vec2 r = P - f; - ivec2 i(f); - ivec2 c(clampCoord(i.x, sampler->width - 1), - clampCoord(i.y, sampler->height)); - r.x = if_then_else(i.x >= 0, if_then_else(i.x < sampler->width - 1, r.x, 1.0), - 0.0f); - I32 offset0 = c.x * 4 + c.y * sampler->stride; - I32 offset1 = offset0 + computeNextRowOffset(sampler, i); - - 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); -} - -struct WidePlanarYUV8 { - U16 y; - U16 u; - U16 v; -}; - -template <typename S> -SI WidePlanarYUV8 textureLinearPlanarYUV422(S sampler, ivec2 i) { - assert(sampler->format == TextureFormat::YUV422); - - ivec2 frac = i; - i >>= 7; - - I32 row0 = computeRow(sampler, i, 2); - // Layout is 2 pixel chunks (occupying 4 bytes) organized as: G0, B, G1, R. - // Get the selector for the pixel within the chunk. - I32 selector = row0 & 1; - // Align the row index to the chunk. - row0 &= ~1; - I32 row1 = row0 + computeNextRowOffset(sampler, i); - // G only needs to be clamped to a pixel boundary for safe interpolation, - // whereas the BR fraction needs to be clamped 1 extra pixel inside to a chunk - // boundary. - frac.x &= (i.x >= 0); - auto fracx = - CONVERT(combine(frac.x | (i.x > int32_t(sampler->width) - 3), - (frac.x >> 1) | (i.x > int32_t(sampler->width) - 3)) & - 0x7F, - V8<int16_t>); - I16 fracy = computeFracY(frac); - - uint16_t* buf = (uint16_t*)sampler->buf; - - // Load bytes for two adjacent chunks - g0,b,g1,r,G0,B,G1,R - // We always need to interpolate between (b,r) and (B,R). - // Depending on selector we need to either interpolate between g0 and g1 - // or between g1 and G0. So for now we just interpolate both cases for g - // and will select the appropriate one on output. - auto a0 = CONVERT(unaligned_load<V8<uint8_t>>(&buf[row0.x]), V8<int16_t>); - auto a1 = CONVERT(unaligned_load<V8<uint8_t>>(&buf[row1.x]), V8<int16_t>); - // Combine with next row. - a0 += ((a1 - a0) * fracy.x) >> 7; - - auto b0 = CONVERT(unaligned_load<V8<uint8_t>>(&buf[row0.y]), V8<int16_t>); - auto b1 = CONVERT(unaligned_load<V8<uint8_t>>(&buf[row1.y]), V8<int16_t>); - b0 += ((b1 - b0) * fracy.y) >> 7; - - auto c0 = CONVERT(unaligned_load<V8<uint8_t>>(&buf[row0.z]), V8<int16_t>); - auto c1 = CONVERT(unaligned_load<V8<uint8_t>>(&buf[row1.z]), V8<int16_t>); - c0 += ((c1 - c0) * fracy.z) >> 7; - - auto d0 = CONVERT(unaligned_load<V8<uint8_t>>(&buf[row0.w]), V8<int16_t>); - auto d1 = CONVERT(unaligned_load<V8<uint8_t>>(&buf[row1.w]), V8<int16_t>); - d0 += ((d1 - d0) * fracy.w) >> 7; - - // Shuffle things around so we end up with g0,g0,g0,g0,b,b,b,b and - // g1,g1,g1,g1,r,r,r,r. - auto abl = zipLow(a0, b0); - auto cdl = zipLow(c0, d0); - auto g0b = zip2Low(abl, cdl); - auto g1r = zip2High(abl, cdl); - - // Need to zip g1,B,G0,R. Instead of using a bunch of complicated masks and - // and shifts, just shuffle here instead... We finally end up with - // g1,g1,g1,g1,B,B,B,B and G0,G0,G0,G0,R,R,R,R. - auto abh = SHUFFLE(a0, b0, 2, 10, 5, 13, 4, 12, 7, 15); - auto cdh = SHUFFLE(c0, d0, 2, 10, 5, 13, 4, 12, 7, 15); - auto g1B = zip2Low(abh, cdh); - auto G0R = zip2High(abh, cdh); - - // Finally interpolate between adjacent columns. - g0b += ((g1B - g0b) * fracx) >> 7; - g1r += ((G0R - g1r) * fracx) >> 7; - - // Choose either g0 or g1 based on selector. - return WidePlanarYUV8{ - U16(if_then_else(CONVERT(-selector, I16), lowHalf(g1r), lowHalf(g0b))), - U16(highHalf(g0b)), U16(highHalf(g1r))}; -} - -template <typename S> -vec4 textureLinearYUV422(S sampler, vec2 P) { - ivec2 i(linearQuantize(P, 128, sampler)); - auto planar = textureLinearPlanarYUV422(sampler, i); - auto y = CONVERT(planar.y, Float) * (1.0f / 255.0f); - auto u = CONVERT(planar.u, Float) * (1.0f / 255.0f); - auto v = CONVERT(planar.v, Float) * (1.0f / 255.0f); - return vec4(v, y, u, 1.0f); -} - -SI vec4 texture(sampler2D sampler, vec2 P) { - if (sampler->filter == TextureFilter::LINEAR) { - switch (sampler->format) { - case TextureFormat::RGBA32F: - return textureLinearRGBA32F(sampler, P); - case TextureFormat::RGBA8: - return textureLinearRGBA8(sampler, P); - case TextureFormat::R8: - return textureLinearR8(sampler, P); - case TextureFormat::RG8: - return textureLinearRG8(sampler, P); - case TextureFormat::R16: - return textureLinearR16(sampler, P); - case TextureFormat::YUV422: - return textureLinearYUV422(sampler, P); - default: - assert(false); - return vec4(); - } - } else { - ivec2 coord(roundzero(P.x, sampler->width), - roundzero(P.y, sampler->height)); - return texelFetch(sampler, coord, 0); - } -} - -vec4 texture(sampler2DRect sampler, vec2 P) { - if (sampler->filter == TextureFilter::LINEAR) { - switch (sampler->format) { - case TextureFormat::RGBA8: - return textureLinearRGBA8(sampler, P); - case TextureFormat::R8: - return textureLinearR8(sampler, P); - case TextureFormat::RG8: - return textureLinearRG8(sampler, P); - case TextureFormat::R16: - return textureLinearR16(sampler, P); - case TextureFormat::YUV422: - return textureLinearYUV422(sampler, P); - default: - assert(false); - return vec4(); - } - } else { - ivec2 coord(roundzero(P.x, 1.0f), roundzero(P.y, 1.0f)); - return texelFetch(sampler, coord); - } -} - -template <typename S> -vec4_scalar texture(S sampler, vec2_scalar P) { - return force_scalar(texture(sampler, vec2(P))); -} - -ivec2_scalar textureSize(sampler2D sampler, int) { - return ivec2_scalar{int32_t(sampler->width), int32_t(sampler->height)}; -} - -ivec2_scalar textureSize(sampler2DRect sampler) { - return ivec2_scalar{int32_t(sampler->width), int32_t(sampler->height)}; -} - -template <typename S> -static WideRGBA8 textureLinearUnpackedRGBA8(S sampler, ivec2 i) { - assert(sampler->format == TextureFormat::RGBA8); - ivec2 frac = i; - i >>= 7; - - I32 row0 = computeRow(sampler, i); - I32 row1 = row0 + computeNextRowOffset(sampler, i); - I16 fracx = computeFracX(sampler, i, frac); - I16 fracy = computeFracY(frac); + 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<V8<uint8_t>>(&sampler->buf[row0.x]), V8<int16_t>); @@ -913,233 +48,80 @@ static WideRGBA8 textureLinearUnpackedRGBA8(S sampler, ivec2 i) { auto cdh = combine(highHalf(c0), highHalf(d0)); cdl += ((cdh - cdl) * fracx.zzzzwwww) >> 7; - return combine(HalfRGBA8(abl), HalfRGBA8(cdl)); + return pack(combine(HalfRGBA8(abl), HalfRGBA8(cdl))); } template <typename S> -static PackedRGBA8 textureLinearPackedRGBA8(S sampler, ivec2 i) { - return pack(textureLinearUnpackedRGBA8(sampler, i)); +static inline void textureLinearCommit4(S sampler, ivec2 i, int zoffset, + uint32_t* buf) { + commit_span(buf, textureLinearPackedRGBA8(sampler, i, zoffset)); } template <typename S> -static PackedRGBA8 textureNearestPackedRGBA8(S sampler, ivec2 i) { +static void textureLinearCommit8(S sampler, ivec2_scalar i, int zoffset, + uint32_t* buf) { assert(sampler->format == TextureFormat::RGBA8); - I32 row = computeRow(sampler, i, 0); - return combine(unaligned_load<V4<uint8_t>>(&sampler->buf[row.x]), - unaligned_load<V4<uint8_t>>(&sampler->buf[row.y]), - unaligned_load<V4<uint8_t>>(&sampler->buf[row.z]), - unaligned_load<V4<uint8_t>>(&sampler->buf[row.w])); -} - -template <typename S> -static PackedR8 textureLinearPackedR8(S sampler, ivec2 i) { - return pack(textureLinearUnpackedR8(sampler, i)); -} - -template <typename S> -static WideRG8 textureLinearUnpackedRG8(S sampler, ivec2 i) { - assert(sampler->format == TextureFormat::RG8); - ivec2 frac = i & 0x7F; + ivec2_scalar frac = i & 0x7F; i >>= 7; - I32 row0 = computeRow(sampler, i); - I32 row1 = row0 + computeNextRowOffset(sampler, i); - I16 fracx = computeFracX(sampler, i, frac); - I16 fracy = computeFracY(frac); - - uint16_t* buf = (uint16_t*)sampler->buf; - - // Load RG bytes for two adjacent pixels - rgRG - auto a0 = unaligned_load<V4<uint8_t>>(&buf[row0.x]); - auto b0 = unaligned_load<V4<uint8_t>>(&buf[row0.y]); - auto ab0 = CONVERT(combine(a0, b0), V8<int16_t>); - // Load two pixels for next row - auto a1 = unaligned_load<V4<uint8_t>>(&buf[row1.x]); - auto b1 = unaligned_load<V4<uint8_t>>(&buf[row1.y]); - auto ab1 = CONVERT(combine(a1, b1), V8<int16_t>); - // Blend rows - ab0 += ((ab1 - ab0) * fracy.xxxxyyyy) >> 7; - - auto c0 = unaligned_load<V4<uint8_t>>(&buf[row0.z]); - auto d0 = unaligned_load<V4<uint8_t>>(&buf[row0.w]); - auto cd0 = CONVERT(combine(c0, d0), V8<int16_t>); - auto c1 = unaligned_load<V4<uint8_t>>(&buf[row1.z]); - auto d1 = unaligned_load<V4<uint8_t>>(&buf[row1.w]); - auto cd1 = CONVERT(combine(c1, d1), V8<int16_t>); - // Blend rows - cd0 += ((cd1 - cd0) * fracy.zzzzwwww) >> 7; - - // ab = a.rgRG,b.rgRG - // cd = c.rgRG,d.rgRG - // ... ac = a.rg,c.rg,a.RG,c.RG - // ... bd = b.rg,d.rg,b.RG,d.RG - auto ac = zip2Low(ab0, cd0); - auto bd = zip2High(ab0, cd0); - // a.rg,b.rg,c.rg,d.rg - // a.RG,b.RG,c.RG,d.RG - auto abcdl = zip2Low(ac, bd); - auto abcdh = zip2High(ac, bd); - // Blend columns - abcdl += ((abcdh - abcdl) * fracx.xxyyzzww) >> 7; - - return WideRG8(abcdl); -} - -template <typename S> -static PackedRG8 textureLinearPackedRG8(S sampler, ivec2 i) { - return pack(textureLinearUnpackedRG8(sampler, i)); -} - -template <int N> -static ALWAYS_INLINE VectorType<uint16_t, N> addsat(VectorType<uint16_t, N> x, - VectorType<uint16_t, N> y) { - auto r = x + y; - return r | (r < x); -} - -template <typename P, typename S> -static VectorType<uint16_t, 4 * sizeof(P)> gaussianBlurHorizontal( - S sampler, const ivec2_scalar& i, int minX, int maxX, int radius, - float coeff, float coeffStep) { - // Packed and unpacked vectors for a chunk of the given pixel type. - typedef VectorType<uint8_t, 4 * sizeof(P)> packed_type; - typedef VectorType<uint16_t, 4 * sizeof(P)> unpacked_type; - - // Pre-scale the coefficient by 8 bits of fractional precision, so that when - // the sample is multiplied by it, it will yield a 16 bit unsigned integer - // that will use all 16 bits of precision to accumulate the sum. - coeff *= 1 << 8; - float coeffStep2 = coeffStep * coeffStep; - - int row = computeRow(sampler, i); - P* buf = (P*)sampler->buf; - auto pixelsRight = unaligned_load<V4<P>>(&buf[row]); - auto pixelsLeft = pixelsRight; - auto sum = CONVERT(bit_cast<packed_type>(pixelsRight), unpacked_type) * - uint16_t(coeff + 0.5f); - - // Here we use some trickery to reuse the pixels within a chunk, shifted over - // by one pixel, to get the next sample for the entire chunk. This allows us - // to sample only one pixel for each offset across the entire chunk in both - // the left and right directions. To avoid clamping within the loop to the - // texture bounds, we compute the valid radius that doesn't require clamping - // and fall back to a slower clamping loop outside of that valid radius. - int offset = 1; - // The left bound is how much we can offset the sample before the start of - // the row bounds. - int leftBound = i.x - max(minX, 0); - // The right bound is how much we can offset the sample before the end of the - // row bounds. - int rightBound = min(maxX, sampler->width - 1) - i.x; - int validRadius = min(radius, min(leftBound, rightBound - (4 - 1))); - for (; offset <= validRadius; offset++) { - // Overwrite the pixel that needs to be shifted out with the new pixel, and - // shift it into the correct location. - pixelsRight.x = unaligned_load<P>(&buf[row + offset + 4 - 1]); - pixelsRight = pixelsRight.yzwx; - pixelsLeft = pixelsLeft.wxyz; - pixelsLeft.x = unaligned_load<P>(&buf[row - offset]); - - // Accumulate the Gaussian coefficients step-wise. - coeff *= coeffStep; - coeffStep *= coeffStep2; - - // Both left and right samples at this offset use the same coefficient. - sum = addsat(sum, - (CONVERT(bit_cast<packed_type>(pixelsRight), unpacked_type) + - CONVERT(bit_cast<packed_type>(pixelsLeft), unpacked_type)) * - uint16_t(coeff + 0.5f)); - } - - for (; offset <= radius; offset++) { - pixelsRight.x = - unaligned_load<P>(&buf[row + min(offset + 4 - 1, rightBound)]); - pixelsRight = pixelsRight.yzwx; - pixelsLeft = pixelsLeft.wxyz; - pixelsLeft.x = unaligned_load<P>(&buf[row - min(offset, leftBound)]); - - coeff *= coeffStep; - coeffStep *= coeffStep2; - - sum = addsat(sum, - (CONVERT(bit_cast<packed_type>(pixelsRight), unpacked_type) + - CONVERT(bit_cast<packed_type>(pixelsLeft), unpacked_type)) * - uint16_t(coeff + 0.5f)); - } - - // Shift away the intermediate precision. - return sum >> 8; -} - -template <typename P, typename S> -static VectorType<uint16_t, 4 * sizeof(P)> gaussianBlurVertical( - S sampler, const ivec2_scalar& i, int minY, int maxY, int radius, - float coeff, float coeffStep) { - // Packed and unpacked vectors for a chunk of the given pixel type. - typedef VectorType<uint8_t, 4 * sizeof(P)> packed_type; - typedef VectorType<uint16_t, 4 * sizeof(P)> unpacked_type; - - // Pre-scale the coefficient by 8 bits of fractional precision, so that when - // the sample is multiplied by it, it will yield a 16 bit unsigned integer - // that will use all 16 bits of precision to accumulate the sum. - coeff *= 1 << 8; - float coeffStep2 = coeffStep * coeffStep; - - int rowAbove = computeRow(sampler, i); - int rowBelow = rowAbove; - P* buf = (P*)sampler->buf; - auto pixels = unaligned_load<V4<P>>(&buf[rowAbove]); - auto sum = CONVERT(bit_cast<packed_type>(pixels), unpacked_type) * - uint16_t(coeff + 0.5f); - - // For the vertical loop we can't be quite as creative with reusing old values - // as we were in the horizontal loop. We just do the obvious implementation of - // loading a chunk from each row in turn and accumulating it into the sum. We - // compute a valid radius within which we don't need to clamp the sampled row - // and use that to avoid any clamping in the main inner loop. We fall back to - // a slower clamping loop outside of that valid radius. - int offset = 1; - int belowBound = i.y - max(minY, 0); - int aboveBound = min(maxY, sampler->height - 1) - i.y; - int validRadius = min(radius, min(belowBound, aboveBound)); - for (; offset <= validRadius; offset++) { - rowAbove += sampler->stride; - rowBelow -= sampler->stride; - auto pixelsAbove = unaligned_load<V4<P>>(&buf[rowAbove]); - auto pixelsBelow = unaligned_load<V4<P>>(&buf[rowBelow]); - - // Accumulate the Gaussian coefficients step-wise. - coeff *= coeffStep; - coeffStep *= coeffStep2; - - // Both above and below samples at this offset use the same coefficient. - sum = addsat(sum, - (CONVERT(bit_cast<packed_type>(pixelsAbove), unpacked_type) + - CONVERT(bit_cast<packed_type>(pixelsBelow), unpacked_type)) * - uint16_t(coeff + 0.5f)); + 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<U32>(row0); + U32 pix0n = unaligned_load<U32>(row0 + 4); + uint32_t pix0x = row0[8]; + U32 pix1 = unaligned_load<U32>(row1); + U32 pix1n = unaligned_load<U32>(row1 + 4); + uint32_t pix1x = row1[8]; + + { + auto ab0 = CONVERT(bit_cast<V16<uint8_t>>(SHUFFLE(pix0, pix0, 0, 1, 1, 2)), + V16<int16_t>); + auto ab1 = CONVERT(bit_cast<V16<uint8_t>>(SHUFFLE(pix1, pix1, 0, 1, 1, 2)), + V16<int16_t>); + ab0 += ((ab1 - ab0) * fracy) >> 7; + + auto cd0 = CONVERT(bit_cast<V16<uint8_t>>(SHUFFLE(pix0, pix0n, 2, 3, 3, 4)), + V16<int16_t>); + auto cd1 = CONVERT(bit_cast<V16<uint8_t>>(SHUFFLE(pix1, pix1n, 2, 3, 3, 4)), + V16<int16_t>); + 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))); } - for (; offset <= radius; offset++) { - if (offset <= aboveBound) { - rowAbove += sampler->stride; - } - if (offset <= belowBound) { - rowBelow -= sampler->stride; - } - auto pixelsAbove = unaligned_load<V4<P>>(&buf[rowAbove]); - auto pixelsBelow = unaligned_load<V4<P>>(&buf[rowBelow]); - - coeff *= coeffStep; - coeffStep *= coeffStep2; - - sum = addsat(sum, - (CONVERT(bit_cast<packed_type>(pixelsAbove), unpacked_type) + - CONVERT(bit_cast<packed_type>(pixelsBelow), unpacked_type)) * - uint16_t(coeff + 0.5f)); + { + auto ab0 = + CONVERT(bit_cast<V16<uint8_t>>(SHUFFLE(pix0n, pix0n, 0, 1, 1, 2)), + V16<int16_t>); + auto ab1 = + CONVERT(bit_cast<V16<uint8_t>>(SHUFFLE(pix1n, pix1n, 0, 1, 1, 2)), + V16<int16_t>); + ab0 += ((ab1 - ab0) * fracy) >> 7; + + auto cd0 = + CONVERT(bit_cast<V16<uint8_t>>(SHUFFLE(pix0n, U32(pix0x), 2, 3, 3, 4)), + V16<int16_t>); + auto cd1 = + CONVERT(bit_cast<V16<uint8_t>>(SHUFFLE(pix1n, U32(pix1x), 2, 3, 3, 4)), + V16<int16_t>); + 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))); } - - // Shift away the intermediate precision. - return sum >> 8; } - -} // namespace glsl diff --git a/third_party/webrender/swgl/src/vector_type.h b/third_party/webrender/swgl/src/vector_type.h index 43364ffcce2..8ec5876c340 100644 --- a/third_party/webrender/swgl/src/vector_type.h +++ b/third_party/webrender/swgl/src/vector_type.h @@ -39,16 +39,6 @@ SI VectorType<T, 16> combine(VectorType<T, 8> a, VectorType<T, 8> b) { } template <typename T> -SI VectorType<T, 2> lowHalf(VectorType<T, 4> a) { - return __builtin_shufflevector(a, a, 0, 1); -} - -template <typename T> -SI VectorType<T, 2> highHalf(VectorType<T, 4> a) { - return __builtin_shufflevector(a, a, 2, 3); -} - -template <typename T> SI VectorType<T, 4> lowHalf(VectorType<T, 8> a) { return __builtin_shufflevector(a, a, 0, 1, 2, 3); } @@ -114,7 +104,7 @@ struct VectorType { }; }; - VectorType() : data{0} {} + VectorType() : data{0} { } constexpr VectorType(const VectorType& rhs) : data(rhs.data) {} // GCC vector extensions only support broadcasting scalars on arithmetic ops, @@ -315,27 +305,10 @@ struct VectorType { return VectorType<T, N * 2>::wrap(data, high.data); } -# define xxxx swizzle(0, 0, 0, 0) -# define yyyy swizzle(1, 1, 1, 1) -# define zzzz swizzle(2, 2, 2, 2) -# define wwww swizzle(3, 3, 3, 3) -# define xxyy swizzle(0, 0, 1, 1) -# define xxzz swizzle(0, 0, 2, 2) -# define yyww swizzle(1, 1, 3, 3) -# define zzww swizzle(2, 2, 3, 3) # define xyxy swizzle(0, 1, 0, 1) -# define xzxz swizzle(0, 2, 0, 2) -# define ywyw swizzle(1, 3, 1, 3) # define zwzw swizzle(2, 3, 2, 3) -# define zwxy swizzle(2, 3, 0, 1) # define zyxw swizzle(2, 1, 0, 3) -# define xxyz swizzle(0, 0, 1, 2) -# define xyyz swizzle(0, 1, 1, 2) # define xyzz swizzle(0, 1, 2, 2) -# define xzyw swizzle(0, 2, 1, 3) -# define yzwx swizzle(1, 2, 3, 0) -# define wxyz swizzle(3, 0, 1, 2) -# define wzyx swizzle(3, 2, 1, 0) # define xxxxyyyy XXXXYYYY() VectorType<T, 8> XXXXYYYY() const { return swizzle(0, 0, 0, 0).combine(swizzle(1, 1, 1, 1)); @@ -358,10 +331,6 @@ struct VectorType { VectorType<T, 8> XXYYZZWW() const { return swizzle(0, 0, 1, 1).combine(swizzle(2, 2, 3, 3)); } -# define xxxxyyyyzzzzwwww XXXXYYYYZZZZWWWW() - VectorType<T, 16> XXXXYYYYZZZZWWWW() { - return XXXXYYYY().combine(ZZZZWWWW()); - } }; template <typename T> @@ -374,17 +343,6 @@ struct VectorType<T, 2> { }; T elements[2]; }; - - SI VectorType wrap(const data_type& data) { - VectorType v; - v.data = data; - return v; - } - - 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); } }; # define CONVERT(vector, type) ((type)(vector)) @@ -411,32 +369,6 @@ SI VectorType<T, N * 2> expand(VectorType<T, N> a) { } #endif -template <typename T, int N> -SI VectorType<T, N * 4> combine(VectorType<T, N> a, VectorType<T, N> b, - VectorType<T, N> c, VectorType<T, N> d) { - return combine(combine(a, b), combine(c, d)); -} - -template <typename T, int N> -SI VectorType<T, N> combineLow(VectorType<T, N> a, VectorType<T, N> b) { - return combine(lowHalf(a), lowHalf(b)); -} - -template <typename T, int N> -SI VectorType<T, N> combineHigh(VectorType<T, N> a, VectorType<T, N> b) { - return combine(highHalf(a), highHalf(b)); -} - -template <typename T, int N> -SI VectorType<T, N * 2> repeat2(VectorType<T, N> a) { - return combine(a, a); -} - -template <typename T, int N> -SI VectorType<T, N * 4> repeat4(VectorType<T, N> a) { - return combine(a, a, a, a); -} - template <typename T> SI VectorType<T, 4> zipLow(VectorType<T, 4> a, VectorType<T, 4> b) { return SHUFFLE(a, b, 0, 4, 1, 5); @@ -478,23 +410,6 @@ SI VectorType<T, 8> zip2High(VectorType<T, 8> a, VectorType<T, 8> b) { return SHUFFLE(a, b, 4, 5, 12, 13, 6, 7, 14, 15); } -#ifdef __clang__ -template <typename T> -SI VectorType<T, 8> zip(VectorType<T, 4> a, VectorType<T, 4> b) { - return SHUFFLE(a, b, 0, 4, 1, 5, 2, 6, 3, 7); -} - -template <typename T> -SI VectorType<T, 16> zip(VectorType<T, 8> a, VectorType<T, 8> b) { - return SHUFFLE(a, b, 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15); -} -#else -template <typename T, int N> -SI VectorType<T, N * 2> zip(VectorType<T, N> a, VectorType<T, N> b) { - return combine(zipLow(a, b), zipHigh(a, b)); -} -#endif - template <typename T> struct Unaligned { template <typename P> diff --git a/third_party/webrender/tileview/Cargo.toml b/third_party/webrender/tileview/Cargo.toml index 644f908a8c0..66e955d5a34 100644 --- a/third_party/webrender/tileview/Cargo.toml +++ b/third_party/webrender/tileview/Cargo.toml @@ -8,8 +8,8 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ron = "0.6.2" +ron = "0.5" serde = {version = "1.0.88", features = ["derive"] } -webrender = {path = "../webrender", features=["capture","replay","png","profiler","no_static_freetype", "leak_checks"]} +webrender = {path = "../webrender", features=["capture","replay","debugger","png","profiler","no_static_freetype", "leak_checks"]} webrender_api = {path = "../webrender_api", features=["serialize","deserialize"]} euclid = { version = "0.22.0", features = ["serde"] } diff --git a/third_party/webrender/tileview/src/main.rs b/third_party/webrender/tileview/src/main.rs index d3290260cae..0e33d15e426 100644 --- a/third_party/webrender/tileview/src/main.rs +++ b/third_party/webrender/tileview/src/main.rs @@ -36,8 +36,7 @@ use std::io::prelude::*; use std::path::Path; use std::ffi::OsString; use std::collections::HashMap; -use webrender::enumerate_interners; -use webrender::api::ColorF; +use webrender::api::{enumerate_interners, ColorF}; use euclid::{Rect, Transform3D}; use webrender_api::units::{PicturePoint, PictureSize, PicturePixel, WorldPixel}; @@ -60,7 +59,6 @@ static CSS_PRIM_COUNT: &str = "fill:#40f0f0;fill-opacity:0.1;"; static CSS_CONTENT: &str = "fill:#f04040;fill-opacity:0.1;"; static CSS_COMPOSITOR_KIND_CHANGED: &str = "fill:#f0c070;fill-opacity:0.1;"; static CSS_VALID_RECT_CHANGED: &str = "fill:#ff00ff;fill-opacity:0.1;"; -static CSS_SCALE_CHANGED: &str = "fill:#ff80ff;fill-opacity:0.1;"; // parameters to tweak the SVG generation struct SvgSettings { @@ -113,7 +111,6 @@ fn tile_to_svg(key: TileOffset, Some(InvalidationReason::CompositorKindChanged) => CSS_COMPOSITOR_KIND_CHANGED.to_string(), Some(InvalidationReason::Content { .. } ) => CSS_CONTENT.to_string(), Some(InvalidationReason::ValidRectChanged) => CSS_VALID_RECT_CHANGED.to_string(), - Some(InvalidationReason::ScaleChanged) => CSS_SCALE_CHANGED.to_string(), None => { let mut background = tile.background_color; if background.is_none() { diff --git a/third_party/webrender/webrender/Cargo.toml b/third_party/webrender/webrender/Cargo.toml index e82ac857181..6e3389a309b 100644 --- a/third_party/webrender/webrender/Cargo.toml +++ b/third_party/webrender/webrender/Cargo.toml @@ -12,28 +12,30 @@ edition = "2018" default = ["freetype-lib"] freetype-lib = ["freetype/freetype-sys"] profiler = ["tracy-rs/enable_profiler"] -capture = ["api/serialize", "ron", "serde", "smallvec/serde", "etagere/serialization"] -replay = ["api/deserialize", "ron", "serde", "smallvec/serde", "etagere/serialization"] +debugger = ["ws", "serde_json", "serde", "image_loader", "base64"] +capture = ["api/serialize", "ron", "serde", "smallvec/serde"] +replay = ["api/deserialize", "ron", "serde", "smallvec/serde"] display_list_stats = ["api/display_list_stats"] serialize_program = ["serde", "webrender_build/serialize_program"] no_static_freetype = [] leak_checks = [] -gecko = [] -sw_compositor = ["swgl"] [build-dependencies] -build-parallel = "0.1.2" -glslopt = "0.1.9" +build-parallel = "0.1.1" +glslopt = "0.1.2" webrender_build = { version = "0.0.1", path = "../webrender_build" } [dependencies] +base64 = { optional = true, version = "0.10" } bincode = "1.0" bitflags = "1.2" byteorder = "1.0" -cstr = "0.2" +cfg-if = "0.1.2" +cstr = "0.1.2" euclid = { version = "0.22.0", features = ["serde"] } fxhash = "0.2.1" -gleam = "0.15" +gleam = "0.12.1" +image_loader = { optional = true, version = "0.23", package = "image", default-features = false, features = ["png"] } lazy_static = "1" log = "0.4" malloc_size_of_derive = "0.1" @@ -41,23 +43,27 @@ num-traits = "0.2" plane-split = "0.17" png = { optional = true, version = "0.16" } rayon = "1" -ron = { optional = true, version = "0.6.2" } +ron = { optional = true, version = "0.5" } serde = { optional = true, version = "1.0", features = ["serde_derive"] } +serde_json = { optional = true, version = "1.0" } smallvec = "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 = "0.1.2" -derive_more = "0.99" -etagere = "0.2.4" -swgl = { path = "../swgl", optional = true } +tracy-rs = { version = "0.1" } [dev-dependencies] -mozangle = "0.3.3" +mozangle = "0.3.1" rand = "0.4" +[target.'cfg(any(target_os = "macos", target_os = "linux"))'.build-dependencies] +backtrace = "0.3" +sig = "1.0" +libc = "0.2" + [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies] freetype = { version = "0.7", default-features = false } libc = "0.2" @@ -69,4 +75,3 @@ dwrote = "0.11" core-foundation = "0.9" core-graphics = "0.22" core-text = { version = "19", default-features = false } -objc = "0.2" diff --git a/third_party/webrender/webrender/backtrace.rs b/third_party/webrender/webrender/backtrace.rs new file mode 100644 index 00000000000..aa6bb6b3297 --- /dev/null +++ b/third_party/webrender/webrender/backtrace.rs @@ -0,0 +1,103 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +//! Similar to `println!("{:?}", Backtrace::new())`, but doesn’t allocate. +//! +//! Seems to fix some deadlocks: https://github.com/servo/servo/issues/24881 +//! +//! FIXME: if/when a future version of the `backtrace` crate has +//! https://github.com/rust-lang/backtrace-rs/pull/265, use that instead. + +use std::fmt::{self, Write}; +use backtrace::{BytesOrWideString, PrintFmt}; + +#[inline(never)] +pub(crate) fn print(w: &mut dyn std::io::Write) -> Result<(), std::io::Error> { + write!(w, "{:?}", Print { + print_fn_address: print as usize, + }) +} + +struct Print { + print_fn_address: usize, +} + +impl fmt::Debug for Print { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + // Safety: we’re in a signal handler that is about to call `libc::_exit`. + // Potential data races from using `*_unsynchronized` functions are perhaps + // less bad than potential deadlocks? + unsafe { + let mut print_fn_frame = 0; + let mut frame_count = 0; + backtrace::trace_unsynchronized(|frame| { + let found = frame.symbol_address() as usize == self.print_fn_address; + if found { + print_fn_frame = frame_count; + } + frame_count += 1; + !found + }); + + let mode = PrintFmt::Short; + let mut p = print_path; + let mut f = backtrace::BacktraceFmt::new(fmt, mode, &mut p); + f.add_context()?; + let mut result = Ok(()); + let mut frame_count = 0; + backtrace::trace_unsynchronized(|frame| { + let skip = frame_count < print_fn_frame; + frame_count += 1; + if skip { + return true + } + + let mut frame_fmt = f.frame(); + let mut any_symbol = false; + backtrace::resolve_frame_unsynchronized(frame, |symbol| { + any_symbol = true; + if let Err(e) = frame_fmt.symbol(frame, symbol) { + result = Err(e) + } + }); + if !any_symbol { + if let Err(e) = frame_fmt.print_raw(frame.ip(), None, None, None) { + result = Err(e) + } + } + result.is_ok() + }); + result?; + f.finish() + } + } +} + +fn print_path(fmt: &mut fmt::Formatter, path: BytesOrWideString) -> fmt::Result { + match path { + BytesOrWideString::Bytes(mut bytes) => { + loop { + match std::str::from_utf8(bytes) { + Ok(s) => { + fmt.write_str(s)?; + break; + } + Err(err) => { + fmt.write_char(std::char::REPLACEMENT_CHARACTER)?; + match err.error_len() { + Some(len) => bytes = &bytes[err.valid_up_to() + len..], + None => break, + } + } + } + } + } + BytesOrWideString::Wide(wide) => { + for c in std::char::decode_utf16(wide.iter().cloned()) { + fmt.write_char(c.unwrap_or(std::char::REPLACEMENT_CHARACTER))? + } + } + } + Ok(()) +} diff --git a/third_party/webrender/webrender/build.rs b/third_party/webrender/webrender/build.rs index adc5dbf2e88..caea4dd5c2b 100644 --- a/third_party/webrender/webrender/build.rs +++ b/third_party/webrender/webrender/build.rs @@ -2,6 +2,8 @@ * 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 webrender_build; + use std::borrow::Cow; use std::env; use std::fs::{canonicalize, read_dir, File}; @@ -12,12 +14,6 @@ use std::hash::Hasher; use webrender_build::shader::*; use webrender_build::shader_features::{ShaderFeatureFlags, get_shader_features}; -// glsopt is known to leak, but we don't particularly care. -#[no_mangle] -pub extern "C" fn __lsan_default_options() -> *const u8 { - b"detect_leaks=0\0".as_ptr() -} - /// Compute the shader path for insertion into the include_str!() macro. /// This makes for more compact generated code than inserting the literal /// shader source into the generated file. @@ -98,16 +94,6 @@ struct ShaderOptimizationError { message: String, } -fn print_shader_source(shader_src: &str) { - // For some reason the glsl-opt errors are offset by 1 compared - // to the provided shader source string. - println!("0\t|"); - for (n, line) in shader_src.split('\n').enumerate() { - let line_number = n + 1; - println!("{}\t|{}", line_number, line); - } -} - fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: &str) -> Result<(), std::io::Error> { writeln!( shader_file, @@ -133,10 +119,8 @@ fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: & flags.remove(ShaderFeatureFlags::GLES); flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL); } - if !matches!(env::var("CARGO_CFG_TARGET_OS").as_ref().map(|s| &**s), Ok("android")) { - flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1); - } flags.remove(ShaderFeatureFlags::DITHERING); + flags.remove(ShaderFeatureFlags::PIXEL_LOCAL_STORAGE); for (shader_name, configs) in get_shader_features(flags) { for config in configs { @@ -172,17 +156,15 @@ fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: & format!("{}_{}", shader.shader_name, shader.config.replace(",", "_")) }; - let vert = glslopt_ctx.optimize(glslopt::ShaderType::Vertex, vert_src.clone()); + let vert = glslopt_ctx.optimize(glslopt::ShaderType::Vertex, vert_src); if !vert.get_status() { - print_shader_source(&vert_src); return Err(ShaderOptimizationError { shader: shader.clone(), message: vert.get_log().to_string(), }); } - let frag = glslopt_ctx.optimize(glslopt::ShaderType::Fragment, frag_src.clone()); + let frag = glslopt_ctx.optimize(glslopt::ShaderType::Fragment, frag_src); if !frag.get_status() { - print_shader_source(&frag_src); return Err(ShaderOptimizationError { shader: shader.clone(), message: frag.get_log().to_string(), @@ -196,15 +178,17 @@ fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: & // as a literal alongside the source string so that we don't need // to hash large strings at runtime. let mut hasher = DefaultHasher::new(); + hasher.write(vert_source.as_bytes()); + hasher.write(frag_source.as_bytes()); + let digest: ProgramSourceDigest = hasher.into(); let vert_file_path = Path::new(out_dir) .join(format!("{}_{:?}.vert", full_shader_name, shader.gl_version)); - write_optimized_shader_file(&vert_file_path, vert_source, &shader.shader_name, &features, &mut hasher); - + let mut vert_file = File::create(&vert_file_path).unwrap(); + vert_file.write_all(vert_source.as_bytes()).unwrap(); let frag_file_path = vert_file_path.with_extension("frag"); - write_optimized_shader_file(&frag_file_path, frag_source, &shader.shader_name, &features, &mut hasher); - - let digest: ProgramSourceDigest = hasher.into(); + let mut frag_file = File::create(&frag_file_path).unwrap(); + frag_file.write_all(frag_source.as_bytes()).unwrap(); println!("Finished optimizing shader {:?}", shader); @@ -260,35 +244,37 @@ fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: & Ok(()) } -fn write_optimized_shader_file( - path: &Path, - source: &str, - shader_name: &str, - features: &[&str], - hasher: &mut DefaultHasher, -) { - let mut file = File::create(&path).unwrap(); - for (line_number, line) in source.lines().enumerate() { - // We embed the shader name and features as a comment in the - // source to make debugging easier. - // The #version directive must be on the first line so we insert - // the extra information on the next line. - if line_number == 1 { - let prelude = format!( - "// {}\n// features: {:?}\n\n", - shader_name, features - ); - file.write_all(prelude.as_bytes()).unwrap(); - hasher.write(prelude.as_bytes()); +#[cfg(any(target_os = "macos", target_os = "linux"))] +mod backtrace; + +#[cfg(any(target_os = "macos", target_os = "linux"))] +extern "C" fn handler(sig: i32) { + use std::sync::atomic; + static BEEN_HERE_BEFORE: atomic::AtomicBool = atomic::AtomicBool::new(false); + if !BEEN_HERE_BEFORE.swap(true, atomic::Ordering::SeqCst) { + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + let _ = write!(&mut stdout, "Stack trace"); + if let Some(name) = std::thread::current().name() { + let _ = write!(&mut stdout, " for thread \"{}\"", name); } - file.write_all(line.as_bytes()).unwrap(); - file.write_all("\n".as_bytes()).unwrap(); - hasher.write(line.as_bytes()); - hasher.write("\n".as_bytes()); + let _ = write!(&mut stdout, "\n"); + let _ = backtrace::print(&mut stdout); + } + unsafe { + libc::_exit(sig); } } fn main() -> Result<(), std::io::Error> { + #[cfg(any(target_os = "macos", target_os = "linux"))] + { + sig::signal!(sig::ffi::Sig::SEGV, handler); // handle segfaults + sig::signal!(sig::ffi::Sig::ILL, handler); // handle stack overflow and unsupported CPUs + sig::signal!(sig::ffi::Sig::IOT, handler); // handle double panics + sig::signal!(sig::ffi::Sig::BUS, handler); // handle invalid memory access + } + let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned()); let shaders_file_path = Path::new(&out_dir).join("shaders.rs"); diff --git a/third_party/webrender/webrender/res/base.glsl b/third_party/webrender/webrender/res/base.glsl index e381ff6ca9c..eb343c019b5 100644 --- a/third_party/webrender/webrender/res/base.glsl +++ b/third_party/webrender/webrender/res/base.glsl @@ -4,6 +4,12 @@ #if defined(GL_ES) #if GL_ES == 1 + #ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp sampler2DArray; + #else + precision mediump sampler2DArray; + #endif + // Sampler default precision is lowp on mobile GPUs. // This causes RGBA32F texture data to be clamped to 16 bit floats on some GPUs (e.g. Mali-T880). // Define highp precision macro to allow lossless FLOAT texture sampling. @@ -26,20 +32,7 @@ #else #define HIGHP_SAMPLER_FLOAT #define HIGHP_FS_ADDRESS - #if defined(PLATFORM_MACOS) && !defined(SWGL) - // texelFetchOffset introduces a variety of shader compilation bugs on macOS Intel so avoid it. - #define TEXEL_FETCH(sampler, position, lod, offset) texelFetch(sampler, position + offset, lod) - #else - #define TEXEL_FETCH(sampler, position, lod, offset) texelFetchOffset(sampler, position, lod, offset) - #endif -#endif - -#ifdef SWGL - #define SWGL_DRAW_SPAN - #define SWGL_CLIP_MASK - #define SWGL_ANTIALIAS - #define SWGL_BLEND - #define SWGL_CLIP_DIST + #define TEXEL_FETCH(sampler, position, lod, offset) texelFetchOffset(sampler, position, lod, offset) #endif #ifdef WR_VERTEX_SHADER @@ -51,20 +44,10 @@ #define PER_INSTANCE #endif - #if __VERSION__ != 100 - #define varying out - #define attribute in - #endif + #define varying out #endif #ifdef WR_FRAGMENT_SHADER precision highp float; - #if __VERSION__ != 100 - #define varying in - #endif -#endif - -// Flat interpolation is not supported on ESSL 1 -#if __VERSION__ == 100 - #define flat + #define varying in #endif diff --git a/third_party/webrender/webrender/res/blend.glsl b/third_party/webrender/webrender/res/blend.glsl deleted file mode 100644 index 07bda7aba7f..00000000000 --- a/third_party/webrender/webrender/res/blend.glsl +++ /dev/null @@ -1,233 +0,0 @@ -/* 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/. */ - -#define COMPONENT_TRANSFER_IDENTITY 0 -#define COMPONENT_TRANSFER_TABLE 1 -#define COMPONENT_TRANSFER_DISCRETE 2 -#define COMPONENT_TRANSFER_LINEAR 3 -#define COMPONENT_TRANSFER_GAMMA 4 - -// Must be kept in sync with `Filter::as_int` in internal_types.rs -// Not all filters are defined here because some filter use different shaders. -#define FILTER_CONTRAST 0 -#define FILTER_GRAYSCALE 1 -#define FILTER_HUE_ROTATE 2 -#define FILTER_INVERT 3 -#define FILTER_SATURATE 4 -#define FILTER_SEPIA 5 -#define FILTER_BRIGHTNESS 6 -#define FILTER_COLOR_MATRIX 7 -#define FILTER_SRGB_TO_LINEAR 8 -#define FILTER_LINEAR_TO_SRGB 9 -#define FILTER_FLOOD 10 -#define FILTER_COMPONENT_TRANSFER 11 - -#ifdef WR_VERTEX_SHADER -void SetupFilterParams( - int op, - float amount, - int gpu_data_address, - out vec4 color_offset, - out mat4 color_mat, - out int table_address -) { - float lumR = 0.2126; - float lumG = 0.7152; - float lumB = 0.0722; - float oneMinusLumR = 1.0 - lumR; - float oneMinusLumG = 1.0 - lumG; - float oneMinusLumB = 1.0 - lumB; - float invAmount = 1.0 - amount; - - if (op == FILTER_GRAYSCALE) { - color_mat = mat4( - vec4(lumR + oneMinusLumR * invAmount, lumR - lumR * invAmount, lumR - lumR * invAmount, 0.0), - vec4(lumG - lumG * invAmount, lumG + oneMinusLumG * invAmount, lumG - lumG * invAmount, 0.0), - vec4(lumB - lumB * invAmount, lumB - lumB * invAmount, lumB + oneMinusLumB * invAmount, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - color_offset = vec4(0.0); - } else if (op == FILTER_HUE_ROTATE) { - float c = cos(amount); - float s = sin(amount); - color_mat = mat4( - vec4(lumR + oneMinusLumR * c - lumR * s, lumR - lumR * c + 0.143 * s, lumR - lumR * c - oneMinusLumR * s, 0.0), - vec4(lumG - lumG * c - lumG * s, lumG + oneMinusLumG * c + 0.140 * s, lumG - lumG * c + lumG * s, 0.0), - vec4(lumB - lumB * c + oneMinusLumB * s, lumB - lumB * c - 0.283 * s, lumB + oneMinusLumB * c + lumB * s, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - color_offset = vec4(0.0); - } else if (op == FILTER_SATURATE) { - color_mat = mat4( - vec4(invAmount * lumR + amount, invAmount * lumR, invAmount * lumR, 0.0), - vec4(invAmount * lumG, invAmount * lumG + amount, invAmount * lumG, 0.0), - vec4(invAmount * lumB, invAmount * lumB, invAmount * lumB + amount, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - color_offset = vec4(0.0); - } else if (op == FILTER_SEPIA) { - color_mat = mat4( - vec4(0.393 + 0.607 * invAmount, 0.349 - 0.349 * invAmount, 0.272 - 0.272 * invAmount, 0.0), - vec4(0.769 - 0.769 * invAmount, 0.686 + 0.314 * invAmount, 0.534 - 0.534 * invAmount, 0.0), - vec4(0.189 - 0.189 * invAmount, 0.168 - 0.168 * invAmount, 0.131 + 0.869 * invAmount, 0.0), - vec4(0.0, 0.0, 0.0, 1.0) - ); - color_offset = vec4(0.0); - } else if (op == FILTER_COLOR_MATRIX) { - vec4 mat_data[4] = fetch_from_gpu_cache_4(gpu_data_address); - vec4 offset_data = fetch_from_gpu_cache_1(gpu_data_address + 4); - color_mat = mat4(mat_data[0], mat_data[1], mat_data[2], mat_data[3]); - color_offset = offset_data; - } else if (op == FILTER_COMPONENT_TRANSFER) { - table_address = gpu_data_address; - } else if (op == FILTER_FLOOD) { - color_offset = fetch_from_gpu_cache_1(gpu_data_address); - } -} -#endif - -#ifdef WR_FRAGMENT_SHADER -vec3 Contrast(vec3 Cs, float amount) { - return clamp(Cs.rgb * amount - 0.5 * amount + 0.5, 0.0, 1.0); -} - -vec3 Invert(vec3 Cs, float amount) { - return mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount); -} - -vec3 Brightness(vec3 Cs, float amount) { - // Apply the brightness factor. - // Resulting color needs to be clamped to output range - // since we are pre-multiplying alpha in the shader. - return clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0)); -} - -// Based on the Gecko's implementation in -// https://hg.mozilla.org/mozilla-central/file/91b4c3687d75/gfx/src/FilterSupport.cpp#l24 -// These could be made faster by sampling a lookup table stored in a float texture -// with linear interpolation. - -vec3 SrgbToLinear(vec3 color) { - vec3 c1 = color / 12.92; - vec3 c2 = pow(color / 1.055 + vec3(0.055 / 1.055), vec3(2.4)); - return if_then_else(lessThanEqual(color, vec3(0.04045)), c1, c2); -} - -vec3 LinearToSrgb(vec3 color) { - vec3 c1 = color * 12.92; - vec3 c2 = vec3(1.055) * pow(color, vec3(1.0 / 2.4)) - vec3(0.055); - return if_then_else(lessThanEqual(color, vec3(0.0031308)), c1, c2); -} - -// This function has to be factored out due to the following issue: -// https://github.com/servo/webrender/wiki/Driver-issues#bug-1532245---switch-statement-inside-control-flow-inside-switch-statement-fails-to-compile-on-some-android-phones -// (and now the words "default: default:" so angle_shader_validation.rs passes) -vec4 ComponentTransfer(vec4 colora, ivec4 vfuncs, int table_address) { - // We push a different amount of data to the gpu cache depending on the - // function type. - // Identity => 0 blocks - // Table/Discrete => 64 blocks (256 values) - // Linear => 1 block (2 values) - // Gamma => 1 block (3 values) - // We loop through the color components and increment the offset (for the - // next color component) into the gpu cache based on how many blocks that - // function type put into the gpu cache. - // Table/Discrete use a 256 entry look up table. - // Linear/Gamma are a simple calculation. - int offset = 0; - vec4 texel; - int k; - - // Dynamically indexing a vector is buggy on some platforms, so use a temporary array - int[4] funcs = int[4](vfuncs.r, vfuncs.g, vfuncs.b, vfuncs.a); - for (int i = 0; i < 4; i++) { - switch (funcs[i]) { - case COMPONENT_TRANSFER_IDENTITY: - break; - case COMPONENT_TRANSFER_TABLE: - case COMPONENT_TRANSFER_DISCRETE: { - // fetch value from lookup table - k = int(floor(colora[i]*255.0)); - texel = fetch_from_gpu_cache_1(table_address + offset + k/4); - colora[i] = clamp(texel[k % 4], 0.0, 1.0); - // offset plus 256/4 blocks - offset = offset + 64; - break; - } - case COMPONENT_TRANSFER_LINEAR: { - // fetch the two values for use in the linear equation - texel = fetch_from_gpu_cache_1(table_address + offset); - colora[i] = clamp(texel[0] * colora[i] + texel[1], 0.0, 1.0); - // offset plus 1 block - offset = offset + 1; - break; - } - case COMPONENT_TRANSFER_GAMMA: { - // fetch the three values for use in the gamma equation - texel = fetch_from_gpu_cache_1(table_address + offset); - colora[i] = clamp(texel[0] * pow(colora[i], texel[1]) + texel[2], 0.0, 1.0); - // offset plus 1 block - offset = offset + 1; - break; - } - default: - // shouldn't happen - break; - } - } - return colora; -} - -void CalculateFilter( - vec4 Cs, - int op, - float amount, - int table_address, - vec4 color_offset, - mat4 color_mat, - ivec4 v_funcs, - out vec3 color, - out float alpha -) { - // Un-premultiply the input. - alpha = Cs.a; - color = alpha != 0.0 ? Cs.rgb / alpha : Cs.rgb; - - switch (op) { - case FILTER_CONTRAST: - color = Contrast(color, amount); - break; - case FILTER_INVERT: - color = Invert(color, amount); - break; - case FILTER_BRIGHTNESS: - color = Brightness(color, amount); - break; - case FILTER_SRGB_TO_LINEAR: - color = SrgbToLinear(color); - break; - case FILTER_LINEAR_TO_SRGB: - color = LinearToSrgb(color); - break; - case FILTER_COMPONENT_TRANSFER: { - // Get the unpremultiplied color with alpha. - vec4 colora = vec4(color, alpha); - colora = ComponentTransfer(colora, v_funcs, table_address); - color = colora.rgb; - alpha = colora.a; - break; - } - case FILTER_FLOOD: - color = color_offset.rgb; - alpha = color_offset.a; - break; - default: - // Color matrix type filters (sepia, hue-rotate, etc...) - vec4 result = color_mat * vec4(color, alpha) + color_offset; - result = clamp(result, vec4(0.0), vec4(1.0)); - color = result.rgb; - alpha = result.a; - } -} -#endif diff --git a/third_party/webrender/webrender/res/brush.glsl b/third_party/webrender/webrender/res/brush.glsl index 881c798480c..4589e34b29e 100644 --- a/third_party/webrender/webrender/res/brush.glsl +++ b/third_party/webrender/webrender/res/brush.glsl @@ -46,25 +46,54 @@ /// other brush types don't use it. /// -#if (defined(WR_FEATURE_ALPHA_PASS) || defined(WR_FEATURE_ANTIALIASING)) && !defined(SWGL_ANTIALIAS) -varying vec2 v_local_pos; +#ifdef WR_FEATURE_MULTI_BRUSH +flat varying int v_brush_kind; #endif +// 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. +flat varying vec4 flat_varying_vec4_0; +flat varying vec4 flat_varying_vec4_1; +flat varying vec4 flat_varying_vec4_2; +flat varying vec4 flat_varying_vec4_3; +flat varying vec4 flat_varying_vec4_4; + +flat varying ivec4 flat_varying_ivec4_0; + +varying vec4 varying_vec4_0; +varying vec4 varying_vec4_1; + +flat varying HIGHP_FS_ADDRESS int flat_varying_highp_int_address_0; + #ifdef WR_VERTEX_SHADER -void brush_vs( - VertexInfo vi, - int prim_address, - RectWithSize local_rect, - RectWithSize segment_rect, - ivec4 prim_user_data, - int specific_resource_address, - mat4 transform, - PictureTask pic_task, - int brush_flags, - vec4 segment_data +#define FWD_DECLARE_VS_FUNCTION(name) \ +void name( \ + VertexInfo vi, \ + int prim_address, \ + RectWithSize local_rect, \ + RectWithSize segment_rect, \ + ivec4 prim_user_data, \ + int specific_resource_address, \ + mat4 transform, \ + PictureTask pic_task, \ + int brush_flags, \ + vec4 segment_data \ ); +// Forward-declare all brush vertex entry points. +FWD_DECLARE_VS_FUNCTION(image_brush_vs) +FWD_DECLARE_VS_FUNCTION(solid_brush_vs) +FWD_DECLARE_VS_FUNCTION(blend_brush_vs) +FWD_DECLARE_VS_FUNCTION(mix_blend_brush_vs) +FWD_DECLARE_VS_FUNCTION(linear_gradient_brush_vs) +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( @@ -75,6 +104,20 @@ void text_shader_main( ClipArea clip_area ); +void multi_brush_vs( + VertexInfo vi, + int prim_address, + RectWithSize local_rect, + RectWithSize segment_rect, + ivec4 prim_user_data, + int specific_resource_address, + mat4 transform, + PictureTask pic_task, + int brush_flags, + vec4 texel_rect, + int brush_kind +); + #define VECS_PER_SEGMENT 2 #define BRUSH_FLAG_PERSPECTIVE_INTERPOLATION 1 @@ -105,8 +148,18 @@ void brush_shader_main_vs( segment_rect = ph.local_rect; segment_data = vec4(0.0); } else { + // This contraption is needed by the Mac GLSL compiler which falls + // over (generating garbage values) if: + // - we use a variable insead of the ACTUAL_VECS_PER_BRUSH define, + // - this compile time condition is done inside of vecs_per_brush. + #ifdef WR_FEATURE_MULTI_BRUSH + #define ACTUAL_VECS_PER_BRUSH vecs_per_brush(instance.brush_kind) + #else + #define ACTUAL_VECS_PER_BRUSH VECS_PER_SPECIFIC_BRUSH + #endif + int segment_address = ph.specific_prim_address + - VECS_PER_SPECIFIC_BRUSH + + ACTUAL_VECS_PER_BRUSH + instance.segment_index * VECS_PER_SEGMENT; vec4[2] segment_info = fetch_from_gpu_cache_2(segment_address); @@ -137,15 +190,17 @@ void brush_shader_main_vs( // items. For now, just ensure it has no // effect. We can tidy this up as we move // more items to be brush shaders. -#if defined(WR_FEATURE_ALPHA_PASS) && !defined(SWGL_ANTIALIAS) +#ifdef WR_FEATURE_ALPHA_PASS init_transform_vs(vec4(vec2(-1.0e16), vec2(1.0e16))); #endif } else { + bvec4 edge_mask = notEqual(edge_flags & ivec4(1, 2, 4, 8), ivec4(0)); + vi = write_transform_vertex( segment_rect, ph.local_rect, ph.local_clip_rect, - edge_flags, + mix(vec4(0.0), vec4(1.0), edge_mask), ph.z, transform, pic_task @@ -158,20 +213,31 @@ void brush_shader_main_vs( // shaders that don't clip in the future, // but it's reasonable to assume that one // implies the other, for now. - // SW-WR may decay some requests for alpha-pass shaders to - // the opaque version if only the clip-mask is required. In - // that case the opaque vertex shader must still write out - // the clip information, which is cheap to do for SWGL. -#if defined(WR_FEATURE_ALPHA_PASS) || defined(SWGL_CLIP_MASK) +#ifdef WR_FEATURE_ALPHA_PASS write_clip( vi.world_pos, - clip_area, - pic_task + clip_area ); #endif // Run the specific brush VS code to write interpolators. - brush_vs( +#ifdef WR_FEATURE_MULTI_BRUSH + v_brush_kind = instance.brush_kind; + multi_brush_vs( + vi, + ph.specific_prim_address, + ph.local_rect, + segment_rect, + ph.user_data, + instance.resource_address, + transform.m, + pic_task, + brush_flags, + segment_data, + instance.brush_kind + ); +#else + WR_BRUSH_VS_FUNCTION( vi, ph.specific_prim_address, ph.local_rect, @@ -183,10 +249,8 @@ void brush_shader_main_vs( brush_flags, segment_data ); - -#if (defined(WR_FEATURE_ALPHA_PASS) || defined(WR_FEATURE_ANTIALIASING)) && !defined(SWGL_ANTIALIAS) - v_local_pos = vi.local_pos; #endif + } #ifndef WR_VERTEX_SHADER_MAIN_FUNCTION @@ -210,22 +274,31 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER -float antialias_brush() { -#if (defined(WR_FEATURE_ALPHA_PASS) || defined(WR_FEATURE_ANTIALIASING)) && !defined(SWGL_ANTIALIAS) - return init_transform_fs(v_local_pos); -#else - return 1.0; -#endif -} - -Fragment brush_fs(); +// Foward-declare all brush entry-points. +Fragment image_brush_fs(); +Fragment solid_brush_fs(); +Fragment blend_brush_fs(); +Fragment mix_blend_brush_fs(); +Fragment linear_gradient_brush_fs(); +Fragment radial_gradient_brush_fs(); +Fragment conic_gradient_brush_fs(); +Fragment yuv_brush_fs(); +Fragment opacity_brush_fs(); +Fragment text_brush_fs(); +Fragment multi_brush_fs(int brush_kind); void main(void) { #ifdef WR_FEATURE_DEBUG_OVERDRAW oFragColor = WR_DEBUG_OVERDRAW_COLOR; #else - Fragment frag = brush_fs(); + // Run the specific brush FS code to output the color. +#ifdef WR_FEATURE_MULTI_BRUSH + Fragment frag = multi_brush_fs(v_brush_kind); +#else + Fragment frag = WR_BRUSH_FS_FUNCTION(); +#endif + #ifdef WR_FEATURE_ALPHA_PASS // Apply the clip mask diff --git a/third_party/webrender/webrender/res/brush_blend.glsl b/third_party/webrender/webrender/res/brush_blend.glsl index 2977249627e..c871c390d28 100644 --- a/third_party/webrender/webrender/res/brush_blend.glsl +++ b/third_party/webrender/webrender/res/brush_blend.glsl @@ -2,31 +2,63 @@ * 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 VECS_PER_SPECIFIC_BRUSH 3 -#define WR_FEATURE_TEXTURE_2D +#define VECS_PER_BLEND_BRUSH 3 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_BLEND_BRUSH -#include shared,prim_shared,brush,blend +#define WR_BRUSH_VS_FUNCTION blend_brush_vs +#define WR_BRUSH_FS_FUNCTION blend_brush_fs + +#define COMPONENT_TRANSFER_IDENTITY 0 +#define COMPONENT_TRANSFER_TABLE 1 +#define COMPONENT_TRANSFER_DISCRETE 2 +#define COMPONENT_TRANSFER_LINEAR 3 +#define COMPONENT_TRANSFER_GAMMA 4 + +// Must be kept in sync with `Filter::as_int` in internal_types.rs +// Not all filters are defined here because some filter use different shaders. +#define FILTER_CONTRAST 0 +#define FILTER_GRAYSCALE 1 +#define FILTER_HUE_ROTATE 2 +#define FILTER_INVERT 3 +#define FILTER_SATURATE 4 +#define FILTER_SEPIA 5 +#define FILTER_BRIGHTNESS 6 +#define FILTER_COLOR_MATRIX 7 +#define FILTER_SRGB_TO_LINEAR 8 +#define FILTER_LINEAR_TO_SRGB 9 +#define FILTER_FLOOD 10 +#define FILTER_COMPONENT_TRANSFER 11 + +#include shared,prim_shared,brush // Interpolated UV coordinates to sample. -varying vec2 v_uv; - -// Normalized bounds of the source image in the texture, adjusted to avoid -// sampling artifacts. -flat varying vec4 v_uv_sample_bounds; - -// x: Flag to allow perspective interpolation of UV. -// y: Filter-dependent "amount" parameter. -// Please ensure that perspective remains packed in a vector. If refactoring, -// see the v_perspective declaration in brush_image, and bug 1630356. -flat varying vec2 v_perspective_amount; -flat varying int v_op; -flat varying int v_table_address; -flat varying mat4 v_color_mat; -flat varying ivec4 v_funcs; -flat varying vec4 v_color_offset; +#define V_UV varying_vec4_0.zw +#define V_LOCAL_POS varying_vec4_0.xy + +#define V_FLOOD_COLOR flat_varying_vec4_1 + +// Normalized bounds of the source image in the texture. +#define V_UV_BOUNDS flat_varying_vec4_2 + +#define V_COLOR_OFFSET flat_varying_vec4_3 + +// Layer index to sample. +#define V_LAYER flat_varying_vec4_4.x +// Flag to allow perspective interpolation of UV. +#define V_PERSPECTIVE flat_varying_vec4_4.y + +#define V_AMOUNT flat_varying_vec4_4.z + +#define V_OP flat_varying_ivec4_0.x +#define V_TABLE_ADDRESS flat_varying_ivec4_0.y + +flat varying mat4 vColorMat; + +flat varying int vFuncs[4]; #ifdef WR_VERTEX_SHADER -void brush_vs( + +void blend_brush_vs( VertexInfo vi, int prim_address, RectWithSize local_rect, @@ -38,25 +70,37 @@ void brush_vs( int brush_flags, vec4 unused ) { - ImageSource res = fetch_image_source(prim_user_data.x); + ImageResource res = fetch_image_resource(prim_user_data.x); vec2 uv0 = res.uv_rect.p0; vec2 uv1 = res.uv_rect.p1; - vec2 inv_texture_size = vec2(1.0) / vec2(TEX_SIZE(sColor0).xy); + vec2 texture_size = vec2(textureSize(sColor0, 0).xy); vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size; f = get_image_quad_uv(prim_user_data.x, f); vec2 uv = mix(uv0, uv1, f); float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0; - v_uv = uv * inv_texture_size * mix(vi.world_pos.w, 1.0, perspective_interpolate); - v_perspective_amount.x = perspective_interpolate; + V_UV = uv / texture_size * mix(vi.world_pos.w, 1.0, perspective_interpolate); + V_LAYER = res.layer; + V_PERSPECTIVE = perspective_interpolate; - v_uv_sample_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) * inv_texture_size.xyxy; + // TODO: The image shader treats this differently: deflate the rect by half a pixel on each side and + // clamp the uv in the frame shader. Does it make sense to do the same here? + V_UV_BOUNDS = vec4(uv0, uv1) / texture_size.xyxy; + V_LOCAL_POS = vi.local_pos; + + float lumR = 0.2126; + float lumG = 0.7152; + float lumB = 0.0722; + float oneMinusLumR = 1.0 - lumR; + float oneMinusLumG = 1.0 - lumG; + float oneMinusLumB = 1.0 - lumB; float amount = float(prim_user_data.z) / 65536.0; + float invAmount = 1.0 - amount; - v_op = prim_user_data.y & 0xffff; - v_perspective_amount.y = amount; + V_OP = prim_user_data.y & 0xffff; + V_AMOUNT = amount; // This assignment is only used for component transfer filters but this // assignment has to be done here and not in the component transfer case @@ -65,50 +109,227 @@ void brush_vs( // https://github.com/servo/webrender/wiki/Driver-issues#bug-1505871---assignment-to-varying-flat-arrays-inside-switch-statement-of-vertex-shader-suspected-miscompile-on-windows // default: just to satisfy angle_shader_validation.rs which needs one // default: for every switch, even in comments. - v_funcs.r = (prim_user_data.y >> 28) & 0xf; // R - v_funcs.g = (prim_user_data.y >> 24) & 0xf; // G - v_funcs.b = (prim_user_data.y >> 20) & 0xf; // B - v_funcs.a = (prim_user_data.y >> 16) & 0xf; // A - - SetupFilterParams( - v_op, - amount, - prim_user_data.z, - v_color_offset, - v_color_mat, - v_table_address - ); + vFuncs[0] = (prim_user_data.y >> 28) & 0xf; // R + vFuncs[1] = (prim_user_data.y >> 24) & 0xf; // G + vFuncs[2] = (prim_user_data.y >> 20) & 0xf; // B + vFuncs[3] = (prim_user_data.y >> 16) & 0xf; // A + + switch (V_OP) { + case FILTER_GRAYSCALE: { + vColorMat = mat4( + vec4(lumR + oneMinusLumR * invAmount, lumR - lumR * invAmount, lumR - lumR * invAmount, 0.0), + vec4(lumG - lumG * invAmount, lumG + oneMinusLumG * invAmount, lumG - lumG * invAmount, 0.0), + vec4(lumB - lumB * invAmount, lumB - lumB * invAmount, lumB + oneMinusLumB * invAmount, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ); + V_COLOR_OFFSET = vec4(0.0); + break; + } + case FILTER_HUE_ROTATE: { + float c = cos(amount); + float s = sin(amount); + vColorMat = mat4( + vec4(lumR + oneMinusLumR * c - lumR * s, lumR - lumR * c + 0.143 * s, lumR - lumR * c - oneMinusLumR * s, 0.0), + vec4(lumG - lumG * c - lumG * s, lumG + oneMinusLumG * c + 0.140 * s, lumG - lumG * c + lumG * s, 0.0), + vec4(lumB - lumB * c + oneMinusLumB * s, lumB - lumB * c - 0.283 * s, lumB + oneMinusLumB * c + lumB * s, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ); + V_COLOR_OFFSET = vec4(0.0); + break; + } + case FILTER_SATURATE: { + vColorMat = mat4( + vec4(invAmount * lumR + amount, invAmount * lumR, invAmount * lumR, 0.0), + vec4(invAmount * lumG, invAmount * lumG + amount, invAmount * lumG, 0.0), + vec4(invAmount * lumB, invAmount * lumB, invAmount * lumB + amount, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ); + V_COLOR_OFFSET = vec4(0.0); + break; + } + case FILTER_SEPIA: { + vColorMat = mat4( + vec4(0.393 + 0.607 * invAmount, 0.349 - 0.349 * invAmount, 0.272 - 0.272 * invAmount, 0.0), + vec4(0.769 - 0.769 * invAmount, 0.686 + 0.314 * invAmount, 0.534 - 0.534 * invAmount, 0.0), + vec4(0.189 - 0.189 * invAmount, 0.168 - 0.168 * invAmount, 0.131 + 0.869 * invAmount, 0.0), + vec4(0.0, 0.0, 0.0, 1.0) + ); + V_COLOR_OFFSET = vec4(0.0); + break; + } + case FILTER_COLOR_MATRIX: { + vec4 mat_data[4] = fetch_from_gpu_cache_4(prim_user_data.z); + vec4 offset_data = fetch_from_gpu_cache_1(prim_user_data.z + 4); + vColorMat = mat4(mat_data[0], mat_data[1], mat_data[2], mat_data[3]); + V_COLOR_OFFSET = offset_data; + break; + } + case FILTER_COMPONENT_TRANSFER: { + V_TABLE_ADDRESS = prim_user_data.z; + break; + } + case FILTER_FLOOD: { + V_FLOOD_COLOR = fetch_from_gpu_cache_1(prim_user_data.z); + break; + } + default: break; + } } #endif #ifdef WR_FRAGMENT_SHADER -Fragment brush_fs() { - float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective_amount.x); - vec2 uv = v_uv * perspective_divisor; - // Clamp the uvs to avoid sampling artifacts. - uv = clamp(uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw); - - vec4 Cs = texture(sColor0, uv); - - float alpha; - vec3 color; - CalculateFilter( - Cs, - v_op, - v_perspective_amount.y, - v_table_address, - v_color_offset, - v_color_mat, - v_funcs, - color, - alpha - ); - - #ifdef WR_FEATURE_ALPHA_PASS - alpha *= antialias_brush(); - #endif +vec3 Contrast(vec3 Cs, float amount) { + return Cs.rgb * amount - 0.5 * amount + 0.5; +} + +vec3 Invert(vec3 Cs, float amount) { + return mix(Cs.rgb, vec3(1.0) - Cs.rgb, amount); +} + +vec3 Brightness(vec3 Cs, float amount) { + // Apply the brightness factor. + // Resulting color needs to be clamped to output range + // since we are pre-multiplying alpha in the shader. + return clamp(Cs.rgb * amount, vec3(0.0), vec3(1.0)); +} + +// Based on the Gecko's implementation in +// https://hg.mozilla.org/mozilla-central/file/91b4c3687d75/gfx/src/FilterSupport.cpp#l24 +// These could be made faster by sampling a lookup table stored in a float texture +// with linear interpolation. + +vec3 SrgbToLinear(vec3 color) { + vec3 c1 = color / 12.92; + vec3 c2 = pow(color / 1.055 + vec3(0.055 / 1.055), vec3(2.4)); + return if_then_else(lessThanEqual(color, vec3(0.04045)), c1, c2); +} + +vec3 LinearToSrgb(vec3 color) { + vec3 c1 = color * 12.92; + vec3 c2 = vec3(1.055) * pow(color, vec3(1.0 / 2.4)) - vec3(0.055); + return if_then_else(lessThanEqual(color, vec3(0.0031308)), c1, c2); +} + +// This function has to be factored out due to the following issue: +// https://github.com/servo/webrender/wiki/Driver-issues#bug-1532245---switch-statement-inside-control-flow-inside-switch-statement-fails-to-compile-on-some-android-phones +// (and now the words "default: default:" so angle_shader_validation.rs passes) +vec4 ComponentTransfer(vec4 colora) { + // We push a different amount of data to the gpu cache depending on the + // function type. + // Identity => 0 blocks + // Table/Discrete => 64 blocks (256 values) + // Linear => 1 block (2 values) + // Gamma => 1 block (3 values) + // We loop through the color components and increment the offset (for the + // next color component) into the gpu cache based on how many blocks that + // function type put into the gpu cache. + // Table/Discrete use a 256 entry look up table. + // Linear/Gamma are a simple calculation. + int offset = 0; + vec4 texel; + int k; + + for (int i = 0; i < 4; i++) { + switch (vFuncs[i]) { + case COMPONENT_TRANSFER_IDENTITY: + break; + case COMPONENT_TRANSFER_TABLE: + case COMPONENT_TRANSFER_DISCRETE: { + // fetch value from lookup table + k = int(floor(colora[i]*255.0)); + texel = fetch_from_gpu_cache_1(V_TABLE_ADDRESS + offset + k/4); + colora[i] = clamp(texel[k % 4], 0.0, 1.0); + // offset plus 256/4 blocks + offset = offset + 64; + break; + } + case COMPONENT_TRANSFER_LINEAR: { + // fetch the two values for use in the linear equation + texel = fetch_from_gpu_cache_1(V_TABLE_ADDRESS + offset); + colora[i] = clamp(texel[0] * colora[i] + texel[1], 0.0, 1.0); + // offset plus 1 block + offset = offset + 1; + break; + } + case COMPONENT_TRANSFER_GAMMA: { + // fetch the three values for use in the gamma equation + texel = fetch_from_gpu_cache_1(V_TABLE_ADDRESS + offset); + colora[i] = clamp(texel[0] * pow(colora[i], texel[1]) + texel[2], 0.0, 1.0); + // offset plus 1 block + offset = offset + 1; + break; + } + default: + // shouldn't happen + break; + } + } + return colora; +} + +Fragment blend_brush_fs() { + float perspective_divisor = mix(gl_FragCoord.w, 1.0, V_PERSPECTIVE); + vec2 uv = V_UV * perspective_divisor; + vec4 Cs = texture(sColor0, vec3(uv, V_LAYER)); + + // Un-premultiply the input. + float alpha = Cs.a; + vec3 color = alpha != 0.0 ? Cs.rgb / alpha : Cs.rgb; + + switch (V_OP) { + case FILTER_CONTRAST: + color = Contrast(color, V_AMOUNT); + break; + case FILTER_INVERT: + color = Invert(color, V_AMOUNT); + break; + case FILTER_BRIGHTNESS: + color = Brightness(color, V_AMOUNT); + break; + case FILTER_SRGB_TO_LINEAR: + color = SrgbToLinear(color); + break; + case FILTER_LINEAR_TO_SRGB: + color = LinearToSrgb(color); + break; + case FILTER_COMPONENT_TRANSFER: { + // Get the unpremultiplied color with alpha. + vec4 colora = vec4(color, alpha); + colora = ComponentTransfer(colora); + color = colora.rgb; + alpha = colora.a; + break; + } + case FILTER_FLOOD: + color = V_FLOOD_COLOR.rgb; + alpha = V_FLOOD_COLOR.a; + break; + default: + // Color matrix type filters (sepia, hue-rotate, etc...) + vec4 result = vColorMat * vec4(color, alpha) + V_COLOR_OFFSET; + result = clamp(result, vec4(0.0), vec4(1.0)); + color = result.rgb; + alpha = result.a; + } + + // Fail-safe to ensure that we don't sample outside the rendered + // portion of a blend source. + alpha *= min(point_inside_rect(uv, V_UV_BOUNDS.xy, V_UV_BOUNDS.zw), + init_transform_fs(V_LOCAL_POS)); // Pre-multiply the alpha into the output value. return Fragment(alpha * vec4(color, 1.0)); } #endif + +// Undef macro names that could be re-defined by other shaders. +#undef V_UV +#undef V_LOCAL_POS +#undef V_FLOOD_COLOR +#undef V_UV_BOUNDS +#undef V_COLOR_OFFSET +#undef V_AMOUNT +#undef V_LAYER +#undef V_PERSPECTIVE +#undef V_OP +#undef V_TABLE_ADDRESS diff --git a/third_party/webrender/webrender/res/brush_conic_gradient.glsl b/third_party/webrender/webrender/res/brush_conic_gradient.glsl new file mode 100644 index 00000000000..56bd3c25a97 --- /dev/null +++ b/third_party/webrender/webrender/res/brush_conic_gradient.glsl @@ -0,0 +1,147 @@ +/* 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/. */ + +#define VECS_PER_CONIC_GRADIENT_BRUSH 2 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_CONIC_GRADIENT_BRUSH + +#define WR_BRUSH_VS_FUNCTION conic_gradient_brush_vs +#define WR_BRUSH_FS_FUNCTION conic_gradient_brush_fs + +#include shared,prim_shared,brush + +#define V_GRADIENT_ADDRESS flat_varying_highp_int_address_0 + +#define V_CENTER flat_varying_vec4_0.xy +#define V_START_OFFSET flat_varying_vec4_0.z +#define V_END_OFFSET flat_varying_vec4_0.w +#define V_ANGLE flat_varying_vec4_1.w + +// Size of the gradient pattern's rectangle, used to compute horizontal and vertical +// repetitions. Not to be confused with another kind of repetition of the pattern +// which happens along the gradient stops. +#define V_REPEATED_SIZE flat_varying_vec4_1.xy +// Repetition along the gradient stops. +#define V_GRADIENT_REPEAT flat_varying_vec4_1.z + +#define V_POS varying_vec4_0.zw + +#ifdef WR_FEATURE_ALPHA_PASS +#define V_LOCAL_POS varying_vec4_0.xy +#define V_TILE_REPEAT flat_varying_vec4_2.xy +#endif + +#define PI 3.141592653589793 + +#ifdef WR_VERTEX_SHADER + +struct ConicGradient { + vec2 center_point; + vec2 start_end_offset; + float angle; + int extend_mode; + vec2 stretch_size; +}; + +ConicGradient fetch_gradient(int address) { + vec4 data[2] = fetch_from_gpu_cache_2(address); + return ConicGradient( + data[0].xy, + data[0].zw, + float(data[1].x), + int(data[1].y), + data[1].zw + ); +} + +void conic_gradient_brush_vs( + VertexInfo vi, + int prim_address, + RectWithSize local_rect, + RectWithSize segment_rect, + ivec4 prim_user_data, + int specific_resource_address, + mat4 transform, + PictureTask pic_task, + int brush_flags, + vec4 texel_rect +) { + ConicGradient gradient = fetch_gradient(prim_address); + + if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) { + V_POS = (vi.local_pos - segment_rect.p0) / segment_rect.size; + V_POS = V_POS * (texel_rect.zw - texel_rect.xy) + texel_rect.xy; + V_POS = V_POS * local_rect.size; + } else { + V_POS = vi.local_pos - local_rect.p0; + } + + V_CENTER = gradient.center_point; + V_ANGLE = gradient.angle; + V_START_OFFSET = gradient.start_end_offset.x; + V_END_OFFSET = gradient.start_end_offset.y; + + vec2 tile_repeat = local_rect.size / gradient.stretch_size; + V_REPEATED_SIZE = gradient.stretch_size; + + V_GRADIENT_ADDRESS = prim_user_data.x; + + // Whether to repeat the gradient along the line instead of clamping. + V_GRADIENT_REPEAT = float(gradient.extend_mode != EXTEND_MODE_CLAMP); + +#ifdef WR_FEATURE_ALPHA_PASS + V_TILE_REPEAT = tile_repeat; + V_LOCAL_POS = vi.local_pos; +#endif +} +#endif + +#ifdef WR_FRAGMENT_SHADER +Fragment conic_gradient_brush_fs() { + +#ifdef WR_FEATURE_ALPHA_PASS + // Handle top and left inflated edges (see brush_image). + vec2 local_pos = max(V_POS, vec2(0.0)); + + // Apply potential horizontal and vertical repetitions. + vec2 pos = mod(local_pos, V_REPEATED_SIZE); + + vec2 prim_size = V_REPEATED_SIZE * V_TILE_REPEAT; + // Handle bottom and right inflated edges (see brush_image). + if (local_pos.x >= prim_size.x) { + pos.x = V_REPEATED_SIZE.x; + } + if (local_pos.y >= prim_size.y) { + pos.y = V_REPEATED_SIZE.y; + } +#else + // Apply potential horizontal and vertical repetitions. + vec2 pos = mod(V_POS, V_REPEATED_SIZE); +#endif + + vec2 current_dir = pos - V_CENTER; + float current_angle = atan(current_dir.y, current_dir.x) + (PI / 2.0 - V_ANGLE); + float offset = mod(current_angle / (2.0 * PI), 1.0) - V_START_OFFSET; + offset = offset / (V_END_OFFSET - V_START_OFFSET); + + vec4 color = sample_gradient(V_GRADIENT_ADDRESS, + offset, + V_GRADIENT_REPEAT); + +#ifdef WR_FEATURE_ALPHA_PASS + color *= init_transform_fs(V_LOCAL_POS); +#endif + + return Fragment(color); +} +#endif + +// Undef macro names that could be re-defined by other shaders. +#undef V_GRADIENT_ADDRESS +#undef V_START_POINT +#undef V_SCALE_DIR +#undef V_REPEATED_SIZE +#undef V_GRADIENT_REPEAT +#undef V_POS +#undef V_LOCAL_POS +#undef V_TILE_REPEAT diff --git a/third_party/webrender/webrender/res/brush_image.glsl b/third_party/webrender/webrender/res/brush_image.glsl index 96f59cb2056..05ee9043e00 100644 --- a/third_party/webrender/webrender/res/brush_image.glsl +++ b/third_party/webrender/webrender/res/brush_image.glsl @@ -2,38 +2,36 @@ * 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 VECS_PER_SPECIFIC_BRUSH 3 +#define VECS_PER_IMAGE_BRUSH 3 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_IMAGE_BRUSH + +#define WR_BRUSH_VS_FUNCTION image_brush_vs +#define WR_BRUSH_FS_FUNCTION image_brush_fs #include shared,prim_shared,brush +#ifdef WR_FEATURE_ALPHA_PASS +#define V_LOCAL_POS varying_vec4_0.xy +#endif + // Interpolated UV coordinates to sample. -varying vec2 v_uv; +#define V_UV varying_vec4_0.zw #ifdef WR_FEATURE_ALPHA_PASS -flat varying vec4 v_color; -flat varying vec2 v_mask_swizzle; -flat varying vec2 v_tile_repeat; +#define V_COLOR flat_varying_vec4_0 +#define V_MASK_SWIZZLE flat_varying_vec4_1.xy +#define V_TILE_REPEAT flat_varying_vec4_1.zw #endif // Normalized bounds of the source image in the texture. -flat varying vec4 v_uv_bounds; +#define V_UV_BOUNDS flat_varying_vec4_2 // Normalized bounds of the source image in the texture, adjusted to avoid // sampling artifacts. -flat varying vec4 v_uv_sample_bounds; - -#if defined(PLATFORM_ANDROID) && !defined(SWGL) -// On Adreno 3xx devices we have observed that a flat scalar varying used to calculate -// fragment shader output can result in the entire output being "flat". Here, for example, -// v_perspective being flat results in the UV coordinate calculated for every fragment -// being equal to the UV coordinate for the provoking vertex, which results in the entire -// triangle being rendered a solid color. -// Packing the varying in a vec2 works around this. See bug 1630356. -flat varying vec2 v_perspective_vec; -#define v_perspective v_perspective_vec.x -#else +#define V_UV_SAMPLE_BOUNDS flat_varying_vec4_3 +// Layer index to sample. +#define V_LAYER flat_varying_vec4_4.x // Flag to allow perspective interpolation of UV. -flat varying float v_perspective; -#endif +#define V_PERSPECTIVE flat_varying_vec4_4.y #ifdef WR_VERTEX_SHADER @@ -57,7 +55,7 @@ ImageBrushData fetch_image_data(int address) { return data; } -void brush_vs( +void image_brush_vs( VertexInfo vi, int prim_address, RectWithSize prim_rect, @@ -76,10 +74,10 @@ void brush_vs( #ifdef WR_FEATURE_TEXTURE_RECT vec2 texture_size = vec2(1, 1); #else - vec2 texture_size = vec2(TEX_SIZE(sColor0)); + vec2 texture_size = vec2(textureSize(sColor0, 0)); #endif - ImageSource res = fetch_image_source(specific_resource_address); + ImageResource res = fetch_image_resource(specific_resource_address); vec2 uv0 = res.uv_rect.p0; vec2 uv1 = res.uv_rect.p1; @@ -95,93 +93,87 @@ void brush_vs( local_rect = segment_rect; stretch_size = local_rect.size; + // If the extra data is a texel rect, modify the UVs. if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) { - // If the extra data is a texel rect, modify the UVs. vec2 uv_size = res.uv_rect.p1 - res.uv_rect.p0; uv0 = res.uv_rect.p0 + segment_data.xy * uv_size; uv1 = res.uv_rect.p0 + segment_data.zw * uv_size; - } - #ifdef WR_FEATURE_REPETITION + // Size of the uv rect of the segment we are considering when computing + // the repetitions. In most case it is the current segment, but for the + // middle area we look at the border size instead. + vec2 segment_uv_size = uv1 - uv0; + #ifdef WR_FEATURE_REPETITION + // Value of the stretch size with repetition. We have to compute it for + // both axis even if we only repeat on one axis because the value for + // each axis depends on what the repeated value would have been for the + // other axis. + vec2 repeated_stretch_size = stretch_size; + // The repetition parameters for the middle area of a nine-patch are based + // on the size of the border segments rather than the middle segment itself, + // taking top and left by default, falling back to bottom and right when a + // size is empty. // TODO(bug 1609893): Move this logic to the CPU as well as other sources of // branchiness in this shader. - if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) { - // Value of the stretch size with repetition. We have to compute it for - // both axis even if we only repeat on one axis because the value for - // each axis depends on what the repeated value would have been for the - // other axis. - vec2 repeated_stretch_size = stretch_size; - // Size of the uv rect of the segment we are considering when computing - // the repetitions. For the fill area it is a tad more complicated as we - // have to use the uv size of the top-middle segment to drive horizontal - // repetitions, and the size of the left-middle segment to drive vertical - // repetitions. So we track the reference sizes for both axis separately - // even though in the common case (the border segments) they are the same. - vec2 horizontal_uv_size = uv1 - uv0; - vec2 vertical_uv_size = uv1 - uv0; - // We use top and left sizes by default and fall back to bottom and right - // when a size is empty. - if ((brush_flags & BRUSH_FLAG_SEGMENT_NINEPATCH_MIDDLE) != 0) { - repeated_stretch_size = segment_rect.p0 - prim_rect.p0; - - float epsilon = 0.001; - - // Adjust the the referecne uv size to compute vertical repetitions for - // the fill area. - vertical_uv_size.x = uv0.x - res.uv_rect.p0.x; - if (vertical_uv_size.x < epsilon || repeated_stretch_size.x < epsilon) { - vertical_uv_size.x = res.uv_rect.p1.x - uv1.x; - repeated_stretch_size.x = prim_rect.p0.x + prim_rect.size.x - - segment_rect.p0.x - segment_rect.size.x; - } - - // Adjust the the referecne uv size to compute horizontal repetitions - // for the fill area. - horizontal_uv_size.y = uv0.y - res.uv_rect.p0.y; - if (horizontal_uv_size.y < epsilon || repeated_stretch_size.y < epsilon) { - horizontal_uv_size.y = res.uv_rect.p1.y - uv1.y; - repeated_stretch_size.y = prim_rect.p0.y + prim_rect.size.y - - segment_rect.p0.y - segment_rect.size.y; - } + if ((brush_flags & BRUSH_FLAG_SEGMENT_NINEPATCH_MIDDLE) != 0) { + segment_uv_size = uv0 - res.uv_rect.p0; + repeated_stretch_size = segment_rect.p0 - prim_rect.p0; + float epsilon = 0.001; + + if (segment_uv_size.x < epsilon || repeated_stretch_size.x < epsilon) { + segment_uv_size.x = res.uv_rect.p1.x - uv1.x; + repeated_stretch_size.x = prim_rect.p0.x + prim_rect.size.x + - segment_rect.p0.x - segment_rect.size.x; } - if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) { - float uv_ratio = horizontal_uv_size.x / horizontal_uv_size.y; - stretch_size.x = repeated_stretch_size.y * uv_ratio; - } - if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) { - float uv_ratio = vertical_uv_size.y / vertical_uv_size.x; - stretch_size.y = repeated_stretch_size.x * uv_ratio; + if (segment_uv_size.y < epsilon || repeated_stretch_size.y < epsilon) { + segment_uv_size.y = res.uv_rect.p1.y - uv1.y; + repeated_stretch_size.y = prim_rect.p0.y + prim_rect.size.y + - segment_rect.p0.y - segment_rect.size.y; } + } - } else { - if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) { - stretch_size.x = segment_data.z - segment_data.x; - } - if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) { - stretch_size.y = segment_data.w - segment_data.y; - } + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) { + stretch_size.x = repeated_stretch_size.y / segment_uv_size.y * segment_uv_size.x; + } + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) { + stretch_size.y = repeated_stretch_size.x / segment_uv_size.x * segment_uv_size.y; } - if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X_ROUND) != 0) { - float nx = max(1.0, round(segment_rect.size.x / stretch_size.x)); - stretch_size.x = segment_rect.size.x / nx; + #endif + + } else { + #ifdef WR_FEATURE_REPETITION + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) { + stretch_size.x = segment_data.z - segment_data.x; } - if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND) != 0) { - float ny = max(1.0, round(segment_rect.size.y / stretch_size.y)); - stretch_size.y = segment_rect.size.y / ny; + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) { + stretch_size.y = segment_data.w - segment_data.y; } + #endif + } + + #ifdef WR_FEATURE_REPETITION + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X_ROUND) != 0) { + float nx = max(1.0, round(segment_rect.size.x / stretch_size.x)); + stretch_size.x = segment_rect.size.x / nx; + } + if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND) != 0) { + float ny = max(1.0, round(segment_rect.size.y / stretch_size.y)); + stretch_size.y = segment_rect.size.y / ny; + } #endif } float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0; - v_perspective = perspective_interpolate; + V_LAYER = res.layer; + V_PERSPECTIVE = perspective_interpolate; // Handle case where the UV coords are inverted (e.g. from an // external image). vec2 min_uv = min(uv0, uv1); vec2 max_uv = max(uv0, uv1); - v_uv_sample_bounds = vec4( + V_UV_SAMPLE_BOUNDS = vec4( min_uv + vec2(0.5), max_uv - vec2(0.5) ) / texture_size.xyxy; @@ -200,38 +192,36 @@ void brush_vs( // Derive the texture coordinates for this image, based on // whether the source image is a local-space or screen-space // image. - if (raster_space == RASTER_SCREEN) { - // Since the screen space UVs specify an arbitrary quad, do - // a bilinear interpolation to get the correct UV for this - // local position. - f = get_image_quad_uv(specific_resource_address, f); + switch (raster_space) { + case RASTER_SCREEN: { + // Since the screen space UVs specify an arbitrary quad, do + // a bilinear interpolation to get the correct UV for this + // local position. + f = get_image_quad_uv(specific_resource_address, f); + break; + } + default: + break; } #endif - // Offset and scale v_uv here to avoid doing it in the fragment shader. + // Offset and scale V_UV here to avoid doing it in the fragment shader. vec2 repeat = local_rect.size / stretch_size; - v_uv = mix(uv0, uv1, f) - min_uv; - v_uv /= texture_size; - v_uv *= repeat.xy; + V_UV = mix(uv0, uv1, f) - min_uv; + V_UV /= texture_size; + V_UV *= repeat.xy; if (perspective_interpolate == 0.0) { - v_uv *= vi.world_pos.w; + V_UV *= vi.world_pos.w; } #ifdef WR_FEATURE_TEXTURE_RECT - v_uv_bounds = vec4(0.0, 0.0, vec2(textureSize(sColor0))); + V_UV_BOUNDS = vec4(0.0, 0.0, vec2(textureSize(sColor0))); #else - v_uv_bounds = vec4(min_uv, max_uv) / texture_size.xyxy; -#endif - -#ifdef WR_FEATURE_REPETITION - // Normalize UV to 0..1 scale only if using repetition. Otherwise, leave - // UVs unnormalized since we won't compute a modulus without repetition - // enabled. - v_uv /= (v_uv_bounds.zw - v_uv_bounds.xy); + V_UV_BOUNDS = vec4(min_uv, max_uv) / texture_size.xyxy; #endif #ifdef WR_FEATURE_ALPHA_PASS - v_tile_repeat = repeat.xy; + V_TILE_REPEAT = repeat.xy; float opacity = float(prim_user_data.z) / 65535.0; switch (blend_mode) { @@ -246,43 +236,32 @@ void brush_vs( switch (color_mode) { case COLOR_MODE_ALPHA: - case COLOR_MODE_BITMAP_SHADOW: - #ifdef SWGL_BLEND - swgl_blendDropShadow(image_data.color); - v_mask_swizzle = vec2(1.0, 0.0); - v_color = vec4(1.0); - #else - v_mask_swizzle = vec2(0.0, 1.0); - v_color = image_data.color; - #endif + case COLOR_MODE_BITMAP: + V_MASK_SWIZZLE = vec2(0.0, 1.0); + V_COLOR = image_data.color; break; case COLOR_MODE_SUBPX_BG_PASS2: + case COLOR_MODE_SUBPX_DUAL_SOURCE: case COLOR_MODE_IMAGE: - v_mask_swizzle = vec2(1.0, 0.0); - v_color = image_data.color; + V_MASK_SWIZZLE = vec2(1.0, 0.0); + V_COLOR = image_data.color; break; case COLOR_MODE_SUBPX_CONST_COLOR: case COLOR_MODE_SUBPX_BG_PASS0: case COLOR_MODE_COLOR_BITMAP: - v_mask_swizzle = vec2(1.0, 0.0); - v_color = vec4(image_data.color.a); + V_MASK_SWIZZLE = vec2(1.0, 0.0); + V_COLOR = vec4(image_data.color.a); break; case COLOR_MODE_SUBPX_BG_PASS1: - v_mask_swizzle = vec2(-1.0, 1.0); - v_color = vec4(image_data.color.a) * image_data.background_color; - break; - case COLOR_MODE_SUBPX_DUAL_SOURCE: - v_mask_swizzle = vec2(image_data.color.a, 0.0); - v_color = image_data.color; - break; - case COLOR_MODE_MULTIPLY_DUAL_SOURCE: - v_mask_swizzle = vec2(-image_data.color.a, image_data.color.a); - v_color = image_data.color; + V_MASK_SWIZZLE = vec2(-1.0, 1.0); + V_COLOR = vec4(image_data.color.a) * image_data.background_color; break; default: - v_mask_swizzle = vec2(0.0); - v_color = vec4(1.0); + V_MASK_SWIZZLE = vec2(0.0); + V_COLOR = vec4(1.0); } + + V_LOCAL_POS = vi.local_pos; #endif } #endif @@ -290,63 +269,62 @@ void brush_vs( #ifdef WR_FRAGMENT_SHADER vec2 compute_repeated_uvs(float perspective_divisor) { -#ifdef WR_FEATURE_REPETITION - vec2 uv_size = v_uv_bounds.zw - v_uv_bounds.xy; + vec2 uv_size = V_UV_BOUNDS.zw - V_UV_BOUNDS.xy; - #ifdef WR_FEATURE_ALPHA_PASS +#ifdef WR_FEATURE_ALPHA_PASS // This prevents the uv on the top and left parts of the primitive that was inflated // for anti-aliasing purposes from going beyound the range covered by the regular // (non-inflated) primitive. - vec2 local_uv = max(v_uv * perspective_divisor, vec2(0.0)); + vec2 local_uv = max(V_UV * perspective_divisor, vec2(0.0)); // Handle horizontal and vertical repetitions. - vec2 repeated_uv = fract(local_uv) * uv_size + v_uv_bounds.xy; + vec2 repeated_uv = mod(local_uv, uv_size) + V_UV_BOUNDS.xy; // This takes care of the bottom and right inflated parts. // We do it after the modulo because the latter wraps around the values exactly on // the right and bottom edges, which we do not want. - if (local_uv.x >= v_tile_repeat.x) { - repeated_uv.x = v_uv_bounds.z; + if (local_uv.x >= V_TILE_REPEAT.x * uv_size.x) { + repeated_uv.x = V_UV_BOUNDS.z; } - if (local_uv.y >= v_tile_repeat.y) { - repeated_uv.y = v_uv_bounds.w; + if (local_uv.y >= V_TILE_REPEAT.y * uv_size.y) { + repeated_uv.y = V_UV_BOUNDS.w; } - #else - vec2 repeated_uv = fract(v_uv * perspective_divisor) * uv_size + v_uv_bounds.xy; - #endif - - return repeated_uv; #else - return v_uv * perspective_divisor + v_uv_bounds.xy; + vec2 repeated_uv = mod(V_UV * perspective_divisor, uv_size) + V_UV_BOUNDS.xy; #endif + + return repeated_uv; } -Fragment brush_fs() { - float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective); +Fragment image_brush_fs() { + float perspective_divisor = mix(gl_FragCoord.w, 1.0, V_PERSPECTIVE); + +#ifdef WR_FEATURE_REPETITION vec2 repeated_uv = compute_repeated_uvs(perspective_divisor); +#else + vec2 repeated_uv = V_UV * perspective_divisor + V_UV_BOUNDS.xy; +#endif // Clamp the uvs to avoid sampling artifacts. - vec2 uv = clamp(repeated_uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw); + vec2 uv = clamp(repeated_uv, V_UV_SAMPLE_BOUNDS.xy, V_UV_SAMPLE_BOUNDS.zw); - vec4 texel = TEX_SAMPLE(sColor0, uv); + vec4 texel = TEX_SAMPLE(sColor0, vec3(uv, V_LAYER)); Fragment frag; #ifdef WR_FEATURE_ALPHA_PASS #ifdef WR_FEATURE_ANTIALIASING - float alpha = antialias_brush(); + float alpha = init_transform_fs(V_LOCAL_POS); #else float alpha = 1.0; #endif - #ifndef WR_FEATURE_DUAL_SOURCE_BLENDING - texel.rgb = texel.rgb * v_mask_swizzle.x + texel.aaa * v_mask_swizzle.y; - #endif + texel.rgb = texel.rgb * V_MASK_SWIZZLE.x + texel.aaa * V_MASK_SWIZZLE.y; vec4 alpha_mask = texel * alpha; - frag.color = v_color * alpha_mask; + frag.color = V_COLOR * alpha_mask; #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING - frag.blend = alpha_mask * v_mask_swizzle.x + alpha_mask.aaaa * v_mask_swizzle.y; + frag.blend = alpha_mask * V_COLOR.a; #endif #else frag.color = texel; @@ -354,50 +332,15 @@ Fragment brush_fs() { return frag; } - -#if defined(SWGL_DRAW_SPAN) && (!defined(WR_FEATURE_ALPHA_PASS) || !defined(WR_FEATURE_DUAL_SOURCE_BLENDING)) -void swgl_drawSpanRGBA8() { - if (!swgl_isTextureRGBA8(sColor0)) { - return; - } - - #ifdef WR_FEATURE_ALPHA_PASS - if (v_mask_swizzle != vec2(1.0, 0.0)) { - return; - } - #endif - - float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, v_perspective); - - #ifdef WR_FEATURE_REPETITION - // Get the UVs before any repetition, scaling, or offsetting has occurred... - vec2 uv = v_uv * perspective_divisor; - #else - vec2 uv = compute_repeated_uvs(perspective_divisor); - #endif - - #ifdef WR_FEATURE_ALPHA_PASS - if (v_color != vec4(1.0)) { - #ifdef WR_FEATURE_REPETITION - swgl_commitTextureRepeatColorRGBA8(sColor0, uv, v_tile_repeat, v_uv_bounds, v_uv_sample_bounds, v_color); - #else - swgl_commitTextureColorRGBA8(sColor0, uv, v_uv_sample_bounds, v_color); - #endif - return; - } - // No color scaling required, so just fall through to a normal textured span... - #endif - - #ifdef WR_FEATURE_REPETITION - #ifdef WR_FEATURE_ALPHA_PASS - swgl_commitTextureRepeatRGBA8(sColor0, uv, v_tile_repeat, v_uv_bounds, v_uv_sample_bounds); - #else - swgl_commitTextureRepeatRGBA8(sColor0, uv, vec2(0.0), v_uv_bounds, v_uv_sample_bounds); - #endif - #else - swgl_commitTextureRGBA8(sColor0, uv, v_uv_sample_bounds); - #endif -} #endif -#endif +// Undef macro names that could be re-defined by other shaders. +#undef V_LOCAL_POS +#undef V_UV +#undef V_COLOR +#undef V_MASK_SWIZZLE +#undef V_TILE_REPEAT +#undef V_UV_BOUNDS +#undef V_UV_SAMPLE_BOUNDS +#undef V_LAYER +#undef V_PERSPECTIVE diff --git a/third_party/webrender/webrender/res/brush_linear_gradient.glsl b/third_party/webrender/webrender/res/brush_linear_gradient.glsl index fe6b6ab8045..6e6933252cd 100644 --- a/third_party/webrender/webrender/res/brush_linear_gradient.glsl +++ b/third_party/webrender/webrender/res/brush_linear_gradient.glsl @@ -2,12 +2,31 @@ * 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 VECS_PER_SPECIFIC_BRUSH 2 +#define VECS_PER_LINEAR_GRADIENT_BRUSH 2 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_LINEAR_GRADIENT_BRUSH -#include shared,prim_shared,brush,gradient_shared +#define WR_BRUSH_VS_FUNCTION linear_gradient_brush_vs +#define WR_BRUSH_FS_FUNCTION linear_gradient_brush_fs -flat varying float v_start_offset; -flat varying vec2 v_scale_dir; +#include shared,prim_shared,brush + +#define V_GRADIENT_ADDRESS flat_varying_highp_int_address_0 + +#define V_START_POINT flat_varying_vec4_0.xy +#define V_SCALE_DIR flat_varying_vec4_0.zw +// Size of the gradient pattern's rectangle, used to compute horizontal and vertical +// repetitions. Not to be confused with another kind of repetition of the pattern +// which happens along the gradient stops. +#define V_REPEATED_SIZE flat_varying_vec4_1.xy +// Repetition along the gradient stops. +#define V_GRADIENT_REPEAT flat_varying_vec4_1.z + +#define V_POS varying_vec4_0.zw + +#ifdef WR_FEATURE_ALPHA_PASS +#define V_LOCAL_POS varying_vec4_0.xy +#define V_TILE_REPEAT flat_varying_vec4_2.xy +#endif #ifdef WR_VERTEX_SHADER @@ -26,7 +45,7 @@ Gradient fetch_gradient(int address) { ); } -void brush_vs( +void linear_gradient_brush_vs( VertexInfo vi, int prim_address, RectWithSize local_rect, @@ -40,63 +59,79 @@ void brush_vs( ) { Gradient gradient = fetch_gradient(prim_address); - write_gradient_vertex( - vi, - local_rect, - segment_rect, - prim_user_data, - brush_flags, - texel_rect, - gradient.extend_mode, - gradient.stretch_size - ); + if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) { + V_POS = (vi.local_pos - segment_rect.p0) / segment_rect.size; + V_POS = V_POS * (texel_rect.zw - texel_rect.xy) + texel_rect.xy; + V_POS = V_POS * local_rect.size; + } else { + V_POS = vi.local_pos - local_rect.p0; + } vec2 start_point = gradient.start_end_point.xy; vec2 end_point = gradient.start_end_point.zw; vec2 dir = end_point - start_point; - // Normalize UV and offsets to 0..1 scale. - v_scale_dir = dir / dot(dir, dir); - v_start_offset = dot(start_point, v_scale_dir); - v_scale_dir *= v_repeated_size; + V_START_POINT = start_point; + V_SCALE_DIR = dir / dot(dir, dir); + + vec2 tile_repeat = local_rect.size / gradient.stretch_size; + V_REPEATED_SIZE = gradient.stretch_size; + + V_GRADIENT_ADDRESS = prim_user_data.x; + + // Whether to repeat the gradient along the line instead of clamping. + V_GRADIENT_REPEAT = float(gradient.extend_mode != EXTEND_MODE_CLAMP); + +#ifdef WR_FEATURE_ALPHA_PASS + V_TILE_REPEAT = tile_repeat; + V_LOCAL_POS = vi.local_pos; +#endif } #endif #ifdef WR_FRAGMENT_SHADER -float get_gradient_offset(vec2 pos) { - // Project position onto a direction vector to compute offset. - return dot(pos, v_scale_dir) - v_start_offset; -} +Fragment linear_gradient_brush_fs() { + +#ifdef WR_FEATURE_ALPHA_PASS + // Handle top and left inflated edges (see brush_image). + vec2 local_pos = max(V_POS, vec2(0.0)); + + // Apply potential horizontal and vertical repetitions. + vec2 pos = mod(local_pos, V_REPEATED_SIZE); -Fragment brush_fs() { - vec4 color = sample_gradient(get_gradient_offset(compute_repeated_pos())); + vec2 prim_size = V_REPEATED_SIZE * V_TILE_REPEAT; + // Handle bottom and right inflated edges (see brush_image). + if (local_pos.x >= prim_size.x) { + pos.x = V_REPEATED_SIZE.x; + } + if (local_pos.y >= prim_size.y) { + pos.y = V_REPEATED_SIZE.y; + } +#else + // Apply potential horizontal and vertical repetitions. + vec2 pos = mod(V_POS, V_REPEATED_SIZE); +#endif + + float offset = dot(pos - V_START_POINT, V_SCALE_DIR); + + vec4 color = sample_gradient(V_GRADIENT_ADDRESS, + offset, + V_GRADIENT_REPEAT); #ifdef WR_FEATURE_ALPHA_PASS - color *= antialias_brush(); + color *= init_transform_fs(V_LOCAL_POS); #endif return Fragment(color); } - -#ifdef SWGL_DRAW_SPAN -void swgl_drawSpanRGBA8() { - int address = swgl_validateGradient(sGpuCache, get_gpu_cache_uv(v_gradient_address), int(GRADIENT_ENTRIES + 2.0)); - if (address < 0) { - return; - } - #ifndef WR_FEATURE_ALPHA_PASS - swgl_commitLinearGradientRGBA8(sGpuCache, address, GRADIENT_ENTRIES, v_gradient_repeat != 0.0, - get_gradient_offset(v_pos)); - #else - while (swgl_SpanLength > 0) { - float offset = get_gradient_offset(compute_repeated_pos()); - if (v_gradient_repeat != 0.0) offset = fract(offset); - float entry = clamp_gradient_entry(offset); - swgl_commitGradientRGBA8(sGpuCache, address, entry); - v_pos += swgl_interpStep(v_pos); - } - #endif -} #endif -#endif +// Undef macro names that could be re-defined by other shaders. +#undef V_GRADIENT_ADDRESS +#undef V_START_POINT +#undef V_SCALE_DIR +#undef V_REPEATED_SIZE +#undef V_GRADIENT_REPEAT +#undef V_POS +#undef V_LOCAL_POS +#undef V_TILE_REPEAT diff --git a/third_party/webrender/webrender/res/brush_mix_blend.glsl b/third_party/webrender/webrender/res/brush_mix_blend.glsl index 0a02ef00da3..774f2fcb677 100644 --- a/third_party/webrender/webrender/res/brush_mix_blend.glsl +++ b/third_party/webrender/webrender/res/brush_mix_blend.glsl @@ -2,55 +2,25 @@ * 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 VECS_PER_SPECIFIC_BRUSH 3 -#define WR_FEATURE_TEXTURE_2D +#define VECS_PER_MIX_BLEND_BRUSH 3 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_MIX_BLEND_BRUSH -#include shared,prim_shared,brush - -// UV and bounds for the source image -varying vec2 v_src_uv; -flat varying vec4 v_src_uv_sample_bounds; - -// UV and bounds for the backdrop image -varying vec2 v_backdrop_uv; -flat varying vec4 v_backdrop_uv_sample_bounds; - -#if defined(PLATFORM_ANDROID) && !defined(SWGL) -// Work around Adreno 3xx driver bug. See the v_perspective comment in -// brush_image or bug 1630356 for details. -flat varying vec2 v_perspective_vec; -#define v_perspective v_perspective_vec.x -#else -// Flag to allow perspective interpolation of UV. -flat varying float v_perspective; -#endif +#define WR_BRUSH_VS_FUNCTION mix_blend_brush_vs +#define WR_BRUSH_FS_FUNCTION mix_blend_brush_fs -// mix-blend op -flat varying int v_op; +#include shared,prim_shared,brush -#ifdef WR_VERTEX_SHADER +#define V_SRC_UV varying_vec4_0.xy +#define V_SRC_LAYER varying_vec4_0.w -void get_uv( - int res_address, - vec2 f, - ivec2 texture_size, - float perspective_f, - out vec2 out_uv, - out vec4 out_uv_sample_bounds -) { - ImageSource res = fetch_image_source(res_address); - vec2 uv0 = res.uv_rect.p0; - vec2 uv1 = res.uv_rect.p1; +#define V_BACKDROP_UV varying_vec4_1.xy +#define V_BACKDROP_LAYER varying_vec4_1.w - vec2 inv_texture_size = vec2(1.0) / vec2(texture_size); - f = get_image_quad_uv(res_address, f); - vec2 uv = mix(uv0, uv1, f); +#define V_OP flat_varying_ivec4_0.x - out_uv = uv * inv_texture_size * perspective_f; - out_uv_sample_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) * inv_texture_size.xyxy; -} +#ifdef WR_VERTEX_SHADER -void brush_vs( +void mix_blend_brush_vs( VertexInfo vi, int prim_address, RectWithSize local_rect, @@ -62,29 +32,26 @@ void brush_vs( int brush_flags, vec4 unused ) { - vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size; - float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0; - float perspective_f = mix(vi.world_pos.w, 1.0, perspective_interpolate); - v_perspective = perspective_interpolate; - v_op = prim_user_data.x; - - get_uv( - prim_user_data.y, - f, - TEX_SIZE(sColor0).xy, - 1.0, - v_backdrop_uv, - v_backdrop_uv_sample_bounds - ); - - get_uv( - prim_user_data.z, - f, - TEX_SIZE(sColor1).xy, - perspective_f, - v_src_uv, - v_src_uv_sample_bounds - ); + //Note: this is unsafe for `vi.world_pos.w <= 0.0` + vec2 device_pos = vi.world_pos.xy * pic_task.device_pixel_scale / max(0.0, vi.world_pos.w); + vec2 texture_size = vec2(textureSize(sPrevPassColor, 0)); + V_OP = prim_user_data.x; + + PictureTask src_task = fetch_picture_task(prim_user_data.z); + vec2 src_device_pos = vi.world_pos.xy * (src_task.device_pixel_scale / max(0.0, vi.world_pos.w)); + vec2 src_uv = src_device_pos + + src_task.common_data.task_rect.p0 - + src_task.content_origin; + V_SRC_UV = src_uv / texture_size; + V_SRC_LAYER = src_task.common_data.texture_layer_index; + + RenderTaskCommonData backdrop_task = fetch_render_task_common_data(prim_user_data.y); + float src_to_backdrop_scale = pic_task.device_pixel_scale / src_task.device_pixel_scale; + vec2 backdrop_uv = device_pos + + backdrop_task.task_rect.p0 - + src_task.content_origin * src_to_backdrop_scale; + V_BACKDROP_UV = backdrop_uv / texture_size; + V_BACKDROP_LAYER = backdrop_task.texture_layer_index; } #endif @@ -246,16 +213,9 @@ const int MixBlendMode_Saturation = 13; const int MixBlendMode_Color = 14; const int MixBlendMode_Luminosity = 15; -Fragment brush_fs() { - float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective); - - vec2 src_uv = v_src_uv * perspective_divisor; - src_uv = clamp(src_uv, v_src_uv_sample_bounds.xy, v_src_uv_sample_bounds.zw); - - vec2 backdrop_uv = clamp(v_backdrop_uv, v_backdrop_uv_sample_bounds.xy, v_backdrop_uv_sample_bounds.zw); - - vec4 Cb = texture(sColor0, backdrop_uv); - vec4 Cs = texture(sColor1, src_uv); +Fragment mix_blend_brush_fs() { + vec4 Cb = textureLod(sPrevPassColor, vec3(V_BACKDROP_UV, V_BACKDROP_LAYER), 0.0); + vec4 Cs = textureLod(sPrevPassColor, vec3(V_SRC_UV, V_SRC_LAYER), 0.0); // The mix-blend-mode functions assume no premultiplied alpha if (Cb.a != 0.0) { @@ -269,7 +229,7 @@ Fragment brush_fs() { // Return yellow if none of the branches match (shouldn't happen). vec4 result = vec4(1.0, 1.0, 0.0, 1.0); - switch (v_op) { + switch (V_OP) { case MixBlendMode_Multiply: result.rgb = Multiply(Cb.rgb, Cs.rgb); break; @@ -333,3 +293,10 @@ Fragment brush_fs() { return Fragment(result); } #endif + +// Undef macro names that could be re-defined by other shaders. +#undef V_SRC_UV +#undef V_SRC_LAYER +#undef V_BACKDROP_UV +#undef V_BACKDROP_LAYER +#undef V_OP diff --git a/third_party/webrender/webrender/res/brush_multi.glsl b/third_party/webrender/webrender/res/brush_multi.glsl new file mode 100644 index 00000000000..447186b1fcb --- /dev/null +++ b/third_party/webrender/webrender/res/brush_multi.glsl @@ -0,0 +1,296 @@ +/* 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/. */ + +// The multi-brush shader is capable of rendering most types of brushes +// if they are enabled via WR_FEATURE_*_BRUSH defines. +// 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 "uber-shader". + + +#define WR_FEATURE_MULTI_BRUSH + +// These constants must match the BrushShaderKind enum in gpu_types.rs. +#define BRUSH_KIND_SOLID 1 +#define BRUSH_KIND_IMAGE 2 +#define BRUSH_KIND_TEXT 3 +#define BRUSH_KIND_LINEAR_GRADIENT 4 +#define BRUSH_KIND_RADIAL_GRADIENT 5 +#define BRUSH_KIND_CONIC_GRADIENT 6 +#define BRUSH_KIND_BLEND 7 +#define BRUSH_KIND_MIX_BLEND 8 +#define BRUSH_KIND_YV 9 +#define BRUSH_KIND_OPACITY 10 + +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 + +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + +#ifdef WR_FEATURE_SOLID_BRUSH +#include brush_solid +#endif + +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + +#ifdef WR_FEATURE_BLEND_BRUSH +#include brush_blend +#endif + +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + +#ifdef WR_FEATURE_MIX_BLEND_BRUSH +#include brush_mix_blend +#endif + +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + +#ifdef WR_FEATURE_LINEAR_GRADIENT_BRUSH +#include brush_linear_gradient +#endif + +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + +#ifdef WR_FEATURE_RADIAL_GRADIENT_BRUSH +#include brush_radial_gradient +#endif + +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + +#ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH +#include brush_conic_gradient +#endif + +#undef VECS_PER_SPECIFIC_BRUSH +#undef WR_BRUSH_VS_FUNCTION +#undef WR_BRUSH_FS_FUNCTION + +#ifdef WR_FEATURE_OPACITY_BRUSH +#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 + // is enabled first to satisfy ANGLE validation. + default: + + #ifdef WR_FEATURE_IMAGE_BRUSH + case BRUSH_KIND_IMAGE: return VECS_PER_IMAGE_BRUSH; + #endif + + #ifdef WR_FEATURE_IMAGE_BRUSH + case BRUSH_KIND_SOLID: return VECS_PER_SOLID_BRUSH; + #endif + + #ifdef WR_FEATURE_BLEND_BRUSH + case BRUSH_KIND_BLEND: return VECS_PER_BLEND_BRUSH; + #endif + + #ifdef WR_FEATURE_MIX_BLEND_BRUSH + case BRUSH_KIND_MIX_BLEND: return VECS_PER_MIX_BLEND_BRUSH; + #endif + + #ifdef WR_FEATURE_LINEAR_GRADIENT_BRUSH + case BRUSH_KIND_LINEAR_GRADIENT: return VECS_PER_LINEAR_GRADIENT_BRUSH; + #endif + + #ifdef WR_FEATURE_RADIAL_GRADIENT_BRUSH + case BRUSH_KIND_RADIAL_GRADIENT: return VECS_PER_RADIAL_GRADIENT_BRUSH; + #endif + + + #ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH + case BRUSH_KIND_CONIC_GRADIENT: return VECS_PER_CONIC_GRADIENT_BRUSH; + #endif + + #ifdef WR_FEATURE_OPACITY_BRUSH + case BRUSH_KIND_OPACITY: return VECS_PER_OPACITY_BRUSH; + #endif + } +} + +#define BRUSH_VS_PARAMS vi, prim_address, local_rect, segment_rect, \ + prim_user_data, specific_resource_address, transform, pic_task, \ + brush_flags, texel_rect + + +#ifdef WR_VERTEX_SHADER +void multi_brush_vs( + VertexInfo vi, + int prim_address, + RectWithSize local_rect, + RectWithSize segment_rect, + ivec4 prim_user_data, + int specific_resource_address, + mat4 transform, + PictureTask pic_task, + int brush_flags, + vec4 texel_rect, + int brush_kind +) { + switch (brush_kind) { + default: + + #ifdef WR_FEATURE_IMAGE_BRUSH + case BRUSH_KIND_IMAGE: + image_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + + #ifdef WR_FEATURE_SOLID_BRUSH + case BRUSH_KIND_SOLID: + solid_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + + #ifdef WR_FEATURE_BLEND_BRUSH + case BRUSH_KIND_BLEND: + blend_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + + #ifdef WR_FEATURE_MIX_BLEND_BRUSH + case BRUSH_KIND_MIX_BLEND: + mix_blend_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + + #ifdef WR_FEATURE_LINEAR_GRADIENT_BRUSH + case BRUSH_KIND_LINEAR_GRADIENT: + linear_gradient_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + + #ifdef WR_FEATURE_RADIAL_GRADIENT_BRUSH + case BRUSH_KIND_RADIAL_GRADIENT: + radial_gradient_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + + #ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH + case BRUSH_KIND_CONIC_GRADIENT: + conic_gradient_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + + #ifdef WR_FEATURE_OPACITY_BRUSH + case BRUSH_KIND_OPACITY: + opacity_brush_vs(BRUSH_VS_PARAMS); + break; + #endif + } +} + +#endif // WR_VERTEX_SHADER + +#ifdef WR_FRAGMENT_SHADER + +Fragment multi_brush_fs(int brush_kind) { + switch (brush_kind) { + default: + + #ifdef WR_FEATURE_IMAGE_BRUSH + case BRUSH_KIND_IMAGE: return image_brush_fs(); + #endif + + #ifdef WR_FEATURE_SOLID_BRUSH + case BRUSH_KIND_SOLID: return solid_brush_fs(); + #endif + + #ifdef WR_FEATURE_BLEND_BRUSH + case BRUSH_KIND_BLEND: return blend_brush_fs(); + #endif + + #ifdef WR_FEATURE_MIX_BLEND_BRUSH + case BRUSH_KIND_MIX_BLEND: return mix_blend_brush_fs(); + #endif + + #ifdef WR_FEATURE_LINEAR_GRADIENT_BRUSH + case BRUSH_KIND_LINEAR_GRADIENT: return linear_gradient_brush_fs(); + #endif + + #ifdef WR_FEATURE_RADIAL_GRADIENT_BRUSH + case BRUSH_KIND_RADIAL_GRADIENT: return radial_gradient_brush_fs(); + #endif + + #ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH + case BRUSH_KIND_CONIC_GRADIENT: return conic_gradient_brush_fs(); + #endif + + #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 + } +} + +#endif diff --git a/third_party/webrender/webrender/res/brush_opacity.glsl b/third_party/webrender/webrender/res/brush_opacity.glsl index 5355b4ec2ec..e2a0b8e1c3a 100644 --- a/third_party/webrender/webrender/res/brush_opacity.glsl +++ b/third_party/webrender/webrender/res/brush_opacity.glsl @@ -2,32 +2,30 @@ * 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 VECS_PER_SPECIFIC_BRUSH 3 -#define WR_FEATURE_TEXTURE_2D +#define VECS_PER_OPACITY_BRUSH 3 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_OPACITY_BRUSH + +#define WR_BRUSH_VS_FUNCTION opacity_brush_vs +#define WR_BRUSH_FS_FUNCTION opacity_brush_fs #include shared,prim_shared,brush // Interpolated UV coordinates to sample. -varying vec2 v_uv; - -// Normalized bounds of the source image in the texture, adjusted to avoid -// sampling artifacts. -flat varying vec4 v_uv_sample_bounds; - -#if defined(PLATFORM_ANDROID) && !defined(SWGL) -// Work around Adreno 3xx driver bug. See the v_perspective comment in -// brush_image or bug 1630356 for details. -flat varying vec2 v_perspective_vec; -#define v_perspective v_perspective_vec.x -#else +#define V_UV varying_vec4_0.zw +#define V_LOCAL_POS varying_vec4_0.xy + +// Normalized bounds of the source image in the texture. +#define V_UV_BOUNDS flat_varying_vec4_1 + +// Layer index to sample. +#define V_LAYER flat_varying_vec4_2.x // Flag to allow perspective interpolation of UV. -flat varying float v_perspective; -#endif +#define V_PERSPECTIVE flat_varying_vec4_2.y -flat varying float v_opacity; +#define V_OPACITY flat_varying_vec4_2.z #ifdef WR_VERTEX_SHADER -void brush_vs( +void opacity_brush_vs( VertexInfo vi, int prim_address, RectWithSize local_rect, @@ -39,52 +37,55 @@ void brush_vs( int brush_flags, vec4 unused ) { - ImageSource res = fetch_image_source(prim_user_data.x); + ImageResource res = fetch_image_resource(prim_user_data.x); vec2 uv0 = res.uv_rect.p0; vec2 uv1 = res.uv_rect.p1; - vec2 texture_size = vec2(TEX_SIZE(sColor0).xy); + vec2 texture_size = vec2(textureSize(sColor0, 0).xy); vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size; f = get_image_quad_uv(prim_user_data.x, f); vec2 uv = mix(uv0, uv1, f); float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0; - v_uv = uv / texture_size * mix(vi.world_pos.w, 1.0, perspective_interpolate); - v_perspective = perspective_interpolate; + V_UV = uv / texture_size * mix(vi.world_pos.w, 1.0, perspective_interpolate); + V_LAYER = res.layer; + V_PERSPECTIVE = perspective_interpolate; - v_uv_sample_bounds = vec4(uv0 + vec2(0.5), uv1 - vec2(0.5)) / texture_size.xyxy; + // TODO: The image shader treats this differently: deflate the rect by half a pixel on each side and + // clamp the uv in the frame shader. Does it make sense to do the same here? + V_UV_BOUNDS = vec4(uv0, uv1) / texture_size.xyxy; + V_LOCAL_POS = vi.local_pos; - v_opacity = clamp(float(prim_user_data.y) / 65536.0, 0.0, 1.0); + V_OPACITY = float(prim_user_data.y) / 65536.0; } #endif #ifdef WR_FRAGMENT_SHADER -Fragment brush_fs() { - float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective); - vec2 uv = v_uv * perspective_divisor; - // Clamp the uvs to avoid sampling artifacts. - uv = clamp(uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw); +Fragment opacity_brush_fs() { + float perspective_divisor = mix(gl_FragCoord.w, 1.0, V_PERSPECTIVE); + vec2 uv = V_UV * perspective_divisor; + vec4 Cs = texture(sColor0, vec3(uv, V_LAYER)); - // No need to un-premultiply since we'll only apply a factor to the alpha. - vec4 color = texture(sColor0, uv); + // Un-premultiply the input. + float alpha = Cs.a; + vec3 color = alpha != 0.0 ? Cs.rgb / alpha : Cs.rgb; - float alpha = v_opacity; + alpha *= V_OPACITY; - #ifdef WR_FEATURE_ALPHA_PASS - alpha *= antialias_brush(); - #endif + // Fail-safe to ensure that we don't sample outside the rendered + // portion of a blend source. + alpha *= min(point_inside_rect(uv, V_UV_BOUNDS.xy, V_UV_BOUNDS.zw), + init_transform_fs(V_LOCAL_POS)); - // Pre-multiply the contribution of the opacity factor. - return Fragment(alpha * color); -} - -#if defined(SWGL_DRAW_SPAN) && !defined(WR_FEATURE_DUAL_SOURCE_BLENDING) -void swgl_drawSpanRGBA8() { - float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, v_perspective); - vec2 uv = v_uv * perspective_divisor; - - swgl_commitTextureLinearColorRGBA8(sColor0, uv, v_uv_sample_bounds, v_opacity); + // Pre-multiply the alpha into the output value. + return Fragment(alpha * vec4(color, 1.0)); } #endif -#endif +// Undef macro names that could be re-defined by other shaders. +#undef V_UV +#undef V_LOCAL_POS +#undef V_UV_BOUNDS +#undef V_LAYER +#undef V_PERSPECTIVE +#undef V_OPACITY diff --git a/third_party/webrender/webrender/res/brush_radial_gradient.glsl b/third_party/webrender/webrender/res/brush_radial_gradient.glsl new file mode 100644 index 00000000000..b178d606e1f --- /dev/null +++ b/third_party/webrender/webrender/res/brush_radial_gradient.glsl @@ -0,0 +1,176 @@ +/* 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/. */ + +#define VECS_PER_RADIAL_GRADIENT_BRUSH 2 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_RADIAL_GRADIENT_BRUSH + +#define WR_BRUSH_VS_FUNCTION radial_gradient_brush_vs +#define WR_BRUSH_FS_FUNCTION radial_gradient_brush_fs + +#include shared,prim_shared,brush + +#define V_GRADIENT_ADDRESS flat_varying_highp_int_address_0 + +#define V_CENTER flat_varying_vec4_0.xy +#define V_START_RADIUS flat_varying_vec4_0.z +#define V_END_RADIUS flat_varying_vec4_0.w + +#define V_REPEATED_SIZE flat_varying_vec4_1.xy +#define V_GRADIENT_REPEAT flat_varying_vec4_1.z + +#define V_POS varying_vec4_0.zw + +#ifdef WR_FEATURE_ALPHA_PASS +#define V_LOCAL_POS varying_vec4_0.xy +#define V_TILE_REPEAT flat_varying_vec4_2.xy +#endif + +#ifdef WR_VERTEX_SHADER + +struct RadialGradient { + vec4 center_start_end_radius; + float ratio_xy; + int extend_mode; + vec2 stretch_size; +}; + +RadialGradient fetch_radial_gradient(int address) { + vec4 data[2] = fetch_from_gpu_cache_2(address); + return RadialGradient( + data[0], + data[1].x, + int(data[1].y), + data[1].zw + ); +} + +void radial_gradient_brush_vs( + VertexInfo vi, + int prim_address, + RectWithSize local_rect, + RectWithSize segment_rect, + ivec4 prim_user_data, + int specific_resource_address, + mat4 transform, + PictureTask pic_task, + int brush_flags, + vec4 texel_rect +) { + RadialGradient gradient = fetch_radial_gradient(prim_address); + + if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) { + V_POS = (vi.local_pos - segment_rect.p0) / segment_rect.size; + V_POS = V_POS * (texel_rect.zw - texel_rect.xy) + texel_rect.xy; + V_POS = V_POS * local_rect.size; + } else { + V_POS = vi.local_pos - local_rect.p0; + } + + V_CENTER = gradient.center_start_end_radius.xy; + V_START_RADIUS = gradient.center_start_end_radius.z; + V_END_RADIUS = gradient.center_start_end_radius.w; + + // Transform all coordinates by the y scale so the + // fragment shader can work with circles + vec2 tile_repeat = local_rect.size / gradient.stretch_size; + V_POS.y *= gradient.ratio_xy; + V_CENTER.y *= gradient.ratio_xy; + V_REPEATED_SIZE = gradient.stretch_size; + V_REPEATED_SIZE.y *= gradient.ratio_xy; + + V_GRADIENT_ADDRESS = prim_user_data.x; + + // Whether to repeat the gradient instead of clamping. + V_GRADIENT_REPEAT = float(gradient.extend_mode != EXTEND_MODE_CLAMP); + +#ifdef WR_FEATURE_ALPHA_PASS + V_TILE_REPEAT = tile_repeat.xy; + V_LOCAL_POS = vi.local_pos; +#endif +} +#endif + +#ifdef WR_FRAGMENT_SHADER +Fragment radial_gradient_brush_fs() { + +#ifdef WR_FEATURE_ALPHA_PASS + // Handle top and left inflated edges (see brush_image). + vec2 local_pos = max(V_POS, vec2(0.0)); + + // Apply potential horizontal and vertical repetitions. + vec2 pos = mod(local_pos, V_REPEATED_SIZE); + + vec2 prim_size = V_REPEATED_SIZE * V_TILE_REPEAT; + // Handle bottom and right inflated edges (see brush_image). + if (local_pos.x >= prim_size.x) { + pos.x = V_REPEATED_SIZE.x; + } + if (local_pos.y >= prim_size.y) { + pos.y = V_REPEATED_SIZE.y; + } +#else + // Apply potential horizontal and vertical repetitions. + vec2 pos = mod(V_POS, V_REPEATED_SIZE); +#endif + + vec2 pd = pos - V_CENTER; + float rd = V_END_RADIUS - V_START_RADIUS; + + // Solve for t in length(t - pd) = V_START_RADIUS + t * rd + // using a quadratic equation in form of At^2 - 2Bt + C = 0 + float A = -(rd * rd); + float B = V_START_RADIUS * rd; + float C = dot(pd, pd) - V_START_RADIUS * V_START_RADIUS; + + float offset; + if (A == 0.0) { + // Since A is 0, just solve for -2Bt + C = 0 + if (B == 0.0) { + discard; + } + float t = 0.5 * C / B; + if (V_START_RADIUS + rd * t >= 0.0) { + offset = t; + } else { + discard; + } + } else { + float discr = B * B - A * C; + if (discr < 0.0) { + discard; + } + discr = sqrt(discr); + float t0 = (B + discr) / A; + float t1 = (B - discr) / A; + if (V_START_RADIUS + rd * t0 >= 0.0) { + offset = t0; + } else if (V_START_RADIUS + rd * t1 >= 0.0) { + offset = t1; + } else { + discard; + } + } + + vec4 color = sample_gradient(V_GRADIENT_ADDRESS, + offset, + V_GRADIENT_REPEAT); + +#ifdef WR_FEATURE_ALPHA_PASS + color *= init_transform_fs(V_LOCAL_POS); +#endif + + return Fragment(color); +} +#endif + +// Undef macro names that could be re-defined by other shaders. +#undef V_GRADIENT_ADDRESS +#undef V_CENTER +#undef V_START_RADIUS +#undef V_END_RADIUS +#undef V_REPEATED_SIZE +#undef V_GRADIENT_REPEAT +#undef V_POS +#undef V_LOCAL_POS +#undef V_TILE_REPEAT diff --git a/third_party/webrender/webrender/res/brush_solid.frag.h b/third_party/webrender/webrender/res/brush_solid.frag.h new file mode 100644 index 00000000000..9f650638166 --- /dev/null +++ b/third_party/webrender/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; +} diff --git a/third_party/webrender/webrender/res/brush_solid.glsl b/third_party/webrender/webrender/res/brush_solid.glsl index 91704ca1073..86713b04d12 100644 --- a/third_party/webrender/webrender/res/brush_solid.glsl +++ b/third_party/webrender/webrender/res/brush_solid.glsl @@ -2,11 +2,19 @@ * 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 VECS_PER_SPECIFIC_BRUSH 1 +#define VECS_PER_SOLID_BRUSH 1 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_SOLID_BRUSH + +#define WR_BRUSH_VS_FUNCTION solid_brush_vs +#define WR_BRUSH_FS_FUNCTION solid_brush_fs #include shared,prim_shared,brush -flat varying vec4 v_color; +#define V_COLOR flat_varying_vec4_0 + +#ifdef WR_FEATURE_ALPHA_PASS +#define V_LOCAL_POS varying_vec4_0.xy +#endif #ifdef WR_VERTEX_SHADER @@ -19,7 +27,7 @@ SolidBrush fetch_solid_primitive(int address) { return SolidBrush(data); } -void brush_vs( +void solid_brush_vs( VertexInfo vi, int prim_address, RectWithSize local_rect, @@ -34,27 +42,24 @@ void brush_vs( SolidBrush prim = fetch_solid_primitive(prim_address); float opacity = float(prim_user_data.x) / 65535.0; - v_color = prim.color * opacity; + V_COLOR = prim.color * opacity; + +#ifdef WR_FEATURE_ALPHA_PASS + V_LOCAL_POS = vi.local_pos; +#endif } #endif #ifdef WR_FRAGMENT_SHADER -Fragment brush_fs() { - vec4 color = v_color; +Fragment solid_brush_fs() { + vec4 color = V_COLOR; #ifdef WR_FEATURE_ALPHA_PASS - color *= antialias_brush(); + color *= init_transform_fs(V_LOCAL_POS); #endif return Fragment(color); } - -#if defined(SWGL_DRAW_SPAN) && (!defined(WR_FEATURE_ALPHA_PASS) || !defined(WR_FEATURE_DUAL_SOURCE_BLENDING)) -void swgl_drawSpanRGBA8() { - swgl_commitSolidRGBA8(v_color); -} - -void swgl_drawSpanR8() { - swgl_commitSolidR8(v_color.x); -} #endif -#endif +// Undef macro names that could be re-defined by other shaders. +#undef V_COLOR +#undef V_LOCAL_POS diff --git a/third_party/webrender/webrender/res/brush_yuv_image.glsl b/third_party/webrender/webrender/res/brush_yuv_image.glsl index e9f8b8c0714..a8ba62731a7 100644 --- a/third_party/webrender/webrender/res/brush_yuv_image.glsl +++ b/third_party/webrender/webrender/res/brush_yuv_image.glsl @@ -2,10 +2,20 @@ * 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 VECS_PER_SPECIFIC_BRUSH 1 +#define VECS_PER_YUV_BRUSH 1 +#define VECS_PER_SPECIFIC_BRUSH VECS_PER_YUV_BRUSH + +#define WR_BRUSH_VS_FUNCTION yuv_brush_vs +#define WR_BRUSH_FS_FUNCTION yuv_brush_fs #include shared,prim_shared,brush,yuv +#ifdef WR_FEATURE_ALPHA_PASS +varying vec2 vLocalPos; +#endif + +flat varying vec3 vYuvLayers; + varying vec2 vUv_Y; flat varying vec4 vUvBounds_Y; @@ -15,15 +25,10 @@ flat varying vec4 vUvBounds_U; varying vec2 vUv_V; flat varying vec4 vUvBounds_V; +flat varying float vCoefficient; flat varying mat3 vYuvColorMatrix; -flat varying vec4 vYuvOffsetVector_Coefficient; flat varying int vFormat; -#ifdef SWGL_DRAW_SPAN -flat varying int vYuvColorSpace; -flat varying int vRescaleFactor; -#endif - #ifdef WR_VERTEX_SHADER struct YuvPrimitive { @@ -37,7 +42,7 @@ YuvPrimitive fetch_yuv_primitive(int address) { return YuvPrimitive(data.x, int(data.y), int(data.z)); } -void brush_vs( +void yuv_brush_vs( VertexInfo vi, int prim_address, RectWithSize local_rect, @@ -52,47 +57,45 @@ void brush_vs( vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size; YuvPrimitive prim = fetch_yuv_primitive(prim_address); - vYuvOffsetVector_Coefficient.w = prim.coefficient; + vCoefficient = prim.coefficient; vYuvColorMatrix = get_yuv_color_matrix(prim.color_space); - vYuvOffsetVector_Coefficient.xyz = get_yuv_offset_vector(prim.color_space); vFormat = prim.yuv_format; -#ifdef SWGL_DRAW_SPAN - // swgl_commitTextureLinearYUV needs to know the color space specifier and - // also needs to know how many bits of scaling are required to normalize - // HDR textures. - vYuvColorSpace = prim.color_space; - vRescaleFactor = int(log2(prim.coefficient)); +#ifdef WR_FEATURE_ALPHA_PASS + vLocalPos = vi.local_pos; #endif - // The additional test for 99 works around a gen6 shader compiler bug: 1708937 - if (vFormat == YUV_FORMAT_PLANAR || vFormat == 99) { - ImageSource res_y = fetch_image_source(prim_user_data.x); - ImageSource res_u = fetch_image_source(prim_user_data.y); - ImageSource res_v = fetch_image_source(prim_user_data.z); - write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE_YUV(sColor0), vUv_Y, vUvBounds_Y); - write_uv_rect(res_u.uv_rect.p0, res_u.uv_rect.p1, f, TEX_SIZE_YUV(sColor1), vUv_U, vUvBounds_U); - write_uv_rect(res_v.uv_rect.p0, res_v.uv_rect.p1, f, TEX_SIZE_YUV(sColor2), vUv_V, vUvBounds_V); + + if (vFormat == YUV_FORMAT_PLANAR) { + ImageResource res_y = fetch_image_resource(prim_user_data.x); + ImageResource res_u = fetch_image_resource(prim_user_data.y); + ImageResource res_v = fetch_image_resource(prim_user_data.z); + write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE(sColor0), vUv_Y, vUvBounds_Y); + write_uv_rect(res_u.uv_rect.p0, res_u.uv_rect.p1, f, TEX_SIZE(sColor1), vUv_U, vUvBounds_U); + write_uv_rect(res_v.uv_rect.p0, res_v.uv_rect.p1, f, TEX_SIZE(sColor2), vUv_V, vUvBounds_V); + vYuvLayers = vec3(res_y.layer, res_u.layer, res_v.layer); } else if (vFormat == YUV_FORMAT_NV12) { - ImageSource res_y = fetch_image_source(prim_user_data.x); - ImageSource res_u = fetch_image_source(prim_user_data.y); - write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE_YUV(sColor0), vUv_Y, vUvBounds_Y); - write_uv_rect(res_u.uv_rect.p0, res_u.uv_rect.p1, f, TEX_SIZE_YUV(sColor1), vUv_U, vUvBounds_U); + ImageResource res_y = fetch_image_resource(prim_user_data.x); + ImageResource res_u = fetch_image_resource(prim_user_data.y); + write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE(sColor0), vUv_Y, vUvBounds_Y); + write_uv_rect(res_u.uv_rect.p0, res_u.uv_rect.p1, f, TEX_SIZE(sColor1), vUv_U, vUvBounds_U); + vYuvLayers = vec3(res_y.layer, res_u.layer, 0.0); } else if (vFormat == YUV_FORMAT_INTERLEAVED) { - ImageSource res_y = fetch_image_source(prim_user_data.x); - write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE_YUV(sColor0), vUv_Y, vUvBounds_Y); + ImageResource res_y = fetch_image_resource(prim_user_data.x); + write_uv_rect(res_y.uv_rect.p0, res_y.uv_rect.p1, f, TEX_SIZE(sColor0), vUv_Y, vUvBounds_Y); + vYuvLayers = vec3(res_y.layer, 0.0, 0.0); } } #endif #ifdef WR_FRAGMENT_SHADER -Fragment brush_fs() { +Fragment yuv_brush_fs() { vec4 color = sample_yuv( vFormat, vYuvColorMatrix, - vYuvOffsetVector_Coefficient.xyz, - vYuvOffsetVector_Coefficient.w, + vCoefficient, + vYuvLayers, vUv_Y, vUv_U, vUv_V, @@ -102,28 +105,9 @@ Fragment brush_fs() { ); #ifdef WR_FEATURE_ALPHA_PASS - color *= antialias_brush(); + color *= init_transform_fs(vLocalPos); #endif return Fragment(color); } - -#ifdef SWGL_DRAW_SPAN -void swgl_drawSpanRGBA8() { - if (vFormat == YUV_FORMAT_PLANAR) { - swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, - sColor1, vUv_U, vUvBounds_U, - sColor2, vUv_V, vUvBounds_V, - vYuvColorSpace, vRescaleFactor); - } else if (vFormat == YUV_FORMAT_NV12) { - swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, - sColor1, vUv_U, vUvBounds_U, - vYuvColorSpace, vRescaleFactor); - } else if (vFormat == YUV_FORMAT_INTERLEAVED) { - swgl_commitTextureLinearYUV(sColor0, vUv_Y, vUvBounds_Y, - vYuvColorSpace, vRescaleFactor); - } -} -#endif - #endif diff --git a/third_party/webrender/webrender/res/clip_shared.glsl b/third_party/webrender/webrender/res/clip_shared.glsl index 924d6fdd1da..e5b80389687 100644 --- a/third_party/webrender/webrender/res/clip_shared.glsl +++ b/third_party/webrender/webrender/res/clip_shared.glsl @@ -6,29 +6,40 @@ #ifdef WR_VERTEX_SHADER +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; -PER_INSTANCE in ivec2 aTransformIds; -struct ClipMaskInstanceCommon { +struct ClipMaskInstance { + int clip_transform_id; + int prim_transform_id; + ivec2 clip_data_address; + ivec2 resource_address; + vec2 local_pos; + RectWithSize tile_rect; RectWithSize sub_rect; vec2 task_origin; vec2 screen_origin; float device_pixel_scale; - int clip_transform_id; - int prim_transform_id; }; -ClipMaskInstanceCommon fetch_clip_item_common() { - ClipMaskInstanceCommon cmi; +ClipMaskInstance fetch_clip_item() { + ClipMaskInstance cmi; + cmi.clip_transform_id = aTransformIds.x; + cmi.prim_transform_id = aTransformIds.y; + cmi.clip_data_address = aClipDataResourceAddress.xy; + cmi.resource_address = aClipDataResourceAddress.zw; + cmi.local_pos = aClipLocalPos; + cmi.tile_rect = RectWithSize(aClipTileRect.xy, aClipTileRect.zw); cmi.sub_rect = RectWithSize(aClipDeviceArea.xy, aClipDeviceArea.zw); cmi.task_origin = aClipOrigins.xy; cmi.screen_origin = aClipOrigins.zw; cmi.device_pixel_scale = aDevicePixelScale; - cmi.clip_transform_id = aTransformIds.x; - cmi.prim_transform_id = aTransformIds.y; return cmi; } diff --git a/third_party/webrender/webrender/res/composite.glsl b/third_party/webrender/webrender/res/composite.glsl index bfce0ef0262..2a2f36969c7 100644 --- a/third_party/webrender/webrender/res/composite.glsl +++ b/third_party/webrender/webrender/res/composite.glsl @@ -4,26 +4,13 @@ // Composite a picture cache tile into the framebuffer. -// This shader must remain compatible with ESSL 1, at least for the -// WR_FEATURE_TEXTURE_EXTERNAL_ESSL1 feature, so that it can be used to render -// video on GLES devices without GL_OES_EGL_image_external_essl3 support. -// This means we cannot use textureSize(), int inputs/outputs, etc. - -#include shared - -#ifdef WR_FEATURE_YUV -#include yuv -#endif +#include shared,yuv #ifdef WR_FEATURE_YUV flat varying mat3 vYuvColorMatrix; -flat varying vec3 vYuvOffsetVector; flat varying float vYuvCoefficient; flat varying int vYuvFormat; -#ifdef SWGL_DRAW_SPAN -flat varying int vYuvColorSpace; -flat varying int vRescaleFactor; -#endif +flat varying vec3 vYuvLayers; varying vec2 vUV_y; varying vec2 vUV_u; varying vec2 vUV_v; @@ -31,32 +18,29 @@ flat varying vec4 vUVBounds_y; flat varying vec4 vUVBounds_u; flat varying vec4 vUVBounds_v; #else -varying vec2 vUv; -#ifndef WR_FEATURE_FAST_PATH flat varying vec4 vColor; +flat varying float vLayer; +varying vec2 vUv; flat varying vec4 vUVBounds; #endif -#ifdef WR_FEATURE_TEXTURE_EXTERNAL_ESSL1 -uniform vec2 uTextureSize; -#endif -#endif #ifdef WR_VERTEX_SHADER // CPU side data is in CompositeInstance (gpu_types.rs) and is // converted to GPU data using desc::COMPOSITE (renderer.rs) by // filling vaos.composite_vao with VertexArrayKind::Composite. -PER_INSTANCE attribute vec4 aDeviceRect; -PER_INSTANCE attribute vec4 aDeviceClipRect; -PER_INSTANCE attribute vec4 aColor; -PER_INSTANCE attribute vec4 aParams; +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 // YUV treats these as a UV clip rect (clamp) -PER_INSTANCE attribute vec4 aUvRect0; -PER_INSTANCE attribute vec4 aUvRect1; -PER_INSTANCE attribute vec4 aUvRect2; +PER_INSTANCE in vec4 aUvRect0; +PER_INSTANCE in vec4 aUvRect1; +PER_INSTANCE in vec4 aUvRect2; #else -PER_INSTANCE attribute vec4 aUvRect0; +PER_INSTANCE in vec4 aUvRect0; #endif void main(void) { @@ -75,23 +59,15 @@ void main(void) { float yuv_coefficient = aParams.w; vYuvColorMatrix = get_yuv_color_matrix(yuv_color_space); - vYuvOffsetVector = get_yuv_offset_vector(yuv_color_space); vYuvCoefficient = yuv_coefficient; vYuvFormat = yuv_format; -#ifdef SWGL_DRAW_SPAN - // swgl_commitTextureLinearYUV needs to know the color space specifier and - // also needs to know how many bits of scaling are required to normalize - // HDR textures. - vYuvColorSpace = yuv_color_space; - vRescaleFactor = int(log2(yuv_coefficient)); -#endif - + vYuvLayers = aTextureLayers.xyz; write_uv_rect( aUvRect0.xy, aUvRect0.zw, uv, - TEX_SIZE_YUV(sColor0), + TEX_SIZE(sColor0), vUV_y, vUVBounds_y ); @@ -99,7 +75,7 @@ void main(void) { aUvRect1.xy, aUvRect1.zw, uv, - TEX_SIZE_YUV(sColor1), + TEX_SIZE(sColor1), vUV_u, vUVBounds_u ); @@ -107,41 +83,31 @@ void main(void) { aUvRect2.xy, aUvRect2.zw, uv, - TEX_SIZE_YUV(sColor2), + TEX_SIZE(sColor2), vUV_v, vUVBounds_v ); #else - uv = mix(aUvRect0.xy, aUvRect0.zw, uv); + vUv = mix(aUvRect0.xy, aUvRect0.zw, uv); // flip_y might have the UV rect "upside down", make sure // clamp works correctly: - vec4 uvBounds = vec4(aUvRect0.x, min(aUvRect0.y, aUvRect0.w), - aUvRect0.z, max(aUvRect0.y, aUvRect0.w)); + vUVBounds = vec4(aUvRect0.x, min(aUvRect0.y, aUvRect0.w), + aUvRect0.z, max(aUvRect0.y, aUvRect0.w)); int rescale_uv = int(aParams.y); if (rescale_uv == 1) { // using an atlas, so UVs are in pixels, and need to be // normalized and clamped. -#if defined(WR_FEATURE_TEXTURE_RECT) - vec2 texture_size = vec2(1.0, 1.0); -#elif defined(WR_FEATURE_TEXTURE_EXTERNAL_ESSL1) - vec2 texture_size = uTextureSize; -#else - vec2 texture_size = vec2(TEX_SIZE(sColor0)); -#endif - uvBounds += vec4(0.5, 0.5, -0.5, -0.5); + vec2 texture_size = TEX_SIZE(sColor0); + vUVBounds += vec4(0.5, 0.5, -0.5, -0.5); #ifndef WR_FEATURE_TEXTURE_RECT - uv /= texture_size; - uvBounds /= texture_size.xyxy; + vUv /= texture_size; + vUVBounds /= texture_size.xyxy; #endif } - - vUv = uv; -#ifndef WR_FEATURE_FAST_PATH - vUVBounds = uvBounds; - // Pass through color + // Pass through color and texture array layer vColor = aColor; -#endif + vLayer = aTextureLayers.x; #endif gl_Position = uTransform * vec4(clipped_world_pos, aParams.x /* z_id */, 1.0); @@ -154,8 +120,8 @@ void main(void) { vec4 color = sample_yuv( vYuvFormat, vYuvColorMatrix, - vYuvOffsetVector, vYuvCoefficient, + vYuvLayers, vUV_y, vUV_u, vUV_v, @@ -164,58 +130,15 @@ void main(void) { vUVBounds_v ); #else - // The color is just the texture sample modulated by a supplied color. - // In the fast path we avoid clamping the UV coordinates and modulating by the color. -#ifdef WR_FEATURE_FAST_PATH - vec2 uv = vUv; -#else - vec2 uv = clamp(vUv, vUVBounds.xy, vUVBounds.zw); -#endif - vec4 texel = TEX_SAMPLE(sColor0, uv); -#ifdef WR_FEATURE_FAST_PATH - vec4 color = texel; -#else + // The color is just the texture sample modulated by a supplied color + vec2 uv = clamp(vUv.xy, vUVBounds.xy, vUVBounds.zw); +# if defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_2D) || defined(WR_FEATURE_TEXTURE_RECT) + vec4 texel = TEX_SAMPLE(sColor0, vec3(uv, vLayer)); +# else + vec4 texel = textureLod(sColor0, vec3(uv, vLayer), 0.0); +# endif vec4 color = vColor * texel; #endif -#endif write_output(color); } - -#ifdef SWGL_DRAW_SPAN -void swgl_drawSpanRGBA8() { -#ifdef WR_FEATURE_YUV - if (vYuvFormat == YUV_FORMAT_PLANAR) { - swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, - sColor1, vUV_u, vUVBounds_u, - sColor2, vUV_v, vUVBounds_v, - vYuvColorSpace, vRescaleFactor); - } else if (vYuvFormat == YUV_FORMAT_NV12) { - swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, - sColor1, vUV_u, vUVBounds_u, - vYuvColorSpace, vRescaleFactor); - } else if (vYuvFormat == YUV_FORMAT_INTERLEAVED) { - swgl_commitTextureLinearYUV(sColor0, vUV_y, vUVBounds_y, - vYuvColorSpace, vRescaleFactor); - } -#else -#ifdef WR_FEATURE_FAST_PATH - vec4 color = vec4(1.0); -#ifdef WR_FEATURE_TEXTURE_RECT - vec4 uvBounds = vec4(vec2(0.0), vec2(textureSize(sColor0))); -#else - vec4 uvBounds = vec4(0.0, 0.0, 1.0, 1.0); -#endif -#else - vec4 color = vColor; - vec4 uvBounds = vUVBounds; -#endif - if (color != vec4(1.0)) { - swgl_commitTextureColorRGBA8(sColor0, vUv, uvBounds, color); - } else { - swgl_commitTextureRGBA8(sColor0, vUv, uvBounds); - } -#endif -} -#endif - #endif diff --git a/third_party/webrender/webrender/res/cs_blur.glsl b/third_party/webrender/webrender/res/cs_blur.glsl index d54d6ddc452..a10fe00d790 100644 --- a/third_party/webrender/webrender/res/cs_blur.glsl +++ b/third_party/webrender/webrender/res/cs_blur.glsl @@ -2,16 +2,14 @@ * 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_FEATURE_TEXTURE_2D - #include shared,prim_shared -varying vec2 vUv; +varying vec3 vUv; flat varying vec4 vUvRect; flat varying vec2 vOffsetScale; +flat varying float vSigma; // The number of pixels on each end that we apply the blur filter over. flat varying int vSupport; -flat varying vec2 vGaussCoefficients; #ifdef WR_VERTEX_SHADER // Applies a separable gaussian blur in one direction, as specified @@ -25,7 +23,7 @@ PER_INSTANCE in int aBlurSourceTaskAddress; PER_INSTANCE in int aBlurDirection; struct BlurTask { - RectWithSize task_rect; + RenderTaskCommonData common_data; float blur_radius; vec2 blur_region; }; @@ -34,7 +32,7 @@ BlurTask fetch_blur_task(int address) { RenderTaskData task_data = fetch_render_task_data(address); BlurTask task = BlurTask( - task_data.task_rect, + task_data.common_data, task_data.user_data.x, task_data.user_data.yz ); @@ -42,37 +40,20 @@ BlurTask fetch_blur_task(int address) { return task; } -void calculate_gauss_coefficients(float sigma) { - // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889) - vGaussCoefficients = vec2(1.0 / (sqrt(2.0 * 3.14159265) * sigma), - exp(-0.5 / (sigma * sigma))); - - // Pre-calculate the coefficient total in the vertex shader so that - // we can avoid having to do it per-fragment and also avoid division - // by zero in the degenerate case. - vec3 gauss_coefficient = vec3(vGaussCoefficients, - vGaussCoefficients.y * vGaussCoefficients.y); - float gauss_coefficient_total = gauss_coefficient.x; - for (int i = 1; i <= vSupport; i += 2) { - gauss_coefficient.xy *= gauss_coefficient.yz; - float gauss_coefficient_subtotal = gauss_coefficient.x; - gauss_coefficient.xy *= gauss_coefficient.yz; - gauss_coefficient_subtotal += gauss_coefficient.x; - gauss_coefficient_total += 2.0 * gauss_coefficient_subtotal; - } - - // Scale initial coefficient by total to avoid passing the total separately - // to the fragment shader. - vGaussCoefficients.x /= gauss_coefficient_total; -} - void main(void) { BlurTask blur_task = fetch_blur_task(aBlurRenderTaskAddress); - RectWithSize src_rect = fetch_render_task_rect(aBlurSourceTaskAddress); + RenderTaskCommonData src_task = fetch_render_task_common_data(aBlurSourceTaskAddress); - RectWithSize target_rect = blur_task.task_rect; + RectWithSize src_rect = src_task.task_rect; + RectWithSize target_rect = blur_task.common_data.task_rect; - vec2 texture_size = vec2(TEX_SIZE(sColor0).xy); +#if defined WR_FEATURE_COLOR_TARGET + vec2 texture_size = vec2(textureSize(sPrevPassColor, 0).xy); +#else + vec2 texture_size = vec2(textureSize(sPrevPassAlpha, 0).xy); +#endif + vUv.z = src_task.texture_layer_index; + vSigma = blur_task.blur_radius; // Ensure that the support is an even number of pixels to simplify the // fragment shader logic. @@ -81,13 +62,6 @@ void main(void) { // hardware for linear filtering. vSupport = int(ceil(1.5 * blur_task.blur_radius)) * 2; - if (vSupport > 0) { - calculate_gauss_coefficients(blur_task.blur_radius); - } else { - // The gauss function gets NaNs when blur radius is zero. - vGaussCoefficients = vec2(1.0, 1.0); - } - switch (aBlurDirection) { case DIR_HORIZONTAL: vOffsetScale = vec2(1.0 / texture_size.x, 0.0); @@ -107,7 +81,7 @@ void main(void) { vec2 uv0 = src_rect.p0 / texture_size; vec2 uv1 = (src_rect.p0 + src_rect.size) / texture_size; - vUv = mix(uv0, uv1, aPosition.xy); + vUv.xy = mix(uv0, uv1, aPosition.xy); gl_Position = uTransform * vec4(pos, 0.0, 1.0); } @@ -117,10 +91,10 @@ void main(void) { #if defined WR_FEATURE_COLOR_TARGET #define SAMPLE_TYPE vec4 -#define SAMPLE_TEXTURE(uv) texture(sColor0, uv) +#define SAMPLE_TEXTURE(uv) texture(sPrevPassColor, uv) #else #define SAMPLE_TYPE float -#define SAMPLE_TEXTURE(uv) texture(sColor0, uv).r +#define SAMPLE_TEXTURE(uv) texture(sPrevPassAlpha, uv).r #endif // TODO(gw): Write a fast path blur that handles smaller blur radii @@ -130,11 +104,23 @@ void main(void) { void main(void) { SAMPLE_TYPE original_color = SAMPLE_TEXTURE(vUv); + // TODO(gw): The gauss function gets NaNs when blur radius + // is zero. In the future, detect this earlier + // and skip the blur passes completely. + if (vSupport == 0) { + oFragColor = vec4(original_color); + return; + } + // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889) - vec3 gauss_coefficient = vec3(vGaussCoefficients, - vGaussCoefficients.y * vGaussCoefficients.y); + vec3 gauss_coefficient; + gauss_coefficient.x = 1.0 / (sqrt(2.0 * 3.14159265) * vSigma); + gauss_coefficient.y = exp(-0.5 / (vSigma * vSigma)); + gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y; + float gauss_coefficient_total = gauss_coefficient.x; SAMPLE_TYPE avg_color = original_color * gauss_coefficient.x; + gauss_coefficient.xy *= gauss_coefficient.yz; // Evaluate two adjacent texels at a time. We can do this because, if c0 // and c1 are colors of adjacent texels and k0 and k1 are arbitrary @@ -156,8 +142,6 @@ void main(void) { // Equation 1 with a single texture lookup. for (int i = 1; i <= vSupport; i += 2) { - gauss_coefficient.xy *= gauss_coefficient.yz; - float gauss_coefficient_subtotal = gauss_coefficient.x; gauss_coefficient.xy *= gauss_coefficient.yz; gauss_coefficient_subtotal += gauss_coefficient.x; @@ -165,27 +149,16 @@ void main(void) { vec2 offset = vOffsetScale * (float(i) + gauss_ratio); - vec2 st0 = max(vUv - offset, vUvRect.xy); - vec2 st1 = min(vUv + offset, vUvRect.zw); - avg_color += (SAMPLE_TEXTURE(st0) + SAMPLE_TEXTURE(st1)) * - gauss_coefficient_subtotal; - } + vec2 st0 = clamp(vUv.xy - offset, vUvRect.xy, vUvRect.zw); + avg_color += SAMPLE_TEXTURE(vec3(st0, vUv.z)) * gauss_coefficient_subtotal; - oFragColor = vec4(avg_color); -} + vec2 st1 = clamp(vUv.xy + offset, vUvRect.xy, vUvRect.zw); + avg_color += SAMPLE_TEXTURE(vec3(st1, vUv.z)) * gauss_coefficient_subtotal; -#ifdef SWGL_DRAW_SPAN - #ifdef WR_FEATURE_COLOR_TARGET -void swgl_drawSpanRGBA8() { - swgl_commitGaussianBlurRGBA8(sColor0, vUv, vUvRect, vOffsetScale.x != 0.0, - vSupport, vGaussCoefficients); -} - #else -void swgl_drawSpanR8() { - swgl_commitGaussianBlurR8(sColor0, vUv, vUvRect, vOffsetScale.x != 0.0, - vSupport, vGaussCoefficients); -} - #endif -#endif + gauss_coefficient_total += 2.0 * gauss_coefficient_subtotal; + gauss_coefficient.xy *= gauss_coefficient.yz; + } + oFragColor = vec4(avg_color) / gauss_coefficient_total; +} #endif diff --git a/third_party/webrender/webrender/res/cs_border_segment.glsl b/third_party/webrender/webrender/res/cs_border_segment.glsl index 83074f9d655..9eb5155f1f6 100644 --- a/third_party/webrender/webrender/res/cs_border_segment.glsl +++ b/third_party/webrender/webrender/res/cs_border_segment.glsl @@ -2,7 +2,7 @@ * 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 shared,rect,ellipse +#include shared,ellipse // For edges, the colors are the same. For corners, these // are the colors of each edge making up the corner. @@ -265,11 +265,13 @@ vec4 evaluate_color_for_style_in_corner( // third of the rounded edge. float d_radii_a = distance_to_ellipse( clip_relative_pos, - clip_radii.xy - vPartialWidths.xy + clip_radii.xy - vPartialWidths.xy, + aa_range ); float d_radii_b = distance_to_ellipse( clip_relative_pos, - clip_radii.xy - 2.0 * vPartialWidths.xy + clip_radii.xy - 2.0 * vPartialWidths.xy, + aa_range ); float d = min(-d_radii_a, d_radii_b); color0 *= distance_aa(aa_range, d); @@ -279,7 +281,8 @@ vec4 evaluate_color_for_style_in_corner( case BORDER_STYLE_RIDGE: { float d = distance_to_ellipse( clip_relative_pos, - clip_radii.xy - vPartialWidths.zw + clip_radii.xy - vPartialWidths.zw, + aa_range ); float alpha = distance_aa(aa_range, d); float swizzled_factor; @@ -399,8 +402,8 @@ void main(void) { } if (in_clip_region) { - float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy); - float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw); + float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy, aa_range); + float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw, aa_range); float d_radii = max(d_radii_a, -d_radii_b); d = max(d, d_radii); diff --git a/third_party/webrender/webrender/res/cs_border_solid.glsl b/third_party/webrender/webrender/res/cs_border_solid.glsl index 4236405b2ab..d5748143533 100644 --- a/third_party/webrender/webrender/res/cs_border_solid.glsl +++ b/third_party/webrender/webrender/res/cs_border_solid.glsl @@ -2,7 +2,7 @@ * 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 shared,rect,ellipse +#include shared,ellipse #define DONT_MIX 0 #define MIX_AA 1 @@ -148,8 +148,8 @@ void main(void) { float d = -1.0; if (in_clip_region) { - float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy); - float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw); + float d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy, aa_range); + float d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw, aa_range); d = max(d_radii_a, -d_radii_b); } @@ -157,7 +157,7 @@ void main(void) { clip_relative_pos = vPos - vHorizontalClipCenter_Sign.xy; in_clip_region = all(lessThan(vHorizontalClipCenter_Sign.zw * clip_relative_pos, vec2(0.0))); if (in_clip_region) { - float d_radii = distance_to_ellipse(clip_relative_pos, vHorizontalClipRadii.xy); + float d_radii = distance_to_ellipse(clip_relative_pos, vHorizontalClipRadii.xy, aa_range); d = max(d_radii, d); } @@ -165,7 +165,7 @@ void main(void) { clip_relative_pos = vPos - vVerticalClipCenter_Sign.xy; in_clip_region = all(lessThan(vVerticalClipCenter_Sign.zw * clip_relative_pos, vec2(0.0))); if (in_clip_region) { - float d_radii = distance_to_ellipse(clip_relative_pos, vVerticalClipRadii.xy); + float d_radii = distance_to_ellipse(clip_relative_pos, vVerticalClipRadii.xy, aa_range); d = max(d_radii, d); } diff --git a/third_party/webrender/webrender/res/cs_clip_box_shadow.glsl b/third_party/webrender/webrender/res/cs_clip_box_shadow.glsl index d152b039c21..5b4937139ef 100644 --- a/third_party/webrender/webrender/res/cs_clip_box_shadow.glsl +++ b/third_party/webrender/webrender/res/cs_clip_box_shadow.glsl @@ -7,67 +7,43 @@ varying vec4 vLocalPos; varying vec2 vUv; flat varying vec4 vUvBounds; +flat varying float vLayer; flat varying vec4 vEdge; flat varying vec4 vUvBounds_NoClamp; -#if defined(PLATFORM_ANDROID) && !defined(SWGL) -// Work around Adreno 3xx driver bug. See the v_perspective comment in -// brush_image or bug 1630356 for details. -flat varying vec2 vClipModeVec; -#define vClipMode vClipModeVec.x -#else flat varying float vClipMode; -#endif #define MODE_STRETCH 0 #define MODE_SIMPLE 1 #ifdef WR_VERTEX_SHADER -PER_INSTANCE in ivec2 aClipDataResourceAddress; -PER_INSTANCE in vec2 aClipSrcRectSize; -PER_INSTANCE in int aClipMode; -PER_INSTANCE in ivec2 aStretchMode; -PER_INSTANCE in vec4 aClipDestRect; - -struct ClipMaskInstanceBoxShadow { - ClipMaskInstanceCommon base; - ivec2 resource_address; -}; - -ClipMaskInstanceBoxShadow fetch_clip_item() { - ClipMaskInstanceBoxShadow cmi; - - cmi.base = fetch_clip_item_common(); - cmi.resource_address = aClipDataResourceAddress; - - return cmi; -} - struct BoxShadowData { vec2 src_rect_size; - int clip_mode; + float clip_mode; int stretch_mode_x; int stretch_mode_y; RectWithSize dest_rect; }; -BoxShadowData fetch_data() { +BoxShadowData fetch_data(ivec2 address) { + vec4 data[3] = fetch_from_gpu_cache_3_direct(address); + RectWithSize dest_rect = RectWithSize(data[2].xy, data[2].zw); BoxShadowData bs_data = BoxShadowData( - aClipSrcRectSize, - aClipMode, - aStretchMode.x, - aStretchMode.y, - RectWithSize(aClipDestRect.xy, aClipDestRect.zw) + data[0].xy, + data[0].z, + int(data[1].x), + int(data[1].y), + dest_rect ); return bs_data; } void main(void) { - ClipMaskInstanceBoxShadow cmi = fetch_clip_item(); - Transform clip_transform = fetch_transform(cmi.base.clip_transform_id); - Transform prim_transform = fetch_transform(cmi.base.prim_transform_id); - BoxShadowData bs_data = fetch_data(); - ImageSource res = fetch_image_source_direct(cmi.resource_address); + ClipMaskInstance cmi = fetch_clip_item(); + Transform clip_transform = fetch_transform(cmi.clip_transform_id); + Transform prim_transform = fetch_transform(cmi.prim_transform_id); + BoxShadowData bs_data = fetch_data(cmi.clip_data_address); + ImageResource res = fetch_image_resource_direct(cmi.resource_address); RectWithSize dest_rect = bs_data.dest_rect; @@ -75,14 +51,15 @@ void main(void) { dest_rect, prim_transform, clip_transform, - cmi.base.sub_rect, - cmi.base.task_origin, - cmi.base.screen_origin, - cmi.base.device_pixel_scale + cmi.sub_rect, + cmi.task_origin, + cmi.screen_origin, + cmi.device_pixel_scale ); - vClipMode = float(bs_data.clip_mode); + vLayer = res.layer; + vClipMode = bs_data.clip_mode; - vec2 texture_size = vec2(TEX_SIZE(sColor0)); + vec2 texture_size = vec2(textureSize(sColor0, 0)); vec2 local_pos = vi.local_pos.xy / vi.local_pos.w; vLocalPos = vi.local_pos; @@ -134,194 +111,11 @@ void main(void) { float in_shadow_rect = init_transform_rough_fs(vLocalPos.xy / vLocalPos.w); - float texel = TEX_SAMPLE(sColor0, uv).r; + float texel = TEX_SAMPLE(sColor0, vec3(uv, vLayer)).r; float alpha = mix(texel, 1.0 - texel, vClipMode); float result = vLocalPos.w > 0.0 ? mix(vClipMode, alpha, in_shadow_rect) : 0.0; oFragColor = vec4(result); } - -#ifdef SWGL_DRAW_SPAN -// As with cs_clip_rectangle, this shader spends a lot of time doing clipping and -// combining for every fragment, even if outside of the primitive to initialize -// the clip tile, or inside the inner bounds of the primitive, where the shadow -// is unnecessary. To alleviate this, the span shader attempts to first intersect -// the the local clip bounds, outside of which we can just use a solid fill -// to initialize those clip tile fragments. Once inside the primitive bounds, -// we further intersect with the inner region where no shadow is necessary either -// so that we can commit entire spans of texture within this nine-patch region -// instead of having to do the work of mapping per fragment. -void swgl_drawSpanR8() { - // If the span is completely outside the Z-range and clipped out, just - // output clear so we don't need to consider invalid W in the rest of the - // shader. - float w = swgl_forceScalar(vLocalPos.w); - if (w <= 0.0) { - swgl_commitSolidR8(0.0); - return; - } - - // To start, we evaluate the box shadow in both UV and local space relative - // to the local-space position. This will be interpolated across the span to - // track whether we intersect the nine-patch. - w = 1.0 / w; - vec2 uv_linear = vUv * w; - vec2 uv_linear0 = swgl_forceScalar(uv_linear); - vec2 uv_linear_step = swgl_interpStep(vUv).xy * w; - vec2 local_pos = vLocalPos.xy * w; - vec2 local_pos0 = swgl_forceScalar(local_pos); - vec2 local_step = swgl_interpStep(vLocalPos).xy * w; - - // We need to compute the local-space distance to the bounding box and then - // figure out how many processing steps that maps to. If we are stepping in - // a negative direction on an axis, we need to swap the sides of the box - // which we consider as the start or end. If there is no local-space step - // on an axis (i.e. constant Y), we need to take care to force the steps to - // either the start or end of the span depending on if we are inside or - // outside of the bounding box. - vec4 clip_dist = - mix(vTransformBounds, vTransformBounds.zwxy, lessThan(local_step, vec2(0.0)).xyxy) - - local_pos0.xyxy; - clip_dist = - mix(1.0e6 * step(0.0, clip_dist), - clip_dist * recip(local_step).xyxy, - notEqual(local_step, vec2(0.0)).xyxy); - - // Find the start and end of the shadowed region on this span. - float shadow_start = max(clip_dist.x, clip_dist.y); - float shadow_end = min(clip_dist.z, clip_dist.w); - - // Flip the offsets from the start of the span so we can compare against the - // remaining span length which automatically deducts as we commit fragments. - ivec2 shadow_steps = ivec2(clamp( - swgl_SpanLength - swgl_StepSize * vec2(floor(shadow_start), ceil(shadow_end)), - 0.0, swgl_SpanLength)); - int shadow_start_len = shadow_steps.x; - int shadow_end_len = shadow_steps.y; - - // Likewise, once inside the primitive bounds, we also need to track which - // sector of the nine-patch we are in which requires intersecting against - // the inner box instead of the outer box. - vec4 opaque_dist = - mix(vEdge, vEdge.zwxy, lessThan(uv_linear_step, vec2(0.0)).xyxy) - - uv_linear0.xyxy; - opaque_dist = - mix(1.0e6 * step(0.0, opaque_dist), - opaque_dist * recip(uv_linear_step).xyxy, - notEqual(uv_linear_step, vec2(0.0)).xyxy); - - // Unlike for the shadow clipping bounds, here we need to rather find the floor of all - // the offsets so that we don't accidentally process any chunks in the transitional areas - // between sectors of the nine-patch. - ivec4 opaque_steps = ivec4(clamp( - swgl_SpanLength - - swgl_StepSize * - vec4(floor(opaque_dist.x), floor(opaque_dist.y), floor(opaque_dist.z), floor(opaque_dist.w)), - shadow_end_len, swgl_SpanLength)); - - // Fill any initial sections of the span that are clipped out based on clip mode. - if (swgl_SpanLength > shadow_start_len) { - int num_before = swgl_SpanLength - shadow_start_len; - swgl_commitPartialSolidR8(num_before, vClipMode); - float steps_before = float(num_before / swgl_StepSize); - uv_linear += steps_before * uv_linear_step; - local_pos += steps_before * local_step; - } - - // This loop tries to repeatedly process entire spans of the nine-patch that map - // to a contiguous spans of texture in the source box shadow. First, we process - // a chunk with per-fragment clipping and mapping in case we're starting on a - // transitional region between sectors of the nine-patch which may need to map - // to different spans of texture per-fragment. After, we find the largest span - // within the current sector before we hit the next transitional region, and - // attempt to commit an entire span of texture therein. - while (swgl_SpanLength > 0) { - // Here we might be in a transitional chunk, so do everything per-fragment. - { - vec2 uv = clamp(uv_linear, vec2(0.0), vEdge.xy); - uv += max(vec2(0.0), uv_linear - vEdge.zw); - uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv); - uv = clamp(uv, vUvBounds.xy, vUvBounds.zw); - - float in_shadow_rect = init_transform_rough_fs(local_pos); - - float texel = TEX_SAMPLE(sColor0, uv).r; - - float alpha = mix(texel, 1.0 - texel, vClipMode); - float result = mix(vClipMode, alpha, in_shadow_rect); - swgl_commitColorR8(result); - - uv_linear += uv_linear_step; - local_pos += local_step; - } - // If we now hit the end of the clip bounds, just bail out since there is - // no more shadow to map. - if (swgl_SpanLength <= shadow_end_len) { - break; - } - // By here we've determined to be still inside the nine-patch. We need to - // compare against the inner rectangle thresholds to see which sector of - // the nine-patch to use and thus how to map the box shadow texture. Stop - // at least one step before the end of the shadow region to properly clip - // on the boundary. - int num_inside = swgl_SpanLength - swgl_StepSize - shadow_end_len; - vec4 uv_bounds = vUvBounds; - if (swgl_SpanLength >= opaque_steps.y) { - // We're in the top Y band of the nine-patch. - num_inside = min(num_inside, swgl_SpanLength - opaque_steps.y); - } else if (swgl_SpanLength >= opaque_steps.w) { - // We're in the middle Y band of the nine-patch. Set the UV clamp bounds - // to the vertical center texel of the box shadow. - num_inside = min(num_inside, swgl_SpanLength - opaque_steps.w); - uv_bounds.yw = vec2(clamp(mix(vUvBounds_NoClamp.y, vUvBounds_NoClamp.w, vEdge.y), - vUvBounds.y, vUvBounds.w)); - } - if (swgl_SpanLength >= opaque_steps.x) { - // We're in the left X column of the nine-patch. - num_inside = min(num_inside, swgl_SpanLength - opaque_steps.x); - } else if (swgl_SpanLength >= opaque_steps.z) { - // We're in the middle X band of the nine-patch. Set the UV clamp bounds - // to the horizontal center texel of the box shadow. - num_inside = min(num_inside, swgl_SpanLength - opaque_steps.z); - uv_bounds.xz = vec2(clamp(mix(vUvBounds_NoClamp.x, vUvBounds_NoClamp.z, vEdge.x), - vUvBounds.x, vUvBounds.z)); - } - if (num_inside > 0) { - // We have a non-zero span of fragments within the sector. Map to the UV - // start offset of the sector and the UV offset within the sector. - vec2 uv = clamp(uv_linear, vec2(0.0), vEdge.xy); - uv += max(vec2(0.0), uv_linear - vEdge.zw); - uv = mix(vUvBounds_NoClamp.xy, vUvBounds_NoClamp.zw, uv); - // If we're in the center sector of the nine-patch, then we only need to - // sample from a single texel of the box shadow. Just sample that single - // texel once and output it for the entire span. Otherwise, we just need - // to commit an actual span of texture from the box shadow. Depending on - // if we are in clip-out mode, we may need to invert the source texture. - if (uv_bounds.xy == uv_bounds.zw) { - uv = clamp(uv, uv_bounds.xy, uv_bounds.zw); - float texel = TEX_SAMPLE(sColor0, uv).r; - float alpha = mix(texel, 1.0 - texel, vClipMode); - swgl_commitPartialSolidR8(num_inside, alpha); - } else if (vClipMode != 0.0) { - swgl_commitPartialTextureLinearInvertR8(num_inside, sColor0, uv, uv_bounds); - } else { - swgl_commitPartialTextureLinearR8(num_inside, sColor0, uv, uv_bounds); - } - float steps_inside = float(num_inside / swgl_StepSize); - uv_linear += steps_inside * uv_linear_step; - local_pos += steps_inside * local_step; - } - // By here we're probably in a transitional chunk of the nine-patch that - // requires per-fragment processing, so loop around again to the handler - // for that case. - } - - // Fill any remaining sections of the span that are clipped out. - if (swgl_SpanLength > 0) { - swgl_commitPartialSolidR8(swgl_SpanLength, vClipMode); - } -} -#endif - #endif diff --git a/third_party/webrender/webrender/res/cs_clip_image.glsl b/third_party/webrender/webrender/res/cs_clip_image.glsl index d2042e71401..918223343d2 100644 --- a/third_party/webrender/webrender/res/cs_clip_image.glsl +++ b/third_party/webrender/webrender/res/cs_clip_image.glsl @@ -4,114 +4,70 @@ #include shared,clip_shared -varying vec2 vLocalPos; +varying vec4 vLocalPos; varying vec2 vClipMaskImageUv; +flat varying vec4 vClipMaskUvRect; flat varying vec4 vClipMaskUvInnerRect; +flat varying float vLayer; #ifdef WR_VERTEX_SHADER - -PER_INSTANCE in vec4 aClipTileRect; -PER_INSTANCE in ivec2 aClipDataResourceAddress; -PER_INSTANCE in vec4 aClipLocalRect; - -struct ClipMaskInstanceImage { - ClipMaskInstanceCommon base; - RectWithSize tile_rect; - ivec2 resource_address; - RectWithSize local_rect; +struct ImageMaskData { + vec2 local_mask_size; }; -ClipMaskInstanceImage fetch_clip_item() { - ClipMaskInstanceImage cmi; - - cmi.base = fetch_clip_item_common(); - - cmi.tile_rect = RectWithSize(aClipTileRect.xy, aClipTileRect.zw); - cmi.resource_address = aClipDataResourceAddress; - cmi.local_rect = RectWithSize(aClipLocalRect.xy, aClipLocalRect.zw); - - return cmi; -} - -struct ClipImageVertexInfo { - vec2 local_pos; - vec4 world_pos; -}; - -// This differs from write_clip_tile_vertex in that we forward transform the -// primitive's local-space tile rect into the target space. We use scissoring -// to ensure that the primitive does not draw outside the target bounds. -ClipImageVertexInfo write_clip_image_vertex(RectWithSize tile_rect, - RectWithSize local_clip_rect, - Transform prim_transform, - Transform clip_transform, - RectWithSize sub_rect, - vec2 task_origin, - vec2 screen_origin, - float device_pixel_scale) { - vec2 local_pos = clamp_rect(tile_rect.p0 + aPosition.xy * tile_rect.size, local_clip_rect); - vec4 world_pos = prim_transform.m * vec4(local_pos, 0.0, 1.0); - vec4 final_pos = vec4( - world_pos.xy * device_pixel_scale + (task_origin - screen_origin) * world_pos.w, - 0.0, - world_pos.w - ); - gl_Position = uTransform * final_pos; - - init_transform_vs( - clip_transform.is_axis_aligned - ? vec4(vec2(-1.0e16), vec2(1.0e16)) - : vec4(local_clip_rect.p0, local_clip_rect.p0 + local_clip_rect.size)); - - ClipImageVertexInfo vi = ClipImageVertexInfo(local_pos, world_pos); - return vi; +ImageMaskData fetch_mask_data(ivec2 address) { + vec4 data = fetch_from_gpu_cache_1_direct(address); + ImageMaskData mask_data = ImageMaskData(data.xy); + return mask_data; } void main(void) { - ClipMaskInstanceImage cmi = fetch_clip_item(); - Transform clip_transform = fetch_transform(cmi.base.clip_transform_id); - Transform prim_transform = fetch_transform(cmi.base.prim_transform_id); - ImageSource res = fetch_image_source_direct(cmi.resource_address); - - ClipImageVertexInfo vi = write_clip_image_vertex( - cmi.tile_rect, - cmi.local_rect, + ClipMaskInstance cmi = fetch_clip_item(); + Transform clip_transform = fetch_transform(cmi.clip_transform_id); + Transform prim_transform = fetch_transform(cmi.prim_transform_id); + ImageMaskData mask = fetch_mask_data(cmi.clip_data_address); + RectWithSize local_rect = RectWithSize(cmi.local_pos, mask.local_mask_size); + ImageResource res = fetch_image_resource_direct(cmi.resource_address); + + ClipVertexInfo vi = write_clip_tile_vertex( + local_rect, prim_transform, clip_transform, - cmi.base.sub_rect, - cmi.base.task_origin, - cmi.base.screen_origin, - cmi.base.device_pixel_scale + cmi.sub_rect, + cmi.task_origin, + cmi.screen_origin, + cmi.device_pixel_scale ); vLocalPos = vi.local_pos; - vec2 uv = (vi.local_pos - cmi.tile_rect.p0) / cmi.tile_rect.size; - - vec2 texture_size = vec2(TEX_SIZE(sColor0)); - vec4 uv_rect = vec4(res.uv_rect.p0, res.uv_rect.p1); - vClipMaskImageUv = mix(uv_rect.xy, uv_rect.zw, uv) / texture_size; + vLayer = res.layer; + vClipMaskImageUv = (vi.local_pos.xy - cmi.tile_rect.p0 * vi.local_pos.w) / cmi.tile_rect.size; + vec2 texture_size = vec2(textureSize(sColor0, 0)); + vClipMaskUvRect = vec4(res.uv_rect.p0, res.uv_rect.p1 - res.uv_rect.p0) / texture_size.xyxy; // applying a half-texel offset to the UV boundaries to prevent linear samples from the outside - vClipMaskUvInnerRect = (uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; + vec4 inner_rect = vec4(res.uv_rect.p0, res.uv_rect.p1); + vClipMaskUvInnerRect = (inner_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; } #endif #ifdef WR_FRAGMENT_SHADER void main(void) { - float alpha = init_transform_rough_fs(vLocalPos); - vec2 source_uv = clamp(vClipMaskImageUv, vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw); - float clip_alpha = texture(sColor0, source_uv).r; //careful: texture has type A8 - oFragColor = vec4(mix(1.0, clip_alpha, alpha), 0.0, 0.0, 1.0); + vec2 local_pos = vLocalPos.xy / vLocalPos.w; + float alpha = vLocalPos.w > 0.0 ? init_transform_fs(local_pos) : 0.0; + + // TODO: Handle repeating masks? + vec2 clamped_mask_uv = clamp(vClipMaskImageUv, vec2(0.0, 0.0), vLocalPos.ww); + + // Ensure we don't draw outside of our tile. + // FIXME(emilio): Can we do this earlier? + if (clamped_mask_uv != vClipMaskImageUv) + discard; + + vec2 source_uv = clamp( + clamped_mask_uv / vLocalPos.w * vClipMaskUvRect.zw + vClipMaskUvRect.xy, + vClipMaskUvInnerRect.xy, vClipMaskUvInnerRect.zw); + float clip_alpha = texture(sColor0, vec3(source_uv, vLayer)).r; //careful: texture has type A8 + oFragColor = vec4(alpha * clip_alpha, 1.0, 1.0, 1.0); } - -#ifdef SWGL_DRAW_SPAN -void swgl_drawSpanR8() { - if (has_valid_transform_bounds()) { - return; - } - - swgl_commitTextureLinearR8(sColor0, vClipMaskImageUv, vClipMaskUvInnerRect); -} -#endif - #endif diff --git a/third_party/webrender/webrender/res/cs_clip_rectangle.glsl b/third_party/webrender/webrender/res/cs_clip_rectangle.glsl index 470b77a5834..dfbf60ed41f 100644 --- a/third_party/webrender/webrender/res/cs_clip_rectangle.glsl +++ b/third_party/webrender/webrender/res/cs_clip_rectangle.glsl @@ -12,53 +12,36 @@ flat varying vec4 vClipCenter_Radius_TL; flat varying vec4 vClipCenter_Radius_TR; flat varying vec4 vClipCenter_Radius_BL; flat varying vec4 vClipCenter_Radius_BR; - #ifdef SWGL_DRAW_SPAN - flat varying vec4 vClipCorner_TL; - flat varying vec4 vClipCorner_TR; - flat varying vec4 vClipCorner_BL; - flat varying vec4 vClipCorner_BR; - #endif #endif + flat varying float vClipMode; #ifdef WR_VERTEX_SHADER - -PER_INSTANCE in vec2 aClipLocalPos; -PER_INSTANCE in vec4 aClipLocalRect; -PER_INSTANCE in float aClipMode; -PER_INSTANCE in vec4 aClipRect_TL; -PER_INSTANCE in vec4 aClipRadii_TL; -PER_INSTANCE in vec4 aClipRect_TR; -PER_INSTANCE in vec4 aClipRadii_TR; -PER_INSTANCE in vec4 aClipRect_BL; -PER_INSTANCE in vec4 aClipRadii_BL; -PER_INSTANCE in vec4 aClipRect_BR; -PER_INSTANCE in vec4 aClipRadii_BR; - -struct ClipMaskInstanceRect { - ClipMaskInstanceCommon base; - vec2 local_pos; -}; - -ClipMaskInstanceRect fetch_clip_item() { - ClipMaskInstanceRect cmi; - - cmi.base = fetch_clip_item_common(); - cmi.local_pos = aClipLocalPos; - - return cmi; -} - struct ClipRect { RectWithSize rect; - float mode; + vec4 mode; }; +ClipRect fetch_clip_rect(ivec2 address) { + vec4 data[2] = fetch_from_gpu_cache_2_direct(address); + ClipRect rect = ClipRect(RectWithSize(data[0].xy, data[0].zw), data[1]); + return rect; +} + struct ClipCorner { RectWithSize rect; vec4 outer_inner_radius; }; +// index is of type float instead of int because using an int led to shader +// miscompilations with a macOS 10.12 Intel driver. +ClipCorner fetch_clip_corner(ivec2 address, float index) { + address += ivec2(2 + 2 * int(index), 0); + vec4 data[2] = fetch_from_gpu_cache_2_direct(address); + ClipCorner corner = ClipCorner(RectWithSize(data[0].xy, data[0].zw), data[1]); + return corner; +} + struct ClipData { ClipRect rect; ClipCorner top_left; @@ -67,23 +50,23 @@ struct ClipData { ClipCorner bottom_right; }; -ClipData fetch_clip() { +ClipData fetch_clip(ivec2 address) { ClipData clip; - clip.rect = ClipRect(RectWithSize(aClipLocalRect.xy, aClipLocalRect.zw), aClipMode); - clip.top_left = ClipCorner(RectWithSize(aClipRect_TL.xy, aClipRect_TL.zw), aClipRadii_TL); - clip.top_right = ClipCorner(RectWithSize(aClipRect_TR.xy, aClipRect_TR.zw), aClipRadii_TR); - clip.bottom_left = ClipCorner(RectWithSize(aClipRect_BL.xy, aClipRect_BL.zw), aClipRadii_BL); - clip.bottom_right = ClipCorner(RectWithSize(aClipRect_BR.xy, aClipRect_BR.zw), aClipRadii_BR); + clip.rect = fetch_clip_rect(address); + clip.top_left = fetch_clip_corner(address, 0.0); + clip.top_right = fetch_clip_corner(address, 1.0); + clip.bottom_left = fetch_clip_corner(address, 2.0); + clip.bottom_right = fetch_clip_corner(address, 3.0); return clip; } void main(void) { - ClipMaskInstanceRect cmi = fetch_clip_item(); - Transform clip_transform = fetch_transform(cmi.base.clip_transform_id); - Transform prim_transform = fetch_transform(cmi.base.prim_transform_id); - ClipData clip = fetch_clip(); + ClipMaskInstance cmi = fetch_clip_item(); + Transform clip_transform = fetch_transform(cmi.clip_transform_id); + Transform prim_transform = fetch_transform(cmi.prim_transform_id); + ClipData clip = fetch_clip(cmi.clip_data_address); RectWithSize local_rect = clip.rect.rect; local_rect.p0 = cmi.local_pos; @@ -92,13 +75,13 @@ void main(void) { local_rect, prim_transform, clip_transform, - cmi.base.sub_rect, - cmi.base.task_origin, - cmi.base.screen_origin, - cmi.base.device_pixel_scale + cmi.sub_rect, + cmi.task_origin, + cmi.screen_origin, + cmi.device_pixel_scale ); - vClipMode = clip.rect.mode; + vClipMode = clip.rect.mode.x; vLocalPos = vi.local_pos; #ifdef WR_FEATURE_FAST_PATH @@ -116,40 +99,17 @@ void main(void) { vec2 r_br = clip.bottom_right.outer_inner_radius.xy; vec2 r_bl = clip.bottom_left.outer_inner_radius.xy; - vClipCenter_Radius_TL = vec4(clip_rect.p0 + r_tl, - inverse_radii_squared(r_tl)); + vClipCenter_Radius_TL = vec4(clip_rect.p0 + r_tl, r_tl); vClipCenter_Radius_TR = vec4(clip_rect.p1.x - r_tr.x, clip_rect.p0.y + r_tr.y, - inverse_radii_squared(r_tr)); + r_tr); - vClipCenter_Radius_BR = vec4(clip_rect.p1 - r_br, - inverse_radii_squared(r_br)); + vClipCenter_Radius_BR = vec4(clip_rect.p1 - r_br, r_br); vClipCenter_Radius_BL = vec4(clip_rect.p0.x + r_bl.x, clip_rect.p1.y - r_bl.y, - inverse_radii_squared(r_bl)); - - #ifdef SWGL_DRAW_SPAN - // For the half-space span shader, we need to know the half-spaces of - // the corners separate from the center and radius. We compute a point - // that falls on the diagonal (which is just an inner vertex pushed out - // along one axis, but not on both). We also compute the direction of - // the half-space, which is a perpendicular vertex (-y,x) of the vector - // of the diagonal. We leave the scales of the vectors unchanged. - vClipCorner_TL = vec4(clip_rect.p0.x, - clip_rect.p0.y + r_tl.y, - -r_tl.yx); - vClipCorner_TR = vec4(clip_rect.p1.x - r_tr.x, - clip_rect.p0.y, - vec2(r_tr.y, -r_tr.x)); - vClipCorner_BR = vec4(clip_rect.p1.x, - clip_rect.p1.y - r_br.y, - r_br.yx); - vClipCorner_BL = vec4(clip_rect.p0.x + r_bl.x, - clip_rect.p1.y, - vec2(-r_bl.y, r_bl.x)); - #endif + r_bl); #endif } #endif @@ -173,314 +133,26 @@ void main(void) { float aa_range = compute_aa_range(local_pos); #ifdef WR_FEATURE_FAST_PATH - float dist = sd_rounded_box(local_pos, vClipParams.xy, vClipParams.z); + float d = sd_rounded_box(local_pos, vClipParams.xy, vClipParams.z); + float f = distance_aa(aa_range, d); + float final_alpha = mix(f, 1.0 - f, vClipMode); #else - float dist = distance_to_rounded_rect( - local_pos, - vClipCenter_Radius_TL, - vClipCenter_Radius_TR, - vClipCenter_Radius_BR, - vClipCenter_Radius_BL, - vTransformBounds - ); -#endif + float alpha = init_transform_fs(local_pos); + + float clip_alpha = rounded_rect(local_pos, + vClipCenter_Radius_TL, + vClipCenter_Radius_TR, + vClipCenter_Radius_BR, + vClipCenter_Radius_BL, + aa_range); - // Compute AA for the given dist and range. - float alpha = distance_aa(aa_range, dist); + float combined_alpha = alpha * clip_alpha; // Select alpha or inverse alpha depending on clip in/out. - float final_alpha = mix(alpha, 1.0 - alpha, vClipMode); + float final_alpha = mix(combined_alpha, 1.0 - combined_alpha, vClipMode); +#endif float final_final_alpha = vLocalPos.w > 0.0 ? final_alpha : 0.0; oFragColor = vec4(final_final_alpha, 0.0, 0.0, 1.0); } - -#ifdef SWGL_DRAW_SPAN -// Currently the cs_clip_rectangle shader is slow because it always evaluates -// the corner ellipse segments and the rectangle AA for every fragment the -// shader is run on. To alleviate this for now with SWGL, this essentially -// implements a rounded-rectangle span rasterizer inside the span shader. The -// motivation is that we can separate out the parts of the span which are fully -// opaque and fully transparent, outputting runs of fixed color in those areas, -// while only evaluating the ellipse segments and AA in the smaller outlying -// parts of the span that actually need it. -// The shader conceptually represents a rounded rectangle as an inner octagon -// (8 half-spaces) bounding the opaque region and an outer octagon bounding the -// curve and AA parts. Everything outside is transparent. The line of the span -// is intersected with half-spaces, looking for interior spans that minimally -// intersect the half-spaces (start max, end min). In the ideal case we hit a -// start corner ellipse segment and an end corner ellipse segment, rendering -// the two curves on the ends with an opaque run in between, outputting clear -// for any transparent runs before and after the start and end curves. -// This is slightly complicated by the fact that the results here must agree -// with the main results of the fragment shader, in case SWGL has to fall back -// to the main fragment shader for any reason. So, we make an effort to handle -// both ways of operating - the uniform radius fast-path and the varying radius -// slow-path. -void swgl_drawSpanR8() { - // If the span is completely outside the Z-range and clipped out, just - // output clear so we don't need to consider invalid W in the rest of the - // shader. - float w = swgl_forceScalar(vLocalPos.w); - if (w <= 0.0) { - swgl_commitSolidR8(0.0); - return; - } - - // To start, we evaluate the rounded-rectangle in local space relative to - // the local-space position. This will be interpolated across the span to - // track whether we intersect any half-spaces. - w = 1.0 / w; - vec2 local_pos = vLocalPos.xy * w; - vec2 local_pos0 = swgl_forceScalar(local_pos); - vec2 local_step = swgl_interpStep(vLocalPos).xy * w; - float step_scale = max(dot(local_step, local_step), 1.0e-6); - - // Get the local-space AA range. This range represents 1/fwidth(local_pos), - // essentially the scale of how much local-space maps to an AA pixel. We - // need to know the inverse, how much local-space we traverse per AA pixel - // pixel step. We then scale this to represent the amount of span steps - // traversed per AA pixel step. - float aa_range = compute_aa_range(local_pos); - float aa_margin = inversesqrt(aa_range * aa_range * step_scale); - - // We need to know the bounds of the aligned rectangle portion of the rrect - // in local-space. If we're using the fast-path, this is specified as the - // inner bounding-box half-width of the rrect and the uniform outer radius - // of the corners in vClipParams, which we map to the outer bounding-box. - // For the general case, we have already stored the outer bounding box in - // vTransformBounds. - #ifdef WR_FEATURE_FAST_PATH - vec4 clip_rect = vec4(-vClipParams.xy - vClipParams.z, vClipParams.xy + vClipParams.z); - #else - vec4 clip_rect = vTransformBounds; - #endif - - // We need to compute the local-space distance to the bounding box and then - // figure out how many processing steps that maps to. If we are stepping in - // a negative direction on an axis, we need to swap the sides of the box - // which we consider as the start or end. If there is no local-space step - // on an axis (i.e. constant Y), we need to take care to force the steps to - // either the start or end of the span depending on if we are inside or - // outside of the bounding box. - vec4 clip_dist = - mix(clip_rect, clip_rect.zwxy, lessThan(local_step, vec2(0.0)).xyxy) - - local_pos0.xyxy; - clip_dist = - mix(1.0e6 * step(0.0, clip_dist), - clip_dist * recip(local_step).xyxy, - notEqual(local_step, vec2(0.0)).xyxy); - - // Initially, the opaque region is bounded by the further start intersect - // with the bounding box and the nearest end intersect with the bounding - // box. - float opaque_start = max(clip_dist.x, clip_dist.y); - float opaque_end = min(clip_dist.z, clip_dist.w); - float aa_start = opaque_start; - float aa_end = opaque_end; - - // Here we actually intersect with the half-space of the corner. We get the - // plane distance of the local-space position from the diagonal bounding - // ellipse segment from the opaque region. The half-space is defined by the - // direction vector of the plane and an offset point that falls on the - // dividing line (which is a vertex on the corner box, which is actually on - // the outer radius of the bounding box, but not a corner vertex). This - // distance is positive if on the curve side and negative if on the inner - // opaque region. If we are on the curve side, we need to verify we are - // traveling in direction towards the opaque region so that we will - // eventually intersect the diagonal so we can calculate when the start - // corner segment will end, otherwise we are going away from the rrect. - // If we are inside the opaque interior, we need to verify we are traveling - // in direction towards the curve, so that we can calculate when the end - // corner segment will start. Further, if we intersect, we calculate the - // offset of the outer octagon where AA starts from the inner octagon of - // where the opaque region starts using the apex vector (which is transpose - // of the half-space's direction). - // - // We need to intersect the corner ellipse segments. Significantly, we need - // to know where the apex of the ellipse segment is and how far to push the - // outer diagonal of the octagon from the inner diagonal. The position of - // the inner diagonal simply runs diagonal across the corner box and has a - // constant offset from vertex on the inner bounding box. The apex also has - // a constant offset along the opposite diagonal relative to the diagonal - // intersect which is 1/sqrt(2) - 0.5 assuming unit length for the diagonal. - // We then need to project the vector to the apex onto the local-space step - // scale, but we do this with reference to the normal vector of the diagonal - // using dot(normal, apex) / dot(normal, local_step), where the apex vector - // is (0.7071 - 0.5) * abs(normal).yx * sign(normal). - vec4 start_plane = vec4(1.0e6); - vec4 end_plane = vec4(1.0e6); - - #define CLIP_CORNER(offset, normal, info) do { \ - float dist = dot(local_pos0 - (offset), (normal)); \ - float scale = -dot(local_step, (normal)); \ - if (scale >= 0.0) { \ - if (dist > opaque_start * scale) { \ - start_corner = info; \ - start_plane = vec4(offset, normal); \ - float inv_scale = recip(max(scale, 1.0e-6)); \ - opaque_start = dist * inv_scale; \ - float apex = (0.7071 - 0.5) * 2.0 * abs(normal.x * normal.y); \ - aa_start = opaque_start - apex * inv_scale; \ - } \ - } else if (dist > opaque_end * scale) { \ - end_corner = info; \ - end_plane = vec4(offset, normal); \ - float inv_scale = recip(min(scale, -1.0e-6)); \ - opaque_end = dist * inv_scale; \ - float apex = (0.7071 - 0.5) * 2.0 * abs(normal.x * normal.y); \ - aa_end = opaque_end - apex * inv_scale; \ - } \ - } while (false) - - #ifdef WR_FEATURE_FAST_PATH - // For the fast-path, we only have the half-width of the inner bounding - // box. We need to map this to points that fall on the diagonal of the - // half-space for each corner. To do this we just need to push out the - // vertex in the right direction on a single axis, leaving the other - // unchanged. - vec2 corner_tl = -vClipParams.xy - vec2(vClipParams.z, 0.0); - vec2 corner_tr = vec2(vClipParams.x, -vClipParams.y - vClipParams.z); - vec2 corner_br = vClipParams.xy + vec2(vClipParams.z, 0.0); - vec2 corner_bl = vec2(-vClipParams.x, vClipParams.y + vClipParams.z); - // The direction vector of the corner half-space has constant length, - // but just needs an appropriate direction set. - vec2 n_tl = -vClipParams.zz; - vec2 n_tr = vec2(vClipParams.z, -vClipParams.z); - vec2 n_br = vClipParams.zz; - vec2 n_bl = vec2(-vClipParams.z, vClipParams.z); - - bool start_corner = false; - bool end_corner = false; - - // Clip against the corner half-spaces. - CLIP_CORNER(corner_tl, n_tl, true); - CLIP_CORNER(corner_tr, n_tr, true); - CLIP_CORNER(corner_br, n_br, true); - CLIP_CORNER(corner_bl, n_bl, true); - - // Later we need to calculate distance AA for both corners and the - // outer bounding rect. For the fast-path, this is all done inside - // sd_rounded_box. - #define AA_RECT(local_pos) \ - sd_rounded_box(local_pos, vClipParams.xy, vClipParams.z) - #else - // For the general case, we need to remember which of the actual start - // and end corners we intersect, so that we can evaluate the curve AA - // against only those corners rather than having to try against all 4 - // corners for both sides of the span. Initialize these values so that - // if no corner is intersected, they will just zero the AA. - vec4 start_corner = vec4(vec2(1.0e6), vec2(1.0)); - vec4 end_corner = vec4(vec2(1.0e6), vec2(1.0)); - - // Clip against the corner half-spaces. We have already computed the - // corner half-spaces in the vertex shader. - CLIP_CORNER(vClipCorner_TL.xy, vClipCorner_TL.zw, vClipCenter_Radius_TL); - CLIP_CORNER(vClipCorner_TR.xy, vClipCorner_TR.zw, vClipCenter_Radius_TR); - CLIP_CORNER(vClipCorner_BR.xy, vClipCorner_BR.zw, vClipCenter_Radius_BR); - CLIP_CORNER(vClipCorner_BL.xy, vClipCorner_BL.zw, vClipCenter_Radius_BL); - - // Later we need to calculate distance AA for both corners and the - // outer bounding rect. For the general case, we need to explicitly - // evaluate either the ellipse segment distance or the rect distance. - #define AA_RECT(local_pos) \ - signed_distance_rect(local_pos, vTransformBounds.xy, vTransformBounds.zw) - #define AA_CORNER(local_pos, corner) \ - distance_to_ellipse_approx(local_pos - corner.xy, corner.zw, 1.0) - #endif - - // Pad the AA region by a margin, as the intersections take place assuming - // pixel centers, but AA actually starts half a pixel away from the center. - // If the AA region narrows to nothing, be careful not to inflate so much - // that we start processing AA for fragments that don't need it. - aa_margin = max(aa_margin - max(aa_start - aa_end, 0.0), 0.0); - aa_start -= aa_margin; - aa_end += aa_margin; - - // Compute the thresholds at which we need to transition between various - // segments of the span, from fully transparent outside to the start of - // the outer octagon where AA starts, from there to where the inner opaque - // octagon starts, from there to where the opaque inner octagon ends and - // AA starts again, to finally where the outer octagon/AA ends and we're - // back to fully transparent. These thresholds are just flipped offsets - // from the start of the span so we can compare against the remaining - // span length which automatically deducts as we commit fragments. - ivec4 steps = ivec4(clamp( - swgl_SpanLength - - swgl_StepSize * - vec4(floor(aa_start), ceil(opaque_start), floor(opaque_end), ceil(aa_end)), - 0.0, swgl_SpanLength)); - int aa_start_len = steps.x; - int opaque_start_len = steps.y; - int opaque_end_len = steps.z; - int aa_end_len = steps.w; - - // Output fully clear while we're outside the AA region. - if (swgl_SpanLength > aa_start_len) { - int num_aa = swgl_SpanLength - aa_start_len; - swgl_commitPartialSolidR8(num_aa, vClipMode); - local_pos += float(num_aa / swgl_StepSize) * local_step; - } - #ifdef AA_CORNER - if (start_plane.x < 1.0e5) { - // We're now in the outer octagon which requires AA. Evaluate the corner - // distance of the start corner here and output AA for it. Before we hit - // the actual opaque inner octagon, we have a transitional step where the - // diagonal might intersect mid-way through the step. We have consider - // either the corner or rect distance depending on which side we're on. - while (swgl_SpanLength > opaque_start_len) { - float alpha = distance_aa(aa_range, - dot(local_pos - start_plane.xy, start_plane.zw) > 0.0 - ? AA_CORNER(local_pos, start_corner) - : AA_RECT(local_pos)); - swgl_commitColorR8(mix(alpha, 1.0 - alpha, vClipMode)); - local_pos += local_step; - } - } - #endif - // If there's no start corner, just do rect AA until opaque. - while (swgl_SpanLength > opaque_start_len) { - float alpha = distance_aa(aa_range, AA_RECT(local_pos)); - swgl_commitColorR8(mix(alpha, 1.0 - alpha, vClipMode)); - local_pos += local_step; - } - // Now we're finally in the opaque inner octagon part of the span. Just - // output a solid run. - if (swgl_SpanLength > opaque_end_len) { - int num_opaque = swgl_SpanLength - opaque_end_len; - swgl_commitPartialSolidR8(num_opaque, 1.0 - vClipMode); - local_pos += float(num_opaque / swgl_StepSize) * local_step; - } - #ifdef AA_CORNER - if (end_plane.x < 1.0e5) { - // Finally we're in the AA region on the other side, inside the outer - // octagon again. Just evaluate the distance to the end corner and - // compute AA for it. We're leaving the opaque inner octagon, but like - // before, we have to be careful we're not dealing with a step partially - // intersected by the end corner's diagonal. Check which side we are on - // and use either the corner or rect distance as appropriate. - while (swgl_SpanLength > aa_end_len) { - float alpha = distance_aa(aa_range, - dot(local_pos - end_plane.xy, end_plane.zw) > 0.0 - ? AA_CORNER(local_pos, end_corner) - : AA_RECT(local_pos)); - swgl_commitColorR8(mix(alpha, 1.0 - alpha, vClipMode)); - local_pos += local_step; - } - } - #endif - // If there's no end corner, just do rect AA until clear. - while (swgl_SpanLength > aa_end_len) { - float alpha = distance_aa(aa_range, AA_RECT(local_pos)); - swgl_commitColorR8(mix(alpha, 1.0 - alpha, vClipMode)); - local_pos += local_step; - } - // We're now outside the outer AA octagon on the other side. Just output - // fully clear. - if (swgl_SpanLength > 0) { - swgl_commitPartialSolidR8(swgl_SpanLength, vClipMode); - } -} -#endif - #endif diff --git a/third_party/webrender/webrender/res/cs_conic_gradient.glsl b/third_party/webrender/webrender/res/cs_conic_gradient.glsl deleted file mode 100644 index bbe6f245e3e..00000000000 --- a/third_party/webrender/webrender/res/cs_conic_gradient.glsl +++ /dev/null @@ -1,64 +0,0 @@ -/* 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 shared,rect,render_task,gpu_cache,gradient - -#define PI 3.141592653589793 - -varying vec2 v_pos; - -flat varying vec2 v_center; -flat varying float v_start_offset; -flat varying float v_offset_scale; -flat varying float v_angle; - -#ifdef WR_VERTEX_SHADER - -#define EXTEND_MODE_REPEAT 1 - -// Rectangle in origin+size format -PER_INSTANCE in vec4 aTaskRect; -PER_INSTANCE in vec2 aCenter; -PER_INSTANCE in vec2 aScale; -PER_INSTANCE in float aStartOffset; -PER_INSTANCE in float aEndOffset; -PER_INSTANCE in float aAngle; -PER_INSTANCE in int aExtendMode; -PER_INSTANCE in int aGradientStopsAddress; - -void main(void) { - // Store 1/d where d = end_offset - start_offset - // If d = 0, we can't get its reciprocal. Instead, just use a zero scale. - float d = aEndOffset - aStartOffset; - v_offset_scale = d != 0.0 ? 1.0 / d : 0.0; - - vec2 pos = aTaskRect.xy + aTaskRect.zw * aPosition.xy; - gl_Position = uTransform * vec4(pos, 0.0, 1.0); - - v_angle = PI / 2.0 - aAngle; - v_start_offset = aStartOffset * v_offset_scale; - - // v_pos and v_center are in a coordinate space relative to the task rect - // (so they are independent of the task origin). - v_center = aCenter * v_offset_scale; - v_pos = aTaskRect.zw * aPosition.xy * v_offset_scale * aScale; - - v_gradient_repeat = float(aExtendMode == EXTEND_MODE_REPEAT); - v_gradient_address = aGradientStopsAddress; -} -#endif - - -#ifdef WR_FRAGMENT_SHADER - -void main(void) { - // Use inverse trig to find the angle offset from the relative position. - vec2 current_dir = v_pos - v_center; - float current_angle = atan(current_dir.y, current_dir.x) + v_angle; - float offset = fract(current_angle / (2.0 * PI)) * v_offset_scale - v_start_offset; - - oFragColor = sample_gradient(offset); -} - -#endif diff --git a/third_party/webrender/webrender/res/cs_fast_linear_gradient.glsl b/third_party/webrender/webrender/res/cs_fast_linear_gradient.glsl deleted file mode 100644 index a1f1d7a01f0..00000000000 --- a/third_party/webrender/webrender/res/cs_fast_linear_gradient.glsl +++ /dev/null @@ -1,32 +0,0 @@ -/* 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 shared - -varying float vPos; -flat varying vec4 vColor0; -flat varying vec4 vColor1; - -#ifdef WR_VERTEX_SHADER - -PER_INSTANCE in vec4 aTaskRect; -PER_INSTANCE in vec4 aColor0; -PER_INSTANCE in vec4 aColor1; -PER_INSTANCE in float aAxisSelect; - -void main(void) { - vPos = mix(0.0, 1.0, mix(aPosition.x, aPosition.y, aAxisSelect)); - - vColor0 = aColor0; - vColor1 = aColor1; - - gl_Position = uTransform * vec4(aTaskRect.xy + aTaskRect.zw * aPosition.xy, 0.0, 1.0); -} -#endif - -#ifdef WR_FRAGMENT_SHADER -void main(void) { - oFragColor = mix(vColor0, vColor1, vPos); -} -#endif diff --git a/third_party/webrender/webrender/res/cs_gradient.glsl b/third_party/webrender/webrender/res/cs_gradient.glsl new file mode 100644 index 00000000000..6e6b5c4ad06 --- /dev/null +++ b/third_party/webrender/webrender/res/cs_gradient.glsl @@ -0,0 +1,56 @@ +/* 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 shared + +varying float vPos; +flat varying vec4 vStops; +flat varying vec4 vColor0; +flat varying vec4 vColor1; +flat varying vec4 vColor2; +flat varying vec4 vColor3; + +#ifdef WR_VERTEX_SHADER + +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)); + + vStops = aStops; + vColor0 = aColor0; + vColor1 = aColor1; + vColor2 = aColor2; + vColor3 = aColor3; + + gl_Position = uTransform * vec4(aTaskRect.xy + aTaskRect.zw * aPosition.xy, 0.0, 1.0); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +float linear_step(float edge0, float edge1, float x) { + if (edge0 >= edge1) { + return 0.0; + } + + return clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); +} + +void main(void) { + vec4 color = vColor0; + + color = mix(color, vColor1, linear_step(vStops.x, vStops.y, vPos)); + color = mix(color, vColor2, linear_step(vStops.y, vStops.z, vPos)); + color = mix(color, vColor3, linear_step(vStops.z, vStops.w, vPos)); + + oFragColor = color; +} +#endif diff --git a/third_party/webrender/webrender/res/cs_line_decoration.glsl b/third_party/webrender/webrender/res/cs_line_decoration.glsl index 90d0fa5550b..7063bcffb12 100644 --- a/third_party/webrender/webrender/res/cs_line_decoration.glsl +++ b/third_party/webrender/webrender/res/cs_line_decoration.glsl @@ -25,7 +25,7 @@ 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::line_dec::get_line_decoration_sizes. +// prim_store::get_line_decoration_sizes. PER_INSTANCE in vec2 aLocalSize; // A LINE_STYLE_* value, indicating what sort of line to draw. diff --git a/third_party/webrender/webrender/res/cs_linear_gradient.glsl b/third_party/webrender/webrender/res/cs_linear_gradient.glsl deleted file mode 100644 index f3096ac0993..00000000000 --- a/third_party/webrender/webrender/res/cs_linear_gradient.glsl +++ /dev/null @@ -1,68 +0,0 @@ -/* 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 shared,rect,render_task,gpu_cache,gradient - -varying vec2 v_pos; - -flat varying vec2 v_scale_dir; -flat varying float v_start_offset; - -#ifdef WR_VERTEX_SHADER - -#define EXTEND_MODE_REPEAT 1 - -// Rectangle in origin+size format -PER_INSTANCE in vec4 aTaskRect; -PER_INSTANCE in vec2 aStartPoint; -PER_INSTANCE in vec2 aEndPoint; -PER_INSTANCE in vec2 aScale; -PER_INSTANCE in int aExtendMode; -PER_INSTANCE in int aGradientStopsAddress; - -void main(void) { - vec2 pos = aTaskRect.xy + aTaskRect.zw * aPosition.xy; - gl_Position = uTransform * vec4(pos, 0.0, 1.0); - - v_pos = aPosition.xy * aScale; - - vec2 dir = aEndPoint - aStartPoint; - - // Normalize UV and offsets to 0..1 scale. - v_scale_dir = dir / dot(dir, dir); - v_start_offset = dot(aStartPoint, v_scale_dir); - - v_scale_dir *= aTaskRect.zw; - - v_gradient_repeat = float(aExtendMode == EXTEND_MODE_REPEAT); - v_gradient_address = aGradientStopsAddress; -} -#endif - - -#ifdef WR_FRAGMENT_SHADER - -void main(void) { - // Project position onto a direction vector to compute offset. - float offset = dot(v_pos, v_scale_dir) - v_start_offset; - - oFragColor = sample_gradient(offset); -} - - -#ifdef SWGL_DRAW_SPAN -void swgl_drawSpanRGBA8() { - int address = swgl_validateGradient(sGpuCache, get_gpu_cache_uv(v_gradient_address), int(GRADIENT_ENTRIES + 2.0)); - if (address < 0) { - return; - } - - float offset = dot(v_pos, v_scale_dir) - v_start_offset; - swgl_commitLinearGradientRGBA8(sGpuCache, address, GRADIENT_ENTRIES, v_gradient_repeat != 0.0, - offset); -} -#endif - - -#endif diff --git a/third_party/webrender/webrender/res/cs_radial_gradient.glsl b/third_party/webrender/webrender/res/cs_radial_gradient.glsl deleted file mode 100644 index c5b942634cb..00000000000 --- a/third_party/webrender/webrender/res/cs_radial_gradient.glsl +++ /dev/null @@ -1,71 +0,0 @@ -/* 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 shared,rect,render_task,gpu_cache,gradient - -varying vec2 v_pos; - -flat varying float v_start_radius; - -#ifdef WR_VERTEX_SHADER - -#define EXTEND_MODE_REPEAT 1 - -// Rectangle in origin+size format -PER_INSTANCE in vec4 aTaskRect; -PER_INSTANCE in vec2 aCenter; -PER_INSTANCE in vec2 aScale; -PER_INSTANCE in float aStartRadius; -PER_INSTANCE in float aEndRadius; -PER_INSTANCE in float aXYRatio; -PER_INSTANCE in int aExtendMode; -PER_INSTANCE in int aGradientStopsAddress; - -void main(void) { - // Store 1/rd where rd = end_radius - start_radius - // If rd = 0, we can't get its reciprocal. Instead, just use a zero scale. - float rd = aEndRadius - aStartRadius; - float radius_scale = rd != 0.0 ? 1.0 / rd : 0.0; - - vec2 pos = aTaskRect.xy + aTaskRect.zw * aPosition.xy; - gl_Position = uTransform * vec4(pos, 0.0, 1.0); - - v_start_radius = aStartRadius * radius_scale; - - // Transform all coordinates by the y scale so the - // fragment shader can work with circles - - // v_pos is in a coordinate space relative to the task rect - // (so it is independent of the task origin). - v_pos = (aTaskRect.zw * aPosition.xy * aScale - aCenter) * radius_scale; - v_pos.y *= aXYRatio; - - v_gradient_repeat = float(aExtendMode == EXTEND_MODE_REPEAT); - v_gradient_address = aGradientStopsAddress; -} -#endif - - -#ifdef WR_FRAGMENT_SHADER - -void main(void) { - // Solve for t in length(pd) = v_start_radius + t * rd - float offset = length(v_pos) - v_start_radius; - - oFragColor = sample_gradient(offset); -} - -#ifdef SWGL_DRAW_SPAN -void swgl_drawSpanRGBA8() { - int address = swgl_validateGradient(sGpuCache, get_gpu_cache_uv(v_gradient_address), - int(GRADIENT_ENTRIES + 2.0)); - if (address < 0) { - return; - } - swgl_commitRadialGradientRGBA8(sGpuCache, address, GRADIENT_ENTRIES, v_gradient_repeat != 0.0, - v_pos, v_start_radius); -} -#endif - -#endif diff --git a/third_party/webrender/webrender/res/cs_scale.glsl b/third_party/webrender/webrender/res/cs_scale.glsl index 816375343ce..6d0c0e59982 100644 --- a/third_party/webrender/webrender/res/cs_scale.glsl +++ b/third_party/webrender/webrender/res/cs_scale.glsl @@ -2,43 +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/. */ -// This shader must remain compatible with ESSL 1, at least for the -// WR_FEATURE_TEXTURE_EXTERNAL_ESSL1 feature, so that it can be used to render -// video on GLES devices without GL_OES_EGL_image_external_essl3 support. -// This means we cannot use textureSize(), int inputs/outputs, etc. +#include shared,prim_shared -#include shared - -varying vec2 vUv; +varying vec3 vUv; flat varying vec4 vUvRect; -#ifdef WR_FEATURE_TEXTURE_EXTERNAL_ESSL1 -uniform vec2 uTextureSize; -#endif #ifdef WR_VERTEX_SHADER -PER_INSTANCE attribute vec4 aScaleTargetRect; -PER_INSTANCE attribute vec4 aScaleSourceRect; +PER_INSTANCE in vec4 aScaleTargetRect; +PER_INSTANCE in ivec4 aScaleSourceRect; +PER_INSTANCE in int aScaleSourceLayer; void main(void) { - vec2 src_offset = aScaleSourceRect.xy; - vec2 src_size = aScaleSourceRect.zw; - - // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use - // non-normalized texture coordinates. -#ifdef WR_FEATURE_TEXTURE_RECT - vec2 texture_size = vec2(1, 1); -#elif defined(WR_FEATURE_TEXTURE_EXTERNAL_ESSL1) - vec2 texture_size = uTextureSize; -#else - vec2 texture_size = vec2(TEX_SIZE(sColor0)); -#endif + RectWithSize src_rect = RectWithSize(vec2(aScaleSourceRect.xy), vec2(aScaleSourceRect.zw)); + + vec2 texture_size = vec2(textureSize(sColor0, 0).xy); + vUv.z = float(aScaleSourceLayer); - vUvRect = vec4(src_offset + vec2(0.5), - src_offset + src_size - vec2(0.5)) / texture_size.xyxy; + vUvRect = vec4(src_rect.p0 + vec2(0.5), + src_rect.p0 + src_rect.size - vec2(0.5)) / texture_size.xyxy; vec2 pos = aScaleTargetRect.xy + aScaleTargetRect.zw * aPosition.xy; - vUv = (src_offset + src_size * aPosition.xy) / texture_size; + vUv.xy = (src_rect.p0 + src_rect.size * aPosition.xy) / texture_size; gl_Position = uTransform * vec4(pos, 0.0, 1.0); } @@ -48,14 +33,8 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER void main(void) { - vec2 st = clamp(vUv, vUvRect.xy, vUvRect.zw); - oFragColor = TEX_SAMPLE(sColor0, st); + vec2 st = clamp(vUv.xy, vUvRect.xy, vUvRect.zw); + oFragColor = texture(sColor0, vec3(st, vUv.z)); } -#ifdef SWGL_DRAW_SPAN -void swgl_drawSpanRGBA8() { - swgl_commitTextureLinearRGBA8(sColor0, vUv, vUvRect); -} -#endif - #endif diff --git a/third_party/webrender/webrender/res/cs_svg_filter.glsl b/third_party/webrender/webrender/res/cs_svg_filter.glsl index e908536faac..cfb6dd13cfa 100644 --- a/third_party/webrender/webrender/res/cs_svg_filter.glsl +++ b/third_party/webrender/webrender/res/cs_svg_filter.glsl @@ -2,12 +2,10 @@ * 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_FEATURE_TEXTURE_2D - #include shared,prim_shared -varying vec2 vInput1Uv; -varying vec2 vInput2Uv; +varying vec3 vInput1Uv; +varying vec3 vInput2Uv; flat varying vec4 vInput1UvRect; flat varying vec4 vInput2UvRect; flat varying int vFilterInputCount; @@ -15,16 +13,9 @@ flat varying int vFilterKind; flat varying ivec4 vData; flat varying vec4 vFilterData0; flat varying vec4 vFilterData1; -#if defined(PLATFORM_ANDROID) && !defined(SWGL) -// Work around Adreno 3xx driver bug. See the v_perspective comment in -// brush_image or bug 1630356 for details. -flat varying vec2 vFloat0Vec; -#define vFloat0 vFloat0Vec.x -#else flat varying float vFloat0; -#endif flat varying mat4 vColorMat; -flat varying ivec4 vFuncs; +flat varying int vFuncs[4]; #define FILTER_BLEND 0 #define FILTER_FLOOD 1 @@ -57,7 +48,7 @@ PER_INSTANCE in int aFilterGenericInt; PER_INSTANCE in ivec2 aFilterExtraDataAddress; struct FilterTask { - RectWithSize task_rect; + RenderTaskCommonData common_data; vec3 user_data; }; @@ -65,44 +56,51 @@ FilterTask fetch_filter_task(int address) { RenderTaskData task_data = fetch_render_task_data(address); FilterTask task = FilterTask( - task_data.task_rect, + task_data.common_data, task_data.user_data.xyz ); return task; } -vec4 compute_uv_rect(RectWithSize task_rect, vec2 texture_size) { +vec4 compute_uv_rect(RenderTaskCommonData task, vec2 texture_size) { + RectWithSize task_rect = task.task_rect; + vec4 uvRect = vec4(task_rect.p0 + vec2(0.5), task_rect.p0 + task_rect.size - vec2(0.5)); uvRect /= texture_size.xyxy; return uvRect; } -vec2 compute_uv(RectWithSize task_rect, vec2 texture_size) { +vec3 compute_uv(RenderTaskCommonData task, vec2 texture_size) { + RectWithSize task_rect = task.task_rect; + vec3 uv = vec3(0.0, 0.0, task.texture_layer_index); + vec2 uv0 = task_rect.p0 / texture_size; vec2 uv1 = floor(task_rect.p0 + task_rect.size) / texture_size; - return mix(uv0, uv1, aPosition.xy); + uv.xy = mix(uv0, uv1, aPosition.xy); + + return uv; } void main(void) { FilterTask filter_task = fetch_filter_task(aFilterRenderTaskAddress); - RectWithSize target_rect = filter_task.task_rect; + RectWithSize target_rect = filter_task.common_data.task_rect; vec2 pos = target_rect.p0 + target_rect.size * aPosition.xy; - RectWithSize input_1_task; + RenderTaskCommonData input_1_task; if (aFilterInputCount > 0) { - vec2 texture_size = vec2(TEX_SIZE(sColor0).xy); - input_1_task = fetch_render_task_rect(aFilterInput1TaskAddress); + vec2 texture_size = vec2(textureSize(sColor0, 0).xy); + input_1_task = fetch_render_task_common_data(aFilterInput1TaskAddress); vInput1UvRect = compute_uv_rect(input_1_task, texture_size); vInput1Uv = compute_uv(input_1_task, texture_size); } - RectWithSize input_2_task; + RenderTaskCommonData input_2_task; if (aFilterInputCount > 1) { - vec2 texture_size = vec2(TEX_SIZE(sColor1).xy); - input_2_task = fetch_render_task_rect(aFilterInput2TaskAddress); + vec2 texture_size = vec2(textureSize(sColor1, 0).xy); + input_2_task = fetch_render_task_common_data(aFilterInput2TaskAddress); vInput2UvRect = compute_uv_rect(input_2_task, texture_size); vInput2Uv = compute_uv(input_2_task, texture_size); } @@ -117,10 +115,10 @@ void main(void) { // https://github.com/servo/webrender/wiki/Driver-issues#bug-1505871---assignment-to-varying-flat-arrays-inside-switch-statement-of-vertex-shader-suspected-miscompile-on-windows // default: just to satisfy angle_shader_validation.rs which needs one // default: for every switch, even in comments. - vFuncs.r = (aFilterGenericInt >> 12) & 0xf; // R - vFuncs.g = (aFilterGenericInt >> 8) & 0xf; // G - vFuncs.b = (aFilterGenericInt >> 4) & 0xf; // B - vFuncs.a = (aFilterGenericInt) & 0xf; // A + vFuncs[0] = (aFilterGenericInt >> 12) & 0xf; // R + vFuncs[1] = (aFilterGenericInt >> 8) & 0xf; // G + vFuncs[2] = (aFilterGenericInt >> 4) & 0xf; // B + vFuncs[3] = (aFilterGenericInt) & 0xf; // A switch (aFilterKind) { case FILTER_BLEND: @@ -141,10 +139,10 @@ void main(void) { vFilterData0 = fetch_from_gpu_cache_1_direct(aFilterExtraDataAddress); break; case FILTER_OFFSET: - vec2 texture_size = vec2(TEX_SIZE(sColor0).xy); + vec2 texture_size = vec2(textureSize(sColor0, 0).xy); vFilterData0 = vec4(-filter_task.user_data.xy / texture_size, vec2(0.0)); - RectWithSize task_rect = input_1_task; + RectWithSize task_rect = input_1_task.task_rect; vec4 clipRect = vec4(task_rect.p0, task_rect.p0 + task_rect.size); clipRect /= texture_size.xyxy; vFilterData1 = clipRect; @@ -434,10 +432,8 @@ vec4 ComponentTransfer(vec4 colora) { vec4 texel; int k; - // Dynamically indexing a vector is buggy on some devices, so use a temporary array. - int[4] funcs = int[4](vFuncs.r, vFuncs.g, vFuncs.b, vFuncs.a); for (int i = 0; i < 4; i++) { - switch (funcs[i]) { + switch (vFuncs[i]) { case COMPONENT_TRANSFER_IDENTITY: break; case COMPONENT_TRANSFER_TABLE: @@ -511,9 +507,9 @@ vec4 composite(vec4 Cs, vec4 Cb, int mode) { return Cr; } -vec4 sampleInUvRect(sampler2D sampler, vec2 uv, vec4 uvRect) { +vec4 sampleInUvRect(sampler2DArray sampler, vec3 uv, vec4 uvRect) { vec2 clamped = clamp(uv.xy, uvRect.xy, uvRect.zw); - return texture(sampler, clamped); + return texture(sampler, vec3(clamped, uv.z)); } void main(void) { @@ -568,8 +564,8 @@ void main(void) { needsPremul = false; break; case FILTER_OFFSET: - vec2 offsetUv = vInput1Uv + vFilterData0.xy; - result = sampleInUvRect(sColor0, offsetUv, vInput1UvRect); + vec2 offsetUv = vInput1Uv.xy + vFilterData0.xy; + result = sampleInUvRect(sColor0, vec3(offsetUv, vInput1Uv.z), vInput1UvRect); result *= point_inside_rect(offsetUv, vFilterData1.xy, vFilterData1.zw); needsPremul = false; break; diff --git a/third_party/webrender/webrender/res/debug_font.glsl b/third_party/webrender/webrender/res/debug_font.glsl index 475a97dfce2..5b9fb2109fb 100644 --- a/third_party/webrender/webrender/res/debug_font.glsl +++ b/third_party/webrender/webrender/res/debug_font.glsl @@ -2,8 +2,6 @@ * 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_FEATURE_TEXTURE_2D - #include shared,shared_other varying vec2 vColorTexCoord; @@ -24,7 +22,7 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER void main(void) { - float alpha = texture(sColor0, vColorTexCoord).r; + float alpha = texture(sColor0, vec3(vColorTexCoord.xy, 0.0)).r; oFragColor = vColor * alpha; } #endif diff --git a/third_party/webrender/webrender/res/ellipse.glsl b/third_party/webrender/webrender/res/ellipse.glsl index 36d20b8a5dc..1bcacaafda5 100644 --- a/third_party/webrender/webrender/res/ellipse.glsl +++ b/third_party/webrender/webrender/res/ellipse.glsl @@ -2,14 +2,6 @@ * 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/. */ -// Preprocess the radii for computing the distance approximation. This should -// be used in the vertex shader if possible to avoid doing expensive division -// in the fragment shader. When dealing with a point (zero radii), approximate -// it as an ellipse with very small radii so that we don't need to branch. -vec2 inverse_radii_squared(vec2 radii) { - return 1.0 / max(radii * radii, 1.0e-6); -} - #ifdef WR_FRAGMENT_SHADER // One iteration of Newton's method on the 2D equation of an ellipse: @@ -26,60 +18,76 @@ vec2 inverse_radii_squared(vec2 radii) { // // See G. Taubin, "Distance Approximations for Rasterizing Implicit // Curves", section 3. -// -// A scale relative to the unit scale of the ellipse may be passed in to cause -// the math to degenerate to length(p) when scale is 0, or otherwise give the -// normal distance approximation if scale is 1. -float distance_to_ellipse_approx(vec2 p, vec2 inv_radii_sq, float scale) { - vec2 p_r = p * inv_radii_sq; - float g = dot(p, p_r) - scale; - vec2 dG = (1.0 + scale) * p_r; - return g * inversesqrt(dot(dG, dG)); -} - -// Slower but more accurate version that uses the exact distance when dealing -// with a 0-radius point distance and otherwise uses the faster approximation -// when dealing with non-zero radii. -float distance_to_ellipse(vec2 p, vec2 radii) { - return distance_to_ellipse_approx(p, inverse_radii_squared(radii), - float(all(greaterThan(radii, vec2(0.0))))); +float distance_to_ellipse(vec2 p, vec2 radii, float aa_range) { + float dist; + if (any(lessThanEqual(radii, vec2(0.0)))) { + dist = length(p); + } else { + vec2 invRadiiSq = 1.0 / (radii * radii); + float g = dot(p * p * invRadiiSq, vec2(1.0)) - 1.0; + vec2 dG = 2.0 * p * invRadiiSq; + dist = g * inversesqrt(dot(dG, dG)); + } + return clamp(dist, -aa_range, aa_range); } -float distance_to_rounded_rect( +float clip_against_ellipse_if_needed( vec2 pos, - vec4 center_radius_tl, - vec4 center_radius_tr, - vec4 center_radius_br, - vec4 center_radius_bl, - vec4 rect_bounds + float current_distance, + vec4 ellipse_center_radius, + vec2 sign_modifier, + float aa_range ) { - // Clip against each ellipse. If the fragment is in a corner, one of the - // branches below will select it as the corner to calculate the distance - // to. We want to choose the smallest distance inside either of the axis - // bounds as the overall distance we use to compare which corner is closer - // than another. If outside any ellipse, default to a small offset so a - // negative distance is returned for it. - vec4 corner = vec4(vec2(1.0e-6), vec2(1.0)); - center_radius_tl.xy = center_radius_tl.xy - pos; - center_radius_tr.xy = (center_radius_tr.xy - pos) * vec2(-1.0, 1.0); - center_radius_br.xy = pos - center_radius_br.xy; - center_radius_bl.xy = (center_radius_bl.xy - pos) * vec2(1.0, -1.0); - if (min(center_radius_tl.x, center_radius_tl.y) > min(corner.x, corner.y)) { - corner = center_radius_tl; - } - if (min(center_radius_tr.x, center_radius_tr.y) > min(corner.x, corner.y)) { - corner = center_radius_tr; - } - if (min(center_radius_br.x, center_radius_br.y) > min(corner.x, corner.y)) { - corner = center_radius_br; - } - if (min(center_radius_bl.x, center_radius_bl.y) > min(corner.x, corner.y)) { - corner = center_radius_bl; + if (!all(lessThan(sign_modifier * pos, sign_modifier * ellipse_center_radius.xy))) { + return current_distance; } - // Calculate the distance of the selected corner and the rectangle bounds, - // whichever is greater. - return max(distance_to_ellipse_approx(corner.xy, corner.zw, 1.0), - signed_distance_rect(pos, rect_bounds.xy, rect_bounds.zw)); + float distance = distance_to_ellipse(pos - ellipse_center_radius.xy, + ellipse_center_radius.zw, + aa_range); + + return max(distance, current_distance); +} + +float rounded_rect(vec2 pos, + vec4 clip_center_radius_tl, + vec4 clip_center_radius_tr, + vec4 clip_center_radius_br, + vec4 clip_center_radius_bl, + float aa_range) { + // Start with a negative value (means "inside") for all fragments that are not + // in a corner. If the fragment is in a corner, one of the clip_against_ellipse_if_needed + // calls below will update it. + float current_distance = -aa_range; + + // Clip against each ellipse. + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + clip_center_radius_tl, + vec2(1.0), + aa_range); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + clip_center_radius_tr, + vec2(-1.0, 1.0), + aa_range); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + clip_center_radius_br, + vec2(-1.0), + aa_range); + + current_distance = clip_against_ellipse_if_needed(pos, + current_distance, + clip_center_radius_bl, + vec2(1.0, -1.0), + aa_range); + + // Apply AA + // See comment in ps_border_corner about the choice of constants. + + return distance_aa(aa_range, current_distance); } #endif diff --git a/third_party/webrender/webrender/res/gpu_cache.glsl b/third_party/webrender/webrender/res/gpu_cache.glsl index cd5e41fec46..e3ff0f43976 100644 --- a/third_party/webrender/webrender/res/gpu_cache.glsl +++ b/third_party/webrender/webrender/res/gpu_cache.glsl @@ -96,37 +96,38 @@ vec4[4] fetch_from_gpu_cache_4(int address) { //TODO: image resource is too specific for this module -struct ImageSource { +struct ImageResource { RectWithEndpoint uv_rect; - vec4 user_data; + float layer; + vec3 user_data; }; -ImageSource fetch_image_source(int address) { +ImageResource fetch_image_resource(int address) { //Note: number of blocks has to match `renderer::BLOCKS_PER_UV_RECT` vec4 data[2] = fetch_from_gpu_cache_2(address); RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw); - return ImageSource(uv_rect, data[1]); + return ImageResource(uv_rect, data[1].x, data[1].yzw); } -ImageSource fetch_image_source_direct(ivec2 address) { +ImageResource fetch_image_resource_direct(ivec2 address) { vec4 data[2] = fetch_from_gpu_cache_2_direct(address); RectWithEndpoint uv_rect = RectWithEndpoint(data[0].xy, data[0].zw); - return ImageSource(uv_rect, data[1]); + return ImageResource(uv_rect, data[1].x, data[1].yzw); } // Fetch optional extra data for a texture cache resource. This can contain // a polygon defining a UV rect within the texture cache resource. // Note: the polygon coordinates are in homogeneous space. -struct ImageSourceExtra { +struct ImageResourceExtra { vec4 st_tl; vec4 st_tr; vec4 st_bl; vec4 st_br; }; -ImageSourceExtra fetch_image_source_extra(int address) { +ImageResourceExtra fetch_image_resource_extra(int address) { vec4 data[4] = fetch_from_gpu_cache_4(address + VECS_PER_IMAGE_RESOURCE); - return ImageSourceExtra( + return ImageResourceExtra( data[0], data[1], data[2], diff --git a/third_party/webrender/webrender/res/gradient.glsl b/third_party/webrender/webrender/res/gradient.glsl deleted file mode 100644 index 6b374acd530..00000000000 --- a/third_party/webrender/webrender/res/gradient.glsl +++ /dev/null @@ -1,68 +0,0 @@ -/* 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/. */ - -flat varying HIGHP_FS_ADDRESS int v_gradient_address; - -#if defined(PLATFORM_ANDROID) && !defined(SWGL) -// Work around Adreno 3xx driver bug. See the v_perspective comment in -// brush_image or bugs 1630356 and for details. -flat varying vec2 v_gradient_repeat_vec; -#define v_gradient_repeat v_gradient_repeat_vec.x -#else -// Repetition along the gradient stops. -flat varying float v_gradient_repeat; -#endif - -#ifdef WR_FRAGMENT_SHADER - -#ifdef WR_FEATURE_DITHERING -vec4 dither(vec4 color) { - const int matrix_mask = 7; - - ivec2 pos = ivec2(gl_FragCoord.xy) & ivec2(matrix_mask); - float noise_normalized = (texelFetch(sDither, pos, 0).r * 255.0 + 0.5) / 64.0; - float noise = (noise_normalized - 0.5) / 256.0; // scale down to the unit length - - return color + vec4(noise, noise, noise, 0); -} -#else -vec4 dither(vec4 color) { - return color; -} -#endif //WR_FEATURE_DITHERING - -#define GRADIENT_ENTRIES 128.0 - -float clamp_gradient_entry(float offset) { - // Calculate the color entry index to use for this offset: - // offsets < 0 use the first color entry, 0 - // offsets from [0, 1) use the color entries in the range of [1, N-1) - // offsets >= 1 use the last color entry, N-1 - // so transform the range [0, 1) -> [1, N-1) - - // TODO(gw): In the future we might consider making the size of the - // LUT vary based on number / distribution of stops in the gradient. - // Ensure we don't fetch outside the valid range of the LUT. - return clamp(1.0 + offset * GRADIENT_ENTRIES, 0.0, 1.0 + GRADIENT_ENTRIES); -} - -vec4 sample_gradient(float offset) { - // Modulo the offset if the gradient repeats. - offset -= floor(offset) * v_gradient_repeat; - - // Calculate the texel to index into the gradient color entries: - // floor(x) is the gradient color entry index - // fract(x) is the linear filtering factor between start and end - float x = clamp_gradient_entry(offset); - float entry_index = floor(x); - float entry_fract = x - entry_index; - - // Fetch the start and end color. There is a [start, end] color per entry. - vec4 texels[2] = fetch_from_gpu_cache_2(v_gradient_address + 2 * int(entry_index)); - - // Finally interpolate and apply dithering - return dither(texels[0] + texels[1] * entry_fract); -} - -#endif //WR_FRAGMENT_SHADER diff --git a/third_party/webrender/webrender/res/gradient_shared.glsl b/third_party/webrender/webrender/res/gradient_shared.glsl deleted file mode 100644 index a9310be8283..00000000000 --- a/third_party/webrender/webrender/res/gradient_shared.glsl +++ /dev/null @@ -1,78 +0,0 @@ -/* 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 gradient - -// Size of the gradient pattern's rectangle, used to compute horizontal and vertical -// repetitions. Not to be confused with another kind of repetition of the pattern -// which happens along the gradient stops. -flat varying vec2 v_repeated_size; - -varying vec2 v_pos; - -#ifdef WR_FEATURE_ALPHA_PASS -flat varying vec2 v_tile_repeat; -#endif - -#ifdef WR_VERTEX_SHADER -void write_gradient_vertex( - VertexInfo vi, - RectWithSize local_rect, - RectWithSize segment_rect, - ivec4 prim_user_data, - int brush_flags, - vec4 texel_rect, - int extend_mode, - vec2 stretch_size -) { - if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) { - v_pos = (vi.local_pos - segment_rect.p0) / segment_rect.size; - v_pos = v_pos * (texel_rect.zw - texel_rect.xy) + texel_rect.xy; - v_pos = v_pos * local_rect.size; - } else { - v_pos = vi.local_pos - local_rect.p0; - } - - vec2 tile_repeat = local_rect.size / stretch_size; - v_repeated_size = stretch_size; - - // Normalize UV to 0..1 scale. - v_pos /= v_repeated_size; - - v_gradient_address = prim_user_data.x; - - // Whether to repeat the gradient along the line instead of clamping. - v_gradient_repeat = float(extend_mode == EXTEND_MODE_REPEAT); - -#ifdef WR_FEATURE_ALPHA_PASS - v_tile_repeat = tile_repeat; -#endif -} -#endif //WR_VERTEX_SHADER - -#ifdef WR_FRAGMENT_SHADER -vec2 compute_repeated_pos() { -#if defined(WR_FEATURE_ALPHA_PASS) && !defined(SWGL_ANTIALIAS) - // Handle top and left inflated edges (see brush_image). - vec2 local_pos = max(v_pos, vec2(0.0)); - - // Apply potential horizontal and vertical repetitions. - vec2 pos = fract(local_pos); - - // Handle bottom and right inflated edges (see brush_image). - if (local_pos.x >= v_tile_repeat.x) { - pos.x = 1.0; - } - if (local_pos.y >= v_tile_repeat.y) { - pos.y = 1.0; - } - return pos; -#else - // Apply potential horizontal and vertical repetitions. - return fract(v_pos); -#endif -} - -#endif //WR_FRAGMENT_SHADER - diff --git a/third_party/webrender/webrender/res/pf_vector_cover.glsl b/third_party/webrender/webrender/res/pf_vector_cover.glsl new file mode 100644 index 00000000000..1b2eeabb7dc --- /dev/null +++ b/third_party/webrender/webrender/res/pf_vector_cover.glsl @@ -0,0 +1,77 @@ +/* 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 shared + +#ifdef WR_VERTEX_SHADER + +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; + +void main(void) { + vec4 targetRect = vec4(aTargetRect); + vec2 stencilOrigin = vec2(aStencilOrigin); + + vec2 targetOffset = mix(vec2(0.0), targetRect.zw, aPosition.xy); + vec2 targetPosition = targetRect.xy + targetOffset; + vec2 stencilOffset = targetOffset * vec2(aSubpixel == 0 ? 1.0 : 3.0, 1.0); + vec2 stencilPosition = stencilOrigin + stencilOffset; + + gl_Position = uTransform * vec4(targetPosition, aPosition.z, 1.0); + vStencilUV = stencilPosition; + vSubpixel = aSubpixel; +} + +#endif + +#ifdef WR_FRAGMENT_SHADER + +#define LCD_FILTER_FACTOR_0 (86.0 / 255.0) +#define LCD_FILTER_FACTOR_1 (77.0 / 255.0) +#define LCD_FILTER_FACTOR_2 (8.0 / 255.0) + +in vec2 vStencilUV; +flat in int vSubpixel; + +/// Applies a slight horizontal blur to reduce color fringing on LCD screens +/// when performing subpixel AA. +/// +/// The algorithm should be identical to that of FreeType: +/// https://www.freetype.org/freetype2/docs/reference/ft2-lcd_filtering.html +float lcdFilter(float shadeL2, float shadeL1, float shade0, float shadeR1, float shadeR2) { + return LCD_FILTER_FACTOR_2 * shadeL2 + + LCD_FILTER_FACTOR_1 * shadeL1 + + LCD_FILTER_FACTOR_0 * shade0 + + LCD_FILTER_FACTOR_1 * shadeR1 + + LCD_FILTER_FACTOR_2 * shadeR2; +} + +void main(void) { + ivec2 stencilUV = ivec2(vStencilUV); + float shade0 = abs(TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(0, 0)).r); + + if (vSubpixel == 0) { + oFragColor = vec4(shade0); + return; + } + + vec3 shadeL = abs(vec3(TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(-1, 0)).r, + TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(-2, 0)).r, + TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(-3, 0)).r)); + vec3 shadeR = abs(vec3(TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(1, 0)).r, + TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(2, 0)).r, + TEXEL_FETCH(sColor0, stencilUV, 0, ivec2(3, 0)).r)); + + oFragColor = vec4(lcdFilter(shadeL.z, shadeL.y, shadeL.x, shade0, shadeR.x), + lcdFilter(shadeL.y, shadeL.x, shade0, shadeR.x, shadeR.y), + lcdFilter(shadeL.x, shade0, shadeR.x, shadeR.y, shadeR.z), + 1.0); +} + +#endif diff --git a/third_party/webrender/webrender/res/pf_vector_stencil.glsl b/third_party/webrender/webrender/res/pf_vector_stencil.glsl new file mode 100644 index 00000000000..2029768fcb9 --- /dev/null +++ b/third_party/webrender/webrender/res/pf_vector_stencil.glsl @@ -0,0 +1,111 @@ +/* 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 shared + +#ifdef WR_VERTEX_SHADER + +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; +out vec2 vTo; + +void main(void) { + // Unpack. + int pathID = int(aPathID); + + ivec2 pathAddress = ivec2(0.0, aPathID); + mat2 transformLinear = mat2(TEXEL_FETCH(sColor1, pathAddress, 0, ivec2(0, 0))); + vec2 transformTranslation = TEXEL_FETCH(sColor1, pathAddress, 0, ivec2(1, 0)).xy; + + vec4 miscInfo = TEXEL_FETCH(sColor1, pathAddress, 0, ivec2(2, 0)); + float rectHeight = miscInfo.y; + vec2 emboldenAmount = miscInfo.zw * 0.5; + + // TODO(pcwalton): Hint positions. + vec2 from = aFromPosition; + vec2 ctrl = aCtrlPosition; + vec2 to = aToPosition; + + // Embolden as necessary. + from -= aFromNormal * emboldenAmount; + ctrl -= aCtrlNormal * emboldenAmount; + to -= aToNormal * emboldenAmount; + + // Perform the transform. + from = transformLinear * from + transformTranslation; + ctrl = transformLinear * ctrl + transformTranslation; + to = transformLinear * to + transformTranslation; + + // Choose correct quadrant for rotation. + vec2 corner = vec2(0.0, rectHeight) + transformTranslation; + + // Compute edge vectors. De Casteljau subdivide if necessary. + // TODO(pcwalton): Actually do the two-pass rendering. + + // Compute position and dilate. If too thin, discard to avoid artefacts. + vec2 position; + if (abs(from.x - to.x) < 0.0001) + position.x = 0.0; + else if (aPosition.x < 0.5) + position.x = floor(min(min(from.x, to.x), ctrl.x)); + else + position.x = ceil(max(max(from.x, to.x), ctrl.x)); + if (aPosition.y < 0.5) + position.y = floor(min(min(from.y, to.y), ctrl.y)); + else + position.y = corner.y; + + // Compute final position and depth. + vec4 clipPosition = uTransform * vec4(position, aPosition.z, 1.0); + + // Finish up. + gl_Position = clipPosition; + vFrom = from - position; + vCtrl = ctrl - position; + vTo = to - position; +} + +#endif + +#ifdef WR_FRAGMENT_SHADER + +uniform sampler2D uAreaLUT; + +in vec2 vFrom; +in vec2 vCtrl; +in vec2 vTo; + +void main(void) { + // Unpack. + vec2 from = vFrom, ctrl = vCtrl, to = vTo; + + // Determine winding, and sort into a consistent order so we only need to find one root below. + bool winding = from.x < to.x; + vec2 left = winding ? from : to, right = winding ? to : from; + vec2 v0 = ctrl - left, v1 = right - ctrl; + + // Shoot a vertical ray toward the curve. + vec2 window = clamp(vec2(from.x, to.x), -0.5, 0.5); + float offset = mix(window.x, window.y, 0.5) - left.x; + float t = offset / (v0.x + sqrt(v1.x * offset - v0.x * (offset - v0.x))); + + // Compute position and derivative to form a line approximation. + float y = mix(mix(left.y, ctrl.y, t), mix(ctrl.y, right.y, t), t); + float d = mix(v0.y, v1.y, t) / mix(v0.x, v1.x, t); + + // Look up area under that line, and scale horizontally to the window size. + float dX = window.x - window.y; + oFragColor = vec4(texture(sColor0, vec2(y + 8.0, abs(d * dX)) / 16.0).r * dX); +} + +#endif diff --git a/third_party/webrender/webrender/res/pls_init.glsl b/third_party/webrender/webrender/res/pls_init.glsl new file mode 100644 index 00000000000..a9fe0a6c4a6 --- /dev/null +++ b/third_party/webrender/webrender/res/pls_init.glsl @@ -0,0 +1,27 @@ +/* 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/. */ + +// Initialize the pixel local storage area by reading the current +// framebuffer color. We might be able to skip this in future by +// making the opaque pass also write to pixel local storage. + +#define PLS_WRITEONLY + +#include shared + +#ifdef WR_VERTEX_SHADER +PER_INSTANCE in vec4 aRect; + +void main(void) { + vec2 pos = aRect.xy + aPosition.xy * aRect.zw; + gl_Position = uTransform * vec4(pos, 0.0, 1.0); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +void main(void) { + // Store current framebuffer color in our custom PLS struct. + PLS.color = gl_LastFragColorARM; +} +#endif diff --git a/third_party/webrender/webrender/res/pls_resolve.glsl b/third_party/webrender/webrender/res/pls_resolve.glsl new file mode 100644 index 00000000000..363ce2ccfef --- /dev/null +++ b/third_party/webrender/webrender/res/pls_resolve.glsl @@ -0,0 +1,29 @@ +/* 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/. */ + +// Write the final value stored in pixel local store out to normal +// fragment outputs. This will be the color that gets resolved out +// to main memory. + +#define PLS_READONLY + +#include shared + +#ifdef WR_VERTEX_SHADER +PER_INSTANCE in vec4 aRect; + +void main(void) { + vec2 pos = aRect.xy + aPosition.xy * aRect.zw; + gl_Position = uTransform * vec4(pos, 0.0, 1.0); +} +#endif + +#ifdef WR_FRAGMENT_SHADER +out vec4 oFragColor; + +void main(void) { + // Write the final color value in pixel local storage out as a fragment color. + oFragColor = PLS.color; +} +#endif diff --git a/third_party/webrender/webrender/res/prim_shared.glsl b/third_party/webrender/webrender/res/prim_shared.glsl index 467977fb62c..f89340ff49c 100644 --- a/third_party/webrender/webrender/res/prim_shared.glsl +++ b/third_party/webrender/webrender/res/prim_shared.glsl @@ -15,27 +15,31 @@ #define RASTER_LOCAL 0 #define RASTER_SCREEN 1 -uniform sampler2D sClipMask; +uniform sampler2DArray sPrevPassAlpha; +uniform sampler2DArray sPrevPassColor; + +vec2 clamp_rect(vec2 pt, RectWithSize rect) { + return clamp(pt, rect.p0, rect.p0 + rect.size); +} -#ifndef SWGL_CLIP_MASK // TODO: convert back to RectWithEndPoint if driver issues are resolved, if ever. flat varying vec4 vClipMaskUvBounds; -varying vec2 vClipMaskUv; -#endif +// XY and W are homogeneous coordinates, Z is the layer index +varying vec4 vClipMaskUv; + #ifdef WR_VERTEX_SHADER -#define COLOR_MODE_FROM_PASS 0 -#define COLOR_MODE_ALPHA 1 -#define COLOR_MODE_SUBPX_CONST_COLOR 2 -#define COLOR_MODE_SUBPX_BG_PASS0 3 -#define COLOR_MODE_SUBPX_BG_PASS1 4 -#define COLOR_MODE_SUBPX_BG_PASS2 5 -#define COLOR_MODE_SUBPX_DUAL_SOURCE 6 -#define COLOR_MODE_BITMAP_SHADOW 7 -#define COLOR_MODE_COLOR_BITMAP 8 -#define COLOR_MODE_IMAGE 9 -#define COLOR_MODE_MULTIPLY_DUAL_SOURCE 10 +#define COLOR_MODE_FROM_PASS 0 +#define COLOR_MODE_ALPHA 1 +#define COLOR_MODE_SUBPX_CONST_COLOR 2 +#define COLOR_MODE_SUBPX_BG_PASS0 3 +#define COLOR_MODE_SUBPX_BG_PASS1 4 +#define COLOR_MODE_SUBPX_BG_PASS2 5 +#define COLOR_MODE_SUBPX_DUAL_SOURCE 6 +#define COLOR_MODE_BITMAP 7 +#define COLOR_MODE_COLOR_BITMAP 8 +#define COLOR_MODE_IMAGE 9 uniform HIGHP_SAMPLER_FLOAT sampler2D sPrimitiveHeadersF; uniform HIGHP_SAMPLER_FLOAT isampler2D sPrimitiveHeadersI; @@ -120,7 +124,7 @@ VertexInfo write_vertex(vec2 local_pos, vec2 device_pos = world_pos.xy * task.device_pixel_scale; // Apply offsets for the render task to get correct screen location. - vec2 final_offset = -task.content_origin + task.task_rect.p0; + vec2 final_offset = -task.content_origin + task.common_data.task_rect.p0; gl_Position = uTransform * vec4(device_pos + final_offset * world_pos.w, z * world_pos.w, world_pos.w); @@ -154,42 +158,21 @@ vec2 intersect_lines(vec2 p0, vec2 p1, vec2 p2, vec2 p3) { VertexInfo write_transform_vertex(RectWithSize local_segment_rect, RectWithSize local_prim_rect, RectWithSize local_clip_rect, - int edge_flags, + vec4 clip_edge_mask, float z, Transform transform, PictureTask task) { // Calculate a clip rect from local_rect + local clip RectWithEndpoint clip_rect = to_rect_with_endpoint(local_clip_rect); RectWithEndpoint segment_rect = to_rect_with_endpoint(local_segment_rect); - -#ifdef SWGL_ANTIALIAS - // Check if the bounds are smaller than the unmodified segment rect. If so, - // it is safe to enable AA on those edges. - bvec4 clipped = bvec4(greaterThan(clip_rect.p0, segment_rect.p0), - lessThan(clip_rect.p1, segment_rect.p1)); - swgl_antiAlias(edge_flags | (clipped.x ? 1 : 0) | (clipped.y ? 2 : 0) | - (clipped.z ? 4 : 0) | (clipped.w ? 8 : 0)); -#endif - segment_rect.p0 = clamp(segment_rect.p0, clip_rect.p0, clip_rect.p1); segment_rect.p1 = clamp(segment_rect.p1, clip_rect.p0, clip_rect.p1); -#ifdef SWGL_ANTIALIAS - // Trim the segment geometry to the clipped bounds. - local_segment_rect = to_rect_with_size(segment_rect); -#else + // Calculate a clip rect from local_rect + local clip RectWithEndpoint prim_rect = to_rect_with_endpoint(local_prim_rect); prim_rect.p0 = clamp(prim_rect.p0, clip_rect.p0, clip_rect.p1); prim_rect.p1 = clamp(prim_rect.p1, clip_rect.p0, clip_rect.p1); - // Select between the segment and prim edges based on edge mask - bvec4 clip_edge_mask = notEqual(edge_flags & ivec4(1, 2, 4, 8), ivec4(0)); - init_transform_vs(mix( - vec4(prim_rect.p0, prim_rect.p1), - vec4(segment_rect.p0, segment_rect.p1), - clip_edge_mask - )); - // As this is a transform shader, extrude by 2 (local space) pixels // in each direction. This gives enough space around the edge to // apply distance anti-aliasing. Technically, it: @@ -201,18 +184,17 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, // Only extrude along edges where we are going to apply AA. float extrude_amount = 2.0; - vec4 extrude_distance = mix(vec4(0.0), vec4(extrude_amount), clip_edge_mask); + vec4 extrude_distance = vec4(extrude_amount) * clip_edge_mask; local_segment_rect.p0 -= extrude_distance.xy; local_segment_rect.size += extrude_distance.xy + extrude_distance.zw; -#endif // Select the corner of the local rect that we are processing. vec2 local_pos = local_segment_rect.p0 + local_segment_rect.size * aPosition.xy; // Convert the world positions to device pixel space. - vec2 task_offset = task.task_rect.p0 - task.content_origin; + vec2 task_offset = task.common_data.task_rect.p0 - task.content_origin; - // Transform the current vertex to world space. + // Transform the current vertex to the world cpace. vec4 world_pos = transform.m * vec4(local_pos, 0.0, 1.0); vec4 final_pos = vec4( world_pos.xy * task.device_pixel_scale + task_offset * world_pos.w, @@ -222,6 +204,12 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, gl_Position = uTransform * final_pos; + init_transform_vs(mix( + vec4(prim_rect.p0, prim_rect.p1), + vec4(segment_rect.p0, segment_rect.p1), + clip_edge_mask + )); + VertexInfo vi = VertexInfo( local_pos, world_pos @@ -230,29 +218,20 @@ VertexInfo write_transform_vertex(RectWithSize local_segment_rect, return vi; } -void write_clip(vec4 world_pos, ClipArea area, PictureTask task) { -#ifdef SWGL_CLIP_MASK - swgl_clipMask( - sClipMask, - (task.task_rect.p0 - task.content_origin) - (area.task_rect.p0 - area.screen_origin), - area.task_rect.p0, - area.task_rect.size - ); -#else +void write_clip(vec4 world_pos, ClipArea area) { vec2 uv = world_pos.xy * area.device_pixel_scale + - world_pos.w * (area.task_rect.p0 - area.screen_origin); + world_pos.w * (area.common_data.task_rect.p0 - area.screen_origin); vClipMaskUvBounds = vec4( - area.task_rect.p0, - area.task_rect.p0 + area.task_rect.size + area.common_data.task_rect.p0, + area.common_data.task_rect.p0 + area.common_data.task_rect.size ); - vClipMaskUv = uv; -#endif + vClipMaskUv = vec4(uv, area.common_data.texture_layer_index, world_pos.w); } // Read the exta image data containing the homogeneous screen space coordinates // of the corners, interpolate between them, and return real screen space UV. vec2 get_image_quad_uv(int address, vec2 f) { - ImageSourceExtra extra_data = fetch_image_source_extra(address); + ImageResourceExtra extra_data = fetch_image_resource_extra(address); vec4 x = mix(extra_data.st_tl, extra_data.st_tr, f.x); vec4 y = mix(extra_data.st_bl, extra_data.st_br, f.x); vec4 z = mix(x, y, f.y); @@ -269,19 +248,15 @@ struct Fragment { #endif }; + float do_clip() { -#ifdef SWGL_CLIP_MASK - // SWGL relies on builtin clip-mask support to do this more efficiently, - // so no clipping is required here. - return 1.0; -#else // check for the dummy bounds, which are given to the opaque objects if (vClipMaskUvBounds.xy == vClipMaskUvBounds.zw) { return 1.0; } // anything outside of the mask is considered transparent //Note: we assume gl_FragCoord.w == interpolated(1 / vClipMaskUv.w) - vec2 mask_uv = vClipMaskUv * gl_FragCoord.w; + vec2 mask_uv = vClipMaskUv.xy * gl_FragCoord.w; bvec2 left = lessThanEqual(vClipMaskUvBounds.xy, mask_uv); // inclusive bvec2 right = greaterThan(vClipMaskUvBounds.zw, mask_uv); // non-inclusive // bail out if the pixel is outside the valid bounds @@ -289,8 +264,57 @@ float do_clip() { return 0.0; } // finally, the slow path - fetch the mask value from an image - return texelFetch(sClipMask, ivec2(mask_uv), 0).r; -#endif + // Note the Z getting rounded to the nearest integer because the variable + // is still interpolated and becomes a subject of precision-caused + // fluctuations, see https://bugzilla.mozilla.org/show_bug.cgi?id=1491911 + ivec3 tc = ivec3(mask_uv, vClipMaskUv.z + 0.5); + return texelFetch(sPrevPassAlpha, tc, 0).r; +} + +#ifdef WR_FEATURE_DITHERING +vec4 dither(vec4 color) { + const int matrix_mask = 7; + + ivec2 pos = ivec2(gl_FragCoord.xy) & ivec2(matrix_mask); + float noise_normalized = (texelFetch(sDither, pos, 0).r * 255.0 + 0.5) / 64.0; + float noise = (noise_normalized - 0.5) / 256.0; // scale down to the unit length + + return color + vec4(noise, noise, noise, 0); +} +#else +vec4 dither(vec4 color) { + return color; +} +#endif //WR_FEATURE_DITHERING + +vec4 sample_gradient(HIGHP_FS_ADDRESS int address, float offset, float gradient_repeat) { + // Modulo the offset if the gradient repeats. + float x = mix(offset, fract(offset), gradient_repeat); + + // Calculate the color entry index to use for this offset: + // offsets < 0 use the first color entry, 0 + // offsets from [0, 1) use the color entries in the range of [1, N-1) + // offsets >= 1 use the last color entry, N-1 + // so transform the range [0, 1) -> [1, N-1) + + // TODO(gw): In the future we might consider making the size of the + // LUT vary based on number / distribution of stops in the gradient. + const int GRADIENT_ENTRIES = 128; + x = 1.0 + x * float(GRADIENT_ENTRIES); + + // Calculate the texel to index into the gradient color entries: + // floor(x) is the gradient color entry index + // fract(x) is the linear filtering factor between start and end + int lut_offset = 2 * int(floor(x)); // There is a [start, end] color per entry. + + // Ensure we don't fetch outside the valid range of the LUT. + lut_offset = clamp(lut_offset, 0, 2 * (GRADIENT_ENTRIES + 1)); + + // Fetch the start and end color. + vec4 texels[2] = fetch_from_gpu_cache_2(address + lut_offset); + + // Finally interpolate and apply dithering + return dither(mix(texels[0], texels[1], fract(x))); } #endif //WR_FRAGMENT_SHADER diff --git a/third_party/webrender/webrender/res/ps_split_composite.glsl b/third_party/webrender/webrender/res/ps_split_composite.glsl index d50d450ac78..2de94126848 100644 --- a/third_party/webrender/webrender/res/ps_split_composite.glsl +++ b/third_party/webrender/webrender/res/ps_split_composite.glsl @@ -2,23 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#define WR_FEATURE_TEXTURE_2D - #include shared,prim_shared // interpolated UV coordinates to sample. varying vec2 vUv; - -#if defined(PLATFORM_ANDROID) && !defined(SWGL) -// Work around Adreno 3xx driver bug. See the v_perspective comment in -// brush_image or bug 1630356 for details. -flat varying vec2 vPerspectiveVec; -#define vPerspective vPerspectiveVec.x -#else -// Flag to allow perspective interpolation of UV. -flat varying float vPerspective; -#endif - +// X = layer index to sample, Y = flag to allow perspective interpolation of UV. +flat varying vec2 vLayerAndPerspective; flat varying vec4 vUvSampleBounds; #ifdef WR_VERTEX_SHADER @@ -73,10 +62,10 @@ void main(void) { PrimitiveHeader ph = fetch_prim_header(ci.prim_header_index); PictureTask dest_task = fetch_picture_task(ci.render_task_index); Transform transform = fetch_transform(ph.transform_id); - ImageSource res = fetch_image_source(ph.user_data.x); + ImageResource res = fetch_image_resource(ph.user_data.x); ClipArea clip_area = fetch_clip_area(ph.user_data.w); - vec2 dest_origin = dest_task.task_rect.p0 - + vec2 dest_origin = dest_task.common_data.task_rect.p0 - dest_task.content_origin; vec2 local_pos = bilerp(geometry.local[0], geometry.local[1], @@ -92,13 +81,12 @@ void main(void) { write_clip( world_pos, - clip_area, - dest_task + clip_area ); gl_Position = uTransform * final_pos; - vec2 texture_size = vec2(TEX_SIZE(sColor0)); + vec2 texture_size = vec2(textureSize(sPrevPassColor, 0)); vec2 uv0 = res.uv_rect.p0; vec2 uv1 = res.uv_rect.p1; @@ -116,25 +104,15 @@ void main(void) { float perspective_interpolate = float(ph.user_data.y); vUv = uv / texture_size * mix(gl_Position.w, 1.0, perspective_interpolate); - vPerspective = perspective_interpolate; + vLayerAndPerspective = vec2(res.layer, perspective_interpolate); } #endif #ifdef WR_FRAGMENT_SHADER void main(void) { float alpha = do_clip(); - float perspective_divisor = mix(gl_FragCoord.w, 1.0, vPerspective); + float perspective_divisor = mix(gl_FragCoord.w, 1.0, vLayerAndPerspective.y); vec2 uv = clamp(vUv * perspective_divisor, vUvSampleBounds.xy, vUvSampleBounds.zw); - write_output(alpha * texture(sColor0, uv)); + write_output(alpha * textureLod(sPrevPassColor, vec3(uv, vLayerAndPerspective.x), 0.0)); } - -#ifdef SWGL_DRAW_SPAN -void swgl_drawSpanRGBA8() { - float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, vPerspective); - vec2 uv = vUv * perspective_divisor; - - swgl_commitTextureRGBA8(sColor0, uv, vUvSampleBounds); -} -#endif - #endif diff --git a/third_party/webrender/webrender/res/ps_text_run.glsl b/third_party/webrender/webrender/res/ps_text_run.glsl index 41eb668a3f1..c2f626dded6 100644 --- a/third_party/webrender/webrender/res/ps_text_run.glsl +++ b/third_party/webrender/webrender/res/ps_text_run.glsl @@ -2,19 +2,41 @@ * 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 -flat varying vec4 v_color; -flat varying vec3 v_mask_swizzle; +#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 // Normalized bounds of the source image in the texture. -flat varying vec4 v_uv_bounds; +#define V_UV_BOUNDS flat_varying_vec4_2 // Interpolated UV coordinates to sample. -varying vec2 v_uv; +#define V_UV varying_vec4_0.xy +#define V_LAYER varying_vec4_0.z -#if defined(WR_FEATURE_GLYPH_TRANSFORM) && !defined(SWGL_CLIP_DIST) -varying vec4 v_uv_clip; +#ifdef WR_FEATURE_GLYPH_TRANSFORM +#define V_UV_CLIP varying_vec4_1 #endif #ifdef WR_VERTEX_SHADER @@ -47,21 +69,24 @@ Glyph fetch_glyph(int specific_prim_address, int(uint(glyph_index) / GLYPHS_PER_GPU_BLOCK); vec4 data = fetch_from_gpu_cache_1(glyph_address); // Select XY or ZW based on glyph index. + // We use "!= 0" instead of "== 1" here in order to work around a driver + // bug with equality comparisons on integers. vec2 glyph = mix(data.xy, data.zw, - bvec2(uint(glyph_index) % GLYPHS_PER_GPU_BLOCK == 1U)); + bvec2(uint(glyph_index) % GLYPHS_PER_GPU_BLOCK != 0U)); return Glyph(glyph); } struct GlyphResource { vec4 uv_rect; + float layer; vec2 offset; float scale; }; GlyphResource fetch_glyph_resource(int address) { vec4 data[2] = fetch_from_gpu_cache_2(address); - return GlyphResource(data[0], data[1].xy, data[1].z); + return GlyphResource(data[0], data[1].x, data[1].yz, data[1].w); } struct TextRun { @@ -96,13 +121,13 @@ vec2 get_snap_bias(int subpx_dir) { } } -void main() { - Instance instance = decode_instance_attributes(); - 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); - +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; @@ -214,136 +239,97 @@ void main() { #ifdef WR_FEATURE_GLYPH_TRANSFORM vec2 f = (glyph_transform * vi.local_pos - glyph_rect.p0) / glyph_rect.size; - #ifdef SWGL_CLIP_DIST - gl_ClipDistance[0] = f.x; - gl_ClipDistance[1] = f.y; - gl_ClipDistance[2] = 1.0 - f.x; - gl_ClipDistance[3] = 1.0 - f.y; - #else - v_uv_clip = vec4(f, 1.0 - f); - #endif + V_UV_CLIP = vec4(f, 1.0 - f); #else vec2 f = (vi.local_pos - glyph_rect.p0) / glyph_rect.size; #endif - write_clip(vi.world_pos, clip_area, task); + write_clip(vi.world_pos, clip_area); switch (color_mode) { case COLOR_MODE_ALPHA: - v_mask_swizzle = vec3(0.0, 1.0, 1.0); - v_color = text.color; - break; - case COLOR_MODE_BITMAP_SHADOW: - #ifdef SWGL_BLEND - swgl_blendDropShadow(text.color); - v_mask_swizzle = vec3(1.0, 0.0, 0.0); - v_color = vec4(1.0); - #else - v_mask_swizzle = vec3(0.0, 1.0, 0.0); - v_color = text.color; - #endif + case COLOR_MODE_BITMAP: + V_MASK_SWIZZLE = vec2(0.0, 1.0); + V_COLOR = text.color; break; case COLOR_MODE_SUBPX_BG_PASS2: - v_mask_swizzle = vec3(1.0, 0.0, 0.0); - v_color = text.color; + case COLOR_MODE_SUBPX_DUAL_SOURCE: + V_MASK_SWIZZLE = vec2(1.0, 0.0); + V_COLOR = text.color; break; case COLOR_MODE_SUBPX_CONST_COLOR: case COLOR_MODE_SUBPX_BG_PASS0: case COLOR_MODE_COLOR_BITMAP: - v_mask_swizzle = vec3(1.0, 0.0, 0.0); - v_color = vec4(text.color.a); + V_MASK_SWIZZLE = vec2(1.0, 0.0); + V_COLOR = vec4(text.color.a); break; case COLOR_MODE_SUBPX_BG_PASS1: - v_mask_swizzle = vec3(-1.0, 1.0, 0.0); - v_color = vec4(text.color.a) * text.bg_color; - break; - case COLOR_MODE_SUBPX_DUAL_SOURCE: - #ifdef SWGL_BLEND - swgl_blendSubpixelText(text.color); - v_mask_swizzle = vec3(1.0, 0.0, 0.0); - v_color = vec4(1.0); - #else - v_mask_swizzle = vec3(text.color.a, 0.0, 0.0); - v_color = text.color; - #endif + V_MASK_SWIZZLE = vec2(-1.0, 1.0); + V_COLOR = vec4(text.color.a) * text.bg_color; break; default: - v_mask_swizzle = vec3(0.0, 0.0, 0.0); - v_color = vec4(1.0); + V_MASK_SWIZZLE = vec2(0.0); + V_COLOR = vec4(1.0); } - vec2 texture_size = vec2(TEX_SIZE(sColor0)); + vec2 texture_size = vec2(textureSize(sColor0, 0)); vec2 st0 = res.uv_rect.xy / texture_size; vec2 st1 = res.uv_rect.zw / texture_size; - v_uv = mix(st0, st1, f); - v_uv_bounds = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; + V_UV = mix(st0, st1, f); + V_LAYER = res.layer; + V_UV_BOUNDS = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; +} + +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 -Fragment text_fs(void) { +Fragment text_brush_fs(void) { Fragment frag; - vec2 tc = clamp(v_uv, v_uv_bounds.xy, v_uv_bounds.zw); + vec3 tc = vec3(clamp(V_UV, V_UV_BOUNDS.xy, V_UV_BOUNDS.zw), V_LAYER); vec4 mask = texture(sColor0, tc); - // v_mask_swizzle.z != 0 means we are using an R8 texture as alpha, - // and therefore must swizzle from the r channel to all channels. - mask = mix(mask, mask.rrrr, bvec4(v_mask_swizzle.z != 0.0)); - #ifndef WR_FEATURE_DUAL_SOURCE_BLENDING - mask.rgb = mask.rgb * v_mask_swizzle.x + mask.aaa * v_mask_swizzle.y; - #endif + mask.rgb = mask.rgb * V_MASK_SWIZZLE.x + mask.aaa * V_MASK_SWIZZLE.y; - #if defined(WR_FEATURE_GLYPH_TRANSFORM) && !defined(SWGL_CLIP_DIST) - mask *= float(all(greaterThanEqual(v_uv_clip, vec4(0.0)))); + #ifdef WR_FEATURE_GLYPH_TRANSFORM + mask *= float(all(greaterThanEqual(V_UV_CLIP, vec4(0.0)))); #endif - frag.color = v_color * mask; + frag.color = V_COLOR * mask; - #if defined(WR_FEATURE_DUAL_SOURCE_BLENDING) && !defined(SWGL_BLEND) - frag.blend = mask * v_mask_swizzle.x + mask.aaaa * v_mask_swizzle.y; + #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING + frag.blend = V_COLOR.a * mask; #endif return frag; } - -void main() { - Fragment frag = text_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) && !defined(SWGL_BLEND) - oFragColor = frag.color; - oFragBlend = frag.blend * clip_mask; - #else - write_output(frag.color); - #endif -} - -#if defined(SWGL_DRAW_SPAN) && defined(SWGL_BLEND) && defined(SWGL_CLIP_DIST) -void swgl_drawSpanRGBA8() { - // Only support simple swizzles for now. More complex swizzles must either - // be handled by blend overrides or the slow path. - if (v_mask_swizzle.x != 0.0 && v_mask_swizzle.x != 1.0) { - return; - } - - #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING - swgl_commitTextureLinearRGBA8(sColor0, v_uv, v_uv_bounds); - #else - if (swgl_isTextureR8(sColor0)) { - swgl_commitTextureLinearColorR8ToRGBA8(sColor0, v_uv, v_uv_bounds, v_color); - } else { - swgl_commitTextureLinearColorRGBA8(sColor0, v_uv, v_uv_bounds, v_color); - } - #endif -} -#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/third_party/webrender/webrender/res/rect.glsl b/third_party/webrender/webrender/res/rect.glsl index 039b154ceed..093aea02804 100644 --- a/third_party/webrender/webrender/res/rect.glsl +++ b/third_party/webrender/webrender/res/rect.glsl @@ -40,17 +40,3 @@ float point_inside_rect(vec2 p, vec2 p0, vec2 p1) { vec2 s = step(p0, p) - step(p1, p); return s.x * s.y; } - -float signed_distance_rect(vec2 pos, vec2 p0, vec2 p1) { - vec2 d = max(p0 - pos, pos - p1); - // Instead of using a true signed distance to rect here, we just use the - // simpler approximation of the maximum distance on either axis from the - // outside of the rectangle. This avoids expensive use of length() and only - // causes mostly imperceptible differences at corner pixels. - return max(d.x, d.y); -} - -vec2 clamp_rect(vec2 pt, RectWithSize rect) { - return clamp(pt, rect.p0, rect.p0 + rect.size); -} - diff --git a/third_party/webrender/webrender/res/render_task.glsl b/third_party/webrender/webrender/res/render_task.glsl index 96f34c6e2da..54582cd9adf 100644 --- a/third_party/webrender/webrender/res/render_task.glsl +++ b/third_party/webrender/webrender/res/render_task.glsl @@ -8,9 +8,14 @@ uniform HIGHP_SAMPLER_FLOAT sampler2D sRenderTasks; -struct RenderTaskData { +struct RenderTaskCommonData { RectWithSize task_rect; - vec4 user_data; + float texture_layer_index; +}; + +struct RenderTaskData { + RenderTaskCommonData common_data; + vec3 user_data; }; RenderTaskData fetch_render_task_data(int index) { @@ -24,15 +29,20 @@ RenderTaskData fetch_render_task_data(int index) { texel0.zw ); - RenderTaskData data = RenderTaskData( + RenderTaskCommonData common_data = RenderTaskCommonData( task_rect, - texel1 + texel1.x + ); + + RenderTaskData data = RenderTaskData( + common_data, + texel1.yzw ); return data; } -RectWithSize fetch_render_task_rect(int index) { +RenderTaskCommonData fetch_render_task_common_data(int index) { ivec2 uv = get_fetch_uv(index, VECS_PER_RENDER_TASK); vec4 texel0 = TEXEL_FETCH(sRenderTasks, uv, 0, ivec2(0, 0)); @@ -43,7 +53,12 @@ RectWithSize fetch_render_task_rect(int index) { texel0.zw ); - return task_rect; + RenderTaskCommonData data = RenderTaskCommonData( + task_rect, + texel1.x + ); + + return data; } #define PIC_TYPE_IMAGE 1 @@ -55,7 +70,7 @@ RectWithSize fetch_render_task_rect(int index) { the transform mode of primitives on this picture, among other things. */ struct PictureTask { - RectWithSize task_rect; + RenderTaskCommonData common_data; float device_pixel_scale; vec2 content_origin; }; @@ -64,7 +79,7 @@ PictureTask fetch_picture_task(int address) { RenderTaskData task_data = fetch_render_task_data(address); PictureTask task = PictureTask( - task_data.task_rect, + task_data.common_data, task_data.user_data.x, task_data.user_data.yz ); @@ -75,7 +90,7 @@ PictureTask fetch_picture_task(int address) { #define CLIP_TASK_EMPTY 0x7FFF struct ClipArea { - RectWithSize task_rect; + RenderTaskCommonData common_data; float device_pixel_scale; vec2 screen_origin; }; @@ -84,13 +99,15 @@ ClipArea fetch_clip_area(int index) { ClipArea area; if (index >= CLIP_TASK_EMPTY) { - area.task_rect = RectWithSize(vec2(0.0), vec2(0.0)); + RectWithSize rect = RectWithSize(vec2(0.0), vec2(0.0)); + + area.common_data = RenderTaskCommonData(rect, 0.0); area.device_pixel_scale = 0.0; area.screen_origin = vec2(0.0); } else { RenderTaskData task_data = fetch_render_task_data(index); - area.task_rect = task_data.task_rect; + area.common_data = task_data.common_data; area.device_pixel_scale = task_data.user_data.x; area.screen_origin = task_data.user_data.yz; } diff --git a/third_party/webrender/webrender/res/shared.glsl b/third_party/webrender/webrender/res/shared.glsl index 818381187cf..7723b70f19c 100644 --- a/third_party/webrender/webrender/res/shared.glsl +++ b/third_party/webrender/webrender/res/shared.glsl @@ -2,19 +2,22 @@ * 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 WR_FEATURE_PIXEL_LOCAL_STORAGE +// For now, we need both extensions here, in order to initialize +// the PLS to the current framebuffer color. In future, we can +// possibly remove that requirement, or at least support the +// other framebuffer fetch extensions that provide the same +// functionality. +#extension GL_EXT_shader_pixel_local_storage : require +#extension GL_ARM_shader_framebuffer_fetch : require +#endif + #ifdef WR_FEATURE_TEXTURE_EXTERNAL // Please check https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external_essl3.txt // for this extension. #extension GL_OES_EGL_image_external_essl3 : require #endif -#ifdef WR_FEATURE_TEXTURE_EXTERNAL_ESSL1 -// Some GLES 3 devices do not support GL_OES_EGL_image_external_essl3, so we -// must use GL_OES_EGL_image_external instead and make the shader ESSL1 -// compatible. -#extension GL_OES_EGL_image_external : require -#endif - #ifdef WR_FEATURE_ADVANCED_BLEND #extension GL_KHR_blend_equation_advanced : require #endif @@ -29,21 +32,10 @@ #include base -#if defined(WR_FEATURE_TEXTURE_EXTERNAL_ESSL1) -#define TEX_SAMPLE(sampler, tex_coord) texture2D(sampler, tex_coord.xy) -#else +#if defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_RECT) || defined(WR_FEATURE_TEXTURE_2D) #define TEX_SAMPLE(sampler, tex_coord) texture(sampler, tex_coord.xy) -#endif - -#if defined(WR_FEATURE_TEXTURE_EXTERNAL) && defined(PLATFORM_ANDROID) -// On some Mali GPUs we have encountered crashes in glDrawElements when using -// textureSize(samplerExternalOES) in a vertex shader without potentially -// sampling from the texture. This tricks the driver in to thinking the texture -// may be sampled from, avoiding the crash. See bug 1692848. -uniform bool u_mali_workaround_dummy; -#define TEX_SIZE(sampler) (u_mali_workaround_dummy ? ivec2(texture(sampler, vec2(0.0, 0.0)).rr) : textureSize(sampler, 0)) #else -#define TEX_SIZE(sampler) textureSize(sampler, 0) +#define TEX_SAMPLE(sampler, tex_coord) texture(sampler, tex_coord) #endif //====================================================================================== @@ -58,7 +50,7 @@ uniform bool u_mali_workaround_dummy; uniform mat4 uTransform; // Orthographic projection // Attribute inputs - attribute vec2 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. @@ -74,24 +66,67 @@ uniform bool u_mali_workaround_dummy; #ifdef WR_FRAGMENT_SHADER // Uniform inputs - // Fragment shader outputs - #ifdef WR_FEATURE_ADVANCED_BLEND - layout(blend_support_all_equations) out; - #endif + #ifdef WR_FEATURE_PIXEL_LOCAL_STORAGE + // Define the storage class of the pixel local storage. + // If defined as writable, it's a compile time error to + // have a normal fragment output variable declared. + #if defined(PLS_READONLY) + #define PLS_BLOCK __pixel_local_inEXT + #elif defined(PLS_WRITEONLY) + #define PLS_BLOCK __pixel_local_outEXT + #else + #define PLS_BLOCK __pixel_localEXT + #endif - #if __VERSION__ == 100 - #define oFragColor gl_FragColor - #elif defined(WR_FEATURE_DUAL_SOURCE_BLENDING) - layout(location = 0, index = 0) out vec4 oFragColor; - layout(location = 0, index = 1) out vec4 oFragBlend; + // The structure of pixel local storage. Right now, it's + // just the current framebuffer color. In future, we have + // (at least) 12 bytes of space we can store extra info + // here (such as clip mask values). + PLS_BLOCK FrameBuffer { + layout(rgba8) highp vec4 color; + } PLS; + + #ifndef PLS_READONLY + // Write the output of a fragment shader to PLS. Applies + // premultipled alpha blending by default, since the blender + // is disabled when PLS is active. + // TODO(gw): Properly support alpha blend mode for webgl / canvas. + void write_output(vec4 color) { + PLS.color = color + PLS.color * (1.0 - color.a); + } + + // Write a raw value straight to PLS, if the fragment shader has + // already applied blending. + void write_output_raw(vec4 color) { + PLS.color = color; + } + #endif + + #ifndef PLS_WRITEONLY + // Retrieve the current framebuffer color. Useful in conjunction with + // the write_output_raw function. + vec4 get_current_framebuffer_color() { + return PLS.color; + } + #endif #else - out vec4 oFragColor; - #endif + // Fragment shader outputs + #ifdef WR_FEATURE_ADVANCED_BLEND + layout(blend_support_all_equations) out; + #endif - // Write an output color in normal shaders. - void write_output(vec4 color) { - oFragColor = color; - } + #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING + layout(location = 0, index = 0) out vec4 oFragColor; + layout(location = 0, index = 1) out vec4 oFragBlend; + #else + out vec4 oFragColor; + #endif + + // Write an output color in normal (non-PLS) shaders. + void write_output(vec4 color) { + oFragColor = color; + } + #endif #define EPSILON 0.0001 @@ -103,14 +138,11 @@ uniform bool u_mali_workaround_dummy; return dot(normalize(perp_dir), dir_to_p0); } -// fwidth is not defined in ESSL 1, but that's okay because we don't need -// it for any ESSL 1 shader variants. -#if __VERSION__ != 100 /// Find the appropriate half range to apply the AA approximation over. /// This range represents a coefficient to go from one CSS pixel to half a device pixel. float compute_aa_range(vec2 position) { // The constant factor is chosen to compensate for the fact that length(fw) is equal - // to sqrt(2) times the device pixel ratio in the typical case. + // to sqrt(2) times the device pixel ratio in the typical case. 0.5/sqrt(2) = 0.35355. // // This coefficient is chosen to ensure that any sample 0.5 pixels or more inside of // the shape has no anti-aliasing applied to it (since pixels are sampled at their center, @@ -125,38 +157,33 @@ uniform bool u_mali_workaround_dummy; // We may want to adjust this constant in specific scenarios (for example keep the principled // value for straight edges where we want pixel-perfect equivalence with non antialiased lines // when axis aligned, while selecting a larger and smoother aa range on curves). - // - // As a further optimization, we compute the reciprocal of this range, such that we - // can then use the cheaper inversesqrt() instead of length(). This also elides a - // division that would otherwise be necessary inside distance_aa. - #ifdef SWGL - // SWGL uses an approximation for fwidth() such that it returns equal x and y. - // Thus, sqrt(2)/length(w) = sqrt(2)/sqrt(x*x + x*x) = recip(x). - return recip(fwidth(position).x); - #else - // sqrt(2)/length(w) = inversesqrt(0.5 * dot(w, w)) - vec2 w = fwidth(position); - return inversesqrt(0.5 * dot(w, w)); - #endif + return 0.35355 * length(fwidth(position)); } -#endif /// Return the blending coefficient for distance antialiasing. /// /// 0.0 means inside the shape, 1.0 means outside. /// - /// This makes the simplifying assumption that the area of a 1x1 pixel square - /// under a line is reasonably similar to just the signed Euclidian distance - /// from the center of the square to that line. This diverges slightly from - /// better approximations of the exact area, but the difference between the - /// methods is not perceptibly noticeable, while this approximation is much - /// faster to compute. + /// This cubic polynomial approximates the area of a 1x1 pixel square under a + /// line, given the signed Euclidean distance from the center of the square to + /// that line. Calculating the *exact* area would require taking into account + /// not only this distance but also the angle of the line. However, in + /// practice, this complexity is not required, as the area is roughly the same + /// regardless of the angle. + /// + /// The coefficients of this polynomial were determined through least-squares + /// regression and are accurate to within 2.16% of the total area of the pixel + /// square 95% of the time, with a maximum error of 3.53%. /// /// See the comments in `compute_aa_range()` for more information on the /// cutoff values of -0.5 and 0.5. float distance_aa(float aa_range, float signed_distance) { - float dist = signed_distance * aa_range; - return clamp(0.5 - dist, 0.0, 1.0); + float dist = 0.5 * signed_distance / aa_range; + if (dist <= -0.5 + EPSILON) + return 1.0; + if (dist >= 0.5 - EPSILON) + return 0.0; + return 0.5 + dist * (0.8431027 * dist * dist - 1.14453603); } /// Component-wise selection. @@ -187,10 +214,14 @@ uniform sampler2D sColor2; uniform sampler2DRect sColor0; uniform sampler2DRect sColor1; uniform sampler2DRect sColor2; -#elif defined(WR_FEATURE_TEXTURE_EXTERNAL) || defined(WR_FEATURE_TEXTURE_EXTERNAL_ESSL1) +#elif defined WR_FEATURE_TEXTURE_EXTERNAL uniform samplerExternalOES sColor0; uniform samplerExternalOES sColor1; uniform samplerExternalOES sColor2; +#else +uniform sampler2DArray sColor0; +uniform sampler2DArray sColor1; +uniform sampler2DArray sColor2; #endif #ifdef WR_FEATURE_DITHERING diff --git a/third_party/webrender/webrender/res/transform.glsl b/third_party/webrender/webrender/res/transform.glsl index f3f88ff53cc..99eabec2a46 100644 --- a/third_party/webrender/webrender/res/transform.glsl +++ b/third_party/webrender/webrender/res/transform.glsl @@ -93,9 +93,9 @@ vec4 get_node_pos(vec2 pos, Transform transform) { #ifdef WR_FRAGMENT_SHADER -// Assume transform bounds are set to a large scale to signal they are invalid. -bool has_valid_transform_bounds() { - return vTransformBounds.w < 1.0e15; +float signed_distance_rect(vec2 pos, vec2 p0, vec2 p1) { + vec2 d = max(p0 - pos, pos - p1); + return length(max(vec2(0.0), d)) + min(0.0, max(d.x, d.y)); } float init_transform_fs(vec2 local_pos) { diff --git a/third_party/webrender/webrender/res/yuv.glsl b/third_party/webrender/webrender/res/yuv.glsl index 1cd79434188..ce582467be5 100644 --- a/third_party/webrender/webrender/res/yuv.glsl +++ b/third_party/webrender/webrender/res/yuv.glsl @@ -11,15 +11,14 @@ #ifdef WR_VERTEX_SHADER #ifdef WR_FEATURE_TEXTURE_RECT - #define TEX_SIZE_YUV(sampler) vec2(1.0) + #define TEX_SIZE(sampler) vec2(1.0) #else - #define TEX_SIZE_YUV(sampler) vec2(TEX_SIZE(sampler).xy) + #define TEX_SIZE(sampler) vec2(textureSize(sampler, 0).xy) #endif #define YUV_COLOR_SPACE_REC601 0 #define YUV_COLOR_SPACE_REC709 1 #define YUV_COLOR_SPACE_REC2020 2 -#define YUV_COLOR_SPACE_IDENTITY 3 // The constants added to the Y, U and V components are applied in the fragment shader. @@ -65,31 +64,14 @@ const mat3 YuvColorMatrixRec2020 = mat3( 1.67867410714286 , -0.650424318505057, 0.0 ); -// The matrix is stored in column-major. -// Identity is stored as GBR -const mat3 IdentityColorMatrix = mat3( - 0.0 , 1.0, 0.0, - 0.0 , 0.0, 1.0, - 1.0 , 0.0, 0.0 -); - mat3 get_yuv_color_matrix(int color_space) { - if (color_space == YUV_COLOR_SPACE_REC601) { - return YuvColorMatrixRec601; - } else if (color_space == YUV_COLOR_SPACE_REC709) { - return YuvColorMatrixRec709; - } else if (color_space == YUV_COLOR_SPACE_IDENTITY) { - return IdentityColorMatrix; - } else { - return YuvColorMatrixRec2020; - } -} - -vec3 get_yuv_offset_vector(int color_space) { - if (color_space == YUV_COLOR_SPACE_IDENTITY) { - return vec3(0.0, 0.0, 0.0); - } else { - return vec3(0.06275, 0.50196, 0.50196); + switch (color_space) { + case YUV_COLOR_SPACE_REC601: + return YuvColorMatrixRec601; + case YUV_COLOR_SPACE_REC709: + return YuvColorMatrixRec709; + default: + return YuvColorMatrixRec2020; } } @@ -117,8 +99,8 @@ void write_uv_rect( vec4 sample_yuv( int format, mat3 yuv_color_matrix, - vec3 yuv_offset_vector, float coefficient, + vec3 yuv_layers, vec2 in_uv_y, vec2 in_uv_u, vec2 in_uv_v, @@ -135,9 +117,9 @@ vec4 sample_yuv( vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw); vec2 uv_u = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw); vec2 uv_v = clamp(in_uv_v, uv_bounds_v.xy, uv_bounds_v.zw); - yuv_value.x = TEX_SAMPLE(sColor0, uv_y).r; - yuv_value.y = TEX_SAMPLE(sColor1, uv_u).r; - yuv_value.z = TEX_SAMPLE(sColor2, uv_v).r; + yuv_value.x = TEX_SAMPLE(sColor0, vec3(uv_y, yuv_layers.x)).r; + yuv_value.y = TEX_SAMPLE(sColor1, vec3(uv_u, yuv_layers.y)).r; + yuv_value.z = TEX_SAMPLE(sColor2, vec3(uv_v, yuv_layers.z)).r; } break; @@ -145,8 +127,8 @@ vec4 sample_yuv( { vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw); vec2 uv_uv = clamp(in_uv_u, uv_bounds_u.xy, uv_bounds_u.zw); - yuv_value.x = TEX_SAMPLE(sColor0, uv_y).r; - yuv_value.yz = TEX_SAMPLE(sColor1, uv_uv).rg; + yuv_value.x = TEX_SAMPLE(sColor0, vec3(uv_y, yuv_layers.x)).r; + yuv_value.yz = TEX_SAMPLE(sColor1, vec3(uv_uv, yuv_layers.y)).rg; } break; @@ -156,7 +138,7 @@ vec4 sample_yuv( // the existing green, blue and red color channels." // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_rgb_422.txt vec2 uv_y = clamp(in_uv_y, uv_bounds_y.xy, uv_bounds_y.zw); - yuv_value = TEX_SAMPLE(sColor0, uv_y).gbr; + yuv_value = TEX_SAMPLE(sColor0, vec3(uv_y, yuv_layers.x)).gbr; } break; @@ -166,13 +148,7 @@ vec4 sample_yuv( } // See the YuvColorMatrix definition for an explanation of where the constants come from. - vec3 yuv = yuv_value * coefficient - yuv_offset_vector; - vec3 rgb = yuv_color_matrix * yuv; - #if defined(WR_FEATURE_ALPHA_PASS) && defined(SWGL_CLIP_MASK) - // Avoid out-of-range RGB values that can mess with blending. These occur due to invalid - // YUV values outside the mappable space that never the less can be generated. - rgb = clamp(rgb, 0.0, 1.0); - #endif + vec3 rgb = yuv_color_matrix * (yuv_value * coefficient - vec3(0.06275, 0.50196, 0.50196)); vec4 color = vec4(rgb, 1.0); return color; diff --git a/third_party/webrender/webrender/src/batch.rs b/third_party/webrender/webrender/src/batch.rs index 588afdfea81..1ee371c6531 100644 --- a/third_party/webrender/webrender/src/batch.rs +++ b/third_party/webrender/webrender/src/batch.rs @@ -2,38 +2,35 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{AlphaType, ClipMode, ImageRendering, ImageBufferKind}; -use api::{FontInstanceFlags, YuvColorSpace, YuvFormat, ColorDepth, ColorRange, PremultipliedColorF}; +use api::{AlphaType, ClipMode, ExternalImageType, ImageRendering, EdgeAaSegmentMask}; +use api::{YuvColorSpace, YuvFormat, ColorDepth, ColorRange, PremultipliedColorF}; use api::units::*; use crate::clip::{ClipDataStore, ClipNodeFlags, ClipNodeRange, ClipItemKind, ClipStore}; use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, CoordinateSystemId}; use crate::composite::{CompositeState}; -use crate::glyph_rasterizer::{GlyphFormat, SubpixelDirection}; -use crate::gpu_cache::{GpuBlockData, GpuCache, GpuCacheAddress}; +use crate::glyph_rasterizer::GlyphFormat; +use crate::gpu_cache::{GpuBlockData, GpuCache, GpuCacheHandle, GpuCacheAddress}; use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, ZBufferIdGenerator}; -use crate::gpu_types::{SplitCompositeInstance}; +use crate::gpu_types::{ClipMaskInstance, SplitCompositeInstance, BrushShaderKind}; use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance}; use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette}; -use crate::gpu_types::{ImageBrushData, get_shader_opacity, BoxShadowData}; -use crate::gpu_types::{ClipMaskInstanceCommon, ClipMaskInstanceImage, ClipMaskInstanceRect, ClipMaskInstanceBoxShadow}; -use crate::internal_types::{FastHashMap, Swizzle, TextureSource, Filter}; -use crate::picture::{ClusterFlags, Picture3DContext, PictureCompositeMode, PicturePrimitive, SubSliceIndex}; -use crate::prim_store::{DeferredResolve, PrimitiveInstanceKind, ClipData}; -use crate::prim_store::{PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; -use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex}; -use crate::prim_store::VECS_PER_SEGMENT; +use crate::gpu_types::{ImageBrushData, get_shader_opacity}; +use crate::internal_types::{FastHashMap, SavedTargetIndex, Swizzle, TextureSource, Filter}; +use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive}; +use crate::prim_store::{DeferredResolve, PrimitiveInstanceKind, PrimitiveVisibilityIndex, PrimitiveVisibilityMask}; +use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex}; +use crate::prim_store::{BrushSegment, ClipMaskKind, ClipTaskIndex, PrimitiveVisibility, 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}; use crate::render_task::RenderTaskAddress; -use crate::renderer::{BlendMode, ShaderColorMode}; -use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH; -use crate::resource_cache::{GlyphFetchResult, ImageProperties, ImageRequest, ResourceCache}; -use crate::space::SpaceMapper; -use crate::visibility::{PrimitiveVisibilityFlags, VisibilityState}; +use crate::renderer::{BlendMode, ImageBufferKind, ShaderColorMode}; +use crate::renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH}; +use crate::resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache}; use smallvec::SmallVec; use std::{f32, i32, usize}; -use crate::util::{project_rect, MaxRect, MatrixHelpers, TransformedRectKind}; -use crate::segment::EdgeAaSegmentMask; +use crate::util::{project_rect, TransformedRectKind}; // Special sentinel value recognized by the shader. It is considered to be // a dummy task that doesn't mask out anything. @@ -46,22 +43,7 @@ const INVALID_SEGMENT_INDEX: i32 = 0xffff; const CLIP_RECTANGLE_TILE_SIZE: i32 = 128; /// The minimum size of a clip mask before trying to draw in tiles. -const CLIP_RECTANGLE_AREA_THRESHOLD: f32 = (CLIP_RECTANGLE_TILE_SIZE * CLIP_RECTANGLE_TILE_SIZE * 4) as f32; - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Copy, Clone, Debug)] -pub struct BatchFilter { - pub rect_in_pic_space: PictureRect, - pub sub_slice_index: SubSliceIndex, -} - -impl BatchFilter { - pub fn matches(&self, other: &BatchFilter) -> bool { - self.sub_slice_index == other.sub_slice_index && - self.rect_in_pic_space.intersects(&other.rect_in_pic_space) - } -} +const CLIP_RECTANGLE_AREA_THRESHOLD: i32 = CLIP_RECTANGLE_TILE_SIZE * CLIP_RECTANGLE_TILE_SIZE * 4; #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] @@ -72,9 +54,12 @@ pub enum BrushBatchKind { Blend, MixBlend { task_id: RenderTaskId, + source_id: RenderTaskId, backdrop_id: RenderTaskId, }, YuvImage(ImageBufferKind, YuvFormat, ColorDepth, YuvColorSpace, ColorRange), + ConicGradient, + RadialGradient, LinearGradient, Opacity, } @@ -88,49 +73,20 @@ pub enum BatchKind { Brush(BrushBatchKind), } -/// Input textures for a primitive, without consideration of clip mask -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct TextureSet { - pub colors: [TextureSource; 3], -} - -impl TextureSet { - const UNTEXTURED: TextureSet = TextureSet { - colors: [ - TextureSource::Invalid, - TextureSource::Invalid, - TextureSource::Invalid, - ], - }; - - /// A textured primitive - fn prim_textured( - color: TextureSource, - ) -> Self { - TextureSet { - colors: [ - color, - TextureSource::Invalid, - TextureSource::Invalid, - ], - } - } - - fn is_compatible_with(&self, other: &TextureSet) -> bool { - self.colors[0].is_compatible(&other.colors[0]) && - self.colors[1].is_compatible(&other.colors[1]) && - self.colors[2].is_compatible(&other.colors[2]) - } -} - -impl TextureSource { - fn combine(&self, other: TextureSource) -> TextureSource { - if other == TextureSource::Invalid { - *self - } else { - other +impl BatchKind { + fn shader_kind(&self) -> BrushShaderKind { + match self { + BatchKind::Brush(BrushBatchKind::Solid) => BrushShaderKind::Solid, + BatchKind::Brush(BrushBatchKind::Image(..)) => BrushShaderKind::Image, + BatchKind::Brush(BrushBatchKind::LinearGradient) => BrushShaderKind::LinearGradient, + BatchKind::Brush(BrushBatchKind::RadialGradient) => BrushShaderKind::RadialGradient, + BatchKind::Brush(BrushBatchKind::ConicGradient) => BrushShaderKind::ConicGradient, + BatchKind::Brush(BrushBatchKind::Blend) => BrushShaderKind::Blend, + BatchKind::Brush(BrushBatchKind::MixBlend { .. }) => BrushShaderKind::MixBlend, + BatchKind::Brush(BrushBatchKind::YuvImage(..)) => BrushShaderKind::Yuv, + BatchKind::Brush(BrushBatchKind::Opacity) => BrushShaderKind::Opacity, + BatchKind::TextRun(..) => BrushShaderKind::Text, + _ => BrushShaderKind::None, } } } @@ -141,76 +97,34 @@ impl TextureSource { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct BatchTextures { - pub input: TextureSet, - pub clip_mask: TextureSource, + pub colors: [TextureSource; 3], } impl BatchTextures { - /// An empty batch textures (no binding slots set) - pub fn empty() -> BatchTextures { + pub fn no_texture() -> Self { BatchTextures { - input: TextureSet::UNTEXTURED, - clip_mask: TextureSource::Invalid, + colors: [TextureSource::Invalid; 3], } } - /// A textured primitive with optional clip mask - pub fn prim_textured( - color: TextureSource, - clip_mask: TextureSource, - ) -> BatchTextures { + pub fn render_target_cache() -> Self { BatchTextures { - input: TextureSet::prim_textured(color), - clip_mask, - } - } - - /// An untextured primitive with optional clip mask - pub fn prim_untextured( - clip_mask: TextureSource, - ) -> BatchTextures { - BatchTextures { - input: TextureSet::UNTEXTURED, - clip_mask, - } - } - - /// A composite style effect with single input texture - pub fn composite_rgb( - texture: TextureSource, - ) -> BatchTextures { - BatchTextures { - input: TextureSet { - colors: [ - texture, - TextureSource::Invalid, - TextureSource::Invalid, - ], - }, - clip_mask: TextureSource::Invalid, + colors: [ + TextureSource::PrevPassColor, + TextureSource::PrevPassAlpha, + TextureSource::Invalid, + ], } } - /// A composite style effect with up to 3 input textures - pub fn composite_yuv( - color0: TextureSource, - color1: TextureSource, - color2: TextureSource, - ) -> BatchTextures { + pub fn color(texture: TextureSource) -> Self { BatchTextures { - input: TextureSet { - colors: [color0, color1, color2], - }, - clip_mask: TextureSource::Invalid, + colors: [texture, texture, TextureSource::Invalid], } } pub fn is_compatible_with(&self, other: &BatchTextures) -> bool { - if !self.clip_mask.is_compatible(&other.clip_mask) { - return false; - } - - self.input.is_compatible_with(&other.input) + self.colors.iter().zip(other.colors.iter()).all(|(t1, t2)| textures_compatible(*t1, *t2)) } pub fn combine_textures(&self, other: BatchTextures) -> Option<BatchTextures> { @@ -218,24 +132,17 @@ impl BatchTextures { return None; } - let mut new_textures = BatchTextures::empty(); - - new_textures.clip_mask = self.clip_mask.combine(other.clip_mask); - - for i in 0 .. 3 { - new_textures.input.colors[i] = self.input.colors[i].combine(other.input.colors[i]); + let mut new_textures = BatchTextures::no_texture(); + for (i, (color, other_color)) in self.colors.iter().zip(other.colors.iter()).enumerate() { + // If these textures are compatible, for each source either both sources are invalid or only one is not invalid. + new_textures.colors[i] = if *color == TextureSource::Invalid { + *other_color + } else { + *color + }; } - Some(new_textures) } - - fn merge(&mut self, other: &BatchTextures) { - self.clip_mask = self.clip_mask.combine(other.clip_mask); - - for (s, o) in self.input.colors.iter_mut().zip(other.input.colors.iter()) { - *s = s.combine(*o); - } - } } #[derive(Copy, Clone, Debug)] @@ -261,76 +168,29 @@ impl BatchKey { } } -pub struct BatchRects { - /// Union of all of the batch's item rects. - /// - /// Very often we can skip iterating over item rects by testing against - /// this one first. - batch: PictureRect, - /// When the batch rectangle above isn't a good enough approximation, we - /// store per item rects. - items: Option<Vec<PictureRect>>, +#[inline] +fn textures_compatible(t1: TextureSource, t2: TextureSource) -> bool { + t1 == TextureSource::Invalid || t2 == TextureSource::Invalid || t1 == t2 } -impl BatchRects { - fn new() -> Self { - BatchRects { - batch: PictureRect::zero(), - items: None, - } - } - - #[inline] - fn add_rect(&mut self, rect: &PictureRect) { - let union = self.batch.union(rect); - // If we have already started storing per-item rects, continue doing so. - // Otherwise, check whether only storing the batch rect is a good enough - // approximation. - if let Some(items) = &mut self.items { - items.push(*rect); - } else if self.batch.area() + rect.area() < union.area() { - let mut items = Vec::with_capacity(16); - items.push(self.batch); - items.push(*rect); - self.items = Some(items); - } - - self.batch = union; - } - - #[inline] - fn intersects(&mut self, rect: &PictureRect) -> bool { - if !self.batch.intersects(rect) { - return false; - } - - if let Some(items) = &self.items { - items.iter().any(|item| item.intersects(rect)) - } else { - // If we don't have per-item rects it means the batch rect is a good - // enough approximation and we didn't bother storing per-rect items. - true - } - } -} - - pub struct AlphaBatchList { pub batches: Vec<PrimitiveBatch>, - pub batch_rects: Vec<BatchRects>, + pub item_rects: Vec<Vec<PictureRect>>, current_batch_index: usize, current_z_id: ZBufferId, break_advanced_blend_batches: bool, + lookback_count: usize, } impl AlphaBatchList { - fn new(break_advanced_blend_batches: bool, preallocate: usize) -> Self { + fn new(break_advanced_blend_batches: bool, lookback_count: usize) -> Self { AlphaBatchList { - batches: Vec::with_capacity(preallocate), - batch_rects: Vec::with_capacity(preallocate), + batches: Vec::new(), + item_rects: Vec::new(), current_z_id: ZBufferId::invalid(), current_batch_index: usize::MAX, break_advanced_blend_batches, + lookback_count, } } @@ -341,7 +201,7 @@ impl AlphaBatchList { self.current_batch_index = usize::MAX; self.current_z_id = ZBufferId::invalid(); self.batches.clear(); - self.batch_rects.clear(); + self.item_rects.clear(); } pub fn set_params_and_get_batch( @@ -361,12 +221,14 @@ impl AlphaBatchList { match key.blend_mode { BlendMode::SubpixelWithBgColor => { - for (batch_index, batch) in self.batches.iter().enumerate().rev() { + 'outer_multipass: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(self.lookback_count) { // Some subpixel batches are drawn in two passes. Because of this, we need // to check for overlaps with every batch (which is a bit different // than the normal batching below). - if self.batch_rects[batch_index].intersects(z_bounding_rect) { - break; + for item_rect in &self.item_rects[batch_index] { + if item_rect.intersects(z_bounding_rect) { + break 'outer_multipass; + } } if batch.key.is_compatible_with(&key) { @@ -379,7 +241,7 @@ impl AlphaBatchList { // don't try to find a batch } _ => { - for (batch_index, batch) in self.batches.iter().enumerate().rev() { + 'outer_default: for (batch_index, batch) in self.batches.iter().enumerate().rev().take(self.lookback_count) { // For normal batches, we only need to check for overlaps for batches // other than the first batch we consider. If the first batch // is compatible, then we know there isn't any potential overlap @@ -390,40 +252,33 @@ impl AlphaBatchList { } // check for intersections - if self.batch_rects[batch_index].intersects(z_bounding_rect) { - break; + for item_rect in &self.item_rects[batch_index] { + if item_rect.intersects(z_bounding_rect) { + break 'outer_default; + } } } } } if selected_batch_index.is_none() { - // Text runs tend to have a lot of instances per batch, causing a lot of reallocation - // churn as items are added one by one, so we give it a head start. Ideally we'd start - // with a larger number, closer to 1k but in some bad cases with lots of batch break - // we would be wasting a lot of memory. - // Generally it is safe to preallocate small-ish values for other batch kinds because - // the items are small and there are no zero-sized batches so there will always be - // at least one allocation. - let prealloc = match key.kind { - BatchKind::TextRun(..) => 128, - _ => 16, - }; - let mut new_batch = PrimitiveBatch::new(key); - new_batch.instances.reserve(prealloc); + let new_batch = PrimitiveBatch::new(key); selected_batch_index = Some(self.batches.len()); self.batches.push(new_batch); - self.batch_rects.push(BatchRects::new()); + self.item_rects.push(Vec::new()); } self.current_batch_index = selected_batch_index.unwrap(); - self.batch_rects[self.current_batch_index].add_rect(z_bounding_rect); + self.item_rects[self.current_batch_index].push(*z_bounding_rect); self.current_z_id = z_id; + } else if cfg!(debug_assertions) { + // If it's a different segment of the same (larger) primitive, we expect the bounding box + // to be the same - coming from the primitive itself, not the segment. + assert_eq!(self.item_rects[self.current_batch_index].last(), Some(z_bounding_rect)); } let batch = &mut self.batches[self.current_batch_index]; batch.features |= features; - batch.key.textures.merge(&key.textures); &mut batch.instances } @@ -499,7 +354,6 @@ impl OpaqueBatchList { let batch = &mut self.batches[self.current_batch_index]; batch.features |= features; - batch.key.textures.merge(&key.textures); &mut batch.instances } @@ -540,8 +394,6 @@ bitflags! { const ALPHA_PASS = 1 << 0; const ANTIALIASING = 1 << 1; const REPETITION = 1 << 2; - /// Indicates a primitive in this batch may use a clip mask. - const CLIP_MASK = 1 << 3; } } @@ -557,7 +409,6 @@ impl PrimitiveBatch { fn merge(&mut self, other: PrimitiveBatch) { self.instances.extend(other.instances); self.features |= other.features; - self.key.textures.merge(&other.key.textures); } } @@ -635,7 +486,7 @@ impl AlphaBatchContainer { /// texture set and one user data field. #[derive(Debug, Copy, Clone)] struct SegmentInstanceData { - textures: TextureSet, + textures: BatchTextures, specific_resource_address: i32, } @@ -645,7 +496,7 @@ pub struct AlphaBatchBuilder { pub opaque_batch_list: OpaqueBatchList, pub render_task_id: RenderTaskId, render_task_address: RenderTaskAddress, - pub batch_filter: Option<BatchFilter>, + pub vis_mask: PrimitiveVisibilityMask, } impl AlphaBatchBuilder { @@ -655,19 +506,18 @@ impl AlphaBatchBuilder { lookback_count: usize, render_task_id: RenderTaskId, render_task_address: RenderTaskAddress, - batch_filter: Option<BatchFilter>, - preallocate: usize, + vis_mask: PrimitiveVisibilityMask, ) -> Self { // The threshold for creating a new batch is // one quarter the screen size. let batch_area_threshold = (screen_size.width * screen_size.height) as f32 / 4.0; AlphaBatchBuilder { - alpha_batch_list: AlphaBatchList::new(break_advanced_blend_batches, preallocate), + alpha_batch_list: AlphaBatchList::new(break_advanced_blend_batches, lookback_count), opaque_batch_list: OpaqueBatchList::new(batch_area_threshold, lookback_count), render_task_id, render_task_address, - batch_filter, + vis_mask, } } @@ -679,15 +529,6 @@ impl AlphaBatchBuilder { self.opaque_batch_list.clear(); } - /// Return true if a primitive occupying `rect_in_pic_space` should be - /// added this batcher. - fn should_draw( - &self, - prim_filter: &BatchFilter, - ) -> bool { - self.batch_filter.map_or(true, |f| f.matches(prim_filter)) - } - pub fn build( mut self, batch_containers: &mut Vec<AlphaBatchContainer>, @@ -739,10 +580,7 @@ impl AlphaBatchBuilder { BlendMode::SubpixelConstantTextColor(..) | BlendMode::SubpixelWithBgColor | BlendMode::SubpixelDualSource | - BlendMode::Advanced(_) | - BlendMode::MultiplyDualSource | - BlendMode::Screen | - BlendMode::Exclusion => { + BlendMode::Advanced(_) => { self.alpha_batch_list .set_params_and_get_batch(key, features, bounding_rect, z_id) } @@ -785,10 +623,10 @@ impl BatchBuilder { brush_flags: BrushFlags, prim_header_index: PrimitiveHeaderIndex, resource_address: i32, - batch_filter: &BatchFilter, + prim_vis_mask: PrimitiveVisibilityMask, ) { for batcher in &mut self.batchers { - if batcher.should_draw(batch_filter) { + if batcher.vis_mask.intersects(prim_vis_mask) { let render_task_address = batcher.render_task_address; let instance = BrushInstance { @@ -799,6 +637,7 @@ impl BatchBuilder { brush_flags, prim_header_index, resource_address, + brush_kind: batch_key.kind.shader_kind(), }; batcher.push_single_instance( @@ -815,20 +654,19 @@ impl BatchBuilder { fn add_split_composite_instance_to_batches( &mut self, batch_key: BatchKey, - features: BatchFeatures, bounding_rect: &PictureRect, z_id: ZBufferId, prim_header_index: PrimitiveHeaderIndex, polygons_address: GpuCacheAddress, - batch_filter: &BatchFilter, + prim_vis_mask: PrimitiveVisibilityMask, ) { for batcher in &mut self.batchers { - if batcher.should_draw(batch_filter) { + if batcher.vis_mask.intersects(prim_vis_mask) { let render_task_address = batcher.render_task_address; batcher.push_single_instance( batch_key, - features, + BatchFeatures::empty(), bounding_rect, z_id, PrimitiveInstanceData::from(SplitCompositeInstance { @@ -866,11 +704,9 @@ impl BatchBuilder { composite_state: &mut CompositeState, ) { for cluster in &pic.prim_list.clusters { - if !cluster.flags.contains(ClusterFlags::IS_VISIBLE) { - continue; - } - for prim_instance in &pic.prim_list.prim_instances[cluster.prim_range()] { - // Add each run in this picture to the batch. + profile_scope!("cluster"); + // Add each run in this picture to the batch. + for prim_instance in &cluster.prim_instances { self.add_prim_to_batch( prim_instance, cluster.spatial_node_index, @@ -889,6 +725,73 @@ impl BatchBuilder { } } + // If an image is being drawn as a compositor surface, we don't want + // to draw the surface itself into the tile. Instead, we draw a transparent + // rectangle that writes to the z-buffer where this compositor surface is. + // That ensures we 'cut out' the part of the tile that has the compositor + // surface on it, allowing us to draw this tile as an overlay on top of + // the compositor surface. + // TODO(gw): There's a slight performance cost to doing this cutout rectangle + // if we end up not needing to use overlay mode. Consider skipping + // the cutout completely in this path. + fn emit_placeholder( + &mut self, + prim_rect: LayoutRect, + prim_info: &PrimitiveVisibility, + z_id: ZBufferId, + transform_id: TransformPaletteId, + batch_features: BatchFeatures, + ctx: &RenderTargetContext, + gpu_cache: &mut GpuCache, + render_tasks: &RenderTaskGraph, + prim_headers: &mut PrimitiveHeaders, + ) { + let batch_params = BrushBatchParameters::shared( + BrushBatchKind::Solid, + BatchTextures::no_texture(), + [get_shader_opacity(0.0), 0, 0, 0], + 0, + ); + + let prim_cache_address = gpu_cache.get_address( + &ctx.globals.default_transparent_rect_handle, + ); + + let prim_header = PrimitiveHeader { + local_rect: prim_rect, + local_clip_rect: prim_info.combined_local_clip_rect, + specific_prim_address: prim_cache_address, + transform_id, + }; + + let prim_header_index = prim_headers.push( + &prim_header, + z_id, + batch_params.prim_user_data, + ); + + let bounding_rect = &prim_info.clip_chain.pic_clip_rect; + let transform_kind = transform_id.transform_kind(); + let prim_vis_mask = prim_info.visibility_mask; + + self.add_segmented_prim_to_batch( + None, + PrimitiveOpacity::translucent(), + &batch_params, + BlendMode::None, + BlendMode::None, + batch_features, + prim_header_index, + bounding_rect, + transform_kind, + render_tasks, + z_id, + prim_info.clip_task_index, + prim_vis_mask, + ctx, + ); + } + // Adds a primitive to a batch. // It can recursively call itself in some situations, for // example if it encounters a picture where the items @@ -908,137 +811,15 @@ impl BatchBuilder { z_generator: &mut ZBufferIdGenerator, composite_state: &mut CompositeState, ) { - let (batch_filter, vis_flags) = match prim_instance.vis.state { - VisibilityState::Culled => { - return; - } - VisibilityState::Unset | VisibilityState::Coarse { .. } => { - panic!("bug: invalid visibility state"); - } - VisibilityState::Detailed { ref filter, vis_flags } => { - (filter, vis_flags) - } - VisibilityState::PassThrough => { - let pic_index = match prim_instance.kind { - PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index, - _ => unreachable!("Only picture prims can be pass through"), - }; - let picture = &ctx.prim_store.pictures[pic_index.0]; - - match picture.context_3d { - // Convert all children of the 3D hierarchy root into batches. - Picture3DContext::In { root_data: Some(ref list), .. } => { - for child in list { - let child_prim_instance = &picture.prim_list.prim_instances[child.anchor.instance_index]; - let child_prim_info = &child_prim_instance.vis; - - let child_pic_index = match child_prim_instance.kind { - PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index, - _ => unreachable!(), - }; - let pic = &ctx.prim_store.pictures[child_pic_index.0]; - - let child_batch_filter = match child_prim_info.state { - VisibilityState::Detailed { ref filter, .. } => filter, - _ => panic!("bug: culled prim should not be in child list"), - }; - - // Get clip task, if set, for the picture primitive. - let (child_clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - child_prim_info.clip_task_index, - render_tasks, - ).unwrap(); - - let prim_header = PrimitiveHeader { - local_rect: pic.precise_local_rect, - local_clip_rect: child_prim_info.combined_local_clip_rect, - specific_prim_address: GpuCacheAddress::INVALID, - transform_id: transforms - .get_id( - child.spatial_node_index, - root_spatial_node_index, - ctx.spatial_tree, - ), - }; - - let raster_config = pic - .raster_config - .as_ref() - .expect("BUG: 3d primitive was not assigned a surface"); - - let child_pic_task_id = pic - .primary_render_task_id - .unwrap(); - - let (uv_rect_address, texture) = render_tasks.resolve_location( - child_pic_task_id, - gpu_cache, - ).unwrap(); - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, - ); - - // Need a new z-id for each child preserve-3d context added - // by this inner loop. - let z_id = z_generator.next(); - - let prim_header_index = prim_headers.push(&prim_header, z_id, [ - uv_rect_address.as_int(), - if raster_config.establishes_raster_root { 1 } else { 0 }, - 0, - child_clip_task_address.0 as i32, - ]); - - let key = BatchKey::new( - BatchKind::SplitComposite, - BlendMode::PremultipliedAlpha, - textures, - ); - - self.add_split_composite_instance_to_batches( - key, - BatchFeatures::CLIP_MASK, - &child_prim_info.clip_chain.pic_clip_rect, - z_id, - prim_header_index, - child.gpu_address, - child_batch_filter, - ); - } - } - // Ignore the 3D pictures that are not in the root of preserve-3D - // hierarchy, since we process them with the root. - Picture3DContext::In { root_data: None, .. } => { - unreachable!(); - } - // Proceed for non-3D pictures. - Picture3DContext::Out => { - // If this picture is being drawn into an existing target (i.e. with - // no composition operation), recurse and add to the current batch list. - self.add_pic_to_batch( - picture, - ctx, - gpu_cache, - render_tasks, - deferred_resolves, - prim_headers, - transforms, - root_spatial_node_index, - surface_spatial_node_index, - z_generator, - composite_state, - ); - } - } - - return; - } - }; + if prim_instance.visibility_info == PrimitiveVisibilityIndex::INVALID { + return; + } #[cfg(debug_assertions)] //TODO: why is this needed? debug_assert_eq!(prim_instance.prepared_frame_id, render_tasks.frame_id()); + let is_chased = prim_instance.is_chased(); + let transform_id = transforms .get_id( prim_spatial_node_index, @@ -1050,7 +831,7 @@ impl BatchBuilder { // wasteful. We should probably cache this in // the scroll node... let transform_kind = transform_id.transform_kind(); - let prim_info = &prim_instance.vis; + 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 @@ -1058,7 +839,7 @@ impl BatchBuilder { // 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 vis_flags.contains(PrimitiveVisibilityFlags::IS_BACKDROP) { + if prim_info.flags.contains(PrimitiveVisibilityFlags::IS_BACKDROP) { self.clear_batches(); return; } @@ -1079,9 +860,14 @@ impl BatchBuilder { batch_features |= BatchFeatures::ANTIALIASING; } - // Check if the primitive might require a clip mask. - if prim_info.clip_task_index != ClipTaskIndex::INVALID { - batch_features |= BatchFeatures::CLIP_MASK; + let prim_vis_mask = prim_info.visibility_mask; + let clip_task_address = ctx.get_prim_clip_task_address( + prim_info.clip_task_index, + render_tasks, + ); + + if is_chased { + println!("\tbatch {:?} with bound {:?} and clip task {:?}", prim_rect, bounding_rect, clip_task_address); } if !bounding_rect.is_empty() { @@ -1094,11 +880,6 @@ impl BatchBuilder { let prim_data = &ctx.data_stores.prim[data_handle]; let prim_cache_address = gpu_cache.get_address(&prim_data.gpu_cache_handle); - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - // TODO(gw): We can abstract some of the common code below into // helper methods, as we port more primitives to make // use of interning. @@ -1119,7 +900,7 @@ impl BatchBuilder { let batch_key = BatchKey { blend_mode: BlendMode::PremultipliedDestOut, kind: BatchKind::Brush(BrushBatchKind::Solid), - textures: BatchTextures::prim_untextured(clip_mask_texture_id), + textures: BatchTextures::no_texture(), }; self.add_brush_instance_to_batches( @@ -1129,38 +910,37 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::all(), - clip_task_address, + clip_task_address.unwrap(), BrushFlags::PERSPECTIVE_INTERPOLATION, prim_header_index, 0, - &batch_filter, + prim_vis_mask, ); } - PrimitiveInstanceKind::NormalBorder { data_handle, ref render_task_ids, .. } => { + PrimitiveInstanceKind::NormalBorder { data_handle, ref cache_handles, .. } => { let prim_data = &ctx.data_stores.normal_border[data_handle]; let common_data = &prim_data.common; let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle); - let task_ids = &ctx.scratch.border_cache_handles[*render_task_ids]; + let cache_handles = &ctx.scratch.border_cache_handles[*cache_handles]; let specified_blend_mode = BlendMode::PremultipliedAlpha; let mut segment_data: SmallVec<[SegmentInstanceData; 8]> = SmallVec::new(); // Collect the segment instance data from each render // task for each valid edge / corner of the border. - for task_id in task_ids { - if let Some((uv_rect_address, texture)) = render_tasks.resolve_location(*task_id, gpu_cache) { - segment_data.push( - SegmentInstanceData { - textures: TextureSet::prim_textured(texture), - specific_resource_address: uv_rect_address.as_int(), - } - ); - } + for handle in cache_handles { + let rt_cache_entry = ctx.resource_cache + .get_cached_render_task(handle); + let cache_item = ctx.resource_cache + .get_texture_cache_item(&rt_cache_entry.handle); + segment_data.push( + SegmentInstanceData { + textures: BatchTextures::color(cache_item.texture_id), + specific_resource_address: cache_item.uv_rect_handle.as_int(gpu_cache), + } + ); } - // TODO: it would be less error-prone to get this info from the texture cache. - let image_buffer_kind = ImageBufferKind::Texture2D; - let non_segmented_blend_mode = if !common_data.opacity.is_opaque || prim_info.clip_task_index != ClipTaskIndex::INVALID || transform_kind == TransformedRectKind::Complex @@ -1178,7 +958,7 @@ impl BatchBuilder { }; let batch_params = BrushBatchParameters::instanced( - BrushBatchKind::Image(image_buffer_kind), + BrushBatchKind::Image(ImageBufferKind::Texture2DArray), ImageBrushData { color_mode: ShaderColorMode::Image, alpha_type: AlphaType::PremultipliedAlpha, @@ -1205,11 +985,11 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } PrimitiveInstanceKind::TextRun { data_handle, run_index, .. } => { @@ -1242,11 +1022,12 @@ impl BatchBuilder { }; let glyph_keys = &ctx.scratch.glyph_keys[run.glyph_keys_range]; + let raster_scale = run.raster_space.local_scale().unwrap_or(1.0).max(0.001); let prim_header_index = prim_headers.push( &prim_header, z_id, [ - (run.raster_scale * 65535.0).round() as i32, + (raster_scale * 65535.0).round() as i32, 0, 0, 0, @@ -1257,29 +1038,28 @@ impl BatchBuilder { ); let batchers = &mut self.batchers; - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - - // The run.used_font.clone() is here instead of instead of inline in the `fetch_glyph` - // function call to work around a miscompilation. - // https://github.com/rust-lang/rust/issues/80111 - let font = run.used_font.clone(); ctx.resource_cache.fetch_glyphs( - font, + run.used_font.clone(), &glyph_keys, &mut self.glyph_fetch_buffer, gpu_cache, - |texture_id, glyph_format, glyphs| { + |texture_id, mut glyph_format, glyphs| { debug_assert_ne!(texture_id, TextureSource::Invalid); + // Ignore color and only sample alpha when shadowing. + if run.shadow { + glyph_format = glyph_format.ignore_color(); + } + let subpx_dir = subpx_dir.limit_by(glyph_format); - let textures = BatchTextures::prim_textured( - texture_id, - clip_mask_texture_id, - ); + let textures = BatchTextures { + colors: [ + texture_id, + TextureSource::Invalid, + TextureSource::Invalid, + ], + }; let kind = BatchKind::TextRun(glyph_format); @@ -1304,136 +1084,42 @@ impl BatchBuilder { } } GlyphFormat::Alpha | - GlyphFormat::TransformedAlpha | - GlyphFormat::Bitmap => { + GlyphFormat::TransformedAlpha => { ( BlendMode::PremultipliedAlpha, ShaderColorMode::Alpha, ) } + GlyphFormat::Bitmap => { + ( + BlendMode::PremultipliedAlpha, + ShaderColorMode::Bitmap, + ) + } GlyphFormat::ColorBitmap => { ( BlendMode::PremultipliedAlpha, - if run.shadow { - // Ignore color and only sample alpha when shadowing. - ShaderColorMode::BitmapShadow - } else { - ShaderColorMode::ColorBitmap - }, + ShaderColorMode::ColorBitmap, ) } }; - // Calculate a tighter bounding rect of just the glyphs passed to this - // callback from request_glyphs(), rather than using the bounds of the - // entire text run. This improves batching when glyphs are fragmented - // over multiple textures in the texture cache. - // This code is taken from the ps_text_run shader. - let tight_bounding_rect = { - let snap_bias = match subpx_dir { - SubpixelDirection::None => DeviceVector2D::new(0.5, 0.5), - SubpixelDirection::Horizontal => DeviceVector2D::new(0.125, 0.5), - SubpixelDirection::Vertical => DeviceVector2D::new(0.5, 0.125), - SubpixelDirection::Mixed => DeviceVector2D::new(0.125, 0.125), - }; - let text_offset = prim_header.local_rect.size.to_vector(); - - let pic_bounding_rect = if run.used_font.flags.contains(FontInstanceFlags::TRANSFORM_GLYPHS) { - let mut device_bounding_rect = DeviceRect::default(); - - let glyph_transform = ctx.spatial_tree.get_relative_transform( - prim_spatial_node_index, - root_spatial_node_index, - ).into_transform() - .with_destination::<WorldPixel>() - .then(&euclid::Transform3D::from_scale(ctx.global_device_pixel_scale)); - - let glyph_translation = DeviceVector2D::new(glyph_transform.m41, glyph_transform.m42); - - for glyph in glyphs { - let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.origin.to_vector(); - - let raster_glyph_offset = (glyph_transform.transform_point2d(glyph_offset).unwrap() + snap_bias).floor(); - let raster_text_offset = ( - glyph_transform.transform_vector2d(text_offset) + - glyph_translation + - DeviceVector2D::new(0.5, 0.5) - ).floor() - glyph_translation; - - let device_glyph_rect = DeviceRect::new( - glyph.offset + raster_glyph_offset.to_vector() + raster_text_offset, - glyph.size.to_f32(), - ); - - device_bounding_rect = device_bounding_rect.union(&device_glyph_rect); - } - - let map_device_to_surface: SpaceMapper<PicturePixel, DevicePixel> = SpaceMapper::new_with_target( - root_spatial_node_index, - surface_spatial_node_index, - device_bounding_rect, - ctx.spatial_tree, - ); - - match map_device_to_surface.unmap(&device_bounding_rect) { - Some(r) => r.intersection(&bounding_rect), - None => Some(*bounding_rect), - } - } else { - let mut local_bounding_rect = LayoutRect::default(); - - let glyph_raster_scale = run.raster_scale * ctx.global_device_pixel_scale.get(); - - for glyph in glyphs { - let glyph_offset = prim_data.glyphs[glyph.index_in_text_run as usize].point + prim_header.local_rect.origin.to_vector(); - let glyph_scale = LayoutToDeviceScale::new(glyph_raster_scale / glyph.scale); - let raster_glyph_offset = (glyph_offset * LayoutToDeviceScale::new(glyph_raster_scale) + snap_bias).floor() / glyph.scale; - let local_glyph_rect = LayoutRect::new( - (glyph.offset + raster_glyph_offset.to_vector()) / glyph_scale + text_offset, - glyph.size.to_f32() / glyph_scale, - ); - - local_bounding_rect = local_bounding_rect.union(&local_glyph_rect); - } - - let map_prim_to_surface: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target( - surface_spatial_node_index, - prim_spatial_node_index, - *bounding_rect, - ctx.spatial_tree, - ); - map_prim_to_surface.map(&local_bounding_rect) - }; - - let intersected = match pic_bounding_rect { - // The text run may have been clipped, for example if part of it is offscreen. - // So intersect our result with the original bounding rect. - Some(rect) => rect.intersection(bounding_rect).unwrap_or_else(PictureRect::zero), - // If space mapping went off the rails, fall back to the old behavior. - //TODO: consider skipping the glyph run completely in this case. - None => *bounding_rect, - }; - - intersected - }; - let key = BatchKey::new(kind, blend_mode, textures); for batcher in batchers.iter_mut() { - if batcher.should_draw(&batch_filter) { + if batcher.vis_mask.intersects(prim_vis_mask) { let render_task_address = batcher.render_task_address; let batch = batcher.alpha_batch_list.set_params_and_get_batch( key, - batch_features, - &tight_bounding_rect, + BatchFeatures::empty(), + bounding_rect, z_id, ); - batch.reserve(glyphs.len()); for glyph in glyphs { batch.push(base_instance.build( render_task_address, - clip_task_address, + clip_task_address.unwrap(), subpx_dir, glyph.index_in_text_run, glyph.uv_rect_address, @@ -1445,26 +1131,23 @@ impl BatchBuilder { }, ); } - PrimitiveInstanceKind::LineDecoration { data_handle, ref render_task, .. } => { + PrimitiveInstanceKind::LineDecoration { data_handle, ref cache_handle, .. } => { // The GPU cache data is stored in the template and reused across // frames and display lists. let common_data = &ctx.data_stores.line_decoration[data_handle].common; let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle); - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - - let (batch_kind, textures, prim_user_data, specific_resource_address) = match render_task { - Some(task_id) => { - let (uv_rect_address, texture) = render_tasks.resolve_location(*task_id, gpu_cache).unwrap(); - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, - ); + let (batch_kind, textures, prim_user_data, specific_resource_address) = match cache_handle { + Some(cache_handle) => { + let rt_cache_entry = ctx + .resource_cache + .get_cached_render_task(cache_handle); + let cache_item = ctx + .resource_cache + .get_texture_cache_item(&rt_cache_entry.handle); + let textures = BatchTextures::color(cache_item.texture_id); ( - BrushBatchKind::Image(texture.image_buffer_kind()), + BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, ImageBrushData { color_mode: ShaderColorMode::Image, @@ -1472,13 +1155,13 @@ impl BatchBuilder { raster_space: RasterizationSpace::Local, opacity: 1.0, }.encode(), - uv_rect_address.as_int(), + cache_item.uv_rect_handle.as_int(gpu_cache), ) } None => { ( BrushBatchKind::Solid, - BatchTextures::prim_untextured(clip_mask_texture_id), + BatchTextures::no_texture(), [get_shader_opacity(1.0), 0, 0, 0], 0, ) @@ -1523,11 +1206,11 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::all(), - clip_task_address, + clip_task_address.unwrap(), BrushFlags::PERSPECTIVE_INTERPOLATION, prim_header_index, specific_resource_address, - &batch_filter, + prim_vis_mask, ); } PrimitiveInstanceKind::Picture { pic_index, segment_instance_index, .. } => { @@ -1544,8 +1227,74 @@ impl BatchBuilder { match picture.context_3d { // Convert all children of the 3D hierarchy root into batches. - Picture3DContext::In { root_data: Some(_), .. } => { - unreachable!("bug: handled above"); + Picture3DContext::In { root_data: Some(ref list), .. } => { + for child in list { + let cluster = &picture.prim_list.clusters[child.anchor.cluster_index]; + let child_prim_instance = &cluster.prim_instances[child.anchor.instance_index]; + let child_prim_info = &ctx.scratch.prim_info[child_prim_instance.visibility_info.0 as usize]; + + let child_pic_index = match child_prim_instance.kind { + PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index, + _ => unreachable!(), + }; + let pic = &ctx.prim_store.pictures[child_pic_index.0]; + + // Get clip task, if set, for the picture primitive. + let child_clip_task_address = ctx.get_prim_clip_task_address( + child_prim_info.clip_task_index, + render_tasks, + ); + + let prim_header = PrimitiveHeader { + local_rect: pic.precise_local_rect, + local_clip_rect: child_prim_info.combined_local_clip_rect, + specific_prim_address: GpuCacheAddress::INVALID, + transform_id: transforms + .get_id( + child.spatial_node_index, + root_spatial_node_index, + ctx.spatial_tree, + ), + }; + + let raster_config = pic + .raster_config + .as_ref() + .expect("BUG: 3d primitive was not assigned a surface"); + let (uv_rect_address, _) = render_tasks.resolve_surface( + ctx.surfaces[raster_config.surface_index.0] + .render_tasks + .expect("BUG: no surface") + .root, + gpu_cache, + ); + + // Need a new z-id for each child preserve-3d context added + // by this inner loop. + let z_id = z_generator.next(); + + let prim_header_index = prim_headers.push(&prim_header, z_id, [ + uv_rect_address.as_int(), + if raster_config.establishes_raster_root { 1 } else { 0 }, + 0, + child_clip_task_address.unwrap().0 as i32, + ]); + + let key = BatchKey::new( + BatchKind::SplitComposite, + BlendMode::PremultipliedAlpha, + BatchTextures::no_texture(), + ); + + self.add_split_composite_instance_to_batches( + key, + &child_prim_info.clip_chain.pic_clip_rect, + z_id, + prim_header_index, + child.gpu_address, + child_prim_info.visibility_mask, + ); + } } // Ignore the 3D pictures that are not in the root of preserve-3D // hierarchy, since we process them with the root. @@ -1565,42 +1314,49 @@ impl BatchBuilder { }; let surface = &ctx.surfaces[raster_config.surface_index.0]; - - let mut is_opaque = prim_info.clip_task_index == ClipTaskIndex::INVALID - && surface.opaque_rect.contains_rect(&surface.rect) - && transform_kind == TransformedRectKind::AxisAligned; - - let pic_task_id = picture.primary_render_task_id.unwrap(); + let surface_task = surface.render_tasks.map(|s| s.root); match raster_config.composite_mode { PictureCompositeMode::TileCache { .. } => { - // TODO(gw): For now, TileCache is still a composite mode, even though - // it will only exist as a top level primitive and never - // be encountered during batching. Consider making TileCache - // a standalone type, not a picture. + // Tile cache instances are added to the composite config, rather than + // directly added to batches. This allows them to be drawn with various + // present modes during render, such as partial present etc. + let tile_cache = picture.tile_cache.as_ref().unwrap(); + let map_local_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + tile_cache.spatial_node_index, + ctx.screen_world_rect, + ctx.spatial_tree, + ); + // TODO(gw): As a follow up to the valid_rect work, see why we use + // prim_info.combined_local_clip_rect here instead of the + // local_clip_rect built in the TileCacheInstance. Perhaps + // these can be unified or are different for a good reason? + let world_clip_rect = map_local_to_world + .map(&prim_info.combined_local_clip_rect) + .expect("bug: unable to map clip rect"); + let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round(); + + composite_state.push_surface( + tile_cache, + device_clip_rect, + ctx.global_device_pixel_scale, + ctx.resource_cache, + gpu_cache, + deferred_resolves, + ); } PictureCompositeMode::Filter(ref filter) => { assert!(filter.is_visible()); match filter { Filter::Blur(..) => { - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - let kind = BatchKind::Brush( - BrushBatchKind::Image(ImageBufferKind::Texture2D) + BrushBatchKind::Image(ImageBufferKind::Texture2DArray) ); - - let (uv_rect_address, texture) = render_tasks.resolve_location( - pic_task_id, + let (uv_rect_address, textures) = render_tasks.resolve_surface( + surface_task.expect("bug: surface must be allocated by now"), gpu_cache, - ).unwrap(); - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, ); - let key = BatchKey::new( kind, non_segmented_blend_mode, @@ -1624,24 +1380,19 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::empty(), - clip_task_address, + clip_task_address.unwrap(), brush_flags, prim_header_index, uv_rect_address.as_int(), - &batch_filter, + prim_vis_mask, ); } Filter::DropShadows(shadows) => { - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - // Draw an instance per shadow first, following by the content. // The shadows and the content get drawn as a brush image. let kind = BatchKind::Brush( - BrushBatchKind::Image(ImageBufferKind::Texture2D), + BrushBatchKind::Image(ImageBufferKind::Texture2DArray), ); // Gets the saved render task ID of the content, which is @@ -1649,37 +1400,35 @@ impl BatchBuilder { let secondary_id = picture.secondary_render_task_id.expect("no secondary!?"); let content_source = { let secondary_task = &render_tasks[secondary_id]; - let texture_id = secondary_task.get_target_texture(); - TextureSource::TextureCache( - texture_id, - Swizzle::default(), - ) + let saved_index = secondary_task.saved_index.expect("no saved index!?"); + debug_assert_ne!(saved_index, SavedTargetIndex::PENDING); + TextureSource::RenderTaskCache(saved_index, Swizzle::default()) }; - // Retrieve the UV rect addresses for shadow/content. - let (shadow_uv_rect_address, shadow_texture) = render_tasks.resolve_location( - pic_task_id, - gpu_cache, - ).unwrap(); - let shadow_textures = BatchTextures::prim_textured( - shadow_texture, - clip_mask_texture_id, - ); - - let content_uv_rect_address = render_tasks[secondary_id] - .get_texture_address(gpu_cache) - .as_int(); - // Build BatchTextures for shadow/content - let content_textures = BatchTextures::prim_textured( - content_source, - clip_mask_texture_id, - ); + let shadow_textures = BatchTextures::render_target_cache(); + let content_textures = BatchTextures { + colors: [ + content_source, + TextureSource::Invalid, + TextureSource::Invalid, + ], + }; // Build batch keys for shadow/content let shadow_key = BatchKey::new(kind, non_segmented_blend_mode, shadow_textures); let content_key = BatchKey::new(kind, non_segmented_blend_mode, content_textures); + // Retrieve the UV rect addresses for shadow/content. + let cache_task_id = surface_task + .expect("bug: surface must be allocated by now"); + let shadow_uv_rect_address = render_tasks[cache_task_id] + .get_texture_address(gpu_cache) + .as_int(); + let content_uv_rect_address = render_tasks[secondary_id] + .get_texture_address(gpu_cache) + .as_int(); + for (shadow, shadow_gpu_data) in shadows.iter().zip(picture.extra_gpu_data_handles.iter()) { // Get the GPU cache address of the extra data handle. let shadow_prim_address = gpu_cache.get_address(shadow_gpu_data); @@ -1710,11 +1459,11 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::empty(), - clip_task_address, + clip_task_address.unwrap(), brush_flags, shadow_prim_header_index, - shadow_uv_rect_address.as_int(), - &batch_filter, + shadow_uv_rect_address, + prim_vis_mask, ); } let z_id_content = z_generator.next(); @@ -1737,31 +1486,21 @@ impl BatchBuilder { z_id_content, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::empty(), - clip_task_address, + clip_task_address.unwrap(), brush_flags, content_prim_header_index, content_uv_rect_address, - &batch_filter, + prim_vis_mask, ); } Filter::Opacity(_, amount) => { - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - let amount = (amount * 65536.0) as i32; - let (uv_rect_address, texture) = render_tasks.resolve_location( - pic_task_id, + let (uv_rect_address, textures) = render_tasks.resolve_surface( + surface_task.expect("bug: surface must be allocated by now"), gpu_cache, - ).unwrap(); - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, ); - let key = BatchKey::new( BatchKind::Brush(BrushBatchKind::Opacity), BlendMode::PremultipliedAlpha, @@ -1782,19 +1521,14 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::empty(), - clip_task_address, + clip_task_address.unwrap(), brush_flags, prim_header_index, 0, - &batch_filter, + prim_vis_mask, ); } _ => { - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - // Must be kept in sync with brush_blend.glsl let filter_mode = filter.as_int(); @@ -1826,30 +1560,14 @@ impl BatchBuilder { Filter::Opacity(..) => unreachable!(), }; - // Other filters that may introduce opacity are handled via different - // paths. - if let Filter::ColorMatrix(..) = filter { - is_opaque = false; - } - - let (uv_rect_address, texture) = render_tasks.resolve_location( - pic_task_id, + let (uv_rect_address, textures) = render_tasks.resolve_surface( + surface_task.expect("bug: surface must be allocated by now"), gpu_cache, - ).unwrap(); - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, ); - let blend_mode = if is_opaque { - BlendMode::None - } else { - BlendMode::PremultipliedAlpha - }; - let key = BatchKey::new( BatchKind::Brush(BrushBatchKind::Blend), - blend_mode, + BlendMode::PremultipliedAlpha, textures, ); @@ -1867,11 +1585,11 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::empty(), - clip_task_address, + clip_task_address.unwrap(), brush_flags, prim_header_index, 0, - &batch_filter, + prim_vis_mask, ); } } @@ -1889,18 +1607,9 @@ impl BatchBuilder { let user_data = filter_data.gpu_cache_handle.as_int(gpu_cache); - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - - let (uv_rect_address, texture) = render_tasks.resolve_location( - pic_task_id, + let (uv_rect_address, textures) = render_tasks.resolve_surface( + surface_task.expect("bug: surface must be allocated by now"), gpu_cache, - ).unwrap(); - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, ); let key = BatchKey::new( @@ -1923,56 +1632,32 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::empty(), - clip_task_address, + clip_task_address.unwrap(), brush_flags, prim_header_index, 0, - &batch_filter, + prim_vis_mask, ); } - PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode( - mode, - ctx.use_advanced_blending, - !ctx.break_advanced_blend_batches, - ctx.use_dual_source_blending, - ).is_some() => { - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - - let (uv_rect_address, texture) = render_tasks.resolve_location( - pic_task_id, + PictureCompositeMode::MixBlend(mode) if ctx.use_advanced_blending => { + let (uv_rect_address, textures) = render_tasks.resolve_surface( + surface_task.expect("bug: surface must be allocated by now"), gpu_cache, - ).unwrap(); - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, ); - - let key = BatchKey::new( BatchKind::Brush( - BrushBatchKind::Image(ImageBufferKind::Texture2D), + BrushBatchKind::Image(ImageBufferKind::Texture2DArray), ), - BlendMode::from_mix_blend_mode( - mode, - ctx.use_advanced_blending, - !ctx.break_advanced_blend_batches, - ctx.use_dual_source_blending, - ).unwrap(), + BlendMode::Advanced(mode), textures, ); let prim_header_index = prim_headers.push( &prim_header, z_id, ImageBrushData { - color_mode: match key.blend_mode { - BlendMode::MultiplyDualSource => ShaderColorMode::MultiplyDualSource, - _ => ShaderColorMode::Image, - }, + color_mode: ShaderColorMode::Image, alpha_type: AlphaType::PremultipliedAlpha, - raster_space: RasterizationSpace::Screen, + raster_space: RasterizationSpace::Local, opacity: 1.0, }.encode(), ); @@ -1984,107 +1669,73 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::empty(), - clip_task_address, + clip_task_address.unwrap(), brush_flags, prim_header_index, uv_rect_address.as_int(), - &batch_filter, + prim_vis_mask, ); } PictureCompositeMode::MixBlend(mode) => { - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); + let cache_task_id = surface_task.expect("bug: surface must be allocated by now"); let backdrop_id = picture.secondary_render_task_id.expect("no backdrop!?"); - let color0 = render_tasks[backdrop_id].get_target_texture(); - let color1 = render_tasks[pic_task_id].get_target_texture(); - - // Create a separate brush instance for each batcher. For most cases, - // there is only one batcher. However, in the case of drawing onto - // a picture cache, there is one batcher per tile. Although not - // currently used, the implementation of mix-blend-mode now supports - // doing partial readbacks per-tile. In future, this will be enabled - // and allow mix-blends to operate on picture cache surfaces without - // a separate isolated intermediate surface. - - for batcher in &mut self.batchers { - if batcher.should_draw(&batch_filter) { - let render_task_address = batcher.render_task_address; - - let batch_key = BatchKey::new( - BatchKind::Brush( - BrushBatchKind::MixBlend { - task_id: batcher.render_task_id, - backdrop_id, - }, - ), - BlendMode::PremultipliedAlpha, - BatchTextures { - input: TextureSet { - colors: [ - TextureSource::TextureCache( - color0, - Swizzle::default(), - ), - TextureSource::TextureCache( - color1, - Swizzle::default(), - ), - TextureSource::Invalid, - ], - }, - clip_mask: clip_mask_texture_id, - }, - ); - let src_uv_address = render_tasks[pic_task_id].get_texture_address(gpu_cache); - let readback_uv_address = render_tasks[backdrop_id].get_texture_address(gpu_cache); - let prim_header_index = prim_headers.push(&prim_header, z_id, [ - mode as u32 as i32, - readback_uv_address.as_int(), - src_uv_address.as_int(), - 0, - ]); + // TODO(gw): For now, mix-blend is not supported as a picture + // caching root, so we can safely assume there is + // only a single batcher present. + assert_eq!(self.batchers.len(), 1); - let instance = BrushInstance { - segment_index: INVALID_SEGMENT_INDEX, - edge_flags: EdgeAaSegmentMask::empty(), - clip_task_address, - render_task_address, - brush_flags, - prim_header_index, - resource_address: 0, - }; + let key = BatchKey::new( + BatchKind::Brush( + BrushBatchKind::MixBlend { + task_id: self.batchers[0].render_task_id, + source_id: cache_task_id, + backdrop_id, + }, + ), + BlendMode::PremultipliedAlpha, + BatchTextures::no_texture(), + ); + let backdrop_task_address = render_tasks.get_task_address(backdrop_id); + let source_task_address = render_tasks.get_task_address(cache_task_id); + let prim_header_index = prim_headers.push(&prim_header, z_id, [ + mode as u32 as i32, + backdrop_task_address.0 as i32, + source_task_address.0 as i32, + 0, + ]); - batcher.push_single_instance( - batch_key, - batch_features, - bounding_rect, - z_id, - PrimitiveInstanceData::from(instance), - ); - } - } + self.add_brush_instance_to_batches( + key, + batch_features, + bounding_rect, + z_id, + INVALID_SEGMENT_INDEX, + EdgeAaSegmentMask::empty(), + clip_task_address.unwrap(), + brush_flags, + prim_header_index, + 0, + prim_vis_mask, + ); } PictureCompositeMode::Blit(_) => { - let uv_rect_address = render_tasks[pic_task_id] + let cache_task_id = surface_task.expect("bug: surface must be allocated by now"); + let uv_rect_address = render_tasks[cache_task_id] .get_texture_address(gpu_cache) .as_int(); - let cache_render_task = &render_tasks[pic_task_id]; - let texture_id = cache_render_task.get_target_texture(); - let textures = TextureSet { - colors: [ - TextureSource::TextureCache( - texture_id, - Swizzle::default(), - ), - TextureSource::Invalid, - TextureSource::Invalid, - ], + let textures = match render_tasks[cache_task_id].saved_index { + Some(saved_index) => BatchTextures { + colors: [ + TextureSource::RenderTaskCache(saved_index, Swizzle::default()), + TextureSource::PrevPassAlpha, + TextureSource::Invalid, + ] + }, + None => BatchTextures::render_target_cache(), }; let batch_params = BrushBatchParameters::shared( - BrushBatchKind::Image(ImageBufferKind::Texture2D), + BrushBatchKind::Image(ImageBufferKind::Texture2DArray), textures, ImageBrushData { color_mode: ShaderColorMode::Image, @@ -2120,11 +1771,14 @@ impl BatchBuilder { batch_params.prim_user_data, ); - let (opacity, specified_blend_mode) = if is_opaque { - (PrimitiveOpacity::opaque(), BlendMode::None) - } else { - (PrimitiveOpacity::translucent(), BlendMode::PremultipliedAlpha) - }; + // TODO(gw): As before, all pictures that get blitted are assumed + // to have alpha. However, we could determine (at least for + // simple, common cases) if the picture content is opaque. + // That would allow inner segments of pictures to be drawn + // with blend disabled, which is a big performance win on + // integrated GPUs. + let opacity = PrimitiveOpacity::translucent(); + let specified_blend_mode = BlendMode::PremultipliedAlpha; self.add_segmented_prim_to_batch( segments, @@ -2136,29 +1790,20 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } PictureCompositeMode::SvgFilter(..) => { - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - let kind = BatchKind::Brush( - BrushBatchKind::Image(ImageBufferKind::Texture2D) + BrushBatchKind::Image(ImageBufferKind::Texture2DArray) ); - let (uv_rect_address, texture) = render_tasks.resolve_location( - pic_task_id, + let (uv_rect_address, textures) = render_tasks.resolve_surface( + surface_task.expect("bug: surface must be allocated by now"), gpu_cache, - ).unwrap(); - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, ); let key = BatchKey::new( kind, @@ -2183,17 +1828,31 @@ impl BatchBuilder { z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::empty(), - clip_task_address, + clip_task_address.unwrap(), brush_flags, prim_header_index, uv_rect_address.as_int(), - &batch_filter, + prim_vis_mask, ); } } } None => { - unreachable!(); + // If this picture is being drawn into an existing target (i.e. with + // no composition operation), recurse and add to the current batch list. + self.add_pic_to_batch( + picture, + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + prim_headers, + transforms, + root_spatial_node_index, + surface_spatial_node_index, + z_generator, + composite_state, + ); } } } @@ -2202,14 +1861,17 @@ impl BatchBuilder { let common_data = &prim_data.common; let border_data = &prim_data.kind; - let (uv_rect_address, texture) = match render_tasks.resolve_location(border_data.src_color, gpu_cache) { - Some(src) => src, - None => { - return; - } - }; + let cache_item = resolve_image( + border_data.request, + ctx.resource_cache, + gpu_cache, + deferred_resolves, + ); + if cache_item.texture_id == TextureSource::Invalid { + return; + } - let textures = TextureSet::prim_textured(texture); + let textures = BatchTextures::color(cache_item.texture_id); let prim_cache_address = gpu_cache.get_address(&common_data.gpu_cache_handle); let specified_blend_mode = BlendMode::PremultipliedAlpha; let non_segmented_blend_mode = if !common_data.opacity.is_opaque || @@ -2229,7 +1891,7 @@ impl BatchBuilder { }; let batch_params = BrushBatchParameters::shared( - BrushBatchKind::Image(texture.image_buffer_kind()), + BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), textures, ImageBrushData { color_mode: ShaderColorMode::Image, @@ -2237,7 +1899,7 @@ impl BatchBuilder { raster_space: RasterizationSpace::Local, opacity: 1.0, }.encode(), - uv_rect_address.as_int(), + cache_item.uv_rect_handle.as_int(gpu_cache), ); let prim_header_index = prim_headers.push( @@ -2256,18 +1918,22 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } - PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, .. } => { + PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, .. } => { let prim_data = &ctx.data_stores.prim[data_handle]; let specified_blend_mode = BlendMode::PremultipliedAlpha; + let opacity_binding = ctx.prim_store.get_opacity_binding(opacity_binding_index); - let non_segmented_blend_mode = if !prim_data.opacity.is_opaque || + let opacity = PrimitiveOpacity::from_alpha(opacity_binding); + let opacity = opacity.combine(prim_data.opacity); + + let non_segmented_blend_mode = if !opacity.is_opaque || prim_info.clip_task_index != ClipTaskIndex::INVALID || transform_kind == TransformedRectKind::Complex { @@ -2278,8 +1944,8 @@ impl BatchBuilder { let batch_params = BrushBatchParameters::shared( BrushBatchKind::Solid, - TextureSet::UNTEXTURED, - [get_shader_opacity(1.0), 0, 0, 0], + BatchTextures::no_texture(), + [get_shader_opacity(opacity_binding), 0, 0, 0], 0, ); @@ -2306,7 +1972,7 @@ impl BatchBuilder { self.add_segmented_prim_to_batch( segments, - prim_data.opacity, + opacity, &batch_params, specified_blend_mode, non_segmented_blend_mode, @@ -2314,45 +1980,63 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, is_compositor_surface, .. } => { - debug_assert!(!is_compositor_surface); + if is_compositor_surface { + self.emit_placeholder(prim_rect, + prim_info, + z_id, + transform_id, + batch_features, + ctx, + gpu_cache, + render_tasks, + prim_headers); + return; + } let yuv_image_data = &ctx.data_stores.yuv_image[data_handle].kind; - let mut textures = TextureSet::UNTEXTURED; + let mut textures = BatchTextures::no_texture(); let mut uv_rect_addresses = [0; 3]; //yuv channel let channel_count = yuv_image_data.format.get_plane_num(); debug_assert!(channel_count <= 3); for channel in 0 .. channel_count { + let image_key = yuv_image_data.yuv_key[channel]; - let src_channel = render_tasks.resolve_location(yuv_image_data.src_yuv[channel], gpu_cache); + let cache_item = resolve_image( + ImageRequest { + key: image_key, + rendering: yuv_image_data.image_rendering, + tile: None, + }, + ctx.resource_cache, + gpu_cache, + deferred_resolves, + ); - let (uv_rect_address, texture_source) = match src_channel { - Some(src) => src, - None => { - warn!("Warnings: skip a PrimitiveKind::YuvImage"); - return; - } - }; + if cache_item.texture_id == TextureSource::Invalid { + warn!("Warnings: skip a PrimitiveKind::YuvImage"); + return; + } - textures.colors[channel] = texture_source; - uv_rect_addresses[channel] = uv_rect_address.as_int(); + textures.colors[channel] = cache_item.texture_id; + uv_rect_addresses[channel] = cache_item.uv_rect_handle.as_int(gpu_cache); } // All yuv textures should be the same type. - let buffer_kind = textures.colors[0].image_buffer_kind(); + let buffer_kind = get_buffer_kind(textures.colors[0]); assert!( textures.colors[1 .. yuv_image_data.format.get_plane_num()] .iter() - .all(|&tid| buffer_kind == tid.image_buffer_kind()) + .all(|&tid| buffer_kind == get_buffer_kind(tid)) ); let kind = BrushBatchKind::YuvImage( @@ -2419,48 +2103,76 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } PrimitiveInstanceKind::Image { data_handle, image_instance_index, is_compositor_surface, .. } => { - debug_assert!(!is_compositor_surface); - + if is_compositor_surface { + self.emit_placeholder(prim_rect, + prim_info, + z_id, + transform_id, + batch_features, + ctx, + gpu_cache, + render_tasks, + prim_headers); + return; + } let image_data = &ctx.data_stores.image[data_handle].kind; let common_data = &ctx.data_stores.image[data_handle].common; let image_instance = &ctx.prim_store.images[image_instance_index]; + let opacity_binding = ctx.prim_store.get_opacity_binding(image_instance.opacity_binding_index); let specified_blend_mode = match image_data.alpha_type { AlphaType::PremultipliedAlpha => BlendMode::PremultipliedAlpha, AlphaType::Alpha => BlendMode::Alpha, }; + let request = ImageRequest { + key: image_data.key, + rendering: image_data.image_rendering, + tile: None, + }; let prim_user_data = ImageBrushData { color_mode: ShaderColorMode::Image, alpha_type: image_data.alpha_type, raster_space: RasterizationSpace::Local, - opacity: 1.0, + opacity: opacity_binding, }.encode(); if image_instance.visible_tiles.is_empty() { - if cfg!(debug_assertions) { - match ctx.resource_cache.get_image_properties(image_data.key) { - Some(ImageProperties { tiling: None, .. }) | None => (), - other => panic!("Non-tiled image with no visible images detected! Properties {:?}", other), + let cache_item = match image_data.source { + ImageSource::Default => { + resolve_image( + request, + ctx.resource_cache, + gpu_cache, + deferred_resolves, + ) } + ImageSource::Cache { ref handle, .. } => { + let rt_handle = handle + .as_ref() + .expect("bug: render task handle not allocated"); + let rt_cache_entry = ctx.resource_cache + .get_cached_render_task(rt_handle); + ctx.resource_cache.get_texture_cache_item(&rt_cache_entry.handle) + } + }; + + if cache_item.texture_id == TextureSource::Invalid { + return; } - let src_color = render_tasks.resolve_location(image_instance.src_color, gpu_cache); + let textures = BatchTextures::color(cache_item.texture_id); - let (uv_rect_address, texture_source) = match src_color { - Some(src) => src, - None => { - return; - } - }; + let opacity = PrimitiveOpacity::from_alpha(opacity_binding); + let opacity = opacity.combine(common_data.opacity); - let non_segmented_blend_mode = if !common_data.opacity.is_opaque || + let non_segmented_blend_mode = if !opacity.is_opaque || prim_info.clip_task_index != ClipTaskIndex::INVALID || transform_kind == TransformedRectKind::Complex { @@ -2470,10 +2182,10 @@ impl BatchBuilder { }; let batch_params = BrushBatchParameters::shared( - BrushBatchKind::Image(texture_source.image_buffer_kind()), - TextureSet::prim_textured(texture_source), + BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), + textures, prim_user_data, - uv_rect_address.as_int(), + cache_item.uv_rect_handle.as_int(gpu_cache), ); debug_assert_ne!(image_instance.segment_instance_index, SegmentInstanceIndex::INVALID); @@ -2500,7 +2212,7 @@ impl BatchBuilder { self.add_segmented_prim_to_batch( segments, - common_data.opacity, + opacity, &batch_params, specified_blend_mode, non_segmented_blend_mode, @@ -2508,23 +2220,18 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } else { const VECS_PER_SPECIFIC_BRUSH: usize = 3; let max_tiles_per_header = (MAX_VERTEX_TEXTURE_WIDTH - VECS_PER_SPECIFIC_BRUSH) / VECS_PER_SEGMENT; - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - // use temporary block storage since we don't know the number of visible tiles beforehand - let mut gpu_blocks = Vec::<GpuBlockData>::with_capacity(3 + max_tiles_per_header * 2); + let mut gpu_blocks = Vec::<GpuBlockData>::new(); for chunk in image_instance.visible_tiles.chunks(max_tiles_per_header) { gpu_blocks.clear(); gpu_blocks.push(PremultipliedColorF::WHITE.into()); //color @@ -2547,42 +2254,37 @@ impl BatchBuilder { let prim_header_index = prim_headers.push(&prim_header, z_id, prim_user_data); for (i, tile) in chunk.iter().enumerate() { - let (uv_rect_address, texture) = match render_tasks.resolve_location(tile.src_color, gpu_cache) { - Some(result) => result, - None => { - return; - } - }; - - let textures = BatchTextures::prim_textured( - texture, - clip_mask_texture_id, - ); - - let batch_key = BatchKey { - blend_mode: specified_blend_mode, - kind: BatchKind::Brush(BrushBatchKind::Image(texture.image_buffer_kind())), - textures, - }; - - self.add_brush_instance_to_batches( - batch_key, - batch_features, - bounding_rect, - z_id, - i as i32, - tile.edge_flags, - clip_task_address, - BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION, - prim_header_index, - uv_rect_address.as_int(), - &batch_filter, - ); + if let Some((batch_kind, textures, uv_rect_address)) = get_image_tile_params( + ctx.resource_cache, + gpu_cache, + deferred_resolves, + request.with_tile(tile.tile_offset), + ) { + let batch_key = BatchKey { + blend_mode: specified_blend_mode, + kind: BatchKind::Brush(batch_kind), + textures, + }; + self.add_brush_instance_to_batches( + batch_key, + batch_features, + bounding_rect, + z_id, + i as i32, + tile.edge_flags, + clip_task_address.unwrap(), + BrushFlags::SEGMENT_RELATIVE | BrushFlags::PERSPECTIVE_INTERPOLATION, + prim_header_index, + uv_rect_address.as_int(), + prim_vis_mask, + ); + } } } } } - PrimitiveInstanceKind::LinearGradient { data_handle, ref visible_tiles_range, .. } => { + PrimitiveInstanceKind::LinearGradient { data_handle, gradient_index, .. } => { + let gradient = &ctx.prim_store.linear_gradients[gradient_index]; let prim_data = &ctx.data_stores.linear_grad[data_handle]; let specified_blend_mode = BlendMode::PremultipliedAlpha; @@ -2602,135 +2304,83 @@ impl BatchBuilder { BlendMode::None }; - let user_data = [prim_data.stops_handle.as_int(gpu_cache), 0, 0, 0]; - - if visible_tiles_range.is_empty() { - let batch_params = BrushBatchParameters::shared( - BrushBatchKind::LinearGradient, - TextureSet::UNTEXTURED, - user_data, - 0, - ); - - prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle); + if !gradient.cache_segments.is_empty() { - let prim_header_index = prim_headers.push(&prim_header, z_id, user_data); + for segment in &gradient.cache_segments { + let ref cache_handle = segment.handle; + let rt_cache_entry = ctx.resource_cache + .get_cached_render_task(cache_handle); + let cache_item = ctx.resource_cache + .get_texture_cache_item(&rt_cache_entry.handle); - let segments = if prim_data.brush_segments.is_empty() { - None - } else { - Some(prim_data.brush_segments.as_slice()) - }; + if cache_item.texture_id == TextureSource::Invalid { + return; + } - self.add_segmented_prim_to_batch( - segments, - prim_data.opacity, - &batch_params, - specified_blend_mode, - non_segmented_blend_mode, - batch_features, - prim_header_index, - bounding_rect, - transform_kind, - z_id, - prim_info.clip_task_index, - &batch_filter, - ctx, - render_tasks, - ); - } else { - let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range]; + let textures = BatchTextures::color(cache_item.texture_id); + let batch_kind = BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)); + let prim_user_data = ImageBrushData { + color_mode: ShaderColorMode::Image, + alpha_type: AlphaType::PremultipliedAlpha, + raster_space: RasterizationSpace::Local, + opacity: 1.0, + }.encode(); + + let specific_resource_address = cache_item.uv_rect_handle.as_int(gpu_cache); + prim_header.specific_prim_address = gpu_cache.get_address(&ctx.globals.default_image_handle); + + let segment_local_clip_rect = match prim_header.local_clip_rect.intersection(&segment.local_rect) { + Some(rect) => rect, + None => { continue; } + }; - let (clip_task_address, clip_mask_texture_id) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); + let segment_prim_header = PrimitiveHeader { + local_rect: segment.local_rect, + local_clip_rect: segment_local_clip_rect, + specific_prim_address: prim_header.specific_prim_address, + transform_id: prim_header.transform_id, + }; - let key = BatchKey { - blend_mode: specified_blend_mode, - kind: BatchKind::Brush(BrushBatchKind::LinearGradient), - textures: BatchTextures::prim_untextured(clip_mask_texture_id), - }; + let prim_header_index = prim_headers.push( + &segment_prim_header, + z_id, + prim_user_data, + ); - for tile in visible_tiles { - let tile_prim_header = PrimitiveHeader { - specific_prim_address: gpu_cache.get_address(&tile.handle), - local_rect: tile.local_rect, - local_clip_rect: tile.local_clip_rect, - ..prim_header + let batch_key = BatchKey { + blend_mode: non_segmented_blend_mode, + kind: BatchKind::Brush(batch_kind), + textures, }; - let prim_header_index = prim_headers.push(&tile_prim_header, z_id, user_data); self.add_brush_instance_to_batches( - key, + batch_key, batch_features, bounding_rect, z_id, INVALID_SEGMENT_INDEX, EdgeAaSegmentMask::all(), - clip_task_address, + clip_task_address.unwrap(), BrushFlags::PERSPECTIVE_INTERPOLATION, prim_header_index, - 0, - &batch_filter, + specific_resource_address, + prim_vis_mask, ); } - } - } - PrimitiveInstanceKind::CachedLinearGradient { data_handle, ref visible_tiles_range, .. } => { - let prim_data = &ctx.data_stores.linear_grad[data_handle]; - let common_data = &prim_data.common; - let specified_blend_mode = BlendMode::PremultipliedAlpha; - - let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache); - - let (uv_rect_address, texture_source) = match src_color { - Some(src) => src, - None => { - return; - } - }; - - let textures = TextureSet::prim_textured(texture_source); - - let prim_header = PrimitiveHeader { - local_rect: prim_rect, - local_clip_rect: prim_info.combined_local_clip_rect, - specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle), - transform_id, - }; - - let prim_user_data = ImageBrushData { - color_mode: ShaderColorMode::Image, - alpha_type: AlphaType::PremultipliedAlpha, - raster_space: RasterizationSpace::Local, - opacity: 1.0, - }.encode(); - - let non_segmented_blend_mode = if !common_data.opacity.is_opaque || - prim_info.clip_task_index != ClipTaskIndex::INVALID || - transform_kind == TransformedRectKind::Complex - { - specified_blend_mode - } else { - BlendMode::None - }; - - let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind()); - - if visible_tiles_range.is_empty() { + } else if gradient.visible_tiles_range.is_empty() { let batch_params = BrushBatchParameters::shared( - batch_kind, - textures, - prim_user_data, - uv_rect_address.as_int(), + BrushBatchKind::LinearGradient, + BatchTextures::no_texture(), + [ + prim_data.stops_handle.as_int(gpu_cache), + 0, + 0, + 0, + ], + 0, ); - let segments = if prim_data.brush_segments.is_empty() { - None - } else { - Some(&prim_data.brush_segments[..]) - }; + prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle); let prim_header_index = prim_headers.push( &prim_header, @@ -2738,9 +2388,15 @@ impl BatchBuilder { batch_params.prim_user_data, ); + let segments = if prim_data.brush_segments.is_empty() { + None + } else { + Some(prim_data.brush_segments.as_slice()) + }; + self.add_segmented_prim_to_batch( segments, - common_data.opacity, + prim_data.opacity, &batch_params, specified_blend_mode, non_segmented_blend_mode, @@ -2748,108 +2404,64 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } else { - let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range]; - - let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - - let batch_key = BatchKey { - blend_mode: non_segmented_blend_mode, - kind: BatchKind::Brush(batch_kind), - textures: BatchTextures { - input: textures, - clip_mask, - }, - }; - - for tile in visible_tiles { - let tile_prim_header = PrimitiveHeader { - local_rect: tile.local_rect, - local_clip_rect: tile.local_clip_rect, - ..prim_header - }; - let prim_header_index = prim_headers.push(&tile_prim_header, z_id, prim_user_data); + let visible_tiles = &ctx.scratch.gradient_tiles[gradient.visible_tiles_range]; - self.add_brush_instance_to_batches( - batch_key, - batch_features, - bounding_rect, - z_id, - INVALID_SEGMENT_INDEX, - EdgeAaSegmentMask::all(), - clip_task_address, - BrushFlags::PERSPECTIVE_INTERPOLATION, - prim_header_index, - uv_rect_address.as_int(), - &batch_filter, - ); - } + self.add_gradient_tiles( + visible_tiles, + &prim_data.stops_handle, + BrushBatchKind::LinearGradient, + specified_blend_mode, + bounding_rect, + clip_task_address.unwrap(), + gpu_cache, + &prim_header, + prim_headers, + z_id, + prim_vis_mask, + ); } } PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => { let prim_data = &ctx.data_stores.radial_grad[data_handle]; - let common_data = &prim_data.common; let specified_blend_mode = BlendMode::PremultipliedAlpha; - let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache); - - let (uv_rect_address, texture_source) = match src_color { - Some(src) => src, - None => { - return; - } - }; - - let textures = TextureSet::prim_textured(texture_source); - - let prim_header = PrimitiveHeader { + let mut prim_header = PrimitiveHeader { local_rect: prim_rect, local_clip_rect: prim_info.combined_local_clip_rect, - specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle), + specific_prim_address: GpuCacheAddress::INVALID, transform_id, }; - let prim_user_data = ImageBrushData { - color_mode: ShaderColorMode::Image, - alpha_type: AlphaType::PremultipliedAlpha, - raster_space: RasterizationSpace::Local, - opacity: 1.0, - }.encode(); - - - let non_segmented_blend_mode = if !common_data.opacity.is_opaque || - prim_info.clip_task_index != ClipTaskIndex::INVALID || - transform_kind == TransformedRectKind::Complex - { - specified_blend_mode - } else { - BlendMode::None - }; - - let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind()); - if visible_tiles_range.is_empty() { + let non_segmented_blend_mode = if !prim_data.opacity.is_opaque || + prim_info.clip_task_index != ClipTaskIndex::INVALID || + transform_kind == TransformedRectKind::Complex + { + specified_blend_mode + } else { + BlendMode::None + }; + let batch_params = BrushBatchParameters::shared( - batch_kind, - textures, - prim_user_data, - uv_rect_address.as_int(), + BrushBatchKind::RadialGradient, + BatchTextures::no_texture(), + [ + prim_data.stops_handle.as_int(gpu_cache), + 0, + 0, + 0, + ], + 0, ); - let segments = if prim_data.brush_segments.is_empty() { - None - } else { - Some(&prim_data.brush_segments[..]) - }; + prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle); let prim_header_index = prim_headers.push( &prim_header, @@ -2857,9 +2469,15 @@ impl BatchBuilder { batch_params.prim_user_data, ); + let segments = if prim_data.brush_segments.is_empty() { + None + } else { + Some(prim_data.brush_segments.as_slice()) + }; + self.add_segmented_prim_to_batch( segments, - common_data.opacity, + prim_data.opacity, &batch_params, specified_blend_mode, non_segmented_blend_mode, @@ -2867,109 +2485,64 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } else { let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range]; - let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - - let batch_key = BatchKey { - blend_mode: non_segmented_blend_mode, - kind: BatchKind::Brush(batch_kind), - textures: BatchTextures { - input: textures, - clip_mask, - }, - }; - - for tile in visible_tiles { - let tile_prim_header = PrimitiveHeader { - local_rect: tile.local_rect, - local_clip_rect: tile.local_clip_rect, - ..prim_header - }; - let prim_header_index = prim_headers.push(&tile_prim_header, z_id, prim_user_data); - - self.add_brush_instance_to_batches( - batch_key, - batch_features, - bounding_rect, - z_id, - INVALID_SEGMENT_INDEX, - EdgeAaSegmentMask::all(), - clip_task_address, - BrushFlags::PERSPECTIVE_INTERPOLATION, - prim_header_index, - uv_rect_address.as_int(), - &batch_filter, - ); - } + self.add_gradient_tiles( + visible_tiles, + &prim_data.stops_handle, + BrushBatchKind::RadialGradient, + specified_blend_mode, + bounding_rect, + clip_task_address.unwrap(), + gpu_cache, + &prim_header, + prim_headers, + z_id, + prim_vis_mask, + ); } - } PrimitiveInstanceKind::ConicGradient { data_handle, ref visible_tiles_range, .. } => { let prim_data = &ctx.data_stores.conic_grad[data_handle]; - let common_data = &prim_data.common; let specified_blend_mode = BlendMode::PremultipliedAlpha; - let src_color = render_tasks.resolve_location(prim_data.src_color, gpu_cache); - - let (uv_rect_address, texture_source) = match src_color { - Some(src) => src, - None => { - return; - } - }; - - let textures = TextureSet::prim_textured(texture_source); - - let prim_header = PrimitiveHeader { + let mut prim_header = PrimitiveHeader { local_rect: prim_rect, local_clip_rect: prim_info.combined_local_clip_rect, - specific_prim_address: gpu_cache.get_address(&common_data.gpu_cache_handle), + specific_prim_address: GpuCacheAddress::INVALID, transform_id, }; - let prim_user_data = ImageBrushData { - color_mode: ShaderColorMode::Image, - alpha_type: AlphaType::PremultipliedAlpha, - raster_space: RasterizationSpace::Local, - opacity: 1.0, - }.encode(); - - - let non_segmented_blend_mode = if !common_data.opacity.is_opaque || - prim_info.clip_task_index != ClipTaskIndex::INVALID || - transform_kind == TransformedRectKind::Complex - { - specified_blend_mode - } else { - BlendMode::None - }; - - let batch_kind = BrushBatchKind::Image(texture_source.image_buffer_kind()); - if visible_tiles_range.is_empty() { + let non_segmented_blend_mode = if !prim_data.opacity.is_opaque || + prim_info.clip_task_index != ClipTaskIndex::INVALID || + transform_kind == TransformedRectKind::Complex + { + specified_blend_mode + } else { + BlendMode::None + }; + let batch_params = BrushBatchParameters::shared( - batch_kind, - textures, - prim_user_data, - uv_rect_address.as_int(), + BrushBatchKind::ConicGradient, + BatchTextures::no_texture(), + [ + prim_data.stops_handle.as_int(gpu_cache), + 0, + 0, + 0, + ], + 0, ); - let segments = if prim_data.brush_segments.is_empty() { - None - } else { - Some(&prim_data.brush_segments[..]) - }; + prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle); let prim_header_index = prim_headers.push( &prim_header, @@ -2977,9 +2550,15 @@ impl BatchBuilder { batch_params.prim_user_data, ); + let segments = if prim_data.brush_segments.is_empty() { + None + } else { + Some(prim_data.brush_segments.as_slice()) + }; + self.add_segmented_prim_to_batch( segments, - common_data.opacity, + prim_data.opacity, &batch_params, specified_blend_mode, non_segmented_blend_mode, @@ -2987,70 +2566,52 @@ impl BatchBuilder { prim_header_index, bounding_rect, transform_kind, + render_tasks, z_id, prim_info.clip_task_index, - &batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } else { let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range]; - let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture( - prim_info.clip_task_index, - render_tasks, - ).unwrap(); - - let batch_key = BatchKey { - blend_mode: non_segmented_blend_mode, - kind: BatchKind::Brush(batch_kind), - textures: BatchTextures { - input: textures, - clip_mask, - }, - }; - - for tile in visible_tiles { - let tile_prim_header = PrimitiveHeader { - local_rect: tile.local_rect, - local_clip_rect: tile.local_clip_rect, - ..prim_header - }; - let prim_header_index = prim_headers.push(&tile_prim_header, z_id, prim_user_data); - - self.add_brush_instance_to_batches( - batch_key, - batch_features, - bounding_rect, - z_id, - INVALID_SEGMENT_INDEX, - EdgeAaSegmentMask::all(), - clip_task_address, - BrushFlags::PERSPECTIVE_INTERPOLATION, - prim_header_index, - uv_rect_address.as_int(), - &batch_filter, - ); - } + self.add_gradient_tiles( + visible_tiles, + &prim_data.stops_handle, + BrushBatchKind::ConicGradient, + specified_blend_mode, + bounding_rect, + clip_task_address.unwrap(), + gpu_cache, + &prim_header, + prim_headers, + z_id, + prim_vis_mask, + ); } } PrimitiveInstanceKind::Backdrop { data_handle } => { let prim_data = &ctx.data_stores.backdrop[data_handle]; let backdrop_pic_index = prim_data.kind.pic_index; - - let backdrop_task_id = ctx.prim_store - .pictures[backdrop_pic_index.0] - .primary_render_task_id - .expect("backdrop surface should be resolved by now"); - - let (backdrop_uv_rect_address, texture) = render_tasks.resolve_location( - backdrop_task_id, - gpu_cache, - ).unwrap(); - let textures = BatchTextures::prim_textured(texture, TextureSource::Invalid); - + let backdrop_surface_index = ctx.prim_store.pictures[backdrop_pic_index.0] + .raster_config + .as_ref() + .expect("backdrop surface should be alloc by now") + .surface_index; + + let backdrop_task_id = ctx.surfaces[backdrop_surface_index.0] + .render_tasks + .as_ref() + .expect("backdrop task not available") + .root; + + let backdrop_uv_rect_address = render_tasks[backdrop_task_id] + .get_texture_address(gpu_cache) + .as_int(); + + let textures = BatchTextures::render_target_cache(); let batch_key = BatchKey::new( - BatchKind::Brush(BrushBatchKind::Image(ImageBufferKind::Texture2D)), + BatchKind::Brush(BrushBatchKind::Image(ImageBufferKind::Texture2DArray)), BlendMode::PremultipliedAlpha, textures, ); @@ -3085,8 +2646,8 @@ impl BatchBuilder { OPAQUE_TASK_ADDRESS, BrushFlags::empty(), prim_header_index, - backdrop_uv_rect_address.as_int(), - &batch_filter, + backdrop_uv_rect_address, + prim_vis_mask, ); } } @@ -3104,53 +2665,51 @@ impl BatchBuilder { features: BatchFeatures, bounding_rect: &PictureRect, transform_kind: TransformedRectKind, + render_tasks: &RenderTaskGraph, z_id: ZBufferId, prim_opacity: PrimitiveOpacity, clip_task_index: ClipTaskIndex, - batch_filter: &BatchFilter, + prim_vis_mask: PrimitiveVisibilityMask, ctx: &RenderTargetContext, - render_tasks: &RenderTaskGraph, ) { debug_assert!(clip_task_index != ClipTaskIndex::INVALID); // Get GPU address of clip task for this segment, or None if // the entire segment is clipped out. - if let Some((clip_task_address, clip_mask)) = ctx.get_clip_task_and_texture( + let clip_task_address = match ctx.get_clip_task_address( clip_task_index, segment_index, render_tasks, ) { - // If a got a valid (or OPAQUE) clip task address, add the segment. - let is_inner = segment.edge_flags.is_empty(); - let needs_blending = !prim_opacity.is_opaque || - clip_task_address != OPAQUE_TASK_ADDRESS || - (!is_inner && transform_kind == TransformedRectKind::Complex); - - let textures = BatchTextures { - input: segment_data.textures, - clip_mask, - }; + Some(clip_task_address) => clip_task_address, + None => return, + }; - let batch_key = BatchKey { - blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None }, - kind: BatchKind::Brush(batch_kind), - textures, - }; + // If a got a valid (or OPAQUE) clip task address, add the segment. + let is_inner = segment.edge_flags.is_empty(); + let needs_blending = !prim_opacity.is_opaque || + clip_task_address != OPAQUE_TASK_ADDRESS || + (!is_inner && transform_kind == TransformedRectKind::Complex); - self.add_brush_instance_to_batches( - batch_key, - features, - bounding_rect, - z_id, - segment_index, - segment.edge_flags, - clip_task_address, - BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags, - prim_header_index, - segment_data.specific_resource_address, - batch_filter, - ); - } + let batch_key = BatchKey { + blend_mode: if needs_blending { alpha_blend_mode } else { BlendMode::None }, + kind: BatchKind::Brush(batch_kind), + textures: segment_data.textures, + }; + + self.add_brush_instance_to_batches( + batch_key, + features, + bounding_rect, + z_id, + segment_index, + segment.edge_flags, + clip_task_address, + BrushFlags::PERSPECTIVE_INTERPOLATION | segment.brush_flags, + prim_header_index, + segment_data.specific_resource_address, + prim_vis_mask, + ); } /// Add any segment(s) from a brush to batches. @@ -3165,11 +2724,11 @@ impl BatchBuilder { prim_header_index: PrimitiveHeaderIndex, bounding_rect: &PictureRect, transform_kind: TransformedRectKind, + render_tasks: &RenderTaskGraph, z_id: ZBufferId, clip_task_index: ClipTaskIndex, - batch_filter: &BatchFilter, + prim_vis_mask: PrimitiveVisibilityMask, ctx: &RenderTargetContext, - render_tasks: &RenderTaskGraph, ) { match (brush_segments, ¶ms.segment_data) { (Some(ref brush_segments), SegmentDataKind::Instanced(ref segment_data)) => { @@ -3191,12 +2750,12 @@ impl BatchBuilder { features, bounding_rect, transform_kind, + render_tasks, z_id, prim_opacity, clip_task_index, - batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } } @@ -3217,35 +2776,27 @@ impl BatchBuilder { features, bounding_rect, transform_kind, + render_tasks, z_id, prim_opacity, clip_task_index, - batch_filter, + prim_vis_mask, ctx, - render_tasks, ); } } (None, SegmentDataKind::Shared(ref segment_data)) => { // No segments, and thus no per-segment instance data. // Note: the blend mode already takes opacity into account - - let (clip_task_address, clip_mask) = ctx.get_prim_clip_task_and_texture( - clip_task_index, - render_tasks, - ).unwrap(); - - let textures = BatchTextures { - input: segment_data.textures, - clip_mask, - }; - let batch_key = BatchKey { blend_mode: non_segmented_blend_mode, kind: BatchKind::Brush(params.batch_kind), - textures, + textures: segment_data.textures, }; - + let clip_task_address = ctx.get_prim_clip_task_address( + clip_task_index, + render_tasks, + ).unwrap(); self.add_brush_instance_to_batches( batch_key, features, @@ -3257,7 +2808,7 @@ impl BatchBuilder { BrushFlags::PERSPECTIVE_INTERPOLATION, prim_header_index, segment_data.specific_resource_address, - batch_filter, + prim_vis_mask, ); } (None, SegmentDataKind::Instanced(..)) => { @@ -3267,6 +2818,79 @@ impl BatchBuilder { } } } + + fn add_gradient_tiles( + &mut self, + visible_tiles: &[VisibleGradientTile], + stops_handle: &GpuCacheHandle, + kind: BrushBatchKind, + blend_mode: BlendMode, + bounding_rect: &PictureRect, + clip_task_address: RenderTaskAddress, + gpu_cache: &GpuCache, + base_prim_header: &PrimitiveHeader, + prim_headers: &mut PrimitiveHeaders, + z_id: ZBufferId, + prim_vis_mask: PrimitiveVisibilityMask, + ) { + let key = BatchKey { + blend_mode, + kind: BatchKind::Brush(kind), + textures: BatchTextures::no_texture(), + }; + + let user_data = [stops_handle.as_int(gpu_cache), 0, 0, 0]; + + for tile in visible_tiles { + let prim_header = PrimitiveHeader { + specific_prim_address: gpu_cache.get_address(&tile.handle), + local_rect: tile.local_rect, + local_clip_rect: tile.local_clip_rect, + ..*base_prim_header + }; + let prim_header_index = prim_headers.push(&prim_header, z_id, user_data); + + self.add_brush_instance_to_batches( + key, + BatchFeatures::empty(), + bounding_rect, + z_id, + INVALID_SEGMENT_INDEX, + EdgeAaSegmentMask::all(), + clip_task_address, + BrushFlags::PERSPECTIVE_INTERPOLATION, + prim_header_index, + 0, + prim_vis_mask, + ); + } + } +} + +fn get_image_tile_params( + resource_cache: &ResourceCache, + gpu_cache: &mut GpuCache, + deferred_resolves: &mut Vec<DeferredResolve>, + request: ImageRequest, +) -> Option<(BrushBatchKind, BatchTextures, GpuCacheAddress)> { + + let cache_item = resolve_image( + request, + resource_cache, + gpu_cache, + deferred_resolves, + ); + + if cache_item.texture_id == TextureSource::Invalid { + None + } else { + let textures = BatchTextures::color(cache_item.texture_id); + Some(( + BrushBatchKind::Image(get_buffer_kind(cache_item.texture_id)), + textures, + gpu_cache.get_address(&cache_item.uv_rect_handle), + )) + } } /// Either a single texture / user data for all segments, @@ -3303,7 +2927,7 @@ impl BrushBatchParameters { /// across all segments. fn shared( batch_kind: BrushBatchKind, - textures: TextureSet, + textures: BatchTextures, prim_user_data: [i32; 4], specific_resource_address: i32, ) -> Self { @@ -3320,17 +2944,80 @@ impl BrushBatchParameters { } } +impl RenderTaskGraph { + fn resolve_surface( + &self, + task_id: RenderTaskId, + gpu_cache: &GpuCache, + ) -> (GpuCacheAddress, BatchTextures) { + ( + self[task_id].get_texture_address(gpu_cache), + BatchTextures::render_target_cache(), + ) + } +} + +pub fn resolve_image( + request: ImageRequest, + resource_cache: &ResourceCache, + gpu_cache: &mut GpuCache, + deferred_resolves: &mut Vec<DeferredResolve>, +) -> CacheItem { + match resource_cache.get_image_properties(request.key) { + Some(image_properties) => { + // Check if an external image that needs to be resolved + // by the render thread. + match image_properties.external_image { + Some(external_image) => { + // This is an external texture - we will add it to + // the deferred resolves list to be patched by + // the render thread... + let cache_handle = gpu_cache.push_deferred_per_frame_blocks(BLOCKS_PER_UV_RECT); + let cache_item = CacheItem { + texture_id: TextureSource::External(external_image), + uv_rect_handle: cache_handle, + uv_rect: DeviceIntRect::new( + DeviceIntPoint::zero(), + image_properties.descriptor.size, + ), + texture_layer: 0, + }; + + deferred_resolves.push(DeferredResolve { + image_properties, + address: gpu_cache.get_address(&cache_handle), + rendering: request.rendering, + }); + + cache_item + } + None => { + if let Ok(cache_item) = resource_cache.get_cached_image(request) { + cache_item + } else { + // There is no usable texture entry for the image key. Just return an invalid texture here. + CacheItem::invalid() + } + } + } + } + None => { + CacheItem::invalid() + } + } +} + /// A list of clip instances to be drawn into a target. #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ClipBatchList { /// Rectangle draws fill up the rectangles with rounded corners. - pub slow_rectangles: Vec<ClipMaskInstanceRect>, - pub fast_rectangles: Vec<ClipMaskInstanceRect>, + pub slow_rectangles: Vec<ClipMaskInstance>, + pub fast_rectangles: Vec<ClipMaskInstance>, /// Image draws apply the image masking. - pub images: FastHashMap<(TextureSource, Option<DeviceIntRect>), Vec<ClipMaskInstanceImage>>, - pub box_shadows: FastHashMap<TextureSource, Vec<ClipMaskInstanceBoxShadow>>, + pub images: FastHashMap<TextureSource, Vec<ClipMaskInstance>>, + pub box_shadows: FastHashMap<TextureSource, Vec<ClipMaskInstance>>, } impl ClipBatchList { @@ -3373,24 +3060,24 @@ impl ClipBatcher { pub fn add_clip_region( &mut self, + clip_data_address: GpuCacheAddress, local_pos: LayoutPoint, sub_rect: DeviceRect, - clip_data: ClipData, task_origin: DevicePoint, screen_origin: DevicePoint, device_pixel_scale: f32, ) { - let instance = ClipMaskInstanceRect { - common: ClipMaskInstanceCommon { - clip_transform_id: TransformPaletteId::IDENTITY, - prim_transform_id: TransformPaletteId::IDENTITY, - sub_rect, - task_origin, - screen_origin, - device_pixel_scale, - }, + let instance = ClipMaskInstance { + clip_transform_id: TransformPaletteId::IDENTITY, + prim_transform_id: TransformPaletteId::IDENTITY, + clip_data_address, + resource_address: GpuCacheAddress::INVALID, local_pos, - clip_data, + tile_rect: LayoutRect::zero(), + sub_rect, + task_origin, + screen_origin, + device_pixel_scale, }; self.primary_clips.slow_rectangles.push(instance); @@ -3400,13 +3087,14 @@ impl ClipBatcher { /// instead of one large rectangle. fn add_tiled_clip_mask( &mut self, - mask_screen_rect: DeviceRect, + mask_screen_rect: DeviceIntRect, local_clip_rect: LayoutRect, clip_spatial_node_index: SpatialNodeIndex, spatial_tree: &SpatialTree, world_rect: &WorldRect, - global_device_pixel_scale: DevicePixelScale, - common: &ClipMaskInstanceCommon, + device_pixel_scale: DevicePixelScale, + gpu_address: GpuCacheAddress, + instance: &ClipMaskInstance, is_first_clip: bool, ) -> bool { // Only try to draw in tiles if the clip mark is big enough. @@ -3414,7 +3102,6 @@ impl ClipBatcher { return false; } - let mask_screen_rect_size = mask_screen_rect.size.to_i32(); let clip_spatial_node = &spatial_tree .spatial_nodes[clip_spatial_node_index.0 as usize]; @@ -3441,14 +3128,14 @@ impl ClipBatcher { // Work out how many tiles to draw this clip mask in, stretched across the // device rect of the primitive clip mask. - let world_device_rect = world_clip_rect * global_device_pixel_scale; - let x_tiles = (mask_screen_rect_size.width + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE; - let y_tiles = (mask_screen_rect_size.height + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE; + let world_device_rect = world_clip_rect * device_pixel_scale; + let x_tiles = (mask_screen_rect.size.width + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE; + let y_tiles = (mask_screen_rect.size.height + CLIP_RECTANGLE_TILE_SIZE-1) / CLIP_RECTANGLE_TILE_SIZE; // Because we only run this code path for axis-aligned rects (the root coord system check above), // and only for rectangles (not rounded etc), the world_device_rect is not conservative - we know // that there is no inner_rect, and the world_device_rect should be the real, axis-aligned clip rect. - let mask_origin = mask_screen_rect.origin.to_vector(); + let mask_origin = mask_screen_rect.origin.to_f32().to_vector(); let clip_list = self.get_batch_list(is_first_clip); for y in 0 .. y_tiles { @@ -3458,8 +3145,8 @@ impl ClipBatcher { y * CLIP_RECTANGLE_TILE_SIZE, ); let p1 = DeviceIntPoint::new( - (p0.x + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.width), - (p0.y + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect_size.height), + (p0.x + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect.size.width), + (p0.y + CLIP_RECTANGLE_TILE_SIZE).min(mask_screen_rect.size.height), ); let normalized_sub_rect = DeviceIntRect::new( p0, @@ -3474,13 +3161,11 @@ impl ClipBatcher { // these pixels would be redundant - since this clip can't possibly // affect the pixels in this tile, skip them! if !world_device_rect.contains_rect(&world_sub_rect) { - clip_list.slow_rectangles.push(ClipMaskInstanceRect { - common: ClipMaskInstanceCommon { - sub_rect: normalized_sub_rect, - ..*common - }, + clip_list.slow_rectangles.push(ClipMaskInstance { + clip_data_address: gpu_address, + sub_rect: normalized_sub_rect, local_pos: local_clip_rect.origin, - clip_data: ClipData::uniform(local_clip_rect.size, 0.0, ClipMode::Clip), + ..*instance }); } } @@ -3506,22 +3191,19 @@ impl ClipBatcher { &mut self, clip_node_range: ClipNodeRange, root_spatial_node_index: SpatialNodeIndex, - render_tasks: &RenderTaskGraph, resource_cache: &ResourceCache, gpu_cache: &GpuCache, clip_store: &ClipStore, spatial_tree: &SpatialTree, transforms: &mut TransformPalette, clip_data_store: &ClipDataStore, - actual_rect: DeviceRect, + actual_rect: DeviceIntRect, world_rect: &WorldRect, - surface_device_pixel_scale: DevicePixelScale, - global_device_pixel_scale: DevicePixelScale, + device_pixel_scale: DevicePixelScale, task_origin: DevicePoint, screen_origin: DevicePoint, - ) -> bool { + ) { let mut is_first_clip = true; - let mut clear_to_one = false; for i in 0 .. clip_node_range.count { let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i); @@ -3533,37 +3215,26 @@ impl ClipBatcher { spatial_tree, ); - // For clip mask images, we need to map from the primitive's layout space to - // the target space, as the cs_clip_image shader needs to forward transform - // the local image bounds, rather than backwards transform the target bounds - // as in done in write_clip_tile_vertex. - let prim_transform_id = match clip_node.item.kind { - ClipItemKind::Image { .. } => { - transforms.get_id( - clip_instance.spatial_node_index, - root_spatial_node_index, - spatial_tree, - ) - } - _ => { - transforms.get_id( - root_spatial_node_index, - ROOT_SPATIAL_NODE_INDEX, - spatial_tree, - ) - } - }; + let prim_transform_id = transforms.get_id( + root_spatial_node_index, + ROOT_SPATIAL_NODE_INDEX, + spatial_tree, + ); - let common = ClipMaskInstanceCommon { + let instance = ClipMaskInstance { + clip_transform_id, + prim_transform_id, + clip_data_address: GpuCacheAddress::INVALID, + resource_address: GpuCacheAddress::INVALID, + local_pos: LayoutPoint::zero(), + tile_rect: LayoutRect::zero(), sub_rect: DeviceRect::new( DevicePoint::zero(), - actual_rect.size, + actual_rect.size.to_f32(), ), task_origin, screen_origin, - device_pixel_scale: surface_device_pixel_scale.0, - clip_transform_id, - prim_transform_id, + device_pixel_scale: device_pixel_scale.0, }; let added_clip = match clip_node.item.kind { @@ -3574,14 +3245,10 @@ impl ClipBatcher { tile: None, }; - let map_local_to_world = SpaceMapper::new_with_target( - ROOT_SPATIAL_NODE_INDEX, - clip_instance.spatial_node_index, - WorldRect::max_rect(), - spatial_tree, - ); + let clip_data_address = + gpu_cache.get_address(&clip_node.gpu_cache_handle); - let mut add_image = |request: ImageRequest, tile_rect: LayoutRect, sub_rect: DeviceRect| { + let mut add_image = |request: ImageRequest, local_tile_rect: LayoutRect| { let cache_item = match resource_cache.get_cached_image(request) { Ok(item) => item, Err(..) => { @@ -3591,126 +3258,69 @@ impl ClipBatcher { } }; - // If the clip transform is axis-aligned, we can skip any need for scissoring - // by clipping the local clip rect with the backwards transformed target bounds. - // If it is not axis-aligned, then we pass the local clip rect through unmodified - // to the shader and also set up a scissor rect for the overall target bounds to - // ensure nothing is drawn outside the target. If for some reason we can't map the - // rect back to local space, we also fall back to just using a scissor rectangle. - let world_rect = - sub_rect.translate(actual_rect.origin.to_vector()) / surface_device_pixel_scale; - let (clip_transform_id, local_rect, scissor) = match map_local_to_world.unmap(&world_rect) { - Some(local_rect) - if clip_transform_id.transform_kind() == TransformedRectKind::AxisAligned && - !map_local_to_world.get_transform().has_perspective_component() => { - match local_rect.intersection(&rect) { - Some(local_rect) => (clip_transform_id, local_rect, None), - None => return, - } - } - _ => { - // If for some reason inverting the transform failed, then don't consider - // the transform to be axis-aligned if it was. - (clip_transform_id.override_transform_kind(TransformedRectKind::Complex), - rect, - Some(common.sub_rect - .translate(task_origin.to_vector()) - .round_out() - .to_i32())) - } - }; - self.get_batch_list(is_first_clip) .images - .entry((cache_item.texture_id, scissor)) + .entry(cache_item.texture_id) .or_insert_with(Vec::new) - .push(ClipMaskInstanceImage { - common: ClipMaskInstanceCommon { - sub_rect, - clip_transform_id, - ..common - }, + .push(ClipMaskInstance { + clip_data_address, resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle), - tile_rect, - local_rect, + tile_rect: local_tile_rect, + local_pos: rect.origin, + ..instance }); }; - let clip_spatial_node = &spatial_tree.spatial_nodes[clip_instance.spatial_node_index.0 as usize]; - let clip_is_axis_aligned = clip_spatial_node.coordinate_system_id == CoordinateSystemId::root(); - - if clip_instance.has_visible_tiles() { - let sub_rect_bounds = actual_rect.size.into(); - - for tile in clip_store.visible_mask_tiles(&clip_instance) { - let tile_sub_rect = if clip_is_axis_aligned { - let tile_world_rect = map_local_to_world - .map(&tile.tile_rect) - .expect("bug: should always map as axis-aligned"); - let tile_device_rect = tile_world_rect * surface_device_pixel_scale; - tile_device_rect - .translate(-actual_rect.origin.to_vector()) - .round_out() - .intersection(&sub_rect_bounds) - } else { - Some(common.sub_rect) - }; - - if let Some(tile_sub_rect) = tile_sub_rect { - assert!(sub_rect_bounds.contains_rect(&tile_sub_rect)); + match clip_instance.visible_tiles { + Some(ref tiles) => { + for tile in tiles { add_image( request.with_tile(tile.tile_offset), tile.tile_rect, - tile_sub_rect, ) } } - } else { - add_image(request, rect, common.sub_rect) + None => { + add_image(request, rect) + } } - // If this is the first clip and either there is a transform or the image rect - // doesn't cover the entire task, then request a clear so that pixels outside - // the image boundaries will be properly initialized. - if is_first_clip && - (!clip_is_axis_aligned || - !(map_local_to_world.map(&rect).expect("bug: should always map as axis-aligned") - * surface_device_pixel_scale).contains_rect(&actual_rect)) { - clear_to_one = true; - } true } ClipItemKind::BoxShadow { ref source } => { - let task_id = source - .render_task + let gpu_address = + gpu_cache.get_address(&clip_node.gpu_cache_handle); + let rt_handle = source + .cache_handle + .as_ref() .expect("bug: render task handle not allocated"); - let (uv_rect_address, texture) = render_tasks.resolve_location(task_id, gpu_cache).unwrap(); + let rt_cache_entry = resource_cache + .get_cached_render_task(rt_handle); + let cache_item = resource_cache + .get_texture_cache_item(&rt_cache_entry.handle); + debug_assert_ne!(cache_item.texture_id, TextureSource::Invalid); self.get_batch_list(is_first_clip) .box_shadows - .entry(texture) + .entry(cache_item.texture_id) .or_insert_with(Vec::new) - .push(ClipMaskInstanceBoxShadow { - common, - resource_address: uv_rect_address, - shadow_data: BoxShadowData { - src_rect_size: source.original_alloc_size, - clip_mode: source.clip_mode as i32, - stretch_mode_x: source.stretch_mode_x as i32, - stretch_mode_y: source.stretch_mode_y as i32, - dest_rect: source.prim_shadow_rect, - }, + .push(ClipMaskInstance { + clip_data_address: gpu_address, + resource_address: gpu_cache.get_address(&cache_item.uv_rect_handle), + ..instance }); true } ClipItemKind::Rectangle { rect, mode: ClipMode::ClipOut } => { + let gpu_address = + gpu_cache.get_address(&clip_node.gpu_cache_handle); self.get_batch_list(is_first_clip) .slow_rectangles - .push(ClipMaskInstanceRect { - common, + .push(ClipMaskInstance { local_pos: rect.origin, - clip_data: ClipData::uniform(rect.size, 0.0, ClipMode::ClipOut), + clip_data_address: gpu_address, + ..instance }); true @@ -3719,36 +3329,39 @@ impl ClipBatcher { if clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) { false } else { - if self.add_tiled_clip_mask( + let gpu_address = gpu_cache.get_address(&clip_node.gpu_cache_handle); + + if !self.add_tiled_clip_mask( actual_rect, rect, clip_instance.spatial_node_index, spatial_tree, world_rect, - global_device_pixel_scale, - &common, + device_pixel_scale, + gpu_address, + &instance, is_first_clip, ) { - clear_to_one |= is_first_clip; - } else { self.get_batch_list(is_first_clip) .slow_rectangles - .push(ClipMaskInstanceRect { - common, + .push(ClipMaskInstance { + clip_data_address: gpu_address, local_pos: rect.origin, - clip_data: ClipData::uniform(rect.size, 0.0, ClipMode::Clip), + ..instance }); } true } } - ClipItemKind::RoundedRectangle { rect, ref radius, mode, .. } => { + ClipItemKind::RoundedRectangle { rect, .. } => { + let gpu_address = + gpu_cache.get_address(&clip_node.gpu_cache_handle); let batch_list = self.get_batch_list(is_first_clip); - let instance = ClipMaskInstanceRect { - common, + let instance = ClipMaskInstance { + clip_data_address: gpu_address, local_pos: rect.origin, - clip_data: ClipData::rounded_rect(rect.size, radius, mode), + ..instance }; if clip_instance.flags.contains(ClipNodeFlags::USE_FAST_PATH) { batch_list.fast_rectangles.push(instance); @@ -3762,8 +3375,25 @@ impl ClipBatcher { is_first_clip &= !added_clip; } + } +} - clear_to_one +// TODO(gw): This should probably be a method on TextureSource +pub fn get_buffer_kind(texture: TextureSource) -> ImageBufferKind { + match texture { + TextureSource::External(ext_image) => { + match ext_image.image_type { + ExternalImageType::TextureHandle(target) => { + target.into() + } + ExternalImageType::Buffer => { + // The ExternalImageType::Buffer should be handled by resource_cache. + // It should go through the non-external case. + panic!("Unexpected non-texture handle type"); + } + } + } + _ => ImageBufferKind::Texture2DArray, } } @@ -3772,39 +3402,35 @@ impl<'a, 'rc> RenderTargetContext<'a, 'rc> { /// Returns None if the segment was completely clipped out. /// Returns Some(OPAQUE_TASK_ADDRESS) if no clip mask is needed. /// Returns Some(task_address) if there was a valid clip mask. - fn get_clip_task_and_texture( + fn get_clip_task_address( &self, clip_task_index: ClipTaskIndex, offset: i32, render_tasks: &RenderTaskGraph, - ) -> Option<(RenderTaskAddress, TextureSource)> { - match self.scratch.clip_mask_instances[clip_task_index.0 as usize + offset as usize] { + ) -> Option<RenderTaskAddress> { + let address = match self.scratch.clip_mask_instances[clip_task_index.0 as usize + offset as usize] { ClipMaskKind::Mask(task_id) => { - Some(( - task_id.into(), - TextureSource::TextureCache( - render_tasks[task_id].get_target_texture(), - Swizzle::default(), - ) - )) + render_tasks.get_task_address(task_id) } ClipMaskKind::None => { - Some((OPAQUE_TASK_ADDRESS, TextureSource::Invalid)) + OPAQUE_TASK_ADDRESS } ClipMaskKind::Clipped => { - None + return None; } - } + }; + + Some(address) } /// Helper function to get the clip task address for a /// non-segmented primitive. - fn get_prim_clip_task_and_texture( + fn get_prim_clip_task_address( &self, clip_task_index: ClipTaskIndex, render_tasks: &RenderTaskGraph, - ) -> Option<(RenderTaskAddress, TextureSource)> { - self.get_clip_task_and_texture( + ) -> Option<RenderTaskAddress> { + self.get_clip_task_address( clip_task_index, 0, render_tasks, diff --git a/third_party/webrender/webrender/src/border.rs b/third_party/webrender/webrender/src/border.rs index d37b5e6f3ee..3185acd42c5 100644 --- a/third_party/webrender/webrender/src/border.rs +++ b/third_party/webrender/webrender/src/border.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{BorderRadius, BorderSide, BorderStyle, ColorF, ColorU}; -use api::{NormalBorder as ApiNormalBorder, RepeatMode}; +use api::{NormalBorder as ApiNormalBorder, RepeatMode, EdgeAaSegmentMask}; use api::units::*; use crate::clip::ClipChainId; use crate::ellipse::Ellipse; @@ -15,7 +15,6 @@ use crate::prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor}; use crate::prim_store::borders::{NormalBorderPrim, NormalBorderData}; use crate::util::{lerp, RectHelpers}; use crate::internal_types::LayoutPrimitiveInfo; -use crate::segment::EdgeAaSegmentMask; // Using 2048 as the maximum radius in device space before which we // start stretching is up for debate. @@ -35,7 +34,7 @@ pub const MAX_DASH_COUNT: u32 = 2048; // all the border structs with hashable // variants... -#[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct BorderRadiusAu { diff --git a/third_party/webrender/webrender/src/box_shadow.rs b/third_party/webrender/webrender/src/box_shadow.rs index 6c6e9135741..49d6f884ffb 100644 --- a/third_party/webrender/webrender/src/box_shadow.rs +++ b/third_party/webrender/webrender/src/box_shadow.rs @@ -8,8 +8,10 @@ use api::units::*; use crate::clip::{ClipItemKey, ClipItemKeyKind, ClipChainId}; use crate::scene_building::SceneBuilder; use crate::spatial_tree::SpatialNodeIndex; +use crate::gpu_cache::GpuCacheHandle; use crate::gpu_types::BoxShadowStretchMode; -use crate::render_task_graph::RenderTaskId; +use crate::render_task_cache::RenderTaskCacheEntryHandle; +use crate::util::RectHelpers; use crate::internal_types::LayoutPrimitiveInfo; #[derive(Debug, Clone, MallocSizeOf)] @@ -26,7 +28,8 @@ pub struct BoxShadowClipSource { // The current cache key (in device-pixels), and handles // to the cached clip region and blurred texture. pub cache_key: Option<(DeviceIntSize, BoxShadowCacheKey)>, - pub render_task: Option<RenderTaskId>, + pub cache_handle: Option<RenderTaskCacheEntryHandle>, + pub clip_data_handle: GpuCacheHandle, // Local-space size of the required render task size. pub shadow_rect_alloc_size: LayoutSize, @@ -67,7 +70,6 @@ pub struct BoxShadowCacheKey { pub br_top_right: DeviceIntSize, pub br_bottom_right: DeviceIntSize, pub br_bottom_left: DeviceIntSize, - pub device_pixel_scale: Au, } impl<'a> SceneBuilder<'a> { @@ -120,7 +122,7 @@ impl<'a> SceneBuilder<'a> { let mut clips = Vec::with_capacity(2); let (final_prim_rect, clip_radius) = match clip_mode { BoxShadowClipMode::Outset => { - if shadow_rect.is_empty() { + if !shadow_rect.is_well_formed_and_nonempty() { return; } @@ -136,7 +138,7 @@ impl<'a> SceneBuilder<'a> { (shadow_rect, shadow_radius) } BoxShadowClipMode::Inset => { - if !shadow_rect.is_empty() { + if shadow_rect.is_well_formed_and_nonempty() { clips.push(ClipItemKey { kind: ClipItemKeyKind::rounded_rect( shadow_rect, @@ -206,7 +208,7 @@ impl<'a> SceneBuilder<'a> { let prim_info = match clip_mode { BoxShadowClipMode::Outset => { // Certain spread-radii make the shadow invalid. - if shadow_rect.is_empty() { + if !shadow_rect.is_well_formed_and_nonempty() { return; } @@ -230,7 +232,7 @@ impl<'a> SceneBuilder<'a> { // Inset shadows are still visible, even if the // inset shadow rect becomes invalid (they will // just look like a solid rectangle). - if !shadow_rect.is_empty() { + if shadow_rect.is_well_formed_and_nonempty() { extra_clips.push(shadow_clip_source); } diff --git a/third_party/webrender/webrender/src/capture.rs b/third_party/webrender/webrender/src/capture.rs index 0414fcb25e4..d6952b7f82d 100644 --- a/third_party/webrender/webrender/src/capture.rs +++ b/third_party/webrender/webrender/src/capture.rs @@ -5,7 +5,7 @@ use std::fs::File; use std::path::{Path, PathBuf}; -use api::{ExternalImageData, ImageDescriptor}; +use api::{CaptureBits, ExternalImageData, ImageDescriptor}; #[cfg(feature = "png")] use api::ImageFormat; use api::units::TexelRect; @@ -13,7 +13,6 @@ use api::units::TexelRect; use api::units::DeviceIntSize; #[cfg(feature = "capture")] use crate::print_tree::{PrintableTree, PrintTree}; -use crate::render_api::CaptureBits; use ron; use serde; @@ -42,9 +41,10 @@ impl CaptureConfig { frame_id: 0, resource_id: 0, #[cfg(feature = "capture")] - pretty: ron::ser::PrettyConfig::new() - .with_enumerate_arrays(true) - .with_indentor(" ".to_string()), + pretty: ron::ser::PrettyConfig { + enumerate_arrays: true, + .. ron::ser::PrettyConfig::default() + }, } } diff --git a/third_party/webrender/webrender/src/clip.rs b/third_party/webrender/webrender/src/clip.rs index 7a839a68fc9..391170f0b08 100644 --- a/third_party/webrender/webrender/src/clip.rs +++ b/third_party/webrender/webrender/src/clip.rs @@ -92,40 +92,35 @@ //! [segment.rs]: ../segment/index.html //! -use api::{BorderRadius, ClipMode, ComplexClipRegion, ImageMask}; -use api::{BoxShadowClipMode, ClipId, FillRule, ImageKey, ImageRendering, PipelineId}; +use api::{BorderRadius, ClipIntern, ClipMode, ComplexClipRegion, ImageMask}; +use api::{BoxShadowClipMode, ClipId, ImageKey, ImageRendering, PipelineId}; use api::units::*; -use crate::image_tiling::{self, Repetition}; +use api::image_tiling::{self, Repetition}; use crate::border::{ensure_no_corner_overlap, BorderRadiusAu}; use crate::box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey}; use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialTree, SpatialNodeIndex, CoordinateSystemId}; use crate::ellipse::Ellipse; -use crate::gpu_cache::GpuCache; +use crate::gpu_cache::{GpuCache, GpuCacheHandle, ToGpuBlocks}; use crate::gpu_types::{BoxShadowStretchMode}; use crate::intern::{self, ItemUid}; use crate::internal_types::{FastHashMap, FastHashSet}; -use crate::prim_store::{VisibleMaskImageTile}; -use crate::prim_store::{PointKey, SizeKey, RectangleKey, PolygonKey}; +use crate::prim_store::{ClipData, ImageMaskData, SpaceMapper, VisibleMaskImageTile}; +use crate::prim_store::{PointKey, SizeKey, RectangleKey}; use crate::render_task_cache::to_cache_size; use crate::resource_cache::{ImageRequest, ResourceCache}; -use crate::space::SpaceMapper; -use crate::util::{clamp_to_scale_factor, MaxRect, extract_inner_rect_safe, project_rect, ScaleOffset, VecHelper}; +use crate::util::{extract_inner_rect_safe, project_rect, ScaleOffset}; use euclid::approxeq::ApproxEq; -use std::{iter, ops, u32, mem}; +use std::{iter, ops, u32}; +use smallvec::SmallVec; // Type definitions for interning clip nodes. -#[derive(Copy, Clone, Debug, MallocSizeOf, PartialEq)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] -pub enum ClipIntern {} - pub type ClipDataStore = intern::DataStore<ClipIntern>; pub type ClipDataHandle = intern::Handle<ClipIntern>; /// Defines a clip that is positioned by a specific spatial node #[cfg_attr(feature = "capture", derive(Serialize))] -#[derive(Copy, Clone, PartialEq)] -#[derive(MallocSizeOf)] +#[derive(Copy, Clone)] pub struct ClipInstance { /// Handle to the interned clip pub handle: ClipDataHandle, @@ -146,19 +141,6 @@ impl ClipInstance { } } -/// Defines a clip instance with some extra information that is available -/// during scene building (since interned clips cannot retrieve the underlying -/// data from the scene building thread). -#[cfg_attr(feature = "capture", derive(Serialize))] -#[derive(MallocSizeOf)] -#[derive(Copy, Clone)] -pub struct SceneClipInstance { - /// The interned clip + positioning information that is used during frame building. - pub clip: ClipInstance, - /// The definition of the clip, used during scene building to optimize clip-chains. - pub key: ClipItemKey, -} - /// A clip template defines clips in terms of the public API. Specifically, /// this is a parent `ClipId` and some number of clip instances. See the /// CLIPPING_AND_POSITIONING.md document in doc/ for more information. @@ -166,8 +148,8 @@ pub struct SceneClipInstance { pub struct ClipTemplate { /// Parent of this clip, in terms of the public clip API pub parent: ClipId, - /// Range of instances that define this clip template - pub clips: ops::Range<u32>, + /// List of instances that define this clip template + pub instances: SmallVec<[ClipInstance; 2]>, } /// A helper used during scene building to construct (internal) clip chains from @@ -198,7 +180,6 @@ impl ClipChainBuilder { clip_id: Option<ClipId>, clip_chain_nodes: &mut Vec<ClipChainNode>, templates: &FastHashMap<ClipId, ClipTemplate>, - instances: &[SceneClipInstance], ) -> Self { let mut parent_clips = FastHashSet::default(); @@ -219,13 +200,10 @@ impl ClipChainBuilder { &mut parent_clips, clip_chain_nodes, templates, - instances, ) } None => { - // Even if the clip id is None, it's possible that there were parent clips in the builder - // that need to be applied and set as the root of this clip-chain builder. - parent_clip_chain_id + ClipChainId::NONE } }; @@ -246,14 +224,12 @@ impl ClipChainBuilder { existing_clips: &mut FastHashSet<(ItemUid, SpatialNodeIndex)>, clip_chain_nodes: &mut Vec<ClipChainNode>, templates: &FastHashMap<ClipId, ClipTemplate>, - clip_instances: &[SceneClipInstance], ) -> ClipChainId { let template = &templates[&clip_id]; - let instances = &clip_instances[template.clips.start as usize .. template.clips.end as usize]; let mut clip_chain_id = parent_clip_chain_id; - for clip in instances { - let key = (clip.clip.handle.uid(), clip.clip.spatial_node_index); + for clip in &template.instances { + let key = (clip.handle.uid(), clip.spatial_node_index); // If this clip chain already has this clip instance, skip it if existing_clips.contains(&key) { @@ -264,8 +240,8 @@ impl ClipChainBuilder { let new_clip_chain_id = ClipChainId(clip_chain_nodes.len() as u32); existing_clips.insert(key); clip_chain_nodes.push(ClipChainNode { - handle: clip.clip.handle, - spatial_node_index: clip.clip.spatial_node_index, + handle: clip.handle, + spatial_node_index: clip.spatial_node_index, parent_clip_chain_id: clip_chain_id, }); clip_chain_id = new_clip_chain_id; @@ -282,40 +258,6 @@ impl ClipChainBuilder { existing_clips, clip_chain_nodes, templates, - clip_instances, - ) - } - - /// Return true if any of the clips in the hierarchy from clip_id to the - /// root clip are complex. - // TODO(gw): This method should only be required until the shared_clip - // optimization patches are complete, and can then be removed. - fn has_complex_clips( - &self, - clip_id: ClipId, - templates: &FastHashMap<ClipId, ClipTemplate>, - instances: &[SceneClipInstance], - ) -> bool { - let template = &templates[&clip_id]; - - // Check if any of the clips in this template are complex - let clips = &instances[template.clips.start as usize .. template.clips.end as usize]; - for clip in clips { - if let ClipNodeKind::Complex = clip.key.kind.node_kind() { - return true; - } - } - - // The ClipId parenting is terminated when we reach the root ClipId - if clip_id == template.parent { - return false; - } - - // Recurse into parent clip template to also check those - self.has_complex_clips( - template.parent, - templates, - instances, ) } @@ -327,7 +269,6 @@ impl ClipChainBuilder { clip_id: ClipId, clip_chain_nodes: &mut Vec<ClipChainNode>, templates: &FastHashMap<ClipId, ClipTemplate>, - instances: &[SceneClipInstance], ) -> ClipChainId { if self.prev_clip_id == clip_id { return self.prev_clip_chain_id; @@ -348,7 +289,6 @@ impl ClipChainBuilder { &mut self.existing_clips_cache, clip_chain_nodes, templates, - instances, ); self.prev_clip_id = clip_id; @@ -392,6 +332,7 @@ enum ClipResult { #[derive(MallocSizeOf)] pub struct ClipNode { pub item: ClipItem, + pub gpu_cache_handle: GpuCacheHandle, } // Convert from an interning key for a clip item @@ -409,12 +350,11 @@ impl From<ClipItemKey> for ClipNode { mode, } } - ClipItemKeyKind::ImageMask(rect, image, repeat, polygon_handle) => { + ClipItemKeyKind::ImageMask(rect, image, repeat) => { ClipItemKind::Image { image, rect: rect.into(), repeat, - polygon_handle, } } ClipItemKeyKind::BoxShadow(shadow_rect_fract_offset, shadow_rect_size, shadow_radius, prim_shadow_rect, blur_radius, clip_mode) => { @@ -433,6 +373,7 @@ impl From<ClipItemKey> for ClipNode { item: ClipItem { kind, }, + gpu_cache_handle: GpuCacheHandle::new(), } } } @@ -475,16 +416,6 @@ pub struct ClipChainNode { pub parent_clip_chain_id: ClipChainId, } -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -pub struct ClipSet { - /// Local space clip rect - pub local_clip_rect: LayoutRect, - - /// ID of the clip chain that this set is clipped by. - pub clip_chain_id: ClipChainId, -} - // When a clip node is found to be valid for a // clip chain instance, it's stored in an index // buffer style structure. This struct contains @@ -498,13 +429,7 @@ pub struct ClipNodeInstance { pub handle: ClipDataHandle, pub spatial_node_index: SpatialNodeIndex, pub flags: ClipNodeFlags, - pub visible_tiles: Option<ops::Range<usize>>, -} - -impl ClipNodeInstance { - pub fn has_visible_tiles(&self) -> bool { - self.visible_tiles.is_some() - } + pub visible_tiles: Option<Vec<VisibleMaskImageTile>>, } // A range of clip node instances that were found by @@ -606,7 +531,6 @@ impl ClipNodeInfo { clipped_rect: &LayoutRect, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, - mask_tiles: &mut Vec<VisibleMaskImageTile>, spatial_tree: &SpatialTree, request_resources: bool, ) -> Option<ClipNodeInstance> { @@ -628,7 +552,7 @@ impl ClipNodeInfo { let mut visible_tiles = None; - if let ClipItemKind::Image { rect, image, repeat, .. } = node.item.kind { + if let ClipItemKind::Image { rect, image, repeat } = node.item.kind { let request = ImageRequest { key: image, rendering: ImageRendering::Auto, @@ -637,16 +561,12 @@ impl ClipNodeInfo { if let Some(props) = resource_cache.get_image_properties(image) { if let Some(tile_size) = props.tiling { - let tile_range_start = mask_tiles.len(); + let mut mask_tiles = Vec::new(); let visible_rect = if repeat { *clipped_rect } else { - // Bug 1648323 - It is unclear why on rare occasions we get - // a clipped_rect that does not intersect the clip's mask rect. - // defaulting to clipped_rect here results in zero repetitions - // which clips the primitive entirely. - clipped_rect.intersection(&rect).unwrap_or(*clipped_rect) + clipped_rect.intersection(&rect).unwrap() }; let repetitions = image_tiling::repetitions( @@ -679,7 +599,7 @@ impl ClipNodeInfo { }); } } - visible_tiles = Some(tile_range_start..mask_tiles.len()); + visible_tiles = Some(mask_tiles); } else if request_resources { resource_cache.request_image(request, gpu_cache); } @@ -703,26 +623,45 @@ impl ClipNodeInfo { impl ClipNode { pub fn update( &mut self, + gpu_cache: &mut GpuCache, device_pixel_scale: DevicePixelScale, ) { match self.item.kind { - ClipItemKind::Image { .. } | - ClipItemKind::Rectangle { .. } | - ClipItemKind::RoundedRectangle { .. } => {} - + ClipItemKind::Image { rect, .. } => { + if let Some(request) = gpu_cache.request(&mut self.gpu_cache_handle) { + let data = ImageMaskData { + local_mask_size: rect.size, + }; + data.write_gpu_blocks(request); + } + } ClipItemKind::BoxShadow { ref mut source } => { + if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) { + request.push([ + source.original_alloc_size.width, + source.original_alloc_size.height, + source.clip_mode as i32 as f32, + 0.0, + ]); + request.push([ + source.stretch_mode_x as i32 as f32, + source.stretch_mode_y as i32 as f32, + 0.0, + 0.0, + ]); + request.push(source.prim_shadow_rect); + } + // Quote from https://drafts.csswg.org/css-backgrounds-3/#shadow-blur // "the image that would be generated by applying to the shadow a // Gaussian blur with a standard deviation equal to half the blur radius." let blur_radius_dp = source.blur_radius * 0.5; // Create scaling from requested size to cache size. - let mut content_scale = LayoutToWorldScale::new(1.0) * device_pixel_scale; - content_scale.0 = clamp_to_scale_factor(content_scale.0, false); + let content_scale = LayoutToWorldScale::new(1.0) * device_pixel_scale; // Create the cache key for this box-shadow render task. - let cache_size = to_cache_size(source.shadow_rect_alloc_size, &mut content_scale); - + let cache_size = to_cache_size(source.shadow_rect_alloc_size * content_scale); let bs_cache_key = BoxShadowCacheKey { blur_radius_dp: (blur_radius_dp * content_scale.0).round() as i32, clip_mode: source.clip_mode, @@ -731,54 +670,52 @@ impl ClipNode { br_top_right: (source.shadow_radius.top_right * content_scale).round().to_i32(), br_bottom_right: (source.shadow_radius.bottom_right * content_scale).round().to_i32(), br_bottom_left: (source.shadow_radius.bottom_left * content_scale).round().to_i32(), - device_pixel_scale: Au::from_f32_px(content_scale.0), }; source.cache_key = Some((cache_size, bs_cache_key)); - } - } - } -} -pub struct ClipStoreStats { - templates_capacity: usize, - instances_capacity: usize, -} + if let Some(mut request) = gpu_cache.request(&mut source.clip_data_handle) { + let data = ClipData::rounded_rect( + source.minimal_shadow_rect.size, + &source.shadow_radius, + ClipMode::Clip, + ); -impl ClipStoreStats { - pub fn empty() -> Self { - ClipStoreStats { - templates_capacity: 0, - instances_capacity: 0, + data.write(&mut request); + } + } + ClipItemKind::Rectangle { rect, mode } => { + if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) { + let data = ClipData::uniform(rect.size, 0.0, mode); + data.write(&mut request); + } + } + ClipItemKind::RoundedRectangle { rect, ref radius, mode } => { + if let Some(mut request) = gpu_cache.request(&mut self.gpu_cache_handle) { + let data = ClipData::rounded_rect(rect.size, radius, mode); + data.write(&mut request); + } + } } } } -#[derive(Default)] -pub struct ClipStoreScratchBuffer { - clip_node_instances: Vec<ClipNodeInstance>, - mask_tiles: Vec<VisibleMaskImageTile>, -} - /// The main clipping public interface that other modules access. #[derive(MallocSizeOf)] #[cfg_attr(feature = "capture", derive(Serialize))] pub struct ClipStore { pub clip_chain_nodes: Vec<ClipChainNode>, pub clip_node_instances: Vec<ClipNodeInstance>, - mask_tiles: Vec<VisibleMaskImageTile>, active_clip_node_info: Vec<ClipNodeInfo>, active_local_clip_rect: Option<LayoutRect>, - active_pic_clip_rect: PictureRect, // No malloc sizeof since it's not implemented for ops::Range, but these // allocations are tiny anyway. /// Map of all clip templates defined by the public API to templates #[ignore_malloc_size_of = "range missing"] - pub templates: FastHashMap<ClipId, ClipTemplate>, - pub instances: Vec<SceneClipInstance>, + templates: FastHashMap<ClipId, ClipTemplate>, /// A stack of current clip-chain builders. A new clip-chain builder is /// typically created each time a clip root (such as an iframe or stacking @@ -896,25 +833,6 @@ impl ClipChainStack { } } - pub fn clear(&mut self) { - self.clips.clear(); - self.clip_counts.clear(); - self.levels.clear(); - self.levels.push(ClipChainLevel { - shared_clips: Vec::new(), - first_clip_index: 0, - initial_clip_counts_len: 0, - }); - } - - pub fn take(&mut self) -> Self { - ClipChainStack { - levels: self.levels.take(), - clips: self.clips.take(), - clip_counts: self.clip_counts.take(), - } - } - /// Push a clip chain root onto the currently active list. pub fn push_clip( &mut self, @@ -969,7 +887,7 @@ impl ClipChainStack { maybe_shared_clips: &[ClipInstance], spatial_tree: &SpatialTree, ) { - let mut shared_clips = Vec::with_capacity(maybe_shared_clips.len()); + let mut shared_clips = Vec::new(); // If there are clips in the shared list for a picture cache, only include // them if they are simple, axis-aligned clips (i.e. in the root coordinate @@ -988,7 +906,7 @@ impl ClipChainStack { } let level = ClipChainLevel { - shared_clips, + shared_clips: shared_clips.to_vec(), first_clip_index: self.clips.len(), initial_clip_counts_len: self.clip_counts.len(), }; @@ -1011,50 +929,27 @@ impl ClipChainStack { } impl ClipStore { - pub fn new(stats: &ClipStoreStats) -> Self { - let mut templates = FastHashMap::default(); - templates.reserve(stats.templates_capacity); - + pub fn new() -> Self { ClipStore { clip_chain_nodes: Vec::new(), clip_node_instances: Vec::new(), - mask_tiles: Vec::new(), active_clip_node_info: Vec::new(), active_local_clip_rect: None, - active_pic_clip_rect: PictureRect::max_rect(), - templates, - instances: Vec::with_capacity(stats.instances_capacity), + templates: FastHashMap::default(), chain_builder_stack: Vec::new(), } } - pub fn get_stats(&self) -> ClipStoreStats { - // Selecting the smaller of the current capacity and 2*len ensures we don't - // retain a huge hashmap alloc after navigating away from a page with a large - // number of clip templates. - let templates_capacity = self.templates.capacity().min(self.templates.len() * 2); - let instances_capacity = self.instances.capacity().min(self.instances.len() * 2); - - ClipStoreStats { - templates_capacity, - instances_capacity, - } - } - /// Register a new clip template for the clip_id defined in the display list. pub fn register_clip_template( &mut self, clip_id: ClipId, parent: ClipId, - clips: &[SceneClipInstance], + instances: &[ClipInstance], ) { - let start = self.instances.len() as u32; - self.instances.extend_from_slice(clips); - let end = self.instances.len() as u32; - self.templates.insert(clip_id, ClipTemplate { parent, - clips: start..end, + instances: instances.into(), }); } @@ -1081,25 +976,6 @@ impl ClipStore { clip_id, &mut self.clip_chain_nodes, &self.templates, - &self.instances, - ) - } - - /// Return true if any of the clips in the hierarchy from clip_id to the - /// root clip are complex. - // TODO(gw): This method should only be required until the shared_clip - // optimization patches are complete, and can then be removed. - pub fn has_complex_clips( - &self, - clip_id: ClipId, - ) -> bool { - self.chain_builder_stack - .last() - .unwrap() - .has_complex_clips( - clip_id, - &self.templates, - &self.instances, ) } @@ -1123,7 +999,6 @@ impl ClipStore { clip_id, &mut self.clip_chain_nodes, &self.templates, - &self.instances, ); self.chain_builder_stack.push(builder); @@ -1167,15 +1042,13 @@ impl ClipStore { pub fn set_active_clips( &mut self, local_prim_clip_rect: LayoutRect, - prim_spatial_node_index: SpatialNodeIndex, - pic_spatial_node_index: SpatialNodeIndex, + spatial_node_index: SpatialNodeIndex, clip_chains: &[ClipChainId], spatial_tree: &SpatialTree, clip_data_store: &ClipDataStore, ) { self.active_clip_node_info.clear(); self.active_local_clip_rect = None; - self.active_pic_clip_rect = PictureRect::max_rect(); let mut local_clip_rect = local_prim_clip_rect; @@ -1184,11 +1057,9 @@ impl ClipStore { if !add_clip_node_to_current_chain( clip_chain_node, - prim_spatial_node_index, - pic_spatial_node_index, + spatial_node_index, &mut local_clip_rect, &mut self.active_clip_node_info, - &mut self.active_pic_clip_rect, clip_data_store, spatial_tree, ) { @@ -1212,7 +1083,6 @@ impl ClipStore { self.active_clip_node_info.clear(); self.active_local_clip_rect = Some(prim_clip_chain.local_clip_rect); - self.active_pic_clip_rect = prim_clip_chain.pic_clip_rect; let clip_instances = &self .clip_node_instances[prim_clip_chain.clips_range.to_range()]; @@ -1256,7 +1126,7 @@ impl ClipStore { } let local_bounding_rect = local_prim_rect.intersection(&local_clip_rect)?; - let mut pic_clip_rect = prim_to_pic_mapper.map(&local_bounding_rect)?; + let pic_clip_rect = prim_to_pic_mapper.map(&local_bounding_rect)?; let world_clip_rect = pic_to_world_mapper.map(&pic_clip_rect)?; // Now, we've collected all the clip nodes that *potentially* affect this @@ -1308,7 +1178,10 @@ impl ClipStore { // Needs a mask -> add to clip node indices // TODO(gw): Ensure this only runs once on each node per frame? - node.update(device_pixel_scale); + node.update( + gpu_cache, + device_pixel_scale, + ); // Create the clip node instance for this clip node if let Some(instance) = node_info.create_instance( @@ -1316,7 +1189,6 @@ impl ClipStore { &local_bounding_rect, gpu_cache, resource_cache, - &mut self.mask_tiles, spatial_tree, request_resources, ) { @@ -1352,16 +1224,6 @@ impl ClipStore { count: self.clip_node_instances.len() as u32 - first_clip_node_index, }; - // If this clip chain needs a mask, reduce the size of the mask allocation - // by any clips that were in the same space as the picture. This can result - // in much smaller clip mask allocations in some cases. Note that the ordering - // here is important - the reduction must occur *after* the clip item accept - // reject checks above, so that we don't eliminate masks accidentally (since - // we currently only support a local clip rect in the vertex shader). - if needs_mask { - pic_clip_rect = pic_clip_rect.intersection(&self.active_pic_clip_rect)?; - } - // Return a valid clip chain instance Some(ClipChainInstance { clips_range, @@ -1373,24 +1235,8 @@ impl ClipStore { }) } - pub fn begin_frame(&mut self, scratch: &mut ClipStoreScratchBuffer) { - mem::swap(&mut self.clip_node_instances, &mut scratch.clip_node_instances); - mem::swap(&mut self.mask_tiles, &mut scratch.mask_tiles); + pub fn clear_old_instances(&mut self) { self.clip_node_instances.clear(); - self.mask_tiles.clear(); - } - - pub fn end_frame(&mut self, scratch: &mut ClipStoreScratchBuffer) { - mem::swap(&mut self.clip_node_instances, &mut scratch.clip_node_instances); - mem::swap(&mut self.mask_tiles, &mut scratch.mask_tiles); - } - - pub fn visible_mask_tiles(&self, instance: &ClipNodeInstance) -> &[VisibleMaskImageTile] { - if let Some(range) = &instance.visible_tiles { - &self.mask_tiles[range.clone()] - } else { - &[] - } } } @@ -1436,6 +1282,18 @@ impl<J> ClipRegion<ComplexTranslateIter<J>> { } } +impl ClipRegion<Option<ComplexClipRegion>> { + pub fn create_for_clip_node_with_local_clip( + local_clip: &LayoutRect, + reference_frame_relative_offset: &LayoutVector2D + ) -> Self { + ClipRegion { + main: local_clip.translate(*reference_frame_relative_offset), + complex_clips: None, + } + } +} + // The ClipItemKey is a hashable representation of the contents // of a clip item. It is used during interning to de-duplicate // clip nodes between frames and display lists. This allows quick @@ -1443,13 +1301,13 @@ impl<J> ClipRegion<ComplexTranslateIter<J>> { // the uploaded GPU cache handle to be retained between display lists. // TODO(gw): Maybe we should consider constructing these directly // in the DL builder? -#[derive(Copy, Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum ClipItemKeyKind { Rectangle(RectangleKey, ClipMode), RoundedRectangle(RectangleKey, BorderRadiusAu, ClipMode), - ImageMask(RectangleKey, ImageKey, bool, Option<PolygonDataHandle>), + ImageMask(RectangleKey, ImageKey, bool), BoxShadow(PointKey, SizeKey, BorderRadiusAu, RectangleKey, Au, BoxShadowClipMode), } @@ -1471,13 +1329,11 @@ impl ClipItemKeyKind { } } - pub fn image_mask(image_mask: &ImageMask, mask_rect: LayoutRect, - polygon_handle: Option<PolygonDataHandle>) -> Self { + pub fn image_mask(image_mask: &ImageMask, mask_rect: LayoutRect) -> Self { ClipItemKeyKind::ImageMask( mask_rect.into(), image_mask.image, image_mask.repeat, - polygon_handle, ) } @@ -1517,7 +1373,7 @@ impl ClipItemKeyKind { } } -#[derive(Debug, Copy, Clone, Eq, MallocSizeOf, PartialEq, Hash)] +#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ClipItemKey { @@ -1539,7 +1395,6 @@ impl intern::Internable for ClipIntern { type Key = ClipItemKey; type StoreData = ClipNode; type InternData = ClipInternData; - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_CLIPS; } #[derive(Debug, MallocSizeOf)] @@ -1559,7 +1414,6 @@ pub enum ClipItemKind { image: ImageKey, rect: LayoutRect, repeat: bool, - polygon_handle: Option<PolygonDataHandle>, }, BoxShadow { source: BoxShadowClipSource, @@ -1660,8 +1514,9 @@ fn compute_box_shadow_parameters( clip_mode, stretch_mode_x, stretch_mode_y, - render_task: None, + cache_handle: None, cache_key: None, + clip_data_handle: GpuCacheHandle::new(), minimal_shadow_rect, } } @@ -1866,8 +1721,13 @@ impl ClipItemKind { ClipItemKind::RoundedRectangle { rect, ref radius, mode: ClipMode::Clip } => { // TODO(gw): Consider caching this in the ClipNode // if it ever shows in profiles. - if rounded_rectangle_contains_rect_quick(&rect, radius, &prim_rect) { - return ClipResult::Accept; + // TODO(gw): extract_inner_rect_safe is overly + // conservative for this code! + let inner_clip_rect = extract_inner_rect_safe(&rect, radius); + if let Some(inner_clip_rect) = inner_clip_rect { + if inner_clip_rect.contains_rect(prim_rect) { + return ClipResult::Accept; + } } match rect.intersection(prim_rect) { @@ -1882,8 +1742,13 @@ impl ClipItemKind { ClipItemKind::RoundedRectangle { rect, ref radius, mode: ClipMode::ClipOut } => { // TODO(gw): Consider caching this in the ClipNode // if it ever shows in profiles. - if rounded_rectangle_contains_rect_quick(&rect, radius, &prim_rect) { - return ClipResult::Reject; + // TODO(gw): extract_inner_rect_safe is overly + // conservative for this code! + let inner_clip_rect = extract_inner_rect_safe(&rect, radius); + if let Some(inner_clip_rect) = inner_clip_rect { + if inner_clip_rect.contains_rect(prim_rect) { + return ClipResult::Reject; + } } match rect.intersection(prim_rect) { @@ -1971,103 +1836,6 @@ pub fn rounded_rectangle_contains_point( true } -/// Return true if the rounded rectangle described by `container` and `radii` -/// definitely contains `containee`. May return false negatives, but never false -/// positives. -fn rounded_rectangle_contains_rect_quick( - container: &LayoutRect, - radii: &BorderRadius, - containee: &LayoutRect, -) -> bool { - if !container.contains_rect(containee) { - return false; - } - - /// Return true if `point` falls within `corner`. This only covers the - /// upper-left case; we transform the other corners into that form. - fn foul(point: LayoutPoint, corner: LayoutPoint) -> bool { - point.x < corner.x && point.y < corner.y - } - - /// Flip `pt` about the y axis (i.e. negate `x`). - fn flip_x(pt: LayoutPoint) -> LayoutPoint { - LayoutPoint { x: -pt.x, .. pt } - } - - /// Flip `pt` about the x axis (i.e. negate `y`). - fn flip_y(pt: LayoutPoint) -> LayoutPoint { - LayoutPoint { y: -pt.y, .. pt } - } - - if foul(containee.top_left(), container.top_left() + radii.top_left) || - foul(flip_x(containee.top_right()), flip_x(container.top_right()) + radii.top_right) || - foul(flip_y(containee.bottom_left()), flip_y(container.bottom_left()) + radii.bottom_left) || - foul(-containee.bottom_right(), -container.bottom_right() + radii.bottom_right) - { - return false; - } - - true -} - -/// Test where point p is relative to the infinite line that passes through the segment -/// defined by p0 and p1. Point p is on the "left" of the line if the triangle (p0, p1, p) -/// forms a counter-clockwise triangle. -/// > 0 is left of the line -/// < 0 is right of the line -/// == 0 is on the line -pub fn is_left_of_line( - p_x: f32, - p_y: f32, - p0_x: f32, - p0_y: f32, - p1_x: f32, - p1_y: f32, -) -> f32 { - (p1_x - p0_x) * (p_y - p0_y) - (p_x - p0_x) * (p1_y - p0_y) -} - -pub fn polygon_contains_point( - point: &LayoutPoint, - rect: &LayoutRect, - polygon: &PolygonKey, -) -> bool { - if !rect.contains(*point) { - return false; - } - - // p is a LayoutPoint that we'll be comparing to dimensionless PointKeys, - // which were created from LayoutPoints, so it all works out. - let p = LayoutPoint::new(point.x - rect.origin.x, point.y - rect.origin.y); - - // Calculate a winding number for this point. - let mut winding_number: i32 = 0; - - let count = polygon.point_count as usize; - - for i in 0..count { - let p0 = polygon.points[i]; - let p1 = polygon.points[(i + 1) % count]; - - if p0.y <= p.y { - if p1.y > p.y { - if is_left_of_line(p.x, p.y, p0.x, p0.y, p1.x, p1.y) > 0.0 { - winding_number = winding_number + 1; - } - } - } else if p1.y <= p.y { - if is_left_of_line(p.x, p.y, p0.x, p0.y, p1.x, p1.y) < 0.0 { - winding_number = winding_number - 1; - } - } - } - - match polygon.fill_rule { - FillRule::Nonzero => winding_number != 0, - FillRule::Evenodd => winding_number.abs() % 2 == 1, - } -} - pub fn projected_rect_contains( source_rect: &LayoutRect, transform: &LayoutToWorldTransform, @@ -2109,11 +1877,9 @@ pub fn projected_rect_contains( // results in the entire primitive being culled out. fn add_clip_node_to_current_chain( node: &ClipChainNode, - prim_spatial_node_index: SpatialNodeIndex, - pic_spatial_node_index: SpatialNodeIndex, + spatial_node_index: SpatialNodeIndex, local_clip_rect: &mut LayoutRect, clip_node_info: &mut Vec<ClipNodeInfo>, - current_pic_clip_rect: &mut PictureRect, clip_data_store: &ClipDataStore, spatial_tree: &SpatialTree, ) -> bool { @@ -2122,7 +1888,7 @@ fn add_clip_node_to_current_chain( // Determine the most efficient way to convert between coordinate // systems of the primitive and clip node. let conversion = ClipSpaceConversion::new( - prim_spatial_node_index, + spatial_node_index, node.spatial_node_index, spatial_tree, ); @@ -2145,37 +1911,15 @@ fn add_clip_node_to_current_chain( }; } ClipSpaceConversion::Transform(..) => { - // Map the local clip rect directly into the same space as the picture - // surface. This will often be the same space as the clip itself, which - // results in a reduction in allocated clip mask size. - - // For simplicity, only apply this optimization if the clip is in the - // same coord system as the picture. There are some 'advanced' perspective - // clip tests in wrench that break without this check. Those cases are - // never used in Gecko, and we aim to remove support in WR for that - // in future to simplify the clipping pipeline. - let pic_coord_system = spatial_tree - .spatial_nodes[pic_spatial_node_index.0 as usize] - .coordinate_system_id; - - let clip_coord_system = spatial_tree - .spatial_nodes[node.spatial_node_index.0 as usize] - .coordinate_system_id; - - if pic_coord_system == clip_coord_system { - let mapper = SpaceMapper::new_with_target( - pic_spatial_node_index, - node.spatial_node_index, - PictureRect::max_rect(), - spatial_tree, - ); - - if let Some(pic_clip_rect) = mapper.map(&clip_rect) { - *current_pic_clip_rect = pic_clip_rect - .intersection(current_pic_clip_rect) - .unwrap_or(PictureRect::zero()); - } - } + // TODO(gw): In the future, we can reduce the size + // of the pic_clip_rect here. To do this, + // we can use project_rect or the + // inverse_rect_footprint method, depending + // on the relationship of the clip, pic + // and primitive spatial nodes. + // I have left this for now until we + // find some good test cases where this + // would be a worthwhile perf win. } } } @@ -2207,24 +1951,3 @@ mod tests { ); } } - -/// PolygonKeys get interned, because it's a convenient way to move the data -/// for the polygons out of the ClipItemKind and ClipItemKeyKind enums. The -/// polygon data is both interned and retrieved by the scene builder, and not -/// accessed at all by the frame builder. Another oddity is that the -/// PolygonKey contains the totality of the information about the polygon, so -/// the InternData and StoreData types are both PolygonKey. -#[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] -pub enum PolygonIntern {} - -pub type PolygonDataHandle = intern::Handle<PolygonIntern>; - -impl intern::InternDebug for PolygonKey {} - -impl intern::Internable for PolygonIntern { - type Key = PolygonKey; - type StoreData = PolygonKey; - type InternData = PolygonKey; - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_POLYGONS; -} diff --git a/third_party/webrender/webrender/src/composite.rs b/third_party/webrender/webrender/src/composite.rs index d07119268be..bdc11097689 100644 --- a/third_party/webrender/webrender/src/composite.rs +++ b/third_party/webrender/webrender/src/composite.rs @@ -2,20 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ColorF, YuvColorSpace, YuvFormat, ImageRendering, ExternalImageId, ImageBufferKind}; -use api::units::*; -use api::ColorDepth; -use crate::image_source::resolve_image; -use euclid::Transform3D; +use api::{ColorF, YuvColorSpace, YuvFormat, ImageRendering}; +use api::units::{DeviceRect, DeviceIntSize, DeviceIntRect, DeviceIntPoint, WorldRect}; +use api::units::{DevicePixelScale, DevicePoint, PictureRect, TexelRect}; +use crate::batch::{resolve_image, get_buffer_kind}; use crate::gpu_cache::GpuCache; use crate::gpu_types::{ZBufferId, ZBufferIdGenerator}; use crate::internal_types::TextureSource; use crate::picture::{ImageDependency, ResolvedSurfaceTexture, TileCacheInstance, TileId, TileSurface}; use crate::prim_store::DeferredResolve; +use crate::renderer::ImageBufferKind; use crate::resource_cache::{ImageRequest, ResourceCache}; -use crate::util::Preallocator; -use crate::tile_cache::PictureCacheDebugInfo; -use std::{ops, u64, os::raw::c_void}; +use std::{ops, u64}; /* Types and definitions related to compositing picture cache tiles @@ -33,10 +31,6 @@ pub enum NativeSurfaceOperationDetails { tile_size: DeviceIntSize, is_opaque: bool, }, - CreateExternalSurface { - id: NativeSurfaceId, - is_opaque: bool, - }, DestroySurface { id: NativeSurfaceId, }, @@ -45,10 +39,6 @@ pub enum NativeSurfaceOperationDetails { }, DestroyTile { id: NativeTileId, - }, - AttachExternalImage { - id: NativeSurfaceId, - external_image: ExternalImageId, } } @@ -85,27 +75,6 @@ pub enum CompositeSurfaceFormat { Yuv, } -bitflags! { - /// Optional features that can be opted-out of when compositing, - /// possibly allowing a fast path to be selected. - pub struct CompositeFeatures: u8 { - // UV coordinates do not require clamping, for example because the - // entire texture is being composited. - const NO_UV_CLAMP = 1 << 0; - // The texture sample should not be modulated by a specified color. - const NO_COLOR_MODULATION = 1 << 1; - } -} - -#[derive(Copy, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum TileKind { - Opaque, - Alpha, - Clear, -} - /// Describes the geometry and surface of a tile to be composited #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -115,29 +84,7 @@ pub struct CompositeTile { pub clip_rect: DeviceRect, pub dirty_rect: DeviceRect, pub valid_rect: DeviceRect, - pub transform: Option<CompositorSurfaceTransform>, pub z_id: ZBufferId, - pub kind: TileKind, -} - -fn tile_kind(surface: &CompositeTileSurface, is_opaque: bool) -> TileKind { - match surface { - // Color tiles are, by definition, opaque. We might support non-opaque color - // tiles if we ever find pages that have a lot of these. - CompositeTileSurface::Color { .. } => TileKind::Opaque, - // Clear tiles have a special bucket - CompositeTileSurface::Clear => TileKind::Clear, - CompositeTileSurface::Texture { .. } - | CompositeTileSurface::ExternalSurface { .. } => { - // Texture surfaces get bucketed by opaque/alpha, for z-rejection - // on the Draw compositor mode. - if is_opaque { - TileKind::Opaque - } else { - TileKind::Alpha - } - } - } } pub enum ExternalSurfaceDependency { @@ -157,16 +104,11 @@ pub enum ExternalSurfaceDependency { /// For now, we support only YUV images as compositor surfaces, but in future /// this will also support RGBA images. pub struct ExternalSurfaceDescriptor { - // Rectangle of this surface in owning picture's coordinate space pub local_rect: PictureRect, - // Rectangle of this surface in the compositor local space - // TODO(gw): Switch this to CompositorSurfaceRect (CompositorSurfacePixel) in compositor trait. - pub surface_rect: DeviceRect, - // Rectangle of this surface in true device pixels + pub world_rect: WorldRect, pub device_rect: DeviceRect, pub local_clip_rect: PictureRect, pub clip_rect: DeviceRect, - pub transform: CompositorSurfaceTransform, pub image_rendering: ImageRendering, pub z_id: ZBufferId, pub dependency: ExternalSurfaceDependency, @@ -184,6 +126,7 @@ pub struct ExternalSurfaceDescriptor { #[derive(Debug, Copy, Clone)] pub struct ExternalPlaneDescriptor { pub texture: TextureSource, + pub texture_layer: i32, pub uv_rect: TexelRect, } @@ -191,6 +134,7 @@ impl ExternalPlaneDescriptor { fn invalid() -> Self { ExternalPlaneDescriptor { texture: TextureSource::Invalid, + texture_layer: 0, uv_rect: TexelRect::invalid(), } } @@ -198,13 +142,9 @@ impl ExternalPlaneDescriptor { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone)] pub struct ResolvedExternalSurfaceIndex(pub usize); -impl ResolvedExternalSurfaceIndex { - pub const INVALID: ResolvedExternalSurfaceIndex = ResolvedExternalSurfaceIndex(usize::MAX); -} - #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum ResolvedExternalSurfaceColorData { @@ -246,13 +186,10 @@ pub enum CompositorConfig { /// then the operating system supports a form of 'partial present' where /// only dirty regions of the framebuffer need to be updated. max_partial_present_rects: usize, - /// If this is true, WR must draw the previous frames' dirty regions when + /// If this is true, WR would draw the previous frame's dirty region when /// doing a partial present. This is used for EGL which requires the front /// buffer to always be fully consistent. draw_previous_partial_present_regions: bool, - /// A client provided interface to a compositor handling partial present. - /// Required if webrender must query the backbuffer's age. - partial_present: Option<Box<dyn PartialPresentCompositor>>, }, /// Use a native OS compositor to draw tiles. This requires clients to implement /// the Compositor trait, but can be significantly more power efficient on operating @@ -278,18 +215,6 @@ impl CompositorConfig { } } } - - pub fn partial_present(&mut self) -> Option<&mut Box<dyn PartialPresentCompositor>> { - match self { - CompositorConfig::Native { .. } => { - None - } - CompositorConfig::Draw { ref mut partial_present, .. } => { - partial_present.as_mut() - } - } - } - } impl Default for CompositorConfig { @@ -298,7 +223,6 @@ impl Default for CompositorConfig { CompositorConfig::Draw { max_partial_present_rects: 0, draw_previous_partial_present_regions: false, - partial_present: None, } } } @@ -321,8 +245,8 @@ pub enum CompositorKind { Native { /// Maximum dirty rects per compositor surface. max_update_rects: usize, - /// The capabilities of the underlying platform. - capabilities: CompositorCapabilities, + /// The virtual surface size used by underlying platform. + virtual_surface_size: i32, }, } @@ -340,28 +264,17 @@ impl CompositorKind { pub fn get_virtual_surface_size(&self) -> i32 { match self { CompositorKind::Draw { .. } => 0, - CompositorKind::Native { capabilities, .. } => capabilities.virtual_surface_size, - } - } - - // We currently only support transforms for Native compositors, - // bug 1655639 is filed for adding support to Draw. - pub fn supports_transforms(&self) -> bool { - match self { - CompositorKind::Draw { .. } => false, - CompositorKind::Native { .. } => true, + CompositorKind::Native { virtual_surface_size, .. } => *virtual_surface_size, } } +} - pub fn should_redraw_on_invalidation(&self) -> bool { - match self { - CompositorKind::Draw { max_partial_present_rects, .. } => { - // When partial present is enabled, we need to force redraw. - *max_partial_present_rects > 0 - } - CompositorKind::Native { capabilities, .. } => capabilities.redraw_on_invalidation, - } - } +/// Information about an opaque surface used to occlude tiles. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +struct Occluder { + z_id: ZBufferId, + device_rect: DeviceIntRect, } /// The backing surface kind for a tile. Same as `TileSurface`, minus @@ -403,14 +316,13 @@ pub struct CompositeTileDescriptor { #[derive(PartialEq, Clone)] pub struct CompositeSurfaceDescriptor { pub surface_id: Option<NativeSurfaceId>, + pub offset: DevicePoint, pub clip_rect: DeviceRect, - pub transform: CompositorSurfaceTransform, // A list of image keys and generations that this compositor surface // depends on. This avoids composites being skipped when the only // thing that has changed is the generation of an compositor surface // image dependency. pub image_dependencies: [ImageDependency; 3], - pub image_rendering: ImageRendering, // List of the surface information for each tile added to this virtual surface pub tile_descriptors: Vec<CompositeTileDescriptor>, } @@ -433,48 +345,6 @@ impl CompositeDescriptor { } } -pub struct CompositeStatePreallocator { - tiles: Preallocator, - external_surfaces: Preallocator, - occluders: Preallocator, - occluders_events: Preallocator, - occluders_active: Preallocator, - descriptor_surfaces: Preallocator, -} - -impl CompositeStatePreallocator { - pub fn record(&mut self, state: &CompositeState) { - self.tiles.record_vec(&state.tiles); - self.external_surfaces.record_vec(&state.external_surfaces); - self.occluders.record_vec(&state.occluders.occluders); - self.occluders_events.record_vec(&state.occluders.events); - self.occluders_active.record_vec(&state.occluders.active); - self.descriptor_surfaces.record_vec(&state.descriptor.surfaces); - } - - pub fn preallocate(&self, state: &mut CompositeState) { - self.tiles.preallocate_vec(&mut state.tiles); - self.external_surfaces.preallocate_vec(&mut state.external_surfaces); - self.occluders.preallocate_vec(&mut state.occluders.occluders); - self.occluders_events.preallocate_vec(&mut state.occluders.events); - self.occluders_active.preallocate_vec(&mut state.occluders.active); - self.descriptor_surfaces.preallocate_vec(&mut state.descriptor.surfaces); - } -} - -impl Default for CompositeStatePreallocator { - fn default() -> Self { - CompositeStatePreallocator { - tiles: Preallocator::new(56), - external_surfaces: Preallocator::new(0), - occluders: Preallocator::new(16), - occluders_events: Preallocator::new(32), - occluders_active: Preallocator::new(16), - descriptor_surfaces: Preallocator::new(8), - } - } -} - /// The list of tiles to be drawn this frame #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -482,10 +352,12 @@ pub struct CompositeState { // TODO(gw): Consider splitting up CompositeState into separate struct types depending // on the selected compositing mode. Many of the fields in this state struct // are only applicable to either Native or Draw compositing mode. - /// List of tiles to be drawn by the Draw compositor. - /// Tiles are accumulated in this vector and sorted from front to back at the end of the - /// frame. - pub tiles: Vec<CompositeTile>, + /// List of opaque tiles to be drawn by the Draw compositor. + pub opaque_tiles: Vec<CompositeTile>, + /// List of alpha tiles to be drawn by the Draw compositor. + pub alpha_tiles: Vec<CompositeTile>, + /// List of clear tiles to be drawn by the Draw compositor. + pub clear_tiles: Vec<CompositeTile>, /// List of primitives that were promoted to be compositor surfaces. pub external_surfaces: Vec<ResolvedExternalSurface>, /// Used to generate z-id values for tiles in the Draw compositor mode. @@ -499,14 +371,14 @@ pub struct CompositeState { pub dirty_rects_are_valid: bool, /// The kind of compositor for picture cache tiles (e.g. drawn by WR, or OS compositor) pub compositor_kind: CompositorKind, + /// Picture caching may be disabled dynamically, based on debug flags, pinch zoom etc. + pub picture_caching_is_enabled: bool, /// The overall device pixel scale, used for tile occlusion conversions. global_device_pixel_scale: DevicePixelScale, /// List of registered occluders - pub occluders: Occluders, + occluders: Vec<Occluder>, /// Description of the surfaces and properties that are being composited. pub descriptor: CompositeDescriptor, - /// Debugging information about the state of the pictures cached for regression testing. - pub picture_cache_debug: PictureCacheDebugInfo, } impl CompositeState { @@ -514,20 +386,31 @@ impl CompositeState { /// during each frame construction and passed to the renderer. pub fn new( compositor_kind: CompositorKind, + mut picture_caching_is_enabled: bool, global_device_pixel_scale: DevicePixelScale, max_depth_ids: i32, - dirty_rects_are_valid: bool, ) -> Self { + // The native compositor interface requires picture caching to work, so + // force it here and warn if it was disabled. + if let CompositorKind::Native { .. } = compositor_kind { + if !picture_caching_is_enabled { + warn!("Picture caching cannot be disabled in native compositor config"); + } + picture_caching_is_enabled = true; + } + CompositeState { - tiles: Vec::new(), - z_generator: ZBufferIdGenerator::new(max_depth_ids), - dirty_rects_are_valid, + opaque_tiles: Vec::new(), + alpha_tiles: Vec::new(), + clear_tiles: Vec::new(), + z_generator: ZBufferIdGenerator::new(0, max_depth_ids), + dirty_rects_are_valid: true, compositor_kind, + picture_caching_is_enabled, global_device_pixel_scale, - occluders: Occluders::new(), + occluders: Vec::new(), descriptor: CompositeDescriptor::empty(), external_surfaces: Vec::new(), - picture_cache_debug: PictureCacheDebugInfo::new(), } } @@ -540,7 +423,41 @@ impl CompositeState { ) { let device_rect = (rect * self.global_device_pixel_scale).round().to_i32(); - self.occluders.push(device_rect, z_id); + self.occluders.push(Occluder { + device_rect, + z_id, + }); + } + + /// Returns true if a tile with the specified rectangle and z_id + /// is occluded by an opaque surface in front of it. + pub fn is_tile_occluded( + &self, + z_id: ZBufferId, + device_rect: DeviceRect, + ) -> bool { + // It's often the case that a tile is only occluded by considering multiple + // picture caches in front of it (for example, the background tiles are + // often occluded by a combination of the content slice + the scrollbar slices). + + // The basic algorithm is: + // For every occluder: + // If this occluder is in front of the tile we are querying: + // Clip the occluder rectangle to the query rectangle. + // Calculate the total non-overlapping area of those clipped occluders. + // If the cumulative area of those occluders is the same as the area of the query tile, + // Then the entire tile must be occluded and can be skipped during rasterization and compositing. + + // Get the reference area we will compare against. + let device_rect = device_rect.round().to_i32(); + let ref_area = device_rect.size.width * device_rect.size.height; + + // Calculate the non-overlapping area of the valid occluders. + let cover_area = area_of_occluders(&self.occluders, z_id, &device_rect); + debug_assert!(cover_area <= ref_area); + + // Check if the tile area is completely covered + ref_area == cover_area } /// Add a picture cache to be composited @@ -553,318 +470,276 @@ impl CompositeState { gpu_cache: &mut GpuCache, deferred_resolves: &mut Vec<DeferredResolve>, ) { - for sub_slice in &tile_cache.sub_slices { - let mut visible_opaque_tile_count = 0; - let mut visible_alpha_tile_count = 0; - let mut opaque_tile_descriptors = Vec::new(); - let mut alpha_tile_descriptors = Vec::new(); - let mut surface_device_rect = DeviceRect::zero(); - - for tile in sub_slice.tiles.values() { - if !tile.is_visible { - // This can occur when a tile is found to be occluded during frame building. - continue; - } + let mut visible_opaque_tile_count = 0; + let mut visible_alpha_tile_count = 0; + let mut opaque_tile_descriptors = Vec::new(); + let mut alpha_tile_descriptors = Vec::new(); + + for tile in tile_cache.tiles.values() { + if !tile.is_visible { + // This can occur when a tile is found to be occluded during frame building. + continue; + } - let device_rect = (tile.world_tile_rect * global_device_pixel_scale).round(); - let surface = tile.surface.as_ref().expect("no tile surface set!"); - - // Accumulate this tile into the overall surface bounds. This is used below - // to clamp the size of the supplied clip rect to a reasonable value. - // NOTE: This clip rect must include the device_valid_rect rather than - // the tile device rect. This ensures that in the case of a picture - // cache slice that is smaller than a single tile, the clip rect in - // the composite descriptor will change if the position of that slice - // is changed. Otherwise, WR may conclude that no composite is needed - // if the tile itself was not invalidated due to changing content. - // See bug #1675414 for more detail. - surface_device_rect = surface_device_rect.union(&tile.device_valid_rect); - - let descriptor = CompositeTileDescriptor { - surface_kind: surface.into(), - tile_id: tile.id, - }; + let device_rect = (tile.world_tile_rect * global_device_pixel_scale).round(); + let surface = tile.surface.as_ref().expect("no tile surface set!"); - let (surface, is_opaque) = match surface { - TileSurface::Color { color } => { - (CompositeTileSurface::Color { color: *color }, true) - } - TileSurface::Clear => { - // Clear tiles are rendered with blend mode pre-multiply-dest-out. - (CompositeTileSurface::Clear, false) - } - TileSurface::Texture { descriptor, .. } => { - let surface = descriptor.resolve(resource_cache, tile_cache.current_tile_size); - ( - CompositeTileSurface::Texture { surface }, - tile.is_opaque - ) - } - }; + let descriptor = CompositeTileDescriptor { + surface_kind: surface.into(), + tile_id: tile.id, + }; - if is_opaque { - opaque_tile_descriptors.push(descriptor); - visible_opaque_tile_count += 1; - } else { - alpha_tile_descriptors.push(descriptor); - visible_alpha_tile_count += 1; + let (surface, is_opaque) = match surface { + TileSurface::Color { color } => { + (CompositeTileSurface::Color { color: *color }, true) } + TileSurface::Clear => { + (CompositeTileSurface::Clear, false) + } + TileSurface::Texture { descriptor, .. } => { + let surface = descriptor.resolve(resource_cache, tile_cache.current_tile_size); + ( + CompositeTileSurface::Texture { surface }, + // If a tile has compositor surface intersecting with it, we need to + // respect the tile.is_opaque property even if the overall tile cache + // is opaque. In this case, the tile.is_opaque property is required + // in order to ensure correct draw order with compositor surfaces. + tile.is_opaque || (!tile.has_compositor_surface && tile_cache.is_opaque()), + ) + } + }; - let tile = CompositeTile { - kind: tile_kind(&surface, is_opaque), - surface, - rect: device_rect, - valid_rect: tile.device_valid_rect.translate(-device_rect.origin.to_vector()), - dirty_rect: tile.device_dirty_rect.translate(-device_rect.origin.to_vector()), - clip_rect: device_clip_rect, - transform: None, - z_id: tile.z_id, - }; - - self.tiles.push(tile); - } - - // Sort the tile descriptor lists, since iterating values in the tile_cache.tiles - // hashmap doesn't provide any ordering guarantees, but we want to detect the - // composite descriptor as equal if the tiles list is the same, regardless of - // ordering. - opaque_tile_descriptors.sort_by_key(|desc| desc.tile_id); - alpha_tile_descriptors.sort_by_key(|desc| desc.tile_id); - - // If the clip rect is too large, it can cause accuracy and correctness problems - // for some native compositors (specifically, CoreAnimation in this case). To - // work around that, intersect the supplied clip rect with the current bounds - // of the native surface, which ensures it is a reasonable size. - let surface_clip_rect = device_clip_rect - .intersection(&surface_device_rect) - .unwrap_or(DeviceRect::zero()); - - // Add opaque surface before any compositor surfaces - if visible_opaque_tile_count > 0 { - self.descriptor.surfaces.push( - CompositeSurfaceDescriptor { - surface_id: sub_slice.native_surface.as_ref().map(|s| s.opaque), - clip_rect: surface_clip_rect, - transform: CompositorSurfaceTransform::translation( - tile_cache.device_position.x, - tile_cache.device_position.y, - 0.0, - ), - image_dependencies: [ImageDependency::INVALID; 3], - image_rendering: ImageRendering::CrispEdges, - tile_descriptors: opaque_tile_descriptors, - } - ); - } - - // Add alpha tiles after opaque surfaces - if visible_alpha_tile_count > 0 { - self.descriptor.surfaces.push( - CompositeSurfaceDescriptor { - surface_id: sub_slice.native_surface.as_ref().map(|s| s.alpha), - clip_rect: surface_clip_rect, - transform: CompositorSurfaceTransform::translation( - tile_cache.device_position.x, - tile_cache.device_position.y, - 0.0, - ), - image_dependencies: [ImageDependency::INVALID; 3], - image_rendering: ImageRendering::CrispEdges, - tile_descriptors: alpha_tile_descriptors, - } - ); + if is_opaque { + opaque_tile_descriptors.push(descriptor); + visible_opaque_tile_count += 1; + } else { + alpha_tile_descriptors.push(descriptor); + visible_alpha_tile_count += 1; } - // For each compositor surface that was promoted, build the - // information required for the compositor to draw it - for compositor_surface in &sub_slice.compositor_surfaces { - let external_surface = &compositor_surface.descriptor; - - let clip_rect = external_surface - .clip_rect - .intersection(&device_clip_rect) - .unwrap_or_else(DeviceRect::zero); - - let required_plane_count = - match external_surface.dependency { - ExternalSurfaceDependency::Yuv { format, .. } => { - format.get_plane_num() - }, - ExternalSurfaceDependency::Rgb { .. } => { - 1 - } - }; + let tile = CompositeTile { + surface, + rect: device_rect, + valid_rect: tile.device_valid_rect.translate(-device_rect.origin.to_vector()), + dirty_rect: tile.device_dirty_rect.translate(-device_rect.origin.to_vector()), + clip_rect: device_clip_rect, + z_id: tile.z_id, + }; - let mut image_dependencies = [ImageDependency::INVALID; 3]; + self.push_tile(tile, is_opaque); + } - for i in 0 .. required_plane_count { - let dependency = match external_surface.dependency { - ExternalSurfaceDependency::Yuv { image_dependencies, .. } => { - image_dependencies[i] - }, - ExternalSurfaceDependency::Rgb { image_dependency, .. } => { - image_dependency - } - }; - image_dependencies[i] = dependency; + // Sort the tile descriptor lists, since iterating values in the tile_cache.tiles + // hashmap doesn't provide any ordering guarantees, but we want to detect the + // composite descriptor as equal if the tiles list is the same, regardless of + // ordering. + opaque_tile_descriptors.sort_by_key(|desc| desc.tile_id); + alpha_tile_descriptors.sort_by_key(|desc| desc.tile_id); + + // Add opaque surface before any compositor surfaces + if visible_opaque_tile_count > 0 { + self.descriptor.surfaces.push( + CompositeSurfaceDescriptor { + surface_id: tile_cache.native_surface.as_ref().map(|s| s.opaque), + offset: tile_cache.device_position, + clip_rect: device_clip_rect, + image_dependencies: [ImageDependency::INVALID; 3], + tile_descriptors: opaque_tile_descriptors, } + ); + } - // Get a new z_id for each compositor surface, to ensure correct ordering - // when drawing with the simple (Draw) compositor, and to schedule compositing - // of any required updates into the surfaces. - let needs_external_surface_update = match self.compositor_kind { - CompositorKind::Draw { .. } => true, - _ => external_surface.update_params.is_some(), + // For each compositor surface that was promoted, build the + // information required for the compositor to draw it + for external_surface in &tile_cache.external_surfaces { + + let mut planes = [ + ExternalPlaneDescriptor::invalid(), + ExternalPlaneDescriptor::invalid(), + ExternalPlaneDescriptor::invalid(), + ]; + + // Step through the image keys, and build a plane descriptor for each + let required_plane_count = + match external_surface.dependency { + ExternalSurfaceDependency::Yuv { format, .. } => { + format.get_plane_num() + }, + ExternalSurfaceDependency::Rgb { .. } => { + 1 + } }; - let external_surface_index = if needs_external_surface_update { - let external_surface_index = self.compute_external_surface_dependencies( - &external_surface, - &image_dependencies, - required_plane_count, - resource_cache, - gpu_cache, - deferred_resolves, - ); - if external_surface_index == ResolvedExternalSurfaceIndex::INVALID { - continue; + let mut valid_plane_count = 0; + + let mut image_dependencies = [ImageDependency::INVALID; 3]; + + for i in 0 .. required_plane_count { + let dependency = match external_surface.dependency { + ExternalSurfaceDependency::Yuv { image_dependencies, .. } => { + image_dependencies[i] + }, + ExternalSurfaceDependency::Rgb { image_dependency, .. } => { + image_dependency } - external_surface_index - } else { - ResolvedExternalSurfaceIndex::INVALID }; + image_dependencies[i] = dependency; - let surface = CompositeTileSurface::ExternalSurface { external_surface_index }; - let tile = CompositeTile { - kind: tile_kind(&surface, compositor_surface.is_opaque), - surface, - rect: external_surface.surface_rect, - valid_rect: external_surface.surface_rect.translate(-external_surface.surface_rect.origin.to_vector()), - dirty_rect: external_surface.surface_rect.translate(-external_surface.surface_rect.origin.to_vector()), - clip_rect, - transform: Some(external_surface.transform), - z_id: external_surface.z_id, + let request = ImageRequest { + key: dependency.key, + rendering: external_surface.image_rendering, + tile: None, }; - // Add a surface descriptor for each compositor surface. For the Draw - // compositor, this is used to avoid composites being skipped by adding - // a dependency on the compositor surface external image keys / generations. - self.descriptor.surfaces.push( - CompositeSurfaceDescriptor { - surface_id: external_surface.native_surface_id, - clip_rect, - transform: external_surface.transform, - image_dependencies: image_dependencies, - image_rendering: external_surface.image_rendering, - tile_descriptors: Vec::new(), - } + let cache_item = resolve_image( + request, + resource_cache, + gpu_cache, + deferred_resolves, ); - self.tiles.push(tile); + if cache_item.texture_id != TextureSource::Invalid { + valid_plane_count += 1; + let plane = &mut planes[i]; + *plane = ExternalPlaneDescriptor { + texture: cache_item.texture_id, + texture_layer: cache_item.texture_layer, + uv_rect: cache_item.uv_rect.into(), + }; + } } - } - } - fn compute_external_surface_dependencies( - &mut self, - external_surface: &ExternalSurfaceDescriptor, - image_dependencies: &[ImageDependency; 3], - required_plane_count: usize, - resource_cache: &ResourceCache, - gpu_cache: &mut GpuCache, - deferred_resolves: &mut Vec<DeferredResolve>, - ) -> ResolvedExternalSurfaceIndex { - let mut planes = [ - ExternalPlaneDescriptor::invalid(), - ExternalPlaneDescriptor::invalid(), - ExternalPlaneDescriptor::invalid(), - ]; - - let mut valid_plane_count = 0; - for i in 0 .. required_plane_count { - let request = ImageRequest { - key: image_dependencies[i].key, - rendering: external_surface.image_rendering, - tile: None, - }; + // Check if there are valid images added for each YUV plane + if valid_plane_count < required_plane_count { + warn!("Warnings: skip a YUV/RGB compositor surface, found {}/{} valid images", + valid_plane_count, + required_plane_count, + ); + continue; + } - let cache_item = resolve_image( - request, - resource_cache, - gpu_cache, - deferred_resolves, - ); + let clip_rect = external_surface + .clip_rect + .intersection(&device_clip_rect) + .unwrap_or_else(DeviceRect::zero); - if cache_item.texture_id != TextureSource::Invalid { - valid_plane_count += 1; - let plane = &mut planes[i]; - *plane = ExternalPlaneDescriptor { - texture: cache_item.texture_id, - uv_rect: cache_item.uv_rect.into(), - }; - } - } + // Get a new z_id for each compositor surface, to ensure correct ordering + // when drawing with the simple (Draw) compositor. - // Check if there are valid images added for each YUV plane - if valid_plane_count < required_plane_count { - warn!("Warnings: skip a YUV/RGB compositor surface, found {}/{} valid images", - valid_plane_count, - required_plane_count, - ); - return ResolvedExternalSurfaceIndex::INVALID; - } + let surface = CompositeTileSurface::ExternalSurface { + external_surface_index: ResolvedExternalSurfaceIndex(self.external_surfaces.len()), + }; - let external_surface_index = ResolvedExternalSurfaceIndex(self.external_surfaces.len()); + // If the external surface descriptor reports that the native surface + // needs to be updated, create an update params tuple for the renderer + // to use. + let update_params = external_surface.update_params.map(|surface_size| { + ( + external_surface.native_surface_id.expect("bug: no native surface!"), + surface_size + ) + }); + + match external_surface.dependency { + ExternalSurfaceDependency::Yuv{ color_space, format, rescale, .. } => { + + let image_buffer_kind = get_buffer_kind(planes[0].texture); + + self.external_surfaces.push(ResolvedExternalSurface { + color_data: ResolvedExternalSurfaceColorData::Yuv { + image_dependencies, + planes, + color_space, + format, + rescale, + }, + image_buffer_kind, + update_params, + }); + }, + ExternalSurfaceDependency::Rgb{ flip_y, .. } => { + + let image_buffer_kind = get_buffer_kind(planes[0].texture); + + self.external_surfaces.push(ResolvedExternalSurface { + color_data: ResolvedExternalSurfaceColorData::Rgb { + image_dependency: image_dependencies[0], + plane: planes[0], + flip_y, + }, + image_buffer_kind, + update_params, + }); + }, + } - // If the external surface descriptor reports that the native surface - // needs to be updated, create an update params tuple for the renderer - // to use. - let update_params = external_surface.update_params.map(|surface_size| { - ( - external_surface.native_surface_id.expect("bug: no native surface!"), - surface_size - ) - }); + let tile = CompositeTile { + surface, + rect: external_surface.device_rect, + valid_rect: external_surface.device_rect.translate(-external_surface.device_rect.origin.to_vector()), + dirty_rect: external_surface.device_rect.translate(-external_surface.device_rect.origin.to_vector()), + clip_rect, + z_id: external_surface.z_id, + }; - match external_surface.dependency { - ExternalSurfaceDependency::Yuv{ color_space, format, rescale, .. } => { + // Add a surface descriptor for each compositor surface. For the Draw + // compositor, this is used to avoid composites being skipped by adding + // a dependency on the compositor surface external image keys / generations. + self.descriptor.surfaces.push( + CompositeSurfaceDescriptor { + surface_id: external_surface.native_surface_id, + offset: tile.rect.origin, + clip_rect: tile.clip_rect, + image_dependencies: image_dependencies, + tile_descriptors: Vec::new(), + } + ); - let image_buffer_kind = planes[0].texture.image_buffer_kind(); + self.push_tile(tile, true); + } - self.external_surfaces.push(ResolvedExternalSurface { - color_data: ResolvedExternalSurfaceColorData::Yuv { - image_dependencies: *image_dependencies, - planes, - color_space, - format, - rescale, - }, - image_buffer_kind, - update_params, - }); - }, - ExternalSurfaceDependency::Rgb{ flip_y, .. } => { - - let image_buffer_kind = planes[0].texture.image_buffer_kind(); - - // Only propagate flip_y if the compositor doesn't support transforms, - // since otherwise it'll be handled as part of the transform. - self.external_surfaces.push(ResolvedExternalSurface { - color_data: ResolvedExternalSurfaceColorData::Rgb { - image_dependency: image_dependencies[0], - plane: planes[0], - flip_y: flip_y && !self.compositor_kind.supports_transforms(), - }, - image_buffer_kind, - update_params, - }); - }, + // Add alpha / overlay tiles after compositor surfaces + if visible_alpha_tile_count > 0 { + self.descriptor.surfaces.push( + CompositeSurfaceDescriptor { + surface_id: tile_cache.native_surface.as_ref().map(|s| s.alpha), + offset: tile_cache.device_position, + clip_rect: device_clip_rect, + image_dependencies: [ImageDependency::INVALID; 3], + tile_descriptors: alpha_tile_descriptors, + } + ); } - external_surface_index } - pub fn end_frame(&mut self) { - // Sort tiles from front to back. - self.tiles.sort_by_key(|tile| tile.z_id.0); + /// Add a tile to the appropriate array, depending on tile properties and compositor mode. + fn push_tile( + &mut self, + tile: CompositeTile, + is_opaque: bool, + ) { + match tile.surface { + CompositeTileSurface::Color { .. } => { + // Color tiles are, by definition, opaque. We might support non-opaque color + // tiles if we ever find pages that have a lot of these. + self.opaque_tiles.push(tile); + } + CompositeTileSurface::Clear => { + // Clear tiles have a special bucket + self.clear_tiles.push(tile); + } + CompositeTileSurface::Texture { .. } => { + // Texture surfaces get bucketed by opaque/alpha, for z-rejection + // on the Draw compositor mode. + if is_opaque { + self.opaque_tiles.push(tile); + } else { + self.alpha_tiles.push(tile); + } + } + CompositeTileSurface::ExternalSurface { .. } => { + self.opaque_tiles.push(tile); + } + } } } @@ -921,38 +796,10 @@ pub struct NativeSurfaceInfo { } #[repr(C)] -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CompositorCapabilities { - /// The virtual surface size used by the underlying platform. pub virtual_surface_size: i32, - /// Whether the compositor requires redrawing on invalidation. - pub redraw_on_invalidation: bool, -} - -impl Default for CompositorCapabilities { - fn default() -> Self { - // The default set of compositor capabilities for a given platform. - // These should only be modified if a compositor diverges specifically - // from the default behavior so that compositors don't have to track - // which changes to this structure unless necessary. - CompositorCapabilities { - virtual_surface_size: 0, - redraw_on_invalidation: false, - } - } } -/// The transform type to apply to Compositor surfaces. -// TODO: Should transform from CompositorSurfacePixel instead, but this requires a cleanup of the -// Compositor API to use CompositorSurface-space geometry instead of Device-space where necessary -// to avoid a bunch of noisy cast_unit calls and make it actually type-safe. May be difficult due -// to pervasive use of Device-space nomenclature inside WR. -// pub struct CompositorSurfacePixel; -// pub type CompositorSurfaceTransform = Transform3D<f32, CompositorSurfacePixel, DevicePixel>; -pub type CompositorSurfaceTransform = Transform3D<f32, DevicePixel, DevicePixel>; - /// Defines an interface to a native (OS level) compositor. If supplied /// by the client application, then picture cache slices will be /// composited by the OS compositor, rather than drawn via WR batches. @@ -966,16 +813,6 @@ pub trait Compositor { is_opaque: bool, ); - /// Create a new OS compositor surface that can be used with an - /// existing ExternalImageId, instead of being drawn to by WebRender. - /// Surfaces created by this can only be used with attach_external_image, - /// and not create_tile/destroy_tile/bind/unbind. - fn create_external_surface( - &mut self, - id: NativeSurfaceId, - is_opaque: bool, - ); - /// Destroy the surface with the specified id. WR may call this /// at any time the surface is no longer required (including during /// renderer deinit). It's the responsibility of the embedder @@ -999,26 +836,6 @@ pub trait Compositor { id: NativeTileId, ); - /// Attaches an ExternalImageId to an OS compositor surface created - /// by create_external_surface, and uses that as the contents of - /// the surface. It is expected that a single surface will have - /// many different images attached (like one for each video frame). - fn attach_external_image( - &mut self, - id: NativeSurfaceId, - external_image: ExternalImageId - ); - - /// Mark a tile as invalid before any surfaces are queued for - /// composition and before it is updated with bind. This is useful - /// for early composition, allowing for dependency tracking of which - /// surfaces can be composited early while others are still updating. - fn invalidate_tile( - &mut self, - _id: NativeTileId, - _valid_rect: DeviceIntRect - ) {} - /// Bind this surface such that WR can issue OpenGL commands /// that will target the surface. Returns an (x, y) offset /// where WR should draw into the surface. This can be set @@ -1055,26 +872,14 @@ pub trait Compositor { // We might need to change the interface to maintain a visual // tree that can be mutated? // TODO(gw): We might need to add a concept of a hierachy in future. + // TODO(gw): In future, expand to support a more complete transform matrix. fn add_surface( &mut self, id: NativeSurfaceId, - transform: CompositorSurfaceTransform, + position: DeviceIntPoint, clip_rect: DeviceIntRect, - image_rendering: ImageRendering, ); - /// Notify the compositor that all tiles have been invalidated and all - /// native surfaces have been added, thus it is safe to start compositing - /// valid surfaces. The dirty rects array allows native compositors that - /// support partial present to skip copying unchanged areas. - /// Optionally provides a set of rectangles for the areas known to be - /// opaque, this is currently only computed if the caller is SwCompositor. - fn start_compositing( - &mut self, - _dirty_rects: &[DeviceIntRect], - _opaque_rects: &[DeviceIntRect], - ) {} - /// Commit any changes in the compositor tree for this frame. WR calls /// this once when all surface and visual updates are complete, to signal /// that the OS composite transaction should be applied. @@ -1092,244 +897,111 @@ pub trait Compositor { fn get_capabilities(&self) -> CompositorCapabilities; } -/// Information about the underlying data buffer of a mapped tile. -#[repr(C)] -#[derive(Copy, Clone)] -pub struct MappedTileInfo { - pub data: *mut c_void, - pub stride: i32, -} - -/// Descriptor for a locked surface that will be directly composited by SWGL. -#[repr(C)] -pub struct SWGLCompositeSurfaceInfo { - /// The number of YUV planes in the surface. 0 indicates non-YUV BGRA. - /// 1 is interleaved YUV. 2 is NV12. 3 is planar YUV. - pub yuv_planes: u32, - /// Textures for planes of the surface, or 0 if not applicable. - pub textures: [u32; 3], - /// Color space of surface if using a YUV format. - pub color_space: YuvColorSpace, - /// Color depth of surface if using a YUV format. - pub color_depth: ColorDepth, - /// The actual source surface size before transformation. - pub size: DeviceIntSize, -} - -/// A Compositor variant that supports mapping tiles into CPU memory. -pub trait MappableCompositor: Compositor { - /// Map a tile's underlying buffer so it can be used as the backing for - /// a SWGL framebuffer. This is intended to be a replacement for 'bind' - /// in any compositors that intend to directly interoperate with SWGL - /// while supporting some form of native layers. - fn map_tile( - &mut self, - id: NativeTileId, - dirty_rect: DeviceIntRect, - valid_rect: DeviceIntRect, - ) -> Option<MappedTileInfo>; - - /// Unmap a tile that was was previously mapped via map_tile to signal - /// that SWGL is done rendering to the buffer. - fn unmap_tile(&mut self); - - fn lock_composite_surface( - &mut self, - ctx: *mut c_void, - external_image_id: ExternalImageId, - composite_info: *mut SWGLCompositeSurfaceInfo, - ) -> bool; - fn unlock_composite_surface(&mut self, ctx: *mut c_void, external_image_id: ExternalImageId); -} - -/// Defines an interface to a non-native (application-level) Compositor which handles -/// partial present. This is required if webrender must query the backbuffer's age. -/// TODO: Use the Compositor trait for native and non-native compositors, and integrate -/// this functionality there. -pub trait PartialPresentCompositor { - /// Allows webrender to specify the total region that will be rendered to this frame, - /// ie the frame's dirty region and some previous frames' dirty regions, if applicable - /// (calculated using the buffer age). Must be called before anything has been rendered - /// to the main framebuffer. - fn set_buffer_damage_region(&mut self, rects: &[DeviceIntRect]); -} - -/// Information about an opaque surface used to occlude tiles. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -struct Occluder { +/// Return the total area covered by a set of occluders, accounting for +/// overlapping areas between those rectangles. +fn area_of_occluders( + occluders: &[Occluder], z_id: ZBufferId, - device_rect: DeviceIntRect, -} - -// Whether this event is the start or end of a rectangle -#[derive(Debug)] -enum OcclusionEventKind { - Begin, - End, -} + clip_rect: &DeviceIntRect, +) -> i32 { + // This implementation is based on the article https://leetcode.com/articles/rectangle-area-ii/. + // This is not a particularly efficient implementation (it skips building segment trees), however + // we typically use this where the length of the rectangles array is < 10, so simplicity is more important. + + let mut area = 0; + + // Whether this event is the start or end of a rectangle + #[derive(Debug)] + enum EventKind { + Begin, + End, + } -// A list of events on the y-axis, with the rectangle range that it affects on the x-axis -#[derive(Debug)] -struct OcclusionEvent { - y: i32, - x_range: ops::Range<i32>, - kind: OcclusionEventKind, -} + // A list of events on the y-axis, with the rectangle range that it affects on the x-axis + #[derive(Debug)] + struct Event { + y: i32, + x_range: ops::Range<i32>, + kind: EventKind, + } -impl OcclusionEvent { - fn new(y: i32, kind: OcclusionEventKind, x0: i32, x1: i32) -> Self { - OcclusionEvent { - y, - x_range: ops::Range { - start: x0, - end: x1, - }, - kind, + impl Event { + fn new(y: i32, kind: EventKind, x0: i32, x1: i32) -> Self { + Event { + y, + x_range: ops::Range { + start: x0, + end: x1, + }, + kind, + } } } -} - -/// List of registered occluders. -/// -/// Also store a couple of vectors for reuse. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct Occluders { - occluders: Vec<Occluder>, - - // The two vectors below are kept to avoid unnecessary reallocations in area(). - - #[cfg_attr(feature = "serde", serde(skip))] - events: Vec<OcclusionEvent>, - - #[cfg_attr(feature = "serde", serde(skip))] - active: Vec<ops::Range<i32>>, -} -impl Occluders { - fn new() -> Self { - Occluders { - occluders: Vec::new(), - events: Vec::new(), - active: Vec::new(), + // Step through each rectangle and build the y-axis event list + let mut events = Vec::with_capacity(occluders.len() * 2); + for occluder in occluders { + // Only consider occluders in front of this rect + if occluder.z_id.0 > z_id.0 { + // Clip the source rect to the rectangle we care about, since we only + // want to record area for the tile we are comparing to. + if let Some(rect) = occluder.device_rect.intersection(clip_rect) { + let x0 = rect.origin.x; + let x1 = x0 + rect.size.width; + events.push(Event::new(rect.origin.y, EventKind::Begin, x0, x1)); + events.push(Event::new(rect.origin.y + rect.size.height, EventKind::End, x0, x1)); + } } } - fn push(&mut self, device_rect: DeviceIntRect, z_id: ZBufferId) { - self.occluders.push(Occluder { device_rect, z_id }); + // If we didn't end up with any valid events, the area must be 0 + if events.is_empty() { + return 0; } - /// Returns true if a tile with the specified rectangle and z_id - /// is occluded by an opaque surface in front of it. - pub fn is_tile_occluded( - &mut self, - z_id: ZBufferId, - device_rect: DeviceRect, - ) -> bool { - // It's often the case that a tile is only occluded by considering multiple - // picture caches in front of it (for example, the background tiles are - // often occluded by a combination of the content slice + the scrollbar slices). - - // The basic algorithm is: - // For every occluder: - // If this occluder is in front of the tile we are querying: - // Clip the occluder rectangle to the query rectangle. - // Calculate the total non-overlapping area of those clipped occluders. - // If the cumulative area of those occluders is the same as the area of the query tile, - // Then the entire tile must be occluded and can be skipped during rasterization and compositing. - - // Get the reference area we will compare against. - let device_rect = device_rect.round().to_i32(); - let ref_area = device_rect.size.width * device_rect.size.height; - - // Calculate the non-overlapping area of the valid occluders. - let cover_area = self.area(z_id, &device_rect); - debug_assert!(cover_area <= ref_area); - - // Check if the tile area is completely covered - ref_area == cover_area - } - - /// Return the total area covered by a set of occluders, accounting for - /// overlapping areas between those rectangles. - fn area( - &mut self, - z_id: ZBufferId, - clip_rect: &DeviceIntRect, - ) -> i32 { - // This implementation is based on the article https://leetcode.com/articles/rectangle-area-ii/. - // This is not a particularly efficient implementation (it skips building segment trees), however - // we typically use this where the length of the rectangles array is < 10, so simplicity is more important. - - self.events.clear(); - self.active.clear(); - - let mut area = 0; - - // Step through each rectangle and build the y-axis event list - for occluder in &self.occluders { - // Only consider occluders in front of this rect - if occluder.z_id.0 < z_id.0 { - // Clip the source rect to the rectangle we care about, since we only - // want to record area for the tile we are comparing to. - if let Some(rect) = occluder.device_rect.intersection(clip_rect) { - let x0 = rect.origin.x; - let x1 = x0 + rect.size.width; - self.events.push(OcclusionEvent::new(rect.origin.y, OcclusionEventKind::Begin, x0, x1)); - self.events.push(OcclusionEvent::new(rect.origin.y + rect.size.height, OcclusionEventKind::End, x0, x1)); - } + // Sort the events by y-value + events.sort_by_key(|e| e.y); + let mut active: Vec<ops::Range<i32>> = Vec::new(); + let mut cur_y = events[0].y; + + // Step through each y interval + for event in &events { + // This is the dimension of the y-axis we are accumulating areas for + let dy = event.y - cur_y; + + // If we have active events covering x-ranges in this y-interval, process them + if dy != 0 && !active.is_empty() { + assert!(dy > 0); + + // Step through the x-ranges, ordered by x0 of each event + active.sort_by_key(|i| i.start); + let mut query = 0; + let mut cur = active[0].start; + + // Accumulate the non-overlapping x-interval that contributes to area for this y-interval. + for interval in &active { + cur = interval.start.max(cur); + query += (interval.end - cur).max(0); + cur = cur.max(interval.end); } - } - // If we didn't end up with any valid events, the area must be 0 - if self.events.is_empty() { - return 0; + // Accumulate total area for this y-interval + area += query * dy; } - // Sort the events by y-value - self.events.sort_by_key(|e| e.y); - let mut cur_y = self.events[0].y; - - // Step through each y interval - for event in &self.events { - // This is the dimension of the y-axis we are accumulating areas for - let dy = event.y - cur_y; - - // If we have active events covering x-ranges in this y-interval, process them - if dy != 0 && !self.active.is_empty() { - assert!(dy > 0); - - // Step through the x-ranges, ordered by x0 of each event - self.active.sort_by_key(|i| i.start); - let mut query = 0; - let mut cur = self.active[0].start; - - // Accumulate the non-overlapping x-interval that contributes to area for this y-interval. - for interval in &self.active { - cur = interval.start.max(cur); - query += (interval.end - cur).max(0); - cur = cur.max(interval.end); - } - - // Accumulate total area for this y-interval - area += query * dy; + // Update the active events list + match event.kind { + EventKind::Begin => { + active.push(event.x_range.clone()); } - - // Update the active events list - match event.kind { - OcclusionEventKind::Begin => { - self.active.push(event.x_range.clone()); - } - OcclusionEventKind::End => { - let index = self.active.iter().position(|i| *i == event.x_range).unwrap(); - self.active.remove(index); - } + EventKind::End => { + let index = active.iter().position(|i| *i == event.x_range).unwrap(); + active.remove(index); } - - cur_y = event.y; } - area + cur_y = event.y; } + + area } diff --git a/third_party/webrender/webrender/src/compositor/sw_compositor.rs b/third_party/webrender/webrender/src/compositor/sw_compositor.rs deleted file mode 100644 index 03cd2b20b66..00000000000 --- a/third_party/webrender/webrender/src/compositor/sw_compositor.rs +++ /dev/null @@ -1,1484 +0,0 @@ -/* 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 gleam::{gl, gl::Gl}; -use std::cell::{Cell, UnsafeCell}; -use std::collections::{hash_map::HashMap, VecDeque}; -use std::ops::{Deref, DerefMut, Range}; -use std::ptr; -use std::sync::atomic::{AtomicBool, AtomicI8, AtomicIsize, AtomicPtr, AtomicU32, AtomicU8, Ordering}; -use std::sync::{Arc, Condvar, Mutex, MutexGuard}; -use std::thread; -use crate::{ - api::units::*, api::ColorDepth, api::ExternalImageId, api::ImageRendering, api::YuvColorSpace, Compositor, - CompositorCapabilities, CompositorSurfaceTransform, NativeSurfaceId, NativeSurfaceInfo, NativeTileId, - host_utils::{thread_started, thread_stopped}, MappableCompositor, SWGLCompositeSurfaceInfo, -}; - -pub struct SwTile { - x: i32, - y: i32, - fbo_id: u32, - color_id: u32, - valid_rect: DeviceIntRect, - /// Composition of tiles must be ordered such that any tiles that may overlap - /// an invalidated tile in an earlier surface only get drawn after that tile - /// is actually updated. We store a count of the number of overlapping invalid - /// here, that gets decremented when the invalid tiles are finally updated so - /// that we know when it is finally safe to draw. Must use a Cell as we might - /// be analyzing multiple tiles and surfaces - overlaps: Cell<u32>, - /// Whether the tile's contents has been invalidated - invalid: Cell<bool>, - /// Graph node for job dependencies of this tile - graph_node: SwCompositeGraphNodeRef, -} - -impl SwTile { - fn new(x: i32, y: i32) -> Self { - SwTile { - x, - y, - fbo_id: 0, - color_id: 0, - valid_rect: DeviceIntRect::zero(), - overlaps: Cell::new(0), - invalid: Cell::new(false), - graph_node: SwCompositeGraphNode::new(), - } - } - - /// The offset of the tile in the local space of the surface before any - /// transform is applied. - fn origin(&self, surface: &SwSurface) -> DeviceIntPoint { - DeviceIntPoint::new(self.x * surface.tile_size.width, self.y * surface.tile_size.height) - } - - /// The offset valid rect positioned within the local space of the surface - /// before any transform is applied. - fn local_bounds(&self, surface: &SwSurface) -> DeviceIntRect { - self.valid_rect.translate(self.origin(surface).to_vector()) - } - - /// Bounds used for determining overlap dependencies. This may either be the - /// full tile bounds or the actual valid rect, depending on whether the tile - /// is invalidated this frame. These bounds are more conservative as such and - /// may differ from the precise bounds used to actually composite the tile. - fn overlap_rect( - &self, - surface: &SwSurface, - transform: &CompositorSurfaceTransform, - clip_rect: &DeviceIntRect, - ) -> Option<DeviceIntRect> { - let bounds = self.local_bounds(surface); - let device_rect = transform.outer_transformed_rect(&bounds.to_f32())?.round_out().to_i32(); - device_rect.intersection(clip_rect) - } - - /// Determine if the tile's bounds may overlap the dependency rect if it were - /// to be composited at the given position. - fn may_overlap( - &self, - surface: &SwSurface, - transform: &CompositorSurfaceTransform, - clip_rect: &DeviceIntRect, - dep_rect: &DeviceIntRect, - ) -> bool { - self.overlap_rect(surface, transform, clip_rect) - .map_or(false, |r| r.intersects(dep_rect)) - } - - /// Get valid source and destination rectangles for composition of the tile - /// within a surface, bounded by the clipping rectangle. May return None if - /// it falls outside of the clip rect. - fn composite_rects( - &self, - surface: &SwSurface, - transform: &CompositorSurfaceTransform, - clip_rect: &DeviceIntRect, - ) -> Option<(DeviceIntRect, DeviceIntRect, bool)> { - // Offset the valid rect to the appropriate surface origin. - let valid = self.local_bounds(surface); - // The destination rect is the valid rect transformed and then clipped. - let dest_rect = transform.outer_transformed_rect(&valid.to_f32())?.round_out().to_i32(); - if !dest_rect.intersects(clip_rect) { - return None; - } - // To get a valid source rect, we need to inverse transform the clipped destination rect to find out the effect - // of the clip rect in source-space. After this, we subtract off the source-space valid rect origin to get - // a source rect that is now relative to the surface origin rather than absolute. - let inv_transform = transform.inverse()?; - let src_rect = inv_transform - .outer_transformed_rect(&dest_rect.to_f32())? - .round() - .to_i32() - .translate(-valid.origin.to_vector()); - Some((src_rect, dest_rect, transform.m22 < 0.0)) - } -} - -pub struct SwSurface { - tile_size: DeviceIntSize, - is_opaque: bool, - tiles: Vec<SwTile>, - /// An attached external image for this surface. - external_image: Option<ExternalImageId>, - /// Descriptor for the external image if successfully locked for composite. - composite_surface: Option<SWGLCompositeSurfaceInfo>, -} - -impl SwSurface { - fn new(tile_size: DeviceIntSize, is_opaque: bool) -> Self { - SwSurface { - tile_size, - is_opaque, - tiles: Vec::new(), - external_image: None, - composite_surface: None, - } - } - - /// Conserative approximation of local bounds of the surface by combining - /// the local bounds of all enclosed tiles. - fn local_bounds(&self) -> DeviceIntRect { - let mut bounds = DeviceIntRect::zero(); - for tile in &self.tiles { - bounds = bounds.union(&tile.local_bounds(self)); - } - bounds - } - - /// The transformed and clipped conservative device-space bounds of the - /// surface. - fn device_bounds( - &self, - transform: &CompositorSurfaceTransform, - clip_rect: &DeviceIntRect, - ) -> Option<DeviceIntRect> { - let bounds = self.local_bounds(); - let device_rect = transform.outer_transformed_rect(&bounds.to_f32())?.round_out().to_i32(); - device_rect.intersection(clip_rect) - } -} - -fn image_rendering_to_gl_filter(filter: ImageRendering) -> gl::GLenum { - match filter { - ImageRendering::Pixelated => gl::NEAREST, - ImageRendering::Auto | ImageRendering::CrispEdges => gl::LINEAR, - } -} - -/// A source for a composite job which can either be a single BGRA locked SWGL -/// resource or a collection of SWGL resources representing a YUV surface. -#[derive(Clone)] -enum SwCompositeSource { - BGRA(swgl::LockedResource), - YUV( - swgl::LockedResource, - swgl::LockedResource, - swgl::LockedResource, - YuvColorSpace, - ColorDepth, - ), -} - -/// Mark ExternalImage's renderer field as safe to send to SwComposite thread. -unsafe impl Send for SwCompositeSource {} - -/// A tile composition job to be processed by the SwComposite thread. -/// Stores relevant details about the tile and where to composite it. -#[derive(Clone)] -struct SwCompositeJob { - /// Locked texture that will be unlocked immediately following the job - locked_src: SwCompositeSource, - /// Locked framebuffer that may be shared among many jobs - locked_dst: swgl::LockedResource, - src_rect: DeviceIntRect, - dst_rect: DeviceIntRect, - clipped_dst: DeviceIntRect, - opaque: bool, - flip_y: bool, - filter: ImageRendering, - /// The total number of bands for this job - num_bands: u8, -} - -impl SwCompositeJob { - /// Process a composite job - fn process(&self, band_index: i32) { - // Bands are allocated in reverse order, but we want to process them in increasing order. - let num_bands = self.num_bands as i32; - let band_index = num_bands - 1 - band_index; - // Calculate the Y extents for the job's band, starting at the current index and spanning to - // the following index. - let band_offset = (self.clipped_dst.size.height * band_index) / num_bands; - let band_height = (self.clipped_dst.size.height * (band_index + 1)) / num_bands - band_offset; - // Create a rect that is the intersection of the band with the clipped dest - let band_clip = DeviceIntRect::new( - DeviceIntPoint::new(self.clipped_dst.origin.x, self.clipped_dst.origin.y + band_offset), - DeviceIntSize::new(self.clipped_dst.size.width, band_height), - ); - match self.locked_src { - SwCompositeSource::BGRA(ref resource) => { - self.locked_dst.composite( - resource, - self.src_rect.origin.x, - self.src_rect.origin.y, - self.src_rect.size.width, - self.src_rect.size.height, - self.dst_rect.origin.x, - self.dst_rect.origin.y, - self.dst_rect.size.width, - self.dst_rect.size.height, - self.opaque, - self.flip_y, - image_rendering_to_gl_filter(self.filter), - band_clip.origin.x, - band_clip.origin.y, - band_clip.size.width, - band_clip.size.height, - ); - } - SwCompositeSource::YUV(ref y, ref u, ref v, color_space, color_depth) => { - let swgl_color_space = match color_space { - YuvColorSpace::Rec601 => swgl::YUVColorSpace::Rec601, - YuvColorSpace::Rec709 => swgl::YUVColorSpace::Rec709, - YuvColorSpace::Rec2020 => swgl::YUVColorSpace::Rec2020, - YuvColorSpace::Identity => swgl::YUVColorSpace::Identity, - }; - self.locked_dst.composite_yuv( - y, - u, - v, - swgl_color_space, - color_depth.bit_depth(), - self.src_rect.origin.x, - self.src_rect.origin.y, - self.src_rect.size.width, - self.src_rect.size.height, - self.dst_rect.origin.x, - self.dst_rect.origin.y, - self.dst_rect.size.width, - self.dst_rect.size.height, - self.flip_y, - band_clip.origin.x, - band_clip.origin.y, - band_clip.size.width, - band_clip.size.height, - ); - } - } - } -} - -/// A reference to a SwCompositeGraph node that can be passed from the render -/// thread to the SwComposite thread. Consistency of mutation is ensured in -/// SwCompositeGraphNode via use of Atomic operations that prevent more than -/// one thread from mutating SwCompositeGraphNode at once. This avoids using -/// messy and not-thread-safe RefCells or expensive Mutexes inside the graph -/// node and at least signals to the compiler that potentially unsafe coercions -/// are occurring. -#[derive(Clone)] -struct SwCompositeGraphNodeRef(Arc<UnsafeCell<SwCompositeGraphNode>>); - -impl SwCompositeGraphNodeRef { - fn new(graph_node: SwCompositeGraphNode) -> Self { - SwCompositeGraphNodeRef(Arc::new(UnsafeCell::new(graph_node))) - } - - fn get(&self) -> &SwCompositeGraphNode { - unsafe { &*self.0.get() } - } - - fn get_mut(&self) -> &mut SwCompositeGraphNode { - unsafe { &mut *self.0.get() } - } - - fn get_ptr_mut(&self) -> *mut SwCompositeGraphNode { - self.0.get() - } -} - -unsafe impl Send for SwCompositeGraphNodeRef {} - -impl Deref for SwCompositeGraphNodeRef { - type Target = SwCompositeGraphNode; - - fn deref(&self) -> &Self::Target { - self.get() - } -} - -impl DerefMut for SwCompositeGraphNodeRef { - fn deref_mut(&mut self) -> &mut Self::Target { - self.get_mut() - } -} - -/// Dependency graph of composite jobs to be completed. Keeps a list of child jobs that are dependent on the completion of this job. -/// Also keeps track of the number of parent jobs that this job is dependent upon before it can be processed. Once there are no more -/// in-flight parent jobs that it depends on, the graph node is finally added to the job queue for processing. -struct SwCompositeGraphNode { - /// Job to be queued for this graph node once ready. - job: Option<SwCompositeJob>, - /// The number of remaining bands associated with this job. When this is - /// non-zero and the node has no more parents left, then the node is being - /// actively used by the composite thread to process jobs. Once it hits - /// zero, the owning thread (which brought it to zero) can safely retire - /// the node as no other thread is using it. - remaining_bands: AtomicU8, - /// The number of bands that are available for processing. - available_bands: AtomicI8, - /// Count of parents this graph node depends on. While this is non-zero the - /// node must ensure that it is only being actively mutated by the render - /// thread and otherwise never being accessed by the render thread. - parents: AtomicU32, - /// Graph nodes of child jobs that are dependent on this job - children: Vec<SwCompositeGraphNodeRef>, -} - -unsafe impl Sync for SwCompositeGraphNode {} - -impl SwCompositeGraphNode { - fn new() -> SwCompositeGraphNodeRef { - SwCompositeGraphNodeRef::new(SwCompositeGraphNode { - job: None, - remaining_bands: AtomicU8::new(0), - available_bands: AtomicI8::new(0), - parents: AtomicU32::new(0), - children: Vec::new(), - }) - } - - /// Reset the node's state for a new frame - fn reset(&mut self) { - self.job = None; - self.remaining_bands.store(0, Ordering::SeqCst); - self.available_bands.store(0, Ordering::SeqCst); - // Initialize parents to 1 as sentinel dependency for uninitialized job - // to avoid queuing unitialized job as unblocked child dependency. - self.parents.store(1, Ordering::SeqCst); - self.children.clear(); - } - - /// Add a dependent child node to dependency list. Update its parent count. - fn add_child(&mut self, child: SwCompositeGraphNodeRef) { - child.parents.fetch_add(1, Ordering::SeqCst); - self.children.push(child); - } - - /// Install a job for this node. Return whether or not the job has any unprocessed parents - /// that would block immediate composition. - fn set_job(&mut self, job: SwCompositeJob, num_bands: u8) -> bool { - self.job = Some(job); - self.remaining_bands.store(num_bands, Ordering::SeqCst); - self.available_bands.store(num_bands as _, Ordering::SeqCst); - // Subtract off the sentinel parent dependency now that job is initialized and check - // whether there are any remaining parent dependencies to see if this job is ready. - self.parents.fetch_sub(1, Ordering::SeqCst) <= 1 - } - - /// Take an available band if possible. Also return whether there are no more bands left - /// so the caller may properly clean up after. - fn take_band(&self) -> (Option<i32>, bool) { - let available = self.available_bands.fetch_sub(1, Ordering::SeqCst); - if available > 0 { - (Some(available as i32 - 1), available == 1) - } else { - (None, true) - } - } - - /// Try to take the job from this node for processing and then process it within the current band. - fn process_job(&self, band_index: i32) { - if let Some(ref job) = self.job { - job.process(band_index); - } - } - - /// After processing a band, check all child dependencies and remove this parent from - /// their dependency counts. If applicable, queue the new child bands for composition. - fn unblock_children(&mut self, thread: &SwCompositeThread) { - if self.remaining_bands.fetch_sub(1, Ordering::SeqCst) > 1 { - return; - } - // Clear the job to release any locked resources. - self.job = None; - let mut lock = None; - for child in self.children.drain(..) { - // Remove the child's parent dependency on this node. If there are no more - // parent dependencies left, send the child job bands for composition. - if child.parents.fetch_sub(1, Ordering::SeqCst) <= 1 { - if lock.is_none() { - lock = Some(thread.lock()); - } - thread.send_job(lock.as_mut().unwrap(), child); - } - } - } -} - -/// The SwComposite thread processes a queue of composite jobs, also signaling -/// via a condition when all available jobs have been processed, as tracked by -/// the job count. -struct SwCompositeThread { - /// Queue of available composite jobs - jobs: Mutex<SwCompositeJobQueue>, - /// Cache of the current job being processed. This maintains a pointer to - /// the contents of the SwCompositeGraphNodeRef, which is safe due to the - /// fact that SwCompositor maintains a strong reference to the contents - /// in an SwTile to keep it alive while this is in use. - current_job: AtomicPtr<SwCompositeGraphNode>, - /// Count of unprocessed jobs still in the queue - job_count: AtomicIsize, - /// Condition signaled when either there are jobs available to process or - /// there are no more jobs left to process. Otherwise stated, this signals - /// when the job queue transitions from an empty to non-empty state or from - /// a non-empty to empty state. - jobs_available: Condvar, - /// Whether all available jobs have been processed. - jobs_completed: AtomicBool, -} - -/// The SwCompositeThread struct is shared between the SwComposite thread -/// and the rendering thread so that both ends can access the job queue. -unsafe impl Sync for SwCompositeThread {} - -/// A FIFO queue of composite jobs to be processed. -type SwCompositeJobQueue = VecDeque<SwCompositeGraphNodeRef>; - -/// Locked access to the composite job queue. -type SwCompositeThreadLock<'a> = MutexGuard<'a, SwCompositeJobQueue>; - -impl SwCompositeThread { - /// Create the SwComposite thread. Requires a SWGL context in which - /// to do the composition. - fn new() -> Arc<SwCompositeThread> { - let info = Arc::new(SwCompositeThread { - jobs: Mutex::new(SwCompositeJobQueue::new()), - current_job: AtomicPtr::new(ptr::null_mut()), - job_count: AtomicIsize::new(0), - jobs_available: Condvar::new(), - jobs_completed: AtomicBool::new(false), - }); - let result = info.clone(); - let thread_name = "SwComposite"; - thread::Builder::new() - .name(thread_name.into()) - // The composite thread only calls into SWGL to composite, and we - // have potentially many composite threads for different windows, - // so using the default stack size is excessive. A reasonably small - // stack size should be more than enough for SWGL and reduce memory - // overhead. - .stack_size(32 * 1024) - .spawn(move || { - thread_started(thread_name); - // Process any available jobs. This will return a non-Ok - // result when the job queue is dropped, causing the thread - // to eventually exit. - while let Some((job, band)) = info.take_job(true) { - info.process_job(job, band); - } - thread_stopped(); - }) - .expect("Failed creating SwComposite thread"); - result - } - - fn deinit(&self) { - // Force the job count to be negative to signal the thread needs to exit. - self.job_count.store(isize::MIN / 2, Ordering::SeqCst); - // Wake up the thread in case it is blocked waiting for new jobs - self.jobs_available.notify_all(); - } - - /// Process a job contained in a dependency graph node received from the job queue. - /// Any child dependencies will be unblocked as appropriate after processing. The - /// job count will be updated to reflect this. - fn process_job(&self, graph_node: &mut SwCompositeGraphNode, band: i32) { - // Do the actual processing of the job contained in this node. - graph_node.process_job(band); - // Unblock any child dependencies now that this job has been processed. - graph_node.unblock_children(self); - // Decrement the job count. - self.job_count.fetch_sub(1, Ordering::SeqCst); - } - - /// Queue a tile for composition by adding to the queue and increasing the job count. - fn queue_composite( - &self, - locked_src: SwCompositeSource, - locked_dst: swgl::LockedResource, - src_rect: DeviceIntRect, - dst_rect: DeviceIntRect, - clip_rect: DeviceIntRect, - opaque: bool, - flip_y: bool, - filter: ImageRendering, - mut graph_node: SwCompositeGraphNodeRef, - job_queue: &mut SwCompositeJobQueue, - ) { - // For jobs that would span a sufficiently large destination rectangle, split - // it into multiple horizontal bands so that multiple threads can process them. - let clipped_dst = match dst_rect.intersection(&clip_rect) { - Some(clipped_dst) => clipped_dst, - None => return, - }; - - let num_bands = if clipped_dst.size.width >= 64 && clipped_dst.size.height >= 64 { - (clipped_dst.size.height / 64).min(4) as u8 - } else { - 1 - }; - let job = SwCompositeJob { - locked_src, - locked_dst, - src_rect, - dst_rect, - clipped_dst, - opaque, - flip_y, - filter, - num_bands, - }; - self.job_count.fetch_add(num_bands as isize, Ordering::SeqCst); - if graph_node.set_job(job, num_bands) { - self.send_job(job_queue, graph_node); - } - } - - fn prepare_for_composites(&self) { - // Initialize the job count to 1 to prevent spurious signaling of job completion - // in the middle of queuing compositing jobs until we're actually waiting for - // composition. - self.job_count.store(1, Ordering::SeqCst); - } - - /// Lock the thread for access to the job queue. - fn lock(&self) -> SwCompositeThreadLock { - self.jobs.lock().unwrap() - } - - /// Send a job to the composite thread by adding it to the job queue. - /// Signal that this job has been added in case the queue was empty and the - /// SwComposite thread is waiting for jobs. - fn send_job(&self, queue: &mut SwCompositeJobQueue, job: SwCompositeGraphNodeRef) { - if queue.is_empty() { - self.jobs_completed.store(false, Ordering::SeqCst); - self.jobs_available.notify_all(); - } - queue.push_back(job); - } - - /// Try to get a band of work from the currently cached job when available. - /// If there is a job, but it has no available bands left, null out the job - /// so that other threads do not bother checking the job. - fn try_take_job(&self) -> Option<(&mut SwCompositeGraphNode, i32)> { - let current_job_ptr = self.current_job.load(Ordering::SeqCst); - if let Some(current_job) = unsafe { current_job_ptr.as_mut() } { - let (band, done) = current_job.take_band(); - if done { - let _ = self.current_job.compare_exchange( - current_job_ptr, - ptr::null_mut(), - Ordering::SeqCst, - Ordering::SeqCst, - ); - } - if let Some(band) = band { - return Some((current_job, band)); - } - } - return None; - } - - /// Take a job from the queue. Optionally block waiting for jobs to become - /// available if this is called from the SwComposite thread. - fn take_job(&self, wait: bool) -> Option<(&mut SwCompositeGraphNode, i32)> { - // First try checking the cached job outside the scope of the mutex. - // For jobs that have multiple bands, this allows us to avoid having - // to lock the mutex multiple times to check the job for each band. - if let Some((job, band)) = self.try_take_job() { - return Some((job, band)); - } - // Lock the job queue while checking for available jobs. The lock - // won't be held while the job is processed later outside of this - // function so that other threads can pull from the queue meanwhile. - let mut jobs = self.lock(); - loop { - // While inside the mutex, check the cached job again to see if it - // has been updated. - if let Some((job, band)) = self.try_take_job() { - return Some((job, band)); - } - // If no cached job was available, try to take a job from the queue - // and install it as the current job. - if let Some(job) = jobs.pop_front() { - self.current_job.store(job.get_ptr_mut(), Ordering::SeqCst); - continue; - } - // Otherwise, the job queue is currently empty. Depending on the - // value of the job count we may either wait for jobs to become - // available or exit. - if wait { - self.jobs_completed.store(true, Ordering::SeqCst); - } - match self.job_count.load(Ordering::SeqCst) { - // If we completed all available jobs, signal completion. If - // waiting inside the SwCompositeThread, then block waiting for - // more jobs to become available in a new frame. Otherwise, - // return immediately. - 0 => { - self.jobs_available.notify_all(); - if !wait { - return None; - } - } - // A negative job count signals to exit immediately. - job_count if job_count < 0 => return None, - _ => {} - } - // The SwCompositeThread needs to wait for jobs to become - // available to avoid busy waiting on the queue. - jobs = self.jobs_available.wait(jobs).unwrap(); - } - } - - /// Wait for all queued composition jobs to be processed. - /// Instead of blocking on the SwComposite thread to complete all jobs, - /// this may steal some jobs and attempt to process them while waiting. - /// This may optionally process jobs synchronously. When normally doing - /// asynchronous processing, the graph dependencies are relied upon to - /// properly order the jobs, which makes it safe for the render thread - /// to steal jobs from the composite thread without violating those - /// dependencies. Synchronous processing just disables this job stealing - /// so that the composite thread always handles the jobs in the order - /// they were queued without having to rely upon possibly unavailable - /// graph dependencies. - fn wait_for_composites(&self, sync: bool) { - // Subtract off the bias to signal we're now waiting on composition and - // need to know if jobs are completed. - self.job_count.fetch_sub(1, Ordering::SeqCst); - // If processing asynchronously, try to steal jobs from the composite - // thread if it is busy. - if !sync { - while let Some((job, band)) = self.take_job(false) { - self.process_job(job, band); - } - // Once there are no more jobs, just fall through to waiting - // synchronously for the composite thread to finish processing. - } - // If processing synchronously, just wait for the composite thread - // to complete processing any in-flight jobs, then bail. - let mut jobs = self.lock(); - // If the job count is non-zero here, then there are in-flight jobs. - while !self.jobs_completed.load(Ordering::SeqCst) { - jobs = self.jobs_available.wait(jobs).unwrap(); - } - } - - /// Check if there is a non-zero job count (including sentinel job) that - /// would indicate we are starting to already process jobs in the composite - /// thread. - fn is_busy_compositing(&self) -> bool { - self.job_count.load(Ordering::SeqCst) > 0 - } -} - -/// Parameters describing how to composite a surface within a frame -type FrameSurface = ( - NativeSurfaceId, - CompositorSurfaceTransform, - DeviceIntRect, - ImageRendering, -); - -/// Adapter for RenderCompositors to work with SWGL that shuttles between -/// WebRender and the RenderCompositr via the Compositor API. -pub struct SwCompositor { - gl: swgl::Context, - compositor: Box<dyn MappableCompositor>, - use_native_compositor: bool, - surfaces: HashMap<NativeSurfaceId, SwSurface>, - frame_surfaces: Vec<FrameSurface>, - /// Any surface added after we're already compositing (i.e. debug overlay) - /// needs to be processed after those frame surfaces. For simplicity we - /// store them in a separate queue that gets processed later. - late_surfaces: Vec<FrameSurface>, - cur_tile: NativeTileId, - /// The maximum tile size required for any of the allocated surfaces. - max_tile_size: DeviceIntSize, - /// Reuse the same depth texture amongst all tiles in all surfaces. - /// This depth texture must be big enough to accommodate the largest used - /// tile size for any surface. The maximum requested tile size is tracked - /// to ensure that this depth texture is at least that big. - depth_id: u32, - /// Instance of the SwComposite thread, only created if we are not relying - /// on a native RenderCompositor. - composite_thread: Option<Arc<SwCompositeThread>>, - /// SWGL locked resource for sharing framebuffer with SwComposite thread - locked_framebuffer: Option<swgl::LockedResource>, -} - -impl SwCompositor { - pub fn new( - gl: swgl::Context, - compositor: Box<dyn MappableCompositor>, - use_native_compositor: bool, - ) -> Self { - let depth_id = gl.gen_textures(1)[0]; - // Only create the SwComposite thread if we're not using a native render - // compositor. Thus, we are compositing into the main software framebuffer, - // which benefits from compositing asynchronously while updating tiles. - let composite_thread = if !use_native_compositor { - Some(SwCompositeThread::new()) - } else { - None - }; - SwCompositor { - gl, - compositor, - use_native_compositor, - surfaces: HashMap::new(), - frame_surfaces: Vec::new(), - late_surfaces: Vec::new(), - cur_tile: NativeTileId { - surface_id: NativeSurfaceId(0), - x: 0, - y: 0, - }, - max_tile_size: DeviceIntSize::zero(), - depth_id, - composite_thread, - locked_framebuffer: None, - } - } - - fn deinit_tile(&self, tile: &SwTile) { - self.gl.delete_framebuffers(&[tile.fbo_id]); - self.gl.delete_textures(&[tile.color_id]); - } - - fn deinit_surface(&self, surface: &SwSurface) { - for tile in &surface.tiles { - self.deinit_tile(tile); - } - } - - /// Attempt to occlude any queued surfaces with an opaque occluder rect. If - /// an existing surface is occluded, we attempt to restrict its clip rect - /// so long as it can remain a single clip rect. Existing frame surfaces - /// that are opaque will be fused if possible with the supplied occluder - /// rect to further try and restrict any underlying surfaces. - fn occlude_surfaces(&mut self) { - // Check if inner rect is fully included in outer rect - fn includes(outer: &Range<i32>, inner: &Range<i32>) -> bool { - outer.start <= inner.start && outer.end >= inner.end - } - - // Check if outer range overlaps either the start or end of a range. If - // there is overlap, return the portion of the inner range remaining - // after the overlap has been removed. - fn overlaps(outer: &Range<i32>, inner: &Range<i32>) -> Option<Range<i32>> { - if outer.start <= inner.start && outer.end >= inner.start { - Some(outer.end..inner.end.max(outer.end)) - } else if outer.start <= inner.end && outer.end >= inner.end { - Some(inner.start..outer.start.max(inner.start)) - } else { - None - } - } - - fn set_x_range(rect: &mut DeviceIntRect, range: &Range<i32>) { - rect.origin.x = range.start; - rect.size.width = range.end - range.start; - } - - fn set_y_range(rect: &mut DeviceIntRect, range: &Range<i32>) { - rect.origin.y = range.start; - rect.size.height = range.end - range.start; - } - - fn union(base: Range<i32>, extra: Range<i32>) -> Range<i32> { - base.start.min(extra.start)..base.end.max(extra.end) - } - - // Before we can try to occlude any surfaces, we need to fix their clip rects to tightly - // bound the valid region. The clip rect might otherwise enclose an invalid area that - // can't fully occlude anything even if the surface is opaque. - for &mut (ref id, ref transform, ref mut clip_rect, _) in &mut self.frame_surfaces { - if let Some(surface) = self.surfaces.get(id) { - // Restrict the clip rect to fall within the valid region of the surface. - *clip_rect = surface.device_bounds(transform, clip_rect).unwrap_or_default(); - } - } - - // For each frame surface, treat it as an occluder if it is non-empty and opaque. Look - // through the preceding surfaces to see if any can be occluded. - for occlude_index in 0..self.frame_surfaces.len() { - let (ref occlude_id, _, ref occlude_rect, _) = self.frame_surfaces[occlude_index]; - match self.surfaces.get(occlude_id) { - Some(occluder) if occluder.is_opaque && !occlude_rect.is_empty() => {} - _ => continue, - } - - // Traverse the queued surfaces for this frame in the reverse order of - // how they are composited, or rather, in order of visibility. For each - // surface, check if the occluder can restrict the clip rect such that - // the clip rect can remain a single rect. If the clip rect overlaps - // the occluder on one axis interval while remaining fully included in - // the occluder's other axis interval, then we can chop down the edge - // of the clip rect on the overlapped axis. Further, if the surface is - // opaque and its clip rect exactly matches the occluder rect on one - // axis interval while overlapping on the other, fuse it with the - // occluder rect before considering any underlying surfaces. - let (mut occlude_x, mut occlude_y) = (occlude_rect.x_range(), occlude_rect.y_range()); - for &mut (ref id, _, ref mut clip_rect, _) in self.frame_surfaces[..occlude_index].iter_mut().rev() { - if let Some(surface) = self.surfaces.get(id) { - let (clip_x, clip_y) = (clip_rect.x_range(), clip_rect.y_range()); - if includes(&occlude_x, &clip_x) { - if let Some(visible) = overlaps(&occlude_y, &clip_y) { - set_y_range(clip_rect, &visible); - if surface.is_opaque && occlude_x == clip_x { - occlude_y = union(occlude_y, visible); - } - } - } else if includes(&occlude_y, &clip_y) { - if let Some(visible) = overlaps(&occlude_x, &clip_x) { - set_x_range(clip_rect, &visible); - if surface.is_opaque && occlude_y == clip_y { - occlude_x = union(occlude_x, visible); - } - } - } - } - } - } - } - - /// Reset tile dependency state for a new frame. - fn reset_overlaps(&mut self) { - for surface in self.surfaces.values_mut() { - for tile in &mut surface.tiles { - tile.overlaps.set(0); - tile.invalid.set(false); - tile.graph_node.reset(); - } - } - } - - /// Computes an overlap count for a tile that falls within the given composite - /// destination rectangle. This requires checking all surfaces currently queued for - /// composition so far in this frame and seeing if they have any invalidated tiles - /// whose destination rectangles would also overlap the supplied tile. If so, then the - /// increment the overlap count to account for all such dependencies on invalid tiles. - /// Tiles with the same overlap count will still be drawn with a stable ordering in - /// the order the surfaces were queued, so it is safe to ignore other possible sources - /// of composition ordering dependencies, as the later queued tile will still be drawn - /// later than the blocking tiles within that stable order. We assume that the tile's - /// surface hasn't yet been added to the current frame list of surfaces to composite - /// so that we only process potential blockers from surfaces that would come earlier - /// in composition. - fn init_overlaps( - &self, - overlap_id: &NativeSurfaceId, - overlap_surface: &SwSurface, - overlap_tile: &SwTile, - overlap_transform: &CompositorSurfaceTransform, - overlap_clip_rect: &DeviceIntRect, - ) { - // Record an extra overlap for an invalid tile to track the tile's dependency - // on its own future update. - let mut overlaps = if overlap_tile.invalid.get() { 1 } else { 0 }; - - let overlap_rect = match overlap_tile.overlap_rect(overlap_surface, overlap_transform, overlap_clip_rect) { - Some(overlap_rect) => overlap_rect, - None => { - overlap_tile.overlaps.set(overlaps); - return; - } - }; - - for &(ref id, ref transform, ref clip_rect, _) in &self.frame_surfaces { - // We only want to consider surfaces that were added before the current one we're - // checking for overlaps. If we find that surface, then we're done. - if id == overlap_id { - break; - } - // If the surface's clip rect doesn't overlap the tile's rect, - // then there is no need to check any tiles within the surface. - if !overlap_rect.intersects(clip_rect) { - continue; - } - if let Some(surface) = self.surfaces.get(id) { - for tile in &surface.tiles { - // If there is a deferred tile that might overlap the destination rectangle, - // record the overlap. - if tile.may_overlap(surface, transform, clip_rect, &overlap_rect) { - if tile.overlaps.get() > 0 { - overlaps += 1; - } - // Regardless of whether this tile is deferred, if it has dependency - // overlaps, then record that it is potentially a dependency parent. - tile.graph_node.get_mut().add_child(overlap_tile.graph_node.clone()); - } - } - } - } - if overlaps > 0 { - // Has a dependency on some invalid tiles, so need to defer composition. - overlap_tile.overlaps.set(overlaps); - } - } - - /// Helper function that queues a composite job to the current locked framebuffer - fn queue_composite( - &self, - surface: &SwSurface, - transform: &CompositorSurfaceTransform, - clip_rect: &DeviceIntRect, - filter: ImageRendering, - tile: &SwTile, - job_queue: &mut SwCompositeJobQueue, - ) { - if let Some(ref composite_thread) = self.composite_thread { - if let Some((src_rect, dst_rect, flip_y)) = tile.composite_rects(surface, transform, clip_rect) { - let source = if surface.external_image.is_some() { - // If the surface has an attached external image, lock any textures supplied in the descriptor. - match surface.composite_surface { - Some(ref info) => match info.yuv_planes { - 0 => match self.gl.lock_texture(info.textures[0]) { - Some(texture) => SwCompositeSource::BGRA(texture), - None => return, - }, - 3 => match ( - self.gl.lock_texture(info.textures[0]), - self.gl.lock_texture(info.textures[1]), - self.gl.lock_texture(info.textures[2]), - ) { - (Some(y_texture), Some(u_texture), Some(v_texture)) => SwCompositeSource::YUV( - y_texture, - u_texture, - v_texture, - info.color_space, - info.color_depth, - ), - _ => return, - }, - _ => panic!("unsupported number of YUV planes: {}", info.yuv_planes), - }, - None => return, - } - } else if let Some(texture) = self.gl.lock_texture(tile.color_id) { - // Lock the texture representing the picture cache tile. - SwCompositeSource::BGRA(texture) - } else { - return; - }; - if let Some(ref framebuffer) = self.locked_framebuffer { - composite_thread.queue_composite( - source, - framebuffer.clone(), - src_rect, - dst_rect, - *clip_rect, - surface.is_opaque, - flip_y, - filter, - tile.graph_node.clone(), - job_queue, - ); - } - } - } - } - - /// Lock a surface with an attached external image for compositing. - fn try_lock_composite_surface(&mut self, id: &NativeSurfaceId) { - if let Some(surface) = self.surfaces.get_mut(id) { - if let Some(external_image) = surface.external_image { - // If the surface has an attached external image, attempt to lock the external image - // for compositing. Yields a descriptor of textures and data necessary for their - // interpretation on success. - let mut info = SWGLCompositeSurfaceInfo { - yuv_planes: 0, - textures: [0; 3], - color_space: YuvColorSpace::Identity, - color_depth: ColorDepth::Color8, - size: DeviceIntSize::zero(), - }; - assert!(!surface.tiles.is_empty()); - let mut tile = &mut surface.tiles[0]; - if self.compositor.lock_composite_surface(self.gl.into(), external_image, &mut info) { - tile.valid_rect = DeviceIntRect::from_size(info.size); - surface.composite_surface = Some(info); - } else { - tile.valid_rect = DeviceIntRect::zero(); - surface.composite_surface = None; - } - } - } - } - - /// Look for any attached external images that have been locked and then unlock them. - fn unlock_composite_surfaces(&mut self) { - for &(ref id, _, _, _) in self.frame_surfaces.iter().chain(self.late_surfaces.iter()) { - if let Some(surface) = self.surfaces.get_mut(id) { - if let Some(external_image) = surface.external_image { - if surface.composite_surface.is_some() { - self.compositor.unlock_composite_surface(self.gl.into(), external_image); - surface.composite_surface = None; - } - } - } - } - } - - /// Issue composites for any tiles that are no longer blocked following a tile update. - /// We process all surfaces and tiles in the order they were queued. - fn flush_composites(&self, tile_id: &NativeTileId, surface: &SwSurface, tile: &SwTile) { - let composite_thread = match &self.composite_thread { - Some(composite_thread) => composite_thread, - None => return, - }; - - // Look for the tile in the frame list and composite it if it has no dependencies. - let mut frame_surfaces = self - .frame_surfaces - .iter() - .skip_while(|&(ref id, _, _, _)| *id != tile_id.surface_id); - let (overlap_rect, mut lock) = match frame_surfaces.next() { - Some(&(_, ref transform, ref clip_rect, filter)) => { - // Remove invalid tile's update dependency. - if tile.invalid.get() { - tile.overlaps.set(tile.overlaps.get() - 1); - } - // If the tile still has overlaps, keep deferring it till later. - if tile.overlaps.get() > 0 { - return; - } - // Otherwise, the tile's dependencies are all resolved, so composite it. - let mut lock = composite_thread.lock(); - self.queue_composite(surface, transform, clip_rect, filter, tile, &mut lock); - // Finally, get the tile's overlap rect used for tracking dependencies - match tile.overlap_rect(surface, transform, clip_rect) { - Some(overlap_rect) => (overlap_rect, lock), - None => return, - } - } - None => return, - }; - - // Accumulate rects whose dependencies have been satisfied from this update. - // Store the union of all these bounds to quickly reject unaffected tiles. - let mut flushed_bounds = overlap_rect; - let mut flushed_rects = vec![overlap_rect]; - - // Check surfaces following the update in the frame list and see if they would overlap it. - for &(ref id, ref transform, ref clip_rect, filter) in frame_surfaces { - // If the clip rect doesn't overlap the conservative bounds, we can skip the whole surface. - if !flushed_bounds.intersects(clip_rect) { - continue; - } - if let Some(surface) = self.surfaces.get(&id) { - // Search through the surface's tiles for any blocked on this update and queue jobs for them. - for tile in &surface.tiles { - let mut overlaps = tile.overlaps.get(); - // Only check tiles that have existing unresolved dependencies - if overlaps == 0 { - continue; - } - // Get this tile's overlap rect for tracking dependencies - let overlap_rect = match tile.overlap_rect(surface, transform, clip_rect) { - Some(overlap_rect) => overlap_rect, - None => continue, - }; - // Do a quick check to see if the tile overlaps the conservative bounds. - if !overlap_rect.intersects(&flushed_bounds) { - continue; - } - // Decrement the overlap count if this tile is dependent on any flushed rects. - for flushed_rect in &flushed_rects { - if overlap_rect.intersects(flushed_rect) { - overlaps -= 1; - } - } - if overlaps != tile.overlaps.get() { - // If the overlap count changed, this tile had a dependency on some flush rects. - // If the count hit zero, it is ready to composite. - tile.overlaps.set(overlaps); - if overlaps == 0 { - self.queue_composite(surface, transform, clip_rect, filter, tile, &mut lock); - // Record that the tile got flushed to update any downwind dependencies. - flushed_bounds = flushed_bounds.union(&overlap_rect); - flushed_rects.push(overlap_rect); - } - } - } - } - } - } -} - -impl Compositor for SwCompositor { - fn create_surface( - &mut self, - id: NativeSurfaceId, - virtual_offset: DeviceIntPoint, - tile_size: DeviceIntSize, - is_opaque: bool, - ) { - if self.use_native_compositor { - self.compositor.create_surface(id, virtual_offset, tile_size, is_opaque); - } - self.max_tile_size = DeviceIntSize::new( - self.max_tile_size.width.max(tile_size.width), - self.max_tile_size.height.max(tile_size.height), - ); - self.surfaces.insert(id, SwSurface::new(tile_size, is_opaque)); - } - - fn create_external_surface(&mut self, id: NativeSurfaceId, is_opaque: bool) { - if self.use_native_compositor { - self.compositor.create_external_surface(id, is_opaque); - } - self.surfaces - .insert(id, SwSurface::new(DeviceIntSize::zero(), is_opaque)); - } - - fn destroy_surface(&mut self, id: NativeSurfaceId) { - if let Some(surface) = self.surfaces.remove(&id) { - self.deinit_surface(&surface); - } - if self.use_native_compositor { - self.compositor.destroy_surface(id); - } - } - - fn deinit(&mut self) { - if let Some(ref composite_thread) = self.composite_thread { - composite_thread.deinit(); - } - - for surface in self.surfaces.values() { - self.deinit_surface(surface); - } - - self.gl.delete_textures(&[self.depth_id]); - - if self.use_native_compositor { - self.compositor.deinit(); - } - } - - fn create_tile(&mut self, id: NativeTileId) { - if self.use_native_compositor { - self.compositor.create_tile(id); - } - if let Some(surface) = self.surfaces.get_mut(&id.surface_id) { - let mut tile = SwTile::new(id.x, id.y); - tile.color_id = self.gl.gen_textures(1)[0]; - tile.fbo_id = self.gl.gen_framebuffers(1)[0]; - self.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, tile.fbo_id); - self.gl.framebuffer_texture_2d( - gl::DRAW_FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - gl::TEXTURE_2D, - tile.color_id, - 0, - ); - self.gl.framebuffer_texture_2d( - gl::DRAW_FRAMEBUFFER, - gl::DEPTH_ATTACHMENT, - gl::TEXTURE_2D, - self.depth_id, - 0, - ); - self.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, 0); - - surface.tiles.push(tile); - } - } - - fn destroy_tile(&mut self, id: NativeTileId) { - if let Some(surface) = self.surfaces.get_mut(&id.surface_id) { - if let Some(idx) = surface.tiles.iter().position(|t| t.x == id.x && t.y == id.y) { - let tile = surface.tiles.remove(idx); - self.deinit_tile(&tile); - } - } - if self.use_native_compositor { - self.compositor.destroy_tile(id); - } - } - - fn attach_external_image(&mut self, id: NativeSurfaceId, external_image: ExternalImageId) { - if self.use_native_compositor { - self.compositor.attach_external_image(id, external_image); - } - if let Some(surface) = self.surfaces.get_mut(&id) { - // Surfaces with attached external images have a single tile at the origin encompassing - // the entire surface. - assert!(surface.tile_size.is_empty()); - surface.external_image = Some(external_image); - if surface.tiles.is_empty() { - surface.tiles.push(SwTile::new(0, 0)); - } - } - } - - fn invalidate_tile(&mut self, id: NativeTileId, valid_rect: DeviceIntRect) { - if self.use_native_compositor { - self.compositor.invalidate_tile(id, valid_rect); - } - if let Some(surface) = self.surfaces.get_mut(&id.surface_id) { - if let Some(tile) = surface.tiles.iter_mut().find(|t| t.x == id.x && t.y == id.y) { - tile.invalid.set(true); - tile.valid_rect = valid_rect; - } - } - } - - fn bind(&mut self, id: NativeTileId, dirty_rect: DeviceIntRect, valid_rect: DeviceIntRect) -> NativeSurfaceInfo { - let mut surface_info = NativeSurfaceInfo { - origin: DeviceIntPoint::zero(), - fbo_id: 0, - }; - - self.cur_tile = id; - - if let Some(surface) = self.surfaces.get_mut(&id.surface_id) { - if let Some(tile) = surface.tiles.iter_mut().find(|t| t.x == id.x && t.y == id.y) { - assert_eq!(tile.valid_rect, valid_rect); - if valid_rect.is_empty() { - return surface_info; - } - - let mut stride = 0; - let mut buf = ptr::null_mut(); - if self.use_native_compositor { - if let Some(tile_info) = self.compositor.map_tile(id, dirty_rect, valid_rect) { - stride = tile_info.stride; - buf = tile_info.data; - } - } - self.gl.set_texture_buffer( - tile.color_id, - gl::RGBA8, - valid_rect.size.width, - valid_rect.size.height, - stride, - buf, - surface.tile_size.width, - surface.tile_size.height, - ); - // Reallocate the shared depth buffer to fit the valid rect, but within - // a buffer sized to actually fit at least the maximum possible tile size. - // The maximum tile size is supplied to avoid reallocation by ensuring the - // allocated buffer is actually big enough to accommodate the largest tile - // size requested by any used surface, even though supplied valid rect may - // actually be much smaller than this. This will only force a texture - // reallocation inside SWGL if the maximum tile size has grown since the - // last time it was supplied, instead simply reusing the buffer if the max - // tile size is not bigger than what was previously allocated. - self.gl.set_texture_buffer( - self.depth_id, - gl::DEPTH_COMPONENT, - valid_rect.size.width, - valid_rect.size.height, - 0, - ptr::null_mut(), - self.max_tile_size.width, - self.max_tile_size.height, - ); - surface_info.fbo_id = tile.fbo_id; - surface_info.origin -= valid_rect.origin.to_vector(); - } - } - - surface_info - } - - fn unbind(&mut self) { - let id = self.cur_tile; - if let Some(surface) = self.surfaces.get(&id.surface_id) { - if let Some(tile) = surface.tiles.iter().find(|t| t.x == id.x && t.y == id.y) { - if tile.valid_rect.is_empty() { - // If we didn't actually render anything, then just queue any - // dependencies. - self.flush_composites(&id, surface, tile); - return; - } - - // Force any delayed clears to be resolved. - self.gl.resolve_framebuffer(tile.fbo_id); - - if self.use_native_compositor { - self.compositor.unmap_tile(); - } else { - // If we're not relying on a native compositor, then composite - // any tiles that are dependent on this tile being updated but - // are otherwise ready to composite. - self.flush_composites(&id, surface, tile); - } - } - } - } - - fn begin_frame(&mut self) { - if self.use_native_compositor { - self.compositor.begin_frame(); - } - } - - fn add_surface( - &mut self, - id: NativeSurfaceId, - transform: CompositorSurfaceTransform, - clip_rect: DeviceIntRect, - filter: ImageRendering, - ) { - if self.use_native_compositor { - self.compositor.add_surface(id, transform, clip_rect, filter); - } - - if self.composite_thread.is_some() { - // If the surface has an attached external image, try to lock that now. - self.try_lock_composite_surface(&id); - - // If we're already busy compositing, then add to the queue of late - // surfaces instead of trying to sort into the main frame queue. - // These late surfaces will not have any overlap tracking done for - // them and must be processed synchronously at the end of the frame. - if self.composite_thread.as_ref().unwrap().is_busy_compositing() { - self.late_surfaces.push((id, transform, clip_rect, filter)); - return; - } - } - - self.frame_surfaces.push((id, transform, clip_rect, filter)); - } - - /// Now that all the dependency graph nodes have been built, start queuing - /// composition jobs. Any surfaces that get added after this point in the - /// frame will not have overlap dependencies assigned and so must instead - /// be added to the late_surfaces queue to be processed at the end of the - /// frame. - fn start_compositing(&mut self, dirty_rects: &[DeviceIntRect], _opaque_rects: &[DeviceIntRect]) { - // Opaque rects are currently only computed here, not by WR itself, so we - // ignore the passed parameter and forward our own version onto the native - // compositor. - let mut opaque_rects: Vec<DeviceIntRect> = Vec::new(); - for &(ref id, ref transform, ref clip_rect, _filter) in &self.frame_surfaces { - if let Some(surface) = self.surfaces.get(id) { - if !surface.is_opaque { - continue; - } - - for tile in &surface.tiles { - if let Some(rect) = tile.overlap_rect(surface, transform, clip_rect) { - opaque_rects.push(rect); - } - } - } - } - - self.compositor.start_compositing(dirty_rects, &opaque_rects); - - if let Some(dirty_rect) = dirty_rects - .iter() - .fold(DeviceIntRect::zero(), |acc, dirty_rect| acc.union(dirty_rect)) - .to_non_empty() - { - // Factor dirty rect into surface clip rects - for &mut (_, _, ref mut clip_rect, _) in &mut self.frame_surfaces { - *clip_rect = clip_rect.intersection(&dirty_rect).unwrap_or_default(); - } - } - - self.occlude_surfaces(); - - // Discard surfaces that are entirely clipped out - self.frame_surfaces - .retain(|&(_, _, clip_rect, _)| !clip_rect.is_empty()); - - if let Some(ref composite_thread) = self.composite_thread { - // Compute overlap dependencies for surfaces. - for &(ref id, ref transform, ref clip_rect, _filter) in &self.frame_surfaces { - if let Some(surface) = self.surfaces.get(id) { - for tile in &surface.tiles { - self.init_overlaps(id, surface, tile, transform, clip_rect); - } - } - } - - self.locked_framebuffer = self.gl.lock_framebuffer(0); - - composite_thread.prepare_for_composites(); - - // Issue any initial composite jobs for the SwComposite thread. - let mut lock = composite_thread.lock(); - for &(ref id, ref transform, ref clip_rect, filter) in &self.frame_surfaces { - if let Some(surface) = self.surfaces.get(id) { - for tile in &surface.tiles { - if tile.overlaps.get() == 0 { - // Not dependent on any tiles, so go ahead and composite now. - self.queue_composite(surface, transform, clip_rect, filter, tile, &mut lock); - } - } - } - } - } - } - - fn end_frame(&mut self) { - if self.use_native_compositor { - self.compositor.end_frame(); - } else if let Some(ref composite_thread) = self.composite_thread { - // Need to wait for the SwComposite thread to finish any queued jobs. - composite_thread.wait_for_composites(false); - - if !self.late_surfaces.is_empty() { - // All of the main frame surface have been processed by now. But if there - // are any late surfaces, we need to kick off a new synchronous composite - // phase. These late surfaces don't have any overlap/dependency tracking, - // so we just queue them directly and wait synchronously for the composite - // thread to process them in order. - composite_thread.prepare_for_composites(); - { - let mut lock = composite_thread.lock(); - for &(ref id, ref transform, ref clip_rect, filter) in &self.late_surfaces { - if let Some(surface) = self.surfaces.get(id) { - for tile in &surface.tiles { - self.queue_composite(surface, transform, clip_rect, filter, tile, &mut lock); - } - } - } - } - composite_thread.wait_for_composites(true); - } - - self.locked_framebuffer = None; - - self.unlock_composite_surfaces(); - } - - self.frame_surfaces.clear(); - self.late_surfaces.clear(); - - self.reset_overlaps(); - } - - fn enable_native_compositor(&mut self, enable: bool) { - // TODO: The SwComposite thread is not properly instantiated if this is - // ever actually toggled. - assert_eq!(self.use_native_compositor, enable); - self.compositor.enable_native_compositor(enable); - self.use_native_compositor = enable; - } - - fn get_capabilities(&self) -> CompositorCapabilities { - self.compositor.get_capabilities() - } -} diff --git a/third_party/webrender/webrender/src/debug_item.rs b/third_party/webrender/webrender/src/debug_item.rs deleted file mode 100644 index 04ba632f464..00000000000 --- a/third_party/webrender/webrender/src/debug_item.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use api::{units::*, ColorF}; - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum DebugItem { - Text { - msg: String, - color: ColorF, - position: DevicePoint, - }, - Rect { - outer_color: ColorF, - inner_color: ColorF, - rect: DeviceRect, - }, -} diff --git a/third_party/webrender/webrender/src/renderer/debug.rs b/third_party/webrender/webrender/src/debug_render.rs index 57092720cfe..c43bcd1d832 100644 --- a/third_party/webrender/webrender/src/renderer/debug.rs +++ b/third_party/webrender/webrender/src/debug_render.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ColorU, ImageFormat, ImageBufferKind}; +use api::{ColorU, ColorF, ImageFormat, TextureTarget}; use api::units::*; use crate::debug_font_data; use crate::device::{Device, Program, Texture, TextureSlot, VertexDescriptor, ShaderError, VAO}; @@ -11,6 +11,21 @@ use euclid::{Point2D, Rect, Size2D, Transform3D, default}; use crate::internal_types::Swizzle; use std::f32; +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum DebugItem { + Text { + msg: String, + color: ColorF, + position: DevicePoint, + }, + Rect { + outer_color: ColorF, + inner_color: ColorF, + rect: DeviceRect, + }, +} + #[derive(Debug, Copy, Clone)] enum DebugSampler { Font, @@ -120,17 +135,18 @@ impl DebugRenderer { &DESC_COLOR, )?; - let font_vao = device.create_vao(&DESC_FONT, 1); - let line_vao = device.create_vao(&DESC_COLOR, 1); - let tri_vao = device.create_vao(&DESC_COLOR, 1); + let font_vao = device.create_vao(&DESC_FONT); + let line_vao = device.create_vao(&DESC_COLOR); + let tri_vao = device.create_vao(&DESC_COLOR); let font_texture = device.create_texture( - ImageBufferKind::Texture2D, + TextureTarget::Array, ImageFormat::R8, debug_font_data::BMP_WIDTH, debug_font_data::BMP_HEIGHT, TextureFilter::Linear, None, + 1, ); device.upload_texture_immediate( &font_texture, @@ -371,45 +387,3 @@ impl DebugRenderer { self.tri_indices.clear(); } } - -pub struct LazyInitializedDebugRenderer { - debug_renderer: Option<DebugRenderer>, - failed: bool, -} - -impl LazyInitializedDebugRenderer { - pub fn new() -> Self { - Self { - debug_renderer: None, - failed: false, - } - } - - pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer> { - if self.failed { - return None; - } - if self.debug_renderer.is_none() { - match DebugRenderer::new(device) { - Ok(renderer) => { self.debug_renderer = Some(renderer); } - Err(_) => { - // The shader compilation code already logs errors. - self.failed = true; - } - } - } - - self.debug_renderer.as_mut() - } - - /// Returns mut ref to `debug::DebugRenderer` if one already exists, otherwise returns `None`. - pub fn try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer> { - self.debug_renderer.as_mut() - } - - pub fn deinit(self, device: &mut Device) { - if let Some(debug_renderer) = self.debug_renderer { - debug_renderer.deinit(device); - } - } -} diff --git a/third_party/webrender/webrender/src/debug_server.rs b/third_party/webrender/webrender/src/debug_server.rs new file mode 100644 index 00000000000..c3cd29549ad --- /dev/null +++ b/third_party/webrender/webrender/src/debug_server.rs @@ -0,0 +1,402 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use api::{ApiMsg, DebugCommand, DebugFlags}; +use api::units::DeviceIntSize; +use crate::print_tree::PrintTreePrinter; +use crate::renderer; +use std::sync::mpsc::{channel, Receiver}; +use std::sync::mpsc::Sender; +use std::thread; +use ws; +use base64::encode; +use image_loader; + +// Messages that are sent from the render backend to the renderer +// debug command queue. These are sent in a separate queue so +// that none of these types are exposed to the RenderApi interfaces. +// We can't use select!() as it's not stable... +enum DebugMsg { + AddSender(ws::Sender), + RemoveSender(ws::util::Token), +} + +// Represents a connection to a client. +struct Server { + ws: ws::Sender, + debug_tx: Sender<DebugMsg>, + api_tx: Sender<ApiMsg>, + debug_flags: DebugFlags, +} + +impl ws::Handler for Server { + fn on_open(&mut self, _: ws::Handshake) -> ws::Result<()> { + self.debug_tx + .send(DebugMsg::AddSender(self.ws.clone())) + .ok(); + + Ok(()) + } + + fn on_close(&mut self, _: ws::CloseCode, _: &str) { + self.debug_tx + .send(DebugMsg::RemoveSender(self.ws.token())) + .ok(); + } + + fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { + match msg { + ws::Message::Text(string) => { + // First, check for flag change commands. + let mut set_flags = true; + match string.as_str() { + "enable_profiler" => self.debug_flags.insert(DebugFlags::PROFILER_DBG), + "disable_profiler" => self.debug_flags.remove(DebugFlags::PROFILER_DBG), + "enable_texture_cache_debug" => self.debug_flags.insert(DebugFlags::TEXTURE_CACHE_DBG), + "disable_texture_cache_debug" => self.debug_flags.remove(DebugFlags::TEXTURE_CACHE_DBG), + "enable_render_target_debug" => self.debug_flags.insert(DebugFlags::RENDER_TARGET_DBG), + "disable_render_target_debug" => self.debug_flags.remove(DebugFlags::RENDER_TARGET_DBG), + "enable_gpu_time_queries" => self.debug_flags.insert(DebugFlags::GPU_TIME_QUERIES), + "disable_gpu_time_queries" => self.debug_flags.remove(DebugFlags::GPU_TIME_QUERIES), + "enable_gpu_sample_queries" => self.debug_flags.insert(DebugFlags::GPU_SAMPLE_QUERIES), + "disable_gpu_sample_queries" => self.debug_flags.remove(DebugFlags::GPU_SAMPLE_QUERIES), + "disable_opaque_pass" => self.debug_flags.insert(DebugFlags::DISABLE_OPAQUE_PASS), + "enable_opaque_pass" => self.debug_flags.remove(DebugFlags::DISABLE_OPAQUE_PASS), + "disable_alpha_pass" => self.debug_flags.insert(DebugFlags::DISABLE_ALPHA_PASS), + "enable_alpha_pass" => self.debug_flags.remove(DebugFlags::DISABLE_ALPHA_PASS), + "disable_clip_masks" => self.debug_flags.insert(DebugFlags::DISABLE_CLIP_MASKS), + "enable_clip_masks" => self.debug_flags.remove(DebugFlags::DISABLE_CLIP_MASKS), + "disable_text_prims" => self.debug_flags.insert(DebugFlags::DISABLE_TEXT_PRIMS), + "enable_text_prims" => self.debug_flags.remove(DebugFlags::DISABLE_TEXT_PRIMS), + "disable_gradient_prims" => self.debug_flags.insert(DebugFlags::DISABLE_GRADIENT_PRIMS), + "enable_gradient_prims" => self.debug_flags.remove(DebugFlags::DISABLE_GRADIENT_PRIMS), + _ => set_flags = false, + }; + + let cmd = if set_flags { + DebugCommand::SetFlags(self.debug_flags) + } else { + match string.as_str() { + "fetch_passes" => DebugCommand::FetchPasses, + "fetch_screenshot" => DebugCommand::FetchScreenshot, + "fetch_documents" => DebugCommand::FetchDocuments, + "fetch_spatial_tree" => DebugCommand::FetchClipScrollTree, + "fetch_render_tasks" => DebugCommand::FetchRenderTasks, + msg => { + error!("unknown msg {}", msg); + return Ok(()); + } + } + }; + + let msg = ApiMsg::DebugCommand(cmd); + self.api_tx.send(msg).unwrap(); + } + ws::Message::Binary(..) => {} + } + + Ok(()) + } +} + +// Spawn a thread for a given renderer, and wait for +// client connections. +pub struct DebugServerImpl { + join_handle: Option<thread::JoinHandle<()>>, + broadcaster: ws::Sender, + debug_rx: Receiver<DebugMsg>, + senders: Vec<ws::Sender>, +} + +impl DebugServerImpl { + pub fn new(api_tx: Sender<ApiMsg>) -> DebugServerImpl { + let (debug_tx, debug_rx) = channel(); + + let socket = ws::Builder::new() + .build(move |out| { + Server { + ws: out, + debug_tx: debug_tx.clone(), + api_tx: api_tx.clone(), + debug_flags: DebugFlags::empty(), + } + }) + .unwrap(); + + let broadcaster = socket.broadcaster(); + + let join_handle = Some(thread::spawn(move || { + let address = "127.0.0.1:3583"; + debug!("WebRender debug server started: {}", address); + if let Err(..) = socket.listen(address) { + error!("ERROR: Unable to bind debugger websocket (port may be in use)."); + } + })); + + DebugServerImpl { + join_handle, + broadcaster, + debug_rx, + senders: Vec::new(), + } + } +} + +impl renderer::DebugServer for DebugServerImpl { + fn send(&mut self, message: String) { + // Add any new connections that have been queued. + while let Ok(msg) = self.debug_rx.try_recv() { + match msg { + DebugMsg::AddSender(sender) => { + self.senders.push(sender); + } + DebugMsg::RemoveSender(token) => { + self.senders.retain(|sender| sender.token() != token); + } + } + } + + // Broadcast the message to all senders. Keep + // track of the ones that failed, so they can + // be removed from the active sender list. + let mut disconnected_senders = Vec::new(); + + for (i, sender) in self.senders.iter().enumerate() { + if let Err(..) = sender.send(message.clone()) { + disconnected_senders.push(i); + } + } + + // Remove the broken senders from the list + // for next broadcast. Remove in reverse + // order so the indices are valid for the + // entire loop. + for i in disconnected_senders.iter().rev() { + self.senders.remove(*i); + } + } +} + +impl Drop for DebugServerImpl { + fn drop(&mut self) { + self.broadcaster.shutdown().ok(); + self.join_handle.take().unwrap().join().ok(); + } +} + +// A serializable list of debug information about passes +// that can be sent to the client. + +#[derive(Serialize)] +pub enum BatchKind { + Clip, + Cache, + Opaque, + Alpha, +} + +#[derive(Serialize)] +pub struct PassList { + kind: &'static str, + passes: Vec<Pass>, +} + +impl PassList { + pub fn new() -> PassList { + PassList { + kind: "passes", + passes: Vec::new(), + } + } + + pub fn add(&mut self, pass: Pass) { + self.passes.push(pass); + } +} + +#[derive(Serialize)] +pub struct Pass { + pub targets: Vec<Target>, +} + +#[derive(Serialize)] +pub struct Target { + kind: &'static str, + batches: Vec<Batch>, +} + +impl Target { + pub fn new(kind: &'static str) -> Target { + Target { + kind, + batches: Vec::new(), + } + } + + pub fn add(&mut self, kind: BatchKind, description: &str, count: usize) { + if count > 0 { + self.batches.push(Batch { + kind, + description: description.to_owned(), + count, + }); + } + } +} + +#[derive(Serialize)] +struct Batch { + kind: BatchKind, + description: String, + count: usize, +} + +#[derive(Serialize)] +pub struct TreeNode { + description: String, + children: Vec<TreeNode>, +} + +impl TreeNode { + pub fn new(description: &str) -> TreeNode { + TreeNode { + description: description.to_owned(), + children: Vec::new(), + } + } + + pub fn add_child(&mut self, child: TreeNode) { + self.children.push(child); + } + + pub fn add_item(&mut self, description: &str) { + self.children.push(TreeNode::new(description)); + } +} + +#[derive(Serialize)] +pub struct DocumentList { + kind: &'static str, + root: TreeNode, +} + +impl DocumentList { + pub fn new() -> Self { + DocumentList { + kind: "documents", + root: TreeNode::new("root"), + } + } + + pub fn add(&mut self, item: TreeNode) { + self.root.add_child(item); + } +} + +#[derive(Serialize)] +pub struct Screenshot { + kind: &'static str, + data: String +} + +impl Screenshot { + pub fn new(size: DeviceIntSize, data: Vec<u8>) -> Self { + let mut output = Vec::with_capacity((size.width * size.height) as usize); + { + let encoder = image_loader::png::PNGEncoder::new(&mut output); + encoder.encode( + &data, + size.width as u32, + size.height as u32, + image_loader::ColorType::Rgba8, + ).unwrap(); + } + + let data = encode(&output); + Screenshot { + kind: "screenshot", + data + } + } +} + +// A serializable list of debug information about spatial trees +// that can be sent to the client + +#[derive(Serialize)] +pub struct SpatialTreeList { + kind: &'static str, + root: TreeNode, +} + +impl SpatialTreeList { + pub fn new() -> Self { + SpatialTreeList { + kind: "spatial_tree", + root: TreeNode::new("root"), + } + } + + pub fn add(&mut self, item: TreeNode) { + self.root.add_child(item); + } +} + +#[derive(Serialize)] +pub struct RenderTaskList { + kind: &'static str, + root: TreeNode, +} + +impl RenderTaskList { + pub fn new() -> Self { + RenderTaskList { + kind: "render_tasks", + root: TreeNode::new("root"), + } + } + + pub fn add(&mut self, item: TreeNode) { + self.root.add_child(item); + } +} + +// A TreeNode-based PrintTreePrinter to serialize pretty-printed +// trees as json +pub struct TreeNodeBuilder { + levels: Vec<TreeNode>, +} + +impl TreeNodeBuilder { + pub fn new(root: TreeNode) -> TreeNodeBuilder { + TreeNodeBuilder { levels: vec![root] } + } + + fn current_level_mut(&mut self) -> &mut TreeNode { + assert!(!self.levels.is_empty()); + self.levels.last_mut().unwrap() + } + + pub fn build(mut self) -> TreeNode { + assert!(self.levels.len() == 1); + self.levels.pop().unwrap() + } +} + +impl PrintTreePrinter for TreeNodeBuilder { + fn new_level(&mut self, title: String) { + let level = TreeNode::new(&title); + self.levels.push(level); + } + + fn end_level(&mut self) { + assert!(!self.levels.is_empty()); + let last_level = self.levels.pop().unwrap(); + self.current_level_mut().add_child(last_level); + } + + fn add_item(&mut self, text: String) { + self.current_level_mut().add_item(&text); + } +} diff --git a/third_party/webrender/webrender/src/device/gl.rs b/third_party/webrender/webrender/src/device/gl.rs index 5e0c0503782..3eac572081b 100644 --- a/third_party/webrender/webrender/src/device/gl.rs +++ b/third_party/webrender/webrender/src/device/gl.rs @@ -3,14 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::super::shader_source::{OPTIMIZED_SHADERS, UNOPTIMIZED_SHADERS}; -use api::{ColorF, ImageDescriptor, ImageFormat}; -use api::{MixBlendMode, ImageBufferKind, VoidPtrToSizeFn}; -use api::{CrashAnnotator, CrashAnnotation, CrashAnnotatorGuard}; +use api::{ColorF, ImageDescriptor, ImageFormat, MemoryReport}; +use api::{MixBlendMode, TextureTarget, VoidPtrToSizeFn}; use api::units::*; use euclid::default::Transform3D; use gleam::gl; -use crate::render_api::MemoryReport; -use crate::internal_types::{FastHashMap, RenderTargetInfo, Swizzle, SwizzleSettings}; +use crate::internal_types::{FastHashMap, LayerIndex, RenderTargetInfo, Swizzle, SwizzleSettings}; use crate::util::round_up_to_multiple; use crate::profiler; use log::Level; @@ -37,7 +35,6 @@ use webrender_build::shader::{ ProgramSourceDigest, ShaderKind, ShaderVersion, build_shader_main_string, build_shader_prefix_string, do_build_shader_string, shader_source_from_file, }; -use malloc_size_of::MallocSizeOfOps; /// Sequence number for frames, as tracked by the device layer. #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)] @@ -72,7 +69,7 @@ pub enum DepthFunction { } #[repr(u32)] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum TextureFilter { @@ -150,20 +147,12 @@ fn depth_target_size_in_bytes(dimensions: &DeviceIntSize) -> usize { (pixels as usize) * 4 } -pub fn get_gl_target(target: ImageBufferKind) -> gl::GLuint { +pub fn get_gl_target(target: TextureTarget) -> gl::GLuint { match target { - ImageBufferKind::Texture2D => gl::TEXTURE_2D, - ImageBufferKind::TextureRect => gl::TEXTURE_RECTANGLE, - ImageBufferKind::TextureExternal => gl::TEXTURE_EXTERNAL_OES, - } -} - -pub fn from_gl_target(target: gl::GLuint) -> ImageBufferKind { - match target { - gl::TEXTURE_2D => ImageBufferKind::Texture2D, - gl::TEXTURE_RECTANGLE => ImageBufferKind::TextureRect, - gl::TEXTURE_EXTERNAL_OES => ImageBufferKind::TextureExternal, - _ => panic!("Unexpected target {:?}", target), + TextureTarget::Default => gl::TEXTURE_2D, + TextureTarget::Array => gl::TEXTURE_2D_ARRAY, + TextureTarget::Rect => gl::TEXTURE_RECTANGLE, + TextureTarget::External => gl::TEXTURE_EXTERNAL_OES, } } @@ -309,16 +298,14 @@ impl VertexDescriptor { } } - fn bind(&self, gl: &dyn gl::Gl, main: VBOId, instance: VBOId, instance_divisor: u32) { + fn bind(&self, gl: &dyn gl::Gl, main: VBOId, instance: VBOId) { Self::bind_attributes(self.vertex_attributes, 0, 0, gl, main); if !self.instance_attributes.is_empty() { Self::bind_attributes( self.instance_attributes, self.vertex_attributes.len(), - instance_divisor, - gl, - instance, + 1, gl, instance, ); } } @@ -386,22 +373,21 @@ impl<T> Drop for VBO<T> { pub struct ExternalTexture { id: gl::GLuint, target: gl::GLuint, - #[allow(dead_code)] - swizzle: Swizzle, + _swizzle: Swizzle, uv_rect: TexelRect, } impl ExternalTexture { pub fn new( id: u32, - target: ImageBufferKind, + target: TextureTarget, swizzle: Swizzle, uv_rect: TexelRect, ) -> Self { ExternalTexture { id, target: get_gl_target(target), - swizzle, + _swizzle: swizzle, uv_rect, } } @@ -433,16 +419,17 @@ bitflags! { pub struct Texture { id: gl::GLuint, target: gl::GLuint, + layer_count: i32, format: ImageFormat, size: DeviceIntSize, filter: TextureFilter, flags: TextureFlags, /// An internally mutable swizzling state that may change between batches. active_swizzle: Cell<Swizzle>, - /// Framebuffer Object allowing this texture to be rendered to. - /// - /// Empty if this texture is not used as a render target or if a depth buffer is needed. - fbo: Option<FBOId>, + /// Framebuffer Objects, one for each layer of the texture, allowing this + /// texture to be rendered to. Empty if this texture is not used as a render + /// target. + fbos: Vec<FBOId>, /// Same as the above, but with a depth buffer attached. /// /// FBOs are cheap to create but expensive to reconfigure (since doing so @@ -455,12 +442,15 @@ pub struct Texture { /// empty if this texture is not used as a render target _or_ if it is, but /// the depth buffer has never been requested. /// - /// Note that we always fill fbo, and then lazily create fbo_with_depth + /// Note that we always fill fbos, and then lazily create fbos_with_depth /// when needed. We could make both lazy (i.e. render targets would have one /// or the other, but not both, unless they were actually used in both /// configurations). But that would complicate a lot of logic in this module, /// and FBOs are cheap enough to create. - fbo_with_depth: Option<FBOId>, + fbos_with_depth: Vec<FBOId>, + /// If we are unable to blit directly to a texture array then we need + /// an intermediate renderbuffer. + blit_workaround_buffer: Option<(RBOId, FBOId)>, last_frame_used: GpuFrameId, } @@ -469,6 +459,10 @@ impl Texture { self.size } + pub fn get_layer_count(&self) -> i32 { + self.layer_count + } + pub fn get_format(&self) -> ImageFormat { self.format } @@ -477,12 +471,8 @@ impl Texture { self.filter } - pub fn get_target(&self) -> ImageBufferKind { - from_gl_target(self.target) - } - pub fn supports_depth(&self) -> bool { - self.fbo_with_depth.is_some() + !self.fbos_with_depth.is_empty() } pub fn last_frame_used(&self) -> GpuFrameId { @@ -493,10 +483,6 @@ impl Texture { self.last_frame_used == frame_id } - pub fn is_render_target(&self) -> bool { - self.fbo.is_some() - } - /// Returns true if this texture was used within `threshold` frames of /// the current frame. pub fn used_recently(&self, current_frame_id: GpuFrameId, threshold: usize) -> bool { @@ -513,21 +499,28 @@ impl Texture { &mut self.flags } - /// Returns the number of bytes (generally in GPU memory) that this texture - /// consumes. - pub fn size_in_bytes(&self) -> usize { + /// Returns the number of bytes (generally in GPU memory) that each layer of + /// this texture consumes. + pub fn layer_size_in_bytes(&self) -> usize { + assert!(self.layer_count > 0 || self.size.width + self.size.height == 0); let bpp = self.format.bytes_per_pixel() as usize; let w = self.size.width as usize; let h = self.size.height as usize; bpp * w * h } + /// Returns the number of bytes (generally in GPU memory) that this texture + /// consumes. + pub fn size_in_bytes(&self) -> usize { + self.layer_size_in_bytes() * (self.layer_count as usize) + } + #[cfg(feature = "replay")] pub fn into_external(mut self) -> ExternalTexture { let ext = ExternalTexture { id: self.id, target: self.target, - swizzle: Swizzle::default(), + _swizzle: Swizzle::default(), // TODO(gw): Support custom UV rect for external textures during captures uv_rect: TexelRect::new( 0.0, @@ -551,7 +544,6 @@ pub struct Program { id: gl::GLuint, u_transform: gl::GLint, u_mode: gl::GLint, - u_texture_size: gl::GLint, source_info: ProgramSourceInfo, is_initialized: bool, } @@ -590,7 +582,6 @@ pub struct VAO { main_vbo_id: VBOId, instance_vbo_id: VBOId, instance_stride: usize, - instance_divisor: u32, owns_vertices_and_indices: bool, } @@ -603,7 +594,6 @@ impl Drop for VAO { } } -#[derive(Debug)] pub struct PBO { id: gl::GLuint, reserved_size: usize, @@ -619,7 +609,7 @@ impl Drop for PBO { fn drop(&mut self) { debug_assert!( thread::panicking() || self.id == 0, - "renderer::deinit not called or PBO not returned to pool" + "renderer::deinit not called" ); } } @@ -658,7 +648,6 @@ enum ProgramSourceType { pub struct ProgramSourceInfo { base_filename: &'static str, features: Vec<&'static str>, - full_name_cstr: Rc<std::ffi::CString>, source_type: ProgramSourceType, digest: ProgramSourceDigest, } @@ -683,11 +672,11 @@ impl ProgramSourceInfo { // Hash the renderer name. hasher.write(device.capabilities.renderer_name.as_bytes()); - let full_name = Self::make_full_name(name, features); + let full_name = &Self::full_name(name, features); let optimized_source = if device.use_optimized_shaders { - OPTIMIZED_SHADERS.get(&(gl_version, &full_name)).or_else(|| { - warn!("Missing optimized shader source for {}", &full_name); + OPTIMIZED_SHADERS.get(&(gl_version, full_name)).or_else(|| { + warn!("Missing optimized shader source for {}", full_name); None }) } else { @@ -759,14 +748,13 @@ impl ProgramSourceInfo { ProgramSourceInfo { base_filename: name, features: features.to_vec(), - full_name_cstr: Rc::new(std::ffi::CString::new(full_name).unwrap()), source_type, digest: hasher.into(), } } fn compute_source(&self, device: &Device, kind: ShaderKind) -> String { - let full_name = self.full_name(); + let full_name = Self::full_name(self.base_filename, &self.features); match self.source_type { ProgramSourceType::Optimized(gl_version) => { let shader = OPTIMIZED_SHADERS @@ -791,17 +779,13 @@ impl ProgramSourceInfo { } } - fn make_full_name(base_filename: &'static str, features: &[&'static str]) -> String { + fn full_name(base_filename: &'static str, features: &[&'static str]) -> String { if features.is_empty() { base_filename.to_string() } else { format!("{}_{}", base_filename, features.join("_")) } } - - fn full_name(&self) -> String { - Self::make_full_name(self.base_filename, &self.features) - } } #[cfg_attr(feature = "serialize_program", derive(Deserialize, Serialize))] @@ -947,10 +931,14 @@ pub struct Capabilities { pub supports_multisampling: bool, /// Whether the function `glCopyImageSubData` is available. pub supports_copy_image_sub_data: bool, - /// Whether the RGBAF32 textures can be bound to framebuffers. - pub supports_color_buffer_float: bool, - /// Whether the device supports persistently mapped buffers, via glBufferStorage. - pub supports_buffer_storage: bool, + /// Whether we are able to use `glBlitFramebuffers` with the draw fbo + /// bound to a non-0th layer of a texture array. This is buggy on + /// Adreno devices. + pub supports_blit_to_texture_array: bool, + /// Whether we can use the pixel local storage functionality that + /// is available on some mobile GPUs. This allows fast access to + /// the per-pixel tile memory. + pub supports_pixel_local_storage: bool, /// Whether advanced blend equations are supported. pub supports_advanced_blend_equation: bool, /// Whether dual-source blending is supported. @@ -965,28 +953,6 @@ pub struct Capabilities { pub supports_nonzero_pbo_offsets: bool, /// Whether the driver supports specifying the texture usage up front. pub supports_texture_usage: bool, - /// Whether offscreen render targets can be partially updated. - pub supports_render_target_partial_update: bool, - /// Whether we can use SSBOs. - pub supports_shader_storage_object: bool, - /// Whether to enforce that texture uploads be batched regardless of what - /// the pref says. - pub requires_batched_texture_uploads: Option<bool>, - /// Whether we are able to ue glClear to clear regions of an alpha render target. - /// If false, we must use a shader to clear instead. - pub supports_alpha_target_clears: bool, - /// Whether the driver can reliably upload data to R8 format textures. - pub supports_r8_texture_upload: bool, - /// Whether clip-masking is supported natively by the GL implementation - /// rather than emulated in shaders. - pub uses_native_clip_mask: bool, - /// Whether anti-aliasing is supported natively by the GL implementation - /// rather than emulated in shaders. - pub uses_native_antialiasing: bool, - /// Whether the extension GL_OES_EGL_image_external_essl3 is supported. If true, external - /// textures can be used as normal. If false, external textures can only be rendered with - /// certain shaders, and must first be copied in to regular textures for others. - pub supports_image_external_essl3: bool, /// The name of the renderer, as reported by GL pub renderer_name: String, } @@ -1058,9 +1024,8 @@ pub struct Device { // device state bound_textures: [gl::GLuint; 16], bound_program: gl::GLuint, - bound_program_name: Rc<std::ffi::CString>, bound_vao: gl::GLuint, - bound_read_fbo: (FBOId, DeviceIntPoint), + bound_read_fbo: FBOId, bound_draw_fbo: FBOId, program_mode_id: UniformLocation, default_read_fbo: FBOId, @@ -1071,19 +1036,12 @@ pub struct Device { depth_available: bool, upload_method: UploadMethod, - use_batched_texture_uploads: bool, - /// Whether to use draw calls instead of regular blitting commands. - /// - /// Note: this currently only applies to the batched texture uploads - /// path. - use_draw_calls_for_texture_copy: bool, // HW or API capabilities capabilities: Capabilities, color_formats: TextureFormatPair<ImageFormat>, bgra_formats: TextureFormatPair<gl::GLuint>, - bgra_pixel_type: gl::GLuint, swizzle_settings: SwizzleSettings, depth_format: gl::GLuint, @@ -1095,7 +1053,6 @@ pub struct Device { // debug inside_frame: bool, - crash_annotator: Option<Box<dyn CrashAnnotator>>, // resources resource_override_path: Option<PathBuf>, @@ -1104,6 +1061,7 @@ pub struct Device { use_optimized_shaders: bool, max_texture_size: i32, + max_texture_layers: u32, cached_programs: Option<Rc<ProgramCache>>, // Frame counter. This is used to map between CPU @@ -1117,10 +1075,7 @@ pub struct Device { /// format, we fall back to glTexImage*. texture_storage_usage: TexStorageUsage, - /// Required stride alignment for pixel transfers. This may be required for - /// correctness reasons due to driver bugs, or for performance reasons to - /// ensure we remain on the fast-path for transfers. - required_pbo_stride: StrideAlignment, + optimal_pbo_stride: StrideAlignment, /// Whether we must ensure the source strings passed to glShaderSource() /// are null-terminated, to work around driver bugs. @@ -1170,8 +1125,12 @@ pub enum DrawTarget { Texture { /// Size of the texture in pixels dimensions: DeviceIntSize, + /// The slice within the texture array to draw to + layer: LayerIndex, /// Whether to draw with the texture's associated depth target with_depth: bool, + /// Workaround buffers for devices with broken texture array copy implementation + blit_workaround_buffer: Option<(RBOId, FBOId)>, /// FBO that corresponds to the selected layer / depth mode fbo_id: FBOId, /// Native GL texture ID @@ -1212,18 +1171,21 @@ impl DrawTarget { pub fn from_texture( texture: &Texture, + layer: usize, with_depth: bool, ) -> Self { let fbo_id = if with_depth { - texture.fbo_with_depth.unwrap() + texture.fbos_with_depth[layer] } else { - texture.fbo.unwrap() + texture.fbos[layer] }; DrawTarget::Texture { dimensions: texture.get_dimensions(), fbo_id, with_depth, + layer, + blit_workaround_buffer: texture.blit_workaround_buffer, id: texture.id, target: texture.target, } @@ -1249,31 +1211,28 @@ impl DrawTarget { fb_rect.origin.x += rect.origin.x; } } - DrawTarget::Texture { .. } | DrawTarget::External { .. } | DrawTarget::NativeSurface { .. } => (), + DrawTarget::Texture { .. } | DrawTarget::External { .. } => (), + DrawTarget::NativeSurface { .. } => { + panic!("bug: is this ever used for native surfaces?"); + } } fb_rect } - pub fn surface_origin_is_top_left(&self) -> bool { - match *self { - DrawTarget::Default { surface_origin_is_top_left, .. } => surface_origin_is_top_left, - DrawTarget::Texture { .. } | DrawTarget::External { .. } | DrawTarget::NativeSurface { .. } => true, - } - } - /// Given a scissor rect, convert it to the right coordinate space /// depending on the draw target kind. If no scissor rect was supplied, /// returns a scissor rect that encloses the entire render target. pub fn build_scissor_rect( &self, scissor_rect: Option<DeviceIntRect>, + content_origin: DeviceIntPoint, ) -> FramebufferIntRect { let dimensions = self.dimensions(); match scissor_rect { Some(scissor_rect) => match *self { DrawTarget::Default { ref rect, .. } => { - self.to_framebuffer_rect(scissor_rect) + self.to_framebuffer_rect(scissor_rect.translate(-content_origin.to_vector())) .intersection(rect) .unwrap_or_else(FramebufferIntRect::zero) } @@ -1308,33 +1267,15 @@ pub enum ReadTarget { External { fbo: FBOId, }, - /// An FBO bound to a native (OS compositor) surface - NativeSurface { - fbo_id: FBOId, - offset: DeviceIntPoint, - }, } impl ReadTarget { pub fn from_texture( texture: &Texture, + layer: usize, ) -> Self { ReadTarget::Texture { - fbo_id: texture.fbo.unwrap(), - } - } - - fn offset(&self) -> DeviceIntPoint { - match *self { - ReadTarget::Default | - ReadTarget::Texture { .. } | - ReadTarget::External { .. } => { - DeviceIntPoint::zero() - } - - ReadTarget::NativeSurface { offset, .. } => { - offset - } + fbo_id: texture.fbos[layer], } } } @@ -1342,21 +1283,14 @@ impl ReadTarget { impl From<DrawTarget> for ReadTarget { fn from(t: DrawTarget) -> Self { match t { - DrawTarget::Default { .. } => { - ReadTarget::Default - } - DrawTarget::NativeSurface { external_fbo_id, offset, .. } => { - ReadTarget::NativeSurface { - fbo_id: FBOId(external_fbo_id), - offset, - } - } - DrawTarget::Texture { fbo_id, .. } => { - ReadTarget::Texture { fbo_id } - } - DrawTarget::External { fbo, .. } => { - ReadTarget::External { fbo } + DrawTarget::Default { .. } => ReadTarget::Default, + DrawTarget::NativeSurface { .. } => { + unreachable!("bug: native surfaces cannot be read targets"); } + DrawTarget::Texture { fbo_id, .. } => + ReadTarget::Texture { fbo_id }, + DrawTarget::External { fbo, .. } => + ReadTarget::External { fbo }, } } } @@ -1364,11 +1298,11 @@ impl From<DrawTarget> for ReadTarget { impl Device { pub fn new( mut gl: Rc<dyn gl::Gl>, - crash_annotator: Option<Box<dyn CrashAnnotator>>, resource_override_path: Option<PathBuf>, use_optimized_shaders: bool, upload_method: UploadMethod, cached_programs: Option<Rc<ProgramCache>>, + allow_pixel_local_storage_support: bool, allow_texture_storage_support: bool, allow_texture_swizzling: bool, dump_shader_source: Option<String>, @@ -1376,18 +1310,15 @@ impl Device { panic_on_gl_error: bool, ) -> Device { let mut max_texture_size = [0]; + let mut max_texture_layers = [0]; unsafe { gl.get_integer_v(gl::MAX_TEXTURE_SIZE, &mut max_texture_size); + gl.get_integer_v(gl::MAX_ARRAY_TEXTURE_LAYERS, &mut max_texture_layers); } - // We cap the max texture size at 16384. Some hardware report higher - // capabilities but get very unstable with very large textures. - // Bug 1702494 tracks re-evaluating this cap. - let max_texture_size = max_texture_size[0].min(16384); - + let max_texture_size = max_texture_size[0]; + let max_texture_layers = max_texture_layers[0] as u32; let renderer_name = gl.get_string(gl::RENDERER); - info!("Renderer: {}", renderer_name); - info!("Max texture size: {}", max_texture_size); let mut extension_count = [0]; unsafe { @@ -1467,140 +1398,101 @@ impl Device { // So we must use glTexStorage instead. See bug 1591436. let is_emulator = renderer_name.starts_with("Android Emulator"); let avoid_tex_image = is_emulator; - let mut gl_version = [0; 2]; - unsafe { - gl.get_integer_v(gl::MAJOR_VERSION, &mut gl_version[0..1]); - gl.get_integer_v(gl::MINOR_VERSION, &mut gl_version[1..2]); - } - info!("GL context {:?} {}.{}", gl.get_type(), gl_version[0], gl_version[1]); + let gl_version = gl.get_string(gl::VERSION); - // We block texture storage on mac because it doesn't support BGRA - let supports_texture_storage = allow_texture_storage_support && !cfg!(target_os = "macos") && + let supports_texture_storage = allow_texture_storage_support && match gl.get_type() { gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_texture_storage"), - gl::GlType::Gles => true, + // 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 && match gl.get_type() { // see https://www.g-truc.net/post-0734.html - gl::GlType::Gl => gl_version >= [3, 3] || + gl::GlType::Gl => gl_version.as_str() >= "3.3" || supports_extension(&extensions, "GL_ARB_texture_swizzle"), gl::GlType::Gles => true, }; - let (color_formats, bgra_formats, bgra_pixel_type, bgra8_sampling_swizzle, texture_storage_usage) = match gl.get_type() { + 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 supports_texture_storage && supports_texture_swizzle => ( TextureFormatPair::from(ImageFormat::RGBA8), TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA }, - gl::UNSIGNED_BYTE, Swizzle::Bgra, // pretend it's RGBA, rely on swizzling TexStorageUsage::Always ), // There is no `glTexStorage`, upload as `glTexImage` with BGRA input. gl::GlType::Gl => ( - TextureFormatPair { internal: ImageFormat::BGRA8, external: ImageFormat::BGRA8 }, + TextureFormatPair { internal: ImageFormat::RGBA8, external: ImageFormat::BGRA8 }, TextureFormatPair { internal: gl::RGBA, external: gl::BGRA }, - gl::UNSIGNED_INT_8_8_8_8_REV, Swizzle::Rgba, // converted on uploads by the driver, no swizzling needed TexStorageUsage::Never ), - // glTexStorage is always supported in GLES 3, but because the GL_EXT_texture_storage - // extension is supported we can use glTexStorage with BGRA8 as the internal format. - // Prefer BGRA textures over RGBA. - gl::GlType::Gles if supports_gles_bgra - && supports_extension(&extensions, "GL_EXT_texture_storage") => - ( + // We can use glTexStorage with BGRA8 as the internal format. + gl::GlType::Gles if supports_gles_bgra && supports_texture_storage => ( TextureFormatPair::from(ImageFormat::BGRA8), TextureFormatPair { internal: gl::BGRA8_EXT, external: gl::BGRA_EXT }, - gl::UNSIGNED_BYTE, Swizzle::Rgba, // no conversion needed TexStorageUsage::Always, ), - // BGRA is not supported as an internal format with glTexStorage, therefore we will - // use RGBA textures instead and pretend BGRA data is RGBA when uploading. - // The swizzling will happen at the texture unit. + // 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, 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), + TextureFormatPair::from(gl::BGRA_EXT), + Swizzle::Rgba, // no conversion needed + TexStorageUsage::NonBGRA8, + ), + // 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 supports_texture_swizzle => ( TextureFormatPair::from(ImageFormat::RGBA8), TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA }, - gl::UNSIGNED_BYTE, Swizzle::Bgra, // pretend it's RGBA, rely on swizzling TexStorageUsage::Always, ), - // BGRA is not supported as an internal format with glTexStorage, and we cannot use - // swizzling either. Therefore prefer BGRA textures over RGBA, but use glTexImage - // to initialize BGRA textures. glTexStorage can still be used for other formats. - gl::GlType::Gles if supports_gles_bgra && !avoid_tex_image => ( - TextureFormatPair::from(ImageFormat::BGRA8), - TextureFormatPair::from(gl::BGRA_EXT), - gl::UNSIGNED_BYTE, - Swizzle::Rgba, // no conversion needed - TexStorageUsage::NonBGRA8, + // BGRA and swizzling are not supported. We force the conversion done by the driver. + gl::GlType::Gles => ( + TextureFormatPair::from(ImageFormat::RGBA8), + TextureFormatPair { internal: gl::RGBA8, external: gl::BGRA }, + Swizzle::Rgba, + TexStorageUsage::Always, ), - // Neither BGRA or swizzling are supported. GLES does not allow format conversion - // during upload so we must use RGBA textures and pretend BGRA data is RGBA when - // uploading. Images may be rendered incorrectly as a result. - gl::GlType::Gles => { - warn!("Neither BGRA or texture swizzling are supported. Images may be rendered incorrectly."); - ( - TextureFormatPair::from(ImageFormat::RGBA8), - TextureFormatPair { internal: gl::RGBA8, external: gl::RGBA }, - gl::UNSIGNED_BYTE, - Swizzle::Rgba, - TexStorageUsage::Always, - ) - } }; let is_software_webrender = renderer_name.starts_with("Software WebRender"); - let upload_method = if is_software_webrender { - // Uploads in SWGL generally reduce to simple memory copies. - UploadMethod::Immediate + let (depth_format, upload_method) = if is_software_webrender { + (gl::DEPTH_COMPONENT16, UploadMethod::Immediate) } else { - upload_method + (gl::DEPTH_COMPONENT24, upload_method) }; - // Prefer 24-bit depth format. While 16-bit depth also works, it may exhaust depth ids easily. - let depth_format = gl::DEPTH_COMPONENT24; info!("GL texture cache {:?}, bgra {:?} swizzle {:?}, texture storage {:?}, depth {:?}", color_formats, bgra_formats, bgra8_sampling_swizzle, texture_storage_usage, depth_format); - - // On Mali-T devices glCopyImageSubData appears to stall the pipeline until any pending - // renders to the source texture have completed. On Mali-G, it has been observed to - // indefinitely hang in some circumstances. Using an alternative such as glBlitFramebuffer - // is preferable on such devices, so pretend we don't support glCopyImageSubData. - // See bugs 1669494 and 1677757. - let supports_copy_image_sub_data = if renderer_name.starts_with("Mali") { - false - } else { - supports_extension(&extensions, "GL_EXT_copy_image") || - supports_extension(&extensions, "GL_ARB_copy_image") - }; - - // We have seen crashes on x86 PowerVR Rogue G6430 devices during GPU cache - // updates using the scatter shader. It seems likely that GL_EXT_color_buffer_float - // is broken. See bug 1709408. - let is_x86_powervr_rogue_g6430 = renderer_name.starts_with("PowerVR Rogue G6430") - && cfg!(target_arch = "x86"); - let supports_color_buffer_float = match gl.get_type() { - gl::GlType::Gl => true, - gl::GlType::Gles if is_x86_powervr_rogue_g6430 => false, - gl::GlType::Gles => supports_extension(&extensions, "GL_EXT_color_buffer_float"), - }; + let supports_copy_image_sub_data = supports_extension(&extensions, "GL_EXT_copy_image") || + supports_extension(&extensions, "GL_ARB_copy_image"); + + // Due to a bug on Adreno devices, blitting to an fbo bound to + // a non-0th layer of a texture array is not supported. + let supports_blit_to_texture_array = !renderer_name.starts_with("Adreno"); + + // Check if the device supports the two extensions needed in order to use + // pixel local storage. + // TODO(gw): Consider if we can remove fb fetch / init, by using PLS for opaque pass too. + // TODO(gw): Support EXT_shader_framebuffer_fetch as well. + let ext_pixel_local_storage = supports_extension(&extensions, "GL_EXT_shader_pixel_local_storage"); + let ext_framebuffer_fetch = supports_extension(&extensions, "GL_ARM_shader_framebuffer_fetch"); + let supports_pixel_local_storage = + allow_pixel_local_storage_support && + ext_framebuffer_fetch && + ext_pixel_local_storage; let is_adreno = renderer_name.starts_with("Adreno"); - // There appears to be a driver bug on older versions of the Adreno - // driver which prevents usage of persistenly mapped buffers. - // See bugs 1678585 and 1683936. - // TODO: only disable feature for affected driver versions. - let supports_buffer_storage = if is_adreno { - false - } else { - supports_extension(&extensions, "GL_EXT_buffer_storage") || - supports_extension(&extensions, "GL_ARB_buffer_storage") - }; - // KHR_blend_equation_advanced renders incorrectly on Adreno // devices. This has only been confirmed up to Adreno 5xx, and has been // fixed for Android 9, so this condition could be made more specific. @@ -1625,134 +1517,52 @@ impl Device { // from GL_TEXTURE_EXTERNAL_OES before binding another to GL_TEXTURE_2D. See bug 1636085. let requires_texture_external_unbind = is_emulator; - let is_macos = cfg!(target_os = "macos"); - // && renderer_name.starts_with("AMD"); - // (XXX: we apply this restriction to all GPUs to handle switching) - - let is_angle = renderer_name.starts_with("ANGLE"); - let is_adreno_3xx = renderer_name.starts_with("Adreno (TM) 3"); - - // Some GPUs require the stride of the data during texture uploads to be - // aligned to certain requirements, either for correctness or performance - // reasons. - let required_pbo_stride = if is_adreno_3xx { - // On Adreno 3xx, alignments of < 128 bytes can result in corrupted - // glyphs. See bug 1696039. - StrideAlignment::Bytes(NonZeroUsize::new(128).unwrap()) - } else if is_adreno { - // On later Adreno devices it must be a multiple of 64 *pixels* to - // hit the fast path, meaning value in bytes varies with the texture - // format. This is purely an optimization. + let is_amd_macos = cfg!(target_os = "macos") && renderer_name.starts_with("AMD"); + + // On certain GPUs PBO texture upload is only performed asynchronously + // if the stride of the data is a multiple of a certain value. + // On Adreno it must be a multiple of 64 pixels, meaning value in bytes + // varies with the texture format. + // On AMD Mac, it must always be a multiple of 256 bytes. + // Other platforms may have similar requirements and should be added + // here. + // The default value should be 4 bytes. + let optimal_pbo_stride = if is_adreno { StrideAlignment::Pixels(NonZeroUsize::new(64).unwrap()) - } else if is_macos { - // On AMD Mac, it must always be a multiple of 256 bytes. - // We apply this restriction to all GPUs to handle switching + } else if is_amd_macos { StrideAlignment::Bytes(NonZeroUsize::new(256).unwrap()) - } else if is_angle { - // On ANGLE, PBO texture uploads get incorrectly truncated if - // the stride is greater than the width * bpp. - StrideAlignment::Bytes(NonZeroUsize::new(1).unwrap()) } else { - // Other platforms may have similar requirements and should be added - // here. The default value should be 4 bytes. StrideAlignment::Bytes(NonZeroUsize::new(4).unwrap()) }; // On AMD Macs there is a driver bug which causes some texture uploads // from a non-zero offset within a PBO to fail. See bug 1603783. - let supports_nonzero_pbo_offsets = !is_macos; - - let is_mali = renderer_name.starts_with("Mali"); - - // On Mali-Gxx and Txxx there is a driver bug when rendering partial updates to - // offscreen render targets, so we must ensure we render to the entire target. - // See bug 1663355. - let supports_render_target_partial_update = !is_mali; - - let supports_shader_storage_object = match gl.get_type() { - // see https://www.g-truc.net/post-0734.html - gl::GlType::Gl => supports_extension(&extensions, "GL_ARB_shader_storage_buffer_object"), - gl::GlType::Gles => gl_version >= [3, 1], - }; - - // SWGL uses swgl_clipMask() instead of implementing clip-masking in shaders. - // This allows certain shaders to potentially bypass the more expensive alpha- - // pass variants if they know the alpha-pass was only required to deal with - // clip-masking. - let uses_native_clip_mask = is_software_webrender; - - // SWGL uses swgl_antiAlias() instead of implementing anti-aliasing in shaders. - // As above, this allows bypassing certain alpha-pass variants. - let uses_native_antialiasing = is_software_webrender; - - let supports_image_external_essl3 = supports_extension(&extensions, "GL_OES_EGL_image_external_essl3"); - - let is_mali_g = renderer_name.starts_with("Mali-G"); - - let mut requires_batched_texture_uploads = None; - if is_software_webrender { - // No benefit to batching texture uploads with swgl. - requires_batched_texture_uploads = Some(false); - } else if is_mali_g { - // On Mali-Gxx the driver really struggles with many small texture uploads, - // and handles fewer, larger uploads better. - requires_batched_texture_uploads = Some(true); - } - - // On Mali-Txxx devices we have observed crashes during draw calls when rendering - // to an alpha target immediately after using glClear to clear regions of it. - // Using a shader to clear the regions avoids the crash. See bug 1638593. - let is_mali_t = renderer_name.starts_with("Mali-T"); - let supports_alpha_target_clears = !is_mali_t; - - // On Linux we we have seen uploads to R8 format textures result in - // corruption on some AMD cards. - // See https://bugzilla.mozilla.org/show_bug.cgi?id=1687554#c13 - let supports_r8_texture_upload = if cfg!(target_os = "linux") - && renderer_name.starts_with("AMD Radeon RX") - { - false - } else { - true - }; + let supports_nonzero_pbo_offsets = !is_amd_macos; Device { gl, base_gl: None, - crash_annotator, resource_override_path, use_optimized_shaders, upload_method, - use_batched_texture_uploads: requires_batched_texture_uploads.unwrap_or(false), - use_draw_calls_for_texture_copy: false, - inside_frame: false, capabilities: Capabilities { supports_multisampling: false, //TODO supports_copy_image_sub_data, - supports_color_buffer_float, - supports_buffer_storage, + supports_blit_to_texture_array, + supports_pixel_local_storage, supports_advanced_blend_equation, supports_dual_source_blending, supports_khr_debug, supports_texture_swizzle, supports_nonzero_pbo_offsets, supports_texture_usage, - supports_render_target_partial_update, - supports_shader_storage_object, - requires_batched_texture_uploads, - supports_alpha_target_clears, - supports_r8_texture_upload, - uses_native_clip_mask, - uses_native_antialiasing, - supports_image_external_essl3, renderer_name, }, color_formats, bgra_formats, - bgra_pixel_type, swizzle_settings: SwizzleSettings { bgra8_sampling_swizzle, }, @@ -1762,9 +1572,8 @@ impl Device { bound_textures: [0; 16], bound_program: 0, - bound_program_name: Rc::new(std::ffi::CString::new("").unwrap()), bound_vao: 0, - bound_read_fbo: (FBOId(0), DeviceIntPoint::zero()), + bound_read_fbo: FBOId(0), bound_draw_fbo: FBOId(0), program_mode_id: UniformLocation::INVALID, default_read_fbo: FBOId(0), @@ -1773,13 +1582,14 @@ impl Device { depth_available: true, max_texture_size, + max_texture_layers, cached_programs, frame_id: GpuFrameId(0), extensions, texture_storage_usage, requires_null_terminated_shader_source, requires_texture_external_unbind, - required_pbo_stride, + optimal_pbo_stride, dump_shader_source, surface_origin_is_top_left, @@ -1812,6 +1622,11 @@ impl Device { self.surface_origin_is_top_left } + /// Returns the limit on texture array layers. + pub fn max_texture_layers(&self) -> usize { + self.max_texture_layers as usize + } + pub fn get_capabilities(&self) -> &Capabilities { &self.capabilities } @@ -1850,31 +1665,8 @@ impl Device { return (self.max_depth_ids() - 1) as f32; } - pub fn required_pbo_stride(&self) -> StrideAlignment { - self.required_pbo_stride - } - - pub fn upload_method(&self) -> &UploadMethod { - &self.upload_method - } - - pub fn use_batched_texture_uploads(&self) -> bool { - self.use_batched_texture_uploads - } - - pub fn use_draw_calls_for_texture_copy(&self) -> bool { - self.use_draw_calls_for_texture_copy - } - - pub fn set_use_batched_texture_uploads(&mut self, enabled: bool) { - if self.capabilities.requires_batched_texture_uploads.is_some() { - return; - } - self.use_batched_texture_uploads = enabled; - } - - pub fn set_use_draw_calls_for_texture_copy(&mut self, enabled: bool) { - self.use_draw_calls_for_texture_copy = enabled; + pub fn optimal_pbo_stride(&self) -> StrideAlignment { + self.optimal_pbo_stride } pub fn reset_state(&mut self) { @@ -1887,8 +1679,8 @@ impl Device { self.bound_vao = 0; self.gl.bind_vertex_array(0); - self.bound_read_fbo = (self.default_read_fbo, DeviceIntPoint::zero()); - self.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, self.default_read_fbo.0); + self.bound_read_fbo = self.default_read_fbo; + self.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, self.bound_read_fbo.0); self.bound_draw_fbo = self.default_draw_fbo; self.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.bound_draw_fbo.0); @@ -1914,35 +1706,31 @@ impl Device { } pub fn compile_shader( - &self, + gl: &dyn gl::Gl, name: &str, shader_type: gl::GLenum, source: &String, + requires_null_terminated_shader_source: bool, ) -> Result<gl::GLuint, ShaderError> { debug!("compile {}", name); - let id = self.gl.create_shader(shader_type); - - let mut new_source = Cow::from(source.as_str()); - // Ensure the source strings we pass to glShaderSource are - // null-terminated on buggy platforms. - if self.requires_null_terminated_shader_source { - new_source.to_mut().push('\0'); + let id = gl.create_shader(shader_type); + if requires_null_terminated_shader_source { + // Ensure the source strings we pass to glShaderSource are + // null-terminated on buggy platforms. + use std::ffi::CString; + let terminated_source = CString::new(source.as_bytes()).unwrap(); + gl.shader_source(id, &[terminated_source.as_bytes_with_nul()]); + } else { + gl.shader_source(id, &[source.as_bytes()]); } - - self.gl.shader_source(id, &[new_source.as_bytes()]); - self.gl.compile_shader(id); - let log = self.gl.get_shader_info_log(id); + gl.compile_shader(id); + let log = gl.get_shader_info_log(id); let mut status = [0]; unsafe { - self.gl.get_shader_iv(id, gl::COMPILE_STATUS, &mut status); + gl.get_shader_iv(id, gl::COMPILE_STATUS, &mut status); } if status[0] == 0 { - let type_str = match shader_type { - gl::VERTEX_SHADER => "vertex", - gl::FRAGMENT_SHADER => "fragment", - _ => panic!("Unexpected shader type {:x}", shader_type), - }; - error!("Failed to compile {} shader: {}\n{}", type_str, name, log); + error!("Failed to compile shader: {}\n{}", name, log); #[cfg(debug_assertions)] Self::print_shader_errors(source, &log); Err(ShaderError::Compilation(name.to_string(), log)) @@ -1966,15 +1754,7 @@ impl Device { // wrapper from our GL context. let being_profiled = profiler::thread_is_being_profiled(); let using_wrapper = self.base_gl.is_some(); - - // We can usually unwind driver stacks on x86 so we don't need to manually instrument - // gl calls there. Timestamps can be pretty expensive on Windows (2us each and perhaps - // an opportunity to be descheduled?) which makes the profiles gathered with this - // turned on less useful so only profile on ARM. - if cfg!(any(target_arch = "arm", target_arch = "aarch64")) - && being_profiled - && !using_wrapper - { + if being_profiled && !using_wrapper { fn note(name: &str, duration: Duration) { profiler::add_text_marker(cstr!("OpenGL Calls"), name, duration); } @@ -2068,18 +1848,13 @@ impl Device { self.bind_texture_impl(slot.into(), external_texture.id, external_texture.target, None); } - pub fn bind_read_target_impl( - &mut self, - fbo_id: FBOId, - offset: DeviceIntPoint, - ) { + pub fn bind_read_target_impl(&mut self, fbo_id: FBOId) { debug_assert!(self.inside_frame); - if self.bound_read_fbo != (fbo_id, offset) { + if self.bound_read_fbo != fbo_id { + self.bound_read_fbo = fbo_id; fbo_id.bind(self.gl(), FBOTarget::Read); } - - self.bound_read_fbo = (fbo_id, offset); } pub fn bind_read_target(&mut self, target: ReadTarget) { @@ -2087,10 +1862,9 @@ impl Device { ReadTarget::Default => self.default_read_fbo, ReadTarget::Texture { fbo_id } => fbo_id, ReadTarget::External { fbo } => fbo, - ReadTarget::NativeSurface { fbo_id, .. } => fbo_id, }; - self.bind_read_target_impl(fbo_id, target.offset()) + self.bind_read_target_impl(fbo_id) } fn bind_draw_target_impl(&mut self, fbo_id: FBOId) { @@ -2104,7 +1878,7 @@ impl Device { pub fn reset_read_target(&mut self) { let fbo = self.default_read_fbo; - self.bind_read_target_impl(fbo, DeviceIntPoint::zero()); + self.bind_read_target_impl(fbo); } @@ -2120,7 +1894,7 @@ impl Device { ) { let (fbo_id, rect, depth_available) = match target { DrawTarget::Default { rect, .. } => { - (self.default_draw_fbo, rect, false) + (self.default_draw_fbo, rect, true) } DrawTarget::Texture { dimensions, fbo_id, with_depth, .. } => { let rect = FramebufferIntRect::new( @@ -2206,12 +1980,6 @@ impl Device { program: &mut Program, descriptor: &VertexDescriptor, ) -> Result<(), ShaderError> { - let _guard = CrashAnnotatorGuard::new( - &self.crash_annotator, - CrashAnnotation::CompileShader, - &program.source_info.full_name_cstr - ); - assert!(!program.is_initialized()); let mut build_program = true; let info = &program.source_info; @@ -2255,7 +2023,7 @@ impl Device { if build_program { // Compile the vertex shader let vs_source = info.compute_source(self, ShaderKind::Vertex); - let vs_id = match self.compile_shader(&info.full_name(), gl::VERTEX_SHADER, &vs_source) { + let vs_id = match Device::compile_shader(&*self.gl, &info.base_filename, gl::VERTEX_SHADER, &vs_source, self.requires_null_terminated_shader_source) { Ok(vs_id) => vs_id, Err(err) => return Err(err), }; @@ -2263,7 +2031,7 @@ impl Device { // Compile the fragment shader let fs_source = info.compute_source(self, ShaderKind::Fragment); let fs_id = - match self.compile_shader(&info.full_name(), gl::FRAGMENT_SHADER, &fs_source) { + match Device::compile_shader(&*self.gl, &info.base_filename, gl::FRAGMENT_SHADER, &fs_source, self.requires_null_terminated_shader_source) { Ok(fs_id) => fs_id, Err(err) => { self.gl.delete_shader(vs_id); @@ -2356,17 +2124,13 @@ impl Device { program.is_initialized = true; program.u_transform = self.gl.get_uniform_location(program.id, "uTransform"); program.u_mode = self.gl.get_uniform_location(program.id, "uMode"); - program.u_texture_size = self.gl.get_uniform_location(program.id, "uTextureSize"); Ok(()) } - pub fn bind_program(&mut self, program: &Program) -> bool { + pub fn bind_program(&mut self, program: &Program) { debug_assert!(self.inside_frame); debug_assert!(program.is_initialized()); - if !program.is_initialized() { - return false; - } #[cfg(debug_assertions)] { self.shader_is_ready = true; @@ -2375,20 +2139,19 @@ impl Device { if self.bound_program != program.id { self.gl.use_program(program.id); self.bound_program = program.id; - self.bound_program_name = program.source_info.full_name_cstr.clone(); self.program_mode_id = UniformLocation(program.u_mode); } - true } pub fn create_texture( &mut self, - target: ImageBufferKind, + target: TextureTarget, format: ImageFormat, mut width: i32, mut height: i32, filter: TextureFilter, render_target: Option<RenderTargetInfo>, + layer_count: i32, ) -> Texture { debug_assert!(self.inside_frame); @@ -2403,11 +2166,13 @@ impl Device { id: self.gl.gen_textures(1)[0], target: get_gl_target(target), size: DeviceIntSize::new(width, height), + layer_count, format, filter, active_swizzle: Cell::default(), - fbo: None, - fbo_with_depth: None, + fbos: vec![], + fbos_with_depth: vec![], + blit_workaround_buffer: None, last_frame_used: self.frame_id, flags: TextureFlags::default(), }; @@ -2420,6 +2185,12 @@ impl Device { // Allocate storage. let desc = self.gl_describe_format(texture.format); + let is_array = match texture.target { + gl::TEXTURE_2D_ARRAY => true, + gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => false, + _ => panic!("BUG: Unexpected texture target!"), + }; + assert!(is_array || texture.layer_count == 1); // Firefox doesn't use mipmaps, but Servo uses them for standalone image // textures images larger than 512 pixels. This is the only case where @@ -2431,9 +2202,6 @@ impl Device { 1 }; - // We never want to upload texture data at the same time as allocating the texture. - self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0); - // Use glTexStorage where available, since it avoids allocating // unnecessary mipmap storage and generally improves performance with // stronger invariants. @@ -2442,26 +2210,49 @@ impl Device { TexStorageUsage::NonBGRA8 => texture.format != ImageFormat::BGRA8, TexStorageUsage::Never => false, }; - if use_texture_storage { - self.gl.tex_storage_2d( - texture.target, - mipmap_levels, - desc.internal, - texture.size.width as gl::GLint, - texture.size.height as gl::GLint, - ); - } else { - self.gl.tex_image_2d( - texture.target, - 0, - desc.internal as gl::GLint, - texture.size.width as gl::GLint, - texture.size.height as gl::GLint, - 0, - desc.external, - desc.pixel_type, - None, - ); + match (use_texture_storage, is_array) { + (true, true) => + self.gl.tex_storage_3d( + gl::TEXTURE_2D_ARRAY, + mipmap_levels, + desc.internal, + texture.size.width as gl::GLint, + texture.size.height as gl::GLint, + texture.layer_count, + ), + (true, false) => + self.gl.tex_storage_2d( + texture.target, + mipmap_levels, + desc.internal, + texture.size.width as gl::GLint, + texture.size.height as gl::GLint, + ), + (false, true) => + self.gl.tex_image_3d( + gl::TEXTURE_2D_ARRAY, + 0, + desc.internal as gl::GLint, + texture.size.width as gl::GLint, + texture.size.height as gl::GLint, + texture.layer_count, + 0, + desc.external, + desc.pixel_type, + None, + ), + (false, false) => + self.gl.tex_image_2d( + texture.target, + 0, + desc.internal as gl::GLint, + texture.size.width as gl::GLint, + texture.size.height as gl::GLint, + 0, + desc.external, + desc.pixel_type, + None, + ), } // Set up FBOs, if required. @@ -2472,6 +2263,28 @@ impl Device { } } + // Set up intermediate buffer for blitting to texture, if required. + if texture.layer_count > 1 && !self.capabilities.supports_blit_to_texture_array { + let rbo = RBOId(self.gl.gen_renderbuffers(1)[0]); + let fbo = FBOId(self.gl.gen_framebuffers(1)[0]); + self.gl.bind_renderbuffer(gl::RENDERBUFFER, rbo.0); + self.gl.renderbuffer_storage( + gl::RENDERBUFFER, + self.matching_renderbuffer_format(texture.format), + texture.size.width as _, + texture.size.height as _ + ); + + self.bind_draw_target_impl(fbo); + self.gl.framebuffer_renderbuffer( + gl::DRAW_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + gl::RENDERBUFFER, + rbo.0 + ); + texture.blit_workaround_buffer = Some((rbo, fbo)); + } + texture } @@ -2498,10 +2311,8 @@ impl Device { .tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint); } - /// Copies the entire contents of one texture to another. The dest texture must be at least - /// as large as the source texture in each dimension. No scaling is performed, so if the dest - /// texture is larger than the source texture then some of its pixels will not be written to. - pub fn copy_entire_texture( + /// Copies the contents from one renderable texture to another. + pub fn blit_renderable_texture( &mut self, dst: &mut Texture, src: &Texture, @@ -2509,107 +2320,59 @@ impl Device { debug_assert!(self.inside_frame); debug_assert!(dst.size.width >= src.size.width); debug_assert!(dst.size.height >= src.size.height); + debug_assert!(dst.layer_count >= src.layer_count); - self.copy_texture_sub_region( - src, - 0, - 0, - dst, - 0, - 0, - src.size.width as _, - src.size.height as _, - ); - } - - /// Copies the specified subregion from src_texture to dest_texture. - pub fn copy_texture_sub_region( - &mut self, - src_texture: &Texture, - src_x: usize, - src_y: usize, - dest_texture: &Texture, - dest_x: usize, - dest_y: usize, - width: usize, - height: usize, - ) { if self.capabilities.supports_copy_image_sub_data { - assert_ne!( - src_texture.id, dest_texture.id, - "glCopyImageSubData's behaviour is undefined if src and dst images are identical and the rectangles overlap." - ); + assert_ne!(src.id, dst.id, + "glCopyImageSubData's behaviour is undefined if src and dst images are identical and the rectangles overlap."); unsafe { - self.gl.copy_image_sub_data( - src_texture.id, - src_texture.target, - 0, - src_x as _, - src_y as _, - 0, - dest_texture.id, - dest_texture.target, - 0, - dest_x as _, - dest_y as _, - 0, - width as _, - height as _, - 1, - ); + self.gl.copy_image_sub_data(src.id, src.target, 0, + 0, 0, 0, + dst.id, dst.target, 0, + 0, 0, 0, + src.size.width as _, src.size.height as _, src.layer_count); } } else { - let src_offset = FramebufferIntPoint::new(src_x as i32, src_y as i32); - let dest_offset = FramebufferIntPoint::new(dest_x as i32, dest_y as i32); - let size = FramebufferIntSize::new(width as i32, height as i32); - - self.blit_render_target( - ReadTarget::from_texture(src_texture), - FramebufferIntRect::new(src_offset, size), - DrawTarget::from_texture(dest_texture, false), - FramebufferIntRect::new(dest_offset, size), - // In most cases the filter shouldn't matter, as there is no scaling involved - // in the blit. We were previously using Linear, but this caused issues when - // blitting RGBAF32 textures on Mali, so use Nearest to be safe. - TextureFilter::Nearest, + let rect = FramebufferIntRect::new( + FramebufferIntPoint::zero(), + device_size_as_framebuffer_size(src.get_dimensions()), ); + for layer in 0..src.layer_count.min(dst.layer_count) as LayerIndex { + self.blit_render_target( + ReadTarget::from_texture(src, layer), + rect, + DrawTarget::from_texture(dst, layer, false), + rect, + TextureFilter::Linear + ); + } + self.reset_draw_target(); + self.reset_read_target(); } } /// Notifies the device that the contents of a render target are no longer /// needed. + /// + /// FIXME(bholley): We could/should invalidate the depth targets earlier + /// than the color targets, i.e. immediately after each pass. pub fn invalidate_render_target(&mut self, texture: &Texture) { - let (fbo, attachments) = if texture.supports_depth() { - (&texture.fbo_with_depth, + let (fbos, attachments) = if texture.supports_depth() { + (&texture.fbos_with_depth, &[gl::COLOR_ATTACHMENT0, gl::DEPTH_ATTACHMENT] as &[gl::GLenum]) } else { - (&texture.fbo, &[gl::COLOR_ATTACHMENT0] as &[gl::GLenum]) + (&texture.fbos, &[gl::COLOR_ATTACHMENT0] as &[gl::GLenum]) }; - if let Some(fbo_id) = fbo { - let original_bound_fbo = self.bound_draw_fbo; + let original_bound_fbo = self.bound_draw_fbo; + for fbo_id in fbos.iter() { // Note: The invalidate extension may not be supported, in which // case this is a no-op. That's ok though, because it's just a // hint. self.bind_external_draw_target(*fbo_id); self.gl.invalidate_framebuffer(gl::FRAMEBUFFER, attachments); - self.bind_external_draw_target(original_bound_fbo); } - } - - /// Notifies the device that the contents of the current framebuffer's depth - /// attachment is no longer needed. Unlike invalidate_render_target, this can - /// be called even when the contents of the colour attachment is still required. - /// This should be called before unbinding the framebuffer at the end of a pass, - /// to allow tiled GPUs to avoid writing the contents back to memory. - pub fn invalidate_depth_target(&mut self) { - assert!(self.depth_available); - let attachments = if self.bound_draw_fbo == self.default_draw_fbo { - &[gl::DEPTH] as &[gl::GLenum] - } else { - &[gl::DEPTH_ATTACHMENT] as &[gl::GLenum] - }; - self.gl.invalidate_framebuffer(gl::DRAW_FRAMEBUFFER, attachments); + self.bind_external_draw_target(original_bound_fbo); } /// Notifies the device that a render target is about to be reused. @@ -2629,49 +2392,71 @@ impl Device { } fn init_fbos(&mut self, texture: &mut Texture, with_depth: bool) { - let (fbo, depth_rb) = if with_depth { + let (fbos, depth_rb) = if with_depth { let depth_target = self.acquire_depth_target(texture.get_dimensions()); - (&mut texture.fbo_with_depth, Some(depth_target)) + (&mut texture.fbos_with_depth, Some(depth_target)) } else { - (&mut texture.fbo, None) + (&mut texture.fbos, None) }; // Generate the FBOs. - assert!(fbo.is_none()); - let fbo_id = FBOId(*self.gl.gen_framebuffers(1).first().unwrap()); - *fbo = Some(fbo_id); + assert!(fbos.is_empty()); + fbos.extend(self.gl.gen_framebuffers(texture.layer_count).into_iter().map(FBOId)); // Bind the FBOs. let original_bound_fbo = self.bound_draw_fbo; + for (fbo_index, &fbo_id) in fbos.iter().enumerate() { + self.bind_external_draw_target(fbo_id); + match texture.target { + gl::TEXTURE_2D_ARRAY => { + self.gl.framebuffer_texture_layer( + gl::DRAW_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + texture.id, + 0, + fbo_index as _, + ) + } + _ => { + assert_eq!(fbo_index, 0); + self.gl.framebuffer_texture_2d( + gl::DRAW_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + texture.target, + texture.id, + 0, + ) + } + } - self.bind_external_draw_target(fbo_id); - - self.gl.framebuffer_texture_2d( - gl::DRAW_FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - texture.target, - texture.id, - 0, - ); + if let Some(depth_rb) = depth_rb { + self.gl.framebuffer_renderbuffer( + gl::DRAW_FRAMEBUFFER, + gl::DEPTH_ATTACHMENT, + gl::RENDERBUFFER, + depth_rb.0, + ); + } - if let Some(depth_rb) = depth_rb { - self.gl.framebuffer_renderbuffer( - gl::DRAW_FRAMEBUFFER, - gl::DEPTH_ATTACHMENT, - gl::RENDERBUFFER, - depth_rb.0, + debug_assert_eq!( + self.gl.check_frame_buffer_status(gl::DRAW_FRAMEBUFFER), + gl::FRAMEBUFFER_COMPLETE, + "Incomplete framebuffer", ); } - - debug_assert_eq!( - self.gl.check_frame_buffer_status(gl::DRAW_FRAMEBUFFER), - gl::FRAMEBUFFER_COMPLETE, - "Incomplete framebuffer", - ); - self.bind_external_draw_target(original_bound_fbo); } + fn deinit_fbos(&mut self, fbos: &mut Vec<FBOId>) { + if !fbos.is_empty() { + let fbo_ids: SmallVec<[gl::GLuint; 8]> = fbos + .drain(..) + .map(|FBOId(fbo_id)| fbo_id) + .collect(); + self.gl.delete_framebuffers(&fbo_ids[..]); + } + } + fn acquire_depth_target(&mut self, dimensions: DeviceIntSize) -> RBOId { let gl = &self.gl; let depth_format = self.depth_format; @@ -2721,14 +2506,11 @@ impl Device { TextureFilter::Linear | TextureFilter::Trilinear => gl::LINEAR, }; - let src_x0 = src_rect.origin.x + self.bound_read_fbo.1.x; - let src_y0 = src_rect.origin.y + self.bound_read_fbo.1.y; - self.gl.blit_framebuffer( - src_x0, - src_y0, - src_x0 + src_rect.size.width, - src_y0 + src_rect.size.height, + src_rect.origin.x, + src_rect.origin.y, + src_rect.origin.x + src_rect.size.width, + src_rect.origin.y + src_rect.size.height, dest_rect.origin.x, dest_rect.origin.y, dest_rect.origin.x + dest_rect.size.width, @@ -2752,9 +2534,60 @@ impl Device { self.bind_read_target(src_target); - self.bind_draw_target(dest_target); + match dest_target { + DrawTarget::Texture { layer, blit_workaround_buffer, dimensions, id, target, .. } if layer != 0 && + !self.capabilities.supports_blit_to_texture_array => + { + // This should have been initialized in create_texture(). + let (_rbo, fbo) = blit_workaround_buffer.expect("Blit workaround buffer has not been initialized."); + + // Blit from read target to intermediate buffer. + self.bind_draw_target_impl(fbo); + self.blit_render_target_impl( + src_rect, + dest_rect, + filter + ); + + // dest_rect may be inverted, so min_x/y() might actually be the + // bottom-right, max_x/y() might actually be the top-left, + // and width/height might be negative. See servo/euclid#321. + // Calculate the non-inverted rect here. + let dest_bounds = DeviceIntRect::new( + DeviceIntPoint::new( + dest_rect.min_x().min(dest_rect.max_x()), + dest_rect.min_y().min(dest_rect.max_y()), + ), + DeviceIntSize::new( + dest_rect.size.width.abs(), + dest_rect.size.height.abs(), + ), + ).intersection(&dimensions.into()).unwrap_or_else(DeviceIntRect::zero); + + self.bind_read_target_impl(fbo); + self.bind_texture_impl( + DEFAULT_TEXTURE, + id, + target, + None, // not depending on swizzle + ); - self.blit_render_target_impl(src_rect, dest_rect, filter); + // Copy from intermediate buffer to the texture layer. + self.gl.copy_tex_sub_image_3d( + target, 0, + dest_bounds.origin.x, dest_bounds.origin.y, + layer as _, + dest_bounds.origin.x, dest_bounds.origin.y, + dest_bounds.size.width, dest_bounds.size.height, + ); + + } + _ => { + self.bind_draw_target(dest_target); + + self.blit_render_target_impl(src_rect, dest_rect, filter); + } + } } /// Performs a blit while flipping vertically. Useful for blitting textures @@ -2785,18 +2618,15 @@ impl Device { pub fn delete_texture(&mut self, mut texture: Texture) { debug_assert!(self.inside_frame); let had_depth = texture.supports_depth(); - if let Some(fbo) = texture.fbo { - self.gl.delete_framebuffers(&[fbo.0]); - texture.fbo = None; - } - if let Some(fbo) = texture.fbo_with_depth { - self.gl.delete_framebuffers(&[fbo.0]); - texture.fbo_with_depth = None; - } - + self.deinit_fbos(&mut texture.fbos); + self.deinit_fbos(&mut texture.fbos_with_depth); if had_depth { self.release_depth_target(texture.get_dimensions()); } + if let Some((rbo, fbo)) = texture.blit_workaround_buffer { + self.gl.delete_framebuffers(&[fbo.0]); + self.gl.delete_renderbuffers(&[rbo.0]); + } self.gl.delete_textures(&[texture.id]); @@ -2862,7 +2692,6 @@ impl Device { id: pid, u_transform: 0, u_mode: 0, - u_texture_size: 0, source_info, is_initialized: false, }; @@ -2929,22 +2758,6 @@ impl Device { self.gl.uniform_1i(self.program_mode_id.0, mode); } - /// Sets the uTextureSize uniform. Most shaders do not require this to be called - /// as they use the textureSize GLSL function instead. - pub fn set_shader_texture_size( - &self, - program: &Program, - texture_size: DeviceSize, - ) { - debug_assert!(self.inside_frame); - #[cfg(debug_assertions)] - debug_assert!(self.shader_is_ready); - - if program.u_texture_size != -1 { - self.gl.uniform_2f(program.u_texture_size, texture_size.width, texture_size.height); - } - } - pub fn create_pbo(&mut self) -> PBO { let id = self.gl.gen_buffers(1)[0]; PBO { @@ -3046,7 +2859,7 @@ impl Device { let bytes_pp = format.bytes_per_pixel() as usize; let width_bytes = size.width as usize * bytes_pp; - let dst_stride = round_up_to_multiple(width_bytes, self.required_pbo_stride.num_bytes(format)); + let dst_stride = round_up_to_multiple(width_bytes, self.optimal_pbo_stride.num_bytes(format)); // The size of the chunk should only need to be (height - 1) * dst_stride + width_bytes, // however, the android emulator will error unless it is height * dst_stride. @@ -3058,19 +2871,78 @@ impl Device { (dst_size, dst_stride) } + /// (Re)allocates and maps a PBO, returning a `PixelBuffer` if successful. + /// The contents can be written to using the `mapping` field. + /// The buffer must be bound to `GL_PIXEL_UNPACK_BUFFER` before calling this function, + /// and must be unmapped using `glUnmapBuffer` prior to uploading the contents to a texture. + fn create_upload_buffer<'a>(&mut self, hint: VertexUsageHint, size: usize) -> Result<PixelBuffer<'a>, ()> { + self.gl.buffer_data_untyped( + gl::PIXEL_UNPACK_BUFFER, + size as _, + ptr::null(), + hint.to_gl(), + ); + let ptr = self.gl.map_buffer_range( + gl::PIXEL_UNPACK_BUFFER, + 0, + size as _, + gl::MAP_WRITE_BIT | gl::MAP_INVALIDATE_BUFFER_BIT, + ); + + if ptr != ptr::null_mut() { + let mapping = unsafe { + slice::from_raw_parts_mut(ptr as *mut _, size) + }; + Ok(PixelBuffer::new(size, mapping)) + } else { + error!("Failed to map PBO of size {} bytes", size); + Err(()) + } + } + /// Returns a `TextureUploader` which can be used to upload texture data to `texture`. - /// Once uploads have been performed the uploader must be flushed with `TextureUploader::flush()`. - pub fn upload_texture<'a>( - &mut self, - pbo_pool: &'a mut UploadPBOPool, - ) -> TextureUploader<'a> { + /// The total size in bytes is specified by `upload_size`, and must be greater than zero + /// and at least as large as the sum of the sizes returned from + /// `required_upload_size_and_stride()` for each subsequent call to `TextureUploader.upload()`. + pub fn upload_texture<'a, T>( + &'a mut self, + texture: &'a Texture, + pbo: &PBO, + upload_size: usize, + ) -> TextureUploader<'a, T> { debug_assert!(self.inside_frame); + assert_ne!(upload_size, 0, "Must specify valid upload size"); - pbo_pool.begin_frame(self); + self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default()); + + let uploader_type = match self.upload_method { + UploadMethod::Immediate => TextureUploaderType::Immediate, + UploadMethod::PixelBuffer(hint) => { + self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, pbo.id); + if self.capabilities.supports_nonzero_pbo_offsets { + match self.create_upload_buffer(hint, upload_size) { + Ok(buffer) => TextureUploaderType::MutliUseBuffer(buffer), + Err(_) => { + // If allocating the buffer failed, fall back to immediate uploads + self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0); + TextureUploaderType::Immediate + } + } + } else { + // If we cannot upload from non-zero offsets, then we must + // reallocate a new buffer for each upload. + TextureUploaderType::SingleUseBuffers(hint) + } + }, + }; TextureUploader { - buffers: Vec::new(), - pbo_pool, + target: UploadTarget { + device: self, + texture, + }, + uploader_type, + marker: PhantomData, } } @@ -3082,17 +2954,35 @@ impl Device { ) { self.bind_texture(DEFAULT_TEXTURE, texture, Swizzle::default()); let desc = self.gl_describe_format(texture.format); - self.gl.tex_sub_image_2d( - texture.target, - 0, - 0, - 0, - texture.size.width as gl::GLint, - texture.size.height as gl::GLint, - desc.external, - desc.pixel_type, - texels_to_u8_slice(pixels), - ); + match texture.target { + gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => + self.gl.tex_sub_image_2d( + texture.target, + 0, + 0, + 0, + texture.size.width as gl::GLint, + texture.size.height as gl::GLint, + desc.external, + desc.pixel_type, + texels_to_u8_slice(pixels), + ), + gl::TEXTURE_2D_ARRAY => + self.gl.tex_sub_image_3d( + texture.target, + 0, + 0, + 0, + 0, + texture.size.width as gl::GLint, + texture.size.height as gl::GLint, + texture.layer_count as gl::GLint, + desc.external, + desc.pixel_type, + texels_to_u8_slice(pixels), + ), + _ => panic!("BUG: Unexpected texture target!"), + } } pub fn read_pixels(&mut self, img_desc: &ImageDescriptor) -> Vec<u8> { @@ -3149,24 +3039,40 @@ impl Device { } /// Attaches the provided texture to the current Read FBO binding. - fn attach_read_texture_raw(&mut self, texture_id: gl::GLuint, target: gl::GLuint) { - self.gl.framebuffer_texture_2d( - gl::READ_FRAMEBUFFER, - gl::COLOR_ATTACHMENT0, - target, - texture_id, - 0, - ) + fn attach_read_texture_raw( + &mut self, texture_id: gl::GLuint, target: gl::GLuint, layer_id: i32 + ) { + match target { + gl::TEXTURE_2D_ARRAY => { + self.gl.framebuffer_texture_layer( + gl::READ_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + texture_id, + 0, + layer_id, + ) + } + _ => { + assert_eq!(layer_id, 0); + self.gl.framebuffer_texture_2d( + gl::READ_FRAMEBUFFER, + gl::COLOR_ATTACHMENT0, + target, + texture_id, + 0, + ) + } + } } pub fn attach_read_texture_external( - &mut self, texture_id: gl::GLuint, target: ImageBufferKind + &mut self, texture_id: gl::GLuint, target: TextureTarget, layer_id: i32 ) { - self.attach_read_texture_raw(texture_id, get_gl_target(target)) + self.attach_read_texture_raw(texture_id, get_gl_target(target), layer_id) } - pub fn attach_read_texture(&mut self, texture: &Texture) { - self.attach_read_texture_raw(texture.id, texture.target) + pub fn attach_read_texture(&mut self, texture: &Texture, layer_id: i32) { + self.attach_read_texture_raw(texture.id, texture.target, layer_id) } fn bind_vao_impl(&mut self, id: gl::GLuint) { @@ -3191,7 +3097,6 @@ impl Device { descriptor: &VertexDescriptor, main_vbo_id: VBOId, instance_vbo_id: VBOId, - instance_divisor: u32, ibo_id: IBOId, owns_vertices_and_indices: bool, ) -> VAO { @@ -3200,7 +3105,7 @@ impl Device { self.bind_vao_impl(vao_id); - descriptor.bind(self.gl(), main_vbo_id, instance_vbo_id, instance_divisor); + descriptor.bind(self.gl(), main_vbo_id, instance_vbo_id); ibo_id.bind(self.gl()); // force it to be a part of VAO VAO { @@ -3209,7 +3114,6 @@ impl Device { main_vbo_id, instance_vbo_id, instance_stride, - instance_divisor, owns_vertices_and_indices, } } @@ -3260,7 +3164,7 @@ impl Device { vbo.id = 0; } - pub fn create_vao(&mut self, descriptor: &VertexDescriptor, instance_divisor: u32) -> VAO { + pub fn create_vao(&mut self, descriptor: &VertexDescriptor) -> VAO { debug_assert!(self.inside_frame); let buffer_ids = self.gl.gen_buffers(3); @@ -3268,7 +3172,7 @@ impl Device { let main_vbo_id = VBOId(buffer_ids[1]); let intance_vbo_id = VBOId(buffer_ids[2]); - self.create_vao_with_vbos(descriptor, main_vbo_id, intance_vbo_id, instance_divisor, ibo_id, true) + self.create_vao_with_vbos(descriptor, main_vbo_id, intance_vbo_id, ibo_id, true) } pub fn delete_vao(&mut self, mut vao: VAO) { @@ -3346,7 +3250,6 @@ impl Device { descriptor, base_vao.main_vbo_id, intance_vbo_id, - base_vao.instance_divisor, base_vao.ibo_id, false, ) @@ -3362,54 +3265,16 @@ impl Device { self.update_vbo_data(vao.main_vbo_id, vertices, usage_hint) } - pub fn update_vao_instances<V: Clone>( + pub fn update_vao_instances<V>( &mut self, vao: &VAO, instances: &[V], usage_hint: VertexUsageHint, - // if `Some(count)`, each instance is repeated `count` times - repeat: Option<NonZeroUsize>, ) { debug_assert_eq!(self.bound_vao, vao.id); debug_assert_eq!(vao.instance_stride as usize, mem::size_of::<V>()); - match repeat { - Some(count) => { - let target = gl::ARRAY_BUFFER; - self.gl.bind_buffer(target, vao.instance_vbo_id.0); - let size = instances.len() * count.get() * mem::size_of::<V>(); - self.gl.buffer_data_untyped( - target, - size as _, - ptr::null(), - usage_hint.to_gl(), - ); - - let ptr = match self.gl.get_type() { - gl::GlType::Gl => { - self.gl.map_buffer(target, gl::WRITE_ONLY) - } - gl::GlType::Gles => { - self.gl.map_buffer_range(target, 0, size as _, gl::MAP_WRITE_BIT) - } - }; - assert!(!ptr.is_null()); - - let buffer_slice = unsafe { - slice::from_raw_parts_mut(ptr as *mut V, instances.len() * count.get()) - }; - for (quad, instance) in buffer_slice.chunks_mut(4).zip(instances) { - quad[0] = instance.clone(); - quad[1] = instance.clone(); - quad[2] = instance.clone(); - quad[3] = instance.clone(); - } - self.gl.unmap_buffer(target); - } - None => { - self.update_vbo_data(vao.instance_vbo_id, instances, usage_hint); - } - } + self.update_vbo_data(vao.instance_vbo_id, instances, usage_hint) } pub fn update_vao_indices<I>(&mut self, vao: &VAO, indices: &[I], usage_hint: VertexUsageHint) { @@ -3430,12 +3295,6 @@ impl Device { #[cfg(debug_assertions)] debug_assert!(self.shader_is_ready); - let _guard = CrashAnnotatorGuard::new( - &self.crash_annotator, - CrashAnnotation::DrawShader, - &self.bound_program_name, - ); - self.gl.draw_elements( gl::TRIANGLES, index_count, @@ -3449,12 +3308,6 @@ impl Device { #[cfg(debug_assertions)] debug_assert!(self.shader_is_ready); - let _guard = CrashAnnotatorGuard::new( - &self.crash_annotator, - CrashAnnotation::DrawShader, - &self.bound_program_name, - ); - self.gl.draw_elements( gl::TRIANGLES, index_count, @@ -3468,12 +3321,6 @@ impl Device { #[cfg(debug_assertions)] debug_assert!(self.shader_is_ready); - let _guard = CrashAnnotatorGuard::new( - &self.crash_annotator, - CrashAnnotation::DrawShader, - &self.bound_program_name, - ); - self.gl.draw_arrays(gl::POINTS, first_vertex, vertex_count); } @@ -3482,45 +3329,14 @@ impl Device { #[cfg(debug_assertions)] debug_assert!(self.shader_is_ready); - let _guard = CrashAnnotatorGuard::new( - &self.crash_annotator, - CrashAnnotation::DrawShader, - &self.bound_program_name, - ); - self.gl.draw_arrays(gl::LINES, first_vertex, vertex_count); } - pub fn draw_indexed_triangles(&mut self, index_count: i32) { - debug_assert!(self.inside_frame); - #[cfg(debug_assertions)] - debug_assert!(self.shader_is_ready); - - let _guard = CrashAnnotatorGuard::new( - &self.crash_annotator, - CrashAnnotation::DrawShader, - &self.bound_program_name, - ); - - self.gl.draw_elements( - gl::TRIANGLES, - index_count, - gl::UNSIGNED_SHORT, - 0, - ); - } - pub fn draw_indexed_triangles_instanced_u16(&mut self, index_count: i32, instance_count: i32) { debug_assert!(self.inside_frame); #[cfg(debug_assertions)] debug_assert!(self.shader_is_ready); - let _guard = CrashAnnotatorGuard::new( - &self.crash_annotator, - CrashAnnotation::DrawShader, - &self.bound_program_name, - ); - self.gl.draw_elements_instanced( gl::TRIANGLES, index_count, @@ -3682,7 +3498,7 @@ impl Device { pub fn set_blend_mode_alpha(&mut self) { self.set_blend_factors( (gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA), - (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), + (gl::ONE, gl::ONE), ); } @@ -3750,24 +3566,6 @@ impl Device { (gl::ONE, gl::ONE_MINUS_SRC1_ALPHA), ); } - pub fn set_blend_mode_multiply_dual_source(&mut self) { - self.set_blend_factors( - (gl::ONE_MINUS_DST_ALPHA, gl::ONE_MINUS_SRC1_COLOR), - (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), - ); - } - pub fn set_blend_mode_screen(&mut self) { - self.set_blend_factors( - (gl::ONE, gl::ONE_MINUS_SRC_COLOR), - (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), - ); - } - pub fn set_blend_mode_exclusion(&mut self) { - self.set_blend_factors( - (gl::ONE_MINUS_DST_COLOR, gl::ONE_MINUS_SRC_COLOR), - (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), - ); - } pub fn set_blend_mode_show_overdraw(&mut self) { self.set_blend_factors( (gl::ONE, gl::ONE_MINUS_SRC_ALPHA), @@ -3826,6 +3624,18 @@ impl Device { supports_extension(&self.extensions, extension) } + /// Enable the pixel local storage functionality. Caller must + /// have already confirmed the device supports this. + pub fn enable_pixel_local_storage(&mut self, enable: bool) { + debug_assert!(self.capabilities.supports_pixel_local_storage); + + if enable { + self.gl.enable(gl::SHADER_PIXEL_LOCAL_STORAGE_EXT); + } else { + self.gl.disable(gl::SHADER_PIXEL_LOCAL_STORAGE_EXT); + } + } + pub fn echo_driver_messages(&self) { if self.capabilities.supports_khr_debug { Device::log_driver_messages(self.gl()); @@ -3876,7 +3686,7 @@ impl Device { internal: self.bgra_formats.internal, external: self.bgra_formats.external, read: gl::BGRA, - pixel_type: self.bgra_pixel_type, + pixel_type: gl::UNSIGNED_BYTE, } }, ImageFormat::RGBA8 => { @@ -3914,18 +3724,26 @@ impl Device { } } + /// Returns a GL format matching an ImageFormat suitable for a renderbuffer. + fn matching_renderbuffer_format(&self, format: ImageFormat) -> gl::GLenum { + match format { + ImageFormat::R8 => gl::R8, + ImageFormat::R16 => gl::R16UI, + ImageFormat::BGRA8 => panic!("Unable to render to BGRA format!"), + ImageFormat::RGBAF32 => gl::RGBA32F, + ImageFormat::RG8 => gl::RG8, + ImageFormat::RG16 => gl::RG16, + ImageFormat::RGBAI32 => gl::RGBA32I, + ImageFormat::RGBA8 => gl::RGBA8, + } + } + /// Generates a memory report for the resources managed by the device layer. - pub fn report_memory(&self, size_op_funs: &MallocSizeOfOps) -> MemoryReport { + pub fn report_memory(&self) -> MemoryReport { let mut report = MemoryReport::default(); for dim in self.depth_targets.keys() { report.depth_target_textures += depth_target_size_in_bytes(dim); } - #[cfg(feature = "sw_compositor")] - { - report.swgl += swgl::Context::report_memory(size_op_funs.size_of_op); - } - // unconditionally use size_op_funs - let _ = size_op_funs; report } } @@ -3942,43 +3760,40 @@ pub struct FormatDesc { pub pixel_type: gl::GLuint, } -#[derive(Debug)] -struct UploadChunk<'a> { +struct UploadChunk { rect: DeviceIntRect, + layer_index: i32, stride: Option<i32>, offset: usize, format_override: Option<ImageFormat>, - texture: &'a Texture, } -#[derive(Debug)] struct PixelBuffer<'a> { + size_allocated: usize, size_used: usize, // small vector avoids heap allocation for a single chunk - chunks: SmallVec<[UploadChunk<'a>; 1]>, - inner: UploadPBO, + chunks: SmallVec<[UploadChunk; 1]>, mapping: &'a mut [mem::MaybeUninit<u8>], } impl<'a> PixelBuffer<'a> { fn new( - pbo: UploadPBO, + size_allocated: usize, + mapping: &'a mut [mem::MaybeUninit<u8>], ) -> Self { - let mapping = unsafe { - slice::from_raw_parts_mut(pbo.mapping.get_ptr().as_ptr(), pbo.pbo.reserved_size) - }; - Self { + PixelBuffer { + size_allocated, size_used: 0, chunks: SmallVec::new(), - inner: pbo, mapping, } } - fn flush_chunks(&mut self, device: &mut Device) { + fn flush_chunks(&mut self, target: &mut UploadTarget) { for chunk in self.chunks.drain(..) { - TextureUploader::update_impl(device, chunk); + target.update_impl(chunk); } + self.size_used = 0; } } @@ -3988,440 +3803,44 @@ impl<'a> Drop for PixelBuffer<'a> { } } -#[derive(Debug)] -enum PBOMapping { - Unmapped, - Transient(ptr::NonNull<mem::MaybeUninit<u8>>), - Persistent(ptr::NonNull<mem::MaybeUninit<u8>>), -} - -impl PBOMapping { - fn get_ptr(&self) -> ptr::NonNull<mem::MaybeUninit<u8>> { - match self { - PBOMapping::Unmapped => unreachable!("Cannot get pointer to unmapped PBO."), - PBOMapping::Transient(ptr) => *ptr, - PBOMapping::Persistent(ptr) => *ptr, - } - } -} - -/// A PBO for uploading texture data, managed by UploadPBOPool. -#[derive(Debug)] -struct UploadPBO { - pbo: PBO, - mapping: PBOMapping, - can_recycle: bool, +struct UploadTarget<'a> { + device: &'a mut Device, + texture: &'a Texture, } -impl UploadPBO { - fn empty() -> Self { - Self { - pbo: PBO { - id: 0, - reserved_size: 0, - }, - mapping: PBOMapping::Unmapped, - can_recycle: false, - } - } +enum TextureUploaderType<'a> { + Immediate, + SingleUseBuffers(VertexUsageHint), + MutliUseBuffer(PixelBuffer<'a>) } -/// Allocates and recycles PBOs used for uploading texture data. -/// Tries to allocate and recycle PBOs of a fixed size, but will make exceptions when -/// a larger buffer is required or to work around driver bugs. -pub struct UploadPBOPool { - /// Usage hint to provide to the driver for optimizations. - usage_hint: VertexUsageHint, - /// The preferred size, in bytes, of the buffers to allocate. - default_size: usize, - /// List of allocated PBOs ready to be re-used. - available_buffers: Vec<UploadPBO>, - /// PBOs which have been returned during the current frame, - /// and do not yet have an associated sync object. - returned_buffers: Vec<UploadPBO>, - /// PBOs which are waiting until their sync object is signalled, - /// indicating they can are ready to be re-used. - waiting_buffers: Vec<(gl::GLsync, Vec<UploadPBO>)>, - /// PBOs which have been orphaned. - /// We can recycle their IDs but must reallocate their storage. - orphaned_buffers: Vec<PBO>, +pub struct TextureUploader<'a, T> { + target: UploadTarget<'a>, + uploader_type: TextureUploaderType<'a>, + marker: PhantomData<T>, } -impl UploadPBOPool { - pub fn new(device: &mut Device, default_size: usize) -> Self { - let usage_hint = match device.upload_method { - UploadMethod::Immediate => VertexUsageHint::Stream, - UploadMethod::PixelBuffer(usage_hint) => usage_hint, - }; - Self { - usage_hint, - default_size, - available_buffers: Vec::new(), - returned_buffers: Vec::new(), - waiting_buffers: Vec::new(), - orphaned_buffers: Vec::new(), - } - } - - /// To be called at the beginning of a series of uploads. - /// Moves any buffers which are now ready to be used from the waiting list to the ready list. - pub fn begin_frame(&mut self, device: &mut Device) { - // Iterate through the waiting buffers and check if each fence has been signalled. - // If a fence is signalled, move its corresponding buffers to the available list. - // On error, delete the buffers. Stop when we find the first non-signalled fence, - // and clean up the signalled fences. - let mut first_not_signalled = self.waiting_buffers.len(); - for (i, (sync, buffers)) in self.waiting_buffers.iter_mut().enumerate() { - match device.gl.client_wait_sync(*sync, 0, 0) { - gl::TIMEOUT_EXPIRED => { - first_not_signalled = i; - break; - }, - gl::ALREADY_SIGNALED | gl::CONDITION_SATISFIED => { - self.available_buffers.extend(buffers.drain(..)); - } - gl::WAIT_FAILED | _ => { - warn!("glClientWaitSync error in UploadPBOPool::begin_frame()"); - for buffer in buffers.drain(..) { - device.delete_pbo(buffer.pbo); - } - } - } - } - - // Delete signalled fences, and remove their now-empty Vecs from waiting_buffers. - for (sync, _) in self.waiting_buffers.drain(0..first_not_signalled) { - device.gl.delete_sync(sync); - } - } - - // To be called at the end of a series of uploads. - // Creates a sync object, and adds the buffers returned during this frame to waiting_buffers. - pub fn end_frame(&mut self, device: &mut Device) { - if !self.returned_buffers.is_empty() { - let sync = device.gl.fence_sync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0); - if !sync.is_null() { - self.waiting_buffers.push((sync, mem::replace(&mut self.returned_buffers, Vec::new()))) - } else { - warn!("glFenceSync error in UploadPBOPool::end_frame()"); - - for buffer in self.returned_buffers.drain(..) { - device.delete_pbo(buffer.pbo); - } - } - } - } - - /// Obtain a PBO, either by reusing an existing PBO or allocating a new one. - /// min_size specifies the minimum required size of the PBO. The returned PBO - /// may be larger than required. - fn get_pbo(&mut self, device: &mut Device, min_size: usize) -> Result<UploadPBO, ()> { - - // If min_size is smaller than our default size, then use the default size. - // The exception to this is when due to driver bugs we cannot upload from - // offsets other than zero within a PBO. In this case, there is no point in - // allocating buffers larger than required, as they cannot be shared. - let (can_recycle, size) = if min_size <= self.default_size && device.capabilities.supports_nonzero_pbo_offsets { - (true, self.default_size) - } else { - (false, min_size) - }; - - // Try to recycle an already allocated PBO. - if can_recycle { - if let Some(mut buffer) = self.available_buffers.pop() { - assert_eq!(buffer.pbo.reserved_size, size); - assert!(buffer.can_recycle); - - device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, buffer.pbo.id); - - match buffer.mapping { - PBOMapping::Unmapped => { - // If buffer was unmapped then transiently map it. - let ptr = device.gl.map_buffer_range( - gl::PIXEL_UNPACK_BUFFER, - 0, - buffer.pbo.reserved_size as _, - gl::MAP_WRITE_BIT | gl::MAP_UNSYNCHRONIZED_BIT, - ) as *mut _; - - let ptr = ptr::NonNull::new(ptr).ok_or_else(|| { - error!("Failed to transiently map PBO of size {} bytes", buffer.pbo.reserved_size); - })?; - - buffer.mapping = PBOMapping::Transient(ptr); - } - PBOMapping::Transient(_) => { - unreachable!("Transiently mapped UploadPBO must be unmapped before returning to pool."); - } - PBOMapping::Persistent(_) => { - } - } - - return Ok(buffer); - } - } - - // Try to recycle a PBO ID (but not its allocation) from a previously allocated PBO. - // If there are none available, create a new PBO. - let mut pbo = match self.orphaned_buffers.pop() { - Some(pbo) => pbo, - None => device.create_pbo(), - }; - - assert_eq!(pbo.reserved_size, 0); - pbo.reserved_size = size; - - device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, pbo.id); - let mapping = if device.capabilities.supports_buffer_storage && can_recycle { - device.gl.buffer_storage( - gl::PIXEL_UNPACK_BUFFER, - pbo.reserved_size as _, - ptr::null(), - gl::MAP_WRITE_BIT | gl::MAP_PERSISTENT_BIT, - ); - let ptr = device.gl.map_buffer_range( - gl::PIXEL_UNPACK_BUFFER, - 0, - pbo.reserved_size as _, - // GL_MAP_COHERENT_BIT doesn't seem to work on Adreno, so use glFlushMappedBufferRange. - // kvark notes that coherent memory can be faster on some platforms, such as nvidia, - // so in the future we could choose which to use at run time. - gl::MAP_WRITE_BIT | gl::MAP_PERSISTENT_BIT | gl::MAP_FLUSH_EXPLICIT_BIT, - ) as *mut _; - - let ptr = ptr::NonNull::new(ptr).ok_or_else(|| { - error!("Failed to persistently map PBO of size {} bytes", pbo.reserved_size); - })?; - - PBOMapping::Persistent(ptr) - } else { - device.gl.buffer_data_untyped( - gl::PIXEL_UNPACK_BUFFER, - pbo.reserved_size as _, - ptr::null(), - self.usage_hint.to_gl(), - ); - let ptr = device.gl.map_buffer_range( - gl::PIXEL_UNPACK_BUFFER, - 0, - pbo.reserved_size as _, - // Unlike the above code path, where we are re-mapping a buffer that has previously been unmapped, - // this buffer has just been created there is no need for GL_MAP_UNSYNCHRONIZED_BIT. - gl::MAP_WRITE_BIT, - ) as *mut _; - - let ptr = ptr::NonNull::new(ptr).ok_or_else(|| { - error!("Failed to transiently map PBO of size {} bytes", pbo.reserved_size); - })?; - - PBOMapping::Transient(ptr) - }; - - Ok(UploadPBO { pbo, mapping, can_recycle }) - } - - /// Returns a PBO to the pool. If the PBO is recyclable it is placed in the waiting list. - /// Otherwise we orphan the allocation immediately, and will subsequently reuse just the ID. - fn return_pbo(&mut self, device: &mut Device, mut buffer: UploadPBO) { - assert!( - !matches!(buffer.mapping, PBOMapping::Transient(_)), - "Transiently mapped UploadPBO must be unmapped before returning to pool.", - ); - - if buffer.can_recycle { - self.returned_buffers.push(buffer); - } else { - device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, buffer.pbo.id); - device.gl.buffer_data_untyped( - gl::PIXEL_UNPACK_BUFFER, - 0, - ptr::null(), - gl::STREAM_DRAW, - ); - buffer.pbo.reserved_size = 0; - self.orphaned_buffers.push(buffer.pbo); - } - - device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0); - } - - /// Frees all allocated buffers in response to a memory pressure event. - pub fn on_memory_pressure(&mut self, device: &mut Device) { - for buffer in self.available_buffers.drain(..) { - device.delete_pbo(buffer.pbo); - } - for buffer in self.returned_buffers.drain(..) { - device.delete_pbo(buffer.pbo) - } - for (sync, buffers) in self.waiting_buffers.drain(..) { - device.gl.delete_sync(sync); - for buffer in buffers { - device.delete_pbo(buffer.pbo) +impl<'a, T> Drop for TextureUploader<'a, T> { + fn drop(&mut self) { + match self.uploader_type { + TextureUploaderType::MutliUseBuffer(ref mut buffer) => { + self.target.device.gl.unmap_buffer(gl::PIXEL_UNPACK_BUFFER); + buffer.flush_chunks(&mut self.target); + self.target.device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0); } - } - // There is no need to delete orphaned PBOs on memory pressure. - } - - /// Generates a memory report. - pub fn report_memory(&self) -> MemoryReport { - let mut report = MemoryReport::default(); - for buffer in &self.available_buffers { - report.texture_upload_pbos += buffer.pbo.reserved_size; - } - for buffer in &self.returned_buffers { - report.texture_upload_pbos += buffer.pbo.reserved_size; - } - for (_, buffers) in &self.waiting_buffers { - for buffer in buffers { - report.texture_upload_pbos += buffer.pbo.reserved_size; + TextureUploaderType::SingleUseBuffers(_) => { + self.target.device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0); } + TextureUploaderType::Immediate => {} } - report - } - - pub fn deinit(&mut self, device: &mut Device) { - for buffer in self.available_buffers.drain(..) { - device.delete_pbo(buffer.pbo); - } - for buffer in self.returned_buffers.drain(..) { - device.delete_pbo(buffer.pbo) - } - for (sync, buffers) in self.waiting_buffers.drain(..) { - device.gl.delete_sync(sync); - for buffer in buffers { - device.delete_pbo(buffer.pbo) - } - } - for pbo in self.orphaned_buffers.drain(..) { - device.delete_pbo(pbo); - } - } -} - -/// Used to perform a series of texture uploads. -/// Create using Device::upload_texture(). Perform a series of uploads using either -/// upload(), or stage() and upload_staged(), then call flush(). -pub struct TextureUploader<'a> { - /// A list of buffers containing uploads that need to be flushed. - buffers: Vec<PixelBuffer<'a>>, - /// Pool used to obtain PBOs to fill with texture data. - pub pbo_pool: &'a mut UploadPBOPool, -} - -impl<'a> Drop for TextureUploader<'a> { - fn drop(&mut self) { - assert!( - thread::panicking() || self.buffers.is_empty(), - "TextureUploader must be flushed before it is dropped." - ); - } -} - -/// A buffer used to manually stage data to be uploaded to a texture. -/// Created by calling TextureUploader::stage(), the data can then be written to via get_mapping(). -#[derive(Debug)] -pub struct UploadStagingBuffer<'a> { - /// The PixelBuffer containing this upload. - buffer: PixelBuffer<'a>, - /// The offset of this upload within the PixelBuffer. - offset: usize, - /// The size of this upload. - size: usize, - /// The stride of the data within the buffer. - stride: usize, -} - -impl<'a> UploadStagingBuffer<'a> { - /// Returns the required stride of the data to be written to the buffer. - pub fn get_stride(&self) -> usize { - self.stride - } - - /// Returns a mapping of the data in the buffer, to be written to. - pub fn get_mapping(&mut self) -> &mut [mem::MaybeUninit<u8>] { - &mut self.buffer.mapping[self.offset..self.offset + self.size] } } -impl<'a> TextureUploader<'a> { - /// Returns an UploadStagingBuffer which can be used to manually stage data to be uploaded. - /// Once the data has been staged, it can be uploaded with upload_staged(). - pub fn stage( +impl<'a, T> TextureUploader<'a, T> { + pub fn upload( &mut self, - device: &mut Device, - format: ImageFormat, - size: DeviceIntSize, - ) -> Result<UploadStagingBuffer<'a>, ()> { - assert!(matches!(device.upload_method, UploadMethod::PixelBuffer(_)), "Texture uploads should only be staged when using pixel buffers."); - - // for optimal PBO texture uploads the offset and stride of the data in - // the buffer may have to be a multiple of a certain value. - let (dst_size, dst_stride) = device.required_upload_size_and_stride( - size, - format, - ); - - // Find a pixel buffer with enough space remaining, creating a new one if required. - let buffer_index = self.buffers.iter().position(|buffer| { - buffer.size_used + dst_size <= buffer.inner.pbo.reserved_size - }); - let buffer = match buffer_index { - Some(i) => self.buffers.swap_remove(i), - None => PixelBuffer::new(self.pbo_pool.get_pbo(device, dst_size)?), - }; - - if !device.capabilities.supports_nonzero_pbo_offsets { - assert_eq!(buffer.size_used, 0, "PBO uploads from non-zero offset are not supported."); - } - assert!(buffer.size_used + dst_size <= buffer.inner.pbo.reserved_size, "PixelBuffer is too small"); - - let offset = buffer.size_used; - - Ok(UploadStagingBuffer { - buffer, - offset, - size: dst_size, - stride: dst_stride, - }) - } - - /// Uploads manually staged texture data to the specified texture. - pub fn upload_staged( - &mut self, - device: &mut Device, - texture: &'a Texture, - rect: DeviceIntRect, - format_override: Option<ImageFormat>, - mut staging_buffer: UploadStagingBuffer<'a>, - ) -> usize { - let size = staging_buffer.size; - - staging_buffer.buffer.chunks.push(UploadChunk { - rect, - stride: Some(staging_buffer.stride as i32), - offset: staging_buffer.offset, - format_override, - texture, - }); - staging_buffer.buffer.size_used += staging_buffer.size; - - // Flush the buffer if it is full, otherwise return it to the uploader for further use. - if staging_buffer.buffer.size_used < staging_buffer.buffer.inner.pbo.reserved_size { - self.buffers.push(staging_buffer.buffer); - } else { - Self::flush_buffer(device, self.pbo_pool, staging_buffer.buffer); - } - - size - } - - /// Uploads texture data to the specified texture. - pub fn upload<T>( - &mut self, - device: &mut Device, - texture: &'a Texture, mut rect: DeviceIntRect, + layer_index: i32, stride: Option<i32>, format_override: Option<ImageFormat>, data: *const T, @@ -4430,7 +3849,7 @@ impl<'a> TextureUploader<'a> { // Textures dimensions may have been clamped by the hardware. Crop the // upload region to match. let cropped = rect.intersection( - &DeviceIntRect::new(DeviceIntPoint::zero(), texture.get_dimensions()) + &DeviceIntRect::new(DeviceIntPoint::zero(), self.target.texture.get_dimensions()) ); if cfg!(debug_assertions) && cropped.map_or(true, |r| r != rect) { warn!("Cropping texture upload {:?} to {:?}", rect, cropped); @@ -4440,7 +3859,7 @@ impl<'a> TextureUploader<'a> { Some(r) => r, }; - let bytes_pp = texture.format.bytes_per_pixel() as usize; + let bytes_pp = self.target.texture.format.bytes_per_pixel() as usize; let width_bytes = rect.size.width as usize * bytes_pp; let src_stride = stride.map_or(width_bytes, |stride| { @@ -4450,32 +3869,40 @@ impl<'a> TextureUploader<'a> { let src_size = (rect.size.height as usize - 1) * src_stride + width_bytes; assert!(src_size <= len * mem::size_of::<T>()); - match device.upload_method { - UploadMethod::Immediate => { - if cfg!(debug_assertions) { - let mut bound_buffer = [0]; - unsafe { - device.gl.get_integer_v(gl::PIXEL_UNPACK_BUFFER_BINDING, &mut bound_buffer); + // for optimal PBO texture uploads the offset and stride of the data in + // the buffer may have to be a multiple of a certain value. + let (dst_size, dst_stride) = self.target.device.required_upload_size_and_stride( + rect.size, + self.target.texture.format, + ); + + // Choose the buffer to use, if any, allocating a new single-use buffer if required. + let mut single_use_buffer = None; + let mut buffer = match self.uploader_type { + TextureUploaderType::MutliUseBuffer(ref mut buffer) => Some(buffer), + TextureUploaderType::SingleUseBuffers(hint) => { + match self.target.device.create_upload_buffer(hint, dst_size) { + Ok(buffer) => { + single_use_buffer = Some(buffer); + single_use_buffer.as_mut() + } + Err(_) => { + // If allocating the buffer failed, fall back to immediate uploads + self.target.device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0); + self.uploader_type = TextureUploaderType::Immediate; + None } - assert_eq!(bound_buffer[0], 0, "GL_PIXEL_UNPACK_BUFFER must not be bound for immediate uploads."); } - - Self::update_impl(device, UploadChunk { - rect, - stride: Some(src_stride as i32), - offset: data as _, - format_override, - texture, - }); - - width_bytes * rect.size.height as usize } - UploadMethod::PixelBuffer(_) => { - let mut staging_buffer = match self.stage(device, texture.format, rect.size) { - Ok(staging_buffer) => staging_buffer, - Err(_) => return 0, - }; - let dst_stride = staging_buffer.get_stride(); + TextureUploaderType::Immediate => None, + }; + + match buffer { + Some(ref mut buffer) => { + if !self.target.device.capabilities.supports_nonzero_pbo_offsets { + assert_eq!(buffer.size_used, 0, "PBO uploads from non-zero offset are not supported."); + } + assert!(buffer.size_used + dst_size <= buffer.size_allocated, "PixelBuffer is too small"); unsafe { let src: &[mem::MaybeUninit<u8>] = slice::from_raw_parts(data as *const _, src_size); @@ -4483,61 +3910,69 @@ impl<'a> TextureUploader<'a> { if src_stride == dst_stride { // the stride is already optimal, so simply copy // the data as-is in to the buffer - staging_buffer.get_mapping()[..src_size].copy_from_slice(src); + let dst_start = buffer.size_used; + let dst_end = dst_start + src_size; + + buffer.mapping[dst_start..dst_end].copy_from_slice(src); } else { // copy the data line-by-line in to the buffer so // that it has an optimal stride for y in 0..rect.size.height as usize { let src_start = y * src_stride; let src_end = src_start + width_bytes; - let dst_start = y * staging_buffer.get_stride(); + let dst_start = buffer.size_used + y * dst_stride; let dst_end = dst_start + width_bytes; - staging_buffer.get_mapping()[dst_start..dst_end].copy_from_slice(&src[src_start..src_end]) + buffer.mapping[dst_start..dst_end].copy_from_slice(&src[src_start..src_end]) } } } - self.upload_staged(device, texture, rect, format_override, staging_buffer) + buffer.chunks.push(UploadChunk { + rect, + layer_index, + stride: Some(dst_stride as i32), + offset: buffer.size_used, + format_override, + }); + buffer.size_used += dst_size; } - } - } + None => { + if cfg!(debug_assertions) { + let mut bound_buffer = [0]; + unsafe { + self.target.device.gl.get_integer_v(gl::PIXEL_UNPACK_BUFFER_BINDING, &mut bound_buffer); + } + assert_eq!(bound_buffer[0], 0, "GL_PIXEL_UNPACK_BUFFER must not be bound for immediate uploads."); + } - fn flush_buffer(device: &mut Device, pbo_pool: &mut UploadPBOPool, mut buffer: PixelBuffer) { - device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, buffer.inner.pbo.id); - match buffer.inner.mapping { - PBOMapping::Unmapped => unreachable!("UploadPBO should be mapped at this stage."), - PBOMapping::Transient(_) => { - device.gl.unmap_buffer(gl::PIXEL_UNPACK_BUFFER); - buffer.inner.mapping = PBOMapping::Unmapped; - } - PBOMapping::Persistent(_) => { - device.gl.flush_mapped_buffer_range(gl::PIXEL_UNPACK_BUFFER, 0, buffer.size_used as _); + self.target.update_impl(UploadChunk { + rect, + layer_index, + stride, + offset: data as _, + format_override, + }); } } - buffer.flush_chunks(device); - let pbo = mem::replace(&mut buffer.inner, UploadPBO::empty()); - pbo_pool.return_pbo(device, pbo); - } - /// Flushes all pending texture uploads. Must be called after all - /// required upload() or upload_staged() calls have been made. - pub fn flush(mut self, device: &mut Device) { - for buffer in self.buffers.drain(..) { - Self::flush_buffer(device, self.pbo_pool, buffer); + // Flush the buffer if it is for single-use. + if let Some(ref mut buffer) = single_use_buffer { + self.target.device.gl.unmap_buffer(gl::PIXEL_UNPACK_BUFFER); + buffer.flush_chunks(&mut self.target); } - device.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0); + dst_size } +} - fn update_impl(device: &mut Device, chunk: UploadChunk) { - device.bind_texture(DEFAULT_TEXTURE, chunk.texture, Swizzle::default()); - - let format = chunk.format_override.unwrap_or(chunk.texture.format); +impl<'a> UploadTarget<'a> { + fn update_impl(&mut self, chunk: UploadChunk) { + let format = chunk.format_override.unwrap_or(self.texture.format); let (gl_format, bpp, data_type) = match format { ImageFormat::R8 => (gl::RED, 1, gl::UNSIGNED_BYTE), ImageFormat::R16 => (gl::RED, 2, gl::UNSIGNED_SHORT), - ImageFormat::BGRA8 => (device.bgra_formats.external, 4, device.bgra_pixel_type), + ImageFormat::BGRA8 => (self.device.bgra_formats.external, 4, gl::UNSIGNED_BYTE), ImageFormat::RGBA8 => (gl::RGBA, 4, gl::UNSIGNED_BYTE), ImageFormat::RG8 => (gl::RG, 2, gl::UNSIGNED_BYTE), ImageFormat::RG16 => (gl::RG, 4, gl::UNSIGNED_SHORT), @@ -4547,11 +3982,11 @@ impl<'a> TextureUploader<'a> { let row_length = match chunk.stride { Some(value) => value / bpp, - None => chunk.texture.size.width, + None => self.texture.size.width, }; if chunk.stride.is_some() { - device.gl.pixel_store_i( + self.device.gl.pixel_store_i( gl::UNPACK_ROW_LENGTH, row_length as _, ); @@ -4560,10 +3995,25 @@ impl<'a> TextureUploader<'a> { let pos = chunk.rect.origin; let size = chunk.rect.size; - match chunk.texture.target { + match self.texture.target { + gl::TEXTURE_2D_ARRAY => { + self.device.gl.tex_sub_image_3d_pbo( + self.texture.target, + 0, + pos.x as _, + pos.y as _, + chunk.layer_index, + size.width as _, + size.height as _, + 1, + gl_format, + data_type, + chunk.offset, + ); + } gl::TEXTURE_2D | gl::TEXTURE_RECTANGLE | gl::TEXTURE_EXTERNAL_OES => { - device.gl.tex_sub_image_2d_pbo( - chunk.texture.target, + self.device.gl.tex_sub_image_2d_pbo( + self.texture.target, 0, pos.x as _, pos.y as _, @@ -4578,13 +4028,13 @@ impl<'a> TextureUploader<'a> { } // If using tri-linear filtering, build the mip-map chain for this texture. - if chunk.texture.filter == TextureFilter::Trilinear { - device.gl.generate_mipmap(chunk.texture.target); + if self.texture.filter == TextureFilter::Trilinear { + self.device.gl.generate_mipmap(self.texture.target); } // Reset row length to 0, otherwise the stride would apply to all texture uploads. if chunk.stride.is_some() { - device.gl.pixel_store_i(gl::UNPACK_ROW_LENGTH, 0 as _); + self.device.gl.pixel_store_i(gl::UNPACK_ROW_LENGTH, 0 as _); } } } diff --git a/third_party/webrender/webrender/src/device/query_gl.rs b/third_party/webrender/webrender/src/device/query_gl.rs index c7fd9a9070d..95d515dc5ec 100644 --- a/third_party/webrender/webrender/src/device/query_gl.rs +++ b/third_party/webrender/webrender/src/device/query_gl.rs @@ -7,7 +7,6 @@ use std::mem; use std::rc::Rc; use crate::device::GpuFrameId; -use crate::profiler::GpuProfileTag; #[derive(Copy, Clone, Debug)] pub enum GpuDebugMethod { @@ -16,15 +15,19 @@ pub enum GpuDebugMethod { KHR, } +pub trait NamedTag { + fn get_label(&self) -> &str; +} + #[derive(Debug, Clone)] -pub struct GpuTimer { - pub tag: GpuProfileTag, +pub struct GpuTimer<T> { + pub tag: T, pub time_ns: u64, } #[derive(Debug, Clone)] -pub struct GpuSampler { - pub tag: GpuProfileTag, +pub struct GpuSampler<T> { + pub tag: T, pub count: u64, } @@ -66,16 +69,16 @@ impl<T> QuerySet<T> { } } -pub struct GpuFrameProfile { +pub struct GpuFrameProfile<T> { gl: Rc<dyn gl::Gl>, - timers: QuerySet<GpuTimer>, - samplers: QuerySet<GpuSampler>, + timers: QuerySet<GpuTimer<T>>, + samplers: QuerySet<GpuSampler<T>>, frame_id: GpuFrameId, inside_frame: bool, debug_method: GpuDebugMethod, } -impl GpuFrameProfile { +impl<T> GpuFrameProfile<T> { fn new(gl: Rc<dyn gl::Gl>, debug_method: GpuDebugMethod) -> Self { GpuFrameProfile { gl, @@ -137,11 +140,13 @@ impl GpuFrameProfile { self.samplers.pending = 0; } } +} - fn start_timer(&mut self, tag: GpuProfileTag) -> GpuTimeQuery { +impl<T: NamedTag> GpuFrameProfile<T> { + fn start_timer(&mut self, tag: T) -> GpuTimeQuery { self.finish_timer(); - let marker = GpuMarker::new(&self.gl, tag.label, self.debug_method); + let marker = GpuMarker::new(&self.gl, tag.get_label(), self.debug_method); if let Some(query) = self.timers.add(GpuTimer { tag, time_ns: 0 }) { self.gl.begin_query(gl::TIME_ELAPSED, query); @@ -150,7 +155,7 @@ impl GpuFrameProfile { GpuTimeQuery(marker) } - fn start_sampler(&mut self, tag: GpuProfileTag) -> GpuSampleQuery { + fn start_sampler(&mut self, tag: T) -> GpuSampleQuery { self.finish_sampler(); if let Some(query) = self.samplers.add(GpuSampler { tag, count: 0 }) { @@ -160,7 +165,7 @@ impl GpuFrameProfile { GpuSampleQuery } - fn build_samples(&mut self) -> (GpuFrameId, Vec<GpuTimer>, Vec<GpuSampler>) { + fn build_samples(&mut self) -> (GpuFrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) { debug_assert!(!self.inside_frame); let gl = &self.gl; @@ -176,27 +181,27 @@ impl GpuFrameProfile { } } -impl Drop for GpuFrameProfile { +impl<T> Drop for GpuFrameProfile<T> { fn drop(&mut self) { self.disable_timers(); self.disable_samplers(); } } -const NUM_PROFILE_FRAMES: usize = 4; - -pub struct GpuProfiler { +pub struct GpuProfiler<T> { gl: Rc<dyn gl::Gl>, - frames: [GpuFrameProfile; NUM_PROFILE_FRAMES], + frames: Vec<GpuFrameProfile<T>>, next_frame: usize, debug_method: GpuDebugMethod } -impl GpuProfiler { +impl<T> GpuProfiler<T> { pub fn new(gl: Rc<dyn gl::Gl>, debug_method: GpuDebugMethod) -> Self { - let f = || GpuFrameProfile::new(Rc::clone(&gl), debug_method); + const MAX_PROFILE_FRAMES: usize = 4; + let frames = (0 .. MAX_PROFILE_FRAMES) + .map(|_| GpuFrameProfile::new(Rc::clone(&gl), debug_method)) + .collect(); - let frames = [f(), f(), f(), f()]; GpuProfiler { gl, next_frame: 0, @@ -235,8 +240,10 @@ impl GpuProfiler { frame.disable_samplers(); } } +} - pub fn build_samples(&mut self) -> (GpuFrameId, Vec<GpuTimer>, Vec<GpuSampler>) { +impl<T: NamedTag> GpuProfiler<T> { + pub fn build_samples(&mut self) -> (GpuFrameId, Vec<GpuTimer<T>>, Vec<GpuSampler<T>>) { self.frames[self.next_frame].build_samples() } @@ -249,11 +256,11 @@ impl GpuProfiler { self.next_frame = (self.next_frame + 1) % self.frames.len(); } - pub fn start_timer(&mut self, tag: GpuProfileTag) -> GpuTimeQuery { + pub fn start_timer(&mut self, tag: T) -> GpuTimeQuery { self.frames[self.next_frame].start_timer(tag) } - pub fn start_sampler(&mut self, tag: GpuProfileTag) -> GpuSampleQuery { + pub fn start_sampler(&mut self, tag: T) -> GpuSampleQuery { self.frames[self.next_frame].start_sampler(tag) } diff --git a/third_party/webrender/webrender/src/filterdata.rs b/third_party/webrender/webrender/src/filterdata.rs index d399b2252e2..3bbfcebea5e 100644 --- a/third_party/webrender/webrender/src/filterdata.rs +++ b/third_party/webrender/webrender/src/filterdata.rs @@ -7,7 +7,7 @@ use crate::gpu_cache::{GpuCacheHandle}; use crate::frame_builder::FrameBuildingState; use crate::gpu_cache::GpuDataRequest; use crate::intern; -use api::{ComponentTransferFuncType}; +use api::{FilterDataIntern, ComponentTransferFuncType}; pub type FilterDataHandle = intern::Handle<FilterDataIntern>; @@ -154,15 +154,10 @@ impl SFilterDataTemplate { } } -#[derive(Copy, Clone, Debug, MallocSizeOf)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] -pub enum FilterDataIntern {} - impl intern::Internable for FilterDataIntern { type Key = SFilterDataKey; type StoreData = SFilterDataTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_FILTER_DATA; } fn push_component_transfer_data( diff --git a/third_party/webrender/webrender/src/frame_builder.rs b/third_party/webrender/webrender/src/frame_builder.rs index 1d3cb27ca87..afb56778c95 100644 --- a/third_party/webrender/webrender/src/frame_builder.rs +++ b/third_party/webrender/webrender/src/frame_builder.rs @@ -2,37 +2,34 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ColorF, DebugFlags, FontRenderMode, PremultipliedColorF}; +use api::{ColorF, DebugFlags, DocumentLayer, FontRenderMode, PremultipliedColorF}; use api::units::*; use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer}; -use crate::clip::{ClipStore, ClipChainStack}; +use crate::clip::{ClipStore, ClipChainStack, ClipInstance}; use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex}; -use crate::composite::{CompositorKind, CompositeState, CompositeStatePreallocator}; -use crate::debug_item::DebugItem; +use crate::composite::{CompositorKind, CompositeState}; +use crate::debug_render::DebugItem; use crate::gpu_cache::{GpuCache, GpuCacheHandle}; -use crate::gpu_types::{PrimitiveHeaders, TransformPalette, ZBufferIdGenerator}; +use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator}; use crate::gpu_types::TransformData; -use crate::internal_types::{FastHashMap, PlaneSplitter}; -use crate::picture::{DirtyRegion, PictureUpdateState, SliceId, TileCacheInstance}; -use crate::picture::{SurfaceInfo, SurfaceIndex, ROOT_SURFACE_INDEX, SurfaceRenderTasks, SubSliceIndex}; -use crate::picture::{BackdropKind, SubpixelMode, TileCacheLogger, RasterConfig, PictureCompositeMode}; -use crate::prepare::prepare_primitives; -use crate::prim_store::{PictureIndex, PrimitiveDebugId}; -use crate::prim_store::{DeferredResolve}; -use crate::profiler::{self, TransactionProfile}; -use crate::render_backend::{DataStores, FrameStamp, FrameId, ScratchBuffer}; +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::{BackdropKind, TileCacheLogger}; +use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer}; +use crate::prim_store::{DeferredResolve, PrimitiveVisibilityMask}; +use crate::profiler::{FrameProfileCounters, TextureCacheProfileCounters, ResourceProfileCounters}; +use crate::render_backend::{DataStores, FrameStamp, FrameId}; use crate::render_target::{RenderTarget, PictureCacheTarget, TextureCacheRenderTarget}; -use crate::render_target::{RenderTargetContext, RenderTargetKind, AlphaRenderTarget, ColorRenderTarget}; -use crate::render_task_graph::{RenderTaskId, RenderTaskGraph, Pass, SubPassSurface}; -use crate::render_task_graph::{RenderPass, RenderTaskGraphBuilder}; -use crate::render_task::{RenderTaskLocation, RenderTaskKind, StaticRenderTaskSurface}; +use crate::render_target::{RenderTargetContext, RenderTargetKind}; +use crate::render_task_graph::{RenderTaskId, RenderTaskGraph, RenderTaskGraphCounters}; +use crate::render_task_graph::{RenderPassKind, RenderPass}; +use crate::render_task::{RenderTask, RenderTaskLocation, RenderTaskKind}; use crate::resource_cache::{ResourceCache}; use crate::scene::{BuiltScene, SceneProperties}; -use crate::space::SpaceMapper; use crate::segment::SegmentBuilder; use std::{f32, mem}; -use crate::util::{VecHelper, Recycler, Preallocator}; -use crate::visibility::{update_primitive_visibility, FrameVisibilityState, FrameVisibilityContext}; +use crate::util::MaxRect; #[derive(Clone, Copy, Debug, PartialEq)] @@ -58,23 +55,19 @@ pub struct FrameBuilderConfig { pub dual_source_blending_is_supported: bool, pub dual_source_blending_is_enabled: bool, pub chase_primitive: ChasePrimitive, + /// The immutable global picture caching enable from `RendererOptions` + pub global_enable_picture_caching: bool, /// True if we're running tests (i.e. via wrench). pub testing: bool, pub gpu_supports_fast_clears: bool, pub gpu_supports_advanced_blend: bool, pub advanced_blend_is_coherent: bool, - pub gpu_supports_render_target_partial_update: bool, - /// Whether ImageBufferKind::TextureExternal images must first be copied - /// to a regular texture before rendering. - pub external_images_require_copy: bool, pub batch_lookback_count: usize, pub background_color: Option<ColorF>, pub compositor_kind: CompositorKind, pub tile_size_override: Option<DeviceIntSize>, pub max_depth_ids: i32, pub max_target_size: i32, - pub force_invalidation: bool, - pub is_software: bool, } /// A set of common / global resources that are retained between @@ -121,51 +114,56 @@ impl FrameGlobalResources { } } -pub struct FrameScratchBuffer { - surfaces: Vec<SurfaceInfo>, - dirty_region_stack: Vec<DirtyRegion>, - surface_stack: Vec<SurfaceIndex>, - clip_chain_stack: ClipChainStack, +/// Produces the frames that are sent to the renderer. +#[cfg_attr(feature = "capture", derive(Serialize))] +pub struct FrameBuilder { + /// Cache of surface tiles from the previous frame builder + /// that can optionally be consumed by this frame builder. + pending_retained_tiles: RetainedTiles, + pub globals: FrameGlobalResources, } -impl Default for FrameScratchBuffer { - fn default() -> Self { - FrameScratchBuffer { - surfaces: Vec::new(), - dirty_region_stack: Vec::new(), - surface_stack: Vec::new(), - clip_chain_stack: ClipChainStack::new(), - } - } +pub struct FrameVisibilityContext<'a> { + pub spatial_tree: &'a SpatialTree, + pub global_screen_world_rect: WorldRect, + pub global_device_pixel_scale: DevicePixelScale, + pub surfaces: &'a [SurfaceInfo], + pub debug_flags: DebugFlags, + pub scene_properties: &'a SceneProperties, + pub config: FrameBuilderConfig, } -impl FrameScratchBuffer { - pub fn begin_frame(&mut self) { - self.surfaces.clear(); - self.dirty_region_stack.clear(); - self.surface_stack.clear(); - self.clip_chain_stack.clear(); - } +pub struct FrameVisibilityState<'a> { + pub clip_store: &'a mut ClipStore, + pub resource_cache: &'a mut ResourceCache, + pub gpu_cache: &'a mut GpuCache, + pub scratch: &'a mut PrimitiveScratchBuffer, + pub tile_cache: Option<Box<TileCacheInstance>>, + pub retained_tiles: &'a mut RetainedTiles, + pub data_stores: &'a mut DataStores, + pub clip_chain_stack: ClipChainStack, + pub render_tasks: &'a mut RenderTaskGraph, + pub composite_state: &'a mut CompositeState, + /// A stack of currently active off-screen surfaces during the + /// visibility frame traversal. + pub surface_stack: Vec<SurfaceIndex>, +} - pub fn recycle(&mut self, recycler: &mut Recycler) { - recycler.recycle_vec(&mut self.surfaces); - // Don't call recycle on the stacks because the reycler's - // role is to get rid of allocations when the capacity - // is much larger than the lengths. with stacks the - // length varies through the frame but is supposedly - // back to zero by the end so we would always throw the - // allocation away. +impl<'a> FrameVisibilityState<'a> { + pub fn push_surface( + &mut self, + surface_index: SurfaceIndex, + shared_clips: &[ClipInstance], + spatial_tree: &SpatialTree, + ) { + self.surface_stack.push(surface_index); + self.clip_chain_stack.push_surface(shared_clips, spatial_tree); } -} -/// Produces the frames that are sent to the renderer. -#[cfg_attr(feature = "capture", derive(Serialize))] -pub struct FrameBuilder { - pub globals: FrameGlobalResources, - #[cfg_attr(feature = "capture", serde(skip))] - prim_headers_prealloc: Preallocator, - #[cfg_attr(feature = "capture", serde(skip))] - composite_state_prealloc: CompositeStatePreallocator, + pub fn pop_surface(&mut self) { + self.surface_stack.pop().unwrap(); + self.clip_chain_stack.pop_surface(); + } } pub struct FrameBuildingContext<'a> { @@ -179,7 +177,8 @@ pub struct FrameBuildingContext<'a> { } pub struct FrameBuildingState<'a> { - pub rg_builder: &'a mut RenderTaskGraphBuilder, + pub render_tasks: &'a mut RenderTaskGraph, + pub profile_counters: &'a mut FrameProfileCounters, pub clip_store: &'a mut ClipStore, pub resource_cache: &'a mut ResourceCache, pub gpu_cache: &'a mut GpuCache, @@ -188,7 +187,6 @@ pub struct FrameBuildingState<'a> { pub surfaces: &'a mut Vec<SurfaceInfo>, pub dirty_region_stack: Vec<DirtyRegion>, pub composite_state: &'a mut CompositeState, - pub num_visible_primitives: u32, } impl<'a> FrameBuildingState<'a> { @@ -206,76 +204,6 @@ impl<'a> FrameBuildingState<'a> { pub fn pop_dirty_region(&mut self) { self.dirty_region_stack.pop().unwrap(); } - - /// Initialize render tasks for a surface that is tiled (currently applies - /// only to picture cache surfaces). - pub fn init_surface_tiled( - &mut self, - surface_index: SurfaceIndex, - tasks: Vec<RenderTaskId>, - device_rect: DeviceRect, - ) { - let surface = &mut self.surfaces[surface_index.0]; - assert!(surface.render_tasks.is_none()); - surface.render_tasks = Some(SurfaceRenderTasks::Tiled(tasks)); - surface.device_rect = Some(device_rect); - } - - /// Initialize render tasks for a simple surface, that contains only a - /// single render task. - pub fn init_surface( - &mut self, - surface_index: SurfaceIndex, - task_id: RenderTaskId, - parent_surface_index: SurfaceIndex, - device_rect: DeviceRect, - ) { - let surface = &mut self.surfaces[surface_index.0]; - assert!(surface.render_tasks.is_none()); - surface.render_tasks = Some(SurfaceRenderTasks::Simple(task_id)); - surface.device_rect = Some(device_rect); - - self.add_child_render_task( - parent_surface_index, - task_id, - ); - } - - /// Initialize render tasks for a surface that is made up of a chain of - /// render tasks, where the final output render task is different than the - /// input render task (for example, a blur pass on a picture). - pub fn init_surface_chain( - &mut self, - surface_index: SurfaceIndex, - root_task_id: RenderTaskId, - port_task_id: RenderTaskId, - parent_surface_index: SurfaceIndex, - device_rect: DeviceRect, - ) { - let surface = &mut self.surfaces[surface_index.0]; - assert!(surface.render_tasks.is_none()); - surface.render_tasks = Some(SurfaceRenderTasks::Chained { root_task_id, port_task_id }); - surface.device_rect = Some(device_rect); - - self.add_child_render_task( - parent_surface_index, - root_task_id, - ); - } - - /// Add a render task as a dependency of a given surface. - pub fn add_child_render_task( - &mut self, - surface_index: SurfaceIndex, - child_task_id: RenderTaskId, - ) { - add_child_render_task( - surface_index, - child_task_id, - self.surfaces, - self.rg_builder, - ); - } } /// Immutable context of a picture when processing children. @@ -283,6 +211,7 @@ impl<'a> FrameBuildingState<'a> { pub struct PictureContext { pub pic_index: PictureIndex, pub apply_local_clip_rect: bool, + pub is_passthrough: bool, pub surface_spatial_node_index: SpatialNodeIndex, pub raster_spatial_node_index: SpatialNodeIndex, /// The surface that this picture will render on. @@ -306,12 +235,26 @@ pub struct PictureState { impl FrameBuilder { pub fn new() -> Self { FrameBuilder { + pending_retained_tiles: RetainedTiles::new(), globals: FrameGlobalResources::empty(), - prim_headers_prealloc: Preallocator::new(0), - composite_state_prealloc: CompositeStatePreallocator::default(), } } + /// Provide any cached surface tiles from the previous frame builder + /// to a new frame builder. These will be consumed or dropped the + /// first time a new frame builder creates a frame. + pub fn set_retained_resources(&mut self, retained_tiles: RetainedTiles) { + // In general, the pending retained tiles are consumed by the frame + // builder the first time a frame is built after a new scene has + // arrived. However, if two scenes arrive in quick succession, the + // frame builder may not have had a chance to build a frame and + // consume the pending tiles. In this case, the pending tiles will + // be lost, causing a full invalidation of the entire screen. To + // avoid this, if there are still pending tiles, include them in + // the retained tiles passed to the next frame builder. + self.pending_retained_tiles.merge(retained_tiles); + } + /// Compute the contribution (bounding rectangles, and resources) of layers and their /// primitives in screen space. fn build_layer_screen_rects_and_cull_layers( @@ -320,20 +263,25 @@ impl FrameBuilder { global_screen_world_rect: WorldRect, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, - rg_builder: &mut RenderTaskGraphBuilder, + render_tasks: &mut RenderTaskGraph, + profile_counters: &mut FrameProfileCounters, global_device_pixel_scale: DevicePixelScale, scene_properties: &SceneProperties, transform_palette: &mut TransformPalette, data_stores: &mut DataStores, - scratch: &mut ScratchBuffer, + surfaces: &mut Vec<SurfaceInfo>, + scratch: &mut PrimitiveScratchBuffer, debug_flags: DebugFlags, + texture_cache_profile: &mut TextureCacheProfileCounters, composite_state: &mut CompositeState, tile_cache_logger: &mut TileCacheLogger, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, - profile: &mut TransactionProfile, - ) { + ) -> Option<RenderTaskId> { profile_scope!("build_layer_screen_rects_and_cull_layers"); + if scene.prim_store.pictures.is_empty() { + return None + } + scratch.begin_frame(); let root_spatial_node_index = scene.spatial_tree.root_reference_frame_index(); @@ -353,6 +301,21 @@ impl FrameBuilder { fb_config: &scene.config, }; + let root_render_task_id = render_tasks.add().init( + RenderTask::new_picture( + RenderTaskLocation::Fixed(scene.output_rect), + scene.output_rect.size.to_f32(), + scene.root_pic_index, + DeviceIntPoint::zero(), + UvRectKind::Rect, + ROOT_SPATIAL_NODE_INDEX, + global_device_pixel_scale, + PrimitiveVisibilityMask::all(), + None, + None, + ) + ); + // Construct a dummy root surface, that represents the // main framebuffer surface. let root_surface = SurfaceInfo::new( @@ -364,9 +327,13 @@ impl FrameBuilder { global_device_pixel_scale, (1.0, 1.0), ); - let mut surfaces = scratch.frame.surfaces.take(); surfaces.push(root_surface); + let mut retained_tiles = mem::replace( + &mut self.pending_retained_tiles, + RetainedTiles::new(), + ); + // The first major pass of building a frame is to walk the picture // tree. This pass must be quick (it should never touch individual // primitives). For now, all we do here is determine which pictures @@ -374,151 +341,169 @@ impl FrameBuilder { // set up render tasks, determine scaling of surfaces, and detect // which surfaces have valid cached surfaces that don't need to // be rendered this frame. - for pic_index in &scene.tile_cache_pictures { - PictureUpdateState::update_all( - &mut scratch.picture, - &mut surfaces, - *pic_index, - &mut scene.prim_store.pictures, - &frame_context, - gpu_cache, - &scene.clip_store, - data_stores, - ); - } + PictureUpdateState::update_all( + surfaces, + scene.root_pic_index, + &mut scene.prim_store.pictures, + &frame_context, + gpu_cache, + &scene.clip_store, + data_stores, + composite_state, + ); { profile_scope!("UpdateVisibility"); profile_marker!("UpdateVisibility"); - profile.start_time(profiler::FRAME_VISIBILITY_TIME); let visibility_context = FrameVisibilityContext { global_device_pixel_scale, spatial_tree: &scene.spatial_tree, global_screen_world_rect, - surfaces: &mut surfaces, + surfaces, debug_flags, scene_properties, config: scene.config, }; let mut visibility_state = FrameVisibilityState { - clip_chain_stack: scratch.frame.clip_chain_stack.take(), - surface_stack: scratch.frame.surface_stack.take(), resource_cache, gpu_cache, clip_store: &mut scene.clip_store, scratch, tile_cache: None, + retained_tiles: &mut retained_tiles, data_stores, + clip_chain_stack: ClipChainStack::new(), + render_tasks, composite_state, + /// Try to avoid allocating during frame traversal - it's unlikely to have a + /// surface stack depth of > 16 in most cases. + surface_stack: Vec::with_capacity(16), }; - for pic_index in scene.tile_cache_pictures.iter().rev() { - update_primitive_visibility( - &mut scene.prim_store, - *pic_index, - ROOT_SURFACE_INDEX, - &global_screen_world_rect, - &visibility_context, - &mut visibility_state, - tile_caches, - true, - ); + scene.prim_store.update_visibility( + scene.root_pic_index, + ROOT_SURFACE_INDEX, + &global_screen_world_rect, + &visibility_context, + &mut visibility_state, + ); + + // When there are tiles that are left remaining in the `retained_tiles`, + // dirty rects are not valid. + if !visibility_state.retained_tiles.caches.is_empty() { + visibility_state.composite_state.dirty_rects_are_valid = false; } - visibility_state.scratch.frame.clip_chain_stack = visibility_state.clip_chain_stack.take(); - visibility_state.scratch.frame.surface_stack = visibility_state.surface_stack.take(); + // When a new display list is processed by WR, the existing tiles from + // any picture cache are stored in the `retained_tiles` field above. This + // allows the first frame of a new display list to reuse any existing tiles + // and surfaces that match. Once the `update_visibility` call above is + // complete, any tiles that are left remaining in the `retained_tiles` + // map are not needed and will be dropped. For simple compositing mode, + // this is fine, since texture cache handles are garbage collected at + // the end of each frame. However, if we're in native compositor mode, + // we need to manually clean up any native compositor surfaces that were + // allocated by these tiles. + for (_, mut cache_state) in visibility_state.retained_tiles.caches.drain() { + if let Some(native_surface) = cache_state.native_surface.take() { + visibility_state.resource_cache.destroy_compositor_surface(native_surface.opaque); + visibility_state.resource_cache.destroy_compositor_surface(native_surface.alpha); + } - profile.end_time(profiler::FRAME_VISIBILITY_TIME); + for (_, external_surface) in cache_state.external_native_surface_cache.drain() { + visibility_state.resource_cache.destroy_compositor_surface(external_surface.native_surface_id) + } + } } - profile.start_time(profiler::FRAME_PREPARE_TIME); - let mut frame_state = FrameBuildingState { - rg_builder, + render_tasks, + profile_counters, clip_store: &mut scene.clip_store, resource_cache, gpu_cache, transforms: transform_palette, segment_builder: SegmentBuilder::new(), - surfaces: &mut surfaces, - dirty_region_stack: scratch.frame.dirty_region_stack.take(), + surfaces, + dirty_region_stack: Vec::new(), composite_state, - num_visible_primitives: 0, }; + frame_state + .surfaces + .first_mut() + .unwrap() + .render_tasks = Some(SurfaceRenderTasks { + root: root_render_task_id, + port: root_render_task_id, + }); + // Push a default dirty region which culls primitives // against the screen world rect, in absence of any // other dirty regions. - let mut default_dirty_region = DirtyRegion::new( - ROOT_SPATIAL_NODE_INDEX, - ); - default_dirty_region.add_dirty_region( - frame_context.global_screen_world_rect.cast_unit(), - SubSliceIndex::DEFAULT, - frame_context.spatial_tree, + let mut default_dirty_region = DirtyRegion::new(); + default_dirty_region.push( + frame_context.global_screen_world_rect, + PrimitiveVisibilityMask::all(), ); frame_state.push_dirty_region(default_dirty_region); - for pic_index in &scene.tile_cache_pictures { - if let Some((pic_context, mut pic_state, mut prim_list)) = scene - .prim_store - .pictures[pic_index.0] - .take_context( - *pic_index, - root_spatial_node_index, - root_spatial_node_index, - ROOT_SURFACE_INDEX, - SubpixelMode::Allow, - &mut frame_state, - &frame_context, - &mut scratch.primitive, - tile_cache_logger, - tile_caches, - ) - { - profile_marker!("PreparePrims"); - - prepare_primitives( - &mut scene.prim_store, - &mut prim_list, - &pic_context, - &mut pic_state, - &frame_context, - &mut frame_state, - data_stores, - &mut scratch.primitive, - tile_cache_logger, - tile_caches, - ); + let (pic_context, mut pic_state, mut prim_list) = scene + .prim_store + .pictures[scene.root_pic_index.0] + .take_context( + scene.root_pic_index, + WorldRect::max_rect(), + root_spatial_node_index, + root_spatial_node_index, + ROOT_SURFACE_INDEX, + &SubpixelMode::Allow, + &mut frame_state, + &frame_context, + scratch, + tile_cache_logger + ) + .unwrap(); - let pic = &mut scene.prim_store.pictures[pic_index.0]; - pic.restore_context( - prim_list, - pic_context, - pic_state, - &mut frame_state, - ); - } + tile_cache_logger.advance(); + + { + profile_marker!("PreparePrims"); + + scene.prim_store.prepare_primitives( + &mut prim_list, + &pic_context, + &mut pic_state, + &frame_context, + &mut frame_state, + data_stores, + scratch, + tile_cache_logger, + ); } - tile_cache_logger.advance(); - frame_state.pop_dirty_region(); - profile.end_time(profiler::FRAME_PREPARE_TIME); - profile.set(profiler::VISIBLE_PRIMITIVES, frame_state.num_visible_primitives); + let pic = &mut scene.prim_store.pictures[scene.root_pic_index.0]; + pic.restore_context( + ROOT_SURFACE_INDEX, + prim_list, + pic_context, + pic_state, + &mut frame_state, + ); - scratch.frame.dirty_region_stack = frame_state.dirty_region_stack.take(); - scratch.frame.surfaces = surfaces.take(); + frame_state.pop_dirty_region(); { profile_marker!("BlockOnResources"); - resource_cache.block_until_all_resources_added( - gpu_cache, - profile, - ); + resource_cache.block_until_all_resources_added(gpu_cache, + render_tasks, + texture_cache_profile); } + + Some(root_render_task_id) } pub fn build( @@ -526,26 +511,28 @@ impl FrameBuilder { scene: &mut BuiltScene, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, - rg_builder: &mut RenderTaskGraphBuilder, stamp: FrameStamp, global_device_pixel_scale: DevicePixelScale, + layer: DocumentLayer, device_origin: DeviceIntPoint, pan: WorldPoint, + resource_profile: &mut ResourceProfileCounters, scene_properties: &SceneProperties, data_stores: &mut DataStores, - scratch: &mut ScratchBuffer, + scratch: &mut PrimitiveScratchBuffer, + render_task_counters: &mut RenderTaskGraphCounters, debug_flags: DebugFlags, tile_cache_logger: &mut TileCacheLogger, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, - dirty_rects_are_valid: bool, - profile: &mut TransactionProfile, ) -> Frame { profile_scope!("build"); profile_marker!("BuildFrame"); - profile.set(profiler::PRIMITIVES, scene.prim_store.prim_count()); - profile.set(profiler::PICTURE_CACHE_SLICES, scene.tile_cache_config.picture_cache_slice_count); - resource_cache.begin_frame(stamp, profile); + let mut profile_counters = FrameProfileCounters::new(); + profile_counters + .total_primitives + .set(scene.prim_store.prim_count()); + resource_profile.content_slices.set(scene.content_slice_count); + resource_cache.begin_frame(stamp); gpu_cache.begin_frame(stamp); self.globals.update(gpu_cache); @@ -556,66 +543,78 @@ impl FrameBuilder { scene_properties, ); let mut transform_palette = scene.spatial_tree.build_transform_palette(); - scene.clip_store.begin_frame(&mut scratch.clip_store); + scene.clip_store.clear_old_instances(); - rg_builder.begin_frame(stamp.frame_id()); + let mut render_tasks = RenderTaskGraph::new( + stamp.frame_id(), + render_task_counters, + ); + let mut surfaces = Vec::new(); let output_size = scene.output_rect.size.to_i32(); let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out(); + // Determine if we will draw this frame with picture caching enabled. This depends on: + // (1) If globally enabled when WR was initialized + // (2) If current debug flags allow picture caching + // (3) Whether we are currently pinch zooming + // (4) If any picture cache spatial nodes are not in the root coordinate system + let picture_caching_is_enabled = + scene.config.global_enable_picture_caching && + !debug_flags.contains(DebugFlags::DISABLE_PICTURE_CACHING) && + !scene.picture_cache_spatial_nodes.iter().any(|spatial_node_index| { + let spatial_node = &scene + .spatial_tree + .spatial_nodes[spatial_node_index.0 as usize]; + spatial_node.is_ancestor_or_self_zooming + }); + let mut composite_state = CompositeState::new( scene.config.compositor_kind, + picture_caching_is_enabled, global_device_pixel_scale, scene.config.max_depth_ids, - dirty_rects_are_valid, ); - self.composite_state_prealloc.preallocate(&mut composite_state); - - self.build_layer_screen_rects_and_cull_layers( + let main_render_task_id = self.build_layer_screen_rects_and_cull_layers( scene, screen_world_rect, resource_cache, gpu_cache, - rg_builder, + &mut render_tasks, + &mut profile_counters, global_device_pixel_scale, scene_properties, &mut transform_palette, data_stores, + &mut surfaces, scratch, debug_flags, + &mut resource_profile.texture_cache, &mut composite_state, tile_cache_logger, - tile_caches, - profile, ); - profile.start_time(profiler::FRAME_BATCHING_TIME); - + let mut passes; let mut deferred_resolves = vec![]; - - // Finish creating the frame graph and build it. - let render_tasks = rg_builder.end_frame( - resource_cache, - gpu_cache, - &mut deferred_resolves, - ); - - let mut passes = Vec::new(); let mut has_texture_cache_tasks = false; let mut prim_headers = PrimitiveHeaders::new(); - self.prim_headers_prealloc.preallocate_vec(&mut prim_headers.headers_int); - self.prim_headers_prealloc.preallocate_vec(&mut prim_headers.headers_float); { profile_marker!("Batching"); + passes = render_tasks.generate_passes( + main_render_task_id, + output_size, + scene.config.gpu_supports_fast_clears, + ); + // Used to generated a unique z-buffer value per primitive. - let mut z_generator = ZBufferIdGenerator::new(scene.config.max_depth_ids); + 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; - for pass in render_tasks.passes.iter().rev() { + for pass in &mut passes { let mut ctx = RenderTargetContext { global_device_pixel_scale, prim_store: &scene.prim_store, @@ -626,77 +625,53 @@ impl FrameBuilder { batch_lookback_count: scene.config.batch_lookback_count, spatial_tree: &scene.spatial_tree, data_stores, - surfaces: &scratch.frame.surfaces, - scratch: &mut scratch.primitive, + surfaces: &surfaces, + scratch, screen_world_rect, globals: &self.globals, - tile_caches, }; - let pass = build_render_pass( + build_render_pass( pass, - output_size, &mut ctx, gpu_cache, - &render_tasks, + &mut render_tasks, &mut deferred_resolves, &scene.clip_store, &mut transform_palette, &mut prim_headers, &mut z_generator, &mut composite_state, - scene.config.gpu_supports_fast_clears, ); - has_texture_cache_tasks |= !pass.texture_cache.is_empty(); - has_texture_cache_tasks |= !pass.picture_cache.is_empty(); - - passes.push(pass); + match pass.kind { + RenderPassKind::MainFramebuffer { .. } => {} + RenderPassKind::OffScreen { + ref texture_cache, + ref picture_cache, + .. + } => { + has_texture_cache_tasks |= !texture_cache.is_empty(); + has_texture_cache_tasks |= !picture_cache.is_empty(); + } + } } - - let mut ctx = RenderTargetContext { - global_device_pixel_scale, - prim_store: &scene.prim_store, - resource_cache, - use_dual_source_blending, - use_advanced_blending: scene.config.gpu_supports_advanced_blend, - break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent, - batch_lookback_count: scene.config.batch_lookback_count, - spatial_tree: &scene.spatial_tree, - data_stores, - surfaces: &scratch.frame.surfaces, - scratch: &mut scratch.primitive, - screen_world_rect, - globals: &self.globals, - tile_caches, - }; - - self.build_composite_pass( - scene, - &mut ctx, - gpu_cache, - &mut deferred_resolves, - &mut composite_state, - ); } - profile.end_time(profiler::FRAME_BATCHING_TIME); + let gpu_cache_frame_id = gpu_cache.end_frame(&mut resource_profile.gpu_cache).frame_id(); - let gpu_cache_frame_id = gpu_cache.end_frame(profile).frame_id(); - - resource_cache.end_frame(profile); - - self.prim_headers_prealloc.record_vec(&mut prim_headers.headers_int); - self.composite_state_prealloc.record(&composite_state); - - composite_state.end_frame(); - scene.clip_store.end_frame(&mut scratch.clip_store); + render_tasks.write_task_data(); + *render_task_counters = render_tasks.counters(); + resource_cache.end_frame(&mut resource_profile.texture_cache); Frame { + content_origin: scene.output_rect.origin, device_rect: DeviceIntRect::new( device_origin, scene.output_rect.size, ), + layer, + profile_counters, passes, transform_palette: transform_palette.finish(), render_tasks, @@ -705,54 +680,11 @@ impl FrameBuilder { has_been_rendered: false, has_texture_cache_tasks, prim_headers, - debug_items: mem::replace(&mut scratch.primitive.debug_items, Vec::new()), + recorded_dirty_regions: mem::replace(&mut scratch.recorded_dirty_regions, Vec::new()), + debug_items: mem::replace(&mut scratch.debug_items, Vec::new()), composite_state, } } - - fn build_composite_pass( - &self, - scene: &BuiltScene, - ctx: &RenderTargetContext, - gpu_cache: &mut GpuCache, - deferred_resolves: &mut Vec<DeferredResolve>, - composite_state: &mut CompositeState, - ) { - for pic_index in &scene.tile_cache_pictures { - let pic = &ctx.prim_store.pictures[pic_index.0]; - - match pic.raster_config { - Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => { - // Tile cache instances are added to the composite config, rather than - // directly added to batches. This allows them to be drawn with various - // present modes during render, such as partial present etc. - let tile_cache = &ctx.tile_caches[&slice_id]; - let map_local_to_world = SpaceMapper::new_with_target( - ROOT_SPATIAL_NODE_INDEX, - tile_cache.spatial_node_index, - ctx.screen_world_rect, - ctx.spatial_tree, - ); - let world_clip_rect = map_local_to_world - .map(&tile_cache.local_clip_rect) - .expect("bug: unable to map clip rect"); - let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round(); - - composite_state.push_surface( - tile_cache, - device_clip_rect, - ctx.global_device_pixel_scale, - ctx.resource_cache, - gpu_cache, - deferred_resolves, - ); - } - _ => { - panic!("bug: found a top-level prim that isn't a tile cache"); - } - } - } - } } /// Processes this pass to prepare it for rendering. @@ -761,269 +693,328 @@ impl FrameBuilder { /// (added via `add_render_task`) in a RenderTarget and assigns it into that /// target. pub fn build_render_pass( - src_pass: &Pass, - screen_size: DeviceIntSize, + pass: &mut RenderPass, ctx: &mut RenderTargetContext, gpu_cache: &mut GpuCache, - render_tasks: &RenderTaskGraph, + render_tasks: &mut RenderTaskGraph, deferred_resolves: &mut Vec<DeferredResolve>, clip_store: &ClipStore, transforms: &mut TransformPalette, prim_headers: &mut PrimitiveHeaders, z_generator: &mut ZBufferIdGenerator, composite_state: &mut CompositeState, - gpu_supports_fast_clears: bool, -) -> RenderPass { +) { profile_scope!("build_render_pass"); - // TODO(gw): In this initial frame graph work, we try to maintain the existing - // build_render_pass code as closely as possible, to make the review - // simpler and reduce chance of regressions. However, future work should - // include refactoring this to more closely match the built frame graph. - - // Collect a list of picture cache tasks, keyed by picture index. - // This allows us to only walk that picture root once, adding the - // primitives to all relevant batches at the same time. - let mut picture_cache_tasks = FastHashMap::default(); - let mut pass = RenderPass::new(src_pass); - - for sub_pass in &src_pass.sub_passes { - match sub_pass.surface { - SubPassSurface::Dynamic { target_kind, texture_id, used_rect } => { - match target_kind { - RenderTargetKind::Color => { - let mut target = ColorRenderTarget::new( - texture_id, - screen_size, - gpu_supports_fast_clears, - used_rect, - ); - - for task_id in &sub_pass.task_ids { - target.add_task( - *task_id, - ctx, - gpu_cache, - render_tasks, - clip_store, - transforms, - ); + match pass.kind { + RenderPassKind::MainFramebuffer { ref mut main_target, .. } => { + profile_scope!("MainFrameBuffer"); + for &task_id in &pass.tasks { + profile_scope!("task"); + assert_eq!(render_tasks[task_id].target_kind(), RenderTargetKind::Color); + main_target.add_task( + task_id, + ctx, + gpu_cache, + render_tasks, + clip_store, + transforms, + deferred_resolves, + ); + } + main_target.build( + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + prim_headers, + transforms, + z_generator, + composite_state, + ); + } + RenderPassKind::OffScreen { + ref mut color, + ref mut alpha, + ref mut texture_cache, + ref mut picture_cache, + } => { + profile_scope!("OffScreen"); + let saved_color = if pass.tasks.iter().any(|&task_id| { + let t = &render_tasks[task_id]; + t.target_kind() == RenderTargetKind::Color && t.saved_index.is_some() + }) { + Some(render_tasks.save_target()) + } else { + None + }; + let saved_alpha = if pass.tasks.iter().any(|&task_id| { + let t = &render_tasks[task_id]; + t.target_kind() == RenderTargetKind::Alpha && t.saved_index.is_some() + }) { + Some(render_tasks.save_target()) + } else { + None + }; + + // Collect a list of picture cache tasks, keyed by picture index. + // This allows us to only walk that picture root once, adding the + // primitives to all relevant batches at the same time. + let mut picture_cache_tasks = FastHashMap::default(); + + // Step through each task, adding to batches as appropriate. + for &task_id in &pass.tasks { + let (target_kind, texture_target, layer) = { + let task = &mut render_tasks[task_id]; + let target_kind = task.target_kind(); + + // Find a target to assign this task to, or create a new + // one if required. + let (texture_target, layer) = match task.location { + RenderTaskLocation::TextureCache { texture, layer, .. } => { + (Some(texture), layer) + } + RenderTaskLocation::Fixed(..) => { + (None, 0) + } + RenderTaskLocation::Dynamic(ref mut origin, size) => { + let (target_index, alloc_origin) = match target_kind { + RenderTargetKind::Color => color.allocate(size), + RenderTargetKind::Alpha => alpha.allocate(size), + }; + *origin = Some((alloc_origin, target_index)); + (None, target_index.0) + } + RenderTaskLocation::PictureCache { .. } => { + // For picture cache tiles, just store them in the map + // of picture cache tasks, to be handled below. + let pic_index = match task.kind { + RenderTaskKind::Picture(ref info) => { + info.pic_index + } + _ => { + unreachable!(); + } + }; + + picture_cache_tasks + .entry(pic_index) + .or_insert_with(Vec::new) + .push(task_id); + + continue; } + }; - pass.color.targets.push(target); + // Replace the pending saved index with a real one + if let Some(index) = task.saved_index { + assert_eq!(index, SavedTargetIndex::PENDING); + task.saved_index = match target_kind { + RenderTargetKind::Color => saved_color, + RenderTargetKind::Alpha => saved_alpha, + }; } - RenderTargetKind::Alpha => { - let mut target = AlphaRenderTarget::new( - texture_id, - screen_size, - gpu_supports_fast_clears, - used_rect, - ); - - for task_id in &sub_pass.task_ids { - target.add_task( - *task_id, - ctx, - gpu_cache, - render_tasks, - clip_store, - transforms, + + // Give the render task an opportunity to add any + // information to the GPU cache, if appropriate. + task.write_gpu_blocks(gpu_cache); + + (target_kind, texture_target, layer) + }; + + match texture_target { + Some(texture_target) => { + let texture = texture_cache + .entry((texture_target, layer)) + .or_insert_with(|| + TextureCacheRenderTarget::new(target_kind) ); + texture.add_task(task_id, render_tasks); + } + None => { + match target_kind { + RenderTargetKind::Color => { + color.targets[layer].add_task( + task_id, + ctx, + gpu_cache, + render_tasks, + clip_store, + transforms, + deferred_resolves, + ) + } + RenderTargetKind::Alpha => { + alpha.targets[layer].add_task( + task_id, + ctx, + gpu_cache, + render_tasks, + clip_store, + transforms, + deferred_resolves, + ) + } } - - pass.alpha.targets.push(target); } } } - SubPassSurface::Persistent { surface: StaticRenderTaskSurface::PictureCache { .. }, .. } => { - assert_eq!(sub_pass.task_ids.len(), 1); - let task_id = sub_pass.task_ids[0]; - let task = &render_tasks[task_id]; - - // For picture cache tiles, just store them in the map - // of picture cache tasks, to be handled below. - let pic_index = match task.kind { - RenderTaskKind::Picture(ref info) => { - info.pic_index + + // For each picture in this pass that has picture cache tiles, create + // a batcher per task, and then build batches for each of the tasks + // at the same time. + for (pic_index, task_ids) in picture_cache_tasks { + profile_scope!("picture_cache_task"); + let pic = &ctx.prim_store.pictures[pic_index.0]; + let tile_cache = pic.tile_cache.as_ref().expect("bug"); + + // Extract raster/surface spatial nodes for this surface. + let (root_spatial_node_index, surface_spatial_node_index) = match pic.raster_config { + Some(ref rc) => { + let surface = &ctx.surfaces[rc.surface_index.0]; + (surface.raster_spatial_node_index, surface.surface_spatial_node_index) } - _ => { + None => { unreachable!(); } }; - picture_cache_tasks - .entry(pic_index) - .or_insert_with(Vec::new) - .push(task_id); - } - SubPassSurface::Persistent { surface: StaticRenderTaskSurface::TextureCache { target_kind, texture, .. } } => { - let texture = pass.texture_cache - .entry(texture) - .or_insert_with(|| - TextureCacheRenderTarget::new(target_kind) - ); - for task_id in &sub_pass.task_ids { - texture.add_task(*task_id, render_tasks, gpu_cache); - } - } - SubPassSurface::Persistent { surface: StaticRenderTaskSurface::ReadOnly { .. } } => { - panic!("Should not create a render pass for read-only task locations."); - } - } - } + // Determine the clear color for this picture cache. + // If the entire tile cache is opaque, we can skip clear completely. + // If it's the first layer, clear it to white to allow subpixel AA on that + // first layer even if it's technically transparent. + // Otherwise, clear to transparent and composite with alpha. + // TODO(gw): We can detect per-tile opacity for the clear color here + // which might be a significant win on some pages? + let forced_opaque = match tile_cache.background_color { + Some(color) => color.a >= 1.0, + None => false, + }; + let mut clear_color = if forced_opaque { + Some(ColorF::WHITE) + } else { + Some(ColorF::TRANSPARENT) + }; - // For each picture in this pass that has picture cache tiles, create - // a batcher per task, and then build batches for each of the tasks - // at the same time. - for (pic_index, task_ids) in picture_cache_tasks { - profile_scope!("picture_cache_task"); - let pic = &ctx.prim_store.pictures[pic_index.0]; - - // Extract raster/surface spatial nodes for this surface. - let (root_spatial_node_index, surface_spatial_node_index, tile_cache) = match pic.raster_config { - Some(RasterConfig { surface_index, composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => { - let surface = &ctx.surfaces[surface_index.0]; - ( - surface.raster_spatial_node_index, - surface.surface_spatial_node_index, - &ctx.tile_caches[&slice_id], - ) - } - _ => { - unreachable!(); - } - }; + // 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 { - let task_id = *task_id; - let batch_filter = match render_tasks[task_id].kind { - RenderTaskKind::Picture(ref info) => info.batch_filter, - _ => unreachable!(), - }; - batchers.push(AlphaBatchBuilder::new( - screen_size, - ctx.break_advanced_blend_batches, - ctx.batch_lookback_count, - task_id, - task_id.into(), - batch_filter, - 0, - )); - } + // Create an alpha batcher for each of the tasks of this picture. + let mut batchers = Vec::new(); + for task_id in &task_ids { + let task_id = *task_id; + let vis_mask = match render_tasks[task_id].kind { + RenderTaskKind::Picture(ref info) => info.vis_mask, + _ => unreachable!(), + }; + batchers.push(AlphaBatchBuilder::new( + pass.screen_size, + ctx.break_advanced_blend_batches, + ctx.batch_lookback_count, + task_id, + render_tasks.get_task_address(task_id), + vis_mask, + )); + } - // Run the batch creation code for this picture, adding items to - // all relevant per-task batchers. - let mut batch_builder = BatchBuilder::new(batchers); - { - profile_scope!("add_pic_to_batch"); - batch_builder.add_pic_to_batch( - pic, - ctx, - gpu_cache, - render_tasks, - deferred_resolves, - prim_headers, - transforms, - root_spatial_node_index, - surface_spatial_node_index, - z_generator, - composite_state, - ); - } + // Run the batch creation code for this picture, adding items to + // all relevant per-task batchers. + let mut batch_builder = BatchBuilder::new(batchers); + { + profile_scope!("add_pic_to_batch"); + batch_builder.add_pic_to_batch( + pic, + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + prim_headers, + transforms, + root_spatial_node_index, + surface_spatial_node_index, + z_generator, + composite_state, + ); + } - // Create picture cache targets, one per render task, and assign - // the correct batcher to them. - let batchers = batch_builder.finalize(); - for (task_id, batcher) in task_ids.into_iter().zip(batchers.into_iter()) { - profile_scope!("task"); - let task = &render_tasks[task_id]; - let target_rect = task.get_target_rect(); - - match task.location { - RenderTaskLocation::Static { surface: StaticRenderTaskSurface::PictureCache { ref surface, .. }, .. } => { - // TODO(gw): The interface here is a bit untidy since it's - // designed to support batch merging, which isn't - // relevant for picture cache targets. We - // can restructure / tidy this up a bit. - let (scissor_rect, valid_rect, clear_color) = match render_tasks[task_id].kind { - RenderTaskKind::Picture(ref info) => { - let mut clear_color = ColorF::TRANSPARENT; - - // TODO(gw): The way we check the batch filter for is_primary is a bit hacky, tidy up somehow? - if let Some(batch_filter) = info.batch_filter { - if batch_filter.sub_slice_index.is_primary() { - if let Some(background_color) = tile_cache.background_color { - clear_color = background_color; - } - - // 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 = color; - } + // Create picture cache targets, one per render task, and assign + // the correct batcher to them. + let batchers = batch_builder.finalize(); + for (task_id, batcher) in task_ids.into_iter().zip(batchers.into_iter()) { + profile_scope!("task"); + let task = &render_tasks[task_id]; + let (target_rect, _) = task.get_target_rect(); + + match task.location { + RenderTaskLocation::PictureCache { ref surface, .. } => { + // TODO(gw): The interface here is a bit untidy since it's + // designed to support batch merging, which isn't + // relevant for picture cache targets. We + // can restructure / tidy this up a bit. + let (scissor_rect, valid_rect) = match render_tasks[task_id].kind { + RenderTaskKind::Picture(ref info) => { + ( + info.scissor_rect.expect("bug: must be set for cache tasks"), + info.valid_rect.expect("bug: must be set for cache tasks"), + ) } - } + _ => unreachable!(), + }; + let mut batch_containers = Vec::new(); + let mut alpha_batch_container = AlphaBatchContainer::new(Some(scissor_rect)); + batcher.build( + &mut batch_containers, + &mut alpha_batch_container, + target_rect, + None, + ); + debug_assert!(batch_containers.is_empty()); - ( - info.scissor_rect.expect("bug: must be set for cache tasks"), - info.valid_rect.expect("bug: must be set for cache tasks"), + let target = PictureCacheTarget { + surface: surface.clone(), clear_color, - ) - } - _ => unreachable!(), - }; - let mut batch_containers = Vec::new(); - let mut alpha_batch_container = AlphaBatchContainer::new(Some(scissor_rect)); - batcher.build( - &mut batch_containers, - &mut alpha_batch_container, - target_rect, - None, - ); - debug_assert!(batch_containers.is_empty()); - - let target = PictureCacheTarget { - surface: surface.clone(), - clear_color: Some(clear_color), - alpha_batch_container, - dirty_rect: scissor_rect, - valid_rect, - }; + alpha_batch_container, + dirty_rect: scissor_rect, + valid_rect, + }; - pass.picture_cache.push(target); - } - _ => { - unreachable!() + picture_cache.push(target); + } + _ => { + unreachable!() + } + } } } + + color.build( + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + saved_color, + prim_headers, + transforms, + z_generator, + composite_state, + ); + alpha.build( + ctx, + gpu_cache, + render_tasks, + deferred_resolves, + saved_alpha, + prim_headers, + transforms, + z_generator, + composite_state, + ); } } - - pass.color.build( - ctx, - gpu_cache, - render_tasks, - deferred_resolves, - prim_headers, - transforms, - z_generator, - composite_state, - ); - pass.alpha.build( - ctx, - gpu_cache, - render_tasks, - deferred_resolves, - prim_headers, - transforms, - z_generator, - composite_state, - ); - - pass } /// A rendering-oriented representation of the frame built by the render backend @@ -1031,9 +1022,14 @@ pub fn build_render_pass( #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct Frame { + /// The origin on content produced by the render tasks. + pub content_origin: DeviceIntPoint, /// The rectangle to show the frame in, on screen. pub device_rect: DeviceIntRect, + pub layer: DocumentLayer, pub passes: Vec<RenderPass>, + #[cfg_attr(any(feature = "capture", feature = "replay"), serde(default = "FrameProfileCounters::new", skip))] + pub profile_counters: FrameProfileCounters, pub transform_palette: Vec<TransformData>, pub render_tasks: RenderTaskGraph, @@ -1056,6 +1052,11 @@ pub struct Frame { /// renderer. pub has_been_rendered: bool, + /// Dirty regions recorded when generating this frame. Empty when not in + /// testing. + #[cfg_attr(feature = "serde", serde(skip))] + pub recorded_dirty_regions: Vec<RecordedDirtyRegion>, + /// Debugging information to overlay for this frame. pub debug_items: Vec<DebugItem>, @@ -1074,44 +1075,23 @@ impl Frame { // Returns true if this frame doesn't alter what is on screen currently. pub fn is_nop(&self) -> bool { - // If there are no off-screen passes, that implies that there are no - // picture cache tiles, and no texture cache tasks being updates. If this - // is the case, we can consider the frame a nop (higher level checks - // test if a composite is needed due to picture cache surfaces moving - // or external surfaces being updated). - self.passes.is_empty() - } -} - -/// Add a child render task as a dependency to a surface. This is a free -/// function for now as it's also used by the render task cache. -// TODO(gw): Find a more appropriate place for this to live - probably clearer -// once SurfaceInfo gets refactored. -pub fn add_child_render_task( - surface_index: SurfaceIndex, - child_task_id: RenderTaskId, - surfaces: &[SurfaceInfo], - rg_builder: &mut RenderTaskGraphBuilder, -) { - let surface_tasks = surfaces[surface_index.0] - .render_tasks - .as_ref() - .expect("bug: no task for surface"); - - match surface_tasks { - SurfaceRenderTasks::Tiled(ref tasks) => { - // For a tiled render task, add as a dependency to every tile. - for parent_id in tasks { - rg_builder.add_dependency(*parent_id, child_task_id); - } - } - SurfaceRenderTasks::Simple(parent_id) => { - rg_builder.add_dependency(*parent_id, child_task_id); + // If picture caching is disabled, we don't have enough information + // to know if this frame is a nop, so it gets drawn unconditionally. + if !self.composite_state.picture_caching_is_enabled { + return false; } - SurfaceRenderTasks::Chained { port_task_id, .. } => { - // For chained render tasks, add as a dependency of the lowest part of - // the chain (the picture content) - rg_builder.add_dependency(*port_task_id, child_task_id); + + // When picture caching is enabled, the first (main framebuffer) pass + // consists of compositing tiles only (whether via the simple compositor + // or the native OS compositor). If there are no other passes, that + // implies that none of the picture cache tiles were updated, and thus + // the frame content must be exactly the same as last frame. If this is + // true, drawing this frame is a no-op and can be skipped. + + if self.passes.len() > 1 { + return false; } + + true } } diff --git a/third_party/webrender/webrender/src/freelist.rs b/third_party/webrender/webrender/src/freelist.rs index aa90aba03c3..5ca196191cc 100644 --- a/third_party/webrender/webrender/src/freelist.rs +++ b/third_party/webrender/webrender/src/freelist.rs @@ -44,7 +44,6 @@ impl Epoch { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] pub struct FreeListHandle<M> { index: u32, epoch: Epoch, @@ -132,7 +131,7 @@ impl<M> WeakFreeListHandle<M> { } } -#[derive(Debug, MallocSizeOf)] +#[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] struct Slot<T> { @@ -141,7 +140,7 @@ struct Slot<T> { value: Option<T>, } -#[derive(Debug, MallocSizeOf)] +#[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct FreeList<T, M> { diff --git a/third_party/webrender/webrender/src/glyph_cache.rs b/third_party/webrender/webrender/src/glyph_cache.rs index cee2c865630..b0b9ecf65c7 100644 --- a/third_party/webrender/webrender/src/glyph_cache.rs +++ b/third_party/webrender/webrender/src/glyph_cache.rs @@ -5,6 +5,7 @@ use crate::glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer}; use crate::internal_types::FastHashMap; use crate::render_backend::{FrameId, FrameStamp}; +use crate::render_task_cache::RenderTaskCache; use crate::resource_cache::ResourceClassCache; use std::sync::Arc; use crate::texture_cache::{EvictionNotice, TextureCache}; @@ -32,12 +33,21 @@ pub enum GlyphCacheEntry { } impl GlyphCacheEntry { - fn has_been_evicted(&self, texture_cache: &TextureCache) -> bool { + fn get_allocated_size(&self, texture_cache: &TextureCache, _: &RenderTaskCache) + -> Option<usize> { match *self { GlyphCacheEntry::Cached(ref glyph) => { - !texture_cache.is_allocated(&glyph.texture_cache_handle) + texture_cache.get_allocated_size(&glyph.texture_cache_handle) } - GlyphCacheEntry::Pending | GlyphCacheEntry::Blank => false, + GlyphCacheEntry::Pending | GlyphCacheEntry::Blank => Some(0), + } + } + + fn is_recently_used(&self, texture_cache: &mut TextureCache) -> bool { + if let GlyphCacheEntry::Cached(ref glyph) = *self { + texture_cache.is_recently_used(&glyph.texture_cache_handle, 1) + } else { + false } } } @@ -56,32 +66,76 @@ pub enum CachedGlyphData { #[derive(Default)] pub struct GlyphKeyCacheInfo { eviction_notice: EvictionNotice, - #[cfg(debug_assertions)] - #[allow(dead_code)] - #[cfg_attr(feature = "replay", serde(default))] last_frame_used: FrameId, + bytes_used: usize, } pub type GlyphKeyCache = ResourceClassCache<GlyphKey, GlyphCacheEntry, GlyphKeyCacheInfo>; impl GlyphKeyCache { + const DIRTY: usize = !0; + pub fn eviction_notice(&self) -> &EvictionNotice { &self.user_data.eviction_notice } - fn clear_glyphs(&mut self) { + fn is_recently_used(&self, current_frame: FrameId) -> bool { + self.user_data.last_frame_used + 1 >= current_frame + } + + fn clear_glyphs(&mut self) -> usize { + let pruned = self.user_data.bytes_used; self.clear(); + self.user_data.bytes_used = 0; + pruned + } + + fn prune_glyphs( + &mut self, + skip_recent: bool, + excess_bytes_used: usize, + texture_cache: &mut TextureCache, + render_task_cache: &RenderTaskCache, + ) -> usize { + let mut pruned = 0; + self.retain(|_, entry| { + if pruned <= excess_bytes_used && + (!skip_recent || !entry.is_recently_used(texture_cache)) { + match entry.get_allocated_size(texture_cache, render_task_cache) { + Some(size) => { + pruned += size; + false + } + None => true, + } + } else { + true + } + }); + self.user_data.bytes_used -= pruned; + pruned } pub fn add_glyph(&mut self, key: GlyphKey, value: GlyphCacheEntry) { self.insert(key, value); + self.user_data.bytes_used = Self::DIRTY; } - fn clear_evicted(&mut self, texture_cache: &TextureCache) { - if self.eviction_notice().check() { + fn clear_evicted( + &mut self, + texture_cache: &TextureCache, + render_task_cache: &RenderTaskCache, + ) { + if self.eviction_notice().check() || self.user_data.bytes_used == Self::DIRTY { // If there are evictions, filter out any glyphs evicted from the // texture cache from the glyph key cache. - self.retain(|_, entry| !entry.has_been_evicted(texture_cache)); + let mut usage = 0; + self.retain(|_, entry| { + let size = entry.get_allocated_size(texture_cache, render_task_cache); + usage += size.unwrap_or(0); + size.is_some() + }); + self.user_data.bytes_used = usage; } } } @@ -91,13 +145,20 @@ impl GlyphKeyCache { pub struct GlyphCache { glyph_key_caches: FastHashMap<FontInstance, GlyphKeyCache>, current_frame: FrameId, + bytes_used: usize, + max_bytes_used: usize, } impl GlyphCache { - pub fn new() -> Self { + /// The default space usage threshold, in bytes, after which to start pruning away old fonts. + pub const DEFAULT_MAX_BYTES_USED: usize = 6 * 1024 * 1024; + + pub fn new(max_bytes_used: usize) -> Self { GlyphCache { glyph_key_caches: FastHashMap::default(), current_frame: Default::default(), + bytes_used: 0, + max_bytes_used, } } @@ -105,10 +166,7 @@ impl GlyphCache { let cache = self.glyph_key_caches .entry(font) .or_insert_with(GlyphKeyCache::new); - #[cfg(debug_assertions)] - { - cache.user_data.last_frame_used = self.current_frame; - } + cache.user_data.last_frame_used = self.current_frame; cache } @@ -143,11 +201,18 @@ impl GlyphCache { } /// Clear out evicted entries from glyph key caches. - fn clear_evicted(&mut self, texture_cache: &TextureCache) { + fn clear_evicted( + &mut self, + texture_cache: &TextureCache, + render_task_cache: &RenderTaskCache, + ) { + let mut usage = 0; for cache in self.glyph_key_caches.values_mut() { // Scan for any glyph key caches that have evictions. - cache.clear_evicted(texture_cache); + cache.clear_evicted(texture_cache, render_task_cache); + usage += cache.user_data.bytes_used; } + self.bytes_used = usage; } /// If possible, remove entirely any empty glyph key caches. @@ -163,15 +228,55 @@ impl GlyphCache { }); } + /// Check the total space usage of the glyph cache. If it exceeds the maximum usage threshold, + /// then start clearing the oldest glyphs until below the threshold. + fn prune_excess_usage( + &mut self, + texture_cache: &mut TextureCache, + render_task_cache: &RenderTaskCache, + ) { + if self.bytes_used < self.max_bytes_used { + return; + } + // Usage is above the threshold. Get a last-recently-used ordered list of caches to clear. + let mut caches: Vec<_> = self.glyph_key_caches.values_mut().collect(); + caches.sort_unstable_by(|a, b| { + a.user_data.last_frame_used.cmp(&b.user_data.last_frame_used) + }); + // Clear out the oldest caches until below the threshold. + for cache in caches { + if self.bytes_used < self.max_bytes_used { + break; + } + let recent = cache.is_recently_used(self.current_frame); + let excess = self.bytes_used - self.max_bytes_used; + if !recent && excess >= cache.user_data.bytes_used { + // If the excess is greater than the cache's size, just clear the whole thing. + self.bytes_used -= cache.clear_glyphs(); + } else { + // Otherwise, just clear as little of the cache as needed to remove the excess + // and avoid rematerialization costs. + self.bytes_used -= cache.prune_glyphs( + recent, + excess, + texture_cache, + render_task_cache, + ); + } + } + } + pub fn begin_frame( &mut self, stamp: FrameStamp, texture_cache: &mut TextureCache, + render_task_cache: &RenderTaskCache, glyph_rasterizer: &mut GlyphRasterizer, ) { profile_scope!("begin_frame"); self.current_frame = stamp.frame_id(); - self.clear_evicted(texture_cache); + self.clear_evicted(texture_cache, render_task_cache); + self.prune_excess_usage(texture_cache, render_task_cache); // Clearing evicted glyphs and pruning excess usage might have produced empty caches, // so get rid of them if possible. self.clear_empty_caches(glyph_rasterizer); diff --git a/third_party/webrender/webrender/src/glyph_rasterizer/mod.rs b/third_party/webrender/webrender/src/glyph_rasterizer/mod.rs index 80aa37826ba..0a60cc77d6c 100644 --- a/third_party/webrender/webrender/src/glyph_rasterizer/mod.rs +++ b/third_party/webrender/webrender/src/glyph_rasterizer/mod.rs @@ -5,7 +5,6 @@ use api::{FontInstanceFlags, FontSize, BaseFontInstance}; use api::{FontKey, FontRenderMode, FontTemplate}; use api::{ColorU, GlyphIndex, GlyphDimensions, SyntheticItalics}; -use api::channel::{unbounded_channel, Receiver, Sender}; use api::units::*; use api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, DirtyRect}; use crate::internal_types::ResourceCacheError; @@ -13,23 +12,24 @@ use crate::platform::font::FontContext; use crate::device::TextureFilter; use crate::gpu_types::UvRectKind; use crate::glyph_cache::{GlyphCache, CachedGlyphInfo, GlyphCacheEntry}; -use crate::internal_types::FastHashMap; use crate::resource_cache::CachedImageData; -use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader}; +use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction}; use crate::gpu_cache::GpuCache; -use crate::profiler::{self, TransactionProfile}; +use crate::render_task_graph::RenderTaskGraph; +use crate::render_task_cache::RenderTaskCache; +use crate::profiler::TextureCacheProfileCounters; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use rayon::ThreadPool; use rayon::prelude::*; use euclid::approxeq::ApproxEq; use euclid::size2; -use smallvec::SmallVec; use std::cmp; use std::cell::Cell; use std::hash::{Hash, Hasher}; use std::mem; use std::ops::Deref; use std::sync::{Arc, Condvar, Mutex, MutexGuard}; +use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::atomic::{AtomicBool, Ordering}; pub static GLYPH_FLASHING: AtomicBool = AtomicBool::new(false); @@ -66,12 +66,15 @@ impl GlyphRasterizer { glyph_keys: &[GlyphKey], texture_cache: &mut TextureCache, gpu_cache: &mut GpuCache, + _: &mut RenderTaskCache, + _: &mut RenderTaskGraph, ) { assert!( self.font_contexts .lock_shared_context() .has_font(&font.font_key) ); + let mut new_glyphs = Vec::new(); let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone()); @@ -92,63 +95,28 @@ impl GlyphRasterizer { GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => continue, } } + new_glyphs.push(key.clone()); + glyph_key_cache.add_glyph(key.clone(), GlyphCacheEntry::Pending); + } - // Increment the total number of glyphs that are pending. This is used to determine - // later whether to use worker threads for the remaining glyphs during resolve time. - self.pending_glyph_count += 1; - self.glyph_request_count += 1; - - // Find a batch container for the font instance for this glyph. Use get_mut to avoid - // cloning the font instance, since this is the common path. - match self.pending_glyph_requests.get_mut(&font) { - Some(container) => { - container.push(*key); - - // If the batch for this font instance is big enough, kick off an async - // job to start rasterizing these glyphs on other threads now. - if container.len() == 8 { - let glyphs = mem::replace(container, SmallVec::new()); - self.flush_glyph_requests( - font.clone(), - glyphs, - true, - ); - } - } - None => { - // If no batch exists for this font instance, add the glyph to a new one. - self.pending_glyph_requests.insert( - font.clone(), - smallvec![*key], - ); - } - } - - glyph_key_cache.add_glyph(*key, GlyphCacheEntry::Pending); + if new_glyphs.is_empty() { + return; } + + self.pending_glyphs += 1; + + self.request_glyphs_from_backend(font, new_glyphs); } pub fn enable_multithreading(&mut self, enable: bool) { self.enable_multithreading = enable; } - /// Internal method to flush a list of glyph requests to a set of worker threads, - /// or process on this thread if there isn't much work to do (in which case the - /// overhead of processing these on a thread is unlikely to be a performance win). - fn flush_glyph_requests( - &mut self, - font: FontInstance, - glyphs: SmallVec<[GlyphKey; 16]>, - use_workers: bool, - ) { + pub(in super) fn request_glyphs_from_backend(&mut self, font: FontInstance, glyphs: Vec<GlyphKey>) { let font_contexts = Arc::clone(&self.font_contexts); let glyph_tx = self.glyph_tx.clone(); - self.pending_glyph_jobs += 1; - self.pending_glyph_count -= glyphs.len(); - - let can_use_r8_format = self.can_use_r8_format; - let process_glyph = move |key: &GlyphKey, font_contexts: &FontContexts, font: &FontInstance| -> GlyphRasterJob { + fn process_glyph(key: &GlyphKey, font_contexts: &FontContexts, font: &FontInstance) -> GlyphRasterJob { profile_scope!("glyph-raster"); let mut context = font_contexts.lock_current_context(); let mut job = GlyphRasterJob { @@ -183,24 +151,19 @@ impl GlyphRasterizer { // Check if the glyph has a bitmap that needs to be downscaled. glyph.downscale_bitmap_if_required(&font); - - // Convert from BGRA8 to R8 if required. In the future we can make it the - // backends' responsibility to output glyphs in the desired format, - // potentially reducing the number of copies. - if glyph.format.image_format(can_use_r8_format).bytes_per_pixel() == 1 { - glyph.bytes = glyph.bytes - .chunks_mut(4) - .map(|pixel| pixel[3]) - .collect::<Vec<_>>(); - } } job - }; + } // if the number of glyphs is small, do it inline to avoid the threading overhead; // send the result into glyph_tx so downstream code can't tell the difference. - if self.enable_multithreading && use_workers { + if !self.enable_multithreading || glyphs.len() < 8 { + let jobs = glyphs.iter() + .map(|key: &GlyphKey| process_glyph(key, &font_contexts, &font)) + .collect(); + glyph_tx.send(GlyphRasterJobs { font, jobs }).unwrap(); + } else { // spawn an async task to get off of the render backend thread as early as // possible and in that task use rayon's fork join dispatch to rasterize the // glyphs in the thread pool. @@ -213,11 +176,6 @@ impl GlyphRasterizer { glyph_tx.send(GlyphRasterJobs { font, jobs }).unwrap(); }); - } else { - let jobs = glyphs.iter() - .map(|key: &GlyphKey| process_glyph(key, &font_contexts, &font)) - .collect(); - glyph_tx.send(GlyphRasterJobs { font, jobs }).unwrap(); } } @@ -226,39 +184,14 @@ impl GlyphRasterizer { glyph_cache: &mut GlyphCache, texture_cache: &mut TextureCache, gpu_cache: &mut GpuCache, - profile: &mut TransactionProfile, + _: &mut RenderTaskCache, + _: &mut RenderTaskGraph, + _: &mut TextureCacheProfileCounters, ) { - profile.start_time(profiler::GLYPH_RESOLVE_TIME); - - // Work around the borrow checker, since we call flush_glyph_requests below - let mut pending_glyph_requests = mem::replace( - &mut self.pending_glyph_requests, - FastHashMap::default(), - ); - // If we have a large amount of remaining work to do, spawn to worker threads, - // even if that work is shared among a number of different font instances. - let use_workers = self.pending_glyph_count >= 8; - for (font, pending_glyphs) in pending_glyph_requests.drain() { - self.flush_glyph_requests( - font, - pending_glyphs, - use_workers, - ); - } - // Restore this so that we don't heap allocate next frame - self.pending_glyph_requests = pending_glyph_requests; - debug_assert_eq!(self.pending_glyph_count, 0); - debug_assert!(self.pending_glyph_requests.is_empty()); - - if self.glyph_request_count > 0 { - profile.set(profiler::RASTERIZED_GLYPHS, self.glyph_request_count); - self.glyph_request_count = 0; - } - profile_scope!("resolve_glyphs"); // Pull rasterized glyphs from the queue and update the caches. - while self.pending_glyph_jobs > 0 { - self.pending_glyph_jobs -= 1; + while self.pending_glyphs > 0 { + self.pending_glyphs -= 1; // TODO: rather than blocking until all pending glyphs are available // we could try_recv and steal work from the thread pool to take advantage @@ -295,19 +228,18 @@ impl GlyphRasterizer { ImageDescriptor { size: size2(glyph.width, glyph.height), stride: None, - format: glyph.format.image_format(self.can_use_r8_format), + format: FORMAT, flags: ImageDescriptorFlags::empty(), offset: 0, }, TextureFilter::Linear, Some(CachedImageData::Raw(Arc::new(glyph.bytes))), - [glyph.left, -glyph.top, glyph.scale, 0.0], + [glyph.left, -glyph.top, glyph.scale], DirtyRect::All, gpu_cache, Some(glyph_key_cache.eviction_notice()), UvRectKind::Rect, Eviction::Auto, - TargetShader::Text, ); GlyphCacheEntry::Cached(CachedGlyphInfo { texture_cache_handle, @@ -322,11 +254,12 @@ impl GlyphRasterizer { // Now that we are done with the critical path (rendering the glyphs), // we can schedule removing the fonts if needed. self.remove_dead_fonts(); - - profile.end_time(profiler::GLYPH_RESOLVE_TIME); } } +#[allow(dead_code)] +pub const FORMAT: ImageFormat = ImageFormat::BGRA8; + #[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -708,7 +641,7 @@ impl Into<f64> for SubpixelOffset { } } -#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] +#[derive(Clone, Hash, PartialEq, Eq, Debug, Ord, PartialOrd)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct GlyphKey(u32); @@ -759,23 +692,10 @@ pub enum GlyphFormat { } impl GlyphFormat { - /// Returns the ImageFormat that a glyph should be stored as in the texture cache. - /// can_use_r8_format should be set false on platforms where we have encountered - /// issues with R8 textures, so that we do not use them for glyphs. - pub fn image_format(&self, can_use_r8_format: bool) -> ImageFormat { - match *self { - GlyphFormat::Alpha | - GlyphFormat::TransformedAlpha | - GlyphFormat::Bitmap => { - if can_use_r8_format { - ImageFormat::R8 - } else { - ImageFormat::BGRA8 - } - } - GlyphFormat::Subpixel | - GlyphFormat::TransformedSubpixel | - GlyphFormat::ColorBitmap => ImageFormat::BGRA8, + pub fn ignore_color(self) -> Self { + match self { + GlyphFormat::ColorBitmap => GlyphFormat::Bitmap, + _ => self, } } } @@ -957,20 +877,19 @@ pub struct GlyphRasterizer { workers: Arc<ThreadPool>, font_contexts: Arc<FontContexts>, - /// The current number of individual glyphs waiting in pending batches. - pending_glyph_count: usize, - - /// The current number of glyph request jobs that have been kicked to worker threads. - pending_glyph_jobs: usize, - - /// The number of glyphs requested this frame. - glyph_request_count: usize, - - /// A map of current glyph request batches. - pending_glyph_requests: FastHashMap<FontInstance, SmallVec<[GlyphKey; 16]>>, + // Maintain a set of glyphs that have been requested this + // frame. This ensures the glyph thread won't rasterize + // the same glyph more than once in a frame. This is required + // because the glyph cache hash table is not updated + // until the end of the frame when we wait for glyph requests + // to be resolved. + #[allow(dead_code)] + pending_glyphs: usize, // Receives the rendered glyphs. + #[allow(dead_code)] glyph_rx: Receiver<GlyphRasterJobs>, + #[allow(dead_code)] glyph_tx: Sender<GlyphRasterJobs>, // We defer removing fonts to the end of the frame so that: @@ -981,16 +900,16 @@ pub struct GlyphRasterizer { // Defer removal of font instances, as for fonts. font_instances_to_remove: Vec<FontInstance>, + #[allow(dead_code)] + next_gpu_glyph_cache_key: GpuGlyphCacheKey, + // Whether to parallelize glyph rasterization with rayon. enable_multithreading: bool, - - // Whether glyphs can be rasterized in r8 format when it makes sense. - can_use_r8_format: bool, } impl GlyphRasterizer { - pub fn new(workers: Arc<ThreadPool>, can_use_r8_format: bool) -> Result<Self, ResourceCacheError> { - let (glyph_tx, glyph_rx) = unbounded_channel(); + pub fn new(workers: Arc<ThreadPool>) -> Result<Self, ResourceCacheError> { + let (glyph_tx, glyph_rx) = channel(); let num_workers = workers.current_num_threads(); let mut contexts = Vec::with_capacity(num_workers); @@ -1011,17 +930,14 @@ impl GlyphRasterizer { Ok(GlyphRasterizer { font_contexts: Arc::new(font_context), - pending_glyph_jobs: 0, - pending_glyph_count: 0, - glyph_request_count: 0, + pending_glyphs: 0, glyph_rx, glyph_tx, workers, fonts_to_remove: Vec::new(), font_instances_to_remove: Vec::new(), + next_gpu_glyph_cache_key: GpuGlyphCacheKey(0), enable_multithreading: true, - pending_glyph_requests: FastHashMap::default(), - can_use_r8_format, }) } @@ -1093,9 +1009,7 @@ impl GlyphRasterizer { #[cfg(feature = "replay")] pub fn reset(&mut self) { //TODO: any signals need to be sent to the workers? - self.pending_glyph_jobs = 0; - self.pending_glyph_count = 0; - self.glyph_request_count = 0; + self.pending_glyphs = 0; self.fonts_to_remove.clear(); self.font_instances_to_remove.clear(); } @@ -1145,8 +1059,6 @@ struct GlyphRasterJobs { #[cfg(test)] mod test_glyph_rasterizer { - pub const FORMAT: api::ImageFormat = api::ImageFormat::BGRA8; - #[test] fn rasterize_200_glyphs() { // This test loads a font from disc, the renders 4 requests containing @@ -1158,21 +1070,26 @@ mod test_glyph_rasterizer { use crate::texture_cache::TextureCache; use crate::glyph_cache::GlyphCache; use crate::gpu_cache::GpuCache; - use crate::profiler::TransactionProfile; + use crate::render_task_cache::RenderTaskCache; + use crate::render_task_graph::{RenderTaskGraph, RenderTaskGraphCounters}; + use crate::profiler::TextureCacheProfileCounters; use api::{FontKey, FontInstanceKey, FontSize, FontTemplate, FontRenderMode, IdNamespace, ColorU}; use api::units::DevicePoint; + use crate::render_backend::FrameId; use std::sync::Arc; - use crate::glyph_rasterizer::{FontInstance, BaseFontInstance, GlyphKey, GlyphRasterizer}; + use crate::glyph_rasterizer::{FORMAT, FontInstance, BaseFontInstance, GlyphKey, GlyphRasterizer}; let worker = ThreadPoolBuilder::new() .thread_name(|idx|{ format!("WRWorker#{}", idx) }) .build(); let workers = Arc::new(worker.unwrap()); - let mut glyph_rasterizer = GlyphRasterizer::new(workers, true).unwrap(); - let mut glyph_cache = GlyphCache::new(); + let mut glyph_rasterizer = GlyphRasterizer::new(workers).unwrap(); + let mut glyph_cache = GlyphCache::new(GlyphCache::DEFAULT_MAX_BYTES_USED); let mut gpu_cache = GpuCache::new_for_testing(); - let mut texture_cache = TextureCache::new_for_testing(2048, FORMAT); + let mut texture_cache = TextureCache::new_for_testing(2048, 1024, FORMAT); + let mut render_task_cache = RenderTaskCache::new(); + let mut render_task_tree = RenderTaskGraph::new(FrameId::INVALID, &RenderTaskGraphCounters::new()); let mut font_file = File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file"); let mut font_data = vec![]; @@ -1213,6 +1130,8 @@ mod test_glyph_rasterizer { &glyph_keys[(50 * i) .. (50 * (i + 1))], &mut texture_cache, &mut gpu_cache, + &mut render_task_cache, + &mut render_task_tree, ); } @@ -1220,85 +1139,11 @@ mod test_glyph_rasterizer { glyph_rasterizer.resolve_glyphs( &mut glyph_cache, - &mut TextureCache::new_for_testing(4096, FORMAT), - &mut gpu_cache, - &mut TransactionProfile::new(), - ); - } - - #[test] - fn rasterize_large_glyphs() { - // This test loads a font from disc and rasterize a few glyphs with a size of 200px to check - // that the texture cache handles them properly. - use rayon::ThreadPoolBuilder; - use std::fs::File; - use std::io::Read; - use crate::texture_cache::TextureCache; - use crate::glyph_cache::GlyphCache; - use crate::gpu_cache::GpuCache; - use crate::profiler::TransactionProfile; - use api::{FontKey, FontInstanceKey, FontSize, FontTemplate, FontRenderMode, - IdNamespace, ColorU}; - use api::units::DevicePoint; - use std::sync::Arc; - use crate::glyph_rasterizer::{FontInstance, BaseFontInstance, GlyphKey, GlyphRasterizer}; - - let worker = ThreadPoolBuilder::new() - .thread_name(|idx|{ format!("WRWorker#{}", idx) }) - .build(); - let workers = Arc::new(worker.unwrap()); - let mut glyph_rasterizer = GlyphRasterizer::new(workers, true).unwrap(); - let mut glyph_cache = GlyphCache::new(); - let mut gpu_cache = GpuCache::new_for_testing(); - let mut texture_cache = TextureCache::new_for_testing(2048, FORMAT); - let mut font_file = - File::open("../wrench/reftests/text/VeraBd.ttf").expect("Couldn't open font file"); - let mut font_data = vec![]; - font_file - .read_to_end(&mut font_data) - .expect("failed to read font file"); - - let font_key = FontKey::new(IdNamespace(0), 0); - glyph_rasterizer.add_font(font_key, FontTemplate::Raw(Arc::new(font_data), 0)); - - let font = FontInstance::from_base(Arc::new(BaseFontInstance { - instance_key: FontInstanceKey(IdNamespace(0), 0), - font_key, - size: FontSize::from_f32_px(200.0), - bg_color: ColorU::new(0, 0, 0, 0), - render_mode: FontRenderMode::Subpixel, - flags: Default::default(), - synthetic_italics: Default::default(), - platform_options: None, - variations: Vec::new(), - })); - - let subpx_dir = font.get_subpx_dir(); - - let mut glyph_keys = Vec::with_capacity(10); - for i in 0 .. 10 { - glyph_keys.push(GlyphKey::new( - i, - DevicePoint::zero(), - subpx_dir, - )); - } - - glyph_rasterizer.request_glyphs( - &mut glyph_cache, - font.clone(), - &glyph_keys, - &mut texture_cache, - &mut gpu_cache, - ); - - glyph_rasterizer.delete_font(font_key); - - glyph_rasterizer.resolve_glyphs( - &mut glyph_cache, - &mut TextureCache::new_for_testing(4096, FORMAT), + &mut TextureCache::new_for_testing(4096, 1024, FORMAT), &mut gpu_cache, - &mut TransactionProfile::new(), + &mut render_task_cache, + &mut render_task_tree, + &mut TextureCacheProfileCounters::new(), ); } diff --git a/third_party/webrender/webrender/src/gpu_cache.rs b/third_party/webrender/webrender/src/gpu_cache.rs index 98dc8bf5845..c34efd09d8b 100644 --- a/third_party/webrender/webrender/src/gpu_cache.rs +++ b/third_party/webrender/webrender/src/gpu_cache.rs @@ -27,15 +27,13 @@ use api::{DebugFlags, DocumentId, PremultipliedColorF}; #[cfg(test)] use api::IdNamespace; -use api::units::*; +use api::units::TexelRect; use euclid::{HomogeneousVector, Rect}; use crate::internal_types::{FastHashMap, FastHashSet}; -use crate::profiler::{self, TransactionProfile}; +use crate::profiler::GpuCacheProfileCounters; use crate::render_backend::{FrameStamp, FrameId}; -use crate::prim_store::VECS_PER_SEGMENT; use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH; -use crate::util::VecHelper; -use std::{u16, u32}; +use std::{mem, u16, u32}; use std::num::NonZeroU32; use std::ops::Add; use std::time::{Duration, Instant}; @@ -139,6 +137,13 @@ impl From<TexelRect> for GpuBlockData { } +// Any data type that can be stored in the GPU cache should +// implement this trait. +pub trait ToGpuBlocks { + // Request an arbitrary number of GPU data blocks. + fn write_gpu_blocks(&self, _: GpuDataRequest); +} + // A handle to a GPU resource. #[derive(Debug, Copy, Clone, MallocSizeOf)] #[cfg_attr(feature = "capture", derive(Serialize))] @@ -151,10 +156,6 @@ impl GpuCacheHandle { pub fn new() -> Self { GpuCacheHandle { location: None } } - - pub fn as_int(self, gpu_cache: &GpuCache) -> i32 { - gpu_cache.get_address(&self).as_int() - } } // A unique address in the GPU cache. These are uploaded @@ -180,13 +181,6 @@ impl GpuCacheAddress { u: u16::MAX, v: u16::MAX, }; - - pub fn as_int(self) -> i32 { - // TODO(gw): Temporarily encode GPU Cache addresses as a single int. - // In the future, we can change the PrimitiveInstanceData struct - // to use 2x u16 for the vertex attribute instead of an i32. - self.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + self.u as i32 - } } impl Add<usize> for GpuCacheAddress { @@ -648,9 +642,6 @@ impl Texture { /// works as a container that can only grow. #[must_use] pub struct GpuDataRequest<'a> { - //TODO: remove this, see - // https://bugzilla.mozilla.org/show_bug.cgi?id=1690546 - #[allow(dead_code)] handle: &'a mut GpuCacheHandle, frame_stamp: FrameStamp, start_index: usize, @@ -666,17 +657,6 @@ impl<'a> GpuDataRequest<'a> { self.texture.pending_blocks.push(block.into()); } - // Write the GPU cache data for an individual segment. - pub fn write_segment( - &mut self, - local_rect: LayoutRect, - extra_data: [f32; 4], - ) { - let _ = VECS_PER_SEGMENT; - self.push(local_rect); - self.push(extra_data); - } - pub fn current_used_block_num(&self) -> usize { self.texture.pending_blocks.len() - self.start_index } @@ -868,12 +848,18 @@ impl GpuCache { /// device specific cache texture. pub fn end_frame( &mut self, - profile: &mut TransactionProfile, + profile_counters: &mut GpuCacheProfileCounters, ) -> FrameStamp { profile_scope!("end_frame"); - profile.set(profiler::GPU_CACHE_ROWS_TOTAL, self.texture.rows.len()); - profile.set(profiler::GPU_CACHE_BLOCKS_TOTAL, self.texture.allocated_block_count); - profile.set(profiler::GPU_CACHE_BLOCKS_SAVED, self.saved_block_count); + profile_counters + .allocated_rows + .set(self.texture.rows.len()); + profile_counters + .allocated_blocks + .set(self.texture.allocated_block_count); + profile_counters + .saved_blocks + .set(self.saved_block_count); let reached_threshold = self.texture.rows.len() > (GPU_CACHE_INITIAL_HEIGHT as usize) && @@ -903,9 +889,9 @@ impl GpuCache { frame_id: self.now.frame_id(), clear, height: self.texture.height, - debug_commands: self.texture.debug_commands.take_and_preallocate(), - updates: self.texture.updates.take_and_preallocate(), - blocks: self.texture.pending_blocks.take_and_preallocate(), + debug_commands: mem::replace(&mut self.texture.debug_commands, Vec::new()), + updates: mem::replace(&mut self.texture.updates, Vec::new()), + blocks: mem::replace(&mut self.texture.pending_blocks, Vec::new()), } } diff --git a/third_party/webrender/webrender/src/gpu_types.rs b/third_party/webrender/webrender/src/gpu_types.rs index f6d91cab342..8e85b4dff06 100644 --- a/third_party/webrender/webrender/src/gpu_types.rs +++ b/third_party/webrender/webrender/src/gpu_types.rs @@ -2,14 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{AlphaType, PremultipliedColorF, YuvFormat, YuvColorSpace}; +use api::{AlphaType, DocumentLayer, PremultipliedColorF, YuvFormat, YuvColorSpace}; +use api::EdgeAaSegmentMask; use api::units::*; -use crate::composite::CompositeFeatures; -use crate::segment::EdgeAaSegmentMask; use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex}; use crate::gpu_cache::{GpuCacheAddress, GpuDataRequest}; use crate::internal_types::FastHashMap; -use crate::prim_store::ClipData; use crate::render_task::RenderTaskAddress; use crate::renderer::ShaderColorMode; use std::i32; @@ -27,6 +25,10 @@ pub const VECS_PER_TRANSFORM: usize = 8; #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ZBufferId(pub i32); +const MAX_DOCUMENT_LAYERS : i8 = 1 << 3; +const MAX_DOCUMENT_LAYER_VALUE : i8 = MAX_DOCUMENT_LAYERS / 2 - 1; +const MIN_DOCUMENT_LAYER_VALUE : i8 = -MAX_DOCUMENT_LAYERS / 2; + impl ZBufferId { pub fn invalid() -> Self { ZBufferId(i32::MAX) @@ -37,26 +39,53 @@ impl ZBufferId { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ZBufferIdGenerator { + base: i32, next: i32, - max_depth_ids: i32, + max_items_per_document_layer: i32, } impl ZBufferIdGenerator { - pub fn new(max_depth_ids: i32) -> 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, - max_depth_ids, + max_items_per_document_layer, } } pub fn next(&mut self) -> ZBufferId { - debug_assert!(self.next < self.max_depth_ids); - let id = ZBufferId(self.next); + debug_assert!(self.next < self.max_items_per_document_layer); + let id = ZBufferId(self.next + self.base); self.next += 1; id } } +/// A shader kind identifier that can be used by a generic-shader to select the behavior at runtime. +/// +/// Not all brush kinds need to be present in this enum, only those we want to support in the generic +/// brush shader. +/// Do not use the 24 lowest bits. This will be packed with other information in the vertex attributes. +/// The constants must match the corresponding defines in brush_multi.glsl. +#[repr(i32)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum BrushShaderKind { + None = 0, + Solid = 1, + Image = 2, + Text = 3, + LinearGradient = 4, + RadialGradient = 5, + ConicGradient = 6, + Blend = 7, + MixBlend = 8, + Yuv = 9, + Opacity = 10, +} + #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -84,7 +113,7 @@ pub enum BlurDirection { Vertical, } -#[derive(Clone, Debug)] +#[derive(Debug)] #[repr(C)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -94,16 +123,17 @@ pub struct BlurInstance { pub blur_direction: BlurDirection, } -#[derive(Clone, Debug)] +#[derive(Debug)] #[repr(C)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ScalingInstance { pub target_rect: DeviceRect, - pub source_rect: DeviceRect, + pub source_rect: DeviceIntRect, + pub source_layer: i32, } -#[derive(Clone, Debug)] +#[derive(Debug)] #[repr(C)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -147,62 +177,6 @@ pub struct BorderInstance { pub clip_params: [f32; 8], } -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -pub struct ClipMaskInstanceCommon { - pub sub_rect: DeviceRect, - pub task_origin: DevicePoint, - pub screen_origin: DevicePoint, - pub device_pixel_scale: f32, - pub clip_transform_id: TransformPaletteId, - pub prim_transform_id: TransformPaletteId, -} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -pub struct ClipMaskInstanceImage { - pub common: ClipMaskInstanceCommon, - pub tile_rect: LayoutRect, - pub resource_address: GpuCacheAddress, - pub local_rect: LayoutRect, -} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -pub struct ClipMaskInstanceRect { - pub common: ClipMaskInstanceCommon, - pub local_pos: LayoutPoint, - pub clip_data: ClipData, -} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -pub struct BoxShadowData { - pub src_rect_size: LayoutSize, - pub clip_mode: i32, - pub stretch_mode_x: i32, - pub stretch_mode_y: i32, - pub dest_rect: LayoutRect, -} - -#[derive(Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -pub struct ClipMaskInstanceBoxShadow { - pub common: ClipMaskInstanceCommon, - pub resource_address: GpuCacheAddress, - pub shadow_data: BoxShadowData, -} - /// A clipping primitive drawn into the clipping mask. /// Could be an image or a rectangle, which defines the /// way `address` is treated. @@ -223,6 +197,16 @@ pub struct ClipMaskInstance { pub device_pixel_scale: f32, } +/// A border corner dot or dash drawn into the clipping mask. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[repr(C)] +pub struct ClipMaskBorderCornerDotDash { + pub clip_mask_instance: ClipMaskInstance, + pub dot_dash_data: [f32; 8], +} + // 16 bytes per instance should be enough for anyone! #[derive(Debug, Clone)] #[cfg_attr(feature = "capture", derive(Serialize))] @@ -231,20 +215,35 @@ pub struct PrimitiveInstanceData { data: [i32; 4], } -/// Specifies that an RGB CompositeInstance's UV coordinates are normalized. -const UV_TYPE_NORMALIZED: u32 = 0; -/// Specifies that an RGB CompositeInstance's UV coordinates are not normalized. -const UV_TYPE_UNNORMALIZED: u32 = 1; +/// Vertex format for resolve style operations with pixel local storage. +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ResolveInstanceData { + rect: [f32; 4], +} + +impl ResolveInstanceData { + pub fn new(rect: DeviceIntRect) -> Self { + ResolveInstanceData { + rect: [ + rect.origin.x as f32, + rect.origin.y as f32, + rect.size.width as f32, + rect.size.height as f32, + ], + } + } +} /// Vertex format for picture cache composite shader. /// When editing the members, update desc::COMPOSITE /// so its list of instance_attributes matches: -#[derive(Clone, Debug)] +#[derive(Debug, Clone)] #[repr(C)] pub struct CompositeInstance { - // Device space destination rectangle of surface + // Device space rectangle of surface rect: DeviceRect, - // Device space destination clip rect for this surface + // Device space clip rect for this surface clip_rect: DeviceRect, // Color for solid color tiles, white otherwise color: PremultipliedColorF, @@ -258,6 +257,9 @@ pub struct CompositeInstance { // UV rectangles (pixel space) for color / yuv texture planes uv_rects: [TexelRect; 3], + + // Texture array layers for color / yuv texture planes + texture_layers: [f32; 3], } impl CompositeInstance { @@ -265,6 +267,7 @@ impl CompositeInstance { rect: DeviceRect, clip_rect: DeviceRect, color: PremultipliedColorF, + layer: f32, z_id: ZBufferId, ) -> Self { let uv = TexelRect::new(0.0, 0.0, 1.0, 1.0); @@ -273,9 +276,10 @@ impl CompositeInstance { clip_rect, color, z_id: z_id.0 as f32, - color_space_or_uv_type: pack_as_float(UV_TYPE_NORMALIZED), + color_space_or_uv_type: pack_as_float(0u32), yuv_format: 0.0, yuv_rescale: 0.0, + texture_layers: [layer, 0.0, 0.0], uv_rects: [uv, uv, uv], } } @@ -284,6 +288,7 @@ impl CompositeInstance { rect: DeviceRect, clip_rect: DeviceRect, color: PremultipliedColorF, + layer: f32, z_id: ZBufferId, uv_rect: TexelRect, ) -> Self { @@ -292,9 +297,10 @@ impl CompositeInstance { clip_rect, color, z_id: z_id.0 as f32, - color_space_or_uv_type: pack_as_float(UV_TYPE_UNNORMALIZED), + color_space_or_uv_type: pack_as_float(1u32), yuv_format: 0.0, yuv_rescale: 0.0, + texture_layers: [layer, 0.0, 0.0], uv_rects: [uv_rect, uv_rect, uv_rect], } } @@ -306,6 +312,7 @@ impl CompositeInstance { yuv_color_space: YuvColorSpace, yuv_format: YuvFormat, yuv_rescale: f32, + texture_layers: [f32; 3], uv_rects: [TexelRect; 3], ) -> Self { CompositeInstance { @@ -316,29 +323,10 @@ impl CompositeInstance { color_space_or_uv_type: pack_as_float(yuv_color_space as u32), yuv_format: pack_as_float(yuv_format as u32), yuv_rescale, + texture_layers, uv_rects, } } - - // Returns the CompositeFeatures that can be used to composite - // this RGB instance. - pub fn get_rgb_features(&self) -> CompositeFeatures { - let mut features = CompositeFeatures::empty(); - - // If the UV rect covers the entire texture then we can avoid UV clamping. - // We should try harder to determine this for unnormalized UVs too. - if self.color_space_or_uv_type == pack_as_float(UV_TYPE_NORMALIZED) - && self.uv_rects[0] == TexelRect::new(0.0, 0.0, 1.0, 1.0) - { - features |= CompositeFeatures::NO_UV_CLAMP; - } - - if self.color == PremultipliedColorF::WHITE { - features |= CompositeFeatures::NO_COLOR_MODULATION - } - - features - } } /// Vertex format for issuing colored quads. @@ -466,7 +454,8 @@ impl GlyphInstance { (subpx_dir as u32 as i32) << 24 | (color_mode as u32 as i32) << 16 | glyph_index_in_text_run, - glyph_uv_rect.as_int(), + glyph_uv_rect.as_int() + | ((BrushShaderKind::Text as i32) << 24), ], } } @@ -528,6 +517,7 @@ pub struct BrushInstance { pub edge_flags: EdgeAaSegmentMask, pub brush_flags: BrushFlags, pub resource_address: i32, + pub brush_kind: BrushShaderKind, } impl From<BrushInstance> for PrimitiveInstanceData { @@ -540,7 +530,8 @@ impl From<BrushInstance> for PrimitiveInstanceData { instance.segment_index | ((instance.edge_flags.bits() as i32) << 16) | ((instance.brush_flags.bits() as i32) << 24), - instance.resource_address, + instance.resource_address + | ((instance.brush_kind as i32) << 24), ] } } @@ -591,14 +582,6 @@ impl TransformPaletteId { TransformedRectKind::Complex } } - - /// Override the kind of transform stored in this id. This can be useful in - /// cases where we don't want shaders to consider certain transforms axis- - /// aligned (i.e. perspective warp) even though we may still want to for the - /// general case. - pub fn override_transform_kind(&self, kind: TransformedRectKind) -> Self { - TransformPaletteId((self.0 & 0xFFFFFFu32) | ((kind as u32) << 24)) - } } /// The GPU data payload for a transform palette entry. @@ -774,11 +757,8 @@ pub enum UvRectKind { pub struct ImageSource { pub p0: DevicePoint, pub p1: DevicePoint, - // TODO: It appears that only glyphs make use of user_data (to store glyph offset - // and scale). - // Perhaps we should separate the two so we don't have to push an empty unused vec4 - // for all image sources. - pub user_data: [f32; 4], + pub texture_layer: f32, + pub user_data: [f32; 3], pub uv_rect_kind: UvRectKind, } @@ -792,7 +772,12 @@ impl ImageSource { self.p1.x, self.p1.y, ]); - request.push(self.user_data); + request.push([ + self.texture_layer, + self.user_data[0], + self.user_data[1], + self.user_data[2], + ]); // If this is a polygon uv kind, then upload the four vertices. if let UvRectKind::Quad { top_left, top_right, bottom_left, bottom_right } = self.uv_rect_kind { diff --git a/third_party/webrender/webrender/src/hit_test.rs b/third_party/webrender/webrender/src/hit_test.rs index 0bd02cd4269..b0a402b7ed1 100644 --- a/third_party/webrender/webrender/src/hit_test.rs +++ b/third_party/webrender/webrender/src/hit_test.rs @@ -2,18 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BorderRadius, ClipMode, HitTestItem, HitTestResult, ItemTag, PrimitiveFlags, HitTestFlags}; -use api::{PipelineId, ApiHitTester, ClipId}; +use api::{BorderRadius, ClipMode, HitTestFlags, HitTestItem, HitTestResult, ItemTag, PrimitiveFlags}; +use api::{PipelineId, ApiHitTester}; use api::units::*; -use crate::clip::{ClipItemKind, ClipStore, ClipNode, rounded_rectangle_contains_point}; -use crate::clip::polygon_contains_point; -use crate::prim_store::PolygonKey; -use crate::scene_builder_thread::Interners; +use crate::clip::{ClipChainId, ClipDataStore, ClipNode, ClipItemKind, ClipStore}; +use crate::clip::{rounded_rectangle_contains_point}; use crate::spatial_tree::{SpatialNodeIndex, SpatialTree}; -use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo}; -use std::ops; +use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo}; +use std::{ops, u32}; use std::sync::{Arc, Mutex}; -use crate::util::{LayoutToWorldFastTransform, VecHelper}; +use crate::util::LayoutToWorldFastTransform; pub struct SharedHitTester { // We don't really need a mutex here. We could do with some sort of @@ -45,7 +43,7 @@ impl ApiHitTester for SharedHitTester { fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint, - flags: HitTestFlags, + flags: HitTestFlags ) -> HitTestResult { self.get_ref().hit_test(HitTest::new(pipeline_id, point, flags)) } @@ -55,7 +53,7 @@ impl ApiHitTester for SharedHitTester { /// data from the SpatialTree that will persist as a new frame is under construction, /// allowing hit tests consistent with the currently rendered frame. #[derive(MallocSizeOf)] -struct HitTestSpatialNode { +pub struct HitTestSpatialNode { /// The pipeline id of this node. pipeline_id: PipelineId, @@ -70,20 +68,14 @@ struct HitTestSpatialNode { } #[derive(MallocSizeOf)] -struct HitTestClipNode { +pub struct HitTestClipNode { /// A particular point must be inside all of these regions to be considered clipped in /// for the purposes of a hit test. region: HitTestRegion, - /// The positioning node for this clip - spatial_node_index: SpatialNodeIndex, } impl HitTestClipNode { - fn new( - node: ClipNode, - spatial_node_index: SpatialNodeIndex, - interners: &Interners, - ) -> Self { + fn new(node: &ClipNode) -> Self { let region = match node.item.kind { ClipItemKind::Rectangle { rect, mode } => { HitTestRegion::Rectangle(rect, mode) @@ -91,42 +83,58 @@ impl HitTestClipNode { ClipItemKind::RoundedRectangle { rect, radius, mode } => { HitTestRegion::RoundedRectangle(rect, radius, mode) } - ClipItemKind::Image { rect, polygon_handle, .. } => { - if let Some(handle) = polygon_handle { - // Retrieve the polygon data from the interner. - let polygon = &interners.polygon[handle]; - HitTestRegion::Polygon(rect, *polygon) - } else { - HitTestRegion::Rectangle(rect, ClipMode::Clip) - } + ClipItemKind::Image { rect, .. } => { + HitTestRegion::Rectangle(rect, ClipMode::Clip) } ClipItemKind::BoxShadow { .. } => HitTestRegion::Invalid, }; HitTestClipNode { region, - spatial_node_index, } } } +#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq, Eq, Hash)] +pub struct HitTestClipChainId(u32); + +impl HitTestClipChainId { + pub const NONE: Self = HitTestClipChainId(u32::MAX); +} + +/// A hit testing clip chain node is the same as a +/// normal clip chain node, except that the clip +/// node is embedded inside the clip chain, rather +/// than referenced. This means we don't need to +/// copy the complete interned clip data store for +/// hit testing. +#[derive(MallocSizeOf)] +pub struct HitTestClipChainNode { + pub region: HitTestClipNode, + pub spatial_node_index: SpatialNodeIndex, + pub parent_clip_chain_id: HitTestClipChainId, +} + +#[derive(Copy, Clone, Debug, MallocSizeOf)] +pub struct HitTestingClipChainIndex(u32); + #[derive(Clone, MallocSizeOf)] -struct HitTestingItem { +pub struct HitTestingItem { rect: LayoutRect, clip_rect: LayoutRect, tag: ItemTag, is_backface_visible: bool, + #[ignore_malloc_size_of = "simple"] + clip_chain_range: ops::Range<HitTestingClipChainIndex>, spatial_node_index: SpatialNodeIndex, - #[ignore_malloc_size_of = "Range"] - clip_nodes_range: ops::Range<ClipNodeIndex>, } impl HitTestingItem { - fn new( + pub fn new( tag: ItemTag, info: &LayoutPrimitiveInfo, spatial_node_index: SpatialNodeIndex, - clip_nodes_range: ops::Range<ClipNodeIndex>, + clip_chain_range: ops::Range<HitTestingClipChainIndex>, ) -> HitTestingItem { HitTestingItem { rect: info.rect, @@ -134,7 +142,7 @@ impl HitTestingItem { tag, is_backface_visible: info.flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE), spatial_node_index, - clip_nodes_range, + clip_chain_range, } } } @@ -142,22 +150,19 @@ impl HitTestingItem { /// Statistics about allocation sizes of current hit tester, /// used to pre-allocate size of the next hit tester. pub struct HitTestingSceneStats { - pub clip_nodes_count: usize, + pub clip_chain_roots_count: usize, pub items_count: usize, } impl HitTestingSceneStats { pub fn empty() -> Self { HitTestingSceneStats { - clip_nodes_count: 0, + clip_chain_roots_count: 0, items_count: 0, } } } -#[derive(MallocSizeOf, Debug, Copy, Clone)] -pub struct ClipNodeIndex(u32); - /// Defines the immutable part of a hit tester for a given scene. /// The hit tester is recreated each time a frame is built, since /// it relies on the current values of the spatial tree. @@ -166,25 +171,11 @@ pub struct ClipNodeIndex(u32); /// hit tester instances via Arc. #[derive(MallocSizeOf)] pub struct HitTestingScene { - /// Packed array of all hit test clip nodes - clip_nodes: Vec<HitTestClipNode>, + /// The list of variable clip chain roots referenced by the items. + pub clip_chain_roots: Vec<HitTestClipChainId>, /// List of hit testing primitives. - items: Vec<HitTestingItem>, - - /// Current stack of clip ids from stacking context - #[ignore_malloc_size_of = "ClipId"] - clip_id_stack: Vec<ClipId>, - - /// Last cached clip id, useful for scenes with a lot - /// of hit-test items that reference the same clip - #[ignore_malloc_size_of = "simple"] - cached_clip_id: Option<(ClipId, ops::Range<ClipNodeIndex>)>, - - /// Temporary buffer used to de-duplicate clip ids when creating hit - /// test clip nodes. - #[ignore_malloc_size_of = "ClipId"] - seen_clips: FastHashSet<ClipId>, + pub items: Vec<HitTestingItem>, } impl HitTestingScene { @@ -192,104 +183,39 @@ impl HitTestingScene { /// provided by previous scene stats. pub fn new(stats: &HitTestingSceneStats) -> Self { HitTestingScene { - clip_nodes: Vec::with_capacity(stats.clip_nodes_count), + clip_chain_roots: Vec::with_capacity(stats.clip_chain_roots_count), items: Vec::with_capacity(stats.items_count), - clip_id_stack: Vec::with_capacity(8), - cached_clip_id: None, - seen_clips: FastHashSet::default(), } } /// Get stats about the current scene allocation sizes. pub fn get_stats(&self) -> HitTestingSceneStats { HitTestingSceneStats { - clip_nodes_count: self.clip_nodes.len(), + clip_chain_roots_count: self.clip_chain_roots.len(), items_count: self.items.len(), } } /// Add a hit testing primitive. - pub fn add_item( - &mut self, - tag: ItemTag, - info: &LayoutPrimitiveInfo, - spatial_node_index: SpatialNodeIndex, - clip_id: ClipId, - clip_store: &ClipStore, - interners: &Interners, - ) { - let clip_range = match self.cached_clip_id { - Some((cached_clip_id, ref range)) if cached_clip_id == clip_id => { - range.clone() - } - Some(_) | None => { - let start = ClipNodeIndex(self.clip_nodes.len() as u32); - - // Clear the set of which clip ids have been encountered for this item - self.seen_clips.clear(); - - // Flatten all clips from the stacking context hierarchy - for clip_id in &self.clip_id_stack { - add_clips( - *clip_id, - clip_store, - &mut self.clip_nodes, - &mut self.seen_clips, - interners, - ); - } - - // Add the primitive clip - add_clips( - clip_id, - clip_store, - &mut self.clip_nodes, - &mut self.seen_clips, - interners, - ); - - let end = ClipNodeIndex(self.clip_nodes.len() as u32); - - let range = ops::Range { - start, - end, - }; - - self.cached_clip_id = Some((clip_id, range.clone())); - - range - } - }; - - let item = HitTestingItem::new( - tag, - info, - spatial_node_index, - clip_range, - ); - + pub fn add_item(&mut self, item: HitTestingItem) { self.items.push(item); } - /// Push a clip onto the current stack - pub fn push_clip( - &mut self, - clip_id: ClipId, - ) { - // Invalidate the cache since the stack may affect the produced hit test clip struct - self.cached_clip_id = None; - - self.clip_id_stack.push(clip_id); + /// Add a clip chain to the clip chain roots list. + pub fn add_clip_chain(&mut self, clip_chain_id: ClipChainId) { + if clip_chain_id != ClipChainId::INVALID { + self.clip_chain_roots.push(HitTestClipChainId(clip_chain_id.0)); + } } - /// Pop a clip from the current stack - pub fn pop_clip( - &mut self, - ) { - // Invalidate the cache since the stack may affect the produced hit test clip struct - self.cached_clip_id = None; + /// Get the slice of clip chain roots for a given hit test primitive. + fn get_clip_chains_for_item(&self, item: &HitTestingItem) -> &[HitTestClipChainId] { + &self.clip_chain_roots[item.clip_chain_range.start.0 as usize .. item.clip_chain_range.end.0 as usize] + } - self.clip_id_stack.pop().unwrap(); + /// Get the next index of the clip chain roots list. + pub fn next_clip_chain_index(&self) -> HitTestingClipChainIndex { + HitTestingClipChainIndex(self.clip_chain_roots.len() as u32) } } @@ -298,11 +224,10 @@ enum HitTestRegion { Invalid, Rectangle(LayoutRect, ClipMode), RoundedRectangle(LayoutRect, BorderRadius, ClipMode), - Polygon(LayoutRect, PolygonKey), } impl HitTestRegion { - fn contains(&self, point: &LayoutPoint) -> bool { + pub fn contains(&self, point: &LayoutPoint) -> bool { match *self { HitTestRegion::Rectangle(ref rectangle, ClipMode::Clip) => rectangle.contains(*point), @@ -312,8 +237,6 @@ impl HitTestRegion { rounded_rectangle_contains_point(point, &rect, &radii), HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) => !rounded_rectangle_contains_point(point, &rect, &radii), - HitTestRegion::Polygon(rect, polygon) => - polygon_contains_point(point, &rect, &polygon), HitTestRegion::Invalid => true, } } @@ -324,6 +247,7 @@ pub struct HitTester { #[ignore_malloc_size_of = "Arc"] scene: Arc<HitTestingScene>, spatial_nodes: Vec<HitTestSpatialNode>, + clip_chains: Vec<HitTestClipChainNode>, pipeline_root_nodes: FastHashMap<PipelineId, SpatialNodeIndex>, } @@ -332,6 +256,7 @@ impl HitTester { HitTester { scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())), spatial_nodes: Vec::new(), + clip_chains: Vec::new(), pipeline_root_nodes: FastHashMap::default(), } } @@ -339,24 +264,33 @@ impl HitTester { pub fn new( scene: Arc<HitTestingScene>, spatial_tree: &SpatialTree, + clip_store: &ClipStore, + clip_data_store: &ClipDataStore, ) -> HitTester { let mut hit_tester = HitTester { scene, spatial_nodes: Vec::new(), + clip_chains: Vec::new(), pipeline_root_nodes: FastHashMap::default(), }; - hit_tester.read_spatial_tree(spatial_tree); + hit_tester.read_spatial_tree( + spatial_tree, + clip_store, + clip_data_store, + ); hit_tester } fn read_spatial_tree( &mut self, spatial_tree: &SpatialTree, + clip_store: &ClipStore, + clip_data_store: &ClipDataStore, ) { self.spatial_nodes.clear(); + self.clip_chains.clear(); self.spatial_nodes.reserve(spatial_tree.spatial_nodes.len()); - self.pipeline_root_nodes.clear(); for (index, node) in spatial_tree.spatial_nodes.iter().enumerate() { let index = SpatialNodeIndex::new(index); @@ -379,13 +313,151 @@ impl HitTester { external_scroll_offset: spatial_tree.external_scroll_offset(index), }); } + + // For each clip chain node, extract the clip node from the clip + // data store, and store it inline with the clip chain node. + self.clip_chains.reserve(clip_store.clip_chain_nodes.len()); + for node in &clip_store.clip_chain_nodes { + let clip_node = &clip_data_store[node.handle]; + self.clip_chains.push(HitTestClipChainNode { + region: HitTestClipNode::new(clip_node), + spatial_node_index: node.spatial_node_index, + parent_clip_chain_id: HitTestClipChainId(node.parent_clip_chain_id.0), + }); + } + } + + fn is_point_clipped_in_for_clip_chain( + &self, + point: WorldPoint, + clip_chain_id: HitTestClipChainId, + test: &mut HitTest + ) -> bool { + if clip_chain_id == HitTestClipChainId::NONE { + return true; + } + + if let Some(result) = test.get_from_clip_chain_cache(clip_chain_id) { + return result == ClippedIn::ClippedIn; + } + + let descriptor = &self.clip_chains[clip_chain_id.0 as usize]; + let parent_clipped_in = self.is_point_clipped_in_for_clip_chain( + point, + descriptor.parent_clip_chain_id, + test, + ); + + if !parent_clipped_in { + test.set_in_clip_chain_cache(clip_chain_id, ClippedIn::NotClippedIn); + return false; + } + + if !self.is_point_clipped_in_for_clip_node( + point, + clip_chain_id, + descriptor.spatial_node_index, + test, + ) { + test.set_in_clip_chain_cache(clip_chain_id, ClippedIn::NotClippedIn); + return false; + } + + test.set_in_clip_chain_cache(clip_chain_id, ClippedIn::ClippedIn); + true + } + + fn is_point_clipped_in_for_clip_node( + &self, + point: WorldPoint, + clip_chain_node_id: HitTestClipChainId, + spatial_node_index: SpatialNodeIndex, + test: &mut HitTest + ) -> bool { + if let Some(clipped_in) = test.node_cache.get(&clip_chain_node_id) { + return *clipped_in == ClippedIn::ClippedIn; + } + + let node = &self.clip_chains[clip_chain_node_id.0 as usize].region; + let transform = self + .spatial_nodes[spatial_node_index.0 as usize] + .world_content_transform; + let transformed_point = match transform + .inverse() + .and_then(|inverted| inverted.transform_point2d(point)) + { + Some(point) => point, + None => { + test.node_cache.insert(clip_chain_node_id, ClippedIn::NotClippedIn); + return false; + } + }; + + if !node.region.contains(&transformed_point) { + test.node_cache.insert(clip_chain_node_id, ClippedIn::NotClippedIn); + return false; + } + + test.node_cache.insert(clip_chain_node_id, ClippedIn::ClippedIn); + true } - pub fn hit_test(&self, test: HitTest) -> HitTestResult { + pub fn find_node_under_point(&self, mut test: HitTest) -> Option<SpatialNodeIndex> { let point = test.get_absolute_point(self); + let mut current_spatial_node_index = SpatialNodeIndex::INVALID; + let mut point_in_layer = None; - let mut result = HitTestResult::default(); + // For each hit test primitive + for item in self.scene.items.iter().rev() { + let scroll_node = &self.spatial_nodes[item.spatial_node_index.0 as usize]; + + // Update the cached point in layer space, if the spatial node + // changed since last primitive. + if item.spatial_node_index != current_spatial_node_index { + point_in_layer = scroll_node + .world_content_transform + .inverse() + .and_then(|inverted| inverted.transform_point2d(point)); + + current_spatial_node_index = item.spatial_node_index; + } + + // Only consider hit tests on transformable layers. + if let Some(point_in_layer) = point_in_layer { + // If the item's rect or clip rect don't contain this point, + // it's not a valid hit. + if !item.rect.contains(point_in_layer) { + continue; + } + if !item.clip_rect.contains(point_in_layer) { + continue; + } + + // See if any of the clip chain roots for this primitive + // cull out the item. + let clip_chains = self.scene.get_clip_chains_for_item(item); + let mut is_valid = true; + for clip_chain_id in clip_chains { + if !self.is_point_clipped_in_for_clip_chain(point, *clip_chain_id, &mut test) { + is_valid = false; + break; + } + } + + // Found a valid hit test result! + if is_valid { + return Some(item.spatial_node_index); + } + } + } + None + } + + pub fn hit_test(&self, mut test: HitTest) -> HitTestResult { + let point = test.get_absolute_point(self); + + let mut result = HitTestResult::default(); let mut current_spatial_node_index = SpatialNodeIndex::INVALID; let mut point_in_layer = None; let mut current_root_spatial_node_index = SpatialNodeIndex::INVALID; @@ -421,23 +493,12 @@ impl HitTester { continue; } - // See if any of the clips for this primitive cull out the item. + // See if any of the clip chain roots for this primitive + // cull out the item. + let clip_chains = self.scene.get_clip_chains_for_item(item); let mut is_valid = true; - let clip_nodes = &self.scene.clip_nodes[item.clip_nodes_range.start.0 as usize .. item.clip_nodes_range.end.0 as usize]; - for clip_node in clip_nodes { - let transform = self - .spatial_nodes[clip_node.spatial_node_index.0 as usize] - .world_content_transform; - let transformed_point = match transform - .inverse() - .and_then(|inverted| inverted.transform_point2d(point)) - { - Some(point) => point, - None => { - continue; - } - }; - if !clip_node.region.contains(&transformed_point) { + for clip_chain_id in clip_chains { + if !self.is_point_clipped_in_for_clip_chain(point, *clip_chain_id, &mut test) { is_valid = false; break; } @@ -486,10 +547,15 @@ impl HitTester { result } - fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode { + pub fn get_pipeline_root(&self, pipeline_id: PipelineId) -> &HitTestSpatialNode { &self.spatial_nodes[self.pipeline_root_nodes[&pipeline_id].0 as usize] } +} +#[derive(Clone, Copy, MallocSizeOf, PartialEq)] +enum ClippedIn { + ClippedIn, + NotClippedIn, } #[derive(MallocSizeOf)] @@ -497,6 +563,8 @@ pub struct HitTest { pipeline_id: Option<PipelineId>, point: WorldPoint, flags: HitTestFlags, + node_cache: FastHashMap<HitTestClipChainId, ClippedIn>, + clip_chain_cache: Vec<Option<ClippedIn>>, } impl HitTest { @@ -508,8 +576,27 @@ impl HitTest { HitTest { pipeline_id, point, - flags + flags, + node_cache: FastHashMap::default(), + clip_chain_cache: Vec::new(), + } + } + + fn get_from_clip_chain_cache(&mut self, index: HitTestClipChainId) -> Option<ClippedIn> { + let index = index.0 as usize; + if index >= self.clip_chain_cache.len() { + None + } else { + self.clip_chain_cache[index] + } + } + + fn set_in_clip_chain_cache(&mut self, index: HitTestClipChainId, value: ClippedIn) { + let index = index.0 as usize; + if index >= self.clip_chain_cache.len() { + self.clip_chain_cache.resize(index + 1, None); } + self.clip_chain_cache[index] = Some(value); } fn get_absolute_point(&self, hit_tester: &HitTester) -> WorldPoint { @@ -529,45 +616,4 @@ impl HitTest { WorldPoint::new(self.point.x, self.point.y) }) } - -} - -/// Collect clips for a given ClipId, convert and add them to the hit testing -/// scene, if not already present. -fn add_clips( - clip_id: ClipId, - clip_store: &ClipStore, - clip_nodes: &mut Vec<HitTestClipNode>, - seen_clips: &mut FastHashSet<ClipId>, - interners: &Interners, -) { - // If this clip-id has already been added to this hit-test item, skip it - if seen_clips.contains(&clip_id) { - return; - } - seen_clips.insert(clip_id); - - let template = &clip_store.templates[&clip_id]; - let instances = &clip_store.instances[template.clips.start as usize .. template.clips.end as usize]; - - for clip in instances { - clip_nodes.alloc().init( - HitTestClipNode::new( - clip.key.into(), - clip.clip.spatial_node_index, - interners, - ) - ); - } - - // The ClipId parenting is terminated when we reach the root ClipId - if clip_id != template.parent { - add_clips( - template.parent, - clip_store, - clip_nodes, - seen_clips, - interners, - ); - } } diff --git a/third_party/webrender/webrender/src/host_utils.rs b/third_party/webrender/webrender/src/host_utils.rs deleted file mode 100644 index 675680f766c..00000000000 --- a/third_party/webrender/webrender/src/host_utils.rs +++ /dev/null @@ -1,26 +0,0 @@ -#[cfg(feature = "gecko")] -mod utils { - use std::ffi::CString; - extern "C" { - fn gecko_profiler_register_thread(name: *const ::std::os::raw::c_char); - fn gecko_profiler_unregister_thread(); - } - pub fn thread_started(thread_name: &str) { - let name = CString::new(thread_name).unwrap(); - unsafe { - // gecko_profiler_register_thread copies the passed name here. - gecko_profiler_register_thread(name.as_ptr()); - } - } - pub fn thread_stopped() { - unsafe { gecko_profiler_unregister_thread(); } - } -} - -#[cfg(not(feature = "gecko"))] -mod utils { - pub fn thread_started(_: &str) { } - pub fn thread_stopped() { } -} - -pub use utils::*; diff --git a/third_party/webrender/webrender/src/image_source.rs b/third_party/webrender/webrender/src/image_source.rs deleted file mode 100644 index 6b6533494a4..00000000000 --- a/third_party/webrender/webrender/src/image_source.rs +++ /dev/null @@ -1,94 +0,0 @@ -/* 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/. */ - -//! This module contains the logic to obtain a primitive's source texture and uv rect. -//! -//! Currently this is a somewhat involved process because the code grew into having ad-hoc -//! ways to store this information depending on how the image data is produced. The goal -//! is for any textured primitive to be able to read from any source (texture cache, render -//! tasks, etc.) without primitive-specific code. - -use crate::api::ExternalImageType; -use crate::api::units::*; -use crate::gpu_cache::GpuCache; -use crate::prim_store::DeferredResolve; -use crate::renderer::BLOCKS_PER_UV_RECT; -use crate::render_task_cache::RenderTaskCacheEntryHandle; -use crate::resource_cache::{ResourceCache, ImageRequest, CacheItem}; -use crate::internal_types::{TextureSource, DeferredResolveIndex}; - -/// Resolve a resource cache's imagre request into a texture cache item. -pub fn resolve_image( - request: ImageRequest, - resource_cache: &ResourceCache, - gpu_cache: &mut GpuCache, - deferred_resolves: &mut Vec<DeferredResolve>, -) -> CacheItem { - match resource_cache.get_image_properties(request.key) { - Some(image_properties) => { - // Check if an external image that needs to be resolved - // by the render thread. - match image_properties.external_image { - Some(external_image) => { - // This is an external texture - we will add it to - // the deferred resolves list to be patched by - // the render thread... - let cache_handle = gpu_cache.push_deferred_per_frame_blocks(BLOCKS_PER_UV_RECT); - - let deferred_resolve_index = DeferredResolveIndex(deferred_resolves.len() as u32); - - let image_buffer_kind = match external_image.image_type { - ExternalImageType::TextureHandle(target) => { - target - } - ExternalImageType::Buffer => { - // The ExternalImageType::Buffer should be handled by resource_cache. - // It should go through the non-external case. - panic!("Unexpected non-texture handle type"); - } - }; - - let cache_item = CacheItem { - texture_id: TextureSource::External(deferred_resolve_index, image_buffer_kind), - uv_rect_handle: cache_handle, - uv_rect: DeviceIntRect::new( - DeviceIntPoint::zero(), - image_properties.descriptor.size, - ), - user_data: [0.0; 4], - }; - - deferred_resolves.push(DeferredResolve { - image_properties, - address: gpu_cache.get_address(&cache_handle), - rendering: request.rendering, - }); - - cache_item - } - None => { - if let Ok(cache_item) = resource_cache.get_cached_image(request) { - cache_item - } else { - // There is no usable texture entry for the image key. Just return an invalid texture here. - CacheItem::invalid() - } - } - } - } - None => { - CacheItem::invalid() - } - } -} - -pub fn resolve_cached_render_task( - handle: &RenderTaskCacheEntryHandle, - resource_cache: &ResourceCache, -) -> CacheItem { - let rt_cache_entry = resource_cache - .get_cached_render_task(&handle); - - resource_cache.get_texture_cache_item(&rt_cache_entry.handle) -} diff --git a/third_party/webrender/webrender/src/intern.rs b/third_party/webrender/webrender/src/intern.rs index 9ba7aa75100..db7fd0c1c9d 100644 --- a/third_party/webrender/webrender/src/intern.rs +++ b/third_party/webrender/webrender/src/intern.rs @@ -35,17 +35,18 @@ use crate::internal_types::FastHashMap; use malloc_size_of::MallocSizeOf; +use crate::profiler::ResourceProfileCounter; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; -use std::{ops, u64}; +use std::{mem, ops, u64}; +use std::sync::atomic::{AtomicUsize, Ordering}; use crate::util::VecHelper; -use crate::profiler::TransactionProfile; #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Copy, Clone, Hash, MallocSizeOf, PartialEq, Eq)] -struct Epoch(u32); +#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)] +struct Epoch(u64); /// A list of updates to be applied to the data store, /// provided by the interning structure. @@ -93,27 +94,37 @@ impl<S> UpdateList<S> { } } +lazy_static! { + static ref NEXT_UID: AtomicUsize = AtomicUsize::new(0); +} + /// A globally, unique identifier #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Debug, Copy, Clone, Eq, Hash, MallocSizeOf, PartialEq)] pub struct ItemUid { - uid: u64, + uid: usize, } impl ItemUid { + pub fn next_uid() -> ItemUid { + let uid = NEXT_UID.fetch_add(1, Ordering::Relaxed); + ItemUid { uid } + } + // Intended for debug usage only - pub fn get_uid(&self) -> u64 { + pub fn get_uid(&self) -> usize { self.uid } } #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Hash, MallocSizeOf, PartialEq, Eq)] +#[derive(Debug, MallocSizeOf)] pub struct Handle<I> { index: u32, epoch: Epoch, + uid: ItemUid, _marker: PhantomData<I>, } @@ -122,6 +133,7 @@ impl<I> Clone for Handle<I> { Handle { index: self.index, epoch: self.epoch, + uid: self.uid, _marker: self._marker, } } @@ -131,11 +143,7 @@ impl<I> Copy for Handle<I> {} impl<I> Handle<I> { pub fn uid(&self) -> ItemUid { - ItemUid { - // The index in the freelist + the epoch it was interned generates a stable - // unique id for an interned element. - uid: ((self.index as u64) << 32) | self.epoch.0 as u64 - } + self.uid } } @@ -166,7 +174,7 @@ impl<I: Internable> DataStore<I> { pub fn apply_updates( &mut self, update_list: UpdateList<I::Key>, - profile: &mut TransactionProfile, + profile_counter: &mut ResourceProfileCounter, ) { for insertion in update_list.insertions { self.items @@ -178,7 +186,8 @@ impl<I: Internable> DataStore<I> { self.items[removal.index] = None; } - profile.set(I::PROFILE_COUNTER, self.items.len()); + let per_item_size = mem::size_of::<I::Key>() + mem::size_of::<I::StoreData>(); + profile_counter.set(self.items.len(), per_item_size * self.items.len()); } } @@ -198,31 +207,6 @@ impl<I: Internable> ops::IndexMut<Handle<I>> for DataStore<I> { } } -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] -struct ItemDetails<I> { - /// Frame that this element was first interned - interned_epoch: Epoch, - /// Last frame this element was referenced (used to GC intern items) - last_used_epoch: Epoch, - /// Index into the freelist this item is located - index: usize, - /// Type marker for create_handle method - _marker: PhantomData<I>, -} - -impl<I> ItemDetails<I> { - /// Construct a stable handle value from the item details - fn create_handle(&self) -> Handle<I> { - Handle { - index: self.index as u32, - epoch: self.interned_epoch, - _marker: PhantomData, - } - } -} - /// The main interning data structure. This lives in the /// scene builder thread, and handles hashing and interning /// unique data structures. It also manages a free-list for @@ -233,7 +217,7 @@ impl<I> ItemDetails<I> { #[derive(MallocSizeOf)] pub struct Interner<I: Internable> { /// Uniquely map an interning key to a handle - map: FastHashMap<I::Key, ItemDetails<I>>, + map: FastHashMap<I::Key, Handle<I>>, /// List of free slots in the data store for re-use. free_list: Vec<usize>, /// Pending list of updates that need to be applied. @@ -273,11 +257,9 @@ impl<I: Internable> Interner<I> { // Use get_mut rather than entry here to avoid // cloning the (sometimes large) key in the common // case, where the data already exists in the interner. - if let Some(details) = self.map.get_mut(data) { - // Update the last referenced frame for this element - details.last_used_epoch = self.current_epoch; - // Return a stable handle value for dependency checking - return details.create_handle(); + if let Some(handle) = self.map.get_mut(data) { + handle.epoch = self.current_epoch; + return *handle; } // We need to intern a new data item. First, find out @@ -288,14 +270,7 @@ impl<I: Internable> Interner<I> { None => self.local_data.len(), }; - // Generate a handle for access via the data store. - let handle = Handle { - index: index as u32, - epoch: self.current_epoch, - _marker: PhantomData, - }; - - let uid = handle.uid(); + let uid = ItemUid::next_uid(); // Add a pending update to insert the new data. self.update_list.insertions.push(Insertion { @@ -304,17 +279,20 @@ impl<I: Internable> Interner<I> { value: data.clone(), }); + // Generate a handle for access via the data store. + let handle = Handle { + index: index as u32, + epoch: self.current_epoch, + uid, + _marker: PhantomData, + }; + #[cfg(debug_assertions)] - data.on_interned(uid); + data.on_interned(handle.uid); // Store this handle so the next time it is // interned, it gets re-used. - self.map.insert(data.clone(), ItemDetails { - interned_epoch: self.current_epoch, - last_used_epoch: self.current_epoch, - index, - _marker: PhantomData, - }); + self.map.insert(data.clone(), handle); // Create the local data for this item that is // being interned. @@ -339,16 +317,16 @@ impl<I: Internable> Interner<I> { // map each frame). It also might make sense in the // future to adjust how long items remain in the cache // based on the current size of the list. - self.map.retain(|_, details| { - if details.last_used_epoch.0 + 10 < current_epoch { + self.map.retain(|_, handle| { + if handle.epoch.0 + 10 < current_epoch { // To expire an item: // - Add index to the free-list for re-use. // - Add an update to the data store to invalidate this slot. // - Remove from the hash map. - free_list.push(details.index); + free_list.push(handle.index as usize); update_list.removals.push(Removal { - index: details.index, - uid: details.create_handle().uid(), + index: handle.index as usize, + uid: handle.uid, }); return false; } @@ -371,70 +349,6 @@ impl<I: Internable> ops::Index<Handle<I>> for Interner<I> { } } -/// Meta-macro to enumerate the various interner identifiers and types. -/// -/// IMPORTANT: Keep this synchronized with the list in mozilla-central located at -/// gfx/webrender_bindings/webrender_ffi.h -/// -/// Note that this could be a lot less verbose if concat_idents! were stable. :-( -#[macro_export] -macro_rules! enumerate_interners { - ($macro_name: ident) => { - $macro_name! { - clip: ClipIntern, - prim: PrimitiveKeyKind, - normal_border: NormalBorderPrim, - image_border: ImageBorder, - image: Image, - yuv_image: YuvImage, - line_decoration: LineDecoration, - linear_grad: LinearGradient, - radial_grad: RadialGradient, - conic_grad: ConicGradient, - picture: Picture, - text_run: TextRun, - filter_data: FilterDataIntern, - backdrop: Backdrop, - polygon: PolygonIntern, - } - } -} - -macro_rules! declare_interning_memory_report { - ( $( $name:ident: $ty:ident, )+ ) => { - /// - #[repr(C)] - #[derive(AddAssign, Clone, Debug, Default)] - pub struct InternerSubReport { - $( - /// - pub $name: usize, - )+ - } - } -} - -enumerate_interners!(declare_interning_memory_report); - -/// Memory report for interning-related data structures. -/// cbindgen:derive-eq=false -/// cbindgen:derive-ostream=false -#[repr(C)] -#[derive(Clone, Debug, Default)] -pub struct InterningMemoryReport { - /// - pub interners: InternerSubReport, - /// - pub data_stores: InternerSubReport, -} - -impl ::std::ops::AddAssign for InterningMemoryReport { - fn add_assign(&mut self, other: InterningMemoryReport) { - self.interners += other.interners; - self.data_stores += other.data_stores; - } -} - // The trick to make trait bounds configurable by features. mod dummy { #[cfg(not(feature = "capture"))] @@ -460,7 +374,4 @@ pub trait Internable: MallocSizeOf { type Key: Eq + Hash + Clone + Debug + MallocSizeOf + InternDebug + InternSerialize + for<'a> InternDeserialize<'a>; type StoreData: From<Self::Key> + MallocSizeOf + InternSerialize + for<'a> InternDeserialize<'a>; type InternData: MallocSizeOf + InternSerialize + for<'a> InternDeserialize<'a>; - - // Profile counter indices, see the list in profiler.rs - const PROFILE_COUNTER: usize; } diff --git a/third_party/webrender/webrender/src/internal_types.rs b/third_party/webrender/webrender/src/internal_types.rs index 9819850db0b..321afd22216 100644 --- a/third_party/webrender/webrender/src/internal_types.rs +++ b/third_party/webrender/webrender/src/internal_types.rs @@ -2,19 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ColorF, DocumentId, ExternalImageId, PrimitiveFlags}; -use api::{ImageFormat, NotificationRequest, Shadow, FilterOp, ImageBufferKind}; +use api::{ColorF, DebugCommand, DocumentId, ExternalImageData, ExternalImageId, PrimitiveFlags}; +use api::{ImageFormat, ItemTag, NotificationRequest, Shadow, FilterOp}; use api::units::*; use api; -use crate::render_api::DebugCommand; use crate::composite::NativeSurfaceOperation; use crate::device::TextureFilter; -use crate::renderer::{FullFrameStats, PipelineInfo}; +use crate::renderer::PipelineInfo; use crate::gpu_cache::GpuCacheUpdateList; use crate::frame_builder::Frame; -use crate::profiler::TransactionProfile; use fxhash::FxHasher; use plane_split::BspSplitter; +use crate::profiler::BackendProfileCounters; use smallvec::SmallVec; use std::{usize, i32}; use std::collections::{HashMap, HashSet}; @@ -71,7 +70,7 @@ const OPACITY_EPSILON: f32 = 0.001; #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum Filter { Identity, - Blur(f32, f32), + Blur(f32), Brightness(f32), Contrast(f32), Grayscale(f32), @@ -117,13 +116,13 @@ impl Filter { pub fn is_noop(&self) -> bool { match *self { Filter::Identity => false, // this is intentional - Filter::Blur(width, height) => width == 0.0 && height == 0.0, + Filter::Blur(length) => length == 0.0, Filter::Brightness(amount) => amount == 1.0, Filter::Contrast(amount) => amount == 1.0, Filter::Grayscale(amount) => amount == 0.0, Filter::HueRotate(amount) => amount == 0.0, Filter::Invert(amount) => amount == 0.0, - Filter::Opacity(api::PropertyBinding::Value(amount), _) => amount >= 1.0, + Filter::Opacity(_, amount) => amount >= 1.0, Filter::Saturate(amount) => amount == 1.0, Filter::Sepia(amount) => amount == 0.0, Filter::DropShadows(ref shadows) => { @@ -144,7 +143,6 @@ impl Filter { 0.0, 0.0, 0.0, 0.0 ] } - Filter::Opacity(api::PropertyBinding::Binding(..), _) | Filter::SrgbToLinear | Filter::LinearToSrgb | Filter::ComponentTransfer | @@ -180,7 +178,7 @@ impl From<FilterOp> for Filter { fn from(op: FilterOp) -> Self { match op { FilterOp::Identity => Filter::Identity, - FilterOp::Blur(w, h) => Filter::Blur(w, h), + FilterOp::Blur(r) => Filter::Blur(r), FilterOp::Brightness(b) => Filter::Brightness(b), FilterOp::Contrast(c) => Filter::Contrast(c), FilterOp::Grayscale(g) => Filter::Grayscale(g), @@ -233,16 +231,35 @@ pub struct SwizzleSettings { #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct CacheTextureId(pub u32); +pub struct CacheTextureId(pub u64); -impl CacheTextureId { - pub const INVALID: CacheTextureId = CacheTextureId(!0); -} +/// Canonical type for texture layer indices. +/// +/// WebRender is currently not very consistent about layer index types. Some +/// places use i32 (since that's the type used in various OpenGL APIs), some +/// places use u32 (since having it be signed is non-sensical, but the +/// underlying graphics APIs generally operate on 32-bit integers) and some +/// places use usize (since that's most natural in Rust). +/// +/// Going forward, we aim to us usize throughout the codebase, since that allows +/// operations like indexing without a cast, and convert to the required type in +/// the device module when making calls into the platform layer. +pub type LayerIndex = usize; -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +/// Identifies a render pass target that is persisted until the end of the frame. +/// +/// By default, only the targets of the immediately-preceding pass are bound as +/// inputs to the next pass. However, tasks can opt into having their target +/// preserved in a list until the end of the frame, and this type specifies the +/// index in that list. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct DeferredResolveIndex(pub u32); +pub struct SavedTargetIndex(pub usize); + +impl SavedTargetIndex { + pub const PENDING: Self = SavedTargetIndex(!0); +} /// Identifies the source of an input texture to a shader. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] @@ -254,37 +271,20 @@ pub enum TextureSource { /// An entry in the texture cache. TextureCache(CacheTextureId, Swizzle), /// An external image texture, mananged by the embedding. - External(DeferredResolveIndex, ImageBufferKind), + External(ExternalImageData), + /// The alpha target of the immediately-preceding pass. + PrevPassAlpha, + /// The color target of the immediately-preceding pass. + PrevPassColor, + /// A render target from an earlier pass. Unlike the immediately-preceding + /// passes, these are not made available automatically, but are instead + /// opt-in by the `RenderTask` (see `mark_for_saving()`). + RenderTaskCache(SavedTargetIndex, Swizzle), /// Select a dummy 1x1 white texture. This can be used by image /// shaders that want to draw a solid color. Dummy, } -impl TextureSource { - pub fn image_buffer_kind(&self) -> ImageBufferKind { - match *self { - TextureSource::TextureCache(..) => ImageBufferKind::Texture2D, - - TextureSource::External(_, image_buffer_kind) => image_buffer_kind, - - // Render tasks use texture arrays for now. - TextureSource::Dummy => ImageBufferKind::Texture2D, - - TextureSource::Invalid => ImageBufferKind::Texture2D, - } - } - - #[inline] - pub fn is_compatible( - &self, - other: &TextureSource, - ) -> bool { - *self == TextureSource::Invalid || - *other == TextureSource::Invalid || - self == other - } -} - #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -314,13 +314,13 @@ pub struct TextureCacheAllocation { } /// Information used when allocating / reallocating. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Debug)] pub struct TextureCacheAllocInfo { pub width: i32, pub height: i32, + pub layer_count: i32, pub format: ImageFormat, pub filter: TextureFilter, - pub target: ImageBufferKind, /// Indicates whether this corresponds to one of the shared texture caches. pub is_shared_cache: bool, /// If true, this texture requires a depth target. @@ -332,6 +332,10 @@ pub struct TextureCacheAllocInfo { pub enum TextureCacheAllocationKind { /// Performs an initial texture allocation. Alloc(TextureCacheAllocInfo), + /// Reallocates the texture. The existing live texture with the same id + /// will be deallocated and its contents blitted over. The new size must + /// be greater than the old size. + Realloc(TextureCacheAllocInfo), /// Reallocates the texture without preserving its contents. Reset(TextureCacheAllocInfo), /// Frees the texture and the corresponding cache ID. @@ -344,6 +348,7 @@ pub struct TextureCacheUpdate { pub rect: DeviceIntRect, pub stride: Option<i32>, pub offset: i32, + pub layer_index: i32, pub format_override: Option<ImageFormat>, pub source: TextureUpdateSource, } @@ -403,6 +408,7 @@ impl TextureUpdateList { origin: DeviceIntPoint, width: i32, height: i32, + layer_index: usize ) { let size = DeviceIntSize::new(width, height); let rect = DeviceIntRect::new(origin, size); @@ -410,6 +416,7 @@ impl TextureUpdateList { rect, stride: None, offset: 0, + layer_index: layer_index as i32, format_override: None, source: TextureUpdateSource::DebugClear, }); @@ -427,11 +434,30 @@ impl TextureUpdateList { /// Pushes a reallocation operation onto the list, potentially coalescing /// with previous operations. - pub fn push_reset(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) { + pub fn push_realloc(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) { self.debug_assert_coalesced(id); - // Drop any unapplied updates to the to-be-freed texture. - self.updates.remove(&id); + // Coallesce this realloc into a previous alloc or realloc, if available. + if let Some(cur) = self.allocations.iter_mut().find(|x| x.id == id) { + match cur.kind { + TextureCacheAllocationKind::Alloc(ref mut i) => *i = info, + TextureCacheAllocationKind::Realloc(ref mut i) => *i = info, + TextureCacheAllocationKind::Reset(ref mut i) => *i = info, + TextureCacheAllocationKind::Free => panic!("Reallocating freed texture"), + } + return + } + + self.allocations.push(TextureCacheAllocation { + id, + kind: TextureCacheAllocationKind::Realloc(info), + }); + } + + /// Pushes a reallocation operation onto the list, potentially coalescing + /// with previous operations. + pub fn push_reset(&mut self, id: CacheTextureId, info: TextureCacheAllocInfo) { + self.debug_assert_coalesced(id); // Coallesce this realloc into a previous alloc or realloc, if available. if let Some(cur) = self.allocations.iter_mut().find(|x| x.id == id) { @@ -439,6 +465,10 @@ impl TextureUpdateList { TextureCacheAllocationKind::Alloc(ref mut i) => *i = info, TextureCacheAllocationKind::Reset(ref mut i) => *i = info, TextureCacheAllocationKind::Free => panic!("Resetting freed texture"), + TextureCacheAllocationKind::Realloc(_) => { + // Reset takes precedence over realloc + cur.kind = TextureCacheAllocationKind::Reset(info); + } } return } @@ -464,6 +494,7 @@ impl TextureUpdateList { match removed_kind { Some(TextureCacheAllocationKind::Alloc(..)) => { /* no-op! */ }, Some(TextureCacheAllocationKind::Free) => panic!("Double free"), + Some(TextureCacheAllocationKind::Realloc(..)) | Some(TextureCacheAllocationKind::Reset(..)) | None => { self.allocations.push(TextureCacheAllocation { @@ -503,11 +534,11 @@ impl ResourceUpdateList { pub struct RenderedDocument { pub frame: Frame, pub is_new_scene: bool, - pub profile: TransactionProfile, - pub frame_stats: Option<FullFrameStats> } pub enum DebugOutput { + FetchDocuments(String), + FetchClipScrollTree(String), #[cfg(feature = "capture")] SaveCapture(CaptureConfig, Vec<ExternalCaptureImage>), #[cfg(feature = "replay")] @@ -529,6 +560,7 @@ pub enum ResultMsg { DocumentId, RenderedDocument, ResourceUpdateList, + BackendProfileCounters, ), AppendNotificationRequests(Vec<NotificationRequest>), ForceRedraw, @@ -536,14 +568,13 @@ pub enum ResultMsg { #[derive(Clone, Debug)] pub struct ResourceCacheError { - #[allow(dead_code)] - description: String, + _description: String, } impl ResourceCacheError { pub fn new(description: String) -> ResourceCacheError { ResourceCacheError { - description, + _description: description, } } } @@ -556,6 +587,7 @@ pub struct LayoutPrimitiveInfo { pub rect: LayoutRect, pub clip_rect: LayoutRect, pub flags: PrimitiveFlags, + pub hit_info: Option<ItemTag>, } impl LayoutPrimitiveInfo { @@ -564,6 +596,7 @@ impl LayoutPrimitiveInfo { rect, clip_rect, flags: PrimitiveFlags::default(), + hit_info: None, } } } diff --git a/third_party/webrender/webrender/src/lib.rs b/third_party/webrender/webrender/src/lib.rs index cc200e30eb1..0db5feae9ee 100644 --- a/third_party/webrender/webrender/src/lib.rs +++ b/third_party/webrender/webrender/src/lib.rs @@ -57,6 +57,8 @@ macro_rules! matches { #[macro_use] extern crate bitflags; #[macro_use] +extern crate cfg_if; +#[macro_use] extern crate cstr; #[macro_use] extern crate lazy_static; @@ -69,8 +71,7 @@ extern crate malloc_size_of_derive; extern crate serde; #[macro_use] extern crate tracy_rs; -#[macro_use] -extern crate derive_more; + use malloc_size_of; #[macro_use] @@ -82,13 +83,13 @@ mod box_shadow; #[cfg(any(feature = "capture", feature = "replay"))] mod capture; mod clip; -mod space; mod spatial_tree; mod composite; -mod compositor; mod debug_colors; mod debug_font_data; -mod debug_item; +mod debug_render; +#[cfg(feature = "debugger")] +mod debug_server; mod device; mod ellipse; mod filterdata; @@ -101,10 +102,10 @@ mod glyph_rasterizer; mod gpu_cache; mod gpu_types; mod hit_test; +mod intern; mod internal_types; mod lru_cache; mod picture; -mod prepare; mod prim_store; mod print_tree; mod render_backend; @@ -119,22 +120,12 @@ mod scene_builder_thread; mod scene_building; mod screen_capture; mod segment; +mod shade; mod spatial_node; -mod texture_pack; +mod storage; +mod texture_allocator; mod texture_cache; -mod tile_cache; mod util; -mod visibility; -mod api_resources; -mod image_tiling; -mod image_source; -mod rectangle_occlusion; -pub mod host_utils; - -/// -pub mod intern; -/// -pub mod render_api; mod shader_source { include!(concat!(env!("OUT_DIR"), "/shaders.rs")); @@ -162,19 +153,39 @@ mod platform { } } +#[cfg(target_os = "macos")] +use core_foundation; +#[cfg(target_os = "macos")] +use core_graphics; +#[cfg(target_os = "macos")] +use core_text; + +#[cfg(target_os = "windows")] +use dwrote; + pub use euclid; +#[cfg(feature = "debugger")] +use serde_json; #[macro_use] extern crate smallvec; +#[cfg(feature = "debugger")] +use ws; +#[cfg(feature = "debugger")] +use image_loader; +#[cfg(feature = "debugger")] +use base64; +#[cfg(all(feature = "capture", feature = "png"))] +use png; #[cfg(test)] use rand; -pub use api; +#[macro_use] +pub extern crate api; use webrender_build; #[doc(hidden)] -pub use crate::composite::{CompositorConfig, Compositor, CompositorCapabilities, CompositorSurfaceTransform}; -pub use crate::composite::{NativeSurfaceId, NativeTileId, NativeSurfaceInfo, PartialPresentCompositor}; -pub use crate::composite::{MappableCompositor, MappedTileInfo, SWGLCompositeSurfaceInfo}; +pub use crate::composite::{CompositorConfig, Compositor, CompositorCapabilities}; +pub use crate::composite::{NativeSurfaceId, NativeTileId, NativeSurfaceInfo}; pub use crate::device::{UploadMethod, VertexUsageHint, get_gl_target, get_unoptimized_shader_source}; pub use crate::device::{ProgramBinary, ProgramCache, ProgramCacheObserver, FormatDesc}; pub use crate::device::Device; @@ -184,21 +195,16 @@ pub use crate::profiler::{ProfilerHooks, set_profiler_hooks}; pub use crate::renderer::{ AsyncPropertySampler, CpuProfile, DebugFlags, GpuProfile, GraphicsApi, GraphicsApiInfo, PipelineInfo, Renderer, RendererError, RendererOptions, RenderResults, - RendererStats, SceneBuilderHooks, Shaders, SharedShaders, ShaderPrecacheFlags, - MAX_VERTEX_TEXTURE_WIDTH, ONE_TIME_USAGE_HINT, + RendererStats, SceneBuilderHooks, ThreadListener, ShaderPrecacheFlags, + 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::texture_cache::TextureCacheConfig; +pub use crate::shade::{Shaders, WrShaders}; pub use api as webrender_api; pub use webrender_build::shader::ProgramSourceDigest; pub use crate::picture::{TileDescriptor, TileId, InvalidationReason}; pub use crate::picture::{PrimitiveCompareResult, PrimitiveCompareResultDetail, CompareHelperResult}; pub use crate::picture::{TileNode, TileNodeKind, TileSerializer, TileCacheInstanceSerializer, TileOffset, TileCacheLoggerUpdateLists}; pub use crate::intern::ItemUid; -pub use crate::render_api::*; -pub use crate::tile_cache::{PictureCacheDebugInfo, DirtyTileDebugInfo, TileDebugInfo, SliceDebugInfo}; - -#[cfg(feature = "sw_compositor")] -pub use crate::compositor::sw_compositor; diff --git a/third_party/webrender/webrender/src/lru_cache.rs b/third_party/webrender/webrender/src/lru_cache.rs index d53119b77d1..f741f9fb6ff 100644 --- a/third_party/webrender/webrender/src/lru_cache.rs +++ b/third_party/webrender/webrender/src/lru_cache.rs @@ -12,44 +12,29 @@ use std::{mem, num}; texture cache requires, but should be usable as a general LRU cache type if useful in other areas. - The cache is implemented with two types of backing freelists. These allow + The cache is implemented with two backing freelists. These allow random access to the underlying data, while being efficient in both memory access and allocation patterns. - The "entries" freelist stores the elements being cached (for example, the + The first freelist stores the elements being cached (for example, the CacheEntry structure for the texture cache). These elements are stored in arbitrary order, reusing empty slots in the freelist where possible. - The "lru_index" freelists store the LRU tracking information. Although the + The second freelist stores the LRU tracking information. Although the tracking elements are stored in arbitrary order inside a freelist for efficiency, they use next/prev links to represent a doubly-linked list, kept sorted in order of recent use. The next link is also used to store the current freelist within the array when the element is not occupied. - - The LRU cache allows having multiple LRU "partitions". Every entry is tracked - by exactly one partition at any time; all partitions refer to entries in the - shared freelist. Entries can move between partitions, if replace_or_insert is - called with a new partition index for an existing handle. - The partitioning is used by the texture cache so that, for example, allocating - more glyph entries does not cause eviction of image entries (which go into - a different shared texture). If an existing handle's entry is reallocated with - a new size, it might need to move from a shared texture to a standalone - texture; in this case the handle will move to a different LRU partition. */ /// Stores the data supplied by the user to be cached, and an index /// into the LRU tracking freelist for this element. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] struct LRUCacheEntry<T> { - /// The LRU partition that tracks this entry. - partition_index: u8, - - /// The location of the LRU tracking element for this cache entry in the - /// right LRU partition. - lru_index: ItemIndex, - + /// The location of the LRU tracking element for this cache entry. + /// This is None if the entry has manual eviction policy enabled. + lru_index: Option<ItemIndex>, /// The cached data provided by the caller for this element. value: T, } @@ -57,21 +42,19 @@ struct LRUCacheEntry<T> { /// The main public interface to the LRU cache #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] pub struct LRUCache<T, M> { /// A free list of cache entries, and indices into the LRU tracking list entries: FreeList<LRUCacheEntry<T>, M>, /// The LRU tracking list, allowing O(1) access to the oldest element - lru: Vec<LRUTracker<FreeListHandle<M>>>, + lru: LRUTracker<FreeListHandle<M>>, } impl<T, M> LRUCache<T, M> { /// Construct a new LRU cache - pub fn new(lru_partition_count: usize) -> Self { - assert!(lru_partition_count <= u8::MAX as usize + 1); + pub fn new() -> Self { LRUCache { entries: FreeList::new(), - lru: (0..lru_partition_count).map(|_| LRUTracker::new()).collect(), + lru: LRUTracker::new(), } } @@ -80,7 +63,6 @@ impl<T, M> LRUCache<T, M> { /// may be evicted at any time. pub fn push_new( &mut self, - partition_index: u8, value: T, ) -> WeakFreeListHandle<M> { // It's a slightly awkward process to insert an element, since we don't know @@ -89,9 +71,8 @@ impl<T, M> LRUCache<T, M> { // Insert the data provided by the caller let handle = self.entries.insert(LRUCacheEntry { - partition_index: 0, - lru_index: ItemIndex(num::NonZeroU32::new(1).unwrap()), - value + lru_index: None, + value, }); // Get a weak handle to return to the caller @@ -100,13 +81,22 @@ impl<T, M> LRUCache<T, M> { // Add an LRU tracking node that owns the strong handle, and store the location // of this inside the cache entry. let entry = self.entries.get_mut(&handle); - let lru_index = self.lru[partition_index as usize].push_new(handle); - entry.partition_index = partition_index; - entry.lru_index = lru_index; + entry.lru_index = Some(self.lru.push_new(handle)); weak_handle } + /// Get immutable access to the data at a given slot. Since this takes a strong + /// handle, it's guaranteed to be valid. + pub fn get( + &self, + handle: &FreeListHandle<M>, + ) -> &T { + &self.entries + .get(handle) + .value + } + /// Get immutable access to the data at a given slot. Since this takes a weak /// handle, it may have been evicted, so returns an Option. pub fn get_opt( @@ -133,27 +123,18 @@ impl<T, M> LRUCache<T, M> { }) } - /// Return a reference to the oldest item in the cache, keeping it in the cache. - /// If the cache is empty, this will return None. - pub fn peek_oldest(&self, partition_index: u8) -> Option<&T> { - self.lru[partition_index as usize] - .peek_front() - .map(|handle| { - let entry = self.entries.get(handle); - &entry.value - }) - } - /// Remove the oldest item from the cache. This is used to select elements to - /// be evicted. If the cache is empty, this will return None. + /// be evicted. If the cache is empty, or all elements in the cache have manual + /// eviction enabled, this will return None pub fn pop_oldest( &mut self, - partition_index: u8, ) -> Option<T> { - self.lru[partition_index as usize] + self.lru .pop_front() .map(|handle| { let entry = self.entries.free(handle); + // We should only find elements in this list with valid LRU location + debug_assert!(entry.lru_index.is_some()); entry.value }) } @@ -167,68 +148,82 @@ impl<T, M> LRUCache<T, M> { pub fn replace_or_insert( &mut self, handle: &mut WeakFreeListHandle<M>, - partition_index: u8, data: T, ) -> Option<T> { match self.entries.get_opt_mut(handle) { Some(entry) => { - if entry.partition_index != partition_index { - // Move to a different partition. - let strong_handle = self.lru[entry.partition_index as usize].remove(entry.lru_index); - let lru_index = self.lru[partition_index as usize].push_new(strong_handle); - entry.partition_index = partition_index; - entry.lru_index = lru_index; - } Some(mem::replace(&mut entry.value, data)) } None => { - *handle = self.push_new(partition_index, data); + *handle = self.push_new(data); None } } } - /// Manually evict a specific item. - pub fn remove(&mut self, handle: &WeakFreeListHandle<M>) -> Option<T> { - if let Some(entry) = self.entries.get_opt_mut(handle) { - let strong_handle = self.lru[entry.partition_index as usize].remove(entry.lru_index); - return Some(self.entries.free(strong_handle).value); - } - - None - } - /// This is used by the calling code to signal that the element that this handle /// references has been used on this frame. Internally, it updates the links in /// the LRU tracking element to move this item to the end of the LRU list. Returns /// the underlying data in case the client wants to mutate it. pub fn touch( &mut self, - handle: &WeakFreeListHandle<M>, + handle: &WeakFreeListHandle<M> ) -> Option<&mut T> { let lru = &mut self.lru; self.entries .get_opt_mut(handle) .map(|entry| { - lru[entry.partition_index as usize].mark_used(entry.lru_index); + // Only have a valid LRU index if eviction mode is auto + if let Some(lru_index) = entry.lru_index { + lru.mark_used(lru_index); + } + &mut entry.value }) } + /// In some special cases, the caller may want to manually manage the + /// lifetime of a resource. This method removes the LRU tracking information + /// for an element, and returns the strong handle to the caller to manage. + #[must_use] + pub fn set_manual_eviction( + &mut self, + handle: &WeakFreeListHandle<M>, + ) -> Option<FreeListHandle<M>> { + let entry = self.entries + .get_opt_mut(handle) + .expect("bug: trying to set manual eviction on an invalid handle"); + + // Remove the LRU tracking information from this element, if it exists. + // (it may be None if manual eviction was already enabled for this element). + entry.lru_index.take().map(|lru_index| { + self.lru.remove(lru_index) + }) + } + + /// Remove an element that is in manual eviction mode. This takes the caller + /// managed strong handle, and removes this element from the freelist. + pub fn remove_manual_handle( + &mut self, + handle: FreeListHandle<M>, + ) -> T { + let entry = self.entries.free(handle); + debug_assert_eq!(entry.lru_index, None, "Must be manual eviction mode!"); + entry.value + } + /// Try to validate that the state of the cache is consistent #[cfg(test)] fn validate(&self) { - for lru in &self.lru { - lru.validate(); - } + self.lru.validate(); } } /// Index of an LRU tracking element #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, MallocSizeOf)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] struct ItemIndex(num::NonZeroU32); impl ItemIndex { @@ -243,7 +238,7 @@ impl ItemIndex { /// to minimize heap allocations and improve cache access patterns. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, MallocSizeOf)] +#[derive(Debug)] struct Item<H> { prev: Option<ItemIndex>, next: Option<ItemIndex>, @@ -253,7 +248,6 @@ struct Item<H> { /// Internal implementation of the LRU tracking list #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] struct LRUTracker<H> { /// Current head of the list - this is the oldest item that will be evicted next. head: Option<ItemIndex>, @@ -388,11 +382,6 @@ impl<H> LRUTracker<H> where H: std::fmt::Debug { item_index } - /// Returns a reference to the oldest element, or None if the list is empty. - fn peek_front(&self) -> Option<&H> { - self.head.map(|head| self.items[head.as_usize()].handle.as_ref().unwrap()) - } - /// Remove the oldest element from the front of the LRU list. Returns None /// if the list is empty. fn pop_front( @@ -434,7 +423,8 @@ impl<H> LRUTracker<H> where H: std::fmt::Debug { } /// Manually remove an item from the LRU tracking list. This is used - /// when an element switches from one LRU partition to a different one. + /// when an element switches from having its lifetime managed by the LRU + /// algorithm to having a manual eviction policy. fn remove( &mut self, index: ItemIndex, @@ -537,32 +527,6 @@ impl<H> LRUTracker<H> where H: std::fmt::Debug { } #[test] -fn test_lru_tracker_push_peek() { - // Push elements, peek and ensure: - // - peek_oldest returns None before first element pushed - // - peek_oldest returns oldest element - // - subsequent calls to peek_oldest return same element (nothing was removed) - struct CacheMarker; - const NUM_ELEMENTS: usize = 50; - - let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(1); - cache.validate(); - - assert_eq!(cache.peek_oldest(0), None); - - for i in 0 .. NUM_ELEMENTS { - cache.push_new(0, i); - } - cache.validate(); - - assert_eq!(cache.peek_oldest(0), Some(&0)); - assert_eq!(cache.peek_oldest(0), Some(&0)); - - cache.pop_oldest(0); - assert_eq!(cache.peek_oldest(0), Some(&1)); -} - -#[test] fn test_lru_tracker_push_pop() { // Push elements, pop them all off and ensure: // - Returned in oldest order @@ -570,20 +534,20 @@ fn test_lru_tracker_push_pop() { struct CacheMarker; const NUM_ELEMENTS: usize = 50; - let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(1); + let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(); cache.validate(); for i in 0 .. NUM_ELEMENTS { - cache.push_new(0, i); + cache.push_new(i); } cache.validate(); for i in 0 .. NUM_ELEMENTS { - assert_eq!(cache.pop_oldest(0), Some(i)); + assert_eq!(cache.pop_oldest(), Some(i)); } cache.validate(); - assert_eq!(cache.pop_oldest(0), None); + assert_eq!(cache.pop_oldest(), None); } #[test] @@ -594,12 +558,12 @@ fn test_lru_tracker_push_touch_pop() { struct CacheMarker; const NUM_ELEMENTS: usize = 50; - let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(1); + let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(); let mut handles = Vec::new(); cache.validate(); for i in 0 .. NUM_ELEMENTS { - handles.push(cache.push_new(0, i)); + handles.push(cache.push_new(i)); } cache.validate(); @@ -609,15 +573,15 @@ fn test_lru_tracker_push_touch_pop() { cache.validate(); for i in 0 .. NUM_ELEMENTS/2 { - assert_eq!(cache.pop_oldest(0), Some(i*2+1)); + assert_eq!(cache.pop_oldest(), Some(i*2+1)); } cache.validate(); for i in 0 .. NUM_ELEMENTS/2 { - assert_eq!(cache.pop_oldest(0), Some(i*2)); + assert_eq!(cache.pop_oldest(), Some(i*2)); } cache.validate(); - assert_eq!(cache.pop_oldest(0), None); + assert_eq!(cache.pop_oldest(), None); } #[test] @@ -627,12 +591,12 @@ fn test_lru_tracker_push_get() { struct CacheMarker; const NUM_ELEMENTS: usize = 50; - let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(1); + let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(); let mut handles = Vec::new(); cache.validate(); for i in 0 .. NUM_ELEMENTS { - handles.push(cache.push_new(0, i)); + handles.push(cache.push_new(i)); } cache.validate(); @@ -650,17 +614,17 @@ fn test_lru_tracker_push_replace_get() { struct CacheMarker; const NUM_ELEMENTS: usize = 50; - let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(1); + let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(); let mut handles = Vec::new(); cache.validate(); for i in 0 .. NUM_ELEMENTS { - handles.push(cache.push_new(0, i)); + handles.push(cache.push_new(i)); } cache.validate(); for i in 0 .. NUM_ELEMENTS { - assert_eq!(cache.replace_or_insert(&mut handles[i], 0, i * 2), Some(i)); + assert_eq!(cache.replace_or_insert(&mut handles[i], i * 2), Some(i)); } cache.validate(); @@ -670,6 +634,42 @@ fn test_lru_tracker_push_replace_get() { cache.validate(); let mut empty_handle = WeakFreeListHandle::invalid(); - assert_eq!(cache.replace_or_insert(&mut empty_handle, 0, 100), None); + assert_eq!(cache.replace_or_insert(&mut empty_handle, 100), None); assert_eq!(cache.get_opt(&empty_handle), Some(&100)); } + +#[test] +fn test_lru_tracker_manual_evict() { + // Push elements, set even as manual eviction, ensure: + // - correctly pop auto handles in correct order + // - correctly remove manual handles, and have expected value + struct CacheMarker; + const NUM_ELEMENTS: usize = 50; + + let mut cache: LRUCache<usize, CacheMarker> = LRUCache::new(); + let mut handles = Vec::new(); + let mut manual_handles = Vec::new(); + cache.validate(); + + for i in 0 .. NUM_ELEMENTS { + handles.push(cache.push_new(i)); + } + cache.validate(); + + for i in 0 .. NUM_ELEMENTS/2 { + manual_handles.push(cache.set_manual_eviction(&handles[i*2]).unwrap()); + } + cache.validate(); + + for i in 0 .. NUM_ELEMENTS/2 { + assert!(cache.pop_oldest() == Some(i*2 + 1)); + } + cache.validate(); + + assert!(cache.pop_oldest().is_none()); + + for (i, manual_handle) in manual_handles.drain(..).enumerate() { + assert_eq!(*cache.get(&manual_handle), i*2); + assert_eq!(cache.remove_manual_handle(manual_handle), i*2); + } +} diff --git a/third_party/webrender/webrender/src/picture.rs b/third_party/webrender/webrender/src/picture.rs index 5cc690f1798..5b71479b0a5 100644 --- a/third_party/webrender/webrender/src/picture.rs +++ b/third_party/webrender/webrender/src/picture.rs @@ -94,12 +94,11 @@ //! blend the overlay tile (this is not always optimal right now, but will be //! improved as a follow up). -use api::{MixBlendMode, PremultipliedColorF, FilterPrimitiveKind}; +use api::{MixBlendMode, PipelineId, PremultipliedColorF, FilterPrimitiveKind}; use api::{PropertyBinding, PropertyBindingId, FilterPrimitive}; -use api::{DebugFlags, ImageKey, ColorF, ColorU, PrimitiveFlags}; -use api::{ImageRendering, ColorDepth, YuvColorSpace, YuvFormat, AlphaType}; +use api::{DebugFlags, RasterSpace, ImageKey, ColorF, ColorU, PrimitiveFlags}; +use api::{ImageRendering, ColorDepth, YuvColorSpace, YuvFormat}; use api::units::*; -use crate::batch::BatchFilter; use crate::box_shadow::BLUR_SAMPLE_SCALE; use crate::clip::{ClipStore, ClipChainInstance, ClipChainId, ClipInstance}; use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, @@ -108,38 +107,35 @@ use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, use crate::composite::{CompositorKind, CompositeState, NativeSurfaceId, NativeTileId}; use crate::composite::{ExternalSurfaceDescriptor, ExternalSurfaceDependency}; use crate::debug_colors; -use euclid::{vec2, vec3, Point2D, Scale, Size2D, Vector2D, Vector3D, Rect, Transform3D, SideOffsets2D}; +use euclid::{vec2, vec3, Point2D, Scale, Size2D, Vector2D, Rect, Transform3D, SideOffsets2D}; use euclid::approxeq::ApproxEq; use crate::filterdata::SFilterData; +use crate::frame_builder::{FrameBuilderConfig, FrameVisibilityContext, FrameVisibilityState}; use crate::intern::ItemUid; use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter, PlaneSplitAnchor, TextureSource}; use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext}; use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use crate::gpu_types::{UvRectKind, ZBufferId}; use plane_split::{Clipper, Polygon, Splitter}; -use crate::prim_store::{PrimitiveTemplateKind, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; -use crate::prim_store::{ColorBindingStorage, ColorBindingIndex, PrimitiveScratchBuffer}; +use crate::prim_store::{SpaceMapper, PrimitiveVisibilityMask, PrimitiveTemplateKind}; +use crate::prim_store::{SpaceSnapper, PictureIndex, PrimitiveInstance, PrimitiveInstanceKind}; +use crate::prim_store::{get_raster_rects, PrimitiveScratchBuffer}; +use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBindingIndex}; +use crate::prim_store::{ColorBindingStorage, ColorBindingIndex, PrimitiveVisibilityFlags}; use crate::print_tree::{PrintTree, PrintTreePrinter}; use crate::render_backend::{DataStores, FrameId}; use crate::render_task_graph::RenderTaskId; use crate::render_target::RenderTargetKind; -use crate::render_task::{BlurTask, RenderTask, RenderTaskLocation, BlurTaskCache}; -use crate::render_task::{StaticRenderTaskSurface, RenderTaskKind}; -use crate::renderer::BlendMode; -use crate::resource_cache::{ResourceCache, ImageGeneration, ImageRequest}; -use crate::space::SpaceMapper; +use crate::render_task::{RenderTask, RenderTaskLocation, BlurTaskCache, ClearMode}; +use crate::resource_cache::{ResourceCache, ImageGeneration}; use crate::scene::SceneProperties; use smallvec::SmallVec; use std::{mem, u8, marker, u32}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::collections::hash_map::Entry; -use std::ops::Range; use crate::texture_cache::TextureCacheHandle; -use crate::util::{MaxRect, VecHelper, MatrixHelpers, Recycler, raster_rect_to_device_pixels, ScaleOffset}; +use crate::util::{MaxRect, VecHelper, RectHelpers, MatrixHelpers}; use crate::filterdata::{FilterDataHandle}; -use crate::tile_cache::{SliceDebugInfo, TileDebugInfo, DirtyTileDebugInfo}; -use crate::visibility::{PrimitiveVisibilityFlags, FrameVisibilityContext}; -use crate::visibility::{VisibilityState, FrameVisibilityState}; #[cfg(any(feature = "capture", feature = "replay"))] use ron; #[cfg(feature = "capture")] @@ -147,11 +143,7 @@ use crate::scene_builder_thread::InternerUpdates; #[cfg(any(feature = "capture", feature = "replay"))] use crate::intern::{Internable, UpdateList}; #[cfg(any(feature = "capture", feature = "replay"))] -use crate::clip::{ClipIntern, PolygonIntern}; -#[cfg(any(feature = "capture", feature = "replay"))] -use crate::filterdata::FilterDataIntern; -#[cfg(any(feature = "capture", feature = "replay"))] -use api::PrimitiveKeyKind; +use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind}; #[cfg(any(feature = "capture", feature = "replay"))] use crate::prim_store::backdrop::Backdrop; #[cfg(any(feature = "capture", feature = "replay"))] @@ -184,7 +176,7 @@ use std::collections::HashMap; pub const MAX_BLUR_RADIUS: f32 = 100.; /// Specify whether a surface allows subpixel AA text rendering. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SubpixelMode { /// This surface allows subpixel AA text Allow, @@ -194,6 +186,7 @@ pub enum SubpixelMode { /// with the excluded regions, and inside the allowed rect. Conditional { allowed_rect: PictureRect, + excluded_rects: Vec<PictureRect>, }, } @@ -261,15 +254,76 @@ impl<Src, Dst> From<CoordinateSpaceMapping<Src, Dst>> for TransformKey { } } +/// Information about a picture that is pushed / popped on the +/// PictureUpdateState during picture traversal pass. +struct PictureInfo { + /// The spatial node for this picture. + _spatial_node_index: SpatialNodeIndex, +} + +/// Picture-caching state to keep between scenes. +pub struct PictureCacheState { + /// The tiles retained by this picture cache. + pub tiles: FastHashMap<TileOffset, Box<Tile>>, + /// State of the spatial nodes from previous frame + spatial_node_comparer: SpatialNodeComparer, + /// State of opacity bindings from previous frame + opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>, + /// State of color bindings from previous frame + color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>, + /// The current transform of the picture cache root spatial node + root_transform: TransformKey, + /// The current tile size in device pixels + current_tile_size: DeviceIntSize, + /// Various allocations we want to avoid re-doing. + allocations: PictureCacheRecycledAllocations, + /// Currently allocated native compositor surface for this picture cache. + pub native_surface: Option<NativeSurface>, + /// A cache of compositor surfaces that are retained between display lists + pub external_native_surface_cache: FastHashMap<ExternalNativeSurfaceKey, ExternalNativeSurface>, + /// The retained virtual offset for this slice between display lists. + virtual_offset: DeviceIntPoint, + /// Current frame ID of this picture cache + frame_id: FrameId, +} + +pub struct PictureCacheRecycledAllocations { + old_opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>, + old_color_bindings: FastHashMap<PropertyBindingId, ColorBindingInfo>, + compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>, +} + +/// Stores a list of cached picture tiles that are retained +/// between new scenes. +#[cfg_attr(feature = "capture", derive(Serialize))] +pub struct RetainedTiles { + /// The tiles retained between display lists. + #[cfg_attr(feature = "capture", serde(skip))] //TODO + pub caches: FastHashMap<usize, PictureCacheState>, +} + +impl RetainedTiles { + pub fn new() -> Self { + RetainedTiles { + caches: FastHashMap::default(), + } + } + + /// Merge items from one retained tiles into another. + pub fn merge(&mut self, other: RetainedTiles) { + assert!(self.caches.is_empty() || other.caches.is_empty()); + if self.caches.is_empty() { + self.caches = other.caches; + } + } +} + /// Unit for tile coordinates. #[derive(Hash, Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct TileCoordinate; // Geometry types for tile coordinates. pub type TileOffset = Point2D<i32, TileCoordinate>; -// TileSize type is also used in used in lib.rs and cbindgen picks the wrong one when -// generating headers. -/// cbindgen:ignore pub type TileSize = Size2D<i32, TileCoordinate>; pub type TileRect = Rect<i32, TileCoordinate>; @@ -287,18 +341,52 @@ pub const TILE_SIZE_DEFAULT: DeviceIntSize = DeviceIntSize { /// The size in device pixels of a tile for horizontal scroll bars pub const TILE_SIZE_SCROLLBAR_HORIZONTAL: DeviceIntSize = DeviceIntSize { - width: 1024, - height: 32, + width: 512, + height: 16, _unit: marker::PhantomData, }; /// The size in device pixels of a tile for vertical scroll bars pub const TILE_SIZE_SCROLLBAR_VERTICAL: DeviceIntSize = DeviceIntSize { - width: 32, - height: 1024, + width: 16, + height: 512, _unit: marker::PhantomData, }; +const TILE_SIZE_FOR_TESTS: [DeviceIntSize; 6] = [ + DeviceIntSize { + width: 128, + height: 128, + _unit: marker::PhantomData, + }, + DeviceIntSize { + width: 256, + height: 256, + _unit: marker::PhantomData, + }, + DeviceIntSize { + width: 512, + height: 512, + _unit: marker::PhantomData, + }, + TILE_SIZE_DEFAULT, + TILE_SIZE_SCROLLBAR_VERTICAL, + TILE_SIZE_SCROLLBAR_HORIZONTAL, +]; + +// Return the list of tile sizes for the renderer to allocate texture arrays for. +pub fn tile_cache_sizes(testing: bool) -> &'static [DeviceIntSize] { + if testing { + &TILE_SIZE_FOR_TESTS + } else { + &[ + TILE_SIZE_DEFAULT, + TILE_SIZE_SCROLLBAR_HORIZONTAL, + TILE_SIZE_SCROLLBAR_VERTICAL, + ] + } +} + /// The maximum size per axis of a surface, /// in WorldPixel coordinates. const MAX_SURFACE_SIZE: f32 = 4096.0; @@ -487,7 +575,6 @@ struct TilePreUpdateContext { /// The fractional position of the picture cache, which may /// require invalidation of all tiles. fract_offset: PictureVector2D, - device_fract_offset: DeviceVector2D, /// The optional background color of the picture cache instance background_color: Option<ColorF>, @@ -514,7 +601,7 @@ struct TilePostUpdateContext<'a> { local_clip_rect: PictureRect, /// The calculated backdrop information for this cache instance. - backdrop: Option<BackdropInfo>, + backdrop: BackdropInfo, /// Information about opacity bindings from the picture cache. opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>, @@ -528,12 +615,18 @@ struct TilePostUpdateContext<'a> { /// The local rect of the overall picture cache local_rect: PictureRect, - /// Pre-allocated z-id to assign to tiles during post_update. - z_id: ZBufferId, + /// A list of the external surfaces that are present on this slice + external_surfaces: &'a [ExternalSurfaceDescriptor], + + /// Pre-allocated z-id to assign to opaque tiles during post_update. We + /// use a different z-id for opaque/alpha tiles, so that compositor + /// surfaces (such as videos) can have a z-id between these values, + /// which allows compositor surfaces to occlude opaque tiles, but not + /// alpha tiles. + z_id_opaque: ZBufferId, - /// If true, the scale factor of the root transform for this picture - /// cache changed, so we need to invalidate the tile and re-render. - invalidate_all: bool, + /// Pre-allocated z-id to assign to alpha tiles during post_update + z_id_alpha: ZBufferId, } // Mutable state passed to picture cache tiles during post_update @@ -573,6 +666,9 @@ struct PrimitiveDependencyInfo { /// Spatial nodes references by the clip dependencies of this primitive. spatial_nodes: SmallVec<[SpatialNodeIndex; 4]>, + + /// If true, this primitive has been promoted to be a compositor surface. + is_compositor_surface: bool, } impl PrimitiveDependencyInfo { @@ -589,6 +685,7 @@ impl PrimitiveDependencyInfo { prim_clip_box, clips: SmallVec::new(), spatial_nodes: SmallVec::new(), + is_compositor_surface: false, } } } @@ -620,13 +717,15 @@ pub enum SurfaceTextureDescriptor { /// This is the same as a `SurfaceTextureDescriptor` but has been resolved /// into a texture cache handle (if appropriate) that can be used by the /// batching and compositing code in the renderer. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum ResolvedSurfaceTexture { TextureCache { /// The texture ID to draw to. texture: TextureSource, + /// Slice index in the texture array to draw to. + layer: i32, }, Native { /// The arbitrary id of this tile. @@ -649,6 +748,7 @@ impl SurfaceTextureDescriptor { ResolvedSurfaceTexture::TextureCache { texture: cache_item.texture_id, + layer: cache_item.texture_layer, } } SurfaceTextureDescriptor::Native { id } => { @@ -667,6 +767,8 @@ pub enum TileSurface { Texture { /// Descriptor for the surface that this tile draws into. descriptor: SurfaceTextureDescriptor, + /// Bitfield specifying the dirty region(s) that are relevant to this tile. + visibility_mask: PrimitiveVisibilityMask, }, Color { color: ColorF, @@ -776,8 +878,8 @@ pub enum PrimitiveCompareResultDetail { pub enum InvalidationReason { /// The fractional offset changed FractionalOffset { - old: DeviceVector2D, - new: DeviceVector2D, + old: PictureVector2D, + new: PictureVector2D, }, /// The background color changed BackgroundColor { @@ -807,8 +909,6 @@ pub enum InvalidationReason { CompositorKindChanged, // The valid region of the tile changed ValidRectChanged, - // The overall scale of the picture cache changed - ScaleChanged, } /// A minimal subset of Tile for debug capturing @@ -817,7 +917,7 @@ pub enum InvalidationReason { pub struct TileSerializer { pub rect: PictureRect, pub current_descriptor: TileDescriptor, - pub device_fract_offset: DeviceVector2D, + pub fract_offset: PictureVector2D, pub id: TileId, pub root: TileNode, pub background_color: Option<ColorF>, @@ -869,7 +969,7 @@ pub struct Tile { /// The current fractional offset of the cache transform root. If this changes, /// all tiles need to be invalidated and redrawn, since snapping differences are /// likely to occur. - device_fract_offset: DeviceVector2D, + fract_offset: PictureVector2D, /// The tile id is stable between display lists and / or frames, /// if the tile is retained. Useful for debugging tile evictions. pub id: TileId, @@ -882,9 +982,15 @@ pub struct Tile { background_color: Option<ColorF>, /// The first reason the tile was invalidated this frame. invalidation_reason: Option<InvalidationReason>, - /// The local space valid rect for all primitives that affect this tile. - local_valid_rect: PictureBox2D, - /// z-buffer id for this tile + /// If true, this tile has one or more compositor surfaces affecting it. + pub has_compositor_surface: bool, + /// The local space valid rect for any primitives found prior to the first compositor + /// surface that affects this tile. + bg_local_valid_rect: PictureBox2D, + /// The local space valid rect for any primitives found after the first compositor + /// surface that affects this tile. + fg_local_valid_rect: PictureBox2D, + /// z-buffer id for this tile, which is one of z_id_opaque or z_id_alpha, depending on tile opacity pub z_id: ZBufferId, /// The last frame this tile had its dependencies updated (dependency updating is /// skipped if a tile is off-screen). @@ -909,13 +1015,15 @@ impl Tile { prev_descriptor: TileDescriptor::new(), is_valid: false, is_visible: false, - device_fract_offset: DeviceVector2D::zero(), + fract_offset: PictureVector2D::zero(), id, is_opaque: false, root: TileNode::new_leaf(Vec::new()), background_color: None, invalidation_reason: None, - local_valid_rect: PictureBox2D::zero(), + has_compositor_surface: false, + bg_local_valid_rect: PictureBox2D::zero(), + fg_local_valid_rect: PictureBox2D::zero(), z_id: ZBufferId::invalid(), last_updated_frame_id: FrameId::INVALID, } @@ -925,7 +1033,7 @@ impl Tile { fn print(&self, pt: &mut dyn PrintTreePrinter) { pt.new_level(format!("Tile {:?}", self.id)); pt.add_item(format!("local_tile_rect: {:?}", self.local_tile_rect)); - pt.add_item(format!("device_fract_offset: {:?}", self.device_fract_offset)); + pt.add_item(format!("fract_offset: {:?}", self.fract_offset)); pt.add_item(format!("background_color: {:?}", self.background_color)); pt.add_item(format!("invalidation_reason: {:?}", self.invalidation_reason)); self.current_descriptor.print(pt); @@ -989,9 +1097,6 @@ impl Tile { invalidation_reason.expect("bug: no invalidation_reason"), ); } - if ctx.invalidate_all { - self.invalidate(None, InvalidationReason::ScaleChanged); - } // TODO(gw): We can avoid invalidating the whole tile in some cases here, // but it should be a fairly rare invalidation case. if self.current_descriptor.local_valid_rect != self.prev_descriptor.local_valid_rect { @@ -1043,8 +1148,10 @@ impl Tile { self.local_tile_rect.origin, self.local_tile_rect.bottom_right(), ); - self.local_valid_rect = PictureBox2D::zero(); + self.bg_local_valid_rect = PictureBox2D::zero(); + self.fg_local_valid_rect = PictureBox2D::zero(); self.invalidation_reason = None; + self.has_compositor_surface = false; self.world_tile_rect = ctx.pic_to_world_mapper .map(&self.local_tile_rect) @@ -1060,16 +1167,15 @@ impl Tile { return; } - // We may need to rerender if glyph subpixel positions have changed. Note - // that we update the tile fract offset itself after we have completed - // invalidation. This allows for other whole tile invalidation cases to - // update the fract offset appropriately. - let fract_delta = self.device_fract_offset - ctx.device_fract_offset; - let fract_changed = fract_delta.x.abs() > 0.01 || fract_delta.y.abs() > 0.01; + // Determine if the fractional offset of the transform is different this frame + // from the currently cached tile set. + let fract_changed = (self.fract_offset.x - ctx.fract_offset.x).abs() > 0.01 || + (self.fract_offset.y - ctx.fract_offset.y).abs() > 0.01; if fract_changed { self.invalidate(None, InvalidationReason::FractionalOffset { - old: self.device_fract_offset, - new: ctx.device_fract_offset }); + old: self.fract_offset, + new: ctx.fract_offset }); + self.fract_offset = ctx.fract_offset; } if ctx.background_color != self.background_color { @@ -1104,10 +1210,26 @@ impl Tile { return; } - // Incorporate the bounding rect of the primitive in the local valid rect - // for this tile. This is used to minimize the size of the scissor rect - // during rasterization and the draw rect during composition of partial tiles. - self.local_valid_rect = self.local_valid_rect.union(&info.prim_clip_box); + // If this primitive is a compositor surface, any tile it affects must be + // drawn as an overlay tile. + if info.is_compositor_surface { + self.has_compositor_surface = true; + } else { + // Incorporate the bounding rect of the primitive in the local valid rect + // for this tile. This is used to minimize the size of the scissor rect + // during rasterization and the draw rect during composition of partial tiles. + + // Once we have encountered 1+ compositor surfaces affecting this tile, include + // this bounding rect in the foreground. Otherwise, include in the background rect. + // This allows us to determine if we found any primitives that are on top of the + // compositor surface(s) for this tile. If so, we need to draw the tile with alpha + // blending as an overlay. + if self.has_compositor_surface { + self.fg_local_valid_rect = self.fg_local_valid_rect.union(&info.prim_clip_box); + } else { + self.bg_local_valid_rect = self.bg_local_valid_rect.union(&info.prim_clip_box); + } + } // Include any image keys this tile depends on. self.current_descriptor.images.extend_from_slice(&info.images); @@ -1206,8 +1328,12 @@ impl Tile { return false; } - // Calculate the overall valid rect for this tile. - self.current_descriptor.local_valid_rect = self.local_valid_rect.to_rect(); + // Calculate the overall valid rect for this tile, including both the foreground + // and background local valid rects. + self.current_descriptor.local_valid_rect = + self.bg_local_valid_rect + .union(&self.fg_local_valid_rect) + .to_rect(); // TODO(gw): In theory, the local tile rect should always have an // intersection with the overall picture rect. In practice, @@ -1224,22 +1350,6 @@ impl Tile { .and_then(|r| r.intersection(&self.current_descriptor.local_valid_rect)) .unwrap_or_else(PictureRect::zero); - // The device_valid_rect is referenced during `update_content_validity` so it - // must be updated here first. - let world_valid_rect = ctx.pic_to_world_mapper - .map(&self.current_descriptor.local_valid_rect) - .expect("bug: map local valid rect"); - - // The device rect is guaranteed to be aligned on a device pixel - the round - // is just to deal with float accuracy. However, the valid rect is not - // always aligned to a device pixel. To handle this, round out to get all - // required pixels, and intersect with the tile device rect. - let device_rect = (self.world_tile_rect * ctx.global_device_pixel_scale).round(); - self.device_valid_rect = (world_valid_rect * ctx.global_device_pixel_scale) - .round_out() - .intersection(&device_rect) - .unwrap_or_else(DeviceRect::zero); - // Invalidate the tile based on the content changing. self.update_content_validity(ctx, state, frame_context); @@ -1259,19 +1369,51 @@ impl Tile { return false; } + let world_valid_rect = ctx.pic_to_world_mapper + .map(&self.current_descriptor.local_valid_rect) + .expect("bug: map local valid rect"); + + // The device rect is guaranteed to be aligned on a device pixel - the round + // is just to deal with float accuracy. However, the valid rect is not + // always aligned to a device pixel. To handle this, round out to get all + // required pixels, and intersect with the tile device rect. + let device_rect = (self.world_tile_rect * ctx.global_device_pixel_scale).round(); + self.device_valid_rect = (world_valid_rect * ctx.global_device_pixel_scale) + .round_out() + .intersection(&device_rect) + .unwrap_or_else(DeviceRect::zero); + // Check if this tile can be considered opaque. Opacity state must be updated only // after all early out checks have been performed. Otherwise, we might miss updating // the native surface next time this tile becomes visible. 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.opaque_rect.contains_rect(&clipped_rect); + + if self.has_compositor_surface { + // If we found primitive(s) that are ordered _after_ the first compositor + // surface, _and_ intersect with any compositor surface, then we will need + // to draw this tile with alpha blending, as an overlay to the compositor surface. + let fg_world_valid_rect = ctx.pic_to_world_mapper + .map(&self.fg_local_valid_rect.to_rect()) + .expect("bug: map fg local valid rect"); + let fg_device_valid_rect = fg_world_valid_rect * ctx.global_device_pixel_scale; + + for surface in ctx.external_surfaces { + if surface.device_rect.intersects(&fg_device_valid_rect) { + is_opaque = false; + break; + } + } + } - let has_opaque_bg_color = self.background_color.map_or(false, |c| c.a >= 1.0); - let has_opaque_backdrop = ctx.backdrop.map_or(false, |b| b.opaque_rect.contains_rect(&clipped_rect)); - let is_opaque = has_opaque_bg_color || has_opaque_backdrop; - - // Set the correct z_id for this tile - self.z_id = ctx.z_id; + // Set the correct z_id for this tile based on opacity + if is_opaque { + self.z_id = ctx.z_id_opaque; + } else { + self.z_id = ctx.z_id_alpha; + } if is_opaque != self.is_opaque { // If opacity changed, the native compositor surface and all tiles get invalidated. @@ -1293,12 +1435,11 @@ impl Tile { } // Check if the selected composite mode supports dirty rect updates. For Draw composite - // mode, we can always update the content with smaller dirty rects, unless there is a - // driver bug to workaround. For native composite mode, we can only use dirty rects if - // the compositor supports partial surface updates. + // mode, we can always update the content with smaller dirty rects. For native composite + // mode, we can only use dirty rects if the compositor supports partial surface updates. let (supports_dirty_rects, supports_simple_prims) = match state.composite_state.compositor_kind { CompositorKind::Draw { .. } => { - (frame_context.config.gpu_supports_render_target_partial_update, true) + (true, true) } CompositorKind::Native { max_update_rects, .. } => { (max_update_rects > 0, false) @@ -1309,7 +1450,7 @@ impl Tile { // native compositors that don't support dirty rects. if supports_dirty_rects { // Only allow splitting for normal content sized tiles - if ctx.current_tile_size == state.resource_cache.texture_cache.default_picture_tile_size() { + if ctx.current_tile_size == TILE_SIZE_DEFAULT { let max_split_level = 3; // Consider splitting / merging dirty regions @@ -1334,7 +1475,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.map_or(false, |b| b.kind.is_some()) && + ctx.backdrop.kind.is_some() && self.current_descriptor.prims.len() == 1 && self.is_opaque && supports_simple_prims; @@ -1344,7 +1485,7 @@ impl Tile { // If we determine the tile can be represented by a color, set the // surface unconditionally (this will drop any previously used // texture cache backing surface). - match ctx.backdrop.unwrap().kind { + match ctx.backdrop.kind { Some(BackdropKind::Color { color }) => { TileSurface::Color { color, @@ -1364,10 +1505,11 @@ impl Tile { // the tile was previously a color, or not set, then just set // up a new texture cache handle. match self.surface.take() { - Some(TileSurface::Texture { descriptor }) => { + Some(TileSurface::Texture { descriptor, visibility_mask }) => { // Reuse the existing descriptor and vis mask TileSurface::Texture { descriptor, + visibility_mask, } } Some(TileSurface::Color { .. }) | Some(TileSurface::Clear) | None => { @@ -1395,6 +1537,7 @@ impl Tile { TileSurface::Texture { descriptor, + visibility_mask: PrimitiveVisibilityMask::empty(), } } } @@ -1665,106 +1808,122 @@ impl TileDescriptor { } } +/// Stores both the world and devices rects for a single dirty rect. +#[derive(Debug, Clone)] +pub struct DirtyRegionRect { + /// World rect of this dirty region + pub world_rect: WorldRect, + /// Bitfield for picture render tasks that draw this dirty region. + pub visibility_mask: PrimitiveVisibilityMask, +} + /// Represents the dirty region of a tile cache picture. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct DirtyRegion { - /// The individual filters that make up this region. - pub filters: Vec<BatchFilter>, + /// The individual dirty rects of this region. + pub dirty_rects: Vec<DirtyRegionRect>, /// The overall dirty rect, a combination of dirty_rects pub combined: WorldRect, - - /// Spatial node of the picture cache this region represents - spatial_node_index: SpatialNodeIndex, } impl DirtyRegion { /// Construct a new dirty region tracker. pub fn new( - spatial_node_index: SpatialNodeIndex, ) -> Self { DirtyRegion { - filters: Vec::with_capacity(16), + dirty_rects: Vec::with_capacity(PrimitiveVisibilityMask::MAX_DIRTY_REGIONS), combined: WorldRect::zero(), - spatial_node_index, } } /// Reset the dirty regions back to empty - pub fn reset( - &mut self, - spatial_node_index: SpatialNodeIndex, - ) { - self.filters.clear(); + pub fn clear(&mut self) { + self.dirty_rects.clear(); self.combined = WorldRect::zero(); - self.spatial_node_index = spatial_node_index; } - /// Add a dirty region to the tracker. Returns the visibility mask that corresponds to - /// this region in the tracker. - pub fn add_dirty_region( + /// Push a dirty rect into this region + pub fn push( &mut self, - rect_in_pic_space: PictureRect, - sub_slice_index: SubSliceIndex, - spatial_tree: &SpatialTree, + rect: WorldRect, + visibility_mask: PrimitiveVisibilityMask, ) { - let map_pic_to_world = SpaceMapper::new_with_target( - ROOT_SPATIAL_NODE_INDEX, - self.spatial_node_index, - WorldRect::max_rect(), - spatial_tree, - ); - - let world_rect = map_pic_to_world - .map(&rect_in_pic_space) - .expect("bug"); - // Include this in the overall dirty rect - self.combined = self.combined.union(&world_rect); + self.combined = self.combined.union(&rect); - self.filters.push(BatchFilter { - rect_in_pic_space, - sub_slice_index, + // Store the individual dirty rect. + self.dirty_rects.push(DirtyRegionRect { + world_rect: rect, + visibility_mask, }); } + /// Include another rect into an existing dirty region. + pub fn include_rect( + &mut self, + region_index: usize, + rect: WorldRect, + ) { + self.combined = self.combined.union(&rect); + + let region = &mut self.dirty_rects[region_index]; + region.world_rect = region.world_rect.union(&rect); + } + // TODO(gw): This returns a heap allocated object. Perhaps we can simplify this // logic? Although - it's only used very rarely so it may not be an issue. pub fn inflate( &self, inflate_amount: f32, - spatial_tree: &SpatialTree, ) -> DirtyRegion { - let map_pic_to_world = SpaceMapper::new_with_target( - ROOT_SPATIAL_NODE_INDEX, - self.spatial_node_index, - WorldRect::max_rect(), - spatial_tree, - ); - - let mut filters = Vec::with_capacity(self.filters.len()); + let mut dirty_rects = Vec::with_capacity(self.dirty_rects.len()); let mut combined = WorldRect::zero(); - for filter in &self.filters { - let rect_in_pic_space = filter.rect_in_pic_space.inflate(inflate_amount, inflate_amount); - - let world_rect = map_pic_to_world - .map(&rect_in_pic_space) - .expect("bug"); - + for rect in &self.dirty_rects { + let world_rect = rect.world_rect.inflate(inflate_amount, inflate_amount); combined = combined.union(&world_rect); - filters.push(BatchFilter { - rect_in_pic_space, - sub_slice_index: filter.sub_slice_index, + dirty_rects.push(DirtyRegionRect { + world_rect, + visibility_mask: rect.visibility_mask, }); } DirtyRegion { - filters, + dirty_rects, combined, - spatial_node_index: self.spatial_node_index, } } + + /// Creates a record of this dirty region for exporting to test infrastructure. + pub fn record(&self) -> RecordedDirtyRegion { + let mut rects: Vec<WorldRect> = + self.dirty_rects.iter().map(|r| r.world_rect).collect(); + rects.sort_unstable_by_key(|r| (r.origin.y as usize, r.origin.x as usize)); + RecordedDirtyRegion { rects } + } +} + +/// A recorded copy of the dirty region for exporting to test infrastructure. +#[cfg_attr(feature = "capture", derive(Serialize))] +pub struct RecordedDirtyRegion { + pub rects: Vec<WorldRect>, +} + +impl ::std::fmt::Display for RecordedDirtyRegion { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + for r in self.rects.iter() { + let (x, y, w, h) = (r.origin.x, r.origin.y, r.size.width, r.size.height); + write!(f, "[({},{}):{}x{}]", x, y, w, h)?; + } + Ok(()) + } +} + +impl ::std::fmt::Debug for RecordedDirtyRegion { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + ::std::fmt::Display::fmt(self, f) + } } #[derive(Debug, Copy, Clone)] @@ -1911,7 +2070,7 @@ macro_rules! declare_tile_cache_logger_updatelists { } #[cfg(any(feature = "capture", feature = "replay"))] -crate::enumerate_interners!(declare_tile_cache_logger_updatelists); +enumerate_interners!(declare_tile_cache_logger_updatelists); #[cfg(not(any(feature = "capture", feature = "replay")))] pub struct TileCacheLoggerUpdateLists { @@ -2087,9 +2246,6 @@ pub struct ExternalNativeSurfaceKey { pub image_keys: [ImageKey; 3], /// The current device size of the surface. pub size: DeviceIntSize, - /// True if this is an 'external' compositor surface created via - /// Compositor::create_external_surface. - pub is_external_surface: bool, } /// Information about a native compositor surface cached between frames. @@ -2105,129 +2261,6 @@ pub struct ExternalNativeSurface { pub image_dependencies: [ImageDependency; 3], } -/// The key that identifies a tile cache instance. For now, it's simple the index of -/// the slice as it was created during scene building. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct SliceId(usize); - -impl SliceId { - pub fn new(index: usize) -> Self { - SliceId(index) - } -} - -/// Information that is required to reuse or create a new tile cache. Created -/// during scene building and passed to the render backend / frame builder. -pub struct TileCacheParams { - // Index of the slice (also effectively the key of the tile cache, though we use SliceId where that matters) - pub slice: usize, - // Flags describing content of this cache (e.g. scrollbars) - pub slice_flags: SliceFlags, - // The anchoring spatial node / scroll root - pub spatial_node_index: SpatialNodeIndex, - // Optional background color of this tilecache. If present, can be used as an optimization - // to enable opaque blending and/or subpixel AA in more places. - pub background_color: Option<ColorF>, - // List of clips shared by all prims that are promoted to this tile cache - pub shared_clips: Vec<ClipInstance>, - // The clip chain handle representing `shared_clips` - pub shared_clip_chain: ClipChainId, - // Virtual surface sizes are always square, so this represents both the width and height - pub virtual_surface_size: i32, - // The number of compositor surfaces that are being requested for this tile cache. - // This is only a suggestion - the tile cache will clamp this as a reasonable number - // and only promote a limited number of surfaces. - pub compositor_surface_count: usize, -} - -/// Defines which sub-slice (effectively a z-index) a primitive exists on within -/// a picture cache instance. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct SubSliceIndex(u8); - -impl SubSliceIndex { - pub const DEFAULT: SubSliceIndex = SubSliceIndex(0); - - pub fn new(index: usize) -> Self { - SubSliceIndex(index as u8) - } - - /// Return true if this sub-slice is the primary sub-slice (for now, we assume - /// that only the primary sub-slice may be opaque and support subpixel AA, for example). - pub fn is_primary(&self) -> bool { - self.0 == 0 - } -} - -/// Wrapper struct around an external surface descriptor with a little more information -/// that the picture caching code needs. -pub struct CompositorSurface { - // External surface descriptor used by compositing logic - pub descriptor: ExternalSurfaceDescriptor, - // The compositor surface rect + any intersecting prims. Later prims that intersect - // with this must be added to the next sub-slice. - prohibited_rect: PictureRect, - // If the compositor surface content is opaque. - pub is_opaque: bool, -} - -/// A SubSlice represents a potentially overlapping set of tiles within a picture cache. Most -/// picture cache instances will have only a single sub-slice. The exception to this is when -/// a picture cache has compositor surfaces, in which case sub slices are used to interleave -/// content under or order the compositor surface(s). -pub struct SubSlice { - /// Hash of tiles present in this picture. - pub tiles: FastHashMap<TileOffset, Box<Tile>>, - /// The allocated compositor surfaces for this picture cache. May be None if - /// not using native compositor, or if the surface was destroyed and needs - /// to be reallocated next time this surface contains valid tiles. - pub native_surface: Option<NativeSurface>, - /// List of compositor surfaces that have been promoted from primitives - /// in this tile cache. - pub compositor_surfaces: Vec<CompositorSurface>, -} - -impl SubSlice { - /// Construct a new sub-slice - fn new() -> Self { - SubSlice { - tiles: FastHashMap::default(), - native_surface: None, - compositor_surfaces: Vec::new(), - } - } - - /// Reset the list of compositor surfaces that follow this sub-slice. - /// Built per-frame, since APZ may change whether an image is suitable to be a compositor surface. - fn reset(&mut self) { - self.compositor_surfaces.clear(); - } - - /// Resize the tile grid to match a new tile bounds - fn resize(&mut self, new_tile_rect: TileRect) -> FastHashMap<TileOffset, Box<Tile>> { - let mut old_tiles = mem::replace(&mut self.tiles, FastHashMap::default()); - self.tiles.reserve(new_tile_rect.size.area() as usize); - - for y in new_tile_rect.origin.y .. new_tile_rect.origin.y + new_tile_rect.size.height { - for x in new_tile_rect.origin.x .. new_tile_rect.origin.x + new_tile_rect.size.width { - let key = TileOffset::new(x, y); - let tile = old_tiles - .remove(&key) - .unwrap_or_else(|| { - Box::new(Tile::new(key)) - }); - self.tiles.insert(key, tile); - } - } - - old_tiles - } -} - /// Represents a cache of tiles that make up a picture primitives. pub struct TileCacheInstance { /// Index of the tile cache / slice for this frame builder. It's determined @@ -2241,10 +2274,14 @@ pub struct TileCacheInstance { pub slice_flags: SliceFlags, /// The currently selected tile size to use for this cache pub current_tile_size: DeviceIntSize, - /// The list of sub-slices in this tile cache - pub sub_slices: Vec<SubSlice>, /// The positioning node for this tile cache. pub spatial_node_index: SpatialNodeIndex, + /// Hash of tiles present in this picture. + pub tiles: FastHashMap<TileOffset, Box<Tile>>, + /// A helper struct to map local rects into surface coords. + map_local_to_surface: SpaceMapper<LayoutPixel, PicturePixel>, + /// A helper struct to map child picture rects into picture cache surface coords. + map_child_pic_to_surface: SpaceMapper<PicturePixel, PicturePixel>, /// List of opacity bindings, with some extra information /// about whether they changed since last frame. opacity_bindings: FastHashMap<PropertyBindingId, OpacityBindingInfo>, @@ -2270,7 +2307,7 @@ pub struct TileCacheInstance { /// Local rect (unclipped) of the picture this cache covers. pub local_rect: PictureRect, /// The local clip rect, from the shared clips of this picture. - pub local_clip_rect: PictureRect, + local_clip_rect: PictureRect, /// The surface index that this tile cache will be drawn into. surface_index: SurfaceIndex, /// The background color from the renderer. If this is set opaque, we know it's @@ -2290,7 +2327,7 @@ pub struct TileCacheInstance { /// clip rect for this tile cache. shared_clip_chain: ClipChainId, /// The current transform of the picture cache root spatial node - root_transform: ScaleOffset, + root_transform: TransformKey, /// The number of frames until this cache next evaluates what tile size to use. /// If a picture rect size is regularly changing just around a size threshold, /// we don't want to constantly invalidate and reallocate different tile size @@ -2298,8 +2335,6 @@ pub struct TileCacheInstance { frames_until_size_eval: usize, /// The current fractional offset of the cached picture fract_offset: PictureVector2D, - /// The current device fractional offset of the cached picture - device_fract_offset: DeviceVector2D, /// For DirectComposition, virtual surfaces don't support negative coordinates. However, /// picture cache tile coordinates can be negative. To handle this, we apply an offset /// to each tile in DirectComposition. We want to change this as little as possible, @@ -2310,12 +2345,21 @@ pub struct TileCacheInstance { /// keep around the hash map used as compare_cache to avoid reallocating it each /// frame. compare_cache: FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>, + /// The allocated compositor surfaces for this picture cache. May be None if + /// not using native compositor, or if the surface was destroyed and needs + /// to be reallocated next time this surface contains valid tiles. + pub native_surface: Option<NativeSurface>, /// The current device position of this cache. Used to set the compositor /// offset of the surface when building the visual tree. pub device_position: DevicePoint, /// The currently considered tile size override. Used to check if we should /// re-evaluate tile size, even if the frame timer hasn't expired. tile_size_override: Option<DeviceIntSize>, + /// List of external surfaces that have been promoted from primitives + /// in this tile cache. + pub external_surfaces: Vec<ExternalSurfaceDescriptor>, + /// z-buffer ID assigned to opaque tiles in this slice + pub z_id_opaque: ZBufferId, /// A cache of compositor surfaces that are retained between frames pub external_native_surface_cache: FastHashMap<ExternalNativeSurfaceKey, ExternalNativeSurface>, /// Current frame ID of this tile cache instance. Used for book-keeping / garbage collecting @@ -2330,27 +2374,36 @@ enum SurfacePromotionResult { } impl TileCacheInstance { - pub fn new(params: TileCacheParams) -> Self { - // Determine how many sub-slices we need. Clamp to an arbitrary limit to ensure - // we don't create a huge number of OS compositor tiles and sub-slices. - let sub_slice_count = params.compositor_surface_count.min(MAX_COMPOSITOR_SURFACES) + 1; - - let mut sub_slices = Vec::with_capacity(sub_slice_count); - for _ in 0 .. sub_slice_count { - sub_slices.push(SubSlice::new()); - } + pub fn new( + slice: usize, + slice_flags: SliceFlags, + spatial_node_index: SpatialNodeIndex, + background_color: Option<ColorF>, + shared_clips: Vec<ClipInstance>, + shared_clip_chain: ClipChainId, + fb_config: &FrameBuilderConfig, + ) -> Self { + let virtual_surface_size = fb_config.compositor_kind.get_virtual_surface_size(); TileCacheInstance { - slice: params.slice, - slice_flags: params.slice_flags, - spatial_node_index: params.spatial_node_index, - sub_slices, + slice, + slice_flags, + spatial_node_index, + tiles: FastHashMap::default(), + map_local_to_surface: SpaceMapper::new( + ROOT_SPATIAL_NODE_INDEX, + PictureRect::zero(), + ), + map_child_pic_to_surface: SpaceMapper::new( + ROOT_SPATIAL_NODE_INDEX, + PictureRect::zero(), + ), opacity_bindings: FastHashMap::default(), old_opacity_bindings: FastHashMap::default(), spatial_node_comparer: SpatialNodeComparer::new(), color_bindings: FastHashMap::default(), old_color_bindings: FastHashMap::default(), - dirty_region: DirtyRegion::new(params.spatial_node_index), + dirty_region: DirtyRegion::new(), tile_size: PictureSize::zero(), tile_rect: TileRect::zero(), tile_bounds_p0: TileOffset::zero(), @@ -2358,105 +2411,38 @@ impl TileCacheInstance { local_rect: PictureRect::zero(), local_clip_rect: PictureRect::zero(), surface_index: SurfaceIndex(0), - background_color: params.background_color, + background_color, backdrop: BackdropInfo::empty(), subpixel_mode: SubpixelMode::Allow, - root_transform: ScaleOffset::identity(), - shared_clips: params.shared_clips, - shared_clip_chain: params.shared_clip_chain, + root_transform: TransformKey::Local, + shared_clips, + shared_clip_chain, current_tile_size: DeviceIntSize::zero(), frames_until_size_eval: 0, fract_offset: PictureVector2D::zero(), - device_fract_offset: DeviceVector2D::zero(), // Default to centering the virtual offset in the middle of the DC virtual surface virtual_offset: DeviceIntPoint::new( - params.virtual_surface_size / 2, - params.virtual_surface_size / 2, + virtual_surface_size / 2, + virtual_surface_size / 2, ), compare_cache: FastHashMap::default(), + native_surface: None, device_position: DevicePoint::zero(), tile_size_override: None, + external_surfaces: Vec::new(), + z_id_opaque: ZBufferId::invalid(), external_native_surface_cache: FastHashMap::default(), frame_id: FrameId::INVALID, } } - /// Return the total number of tiles allocated by this tile cache - pub fn tile_count(&self) -> usize { - self.tile_rect.size.area() as usize * self.sub_slices.len() - } - - /// Reset this tile cache with the updated parameters from a new scene - /// that has arrived. This allows the tile cache to be retained across - /// new scenes. - pub fn prepare_for_new_scene( - &mut self, - params: TileCacheParams, - resource_cache: &mut ResourceCache, - ) { - // We should only receive updated state for matching slice key - assert_eq!(self.slice, params.slice); - - // Determine how many sub-slices we need, based on how many compositor surface prims are - // in the supplied primitive list. - let required_sub_slice_count = params.compositor_surface_count.min(MAX_COMPOSITOR_SURFACES) + 1; - - if self.sub_slices.len() != required_sub_slice_count { - self.tile_rect = TileRect::zero(); - - if self.sub_slices.len() > required_sub_slice_count { - let old_sub_slices = self.sub_slices.split_off(required_sub_slice_count); - - for mut sub_slice in old_sub_slices { - for tile in sub_slice.tiles.values_mut() { - if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface { - if let Some(id) = id.take() { - resource_cache.destroy_compositor_tile(id); - } - } - } - - if let Some(native_surface) = sub_slice.native_surface { - resource_cache.destroy_compositor_surface(native_surface.opaque); - resource_cache.destroy_compositor_surface(native_surface.alpha); - } - } - } else { - while self.sub_slices.len() < required_sub_slice_count { - self.sub_slices.push(SubSlice::new()); - } - } - } - - // Store the parameters from the scene builder for this slice. Other - // params in the tile cache are retained and reused, or are always - // updated during pre/post_update. - self.slice_flags = params.slice_flags; - self.spatial_node_index = params.spatial_node_index; - self.background_color = params.background_color; - self.shared_clips = params.shared_clips; - self.shared_clip_chain = params.shared_clip_chain; - - // Since the slice flags may have changed, ensure we re-evaluate the - // appropriate tile size for this cache next update. - self.frames_until_size_eval = 0; - } - - /// Destroy any manually managed resources before this picture cache is - /// destroyed, such as native compositor surfaces. - pub fn destroy( - self, - resource_cache: &mut ResourceCache, - ) { - for sub_slice in self.sub_slices { - if let Some(native_surface) = sub_slice.native_surface { - resource_cache.destroy_compositor_surface(native_surface.opaque); - resource_cache.destroy_compositor_surface(native_surface.alpha); - } - } - - for (_, external_surface) in self.external_native_surface_cache { - resource_cache.destroy_compositor_surface(external_surface.native_surface_id) + /// Returns true if this tile cache is considered opaque. + pub fn is_opaque(&self) -> bool { + // If known opaque due to background clear color and being the first slice. + // The background_color will only be Some(..) if this is the first slice. + match self.background_color { + Some(color) => color.a >= 1.0, + None => false } } @@ -2493,18 +2479,29 @@ impl TileCacheInstance { frame_context: &FrameVisibilityContext, frame_state: &mut FrameVisibilityState, ) -> WorldRect { + self.external_surfaces.clear(); self.surface_index = surface_index; self.local_rect = pic_rect; self.local_clip_rect = PictureRect::max_rect(); - for sub_slice in &mut self.sub_slices { - sub_slice.reset(); - } + // Opaque surfaces get the first z_id. Compositor surfaces then get + // allocated a z_id each. After all compositor surfaces are added, + // then we allocate a z_id for alpha tiles. + self.z_id_opaque = frame_state.composite_state.z_generator.next(); // Reset the opaque rect + subpixel mode, as they are calculated // during the prim dependency checks. self.backdrop = BackdropInfo::empty(); + self.map_local_to_surface = SpaceMapper::new( + self.spatial_node_index, + pic_rect, + ); + self.map_child_pic_to_surface = SpaceMapper::new( + self.spatial_node_index, + pic_rect, + ); + let pic_to_world_mapper = SpaceMapper::new_with_target( ROOT_SPATIAL_NODE_INDEX, self.spatial_node_index, @@ -2516,14 +2513,7 @@ impl TileCacheInstance { // which will provide a local clip rect. This is useful for establishing things // like whether the backdrop rect supplied by Gecko can be considered opaque. if self.shared_clip_chain != ClipChainId::NONE { - let shared_clips = &mut frame_state.scratch.picture.clip_chain_ids; - shared_clips.clear(); - - let map_local_to_surface = SpaceMapper::new( - self.spatial_node_index, - pic_rect, - ); - + let mut shared_clips = Vec::new(); let mut current_clip_chain_id = self.shared_clip_chain; while current_clip_chain_id != ClipChainId::NONE { shared_clips.push(current_clip_chain_id); @@ -2534,7 +2524,6 @@ impl TileCacheInstance { frame_state.clip_store.set_active_clips( LayoutRect::max_rect(), self.spatial_node_index, - map_local_to_surface.ref_spatial_node_index, &shared_clips, frame_context.spatial_tree, &mut frame_state.data_stores.clip, @@ -2542,7 +2531,7 @@ impl TileCacheInstance { let clip_chain_instance = frame_state.clip_store.build_clip_chain_instance( pic_rect.cast_unit(), - &map_local_to_surface, + &self.map_local_to_surface, &pic_to_world_mapper, frame_context.spatial_tree, frame_state.gpu_cache, @@ -2562,6 +2551,50 @@ impl TileCacheInstance { }); } + // If there are pending retained state, retrieve it. + if let Some(prev_state) = frame_state.retained_tiles.caches.remove(&self.slice) { + self.tiles.extend(prev_state.tiles); + self.root_transform = prev_state.root_transform; + self.spatial_node_comparer = prev_state.spatial_node_comparer; + self.opacity_bindings = prev_state.opacity_bindings; + self.color_bindings = prev_state.color_bindings; + self.current_tile_size = prev_state.current_tile_size; + self.native_surface = prev_state.native_surface; + self.external_native_surface_cache = prev_state.external_native_surface_cache; + self.virtual_offset = prev_state.virtual_offset; + self.frame_id = prev_state.frame_id; + + fn recycle_map<K: std::cmp::Eq + std::hash::Hash, V>( + ideal_len: usize, + dest: &mut FastHashMap<K, V>, + src: FastHashMap<K, V>, + ) { + if dest.capacity() < src.capacity() { + if src.capacity() < 3 * ideal_len { + *dest = src; + } else { + dest.clear(); + dest.reserve(ideal_len); + } + } + } + recycle_map( + self.opacity_bindings.len(), + &mut self.old_opacity_bindings, + prev_state.allocations.old_opacity_bindings, + ); + recycle_map( + self.color_bindings.len(), + &mut self.old_color_bindings, + prev_state.allocations.old_color_bindings, + ); + recycle_map( + prev_state.allocations.compare_cache.len(), + &mut self.compare_cache, + prev_state.allocations.compare_cache, + ); + } + // Advance the current frame ID counter for this picture cache (must be done // after any retained prev state is taken above). self.frame_id.advance(); @@ -2598,7 +2631,7 @@ impl TileCacheInstance { TILE_SIZE_SCROLLBAR_HORIZONTAL } } else { - frame_state.resource_cache.texture_cache.default_picture_tile_size() + TILE_SIZE_DEFAULT } } }; @@ -2606,16 +2639,13 @@ impl TileCacheInstance { // If the desired tile size has changed, then invalidate and drop any // existing tiles. if desired_tile_size != self.current_tile_size { - for sub_slice in &mut self.sub_slices { - // Destroy any native surfaces on the tiles that will be dropped due - // to resizing. - if let Some(native_surface) = sub_slice.native_surface.take() { - frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque); - frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha); - } - sub_slice.tiles.clear(); + // Destroy any native surfaces on the tiles that will be dropped due + // to resizing. + if let Some(native_surface) = self.native_surface.take() { + frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque); + frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha); } - self.tile_rect = TileRect::zero(); + self.tiles.clear(); self.current_tile_size = desired_tile_size; } @@ -2640,7 +2670,6 @@ impl TileCacheInstance { let device_origin = world_origin * frame_context.global_device_pixel_scale; let desired_device_origin = device_origin.round(); self.device_position = desired_device_origin; - self.device_fract_offset = desired_device_origin - device_origin; // Unmap from device space to world space rect let ref_world_rect = WorldRect::new( @@ -2648,13 +2677,17 @@ impl TileCacheInstance { WorldSize::new(1.0, 1.0), ); - // Unmap from world space to picture space; this should be the fractional offset - // required in picture space to align in device space - self.fract_offset = pic_to_world_mapper + // Unmap from world space to picture space + let ref_point = pic_to_world_mapper .unmap(&ref_world_rect) .expect("bug: unable to unmap ref world rect") - .origin - .to_vector(); + .origin; + + // Extract the fractional offset required in picture space to align in device space + self.fract_offset = PictureVector2D::new( + ref_point.x.fract(), + ref_point.y.fract(), + ); // Do a hacky diff of opacity binding values from the last frame. This is // used later on during tile invalidation tests. @@ -2739,34 +2772,33 @@ impl TileCacheInstance { // virtual offset. If so, we need to invalidate all tiles, and set up // a new virtual offset, centered around the current tile grid. - let virtual_surface_size = frame_context.config.compositor_kind.get_virtual_surface_size(); - // We only need to invalidate in this case if the underlying platform - // uses virtual surfaces. - if virtual_surface_size > 0 { - // Get the extremities of the tile grid after virtual offset is applied - let tx0 = self.virtual_offset.x + x0 * self.current_tile_size.width; - let ty0 = self.virtual_offset.y + y0 * self.current_tile_size.height; - let tx1 = self.virtual_offset.x + (x1+1) * self.current_tile_size.width; - let ty1 = self.virtual_offset.y + (y1+1) * self.current_tile_size.height; - - let need_new_virtual_offset = tx0 < 0 || - ty0 < 0 || - tx1 >= virtual_surface_size || - ty1 >= virtual_surface_size; - - if need_new_virtual_offset { - // Calculate a new virtual offset, centered around the middle of the - // current tile grid. This means we won't need to invalidate and get - // a new offset for a long time! - self.virtual_offset = DeviceIntPoint::new( - (virtual_surface_size/2) - ((x0 + x1) / 2) * self.current_tile_size.width, - (virtual_surface_size/2) - ((y0 + y1) / 2) * self.current_tile_size.height, - ); + if let CompositorKind::Native { virtual_surface_size, .. } = frame_context.config.compositor_kind { + // We only need to invalidate in this case if the underlying platform + // uses virtual surfaces. + if virtual_surface_size > 0 { + // Get the extremities of the tile grid after virtual offset is applied + let tx0 = self.virtual_offset.x + x0 * self.current_tile_size.width; + let ty0 = self.virtual_offset.y + y0 * self.current_tile_size.height; + let tx1 = self.virtual_offset.x + (x1+1) * self.current_tile_size.width; + let ty1 = self.virtual_offset.y + (y1+1) * self.current_tile_size.height; + + let need_new_virtual_offset = tx0 < 0 || + ty0 < 0 || + tx1 >= virtual_surface_size || + ty1 >= virtual_surface_size; + + if need_new_virtual_offset { + // Calculate a new virtual offset, centered around the middle of the + // current tile grid. This means we won't need to invalidate and get + // a new offset for a long time! + self.virtual_offset = DeviceIntPoint::new( + (virtual_surface_size/2) - ((x0 + x1) / 2) * self.current_tile_size.width, + (virtual_surface_size/2) - ((y0 + y1) / 2) * self.current_tile_size.height, + ); - // Invalidate all native tile surfaces. They will be re-allocated next time - // they are scheduled to be rasterized. - for sub_slice in &mut self.sub_slices { - for tile in sub_slice.tiles.values_mut() { + // Invalidate all native tile surfaces. They will be re-allocated next time + // they are scheduled to be rasterized. + for tile in self.tiles.values_mut() { if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface { if let Some(id) = id.take() { frame_state.resource_cache.destroy_compositor_tile(id); @@ -2780,7 +2812,7 @@ impl TileCacheInstance { // Destroy the native virtual surfaces. They will be re-allocated next time a tile // that references them is scheduled to draw. - if let Some(native_surface) = sub_slice.native_surface.take() { + if let Some(native_surface) = self.native_surface.take() { frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque); frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha); } @@ -2790,23 +2822,34 @@ impl TileCacheInstance { // Rebuild the tile grid if the picture cache rect has changed. if new_tile_rect != self.tile_rect { - for sub_slice in &mut self.sub_slices { - let mut old_tiles = sub_slice.resize(new_tile_rect); + let mut old_tiles = mem::replace(&mut self.tiles, FastHashMap::default()); + self.tiles.reserve(new_tile_rect.size.area() as usize); - // When old tiles that remain after the loop, dirty rects are not valid. - if !old_tiles.is_empty() { - frame_state.composite_state.dirty_rects_are_valid = false; + for y in y0 .. y1 { + for x in x0 .. x1 { + let key = TileOffset::new(x, y); + let tile = old_tiles + .remove(&key) + .unwrap_or_else(|| { + Box::new(Tile::new(key)) + }); + self.tiles.insert(key, tile); } + } - // Any old tiles that remain after the loop above are going to be dropped. For - // simple composite mode, the texture cache handle will expire and be collected - // by the texture cache. For native compositor mode, we need to explicitly - // invoke a callback to the client to destroy that surface. - frame_state.composite_state.destroy_native_tiles( - old_tiles.values_mut(), - frame_state.resource_cache, - ); + // When old tiles that remain after the loop, dirty rects are not valid. + if !old_tiles.is_empty() { + frame_state.composite_state.dirty_rects_are_valid = false; } + + // Any old tiles that remain after the loop above are going to be dropped. For + // simple composite mode, the texture cache handle will expire and be collected + // by the texture cache. For native compositor mode, we need to explicitly + // invoke a callback to the client to destroy that surface. + frame_state.composite_state.destroy_native_tiles( + old_tiles.values_mut(), + frame_state.resource_cache, + ); } // This is duplicated information from tile_rect, but cached here to avoid @@ -2817,10 +2860,9 @@ impl TileCacheInstance { let mut world_culling_rect = WorldRect::zero(); - let mut ctx = TilePreUpdateContext { + let ctx = TilePreUpdateContext { pic_to_world_mapper, fract_offset: self.fract_offset, - device_fract_offset: self.device_fract_offset, background_color: self.background_color, global_screen_world_rect: frame_context.global_screen_world_rect, tile_size: self.tile_size, @@ -2828,50 +2870,43 @@ impl TileCacheInstance { }; // Pre-update each tile - for sub_slice in &mut self.sub_slices { - for tile in sub_slice.tiles.values_mut() { - tile.pre_update(&ctx); - - // Only include the tiles that are currently in view into the world culling - // rect. This is a very important optimization for a couple of reasons: - // (1) Primitives that intersect with tiles in the grid that are not currently - // visible can be skipped from primitive preparation, clip chain building - // and tile dependency updates. - // (2) When we need to allocate an off-screen surface for a child picture (for - // example a CSS filter) we clip the size of the GPU surface to the world - // culling rect below (to ensure we draw enough of it to be sampled by any - // tiles that reference it). Making the world culling rect only affected - // by visible tiles (rather than the entire virtual tile display port) can - // result in allocating _much_ smaller GPU surfaces for cases where the - // true off-screen surface size is very large. - if tile.is_visible { - world_culling_rect = world_culling_rect.union(&tile.world_tile_rect); - } + for tile in self.tiles.values_mut() { + tile.pre_update(&ctx); + + // Only include the tiles that are currently in view into the world culling + // rect. This is a very important optimization for a couple of reasons: + // (1) Primitives that intersect with tiles in the grid that are not currently + // visible can be skipped from primitive preparation, clip chain building + // and tile dependency updates. + // (2) When we need to allocate an off-screen surface for a child picture (for + // example a CSS filter) we clip the size of the GPU surface to the world + // culling rect below (to ensure we draw enough of it to be sampled by any + // tiles that reference it). Making the world culling rect only affected + // by visible tiles (rather than the entire virtual tile display port) can + // result in allocating _much_ smaller GPU surfaces for cases where the + // true off-screen surface size is very large. + if tile.is_visible { + world_culling_rect = world_culling_rect.union(&tile.world_tile_rect); } - - // The background color can only be applied to the first sub-slice. - ctx.background_color = None; } // If compositor mode is changed, need to drop all incompatible tiles. match frame_context.config.compositor_kind { CompositorKind::Draw { .. } => { - for sub_slice in &mut self.sub_slices { - for tile in sub_slice.tiles.values_mut() { - if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface { - if let Some(id) = id.take() { - frame_state.resource_cache.destroy_compositor_tile(id); - } - tile.surface = None; - // Invalidate the entire tile to force a redraw. - tile.invalidate(None, InvalidationReason::CompositorKindChanged); + for tile in self.tiles.values_mut() { + if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { ref mut id, .. }, .. }) = tile.surface { + if let Some(id) = id.take() { + frame_state.resource_cache.destroy_compositor_tile(id); } + tile.surface = None; + // Invalidate the entire tile to force a redraw. + tile.invalidate(None, InvalidationReason::CompositorKindChanged); } + } - if let Some(native_surface) = sub_slice.native_surface.take() { - frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque); - frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha); - } + if let Some(native_surface) = self.native_surface.take() { + frame_state.resource_cache.destroy_compositor_surface(native_surface.opaque); + frame_state.resource_cache.destroy_compositor_surface(native_surface.alpha); } for (_, external_surface) in self.external_native_surface_cache.drain() { @@ -2881,13 +2916,11 @@ impl TileCacheInstance { CompositorKind::Native { .. } => { // This could hit even when compositor mode is not changed, // then we need to check if there are incompatible tiles. - for sub_slice in &mut self.sub_slices { - for tile in sub_slice.tiles.values_mut() { - if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::TextureCache { .. }, .. }) = tile.surface { - tile.surface = None; - // Invalidate the entire tile to force a redraw. - tile.invalidate(None, InvalidationReason::CompositorKindChanged); - } + for tile in self.tiles.values_mut() { + if let Some(TileSurface::Texture { descriptor: SurfaceTextureDescriptor::TextureCache { .. }, .. }) = tile.surface { + tile.surface = None; + // Invalidate the entire tile to force a redraw. + tile.invalidate(None, InvalidationReason::CompositorKindChanged); } } } @@ -2901,8 +2934,7 @@ impl TileCacheInstance { flags: PrimitiveFlags, prim_clip_chain: &ClipChainInstance, prim_spatial_node_index: SpatialNodeIndex, - is_root_tile_cache: bool, - sub_slice_index: usize, + on_picture_surface: bool, frame_context: &FrameVisibilityContext, ) -> SurfacePromotionResult { // Check if this primitive _wants_ to be promoted to a compositor surface. @@ -2911,7 +2943,7 @@ impl TileCacheInstance { } // For now, only support a small (arbitrary) number of compositor surfaces. - if sub_slice_index == MAX_COMPOSITOR_SURFACES { + if self.external_surfaces.len() == MAX_COMPOSITOR_SURFACES { return SurfacePromotionResult::Failed; } @@ -2923,9 +2955,9 @@ impl TileCacheInstance { return SurfacePromotionResult::Failed; } - // If not on the root picture cache, it has some kind of + // If not on the same surface as the picture cache, it has some kind of // complex effect (such as a filter, mix-blend-mode or 3d transform). - if !is_root_tile_cache { + if !on_picture_surface { return SurfacePromotionResult::Failed; } @@ -2942,10 +2974,6 @@ impl TileCacheInstance { return SurfacePromotionResult::Failed; } - if self.slice_flags.contains(SliceFlags::IS_BLEND_CONTAINER) { - return SurfacePromotionResult::Failed; - } - SurfacePromotionResult::Success { flip_y: transform.m22 < 0.0, } @@ -2953,43 +2981,21 @@ impl TileCacheInstance { fn setup_compositor_surfaces_yuv( &mut self, - sub_slice_index: usize, prim_info: &mut PrimitiveDependencyInfo, - flags: PrimitiveFlags, - local_prim_rect: LayoutRect, - prim_spatial_node_index: SpatialNodeIndex, - pic_clip_rect: PictureRect, + prim_rect: PictureRect, frame_context: &FrameVisibilityContext, image_dependencies: &[ImageDependency;3], api_keys: &[ImageKey; 3], resource_cache: &mut ResourceCache, composite_state: &mut CompositeState, - gpu_cache: &mut GpuCache, image_rendering: ImageRendering, color_depth: ColorDepth, color_space: YuvColorSpace, format: YuvFormat, ) -> bool { - for &key in api_keys { - if key != ImageKey::DUMMY { - // TODO: See comment in setup_compositor_surfaces_rgb. - resource_cache.request_image(ImageRequest { - key, - rendering: image_rendering, - tile: None, - }, - gpu_cache, - ); - } - } - self.setup_compositor_surfaces_impl( - sub_slice_index, prim_info, - flags, - local_prim_rect, - prim_spatial_node_index, - pic_clip_rect, + prim_rect, frame_context, ExternalSurfaceDependency::Yuv { image_dependencies: *image_dependencies, @@ -3001,54 +3007,26 @@ impl TileCacheInstance { resource_cache, composite_state, image_rendering, - true, ) } fn setup_compositor_surfaces_rgb( &mut self, - sub_slice_index: usize, prim_info: &mut PrimitiveDependencyInfo, - flags: PrimitiveFlags, - local_prim_rect: LayoutRect, - prim_spatial_node_index: SpatialNodeIndex, - pic_clip_rect: PictureRect, + prim_rect: PictureRect, frame_context: &FrameVisibilityContext, image_dependency: ImageDependency, api_key: ImageKey, resource_cache: &mut ResourceCache, composite_state: &mut CompositeState, - gpu_cache: &mut GpuCache, image_rendering: ImageRendering, flip_y: bool, ) -> bool { let mut api_keys = [ImageKey::DUMMY; 3]; api_keys[0] = api_key; - - // TODO: The picture compositing code requires images promoted - // into their own picture cache slices to be requested every - // frame even if they are not visible. However the image updates - // are only reached on the prepare pass for visible primitives. - // So we make sure to trigger an image request when promoting - // the image here. - resource_cache.request_image(ImageRequest { - key: api_key, - rendering: image_rendering, - tile: None, - }, - gpu_cache, - ); - - let is_opaque = resource_cache.get_image_properties(api_key) - .map_or(false, |properties| properties.descriptor.is_opaque()); - self.setup_compositor_surfaces_impl( - sub_slice_index, prim_info, - flags, - local_prim_rect, - prim_spatial_node_index, - pic_clip_rect, + prim_rect, frame_context, ExternalSurfaceDependency::Rgb { image_dependency, @@ -3058,7 +3036,6 @@ impl TileCacheInstance { resource_cache, composite_state, image_rendering, - is_opaque, ) } @@ -3066,37 +3043,16 @@ impl TileCacheInstance { // and the non-compositor path should be used to draw it instead. fn setup_compositor_surfaces_impl( &mut self, - sub_slice_index: usize, prim_info: &mut PrimitiveDependencyInfo, - flags: PrimitiveFlags, - local_prim_rect: LayoutRect, - prim_spatial_node_index: SpatialNodeIndex, - pic_clip_rect: PictureRect, + prim_rect: PictureRect, frame_context: &FrameVisibilityContext, dependency: ExternalSurfaceDependency, api_keys: &[ImageKey; 3], resource_cache: &mut ResourceCache, composite_state: &mut CompositeState, image_rendering: ImageRendering, - is_opaque: bool, ) -> bool { - let map_local_to_surface = SpaceMapper::new_with_target( - self.spatial_node_index, - prim_spatial_node_index, - self.local_rect, - frame_context.spatial_tree, - ); - - // Map the primitive local rect into picture space. - let prim_rect = match map_local_to_surface.map(&local_prim_rect) { - Some(rect) => rect, - None => return true, - }; - - // If the rect is invalid, no need to create dependencies. - if prim_rect.size.is_empty() { - return true; - } + prim_info.is_compositor_surface = true; let pic_to_world_mapper = SpaceMapper::new_with_target( ROOT_SPATIAL_NODE_INDEX, @@ -3105,6 +3061,9 @@ impl TileCacheInstance { frame_context.spatial_tree, ); + let world_rect = pic_to_world_mapper + .map(&prim_rect) + .expect("bug: unable to map the primitive to world space"); let world_clip_rect = pic_to_world_mapper .map(&prim_info.prim_clip_box.to_rect()) .expect("bug: unable to map clip to world space"); @@ -3114,54 +3073,17 @@ impl TileCacheInstance { return true; } - let world_rect = pic_to_world_mapper - .map(&prim_rect) - .expect("bug: unable to map the primitive to world space"); - let device_rect = (world_rect * frame_context.global_device_pixel_scale).round(); - // TODO(gw): Is there any case where if the primitive ends up on a fractional // boundary we want to _skip_ promoting to a compositor surface and // draw it as part of the content? - let (surface_rect, transform) = match composite_state.compositor_kind { - CompositorKind::Draw { .. } => { - (device_rect, Transform3D::identity()) - } - CompositorKind::Native { .. } => { - // If we have a Native Compositor, then we can support doing the transformation - // as part of compositing. Use the local prim rect for the external surface, and - // compute the full local to device transform to provide to the compositor. - let surface_to_world_mapper : SpaceMapper<PicturePixel, WorldPixel> = SpaceMapper::new_with_target( - ROOT_SPATIAL_NODE_INDEX, - prim_spatial_node_index, - frame_context.global_screen_world_rect, - frame_context.spatial_tree, - ); - let prim_origin = Vector3D::new(local_prim_rect.origin.x, local_prim_rect.origin.y, 0.0); - let world_to_device_scale = Transform3D::from_scale(frame_context.global_device_pixel_scale); - let transform = surface_to_world_mapper.get_transform().pre_translate(prim_origin).then(&world_to_device_scale); - - (local_prim_rect.cast_unit(), transform) - } - }; - + let device_rect = (world_rect * frame_context.global_device_pixel_scale).round(); let clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round(); - if surface_rect.size.width >= MAX_COMPOSITOR_SURFACES_SIZE || - surface_rect.size.height >= MAX_COMPOSITOR_SURFACES_SIZE { + if device_rect.size.width >= MAX_COMPOSITOR_SURFACES_SIZE || + device_rect.size.height >= MAX_COMPOSITOR_SURFACES_SIZE { return false; } - // If this primitive is an external image, and supports being used - // directly by a native compositor, then lookup the external image id - // so we can pass that through. - let external_image_id = if flags.contains(PrimitiveFlags::SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE) { - resource_cache.get_image_properties(api_keys[0]) - .and_then(|properties| properties.external_image) - .and_then(|image| Some(image.id)) - } else { - None - }; - // When using native compositing, we need to find an existing native surface // handle to use, or allocate a new one. For existing native surfaces, we can // also determine whether this needs to be updated, depending on whether the @@ -3171,45 +3093,33 @@ impl TileCacheInstance { (None, None) } CompositorKind::Native { .. } => { - let native_surface_size = surface_rect.size.round().to_i32(); + let native_surface_size = device_rect.size.round().to_i32(); let key = ExternalNativeSurfaceKey { image_keys: *api_keys, size: native_surface_size, - is_external_surface: external_image_id.is_some(), }; let native_surface = self.external_native_surface_cache .entry(key) .or_insert_with(|| { - // No existing surface, so allocate a new compositor surface. - let native_surface_id = match external_image_id { - Some(_external_image) => { - // If we have a suitable external image, then create an external - // surface to attach to. - resource_cache.create_compositor_external_surface(is_opaque) - } - None => { - // Otherwise create a normal compositor surface and a single - // compositor tile that covers the entire surface. - let native_surface_id = - resource_cache.create_compositor_surface( - DeviceIntPoint::zero(), - native_surface_size, - is_opaque, - ); + // No existing surface, so allocate a new compositor surface and + // a single compositor tile that covers the entire compositor surface. - let tile_id = NativeTileId { - surface_id: native_surface_id, - x: 0, - y: 0, - }; - resource_cache.create_compositor_tile(tile_id); + let native_surface_id = resource_cache.create_compositor_surface( + DeviceIntPoint::zero(), + native_surface_size, + true, + ); - native_surface_id - } + let tile_id = NativeTileId { + surface_id: native_surface_id, + x: 0, + y: 0, }; + resource_cache.create_compositor_tile(tile_id); + ExternalNativeSurface { used_this_frame: true, native_surface_id, @@ -3221,65 +3131,41 @@ impl TileCacheInstance { // backing native surface handle isn't freed. native_surface.used_this_frame = true; - let update_params = match external_image_id { - Some(external_image) => { - // If this is an external image surface, then there's no update - // to be done. Just attach the current external image to the surface - // and we're done. - resource_cache.attach_compositor_external_image( - native_surface.native_surface_id, - external_image, - ); - None - } - None => { - // If the image dependencies match, there is no need to update - // the backing native surface. - match dependency { - ExternalSurfaceDependency::Yuv{ image_dependencies, .. } => { - if image_dependencies == native_surface.image_dependencies { - None - } else { - Some(native_surface_size) - } - }, - ExternalSurfaceDependency::Rgb{ image_dependency, .. } => { - if image_dependency == native_surface.image_dependencies[0] { - None - } else { - Some(native_surface_size) - } - }, - } - } + // If the image dependencies match, there is no need to update + // the backing native surface. + let update_params = match dependency { + ExternalSurfaceDependency::Yuv{ image_dependencies, .. } => { + if image_dependencies == native_surface.image_dependencies { + None + } else { + Some(native_surface_size) + } + }, + ExternalSurfaceDependency::Rgb{ image_dependency, .. } => { + if image_dependency == native_surface.image_dependencies[0] { + None + } else { + Some(native_surface_size) + } + }, }; (Some(native_surface.native_surface_id), update_params) } }; - // For compositor surfaces, if we didn't find an earlier sub-slice to add to, - // we know we can append to the current slice. - assert!(sub_slice_index < self.sub_slices.len() - 1); - let sub_slice = &mut self.sub_slices[sub_slice_index]; - // Each compositor surface allocates a unique z-id - sub_slice.compositor_surfaces.push(CompositorSurface { - prohibited_rect: pic_clip_rect, - is_opaque, - descriptor: ExternalSurfaceDescriptor { - local_rect: prim_info.prim_clip_box.to_rect(), - local_clip_rect: prim_info.prim_clip_box.to_rect(), - dependency, - image_rendering, - device_rect, - surface_rect, - clip_rect, - transform: transform.cast_unit(), - z_id: ZBufferId::invalid(), - native_surface_id, - update_params, - }, + self.external_surfaces.push(ExternalSurfaceDescriptor { + local_rect: prim_info.prim_clip_box.to_rect(), + world_rect, + local_clip_rect: prim_info.prim_clip_box.to_rect(), + dependency, + image_rendering, + device_rect, + clip_rect, + z_id: composite_state.z_generator.next(), + native_surface_id, + update_params, }); true @@ -3290,22 +3176,45 @@ impl TileCacheInstance { &mut self, prim_instance: &mut PrimitiveInstance, prim_spatial_node_index: SpatialNodeIndex, + prim_clip_chain: Option<&ClipChainInstance>, local_prim_rect: LayoutRect, frame_context: &FrameVisibilityContext, data_stores: &DataStores, clip_store: &ClipStore, pictures: &[PicturePrimitive], resource_cache: &mut ResourceCache, + opacity_binding_store: &OpacityBindingStorage, color_bindings: &ColorBindingStorage, + image_instances: &ImageInstanceStorage, surface_stack: &[SurfaceIndex], composite_state: &mut CompositeState, - gpu_cache: &mut GpuCache, - is_root_tile_cache: bool, - ) { + ) -> Option<PrimitiveVisibilityFlags> { // This primitive exists on the last element on the current surface stack. profile_scope!("update_prim_dependencies"); let prim_surface_index = *surface_stack.last().unwrap(); - let prim_clip_chain = &prim_instance.vis.clip_chain; + + // If the primitive is completely clipped out by the clip chain, there + // 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 None, + }; + + self.map_local_to_surface.set_target_spatial_node( + prim_spatial_node_index, + frame_context.spatial_tree, + ); + + // 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 None, + }; + + // If the rect is invalid, no need to create dependencies. + if prim_rect.size.is_empty() { + return None; + } // If the primitive is directly drawn onto this picture cache surface, then // the pic_clip_rect is in the same space. If not, we need to map it from @@ -3344,7 +3253,7 @@ impl TileCacheInstance { rect.inflate(surface.inflation_factor, surface.inflation_factor) } None => { - return; + return None; } }; @@ -3360,7 +3269,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; + return None; } // Build the list of resources that this primitive has dependencies on. @@ -3369,30 +3278,6 @@ impl TileCacheInstance { pic_clip_rect.to_box2d(), ); - let mut sub_slice_index = self.sub_slices.len() - 1; - - // Only need to evaluate sub-slice regions if we have compositor surfaces present - if sub_slice_index > 0 { - // Find the first sub-slice we can add this primitive to (we want to add - // prims to the primary surface if possible, so they get subpixel AA). - for (i, sub_slice) in self.sub_slices.iter_mut().enumerate() { - let mut intersects_prohibited_region = false; - - for surface in &mut sub_slice.compositor_surfaces { - if pic_clip_rect.intersects(&surface.prohibited_rect) { - surface.prohibited_rect = surface.prohibited_rect.union(&pic_clip_rect); - - intersects_prohibited_region = true; - } - } - - if !intersects_prohibited_region { - sub_slice_index = i; - break; - } - } - } - // Include the prim spatial node, if differs relative to cache root. if prim_spatial_node_index != self.spatial_node_index { prim_info.spatial_nodes.push(prim_spatial_node_index); @@ -3416,6 +3301,7 @@ impl TileCacheInstance { // then applied below. let mut backdrop_candidate = None; + // For pictures, we don't (yet) know the valid clip rect, so we can't correctly // use it to calculate the local bounding rect for the tiles. If we include them // then we may calculate a bounding rect that is too large, since it won't include @@ -3435,78 +3321,84 @@ impl TileCacheInstance { prim_info.opacity_bindings.push(binding.into()); } } - PrimitiveInstanceKind::Rectangle { data_handle, color_binding_index, .. } => { - // Rectangles can only form a backdrop candidate if they are known opaque. - // TODO(gw): We could resolve the opacity binding here, but the common - // case for background rects is that they don't have animated opacity. - let color = match data_stores.prim[data_handle].kind { - PrimitiveTemplateKind::Rectangle { color, .. } => { - frame_context.scene_properties.resolve_color(&color) + PrimitiveInstanceKind::Rectangle { data_handle, opacity_binding_index, color_binding_index, .. } => { + if opacity_binding_index == OpacityBindingIndex::INVALID { + // Rectangles can only form a backdrop candidate if they are known opaque. + // TODO(gw): We could resolve the opacity binding here, but the common + // case for background rects is that they don't have animated opacity. + let color = match data_stores.prim[data_handle].kind { + PrimitiveTemplateKind::Rectangle { color, .. } => { + frame_context.scene_properties.resolve_color(&color) + } + _ => unreachable!(), + }; + if color.a >= 1.0 { + backdrop_candidate = Some(BackdropInfo { + opaque_rect: pic_clip_rect, + kind: Some(BackdropKind::Color { color }), + }); + } + } else { + let opacity_binding = &opacity_binding_store[opacity_binding_index]; + for binding in &opacity_binding.bindings { + prim_info.opacity_bindings.push(OpacityBinding::from(*binding)); } - _ => unreachable!(), - }; - if color.a >= 1.0 { - backdrop_candidate = Some(BackdropInfo { - opaque_rect: pic_clip_rect, - kind: Some(BackdropKind::Color { color }), - }); } if color_binding_index != ColorBindingIndex::INVALID { prim_info.color_binding = Some(color_bindings[color_binding_index].into()); } } - PrimitiveInstanceKind::Image { data_handle, ref mut is_compositor_surface, .. } => { + PrimitiveInstanceKind::Image { data_handle, image_instance_index, ref mut is_compositor_surface, .. } => { let image_key = &data_stores.image[data_handle]; let image_data = &image_key.kind; + let image_instance = &image_instances[image_instance_index]; + let opacity_binding_index = image_instance.opacity_binding_index; let mut promote_to_surface = false; let mut promote_with_flip_y = false; - match self.can_promote_to_surface(image_key.common.flags, - prim_clip_chain, - prim_spatial_node_index, - is_root_tile_cache, - sub_slice_index, - frame_context) { - SurfacePromotionResult::Failed => { - } - SurfacePromotionResult::Success{flip_y} => { - promote_to_surface = true; - promote_with_flip_y = flip_y; + // If picture caching is disabled, we can't support any compositor surfaces. + if composite_state.picture_caching_is_enabled { + match self.can_promote_to_surface(image_key.common.flags, + prim_clip_chain, + prim_spatial_node_index, + on_picture_surface, + frame_context) { + SurfacePromotionResult::Failed => { + } + SurfacePromotionResult::Success{flip_y} => { + promote_to_surface = true; + promote_with_flip_y = flip_y; + } } } - // Native OS compositors (DC and CA, at least) support premultiplied alpha - // only. If we have an image that's not pre-multiplied alpha, we can't promote it. - if image_data.alpha_type == AlphaType::Alpha { - promote_to_surface = false; - } - - if let Some(image_properties) = resource_cache.get_image_properties(image_data.key) { - // 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 - // - Have opaque alpha in the instance (flattened) color - if image_properties.descriptor.is_opaque() && - image_properties.tiling.is_none() && - image_data.tile_spacing == LayoutSize::zero() && - image_data.color.a >= 1.0 { - backdrop_candidate = Some(BackdropInfo { - opaque_rect: pic_clip_rect, - kind: None, - }); + if opacity_binding_index == OpacityBindingIndex::INVALID { + if let Some(image_properties) = resource_cache.get_image_properties(image_data.key) { + // 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 { + let opacity_binding = &opacity_binding_store[opacity_binding_index]; + for binding in &opacity_binding.bindings { + prim_info.opacity_bindings.push(OpacityBinding::from(*binding)); } } if promote_to_surface { promote_to_surface = self.setup_compositor_surfaces_rgb( - sub_slice_index, &mut prim_info, - image_key.common.flags, - local_prim_rect, - prim_spatial_node_index, - pic_clip_rect, + prim_rect, frame_context, ImageDependency { key: image_data.key, @@ -3515,39 +3407,42 @@ impl TileCacheInstance { image_data.key, resource_cache, composite_state, - gpu_cache, image_data.image_rendering, promote_with_flip_y, ); } - *is_compositor_surface = promote_to_surface; - - if promote_to_surface { - prim_instance.vis.state = VisibilityState::Culled; - return; - } else { + if !promote_to_surface { prim_info.images.push(ImageDependency { key: image_data.key, generation: resource_cache.get_image_generation(image_data.key), }); } + + *is_compositor_surface = promote_to_surface; } PrimitiveInstanceKind::YuvImage { data_handle, ref mut is_compositor_surface, .. } => { let prim_data = &data_stores.yuv_image[data_handle]; - let mut promote_to_surface = match self.can_promote_to_surface( - prim_data.common.flags, - prim_clip_chain, - prim_spatial_node_index, - is_root_tile_cache, - sub_slice_index, - frame_context) { - SurfacePromotionResult::Failed => false, - SurfacePromotionResult::Success{flip_y} => !flip_y, - }; + // TODO(gw): For now, we only support promoting YUV primitives to be compositor + // surfaces. However, some videos are RGBA images. As a follow up, + // extract the logic below and support RGBA compositor surfaces too. + let mut promote_to_surface = false; + + // If picture caching is disabled, we can't support any compositor surfaces. + if composite_state.picture_caching_is_enabled { + promote_to_surface = match self.can_promote_to_surface( + prim_data.common.flags, + prim_clip_chain, + prim_spatial_node_index, + on_picture_surface, + frame_context) { + SurfacePromotionResult::Failed => false, + SurfacePromotionResult::Success{flip_y} => !flip_y, + }; - // TODO(gw): When we support RGBA images for external surfaces, we also - // need to check if opaque (YUV images are implicitly opaque). + // TODO(gw): When we support RGBA images for external surfaces, we also + // need to check if opaque (YUV images are implicitly opaque). + } // If this primitive is being promoted to a surface, construct an external // surface descriptor for use later during batching and compositing. We only @@ -3566,18 +3461,13 @@ impl TileCacheInstance { } promote_to_surface = self.setup_compositor_surfaces_yuv( - sub_slice_index, &mut prim_info, - prim_data.common.flags, - local_prim_rect, - prim_spatial_node_index, - pic_clip_rect, + prim_rect, frame_context, &image_dependencies, &prim_data.kind.yuv_key, resource_cache, composite_state, - gpu_cache, prim_data.kind.image_rendering, prim_data.kind.color_depth, prim_data.kind.color_space, @@ -3585,15 +3475,7 @@ impl TileCacheInstance { ); } - // Store on the YUV primitive instance whether this is a promoted surface. - // This is used by the batching code to determine whether to draw the - // image to the content tiles, or just a transparent z-write. - *is_compositor_surface = promote_to_surface; - - if promote_to_surface { - prim_instance.vis.state = VisibilityState::Culled; - return; - } else { + if !promote_to_surface { prim_info.images.extend( prim_data.kind.yuv_key.iter().map(|key| { ImageDependency { @@ -3603,6 +3485,12 @@ impl TileCacheInstance { }) ); } + + // Store on the YUV primitive instance whether this is a promoted surface. + // This is used by the batching code to determine whether to draw the + // image to the content tiles, or just a transparent z-write. + *is_compositor_surface = promote_to_surface; + } PrimitiveInstanceKind::ImageBorder { data_handle, .. } => { let border_data = &data_stores.image_border[data_handle].kind; @@ -3617,8 +3505,7 @@ impl TileCacheInstance { kind: Some(BackdropKind::Clear), }); } - PrimitiveInstanceKind::LinearGradient { data_handle, .. } - | PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => { + PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { let gradient_data = &data_stores.linear_grad[data_handle]; if gradient_data.stops_opacity.is_opaque && gradient_data.tile_spacing == LayoutSize::zero() @@ -3663,8 +3550,6 @@ impl TileCacheInstance { // checks to see if it matches all conditions to be a backdrop. let mut vis_flags = PrimitiveVisibilityFlags::empty(); - let sub_slice = &mut self.sub_slices[sub_slice_index]; - if let Some(backdrop_candidate) = backdrop_candidate { let is_suitable_backdrop = match backdrop_candidate.kind { Some(BackdropKind::Clear) => { @@ -3696,10 +3581,9 @@ impl TileCacheInstance { } }; - if sub_slice_index == 0 && - is_suitable_backdrop && - sub_slice.compositor_surfaces.is_empty() && - !prim_clip_chain.needs_mask { + if is_suitable_backdrop + && 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; @@ -3742,19 +3626,13 @@ impl TileCacheInstance { for x in p0.x .. p1.x { // TODO(gw): Convert to 2d array temporarily to avoid hash lookups per-tile? let key = TileOffset::new(x, y); - let tile = sub_slice.tiles.get_mut(&key).expect("bug: no tile"); + let tile = self.tiles.get_mut(&key).expect("bug: no tile"); tile.add_prim_dependency(&prim_info); } } - prim_instance.vis.state = VisibilityState::Coarse { - filter: BatchFilter { - rect_in_pic_space: pic_clip_rect, - sub_slice_index: SubSliceIndex::new(sub_slice_index), - }, - vis_flags, - }; + Some(vis_flags) } /// Print debug information about this picture cache to a tree printer. @@ -3766,38 +3644,30 @@ impl TileCacheInstance { // diff'ing the invalidation states in a visual tool. let mut pt = PrintTree::new("Picture Cache"); - pt.new_level(format!("Slice {:?}", self.slice)); + pt.new_level(format!("Slice {}", self.slice)); pt.add_item(format!("fract_offset: {:?}", self.fract_offset)); pt.add_item(format!("background_color: {:?}", self.background_color)); - for (sub_slice_index, sub_slice) in self.sub_slices.iter().enumerate() { - pt.new_level(format!("SubSlice {:?}", sub_slice_index)); - - for y in self.tile_bounds_p0.y .. self.tile_bounds_p1.y { - for x in self.tile_bounds_p0.x .. self.tile_bounds_p1.x { - let key = TileOffset::new(x, y); - let tile = &sub_slice.tiles[&key]; - tile.print(&mut pt); - } + for y in self.tile_bounds_p0.y .. self.tile_bounds_p1.y { + for x in self.tile_bounds_p0.x .. self.tile_bounds_p1.x { + let key = TileOffset::new(x, y); + let tile = &self.tiles[&key]; + tile.print(&mut pt); } - - pt.end_level(); } pt.end_level(); } fn calculate_subpixel_mode(&self) -> SubpixelMode { - let has_opaque_bg_color = self.background_color.map_or(false, |c| c.a >= 1.0); - // If the overall tile cache is known opaque, subpixel AA is allowed everywhere - if has_opaque_bg_color { + if self.is_opaque() { return SubpixelMode::Allow; } // If we didn't find any valid opaque backdrop, no subpixel AA allowed - if self.backdrop.opaque_rect.is_empty() { + if !self.backdrop.opaque_rect.is_well_formed_and_nonempty() { return SubpixelMode::Deny; } @@ -3808,15 +3678,26 @@ impl TileCacheInstance { return SubpixelMode::Allow; } - // If none of the simple cases above match, we need test where we can support subpixel AA. + // If none of the simple cases above match, we need to build a list + // of excluded rects (compositor surfaces) and a valid inclusion rect + // (known opaque area) where we can support subpixel AA. // TODO(gw): In future, it may make sense to have > 1 inclusion rect, // but this handles the common cases. // TODO(gw): If a text run gets animated such that it's moving in a way that is // sometimes intersecting with the video rect, this can result in subpixel // AA flicking on/off for that text run. It's probably very rare, but // something we should handle in future. + + let excluded_rects = self.external_surfaces + .iter() + .map(|s| { + s.local_rect + }) + .collect(); + SubpixelMode::Conditional { allowed_rect: self.backdrop.opaque_rect, + excluded_rects, } } @@ -3828,7 +3709,7 @@ impl TileCacheInstance { frame_context: &FrameVisibilityContext, frame_state: &mut FrameVisibilityState, ) { - self.dirty_region.reset(self.spatial_node_index); + self.dirty_region.clear(); self.subpixel_mode = self.calculate_subpixel_mode(); let map_pic_to_world = SpaceMapper::new_with_target( @@ -3838,14 +3719,56 @@ impl TileCacheInstance { 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.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) + }); + + if let Some(backdrop_rect) = backdrop_rect { + let world_backdrop_rect = map_pic_to_world + .map(&backdrop_rect) + .expect("bug: unable to map backdrop to world space"); + + // Since we register the entire backdrop rect, use the opaque z-id for the + // picture cache slice. + frame_state.composite_state.register_occluder( + self.z_id_opaque, + world_backdrop_rect, + ); + } + } + + // Register any external compositor surfaces as potential occluders. This + // is especially useful when viewing video in full-screen mode, as it is + // able to occlude every background tile (avoiding allocation, rasterizion + // and compositing). + for external_surface in &self.external_surfaces { + 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 // surfaces that were not referenced during the update_prim_dependencies pass. self.external_native_surface_cache.retain(|_, surface| { if !surface.used_this_frame { - // If we removed an external surface, we need to mark the dirty rects as - // invalid so a full composite occurs on the next frame. - frame_state.composite_state.dirty_rects_are_valid = false; - frame_state.resource_cache.destroy_compositor_surface(surface.native_surface_id); } @@ -3860,21 +3783,10 @@ impl TileCacheInstance { .get_relative_transform( self.spatial_node_index, ROOT_SPATIAL_NODE_INDEX, - ); - let root_transform = match root_transform { - CoordinateSpaceMapping::Local => ScaleOffset::identity(), - CoordinateSpaceMapping::ScaleOffset(scale_offset) => scale_offset, - CoordinateSpaceMapping::Transform(..) => panic!("bug: picture caches don't support complex transforms"), - }; - const EPSILON: f32 = 0.001; - let root_translation_changed = - !root_transform.offset.x.approx_eq_eps(&self.root_transform.offset.x, &EPSILON) || - !root_transform.offset.y.approx_eq_eps(&self.root_transform.offset.y, &EPSILON); - let root_scale_changed = - !root_transform.scale.x.approx_eq_eps(&self.root_transform.scale.x, &EPSILON) || - !root_transform.scale.y.approx_eq_eps(&self.root_transform.scale.y, &EPSILON); - - if root_translation_changed || root_scale_changed || frame_context.config.force_invalidation { + ) + .into(); + let root_transform_changed = root_transform != self.root_transform; + if root_transform_changed { self.root_transform = root_transform; frame_state.composite_state.dirty_rects_are_valid = false; } @@ -3886,17 +3798,21 @@ impl TileCacheInstance { frame_context.spatial_tree, ); - let mut ctx = TilePostUpdateContext { + // All compositor surfaces have allocated a z_id, so reserve a z_id for alpha tiles. + let z_id_alpha = frame_state.composite_state.z_generator.next(); + + let ctx = TilePostUpdateContext { pic_to_world_mapper, global_device_pixel_scale: frame_context.global_device_pixel_scale, local_clip_rect: self.local_clip_rect, - backdrop: None, + backdrop: self.backdrop, opacity_bindings: &self.opacity_bindings, color_bindings: &self.color_bindings, current_tile_size: self.current_tile_size, local_rect: self.local_rect, - z_id: ZBufferId::invalid(), - invalidate_all: root_scale_changed || frame_context.config.force_invalidation, + external_surfaces: &self.external_surfaces, + z_id_opaque: self.z_id_opaque, + z_id_alpha, }; let mut state = TilePostUpdateState { @@ -3908,115 +3824,30 @@ impl TileCacheInstance { // Step through each tile and invalidate if the dependencies have changed. Determine // the current opacity setting and whether it's changed. - for (i, sub_slice) in self.sub_slices.iter_mut().enumerate().rev() { - // The backdrop is only relevant for the first sub-slice - if i == 0 { - ctx.backdrop = Some(self.backdrop); - } - - for compositor_surface in sub_slice.compositor_surfaces.iter_mut().rev() { - compositor_surface.descriptor.z_id = state.composite_state.z_generator.next(); - } - - ctx.z_id = state.composite_state.z_generator.next(); - - for tile in sub_slice.tiles.values_mut() { - tile.post_update(&ctx, &mut state, frame_context); - } + for tile in self.tiles.values_mut() { + tile.post_update(&ctx, &mut state, frame_context); } - // Register any opaque external compositor surfaces as potential occluders. This - // is especially useful when viewing video in full-screen mode, as it is - // able to occlude every background tile (avoiding allocation, rasterizion - // and compositing). - - for sub_slice in &self.sub_slices { - for compositor_surface in &sub_slice.compositor_surfaces { - if compositor_surface.is_opaque { - let local_surface_rect = compositor_surface - .descriptor - .local_rect - .intersection(&compositor_surface.descriptor.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( - compositor_surface.descriptor.z_id, - world_surface_rect, - ); - } - } - } - } - - // 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.opaque_rect.is_empty() { - let z_id_backdrop = frame_state.composite_state.z_generator.next(); - - let backdrop_rect = self.backdrop.opaque_rect - .intersection(&self.local_rect) - .and_then(|r| { - r.intersection(&self.local_clip_rect) - }); - - if let Some(backdrop_rect) = backdrop_rect { - let world_backdrop_rect = map_pic_to_world - .map(&backdrop_rect) - .expect("bug: unable to map backdrop to world space"); - - // Since we register the entire backdrop rect, use the opaque z-id for the - // picture cache slice. - frame_state.composite_state.register_occluder( - z_id_backdrop, - world_backdrop_rect, - ); - } - } - } -} - -pub struct PictureScratchBuffer { - surface_stack: Vec<SurfaceIndex>, - clip_chain_ids: Vec<ClipChainId>, -} - -impl Default for PictureScratchBuffer { - fn default() -> Self { - PictureScratchBuffer { - surface_stack: Vec::new(), - clip_chain_ids: Vec::new(), + // When under test, record a copy of the dirty region to support + // invalidation testing in wrench. + if frame_context.config.testing { + frame_state.scratch.recorded_dirty_regions.push(self.dirty_region.record()); } } } -impl PictureScratchBuffer { - pub fn begin_frame(&mut self) { - self.surface_stack.clear(); - self.clip_chain_ids.clear(); - } - - pub fn recycle(&mut self, recycler: &mut Recycler) { - recycler.recycle_vec(&mut self.surface_stack); - } - } - /// Maintains a stack of picture and surface information, that /// is used during the initial picture traversal. pub struct PictureUpdateState<'a> { surfaces: &'a mut Vec<SurfaceInfo>, surface_stack: Vec<SurfaceIndex>, + picture_stack: Vec<PictureInfo>, + are_raster_roots_assigned: bool, + composite_state: &'a CompositeState, } impl<'a> PictureUpdateState<'a> { pub fn update_all( - buffers: &mut PictureScratchBuffer, surfaces: &'a mut Vec<SurfaceInfo>, pic_index: PictureIndex, picture_primitives: &mut [PicturePrimitive], @@ -4024,17 +3855,19 @@ impl<'a> PictureUpdateState<'a> { gpu_cache: &mut GpuCache, clip_store: &ClipStore, data_stores: &mut DataStores, + composite_state: &CompositeState, ) { profile_scope!("UpdatePictures"); profile_marker!("UpdatePictures"); let mut state = PictureUpdateState { surfaces, - surface_stack: buffers.surface_stack.take().cleared(), + surface_stack: vec![SurfaceIndex(0)], + picture_stack: Vec::new(), + are_raster_roots_assigned: true, + composite_state, }; - state.surface_stack.push(SurfaceIndex(0)); - state.update( pic_index, picture_primitives, @@ -4044,7 +3877,13 @@ impl<'a> PictureUpdateState<'a> { data_stores, ); - buffers.surface_stack = state.surface_stack.take(); + if !state.are_raster_roots_assigned { + state.assign_raster_roots( + pic_index, + picture_primitives, + ROOT_SPATIAL_NODE_INDEX, + ); + } } /// Return the current surface @@ -4073,6 +3912,21 @@ impl<'a> PictureUpdateState<'a> { self.surface_stack.pop().unwrap() } + /// Push information about a picture on the update stack + fn push_picture( + &mut self, + info: PictureInfo, + ) { + self.picture_stack.push(info); + } + + /// Pop the picture info off, on the way up the picture traversal + fn pop_picture( + &mut self, + ) -> PictureInfo { + self.picture_stack.pop().unwrap() + } + /// Update a picture, determining surface configuration, /// rasterization roots, and (in future) whether there /// are cached surfaces that can be used by this picture. @@ -4089,15 +3943,24 @@ impl<'a> PictureUpdateState<'a> { self, frame_context, ) { - for child_pic_index in &prim_list.child_pictures { - self.update( - *child_pic_index, - picture_primitives, - frame_context, - gpu_cache, - clip_store, - data_stores, - ); + for cluster in &prim_list.clusters { + if cluster.flags.contains(ClusterFlags::IS_PICTURE) { + for prim_instance in &cluster.prim_instances { + let child_pic_index = match prim_instance.kind { + PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index, + _ => unreachable!(), + }; + + self.update( + child_pic_index, + picture_primitives, + frame_context, + gpu_cache, + clip_store, + data_stores, + ); + } + } } picture_primitives[pic_index.0].post_update( @@ -4108,6 +3971,48 @@ impl<'a> PictureUpdateState<'a> { ); } } + + /// Process the picture tree again in a depth-first order, + /// and adjust the raster roots of the pictures that want to establish + /// their own roots but are not able to due to the size constraints. + fn assign_raster_roots( + &mut self, + pic_index: PictureIndex, + picture_primitives: &[PicturePrimitive], + fallback_raster_spatial_node: SpatialNodeIndex, + ) { + let picture = &picture_primitives[pic_index.0]; + if !picture.is_visible() { + return + } + + let new_fallback = match picture.raster_config { + Some(ref config) => { + let surface = &mut self.surfaces[config.surface_index.0]; + if !config.establishes_raster_root { + surface.raster_spatial_node_index = fallback_raster_spatial_node; + } + surface.raster_spatial_node_index + } + None => fallback_raster_spatial_node, + }; + + for cluster in &picture.prim_list.clusters { + if cluster.flags.contains(ClusterFlags::IS_PICTURE) { + for instance in &cluster.prim_instances { + let child_pic_index = match instance.kind { + PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index, + _ => unreachable!(), + }; + self.assign_raster_roots( + child_pic_index, + picture_primitives, + new_fallback, + ); + } + } + } + } } #[derive(Debug, Copy, Clone, PartialEq)] @@ -4116,19 +4021,15 @@ pub struct SurfaceIndex(pub usize); pub const ROOT_SURFACE_INDEX: SurfaceIndex = SurfaceIndex(0); -/// Describes the render task configuration for a picture surface. -#[derive(Debug)] -pub enum SurfaceRenderTasks { - /// The common type of surface is a single render task - Simple(RenderTaskId), - /// Some surfaces draw their content, and then have further tasks applied - /// to that input (such as blur passes for shadows). These tasks have a root - /// (the output of the surface), and a port (for attaching child task dependencies - /// to the content). - Chained { root_task_id: RenderTaskId, port_task_id: RenderTaskId }, - /// Picture caches are a single surface consisting of multiple render - /// tasks, one per tile with dirty content. - Tiled(Vec<RenderTaskId>), +#[derive(Debug, Copy, Clone)] +pub struct SurfaceRenderTasks { + /// The root of the render task chain for this surface. This + /// is attached to parent tasks, and also the surface that + /// gets added during batching. + pub root: RenderTaskId, + /// The port of the render task change for this surface. This + /// is where child tasks for this surface get attached to. + pub port: RenderTaskId, } /// Information about an offscreen surface. For now, @@ -4142,8 +4043,6 @@ pub struct SurfaceInfo { /// A local rect defining the size of this surface, in the /// coordinate system of the surface itself. pub rect: PictureRect, - /// Part of the surface that we know to be opaque. - pub opaque_rect: PictureRect, /// Helper structs for mapping local rects in different /// coordinate systems into the surface coordinates. pub map_local_to_surface: SpaceMapper<LayoutPixel, PicturePixel>, @@ -4159,8 +4058,6 @@ pub struct SurfaceInfo { pub device_pixel_scale: DevicePixelScale, /// The scale factors of the surface to raster transform. pub scale_factors: (f32, f32), - /// The allocated device rect for this surface - pub device_rect: Option<DeviceRect>, } impl SurfaceInfo { @@ -4191,7 +4088,6 @@ impl SurfaceInfo { SurfaceInfo { rect: PictureRect::zero(), - opaque_rect: PictureRect::zero(), map_local_to_surface, render_tasks: None, raster_spatial_node_index, @@ -4199,13 +4095,8 @@ impl SurfaceInfo { inflation_factor, device_pixel_scale, scale_factors, - device_rect: None, } } - - pub fn get_device_rect(&self) -> DeviceRect { - self.device_rect.expect("bug: queried before surface was initialized") - } } #[derive(Debug)] @@ -4226,11 +4117,6 @@ pub struct RasterConfig { /// However e.g. text rasterization uses it to ensure consistent /// on-screen font size. pub root_scaling_factor: f32, - /// The world rect of this picture clipped to the current culling - /// rect. This is used for determining the size of the render - /// target rect for this surface, and calculating raster scale - /// factors. - pub clipped_bounding_rect: WorldRect, } bitflags! { @@ -4265,7 +4151,6 @@ pub enum PictureCompositeMode { Blit(BlitReason), /// Used to cache a picture as a series of tiles. TileCache { - slice_id: SliceId, }, /// Apply an SVG filter SvgFilter(Vec<FilterPrimitive>, Vec<SFilterData>), @@ -4276,10 +4161,9 @@ impl PictureCompositeMode { let mut result_rect = picture_rect; match self { PictureCompositeMode::Filter(filter) => match filter { - Filter::Blur(width, height) => { - let width_factor = clamp_blur_radius(*width, scale_factors).ceil() * BLUR_SAMPLE_SCALE; - let height_factor = clamp_blur_radius(*height, scale_factors).ceil() * BLUR_SAMPLE_SCALE; - result_rect = picture_rect.inflate(width_factor, height_factor); + Filter::Blur(blur_radius) => { + let inflation_factor = clamp_blur_radius(*blur_radius, scale_factors).ceil() * BLUR_SAMPLE_SCALE; + result_rect = picture_rect.inflate(inflation_factor, inflation_factor); }, Filter::DropShadows(shadows) => { let mut max_inflation: f32 = 0.0; @@ -4297,9 +4181,8 @@ impl PictureCompositeMode { let output_rect = match primitive.kind { FilterPrimitiveKind::Blur(ref primitive) => { let input = primitive.input.to_index(cur_index).map(|index| output_rects[index]).unwrap_or(picture_rect); - let width_factor = primitive.width.round() * BLUR_SAMPLE_SCALE; - let height_factor = primitive.height.round() * BLUR_SAMPLE_SCALE; - input.inflate(width_factor, height_factor) + let inflation_factor = primitive.radius.round() * BLUR_SAMPLE_SCALE; + input.inflate(inflation_factor, inflation_factor) } FilterPrimitiveKind::DropShadow(ref primitive) => { let inflation_factor = primitive.shadow.blur_radius.ceil() * BLUR_SAMPLE_SCALE; @@ -4373,14 +4256,28 @@ bitflags! { /// A set of flags describing why a picture may need a backing surface. #[cfg_attr(feature = "capture", derive(Serialize))] pub struct ClusterFlags: u32 { + /// This cluster is a picture + const IS_PICTURE = 1; /// Whether this cluster is visible when the position node is a backface. - const IS_BACKFACE_VISIBLE = 1; + const IS_BACKFACE_VISIBLE = 2; /// This flag is set during the first pass picture traversal, depending on whether /// the cluster is visible or not. It's read during the second pass when primitives /// consult their owning clusters to see if the primitive itself is visible. - const IS_VISIBLE = 2; + const IS_VISIBLE = 4; /// Is a backdrop-filter cluster that requires special handling during post_update. - const IS_BACKDROP_FILTER = 4; + const IS_BACKDROP_FILTER = 8; + /// Force creation of a picture caching slice before this cluster. + const CREATE_PICTURE_CACHE_PRE = 16; + /// Force creation of a picture caching slice after this cluster. + const CREATE_PICTURE_CACHE_POST = 32; + /// If set, this cluster represents a scroll bar container. + const SCROLLBAR_CONTAINER = 64; + /// If set, this cluster contains clear rectangle primitives. + const IS_CLEAR_PRIMITIVE = 128; + /// This is used as a performance hint - this primitive may be promoted to a native + /// compositor surface under certain (implementation specific) conditions. This + /// is typically used for large videos, and canvas elements. + const PREFER_COMPOSITOR_SURFACE = 256; } } @@ -4395,14 +4292,19 @@ pub struct PrimitiveCluster { /// during the first picture traversal, which is needed for local scale /// determination, and render task size calculations. bounding_rect: LayoutRect, - /// a part of the cluster that we know to be opaque if any. Does not always - /// describe the entire opaque region, but all content within that rect must - /// be opaque. - pub opaque_rect: LayoutRect, - /// The range of primitive instance indices associated with this cluster. - pub prim_range: Range<usize>, + /// The list of primitive instances in this cluster. + pub prim_instances: Vec<PrimitiveInstance>, /// Various flags / state for this cluster. pub flags: ClusterFlags, + /// An optional scroll root to use if this cluster establishes a picture cache slice. + pub cache_scroll_root: Option<SpatialNodeIndex>, +} + +/// Where to insert a prim instance in a primitive list. +#[derive(Debug, Copy, Clone)] +enum PrimitiveListPosition { + Begin, + End, } impl PrimitiveCluster { @@ -4410,14 +4312,13 @@ impl PrimitiveCluster { fn new( spatial_node_index: SpatialNodeIndex, flags: ClusterFlags, - first_instance_index: usize, ) -> Self { PrimitiveCluster { bounding_rect: LayoutRect::zero(), - opaque_rect: LayoutRect::zero(), spatial_node_index, flags, - prim_range: first_instance_index..first_instance_index + prim_instances: Vec::new(), + cache_scroll_root: None, } } @@ -4427,22 +4328,28 @@ impl PrimitiveCluster { spatial_node_index: SpatialNodeIndex, flags: ClusterFlags, ) -> bool { - self.flags == flags && self.spatial_node_index == spatial_node_index - } + // If this cluster is a scrollbar, ensure that a matching scrollbar + // container that follows is split up, so we don't combine the + // scrollbars into a single slice. + if self.flags.contains(ClusterFlags::SCROLLBAR_CONTAINER) { + return false; + } - pub fn prim_range(&self) -> Range<usize> { - self.prim_range.clone() + self.flags == flags && self.spatial_node_index == spatial_node_index } /// Add a primitive instance to this cluster, at the start or end - fn add_instance( + fn push( &mut self, - culling_rect: &LayoutRect, - instance_index: usize, + prim_instance: PrimitiveInstance, + prim_rect: LayoutRect, ) { - debug_assert_eq!(instance_index, self.prim_range.end); - self.bounding_rect = self.bounding_rect.union(culling_rect); - self.prim_range.end += 1; + let culling_rect = prim_instance.local_clip_rect + .intersection(&prim_rect) + .unwrap_or_else(LayoutRect::zero); + + self.bounding_rect = self.bounding_rect.union(&culling_rect); + self.prim_instances.push(prim_instance); } } @@ -4454,11 +4361,6 @@ impl PrimitiveCluster { pub struct PrimitiveList { /// List of primitives grouped into clusters. pub clusters: Vec<PrimitiveCluster>, - pub prim_instances: Vec<PrimitiveInstance>, - pub child_pictures: Vec<PictureIndex>, - /// The number of preferred compositor surfaces that were found when - /// adding prims to this list. - pub compositor_surface_count: usize, } impl PrimitiveList { @@ -4469,31 +4371,32 @@ impl PrimitiveList { pub fn empty() -> Self { PrimitiveList { clusters: Vec::new(), - prim_instances: Vec::new(), - child_pictures: Vec::new(), - compositor_surface_count: 0, } } - /// Add a primitive instance to the end of the list - pub fn add_prim( + /// Add a primitive instance to this list, at the start or end + fn push( &mut self, prim_instance: PrimitiveInstance, prim_rect: LayoutRect, spatial_node_index: SpatialNodeIndex, prim_flags: PrimitiveFlags, + insert_position: PrimitiveListPosition, ) { let mut flags = ClusterFlags::empty(); // Pictures are always put into a new cluster, to make it faster to // iterate all pictures in a given primitive list. match prim_instance.kind { - PrimitiveInstanceKind::Picture { pic_index, .. } => { - self.child_pictures.push(pic_index); + PrimitiveInstanceKind::Picture { .. } => { + flags.insert(ClusterFlags::IS_PICTURE); } PrimitiveInstanceKind::Backdrop { .. } => { flags.insert(ClusterFlags::IS_BACKDROP_FILTER); } + PrimitiveInstanceKind::Clear { .. } => { + flags.insert(ClusterFlags::IS_CLEAR_PRIMITIVE); + } _ => {} } @@ -4501,73 +4404,90 @@ impl PrimitiveList { flags.insert(ClusterFlags::IS_BACKFACE_VISIBLE); } - if prim_flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) { - self.compositor_surface_count += 1; + if prim_flags.contains(PrimitiveFlags::IS_SCROLLBAR_CONTAINER) { + flags.insert(ClusterFlags::SCROLLBAR_CONTAINER); } - let culling_rect = prim_instance.clip_set.local_clip_rect - .intersection(&prim_rect) - .unwrap_or_else(LayoutRect::zero); - - // Primitive lengths aren't evenly distributed among primitive lists: - // We often have a large amount of single primitive lists, a - // few below 20~30 primitives, and even fewer lists (maybe a couple) - // in the multiple hundreds with nothing in between. - // We can see in profiles that reallocating vectors while pushing - // primitives is taking a large amount of the total scene build time, - // so we take advantage of what we know about the length distributions - // to go for an adapted vector growth pattern that avoids over-allocating - // for the many small allocations while avoiding a lot of reallocation by - // quickly converging to the common sizes. - // Rust's default vector growth strategy (when pushing elements one by one) - // is to double the capacity every time. - let prims_len = self.prim_instances.len(); - if prims_len == self.prim_instances.capacity() { - let next_alloc = match prims_len { - 1 ..= 31 => 32 - prims_len, - 32 ..= 256 => 512 - prims_len, - _ => prims_len * 2, - }; - - self.prim_instances.reserve(next_alloc); + if prim_flags.contains(PrimitiveFlags::PREFER_COMPOSITOR_SURFACE) { + flags.insert(ClusterFlags::PREFER_COMPOSITOR_SURFACE); } - let instance_index = prims_len; - self.prim_instances.push(prim_instance); - - if let Some(cluster) = self.clusters.last_mut() { - if cluster.is_compatible(spatial_node_index, flags) { - cluster.add_instance(&culling_rect, instance_index); - return; + // Insert the primitive into the first or last cluster as required + match insert_position { + PrimitiveListPosition::Begin => { + let mut cluster = PrimitiveCluster::new( + spatial_node_index, + flags, + ); + cluster.push(prim_instance, prim_rect); + self.clusters.insert(0, cluster); } - } - - // Same idea with clusters, using a different distribution. - let clusters_len = self.clusters.len(); - if clusters_len == self.clusters.capacity() { - let next_alloc = match clusters_len { - 1 ..= 15 => 16 - clusters_len, - 16 ..= 127 => 128 - clusters_len, - _ => clusters_len * 2, - }; + PrimitiveListPosition::End => { + if let Some(cluster) = self.clusters.last_mut() { + if cluster.is_compatible(spatial_node_index, flags) { + cluster.push(prim_instance, prim_rect); + return; + } + } - self.clusters.reserve(next_alloc); + let mut cluster = PrimitiveCluster::new( + spatial_node_index, + flags, + ); + cluster.push(prim_instance, prim_rect); + self.clusters.push(cluster); + } } + } - let mut cluster = PrimitiveCluster::new( + /// Add a primitive instance to the start of the list + pub fn add_prim_to_start( + &mut self, + prim_instance: PrimitiveInstance, + prim_rect: LayoutRect, + spatial_node_index: SpatialNodeIndex, + flags: PrimitiveFlags, + ) { + self.push( + prim_instance, + prim_rect, spatial_node_index, flags, - instance_index, - ); + PrimitiveListPosition::Begin, + ) + } - cluster.add_instance(&culling_rect, instance_index); - self.clusters.push(cluster); + /// Add a primitive instance to the end of the list + pub fn add_prim( + &mut self, + prim_instance: PrimitiveInstance, + prim_rect: LayoutRect, + spatial_node_index: SpatialNodeIndex, + flags: PrimitiveFlags, + ) { + self.push( + prim_instance, + prim_rect, + spatial_node_index, + flags, + PrimitiveListPosition::End, + ) } /// Returns true if there are no clusters (and thus primitives) pub fn is_empty(&self) -> bool { self.clusters.is_empty() } + + /// Add an existing cluster to this prim list + pub fn add_cluster(&mut self, cluster: PrimitiveCluster) { + self.clusters.push(cluster); + } + + /// Merge another primitive list into this one + pub fn extend(&mut self, prim_list: PrimitiveList) { + self.clusters.extend(prim_list.clusters); + } } /// Defines configuration options for a given picture primitive. @@ -4601,21 +4521,27 @@ pub struct PicturePrimitive { /// it will be considered invisible. pub is_backface_visible: bool, - pub primary_render_task_id: Option<RenderTaskId>, - /// If a mix-blend-mode, contains the render task for - /// the readback of the framebuffer that we use to sample - /// from in the mix-blend-mode shader. - /// For drop-shadow filter, this will store the original - /// picture task which would be rendered on screen after - /// blur pass. + // If a mix-blend-mode, contains the render task for + // the readback of the framebuffer that we use to sample + // from in the mix-blend-mode shader. + // For drop-shadow filter, this will store the original + // picture task which would be rendered on screen after + // blur pass. pub secondary_render_task_id: Option<RenderTaskId>, /// How this picture should be composited. /// If None, don't composite - just draw directly on parent surface. pub requested_composite_mode: Option<PictureCompositeMode>, + /// Requested rasterization space for this picture. It is + /// a performance hint only. + pub requested_raster_space: RasterSpace, pub raster_config: Option<RasterConfig>, pub context_3d: Picture3DContext<OrderedPictureChild>, + // If requested as a frame output (for rendering + // pages to a texture), this is the pipeline this + // picture is the root of. + pub frame_output_pipeline_id: Option<PipelineId>, // Optional cache handles for storing extra data // in the GPU cache, depending on the type of // picture. @@ -4639,22 +4565,22 @@ pub struct PicturePrimitive { /// different depending on how much was culled. pub precise_local_rect: LayoutRect, - /// Store the state of the previous precise local rect - /// for this picture. We need this in order to know when - /// to invalidate segments / drop-shadow gpu cache handles. - pub prev_precise_local_rect: LayoutRect, - /// If false, this picture needs to (re)build segments /// if it supports segment rendering. This can occur /// if the local rect of the picture changes due to /// transform animation and/or scrolling. pub segments_are_valid: bool, + /// If Some(..) the tile cache that is associated with this picture. + #[cfg_attr(feature = "capture", serde(skip))] //TODO + pub tile_cache: Option<Box<TileCacheInstance>>, + /// The config options for this picture. pub options: PictureOptions, - /// Set to true if we know for sure the picture is fully opaque. - pub is_opaque: bool, + /// Keep track of the number of render tasks dependencies to pre-allocate + /// the dependency array next frame. + num_render_tasks: usize, } impl PicturePrimitive { @@ -4672,8 +4598,16 @@ impl PicturePrimitive { pt.add_item(format!("raster_config: {:?}", self.raster_config)); pt.add_item(format!("requested_composite_mode: {:?}", self.requested_composite_mode)); - for child_pic_index in &self.prim_list.child_pictures { - pictures[child_pic_index.0].print(pictures, *child_pic_index, pt); + for cluster in &self.prim_list.clusters { + if cluster.flags.contains(ClusterFlags::IS_PICTURE) { + for instance in &cluster.prim_instances { + let index = match instance.kind { + PrimitiveInstanceKind::Picture { pic_index, .. } => pic_index, + _ => unreachable!(), + }; + pictures[index.0].print(pictures, index, pt); + } + } } pt.end_level(); @@ -4724,6 +4658,40 @@ impl PicturePrimitive { } } + /// Destroy an existing picture. This is called just before + /// a frame builder is replaced with a newly built scene. It + /// gives a picture a chance to retain any cached tiles that + /// may be useful during the next scene build. + pub fn destroy( + &mut self, + retained_tiles: &mut RetainedTiles, + ) { + if let Some(tile_cache) = self.tile_cache.take() { + if !tile_cache.tiles.is_empty() { + retained_tiles.caches.insert( + tile_cache.slice, + PictureCacheState { + tiles: tile_cache.tiles, + spatial_node_comparer: tile_cache.spatial_node_comparer, + opacity_bindings: tile_cache.opacity_bindings, + color_bindings: tile_cache.color_bindings, + root_transform: tile_cache.root_transform, + current_tile_size: tile_cache.current_tile_size, + native_surface: tile_cache.native_surface, + external_native_surface_cache: tile_cache.external_native_surface_cache, + virtual_offset: tile_cache.virtual_offset, + frame_id: tile_cache.frame_id, + allocations: PictureCacheRecycledAllocations { + old_opacity_bindings: tile_cache.old_opacity_bindings, + old_color_bindings: tile_cache.old_color_bindings, + compare_cache: tile_cache.compare_cache, + }, + }, + ); + } + } + } + // TODO(gw): We have the PictureOptions struct available. We // should move some of the parameter list in this // method to be part of the PictureOptions, and @@ -4731,54 +4699,79 @@ impl PicturePrimitive { pub fn new_image( requested_composite_mode: Option<PictureCompositeMode>, context_3d: Picture3DContext<OrderedPictureChild>, + frame_output_pipeline_id: Option<PipelineId>, apply_local_clip_rect: bool, flags: PrimitiveFlags, + requested_raster_space: RasterSpace, prim_list: PrimitiveList, spatial_node_index: SpatialNodeIndex, + tile_cache: Option<Box<TileCacheInstance>>, options: PictureOptions, ) -> Self { PicturePrimitive { prim_list, state: None, - primary_render_task_id: None, secondary_render_task_id: None, requested_composite_mode, raster_config: None, context_3d, + frame_output_pipeline_id, extra_gpu_data_handles: SmallVec::new(), apply_local_clip_rect, is_backface_visible: flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE), + requested_raster_space, spatial_node_index, estimated_local_rect: LayoutRect::zero(), precise_local_rect: LayoutRect::zero(), - prev_precise_local_rect: LayoutRect::zero(), + tile_cache, options, segments_are_valid: false, - is_opaque: false, + num_render_tasks: 0, + } + } + + /// Gets the raster space to use when rendering the picture. + /// Usually this would be the requested raster space. However, if the + /// picture's spatial node or one of its ancestors is being pinch zoomed + /// then we round it. This prevents us rasterizing glyphs for every minor + /// change in zoom level, as that would be too expensive. + pub fn get_raster_space(&self, spatial_tree: &SpatialTree) -> RasterSpace { + let spatial_node = &spatial_tree.spatial_nodes[self.spatial_node_index.0 as usize]; + if spatial_node.is_ancestor_or_self_zooming { + let scale_factors = spatial_tree + .get_relative_transform(self.spatial_node_index, ROOT_SPATIAL_NODE_INDEX) + .scale_factors(); + + // Round the scale up to the nearest power of 2, but don't exceed 8. + let scale = scale_factors.0.max(scale_factors.1).min(8.0); + let rounded_up = 2.0f32.powf(scale.log2().ceil()); + + RasterSpace::Local(rounded_up) + } else { + self.requested_raster_space } } pub fn take_context( &mut self, pic_index: PictureIndex, + clipped_prim_bounding_rect: WorldRect, surface_spatial_node_index: SpatialNodeIndex, raster_spatial_node_index: SpatialNodeIndex, parent_surface_index: SurfaceIndex, - parent_subpixel_mode: SubpixelMode, + parent_subpixel_mode: &SubpixelMode, frame_state: &mut FrameBuildingState, frame_context: &FrameBuildingContext, scratch: &mut PrimitiveScratchBuffer, tile_cache_logger: &mut TileCacheLogger, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, ) -> Option<(PictureContext, PictureState, PrimitiveList)> { - self.primary_render_task_id = None; - self.secondary_render_task_id = None; - if !self.is_visible() { return None; } profile_scope!("take_context"); + let task_id = frame_state.surfaces[parent_surface_index.0].render_tasks.unwrap().port; + frame_state.render_tasks[task_id].children.reserve(self.num_render_tasks); // Extract the raster and surface spatial nodes from the raster // config, if this picture establishes a surface. Otherwise just @@ -4839,345 +4832,6 @@ impl PicturePrimitive { }; match self.raster_config { - Some(RasterConfig { surface_index, composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => { - let tile_cache = tile_caches.get_mut(&slice_id).unwrap(); - let mut debug_info = SliceDebugInfo::new(); - let mut surface_tasks = Vec::with_capacity(tile_cache.tile_count()); - let mut surface_device_rect = DeviceRect::zero(); - let device_pixel_scale = frame_state - .surfaces[surface_index.0] - .device_pixel_scale; - - // Get the overall world space rect of the picture cache. Used to clip - // the tile rects below for occlusion testing to the relevant area. - let world_clip_rect = map_pic_to_world - .map(&tile_cache.local_clip_rect) - .expect("bug: unable to map clip rect"); - let device_clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round(); - - for (sub_slice_index, sub_slice) in tile_cache.sub_slices.iter_mut().enumerate() { - for tile in sub_slice.tiles.values_mut() { - surface_device_rect = surface_device_rect.union(&tile.device_valid_rect); - - if tile.is_visible { - // Get the world space rect that this tile will actually occupy on screem - let device_draw_rect = device_clip_rect.intersection(&tile.device_valid_rect); - - // If that draw rect is occluded by some set of tiles in front of it, - // then mark it as not visible and skip drawing. When it's not occluded - // it will fail this test, and get rasterized by the render task setup - // code below. - match device_draw_rect { - Some(device_draw_rect) => { - // Only check for occlusion on visible tiles that are fixed position. - if tile_cache.spatial_node_index == ROOT_SPATIAL_NODE_INDEX && - frame_state.composite_state.occluders.is_tile_occluded(tile.z_id, device_draw_rect) { - // If this tile has an allocated native surface, free it, since it's completely - // occluded. We will need to re-allocate this surface if it becomes visible, - // but that's likely to be rare (e.g. when there is no content display list - // for a frame or two during a tab switch). - let surface = tile.surface.as_mut().expect("no tile surface set!"); - - if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. } = surface { - if let Some(id) = id.take() { - frame_state.resource_cache.destroy_compositor_tile(id); - } - } - - tile.is_visible = false; - - if frame_context.fb_config.testing { - debug_info.tiles.insert( - tile.tile_offset, - TileDebugInfo::Occluded, - ); - } - - continue; - } - } - None => { - tile.is_visible = false; - } - } - } - - // If we get here, we want to ensure that the surface remains valid in the texture - // cache, _even if_ it's not visible due to clipping or being scrolled off-screen. - // This ensures that we retain valid tiles that are off-screen, but still in the - // display port of this tile cache instance. - if let Some(TileSurface::Texture { descriptor, .. }) = tile.surface.as_ref() { - if let SurfaceTextureDescriptor::TextureCache { ref handle, .. } = descriptor { - frame_state.resource_cache.texture_cache.request( - handle, - frame_state.gpu_cache, - ); - } - } - - // If the tile has been found to be off-screen / clipped, skip any further processing. - if !tile.is_visible { - if frame_context.fb_config.testing { - debug_info.tiles.insert( - tile.tile_offset, - TileDebugInfo::Culled, - ); - } - - continue; - } - - if frame_context.debug_flags.contains(DebugFlags::PICTURE_CACHING_DBG) { - tile.root.draw_debug_rects( - &map_pic_to_world, - tile.is_opaque, - tile.current_descriptor.local_valid_rect, - scratch, - frame_context.global_device_pixel_scale, - ); - - let label_offset = DeviceVector2D::new( - 20.0 + sub_slice_index as f32 * 20.0, - 30.0 + sub_slice_index as f32 * 20.0, - ); - let tile_device_rect = tile.world_tile_rect * frame_context.global_device_pixel_scale; - if tile_device_rect.size.height >= label_offset.y { - let surface = tile.surface.as_ref().expect("no tile surface set!"); - - scratch.push_debug_string( - tile_device_rect.origin + label_offset, - debug_colors::RED, - format!("{:?}: s={} is_opaque={} surface={} sub={}", - tile.id, - tile_cache.slice, - tile.is_opaque, - surface.kind(), - sub_slice_index, - ), - ); - } - } - - if let TileSurface::Texture { descriptor, .. } = tile.surface.as_mut().unwrap() { - match descriptor { - SurfaceTextureDescriptor::TextureCache { ref handle, .. } => { - // Invalidate if the backing texture was evicted. - if frame_state.resource_cache.texture_cache.is_allocated(handle) { - // Request the backing texture so it won't get evicted this frame. - // We specifically want to mark the tile texture as used, even - // if it's detected not visible below and skipped. This is because - // we maintain the set of tiles we care about based on visibility - // during pre_update. If a tile still exists after that, we are - // assuming that it's either visible or we want to retain it for - // a while in case it gets scrolled back onto screen soon. - // TODO(gw): Consider switching to manual eviction policy? - frame_state.resource_cache.texture_cache.request(handle, frame_state.gpu_cache); - } else { - // If the texture was evicted on a previous frame, we need to assume - // that the entire tile rect is dirty. - tile.invalidate(None, InvalidationReason::NoTexture); - } - } - SurfaceTextureDescriptor::Native { id, .. } => { - if id.is_none() { - // There is no current surface allocation, so ensure the entire tile is invalidated - tile.invalidate(None, InvalidationReason::NoSurface); - } - } - } - } - - // Ensure that the dirty rect doesn't extend outside the local valid rect. - tile.local_dirty_rect = tile.local_dirty_rect - .intersection(&tile.current_descriptor.local_valid_rect) - .unwrap_or_else(PictureRect::zero); - - // Update the world/device dirty rect - let world_dirty_rect = map_pic_to_world.map(&tile.local_dirty_rect).expect("bug"); - - let device_rect = (tile.world_tile_rect * frame_context.global_device_pixel_scale).round(); - tile.device_dirty_rect = (world_dirty_rect * frame_context.global_device_pixel_scale) - .round_out() - .intersection(&device_rect) - .unwrap_or_else(DeviceRect::zero); - - if tile.is_valid { - if frame_context.fb_config.testing { - debug_info.tiles.insert( - tile.tile_offset, - TileDebugInfo::Valid, - ); - } - - continue; - } - - // Add this dirty rect to the dirty region tracker. This must be done outside the if statement below, - // so that we include in the dirty region tiles that are handled by a background color only (no - // surface allocation). - tile_cache.dirty_region.add_dirty_region( - tile.local_dirty_rect, - SubSliceIndex::new(sub_slice_index), - frame_context.spatial_tree, - ); - - // Ensure that this texture is allocated. - if let TileSurface::Texture { ref mut descriptor } = tile.surface.as_mut().unwrap() { - match descriptor { - SurfaceTextureDescriptor::TextureCache { ref mut handle } => { - if !frame_state.resource_cache.texture_cache.is_allocated(handle) { - frame_state.resource_cache.texture_cache.update_picture_cache( - tile_cache.current_tile_size, - handle, - frame_state.gpu_cache, - ); - } - } - SurfaceTextureDescriptor::Native { id } => { - if id.is_none() { - // Allocate a native surface id if we're in native compositing mode, - // and we don't have a surface yet (due to first frame, or destruction - // due to tile size changing etc). - if sub_slice.native_surface.is_none() { - let opaque = frame_state - .resource_cache - .create_compositor_surface( - tile_cache.virtual_offset, - tile_cache.current_tile_size, - true, - ); - - let alpha = frame_state - .resource_cache - .create_compositor_surface( - tile_cache.virtual_offset, - tile_cache.current_tile_size, - false, - ); - - sub_slice.native_surface = Some(NativeSurface { - opaque, - alpha, - }); - } - - // Create the tile identifier and allocate it. - let surface_id = if tile.is_opaque { - sub_slice.native_surface.as_ref().unwrap().opaque - } else { - sub_slice.native_surface.as_ref().unwrap().alpha - }; - - let tile_id = NativeTileId { - surface_id, - x: tile.tile_offset.x, - y: tile.tile_offset.y, - }; - - frame_state.resource_cache.create_compositor_tile(tile_id); - - *id = Some(tile_id); - } - } - } - - let content_origin_f = tile.world_tile_rect.origin * device_pixel_scale; - let content_origin = content_origin_f.round(); - debug_assert!((content_origin_f.x - content_origin.x).abs() < 0.01); - debug_assert!((content_origin_f.y - content_origin.y).abs() < 0.01); - - let surface = descriptor.resolve( - frame_state.resource_cache, - tile_cache.current_tile_size, - ); - - let scissor_rect = tile.device_dirty_rect - .translate(-device_rect.origin.to_vector()) - .round() - .to_i32(); - - let valid_rect = tile.device_valid_rect - .translate(-device_rect.origin.to_vector()) - .round() - .to_i32(); - - let task_size = tile_cache.current_tile_size; - - let batch_filter = BatchFilter { - rect_in_pic_space: tile.local_dirty_rect, - sub_slice_index: SubSliceIndex::new(sub_slice_index), - }; - - let render_task_id = frame_state.rg_builder.add().init( - RenderTask::new( - RenderTaskLocation::Static { - surface: StaticRenderTaskSurface::PictureCache { - surface, - }, - rect: task_size.into(), - }, - RenderTaskKind::new_picture( - task_size, - tile_cache.current_tile_size.to_f32(), - pic_index, - content_origin, - surface_spatial_node_index, - device_pixel_scale, - Some(batch_filter), - Some(scissor_rect), - Some(valid_rect), - ) - ), - ); - - surface_tasks.push(render_task_id); - } - - if frame_context.fb_config.testing { - debug_info.tiles.insert( - tile.tile_offset, - TileDebugInfo::Dirty(DirtyTileDebugInfo { - local_valid_rect: tile.current_descriptor.local_valid_rect, - local_dirty_rect: tile.local_dirty_rect, - }), - ); - } - - // If the entire tile valid region is dirty, we can update the fract offset - // at which the tile was rendered. - if tile.device_dirty_rect.contains_rect(&tile.device_valid_rect) { - tile.device_fract_offset = tile_cache.device_fract_offset; - } - - // Now that the tile is valid, reset the dirty rect. - tile.local_dirty_rect = PictureRect::zero(); - tile.is_valid = true; - } - } - - // If invalidation debugging is enabled, dump the picture cache state to a tree printer. - if frame_context.debug_flags.contains(DebugFlags::INVALIDATION_DBG) { - tile_cache.print(); - } - - // If testing mode is enabled, write some information about the current state - // of this picture cache (made available in RenderResults). - if frame_context.fb_config.testing { - frame_state.composite_state - .picture_cache_debug - .slices - .insert( - tile_cache.slice, - debug_info, - ); - } - - frame_state.init_surface_tiled( - surface_index, - surface_tasks, - surface_device_rect, - ); - } Some(ref mut raster_config) => { let pic_rect = self.precise_local_rect.cast_unit(); @@ -5220,7 +4874,7 @@ impl PicturePrimitive { pic_rect, &map_pic_to_raster, &map_raster_to_world, - raster_config.clipped_bounding_rect.outer_rect(clip_inflation), + clipped_prim_bounding_rect.outer_rect(clip_inflation), device_pixel_scale, ) { Some(info) => info, @@ -5243,7 +4897,7 @@ impl PicturePrimitive { /// support. The on-the-fly scaling can be seen as on-the-fly, /// per-task DPI adjustment. Logical pixels are unaffected. /// - /// The scaling factor is returned to the caller; blur radius, + /// The scaling factor is returned to the caller; blur radius, /// font size, etc. need to be scaled accordingly. fn adjust_scale_for_max_surface_size( raster_config: &RasterConfig, @@ -5290,17 +4944,12 @@ impl PicturePrimitive { } } - let primary_render_task_id; - match raster_config.composite_mode { - PictureCompositeMode::TileCache { .. } => { - unreachable!("handled above"); - } - PictureCompositeMode::Filter(Filter::Blur(width, height)) => { - let width_std_deviation = clamp_blur_radius(width, scale_factors) * device_pixel_scale.0; - let height_std_deviation = clamp_blur_radius(height, scale_factors) * device_pixel_scale.0; + let dep_info = match raster_config.composite_mode { + PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => { + let blur_std_deviation = clamp_blur_radius(blur_radius, scale_factors) * device_pixel_scale.0; let mut blur_std_deviation = DeviceSize::new( - width_std_deviation * scale_factors.0, - height_std_deviation * scale_factors.1 + blur_std_deviation * scale_factors.0, + blur_std_deviation * scale_factors.1 ); let mut device_rect = if self.options.inflate_if_required { let inflation_factor = frame_state.surfaces[raster_config.surface_index.0].inflation_factor; @@ -5327,7 +4976,7 @@ impl PicturePrimitive { // Adjust the size to avoid introducing sampling errors during the down-scaling passes. // what would be even better is to rasterize the picture at the down-scaled size // directly. - device_rect.size = BlurTask::adjusted_blur_source_size( + device_rect.size = RenderTask::adjusted_blur_source_size( device_rect.size, blur_std_deviation, ); @@ -5335,7 +4984,7 @@ impl PicturePrimitive { if let Some(scale) = adjust_scale_for_max_surface_size( raster_config, frame_context.fb_config.max_target_size, pic_rect, &map_pic_to_raster, &map_raster_to_world, - raster_config.clipped_bounding_rect, + clipped_prim_bounding_rect, &mut device_pixel_scale, &mut device_rect, &mut unclipped, ) { blur_std_deviation = blur_std_deviation * scale; @@ -5343,6 +4992,8 @@ impl PicturePrimitive { raster_config.root_scaling_factor = scale; } + let device_rect = device_rect.to_i32(); + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -5350,43 +5001,32 @@ impl PicturePrimitive { device_pixel_scale, ); - let task_size = device_rect.size.to_i32(); - - let picture_task_id = frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_picture( - task_size, - unclipped.size, - pic_index, - device_rect.origin, - surface_spatial_node_index, - device_pixel_scale, - None, - None, - None, - ) - ).with_uv_rect_kind(uv_rect_kind) + let picture_task_id = frame_state.render_tasks.add().init( + RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, device_rect.size), + unclipped.size, + pic_index, + device_rect.origin, + uv_rect_kind, + surface_spatial_node_index, + device_pixel_scale, + PrimitiveVisibilityMask::all(), + None, + None, + ) ); let blur_render_task_id = RenderTask::new_blur( blur_std_deviation, picture_task_id, - frame_state.rg_builder, + frame_state.render_tasks, RenderTargetKind::Color, + ClearMode::Transparent, None, original_size.to_i32(), ); - primary_render_task_id = Some(blur_render_task_id); - - frame_state.init_surface_chain( - raster_config.surface_index, - blur_render_task_id, - picture_task_id, - parent_surface_index, - device_rect, - ); + Some((blur_render_task_id, picture_task_id)) } PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => { let mut max_std_deviation = 0.0; @@ -5403,7 +5043,7 @@ impl PicturePrimitive { .intersection(&unclipped) .unwrap(); - device_rect.size = BlurTask::adjusted_blur_source_size( + device_rect.size = RenderTask::adjusted_blur_source_size( device_rect.size, DeviceSize::new( max_std_deviation * scale_factors.0, @@ -5414,13 +5054,15 @@ impl PicturePrimitive { if let Some(scale) = adjust_scale_for_max_surface_size( raster_config, frame_context.fb_config.max_target_size, pic_rect, &map_pic_to_raster, &map_raster_to_world, - raster_config.clipped_bounding_rect, + clipped_prim_bounding_rect, &mut device_pixel_scale, &mut device_rect, &mut unclipped, ) { // std_dev adjusts automatically from using device_pixel_scale raster_config.root_scaling_factor = scale; } + let device_rect = device_rect.to_i32(); + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -5428,31 +5070,25 @@ impl PicturePrimitive { device_pixel_scale, ); - let task_size = device_rect.size.to_i32(); - - let picture_task_id = frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_picture( - task_size, - unclipped.size, - pic_index, - device_rect.origin, - surface_spatial_node_index, - device_pixel_scale, - None, - None, - None, - ), - ).with_uv_rect_kind(uv_rect_kind) - ); + let picture_task_id = frame_state.render_tasks.add().init({ + let mut picture_task = RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, device_rect.size), + unclipped.size, + pic_index, + device_rect.origin, + uv_rect_kind, + surface_spatial_node_index, + device_pixel_scale, + PrimitiveVisibilityMask::all(), + None, + None, + ); + picture_task.mark_for_saving(); - // Add this content picture as a dependency of the parent surface, to - // ensure it isn't free'd after the shadow uses it as an input. - frame_state.add_child_render_task( - parent_surface_index, - picture_task_id, - ); + picture_task + }); + + self.secondary_render_task_id = Some(picture_task_id); let mut blur_tasks = BlurTaskCache::default(); @@ -5467,40 +5103,29 @@ impl PicturePrimitive { blur_radius * scale_factors.1, ), picture_task_id, - frame_state.rg_builder, + frame_state.render_tasks, RenderTargetKind::Color, + ClearMode::Transparent, Some(&mut blur_tasks), - device_rect.size.to_i32(), + device_rect.size, ); } - primary_render_task_id = Some(blur_render_task_id); - self.secondary_render_task_id = Some(picture_task_id); - - frame_state.init_surface_chain( - raster_config.surface_index, - blur_render_task_id, - picture_task_id, - parent_surface_index, - device_rect, - ); + // TODO(nical) the second one should to be the blur's task id but we have several blurs now + Some((blur_render_task_id, picture_task_id)) } - PictureCompositeMode::MixBlend(mode) if BlendMode::from_mix_blend_mode( - mode, - frame_context.fb_config.gpu_supports_advanced_blend, - frame_context.fb_config.advanced_blend_is_coherent, - frame_context.fb_config.dual_source_blending_is_enabled && - frame_context.fb_config.dual_source_blending_is_supported, - ).is_none() => { + PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => { if let Some(scale) = adjust_scale_for_max_surface_size( raster_config, frame_context.fb_config.max_target_size, pic_rect, &map_pic_to_raster, &map_raster_to_world, - raster_config.clipped_bounding_rect, + clipped_prim_bounding_rect, &mut device_pixel_scale, &mut clipped, &mut unclipped, ) { raster_config.root_scaling_factor = scale; } + let clipped = clipped.to_i32(); + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -5508,116 +5133,47 @@ impl PicturePrimitive { device_pixel_scale, ); - let parent_surface = &frame_state.surfaces[parent_surface_index.0]; - let parent_raster_spatial_node_index = parent_surface.raster_spatial_node_index; - let parent_device_pixel_scale = parent_surface.device_pixel_scale; - - // Create a space mapper that will allow mapping from the local rect - // of the mix-blend primitive into the space of the surface that we - // need to read back from. Note that we use the parent's raster spatial - // node here, so that we are in the correct device space of the parent - // surface, whether it establishes a raster root or not. - let map_pic_to_parent = SpaceMapper::new_with_target( - parent_raster_spatial_node_index, - self.spatial_node_index, - RasterRect::max_rect(), // TODO(gw): May need a conservative estimate? - frame_context.spatial_tree, - ); - let pic_in_raster_space = map_pic_to_parent - .map(&pic_rect) - .expect("bug: unable to map mix-blend content into parent"); - - // Apply device pixel ratio for parent surface to get into device - // pixels for that surface. - let backdrop_rect = raster_rect_to_device_pixels( - pic_in_raster_space, - parent_device_pixel_scale, + let readback_task_id = frame_state.render_tasks.add().init( + RenderTask::new_readback(clipped) ); - let parent_surface_rect = parent_surface.get_device_rect(); - - // If there is no available parent surface to read back from (for example, if - // the parent surface is affected by a clip that doesn't affect the child - // surface), then create a dummy 16x16 readback. In future, we could alter - // the composite mode of this primitive to skip the mix-blend, but for simplicity - // we just create a dummy readback for now. - - let readback_task_id = match backdrop_rect.intersection(&parent_surface_rect) { - Some(available_rect) => { - // Calculate the UV coords necessary for the shader to sampler - // from the primitive rect within the readback region. This is - // 0..1 for aligned surfaces, but doing it this way allows - // accurate sampling if the primitive bounds have fractional values. - let backdrop_uv = calculate_uv_rect_kind( - &pic_rect, - &map_pic_to_parent.get_transform(), - &available_rect, - parent_device_pixel_scale, - ); - - frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - available_rect.size.to_i32(), - RenderTaskKind::new_readback(Some(available_rect.origin)), - ).with_uv_rect_kind(backdrop_uv) - ) - } - None => { - frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - DeviceIntSize::new(16, 16), - RenderTaskKind::new_readback(None), - ) - ) - } - }; - - frame_state.add_child_render_task( - parent_surface_index, + frame_state.render_tasks.add_dependency( + frame_state.surfaces[parent_surface_index.0].render_tasks.unwrap().port, readback_task_id, ); self.secondary_render_task_id = Some(readback_task_id); - let task_size = clipped.size.to_i32(); - - let render_task_id = frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_picture( - task_size, - unclipped.size, - pic_index, - clipped.origin, - surface_spatial_node_index, - device_pixel_scale, - None, - None, - None, - ) - ).with_uv_rect_kind(uv_rect_kind) + let render_task_id = frame_state.render_tasks.add().init( + RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, clipped.size), + unclipped.size, + pic_index, + clipped.origin, + uv_rect_kind, + surface_spatial_node_index, + device_pixel_scale, + PrimitiveVisibilityMask::all(), + None, + None, + ) ); - primary_render_task_id = Some(render_task_id); - - frame_state.init_surface( - raster_config.surface_index, - render_task_id, - parent_surface_index, - clipped, - ); + Some((render_task_id, render_task_id)) } PictureCompositeMode::Filter(..) => { if let Some(scale) = adjust_scale_for_max_surface_size( raster_config, frame_context.fb_config.max_target_size, pic_rect, &map_pic_to_raster, &map_raster_to_world, - raster_config.clipped_bounding_rect, + clipped_prim_bounding_rect, &mut device_pixel_scale, &mut clipped, &mut unclipped, ) { raster_config.root_scaling_factor = scale; } + let clipped = clipped.to_i32(); + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -5625,44 +5181,35 @@ impl PicturePrimitive { device_pixel_scale, ); - let task_size = clipped.size.to_i32(); - - let render_task_id = frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_picture( - task_size, - unclipped.size, - pic_index, - clipped.origin, - surface_spatial_node_index, - device_pixel_scale, - None, - None, - None, - ) - ).with_uv_rect_kind(uv_rect_kind) + let render_task_id = frame_state.render_tasks.add().init( + RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, clipped.size), + unclipped.size, + pic_index, + clipped.origin, + uv_rect_kind, + surface_spatial_node_index, + device_pixel_scale, + PrimitiveVisibilityMask::all(), + None, + None, + ) ); - primary_render_task_id = Some(render_task_id); - - frame_state.init_surface( - raster_config.surface_index, - render_task_id, - parent_surface_index, - clipped, - ); + Some((render_task_id, render_task_id)) } PictureCompositeMode::ComponentTransferFilter(..) => { if let Some(scale) = adjust_scale_for_max_surface_size( raster_config, frame_context.fb_config.max_target_size, pic_rect, &map_pic_to_raster, &map_raster_to_world, - raster_config.clipped_bounding_rect, + clipped_prim_bounding_rect, &mut device_pixel_scale, &mut clipped, &mut unclipped, ) { raster_config.root_scaling_factor = scale; } + let clipped = clipped.to_i32(); + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -5670,45 +5217,328 @@ impl PicturePrimitive { device_pixel_scale, ); - let task_size = clipped.size.to_i32(); - - let render_task_id = frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_picture( - task_size, - unclipped.size, - pic_index, - clipped.origin, - surface_spatial_node_index, - device_pixel_scale, - None, - None, - None, - ) - ).with_uv_rect_kind(uv_rect_kind) + let render_task_id = frame_state.render_tasks.add().init( + RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, clipped.size), + unclipped.size, + pic_index, + clipped.origin, + uv_rect_kind, + surface_spatial_node_index, + device_pixel_scale, + PrimitiveVisibilityMask::all(), + None, + None, + ) ); - primary_render_task_id = Some(render_task_id); + Some((render_task_id, render_task_id)) + } + PictureCompositeMode::TileCache { .. } => { + let tile_cache = self.tile_cache.as_mut().unwrap(); + let mut first = true; + + // Get the overall world space rect of the picture cache. Used to clip + // the tile rects below for occlusion testing to the relevant area. + let world_clip_rect = map_pic_to_world + .map(&tile_cache.local_clip_rect) + .expect("bug: unable to map clip rect"); + let device_clip_rect = (world_clip_rect * frame_context.global_device_pixel_scale).round(); + + for tile in tile_cache.tiles.values_mut() { + + if tile.is_visible { + // Get the world space rect that this tile will actually occupy on screem + let device_draw_rect = device_clip_rect.intersection(&tile.device_valid_rect); + + // If that draw rect is occluded by some set of tiles in front of it, + // then mark it as not visible and skip drawing. When it's not occluded + // it will fail this test, and get rasterized by the render task setup + // code below. + match device_draw_rect { + Some(device_draw_rect) => { + // Only check for occlusion on visible tiles that are fixed position. + if tile_cache.spatial_node_index == ROOT_SPATIAL_NODE_INDEX && + frame_state.composite_state.is_tile_occluded(tile.z_id, device_draw_rect) { + // If this tile has an allocated native surface, free it, since it's completely + // occluded. We will need to re-allocate this surface if it becomes visible, + // but that's likely to be rare (e.g. when there is no content display list + // for a frame or two during a tab switch). + let surface = tile.surface.as_mut().expect("no tile surface set!"); + + if let TileSurface::Texture { descriptor: SurfaceTextureDescriptor::Native { id, .. }, .. } = surface { + if let Some(id) = id.take() { + frame_state.resource_cache.destroy_compositor_tile(id); + } + } - frame_state.init_surface( - raster_config.surface_index, - render_task_id, - parent_surface_index, - clipped, - ); + tile.is_visible = false; + continue; + } + } + None => { + tile.is_visible = false; + } + } + } + + // If we get here, we want to ensure that the surface remains valid in the texture + // cache, _even if_ it's not visible due to clipping or being scrolled off-screen. + // This ensures that we retain valid tiles that are off-screen, but still in the + // display port of this tile cache instance. + if let Some(TileSurface::Texture { descriptor, .. }) = tile.surface.as_ref() { + if let SurfaceTextureDescriptor::TextureCache { ref handle, .. } = descriptor { + frame_state.resource_cache.texture_cache.request( + handle, + frame_state.gpu_cache, + ); + } + } + + // If the tile has been found to be off-screen / clipped, skip any further processing. + if !tile.is_visible { + continue; + } + + if frame_context.debug_flags.contains(DebugFlags::PICTURE_CACHING_DBG) { + tile.root.draw_debug_rects( + &map_pic_to_world, + tile.is_opaque, + tile.current_descriptor.local_valid_rect, + scratch, + frame_context.global_device_pixel_scale, + ); + + let label_offset = DeviceVector2D::new(20.0, 30.0); + let tile_device_rect = tile.world_tile_rect * frame_context.global_device_pixel_scale; + if tile_device_rect.size.height >= label_offset.y { + let surface = tile.surface.as_ref().expect("no tile surface set!"); + + scratch.push_debug_string( + tile_device_rect.origin + label_offset, + debug_colors::RED, + format!("{:?}: s={} is_opaque={} surface={}", + tile.id, + tile_cache.slice, + tile.is_opaque, + surface.kind(), + ), + ); + } + } + + if let TileSurface::Texture { descriptor, .. } = tile.surface.as_mut().unwrap() { + match descriptor { + SurfaceTextureDescriptor::TextureCache { ref handle, .. } => { + // Invalidate if the backing texture was evicted. + if frame_state.resource_cache.texture_cache.is_allocated(handle) { + // Request the backing texture so it won't get evicted this frame. + // We specifically want to mark the tile texture as used, even + // if it's detected not visible below and skipped. This is because + // we maintain the set of tiles we care about based on visibility + // during pre_update. If a tile still exists after that, we are + // assuming that it's either visible or we want to retain it for + // a while in case it gets scrolled back onto screen soon. + // TODO(gw): Consider switching to manual eviction policy? + frame_state.resource_cache.texture_cache.request(handle, frame_state.gpu_cache); + } else { + // If the texture was evicted on a previous frame, we need to assume + // that the entire tile rect is dirty. + tile.invalidate(None, InvalidationReason::NoTexture); + } + } + SurfaceTextureDescriptor::Native { id, .. } => { + if id.is_none() { + // There is no current surface allocation, so ensure the entire tile is invalidated + tile.invalidate(None, InvalidationReason::NoSurface); + } + } + } + } + + // Ensure that the dirty rect doesn't extend outside the local valid rect. + tile.local_dirty_rect = tile.local_dirty_rect + .intersection(&tile.current_descriptor.local_valid_rect) + .unwrap_or_else(PictureRect::zero); + + // Update the world/device dirty rect + let world_dirty_rect = map_pic_to_world.map(&tile.local_dirty_rect).expect("bug"); + + let device_rect = (tile.world_tile_rect * frame_context.global_device_pixel_scale).round(); + tile.device_dirty_rect = (world_dirty_rect * frame_context.global_device_pixel_scale) + .round_out() + .intersection(&device_rect) + .unwrap_or_else(DeviceRect::zero); + + if tile.is_valid { + continue; + } + + // Ensure that this texture is allocated. + if let TileSurface::Texture { ref mut descriptor, ref mut visibility_mask } = tile.surface.as_mut().unwrap() { + match descriptor { + SurfaceTextureDescriptor::TextureCache { ref mut handle } => { + if !frame_state.resource_cache.texture_cache.is_allocated(handle) { + frame_state.resource_cache.texture_cache.update_picture_cache( + tile_cache.current_tile_size, + handle, + frame_state.gpu_cache, + ); + } + } + SurfaceTextureDescriptor::Native { id } => { + if id.is_none() { + // Allocate a native surface id if we're in native compositing mode, + // and we don't have a surface yet (due to first frame, or destruction + // due to tile size changing etc). + if tile_cache.native_surface.is_none() { + let opaque = frame_state + .resource_cache + .create_compositor_surface( + tile_cache.virtual_offset, + tile_cache.current_tile_size, + true, + ); + + let alpha = frame_state + .resource_cache + .create_compositor_surface( + tile_cache.virtual_offset, + tile_cache.current_tile_size, + false, + ); + + tile_cache.native_surface = Some(NativeSurface { + opaque, + alpha, + }); + } + + // Create the tile identifier and allocate it. + let surface_id = if tile.is_opaque { + tile_cache.native_surface.as_ref().unwrap().opaque + } else { + tile_cache.native_surface.as_ref().unwrap().alpha + }; + + let tile_id = NativeTileId { + surface_id, + x: tile.tile_offset.x, + y: tile.tile_offset.y, + }; + + frame_state.resource_cache.create_compositor_tile(tile_id); + + *id = Some(tile_id); + } + } + } + + *visibility_mask = PrimitiveVisibilityMask::empty(); + let dirty_region_index = tile_cache.dirty_region.dirty_rects.len(); + + // If we run out of dirty regions, then force the last dirty region to + // be a union of any remaining regions. This is an inefficiency, in that + // we'll add items to batches later on that are redundant / outside this + // tile, but it's really rare except in pathological cases (even on a + // 4k screen, the typical dirty region count is < 16). + if dirty_region_index < PrimitiveVisibilityMask::MAX_DIRTY_REGIONS { + visibility_mask.set_visible(dirty_region_index); + + tile_cache.dirty_region.push( + world_dirty_rect, + *visibility_mask, + ); + } else { + visibility_mask.set_visible(PrimitiveVisibilityMask::MAX_DIRTY_REGIONS - 1); + + tile_cache.dirty_region.include_rect( + PrimitiveVisibilityMask::MAX_DIRTY_REGIONS - 1, + world_dirty_rect, + ); + } + + let content_origin_f = tile.world_tile_rect.origin * device_pixel_scale; + let content_origin = content_origin_f.round(); + debug_assert!((content_origin_f.x - content_origin.x).abs() < 0.01); + debug_assert!((content_origin_f.y - content_origin.y).abs() < 0.01); + + let surface = descriptor.resolve( + frame_state.resource_cache, + tile_cache.current_tile_size, + ); + + let scissor_rect = tile.device_dirty_rect + .translate(-device_rect.origin.to_vector()) + .round() + .to_i32(); + + let valid_rect = tile.device_valid_rect + .translate(-device_rect.origin.to_vector()) + .round() + .to_i32(); + + let render_task_id = frame_state.render_tasks.add().init( + RenderTask::new_picture( + RenderTaskLocation::PictureCache { + size: tile_cache.current_tile_size, + surface, + }, + tile_cache.current_tile_size.to_f32(), + pic_index, + content_origin.to_i32(), + UvRectKind::Rect, + surface_spatial_node_index, + device_pixel_scale, + *visibility_mask, + Some(scissor_rect), + Some(valid_rect), + ) + ); + + frame_state.render_tasks.add_dependency( + frame_state.surfaces[parent_surface_index.0].render_tasks.unwrap().port, + render_task_id, + ); + + if first { + // TODO(gw): Maybe we can restructure this code to avoid the + // first hack here. Or at least explain it with a follow up + // bug. + frame_state.surfaces[raster_config.surface_index.0].render_tasks = Some(SurfaceRenderTasks { + root: render_task_id, + port: render_task_id, + }); + + first = false; + } + } + + // Now that the tile is valid, reset the dirty rect. + tile.local_dirty_rect = PictureRect::zero(); + tile.is_valid = true; + } + + // If invalidation debugging is enabled, dump the picture cache state to a tree printer. + if frame_context.debug_flags.contains(DebugFlags::INVALIDATION_DBG) { + tile_cache.print(); + } + + None } PictureCompositeMode::MixBlend(..) | PictureCompositeMode::Blit(_) => { if let Some(scale) = adjust_scale_for_max_surface_size( raster_config, frame_context.fb_config.max_target_size, pic_rect, &map_pic_to_raster, &map_raster_to_world, - raster_config.clipped_bounding_rect, + clipped_prim_bounding_rect, &mut device_pixel_scale, &mut clipped, &mut unclipped, ) { raster_config.root_scaling_factor = scale; } + let clipped = clipped.to_i32(); + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -5716,45 +5546,36 @@ impl PicturePrimitive { device_pixel_scale, ); - let task_size = clipped.size.to_i32(); - - let render_task_id = frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_picture( - task_size, - unclipped.size, - pic_index, - clipped.origin, - surface_spatial_node_index, - device_pixel_scale, - None, - None, - None, - ) - ).with_uv_rect_kind(uv_rect_kind) + let render_task_id = frame_state.render_tasks.add().init( + RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, clipped.size), + unclipped.size, + pic_index, + clipped.origin, + uv_rect_kind, + surface_spatial_node_index, + device_pixel_scale, + PrimitiveVisibilityMask::all(), + None, + None, + ) ); - primary_render_task_id = Some(render_task_id); - - frame_state.init_surface( - raster_config.surface_index, - render_task_id, - parent_surface_index, - clipped, - ); + Some((render_task_id, render_task_id)) } PictureCompositeMode::SvgFilter(ref primitives, ref filter_datas) => { if let Some(scale) = adjust_scale_for_max_surface_size( raster_config, frame_context.fb_config.max_target_size, pic_rect, &map_pic_to_raster, &map_raster_to_world, - raster_config.clipped_bounding_rect, + clipped_prim_bounding_rect, &mut device_pixel_scale, &mut clipped, &mut unclipped, ) { raster_config.root_scaling_factor = scale; } + let clipped = clipped.to_i32(); + let uv_rect_kind = calculate_uv_rect_kind( &pic_rect, &transform, @@ -5762,87 +5583,75 @@ impl PicturePrimitive { device_pixel_scale, ); - let task_size = clipped.size.to_i32(); - - let picture_task_id = frame_state.rg_builder.add().init( - RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_picture( - task_size, - unclipped.size, - pic_index, - clipped.origin, - surface_spatial_node_index, - device_pixel_scale, - None, - None, - None, - ) - ).with_uv_rect_kind(uv_rect_kind) + let picture_task_id = frame_state.render_tasks.add().init( + RenderTask::new_picture( + RenderTaskLocation::Dynamic(None, clipped.size), + unclipped.size, + pic_index, + clipped.origin, + uv_rect_kind, + surface_spatial_node_index, + device_pixel_scale, + PrimitiveVisibilityMask::all(), + None, + None, + ) ); let filter_task_id = RenderTask::new_svg_filter( primitives, filter_datas, - frame_state.rg_builder, - clipped.size.to_i32(), + &mut frame_state.render_tasks, + clipped.size, uv_rect_kind, picture_task_id, device_pixel_scale, ); - primary_render_task_id = Some(filter_task_id); - - frame_state.init_surface_chain( - raster_config.surface_index, - filter_task_id, - picture_task_id, - parent_surface_index, - clipped, - ); + Some((filter_task_id, picture_task_id)) } - } + }; - self.primary_render_task_id = primary_render_task_id; + if let Some((root, port)) = dep_info { + frame_state.surfaces[raster_config.surface_index.0].render_tasks = Some(SurfaceRenderTasks { + root, + port, + }); - // Update the device pixel ratio in the surface, in case it was adjusted due - // to the surface being too large. This ensures the correct scale is available - // in case it's used as input to a parent mix-blend-mode readback. - frame_state - .surfaces[raster_config.surface_index.0] - .device_pixel_scale = device_pixel_scale; + frame_state.render_tasks.add_dependency( + frame_state.surfaces[parent_surface_index.0].render_tasks.unwrap().port, + root, + ); + } } None => {} }; - #[cfg(feature = "capture")] { if frame_context.debug_flags.contains(DebugFlags::TILE_CACHE_LOGGING_DBG) { - if let Some(PictureCompositeMode::TileCache { slice_id }) = self.requested_composite_mode { - if let Some(ref tile_cache) = tile_caches.get(&slice_id) { - // extract just the fields that we're interested in - let mut tile_cache_tiny = TileCacheInstanceSerializer { - slice: tile_cache.slice, - tiles: FastHashMap::default(), - background_color: tile_cache.background_color, - fract_offset: tile_cache.fract_offset - }; - // TODO(gw): Debug output only writes the primary sub-slice for now - for (key, tile) in &tile_cache.sub_slices.first().unwrap().tiles { - tile_cache_tiny.tiles.insert(*key, TileSerializer { - rect: tile.local_tile_rect, - current_descriptor: tile.current_descriptor.clone(), - device_fract_offset: tile.device_fract_offset, - id: tile.id, - root: tile.root.clone(), - background_color: tile.background_color, - invalidation_reason: tile.invalidation_reason.clone() - }); - } - let text = ron::ser::to_string_pretty(&tile_cache_tiny, Default::default()).unwrap(); - tile_cache_logger.add(text, map_pic_to_world.get_transform()); + if let Some(ref tile_cache) = self.tile_cache + { + // extract just the fields that we're interested in + let mut tile_cache_tiny = TileCacheInstanceSerializer { + slice: tile_cache.slice, + tiles: FastHashMap::default(), + background_color: tile_cache.background_color, + fract_offset: tile_cache.fract_offset + }; + for (key, tile) in &tile_cache.tiles { + tile_cache_tiny.tiles.insert(*key, TileSerializer { + rect: tile.local_tile_rect, + current_descriptor: tile.current_descriptor.clone(), + fract_offset: tile.fract_offset, + id: tile.id, + root: tile.root.clone(), + background_color: tile.background_color, + invalidation_reason: tile.invalidation_reason.clone() + }); } + let text = ron::ser::to_string_pretty(&tile_cache_tiny, Default::default()).unwrap(); + tile_cache_logger.add(text, map_pic_to_world.get_transform()); } } } @@ -5864,28 +5673,25 @@ impl PicturePrimitive { // If this is a picture cache, push the dirty region to ensure any // child primitives are culled and clipped to the dirty rect(s). - if let Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) = self.raster_config { - let dirty_region = tile_caches[&slice_id].dirty_region.clone(); + if let Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) = self.raster_config { + let dirty_region = self.tile_cache.as_ref().unwrap().dirty_region.clone(); frame_state.push_dirty_region(dirty_region); dirty_region_count += 1; } if inflation_factor > 0.0 { - let inflated_region = frame_state.current_dirty_region().inflate( - inflation_factor, - frame_context.spatial_tree, - ); + let inflated_region = frame_state.current_dirty_region().inflate(inflation_factor); frame_state.push_dirty_region(inflated_region); dirty_region_count += 1; } // Disallow subpixel AA if an intermediate surface is needed. // TODO(lsalzman): allow overriding parent if intermediate surface is opaque - let subpixel_mode = match self.raster_config { + let (is_passthrough, subpixel_mode) = match self.raster_config { Some(RasterConfig { ref composite_mode, .. }) => { let subpixel_mode = match composite_mode { - PictureCompositeMode::TileCache { slice_id } => { - tile_caches[&slice_id].subpixel_mode + PictureCompositeMode::TileCache { .. } => { + self.tile_cache.as_ref().unwrap().subpixel_mode.clone() } PictureCompositeMode::Blit(..) | PictureCompositeMode::ComponentTransferFilter(..) | @@ -5900,10 +5706,10 @@ impl PicturePrimitive { } }; - subpixel_mode + (false, subpixel_mode) } None => { - SubpixelMode::Allow + (true, SubpixelMode::Allow) } }; @@ -5913,16 +5719,18 @@ impl PicturePrimitive { // Both parent and this surface unconditionally allow subpixel AA SubpixelMode::Allow } - (SubpixelMode::Allow, SubpixelMode::Conditional { allowed_rect }) => { + (SubpixelMode::Allow, SubpixelMode::Conditional { allowed_rect, excluded_rects }) => { // Parent allows, but we are conditional subpixel AA SubpixelMode::Conditional { allowed_rect, + excluded_rects, } } - (SubpixelMode::Conditional { allowed_rect }, SubpixelMode::Allow) => { + (SubpixelMode::Conditional { allowed_rect, excluded_rects }, SubpixelMode::Allow) => { // Propagate conditional subpixel mode to child pictures that allow subpixel AA SubpixelMode::Conditional { - allowed_rect, + allowed_rect: *allowed_rect, + excluded_rects: excluded_rects.clone(), } } (SubpixelMode::Conditional { .. }, SubpixelMode::Conditional { ..}) => { @@ -5937,6 +5745,7 @@ impl PicturePrimitive { let context = PictureContext { pic_index, apply_local_clip_rect: self.apply_local_clip_rect, + is_passthrough, raster_spatial_node_index, surface_spatial_node_index, surface_index, @@ -5951,6 +5760,7 @@ impl PicturePrimitive { pub fn restore_context( &mut self, + parent_surface_index: SurfaceIndex, prim_list: PrimitiveList, context: PictureContext, state: PictureState, @@ -5961,6 +5771,9 @@ impl PicturePrimitive { frame_state.pop_dirty_region(); } + let task_id = frame_state.surfaces[parent_surface_index.0].render_tasks.unwrap().port; + self.num_render_tasks = frame_state.render_tasks[task_id].children.len(); + self.prim_list = prim_list; self.state = Some(state); } @@ -6053,9 +5866,7 @@ impl PicturePrimitive { // Process the accumulated split planes and order them for rendering. // Z axis is directed at the screen, `sort` is ascending, and we need back-to-front order. - let sorted = splitter.sort(vec3(0.0, 0.0, 1.0)); - ordered.reserve(sorted.len()); - for poly in sorted { + for poly in splitter.sort(vec3(0.0, 0.0, 1.0)) { let cluster = &self.prim_list.clusters[poly.anchor.cluster_index]; let spatial_node_index = cluster.spatial_node_index; let transform = match spatial_tree @@ -6129,9 +5940,25 @@ impl PicturePrimitive { } } + // Push information about this pic on stack for children to read. + state.push_picture(PictureInfo { + _spatial_node_index: self.spatial_node_index, + }); + // See if this picture actually needs a surface for compositing. - // TODO(gw): FPC: Remove the actual / requested composite mode distinction. - let actual_composite_mode = self.requested_composite_mode.clone(); + let actual_composite_mode = match self.requested_composite_mode { + Some(PictureCompositeMode::Filter(ref filter)) if filter.is_noop() => None, + Some(PictureCompositeMode::TileCache { .. }) => { + // Only allow picture caching composite mode if global picture caching setting + // is enabled this frame. + if state.composite_state.picture_caching_is_enabled { + Some(PictureCompositeMode::TileCache { }) + } else { + None + } + }, + ref mode => mode.clone(), + }; if let Some(composite_mode) = actual_composite_mode { // Retrieve the positioning node information for the parent surface. @@ -6139,29 +5966,19 @@ impl PicturePrimitive { let parent_device_pixel_scale = state.current_surface().device_pixel_scale; let surface_spatial_node_index = self.spatial_node_index; + // Filters must be applied before transforms, to do this, we can mark this picture as establishing a raster root. + let has_svg_filter = if let PictureCompositeMode::SvgFilter(..) = composite_mode { + true + } else { + false + }; + let surface_to_parent_transform = frame_context.spatial_tree .get_relative_transform(surface_spatial_node_index, parent_raster_node_index); // Check if there is perspective or if an SVG filter is applied, and thus whether a new // rasterization root should be established. - let establishes_raster_root = match composite_mode { - PictureCompositeMode::TileCache { .. } => { - // Picture caches are special cased - they never need to establish a raster root. In future, - // we will probably remove TileCache as a specific composite mode. - false - } - PictureCompositeMode::SvgFilter(..) => { - // Filters must be applied before transforms, to do this, we can mark this picture as establishing a raster root. - true - } - PictureCompositeMode::MixBlend(..) | - PictureCompositeMode::Filter(..) | - PictureCompositeMode::ComponentTransferFilter(..) | - PictureCompositeMode::Blit(..) => { - // TODO(gw): As follow ups, individually move each of these composite modes to create raster roots. - surface_to_parent_transform.is_perspective() - } - }; + let establishes_raster_root = has_svg_filter || surface_to_parent_transform.is_perspective(); let (raster_spatial_node_index, device_pixel_scale) = if establishes_raster_root { // If a raster root is established, this surface should be scaled based on the scale factors of the surface raster to parent raster transform. @@ -6188,8 +6005,8 @@ impl PicturePrimitive { let mut inflation_factor = 0.0; if self.options.inflate_if_required { match composite_mode { - PictureCompositeMode::Filter(Filter::Blur(width, height)) => { - let blur_radius = f32::max(clamp_blur_radius(width, scale_factors), clamp_blur_radius(height, scale_factors)); + PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => { + let blur_radius = clamp_blur_radius(blur_radius, scale_factors); // The amount of extra space needed for primitives inside // this picture to ensure the visibility check is correct. inflation_factor = blur_radius * BLUR_SAMPLE_SCALE; @@ -6198,8 +6015,7 @@ impl PicturePrimitive { let mut max = 0.0; for primitive in primitives { if let FilterPrimitiveKind::Blur(ref blur) = primitive.kind { - max = f32::max(max, blur.width); - max = f32::max(max, blur.height); + max = f32::max(max, blur.radius); } } inflation_factor = clamp_blur_radius(max, scale_factors) * BLUR_SAMPLE_SCALE; @@ -6237,7 +6053,6 @@ impl PicturePrimitive { establishes_raster_root, surface_index: state.push_surface(surface), root_scaling_factor: 1.0, - clipped_bounding_rect: WorldRect::zero(), }); } @@ -6256,7 +6071,8 @@ impl PicturePrimitive { // Restore the pictures list used during recursion. self.prim_list = prim_list; - let surface = state.current_surface_mut(); + // Pop the state information about this picture. + state.pop_picture(); for cluster in &mut self.prim_list.clusters { cluster.flags.remove(ClusterFlags::IS_VISIBLE); @@ -6266,14 +6082,12 @@ impl PicturePrimitive { // For in-preserve-3d primitives and pictures, the backface visibility is // evaluated relative to the containing block. if let Picture3DContext::In { ancestor_index, .. } = self.context_3d { - let mut face = VisibleFace::Front; - frame_context.spatial_tree.get_relative_transform_with_face( - cluster.spatial_node_index, - ancestor_index, - Some(&mut face), - ); - if face == VisibleFace::Back { - continue + match frame_context.spatial_tree + .get_relative_transform(cluster.spatial_node_index, ancestor_index) + .visible_face() + { + VisibleFace::Back => continue, + VisibleFace::Front => (), } } } @@ -6296,7 +6110,7 @@ impl PicturePrimitive { frame_context.spatial_tree, ); - for prim_instance in &mut self.prim_list.prim_instances[cluster.prim_range()] { + for prim_instance in &mut cluster.prim_instances { match prim_instance.kind { PrimitiveInstanceKind::Backdrop { data_handle, .. } => { // The actual size and clip rect of this primitive are determined by computing the bounding @@ -6326,7 +6140,7 @@ impl PicturePrimitive { // frame building is usually problematic since scene building will cache // the primitive information in the GPU already. prim_data.common.prim_rect = prim_rect; - prim_instance.clip_set.local_clip_rect = prim_rect; + prim_instance.local_clip_rect = prim_rect; // Update the cluster bounding rect now that we have the backdrop rect. cluster.bounding_rect = cluster.bounding_rect.union(&prim_rect); @@ -6340,13 +6154,16 @@ impl PicturePrimitive { // Map the cluster bounding rect into the space of the surface, and // include it in the surface bounding rect. + let surface = state.current_surface_mut(); surface.map_local_to_surface.set_target_spatial_node( cluster.spatial_node_index, frame_context.spatial_tree, ); // Mark the cluster visible, since it passed the invertible and - // backface checks. + // backface checks. In future, this will include spatial clustering + // which will allow the frame building code to skip most of the + // current per-primitive culling code. cluster.flags.insert(ClusterFlags::IS_VISIBLE); if let Some(cluster_rect) = surface.map_local_to_surface.map(&cluster.bounding_rect) { surface.rect = surface.rect.union(&cluster_rect); @@ -6359,8 +6176,23 @@ impl PicturePrimitive { if let Some(ref mut raster_config) = self.raster_config { let surface = state.current_surface_mut(); // Inflate the local bounding rect if required by the filter effect. + // This inflaction factor is to be applied to the surface itself. if self.options.inflate_if_required { surface.rect = raster_config.composite_mode.inflate_picture_rect(surface.rect, surface.scale_factors); + + // The picture's local rect is calculated as the union of the + // snapped primitive rects, which should result in a snapped + // local rect, unless it was inflated. This is also done during + // update visibility when calculating the picture's precise + // local rect. + let snap_surface_to_raster = SpaceSnapper::new_with_target( + surface.raster_spatial_node_index, + self.spatial_node_index, + surface.device_pixel_scale, + frame_context.spatial_tree, + ); + + surface.rect = snap_surface_to_raster.snap_rect(&surface.rect); } let mut surface_rect = surface.rect * Scale::new(1.0); @@ -6369,6 +6201,16 @@ impl PicturePrimitive { let surface_index = state.pop_surface(); debug_assert_eq!(surface_index, raster_config.surface_index); + // Check if any of the surfaces can't be rasterized in local space but want to. + if raster_config.establishes_raster_root + && (surface_rect.size.width > MAX_SURFACE_SIZE + || surface_rect.size.height > MAX_SURFACE_SIZE) + && frame_context.debug_flags.contains(DebugFlags::DISABLE_RASTER_ROOT_SCALING) + { + raster_config.establishes_raster_root = false; + state.are_raster_roots_assigned = false; + } + // Set the estimated and precise local rects. The precise local rect // may be changed again during frame visibility. self.estimated_local_rect = surface_rect; @@ -6459,6 +6301,7 @@ impl PicturePrimitive { } } } + PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {} PictureCompositeMode::Filter(ref filter) => { match *filter { Filter::ColorMatrix(ref m) => { @@ -6517,9 +6360,11 @@ fn calculate_screen_uv( fn calculate_uv_rect_kind( pic_rect: &PictureRect, transform: &PictureToRasterTransform, - rendered_rect: &DeviceRect, + rendered_rect: &DeviceIntRect, device_pixel_scale: DevicePixelScale, ) -> UvRectKind { + let rendered_rect = rendered_rect.to_f32(); + let top_left = calculate_screen_uv( &pic_rect.origin, transform, @@ -7338,40 +7183,3 @@ impl CompositeState { } } } - -pub fn get_raster_rects( - pic_rect: PictureRect, - map_to_raster: &SpaceMapper<PicturePixel, RasterPixel>, - map_to_world: &SpaceMapper<RasterPixel, WorldPixel>, - prim_bounding_rect: WorldRect, - device_pixel_scale: DevicePixelScale, -) -> Option<(DeviceRect, DeviceRect)> { - let unclipped_raster_rect = map_to_raster.map(&pic_rect)?; - - let unclipped = raster_rect_to_device_pixels( - unclipped_raster_rect, - device_pixel_scale, - ); - - let unclipped_world_rect = map_to_world.map(&unclipped_raster_rect)?; - let clipped_world_rect = unclipped_world_rect.intersection(&prim_bounding_rect)?; - - // We don't have to be able to do the back-projection from world into raster. - // Rendering only cares one way, so if that fails, we fall back to the full rect. - let clipped_raster_rect = match map_to_world.unmap(&clipped_world_rect) { - Some(rect) => rect.intersection(&unclipped_raster_rect)?, - None => return Some((unclipped, unclipped)), - }; - - let clipped = raster_rect_to_device_pixels( - clipped_raster_rect, - device_pixel_scale, - ); - - // Ensure that we won't try to allocate a zero-sized clip render task. - if clipped.is_empty() { - return None; - } - - Some((clipped, unclipped)) -} diff --git a/third_party/webrender/webrender/src/platform/macos/font.rs b/third_party/webrender/webrender/src/platform/macos/font.rs index 919e3a0086d..437522d5e9e 100644 --- a/third_party/webrender/webrender/src/platform/macos/font.rs +++ b/third_party/webrender/webrender/src/platform/macos/font.rs @@ -4,7 +4,7 @@ use api::{ColorU, FontKey, FontRenderMode, FontSize, GlyphDimensions}; use api::{FontInstanceFlags, FontVariation, NativeFontHandle}; -use core_foundation::{array::{CFArray, CFArrayRef}, data::CFData}; +use core_foundation::array::{CFArray, CFArrayRef}; use core_foundation::base::TCFType; use core_foundation::dictionary::CFDictionary; use core_foundation::number::{CFNumber, CFNumberRef}; @@ -14,12 +14,12 @@ use core_graphics::base::{kCGBitmapByteOrder32Little}; use core_graphics::color_space::CGColorSpace; use core_graphics::context::CGContext; use core_graphics::context::{CGBlendMode, CGTextDrawingMode}; +use core_graphics::data_provider::CGDataProvider; use core_graphics::font::{CGFont, CGGlyph}; use core_graphics::geometry::{CGAffineTransform, CGPoint, CGSize}; use core_graphics::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CGRect}; -use core_text::{self, font_descriptor::CTFontDescriptorCreateCopyWithAttributes}; +use core_text; use core_text::font::{CTFont, CTFontRef}; -use core_text::font_descriptor::{CTFontDescriptor, CTFontSymbolicTraits}; use core_text::font_descriptor::{kCTFontDefaultOrientation, kCTFontColorGlyphsTrait}; use euclid::default::Size2D; use crate::gamma_lut::{ColorLut, GammaLut}; @@ -31,21 +31,9 @@ use std::sync::Arc; const INITIAL_CG_CONTEXT_SIDE_LENGTH: u32 = 32; -// We prefer to create CTFonts from a CTFontDescriptor, but that doesn't work in the case -// of hidden system fonts on recent macOS versions, so for those we will instead use a -// native CGFont as the basis. -enum DescOrFont { - Desc(CTFontDescriptor), - Font(CGFont), -} - pub struct FontContext { - desc_or_fonts: FastHashMap<FontKey, DescOrFont>, - // Table mapping a sized font key with variations to its instantiated CoreText font. - // We also cache the symbolic traits for the given CT font when it is instantiated. - // This avoids an expensive bottleneck accessing the symbolic traits every time we - // need to rasterize a glyph or access its dimensions. - ct_fonts: FastHashMap<(FontKey, FontSize, Vec<FontVariation>), (CTFont, CTFontSymbolicTraits)>, + cg_fonts: FastHashMap<FontKey, CGFont>, + ct_fonts: FastHashMap<(FontKey, FontSize, Vec<FontVariation>), CTFont>, #[allow(dead_code)] graphics_context: GraphicsContext, #[allow(dead_code)] @@ -66,75 +54,29 @@ struct GlyphMetrics { advance: f32, } -// There are a number of different OS prefs that control whether or not -// requesting font smoothing actually results in subpixel AA. This gets even -// murkier in newer macOS versions that deprecate subpixel AA, with the prefs -// potentially interacting and overriding each other. In an attempt to future- -// proof things against any new prefs or interpretation of those prefs in -// future macOS versions, we do a check here to request font smoothing and see -// what result it actually gives us much like Skia does. We need to check for -// each of three potential results and process them in the font backend in -// distinct ways: -// 1) subpixel AA (differing RGB channels) with dilation -// 2) grayscale AA (matching RGB channels) with dilation, a compatibility mode -// 3) grayscale AA without dilation as if font smoothing was not requested -// We can discern between case 1 and the rest by checking if the subpixels differ. -// We can discern between cases 2 and 3 by rendering with and without smoothing -// and comparing the two to determine if there was some dilation. -// This returns the actual FontRenderMode needed to support each case, if any. -fn determine_font_smoothing_mode() -> Option<FontRenderMode> { - let mut smooth_context = CGContext::create_bitmap_context( - None, - 12, - 12, - 8, - 12 * 4, - &CGColorSpace::create_device_rgb(), - kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, - ); - smooth_context.set_should_smooth_fonts(true); - smooth_context.set_should_antialias(true); - smooth_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); - let mut gray_context = CGContext::create_bitmap_context( +// According to the Skia source code, there's no public API to +// determine if subpixel AA is supported. So jrmuizel ported +// this function from Skia which is used to check if a glyph +// can be rendered with subpixel AA. +fn supports_subpixel_aa() -> bool { + let mut cg_context = CGContext::create_bitmap_context( None, - 12, - 12, + 1, + 1, 8, - 12 * 4, + 4, &CGColorSpace::create_device_rgb(), kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, ); - gray_context.set_should_smooth_fonts(false); - gray_context.set_should_antialias(true); - gray_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); - - // Autorelease pool for CTFont - objc::rc::autoreleasepool(|| { - // Lucida Grande 12 is the default fallback font in Firefox - let ct_font = core_text::font::new_from_name("Lucida Grande", 12.).unwrap(); - let point = CGPoint { x: 0., y: 0. }; - let glyph = 'X' as CGGlyph; - ct_font.draw_glyphs(&[glyph], &[point], smooth_context.clone()); - ct_font.draw_glyphs(&[glyph], &[point], gray_context.clone()); - }); - - let mut mode = None; - for (smooth, gray) in smooth_context.data().chunks(4).zip(gray_context.data().chunks(4)) { - if smooth[0] != smooth[1] || smooth[1] != smooth[2] { - return Some(FontRenderMode::Subpixel); - } - if smooth[0] != gray[0] || smooth[1] != gray[1] || smooth[2] != gray[2] { - mode = Some(FontRenderMode::Alpha); - } - } - return mode; -} - -// We cache the font smoothing mode globally, rather than storing it in each FontContext, -// to avoid having to determine this redundantly in each context and to avoid needing to -// lock them to access this setting in prepare_font. -lazy_static! { - static ref FONT_SMOOTHING_MODE: Option<FontRenderMode> = determine_font_smoothing_mode(); + let ct_font = core_text::font::new_from_name("Helvetica", 16.).unwrap(); + cg_context.set_should_smooth_fonts(true); + cg_context.set_should_antialias(true); + cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); + let point = CGPoint { x: -1., y: 0. }; + let glyph = '|' as CGGlyph; + ct_font.draw_glyphs(&[glyph], &[point], cg_context.clone()); + let data = cg_context.data(); + data[0] != data[1] || data[1] != data[2] } fn should_use_white_on_black(color: ColorU) -> bool { @@ -223,17 +165,13 @@ extern { static kCTFontVariationAxisMinimumValueKey: CFStringRef; static kCTFontVariationAxisMaximumValueKey: CFStringRef; static kCTFontVariationAxisDefaultValueKey: CFStringRef; - static kCTFontVariationAttribute: CFStringRef; fn CTFontCopyVariationAxes(font: CTFontRef) -> CFArrayRef; } -fn new_ct_font_with_variations(desc_or_font: &DescOrFont, size: f64, variations: &[FontVariation]) -> CTFont { +fn new_ct_font_with_variations(cg_font: &CGFont, size: f64, variations: &[FontVariation]) -> CTFont { unsafe { - let ct_font = match desc_or_font { - DescOrFont::Desc(ct_font_desc) => core_text::font::new_from_descriptor(ct_font_desc, size), - DescOrFont::Font(cg_font) => core_text::font::new_from_CGFont(cg_font, size) - }; + let ct_font = core_text::font::new_from_CGFont(cg_font, size); if variations.is_empty() { return ct_font; } @@ -242,12 +180,7 @@ fn new_ct_font_with_variations(desc_or_font: &DescOrFont, size: f64, variations: return ct_font; } let axes: CFArray<CFDictionary> = TCFType::wrap_under_create_rule(axes_ref); - // We collect the values with either number or string keys, depending whether - // we're going to instantiate the CTFont from a descriptor or a CGFont. - // It'd probably be better to switch the CGFont-related APIs to expect numbers, - // but that's left for a future cleanup. - let mut vals: Vec<(CFNumber, CFNumber)> = Vec::with_capacity(variations.len() as usize); - let mut vals_str: Vec<(CFString, CFNumber)> = Vec::with_capacity(variations.len() as usize); + let mut vals: Vec<(CFString, CFNumber)> = Vec::with_capacity(variations.len() as usize); for axis in axes.iter() { if !axis.instance_of::<CFDictionary>() { return ct_font; @@ -320,48 +253,33 @@ fn new_ct_font_with_variations(desc_or_font: &DescOrFont, size: f64, variations: val = val.max(min_val).min(max_val); if val != def_val { - match desc_or_font { - DescOrFont::Font(_) => vals_str.push((name, CFNumber::from(val))), - DescOrFont::Desc(_) => vals.push((CFNumber::from(tag_val), CFNumber::from(val))), - } + vals.push((name, CFNumber::from(val))); } } - match desc_or_font { - DescOrFont::Desc(ct_font_desc) => { - if vals.is_empty() { - return ct_font; - } - let vals_dict = CFDictionary::from_CFType_pairs(&vals); - let attrs_dict = CFDictionary::from_CFType_pairs(&[(CFString::wrap_under_get_rule(kCTFontVariationAttribute), vals_dict)]); - let ct_var_font_desc = create_copy_with_attributes(ct_font_desc, attrs_dict.to_untyped()).unwrap(); - core_text::font::new_from_descriptor(&ct_var_font_desc, size) - } - DescOrFont::Font(cg_font) => { - if vals_str.is_empty() { - return ct_font; - } - let vals_dict = CFDictionary::from_CFType_pairs(&vals_str); - let cg_var_font = cg_font.create_copy_from_variations(&vals_dict).unwrap(); - core_text::font::new_from_CGFont_with_variations(&cg_var_font, size, &vals_dict) - } + if vals.is_empty() { + return ct_font; } + let vals_dict = CFDictionary::from_CFType_pairs(&vals); + let cg_var_font = cg_font.create_copy_from_variations(&vals_dict).unwrap(); + core_text::font::new_from_CGFont_with_variations(&cg_var_font, size, &vals_dict) } } -fn is_bitmap_font(traits: CTFontSymbolicTraits) -> bool { +fn is_bitmap_font(ct_font: &CTFont) -> bool { + let traits = ct_font.symbolic_traits(); (traits & kCTFontColorGlyphsTrait) != 0 } impl FontContext { pub fn new() -> Result<FontContext, ResourceCacheError> { - debug!("Test for subpixel AA support: {:?}", *FONT_SMOOTHING_MODE); + debug!("Test for subpixel AA support: {}", supports_subpixel_aa()); // Force CG to use sRGB color space to gamma correct. let contrast = 0.0; let gamma = 0.0; Ok(FontContext { - desc_or_fonts: FastHashMap::default(), + cg_fonts: FastHashMap::default(), ct_fonts: FastHashMap::default(), graphics_context: GraphicsContext::new(), gamma_lut: GammaLut::new(contrast, gamma, gamma), @@ -369,47 +287,34 @@ impl FontContext { } pub fn has_font(&self, font_key: &FontKey) -> bool { - self.desc_or_fonts.contains_key(font_key) + self.cg_fonts.contains_key(font_key) } pub fn add_raw_font(&mut self, font_key: &FontKey, bytes: Arc<Vec<u8>>, index: u32) { - if self.desc_or_fonts.contains_key(font_key) { + if self.cg_fonts.contains_key(font_key) { return; } assert_eq!(index, 0); - let data = CFData_wrapping_arc_vec(bytes); - let ct_font_desc = match create_font_descriptor(data) { + let data_provider = CGDataProvider::from_buffer(bytes); + let cg_font = match CGFont::from_data_provider(data_provider) { Err(_) => return, - Ok(desc) => desc, + Ok(cg_font) => cg_font, }; - self.desc_or_fonts.insert(*font_key, DescOrFont::Desc(ct_font_desc)); + self.cg_fonts.insert(*font_key, cg_font); } pub fn add_native_font(&mut self, font_key: &FontKey, native_font_handle: NativeFontHandle) { - if self.desc_or_fonts.contains_key(font_key) { + if self.cg_fonts.contains_key(font_key) { return; } - // there's no way great way to go from a CGFont to a CTFontDescriptor - // so we use the postscript name. Ideally NativeFontHandle would - // just use a CTFontDescriptor. - let name = native_font_handle.0.postscript_name(); - // For "hidden" system fonts, whose names start with a period, - // we can't instantiate CTFonts via a descriptor. We're really - // supposed to use CTFontCreateUIFontForLanguage, but for now - // we just use the CGFont. - let desc_or_font = if name.to_string().starts_with('.') { - DescOrFont::Font(native_font_handle.0) - } else { - DescOrFont::Desc(core_text::font_descriptor::new_from_postscript_name(&name)) - }; - - self.desc_or_fonts.insert(*font_key, desc_or_font); + self.cg_fonts + .insert(*font_key, native_font_handle.0); } pub fn delete_font(&mut self, font_key: &FontKey) { - if let Some(_) = self.desc_or_fonts.remove(font_key) { + if let Some(_) = self.cg_fonts.remove(font_key) { self.ct_fonts.retain(|k, _| k.0 != *font_key); } } @@ -425,20 +330,16 @@ impl FontContext { font_key: FontKey, size: f64, variations: &[FontVariation], - ) -> Option<(CTFont, CTFontSymbolicTraits)> { - // Interacting with CoreText can create autorelease garbage. - objc::rc::autoreleasepool(|| { - match self.ct_fonts.entry((font_key, FontSize::from_f64_px(size), variations.to_vec())) { - Entry::Occupied(entry) => Some((*entry.get()).clone()), - Entry::Vacant(entry) => { - let desc_or_font = self.desc_or_fonts.get(&font_key)?; - let ct_font = new_ct_font_with_variations(desc_or_font, size, variations); - let traits = ct_font.symbolic_traits(); - entry.insert((ct_font.clone(), traits)); - Some((ct_font, traits)) - } + ) -> Option<CTFont> { + match self.ct_fonts.entry((font_key, FontSize::from_f64_px(size), variations.to_vec())) { + Entry::Occupied(entry) => Some((*entry.get()).clone()), + Entry::Vacant(entry) => { + let cg_font = self.cg_fonts.get(&font_key)?; + let ct_font = new_ct_font_with_variations(cg_font, size, variations); + entry.insert(ct_font.clone()); + Some(ct_font) } - }) + } } pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> { @@ -446,7 +347,7 @@ impl FontContext { let mut glyph = 0; self.get_ct_font(font_key, 16.0, &[]) - .and_then(|(ct_font, _)| { + .and_then(|ref ct_font| { unsafe { let result = ct_font.get_glyphs_for_characters(&character, &mut glyph, 1); @@ -467,9 +368,9 @@ impl FontContext { let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); let size = font.size.to_f64_px() * y_scale; self.get_ct_font(font.font_key, size, &font.variations) - .and_then(|(ct_font, traits)| { + .and_then(|ref ct_font| { let glyph = key.index() as CGGlyph; - let bitmap = is_bitmap_font(traits); + let bitmap = is_bitmap_font(ct_font); let (mut shape, (x_offset, y_offset)) = if bitmap { (FontTransform::identity(), (0.0, 0.0)) } else { @@ -510,7 +411,7 @@ impl FontContext { }; let extra_strikes = font.get_extra_strikes(strike_scale); let metrics = get_glyph_metrics( - &ct_font, + ct_font, transform.as_ref(), glyph, x_offset, @@ -569,22 +470,6 @@ impl FontContext { } pub fn prepare_font(font: &mut FontInstance) { - // Sanitize the render mode for font smoothing. If font smoothing is supported, - // then we just need to ensure the render mode is limited to what is supported. - // If font smoothing is actually disabled, then we need to fall back to grayscale. - if font.flags.contains(FontInstanceFlags::FONT_SMOOTHING) || - font.render_mode == FontRenderMode::Subpixel { - match *FONT_SMOOTHING_MODE { - Some(mode) => { - font.render_mode = font.render_mode.limit_by(mode); - font.flags.insert(FontInstanceFlags::FONT_SMOOTHING); - } - None => { - font.render_mode = font.render_mode.limit_by(FontRenderMode::Alpha); - font.flags.remove(FontInstanceFlags::FONT_SMOOTHING); - } - } - } match font.render_mode { FontRenderMode::Mono => { // In mono mode the color of the font is irrelevant. @@ -616,12 +501,10 @@ impl FontContext { } pub fn rasterize_glyph(&mut self, font: &FontInstance, key: &GlyphKey) -> GlyphRasterResult { - objc::rc::autoreleasepool(|| { let (x_scale, y_scale) = font.transform.compute_scale().unwrap_or((1.0, 1.0)); let size = font.size.to_f64_px() * y_scale; - let (ct_font, traits) = - self.get_ct_font(font.font_key, size, &font.variations).ok_or(GlyphRasterError::LoadFailed)?; - let glyph_type = if is_bitmap_font(traits) { + let ct_font = self.get_ct_font(font.font_key, size, &font.variations).ok_or(GlyphRasterError::LoadFailed)?; + let glyph_type = if is_bitmap_font(&ct_font) { GlyphType::Bitmap } else { GlyphType::Vector @@ -860,7 +743,7 @@ impl FontContext { GlyphType::Vector => font.get_glyph_format(), }, bytes: rasterized_pixels, - })}) + }) } } @@ -969,72 +852,3 @@ enum GlyphType { Vector, Bitmap, } - -// This stuff should eventually migrate to upstream core-foundation -#[allow(non_snake_case)] -fn CFData_wrapping_arc_vec(buffer: Arc<Vec<u8>>) -> CFData { - use core_foundation::base::*; - use core_foundation::data::CFDataRef; - use std::os::raw::c_void; - - extern "C" { - pub fn CFDataCreateWithBytesNoCopy( - allocator: CFAllocatorRef, - bytes: *const u8, - length: CFIndex, - allocator: CFAllocatorRef, - ) -> CFDataRef; - } - unsafe { - let ptr = (*buffer).as_ptr() as *const _; - let len = buffer.len().to_CFIndex(); - let info = Arc::into_raw(buffer) as *mut c_void; - - extern "C" fn deallocate(_: *mut c_void, info: *mut c_void) { - unsafe { - drop(Arc::from_raw(info as *mut Vec<u8>)); - } - } - - // CFAllocatorContext doesn't have nullable members so we transmute - let allocator = CFAllocator::new(CFAllocatorContext { - info: info, - version: 0, - retain: None, - reallocate: None, - release: None, - copyDescription: None, - allocate: None, - deallocate: Some(deallocate), - preferredSize: None, - }); - let data_ref = - CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, ptr, len, allocator.as_CFTypeRef()); - TCFType::wrap_under_create_rule(data_ref) - } -} - -fn create_font_descriptor(cf_data: CFData) -> Result<CTFontDescriptor, ()> { - use core_text::font_descriptor::CTFontDescriptorRef; - use core_foundation::data::CFDataRef; - extern { - pub fn CTFontManagerCreateFontDescriptorFromData(data: CFDataRef) -> CTFontDescriptorRef; - } - unsafe { - let ct_font_descriptor_ref = CTFontManagerCreateFontDescriptorFromData(cf_data.as_concrete_TypeRef()); - if ct_font_descriptor_ref.is_null() { - return Err(()); - } - Ok(CTFontDescriptor::wrap_under_create_rule(ct_font_descriptor_ref)) - } -} - -fn create_copy_with_attributes(desc: &CTFontDescriptor, attr: CFDictionary) -> Result<CTFontDescriptor, ()> { - unsafe { - let ct_font_descriptor_ref = CTFontDescriptorCreateCopyWithAttributes(desc.as_concrete_TypeRef(), attr.as_concrete_TypeRef()); - if ct_font_descriptor_ref.is_null() { - return Err(()); - } - Ok(CTFontDescriptor::wrap_under_create_rule(ct_font_descriptor_ref)) -} -} diff --git a/third_party/webrender/webrender/src/platform/unix/font.rs b/third_party/webrender/webrender/src/platform/unix/font.rs index aa02e9460a9..52c0d114101 100644 --- a/third_party/webrender/webrender/src/platform/unix/font.rs +++ b/third_party/webrender/webrender/src/platform/unix/font.rs @@ -104,7 +104,6 @@ macro_rules! ft_dyn_fn { ft_dyn_fn!(FT_Get_MM_Var(face: FT_Face, desc: *mut *mut FT_MM_Var) -> FT_Error); ft_dyn_fn!(FT_Done_MM_Var(library: FT_Library, desc: *mut FT_MM_Var) -> FT_Error); ft_dyn_fn!(FT_Set_Var_Design_Coordinates(face: FT_Face, num_vals: FT_UInt, vals: *mut FT_Fixed) -> FT_Error); -ft_dyn_fn!(FT_Get_Var_Design_Coordinates(face: FT_Face, num_vals: FT_UInt, vals: *mut FT_Fixed) -> FT_Error); extern "C" { fn FT_GlyphSlot_Embolden(slot: FT_GlyphSlot); @@ -336,9 +335,9 @@ impl FontContext { }) } else { // TODO(gw): Provide detailed error values. - // Once this panic has been here for a while with no issues we should get rid of - // ResourceCacheError as this was the only place that could fail previously. - panic!("Failed to initialize FreeType - {}", result) + Err(ResourceCacheError::new( + format!("Failed to initialize FreeType - {}", result) + )) } } @@ -398,19 +397,6 @@ impl FontContext { let mm_var = normal_face.mm_var; let num_axis = (*mm_var).num_axis; let mut coords: Vec<FT_Fixed> = Vec::with_capacity(num_axis as usize); - - // Calling this before FT_Set_Var_Design_Coordinates avoids a bug with font variations - // not initialized properly in the font face, even if we ignore the result. - // See bug 1647035. - let mut tmp = [0; 16]; - let res = FT_Get_Var_Design_Coordinates( - normal_face.face, - num_axis.min(16), - tmp.as_mut_ptr() - ); - debug_assert!(succeeded(res)); - - for i in 0 .. num_axis { let axis = (*mm_var).axis.offset(i as isize); let mut value = (*axis).def; @@ -424,8 +410,7 @@ impl FontContext { } coords.push(value); } - let res = FT_Set_Var_Design_Coordinates(var_face, num_axis, coords.as_mut_ptr()); - debug_assert!(succeeded(res)); + FT_Set_Var_Design_Coordinates(var_face, num_axis, coords.as_mut_ptr()); } entry.insert(VariationFace(var_face)); Some(var_face) diff --git a/third_party/webrender/webrender/src/prepare.rs b/third_party/webrender/webrender/src/prepare.rs deleted file mode 100644 index c4cfcc982c7..00000000000 --- a/third_party/webrender/webrender/src/prepare.rs +++ /dev/null @@ -1,1606 +0,0 @@ -/* 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/. */ - -//! # Prepare pass -//! -//! TODO: document this! - -use std::cmp; -use api::{PremultipliedColorF, PropertyBinding}; -use api::{BoxShadowClipMode, BorderStyle, ClipMode}; -use api::units::*; -use euclid::Scale; -use smallvec::SmallVec; -use crate::image_tiling::{self, Repetition}; -use crate::border::{get_max_scale_for_border, build_border_instances}; -use crate::clip::{ClipStore}; -use crate::spatial_tree::{SpatialNodeIndex, SpatialTree}; -use crate::clip::{ClipDataStore, ClipNodeFlags, ClipChainInstance, ClipItemKind}; -use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; -use crate::gpu_cache::{GpuCacheHandle, GpuDataRequest}; -use crate::gpu_types::{BrushFlags}; -use crate::internal_types::{FastHashMap, PlaneSplitAnchor}; -use crate::picture::{PicturePrimitive, SliceId, TileCacheLogger, ClusterFlags, SurfaceRenderTasks}; -use crate::picture::{PrimitiveList, PrimitiveCluster, SurfaceIndex, TileCacheInstance, SubpixelMode}; -use crate::prim_store::line_dec::MAX_LINE_DECORATION_RESOLUTION; -use crate::prim_store::*; -use crate::render_backend::DataStores; -use crate::render_task_graph::RenderTaskId; -use crate::render_task_cache::RenderTaskCacheKeyKind; -use crate::render_task_cache::{RenderTaskCacheKey, to_cache_size, RenderTaskParent}; -use crate::render_task::{RenderTaskKind, RenderTask}; -use crate::segment::SegmentBuilder; -use crate::space::SpaceMapper; -use crate::util::{clamp_to_scale_factor, pack_as_float, raster_rect_to_device_pixels}; -use crate::visibility::{compute_conservative_visible_rect, PrimitiveVisibility, VisibilityState}; - - -const MAX_MASK_SIZE: f32 = 4096.0; - -const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0; - - -pub fn prepare_primitives( - store: &mut PrimitiveStore, - prim_list: &mut PrimitiveList, - pic_context: &PictureContext, - pic_state: &mut PictureState, - frame_context: &FrameBuildingContext, - frame_state: &mut FrameBuildingState, - data_stores: &mut DataStores, - scratch: &mut PrimitiveScratchBuffer, - tile_cache_log: &mut TileCacheLogger, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, -) { - profile_scope!("prepare_primitives"); - for (cluster_index, cluster) in prim_list.clusters.iter_mut().enumerate() { - if !cluster.flags.contains(ClusterFlags::IS_VISIBLE) { - continue; - } - profile_scope!("cluster"); - pic_state.map_local_to_pic.set_target_spatial_node( - cluster.spatial_node_index, - frame_context.spatial_tree, - ); - - frame_state.surfaces[pic_context.surface_index.0].opaque_rect = PictureRect::zero(); - - for (idx, prim_instance) in (&mut prim_list.prim_instances[cluster.prim_range()]).iter_mut().enumerate() { - let prim_instance_index = cluster.prim_range.start + idx; - - // First check for coarse visibility (if this primitive was completely off-screen) - match prim_instance.vis.state { - VisibilityState::Unset => { - panic!("bug: invalid vis state"); - } - VisibilityState::Culled => { - continue; - } - VisibilityState::Coarse { ref filter, vis_flags } => { - // The original coarse state was calculated during the initial visibility pass. - // However, it's possible that the dirty rect has got smaller, if tiles were not - // dirty. Intersecting with the dirty rect here eliminates preparing any primitives - // outside the dirty rect, and reduces the size of any off-screen surface allocations - // for clip masks / render tasks that we make. - - // Clear the current visibiilty mask, and build a more detailed one based on the dirty rect - // regions below. - let dirty_region = frame_state.current_dirty_region(); - let is_in_dirty_region = dirty_region.filters - .iter() - .any(|region_filter| region_filter.matches(filter)); - - if is_in_dirty_region { - prim_instance.vis.state = VisibilityState::Detailed { - filter: *filter, - vis_flags, - } - } else { - prim_instance.clear_visibility(); - continue; - } - } - VisibilityState::Detailed { .. } => { - // Was already set to detailed (picture caching disabled or a root element) - } - VisibilityState::PassThrough => {} - } - - let plane_split_anchor = PlaneSplitAnchor::new(cluster_index, prim_instance_index); - - if prepare_prim_for_render( - store, - prim_instance, - cluster, - pic_context, - pic_state, - frame_context, - frame_state, - plane_split_anchor, - data_stores, - scratch, - tile_cache_log, - tile_caches, - ) { - frame_state.num_visible_primitives += 1; - } else { - prim_instance.clear_visibility(); - } - } - - if !cluster.opaque_rect.is_empty() { - let surface = &mut frame_state.surfaces[pic_context.surface_index.0]; - - if let Some(cluster_opaque_rect) = surface.map_local_to_surface.map_inner_bounds(&cluster.opaque_rect) { - surface.opaque_rect = crate::util::conservative_union_rect(&surface.opaque_rect, &cluster_opaque_rect); - } - } - } -} - -fn prepare_prim_for_render( - store: &mut PrimitiveStore, - prim_instance: &mut PrimitiveInstance, - cluster: &mut PrimitiveCluster, - pic_context: &PictureContext, - pic_state: &mut PictureState, - frame_context: &FrameBuildingContext, - frame_state: &mut FrameBuildingState, - plane_split_anchor: PlaneSplitAnchor, - data_stores: &mut DataStores, - scratch: &mut PrimitiveScratchBuffer, - tile_cache_log: &mut TileCacheLogger, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, -) -> bool { - profile_scope!("prepare_prim_for_render"); - - // If we have dependencies, we need to prepare them first, in order - // to know the actual rect of this primitive. - // For example, scrolling may affect the location of an item in - // local space, which may force us to render this item on a larger - // picture target, if being composited. - if let PrimitiveInstanceKind::Picture { pic_index, .. } = prim_instance.kind { - let pic = &mut store.pictures[pic_index.0]; - - match pic.take_context( - pic_index, - pic_context.surface_spatial_node_index, - pic_context.raster_spatial_node_index, - pic_context.surface_index, - pic_context.subpixel_mode, - frame_state, - frame_context, - scratch, - tile_cache_log, - tile_caches, - ) { - Some((pic_context_for_children, mut pic_state_for_children, mut prim_list)) => { - prepare_primitives( - store, - &mut prim_list, - &pic_context_for_children, - &mut pic_state_for_children, - frame_context, - frame_state, - data_stores, - scratch, - tile_cache_log, - tile_caches, - ); - - // Restore the dependencies (borrow check dance) - store.pictures[pic_context_for_children.pic_index.0] - .restore_context( - prim_list, - pic_context_for_children, - pic_state_for_children, - frame_state, - ); - } - None => { - if prim_instance.is_chased() { - println!("\tculled for carrying an invisible composite filter"); - } - - return false; - } - } - } - - let prim_rect = data_stores.get_local_prim_rect( - prim_instance, - store, - ); - - if !update_clip_task( - prim_instance, - &prim_rect.origin, - cluster.spatial_node_index, - pic_context.raster_spatial_node_index, - pic_context, - pic_state, - frame_context, - frame_state, - store, - data_stores, - scratch, - ) { - if prim_instance.is_chased() { - println!("\tconsidered invisible"); - } - return false; - } - - if prim_instance.is_chased() { - println!("\tconsidered visible and ready with local pos {:?}", prim_rect.origin); - } - - #[cfg(debug_assertions)] - { - prim_instance.prepared_frame_id = frame_state.rg_builder.frame_id(); - } - - prepare_interned_prim_for_render( - store, - prim_instance, - cluster, - plane_split_anchor, - pic_context, - pic_state, - frame_context, - frame_state, - data_stores, - scratch, - ); - - true -} - -/// Prepare an interned primitive for rendering, by requesting -/// resources, render tasks etc. This is equivalent to the -/// prepare_prim_for_render_inner call for old style primitives. -fn prepare_interned_prim_for_render( - store: &mut PrimitiveStore, - prim_instance: &mut PrimitiveInstance, - cluster: &mut PrimitiveCluster, - plane_split_anchor: PlaneSplitAnchor, - pic_context: &PictureContext, - pic_state: &mut PictureState, - frame_context: &FrameBuildingContext, - frame_state: &mut FrameBuildingState, - data_stores: &mut DataStores, - scratch: &mut PrimitiveScratchBuffer, -) { - let prim_spatial_node_index = cluster.spatial_node_index; - let is_chased = prim_instance.is_chased(); - let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale; - let mut is_opaque = false; - - match &mut prim_instance.kind { - PrimitiveInstanceKind::LineDecoration { data_handle, ref mut render_task, .. } => { - profile_scope!("LineDecoration"); - let prim_data = &mut data_stores.line_decoration[*data_handle]; - let common_data = &mut prim_data.common; - let line_dec_data = &mut prim_data.kind; - - // Update the template this instane references, which may refresh the GPU - // cache with any shared template data. - line_dec_data.update(common_data, frame_state); - - // Work out the device pixel size to be used to cache this line decoration. - if is_chased { - println!("\tline decoration key={:?}", line_dec_data.cache_key); - } - - // If we have a cache key, it's a wavy / dashed / dotted line. Otherwise, it's - // a simple solid line. - if let Some(cache_key) = line_dec_data.cache_key.as_ref() { - // TODO(gw): Do we ever need / want to support scales for text decorations - // based on the current transform? - let scale_factor = Scale::new(1.0) * device_pixel_scale; - let mut task_size = (LayoutSize::from_au(cache_key.size) * scale_factor).ceil().to_i32(); - if task_size.width > MAX_LINE_DECORATION_RESOLUTION as i32 || - task_size.height > MAX_LINE_DECORATION_RESOLUTION as i32 { - let max_extent = cmp::max(task_size.width, task_size.height); - let task_scale_factor = Scale::new(MAX_LINE_DECORATION_RESOLUTION as f32 / max_extent as f32); - task_size = (LayoutSize::from_au(cache_key.size) * scale_factor * task_scale_factor) - .ceil().to_i32(); - } - - // Request a pre-rendered image task. - // TODO(gw): This match is a bit untidy, but it should disappear completely - // once the prepare_prims and batching are unified. When that - // happens, we can use the cache handle immediately, and not need - // to temporarily store it in the primitive instance. - *render_task = Some(frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size: task_size, - kind: RenderTaskCacheKeyKind::LineDecoration(cache_key.clone()), - }, - frame_state.gpu_cache, - frame_state.rg_builder, - None, - false, - RenderTaskParent::Surface(pic_context.surface_index), - frame_state.surfaces, - |rg_builder| { - rg_builder.add().init(RenderTask::new_dynamic( - task_size, - RenderTaskKind::new_line_decoration( - cache_key.style, - cache_key.orientation, - cache_key.wavy_line_thickness.to_f32_px(), - LayoutSize::from_au(cache_key.size), - ), - )) - } - )); - } - } - PrimitiveInstanceKind::TextRun { run_index, data_handle, .. } => { - profile_scope!("TextRun"); - let prim_data = &mut data_stores.text_run[*data_handle]; - let run = &mut store.text_runs[*run_index]; - - prim_data.common.may_need_repetition = false; - - // The glyph transform has to match `glyph_transform` in "ps_text_run" shader. - // It's relative to the rasterizing space of a glyph. - let transform = frame_context.spatial_tree - .get_relative_transform( - prim_spatial_node_index, - pic_context.raster_spatial_node_index, - ) - .into_fast_transform(); - let prim_offset = prim_data.common.prim_rect.origin.to_vector() - run.reference_frame_relative_offset; - - let pic = &store.pictures[pic_context.pic_index.0]; - let surface = &frame_state.surfaces[pic_context.surface_index.0]; - let root_scaling_factor = match pic.raster_config { - Some(ref raster_config) => raster_config.root_scaling_factor, - None => 1.0 - }; - - // If subpixel AA is disabled due to the backing surface the glyphs - // are being drawn onto, disable it (unless we are using the - // specifial subpixel mode that estimates background color). - let allow_subpixel = match prim_instance.vis.state { - VisibilityState::Culled | - VisibilityState::Unset | - VisibilityState::Coarse { .. } | - VisibilityState::PassThrough => { - panic!("bug: invalid visibility state"); - } - VisibilityState::Detailed { ref filter, .. } => { - // For now, we only allow subpixel AA on primary sub-slices. In future we - // may support other sub-slices if we find content that does this. - if filter.sub_slice_index.is_primary() { - match pic_context.subpixel_mode { - SubpixelMode::Allow => true, - SubpixelMode::Deny => false, - SubpixelMode::Conditional { allowed_rect } => { - // Conditional mode allows subpixel AA to be enabled for this - // text run, so long as it's inside the allowed rect. - allowed_rect.contains_rect(&prim_instance.vis.clip_chain.pic_clip_rect) - } - } - } else { - false - } - } - }; - - run.request_resources( - prim_offset, - &prim_data.font, - &prim_data.glyphs, - &transform.to_transform().with_destination::<_>(), - surface, - prim_spatial_node_index, - root_scaling_factor, - allow_subpixel, - frame_state.resource_cache, - frame_state.gpu_cache, - frame_context.spatial_tree, - scratch, - ); - - // Update the template this instane references, which may refresh the GPU - // cache with any shared template data. - prim_data.update(frame_state); - } - PrimitiveInstanceKind::Clear { data_handle, .. } => { - profile_scope!("Clear"); - let prim_data = &mut data_stores.prim[*data_handle]; - - prim_data.common.may_need_repetition = false; - - // Update the template this instane references, which may refresh the GPU - // cache with any shared template data. - prim_data.update(frame_state, frame_context.scene_properties); - } - PrimitiveInstanceKind::NormalBorder { data_handle, ref mut render_task_ids, .. } => { - profile_scope!("NormalBorder"); - let prim_data = &mut data_stores.normal_border[*data_handle]; - let common_data = &mut prim_data.common; - let border_data = &mut prim_data.kind; - - common_data.may_need_repetition = - matches!(border_data.border.top.style, BorderStyle::Dotted | BorderStyle::Dashed) || - matches!(border_data.border.right.style, BorderStyle::Dotted | BorderStyle::Dashed) || - matches!(border_data.border.bottom.style, BorderStyle::Dotted | BorderStyle::Dashed) || - matches!(border_data.border.left.style, BorderStyle::Dotted | BorderStyle::Dashed); - - - // Update the template this instance references, which may refresh the GPU - // cache with any shared template data. - border_data.update(common_data, frame_state); - - // TODO(gw): For now, the scale factors to rasterize borders at are - // based on the true world transform of the primitive. When - // raster roots with local scale are supported in future, - // that will need to be accounted for here. - let scale = frame_context - .spatial_tree - .get_world_transform(prim_spatial_node_index) - .scale_factors(); - - // Scale factors are normalized to a power of 2 to reduce the number of - // resolution changes. - // For frames with a changing scale transform round scale factors up to - // nearest power-of-2 boundary so that we don't keep having to redraw - // the content as it scales up and down. Rounding up to nearest - // power-of-2 boundary ensures we never scale up, only down --- avoiding - // jaggies. It also ensures we never scale down by more than a factor of - // 2, avoiding bad downscaling quality. - let scale_width = clamp_to_scale_factor(scale.0, false); - let scale_height = clamp_to_scale_factor(scale.1, false); - // Pick the maximum dimension as scale - let world_scale = LayoutToWorldScale::new(scale_width.max(scale_height)); - let mut scale = world_scale * device_pixel_scale; - let max_scale = get_max_scale_for_border(border_data); - scale.0 = scale.0.min(max_scale.0); - - // For each edge and corner, request the render task by content key - // from the render task cache. This ensures that the render task for - // this segment will be available for batching later in the frame. - let mut handles: SmallVec<[RenderTaskId; 8]> = SmallVec::new(); - - for segment in &border_data.border_segments { - // Update the cache key device size based on requested scale. - let cache_size = to_cache_size(segment.local_task_size, &mut scale); - let cache_key = RenderTaskCacheKey { - kind: RenderTaskCacheKeyKind::BorderSegment(segment.cache_key.clone()), - size: cache_size, - }; - - handles.push(frame_state.resource_cache.request_render_task( - cache_key, - frame_state.gpu_cache, - frame_state.rg_builder, - None, - false, // TODO(gw): We don't calculate opacity for borders yet! - RenderTaskParent::Surface(pic_context.surface_index), - frame_state.surfaces, - |rg_builder| { - rg_builder.add().init(RenderTask::new_dynamic( - cache_size, - RenderTaskKind::new_border_segment( - build_border_instances( - &segment.cache_key, - cache_size, - &border_data.border, - scale, - ) - ), - )) - } - )); - } - - *render_task_ids = scratch - .border_cache_handles - .extend(handles); - } - PrimitiveInstanceKind::ImageBorder { data_handle, .. } => { - profile_scope!("ImageBorder"); - let prim_data = &mut data_stores.image_border[*data_handle]; - - // TODO: get access to the ninepatch and to check whether we need support - // for repetitions in the shader. - - // Update the template this instance references, which may refresh the GPU - // cache with any shared template data. - prim_data.kind.update( - &mut prim_data.common, - frame_state - ); - } - PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, color_binding_index, .. } => { - profile_scope!("Rectangle"); - let prim_data = &mut data_stores.prim[*data_handle]; - prim_data.common.may_need_repetition = false; - - if *color_binding_index != ColorBindingIndex::INVALID { - match store.color_bindings[*color_binding_index] { - PropertyBinding::Binding(..) => { - // We explicitly invalidate the gpu cache - // if the color is animating. - let gpu_cache_handle = - if *segment_instance_index == SegmentInstanceIndex::INVALID { - None - } else if *segment_instance_index == SegmentInstanceIndex::UNUSED { - Some(&prim_data.common.gpu_cache_handle) - } else { - Some(&scratch.segment_instances[*segment_instance_index].gpu_cache_handle) - }; - if let Some(gpu_cache_handle) = gpu_cache_handle { - frame_state.gpu_cache.invalidate(gpu_cache_handle); - } - } - PropertyBinding::Value(..) => {}, - } - } - - // Update the template this instane references, which may refresh the GPU - // cache with any shared template data. - prim_data.update( - frame_state, - frame_context.scene_properties, - ); - - is_opaque = prim_data.common.opacity.is_opaque; - - write_segment( - *segment_instance_index, - frame_state, - &mut scratch.segments, - &mut scratch.segment_instances, - |request| { - prim_data.kind.write_prim_gpu_blocks( - request, - frame_context.scene_properties, - ); - } - ); - } - PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, .. } => { - profile_scope!("YuvImage"); - let prim_data = &mut data_stores.yuv_image[*data_handle]; - let common_data = &mut prim_data.common; - let yuv_image_data = &mut prim_data.kind; - is_opaque = true; - - common_data.may_need_repetition = false; - - // Update the template this instane references, which may refresh the GPU - // cache with any shared template data. - yuv_image_data.update(common_data, frame_state); - - write_segment( - *segment_instance_index, - frame_state, - &mut scratch.segments, - &mut scratch.segment_instances, - |request| { - yuv_image_data.write_prim_gpu_blocks(request); - } - ); - } - PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { - profile_scope!("Image"); - - let prim_data = &mut data_stores.image[*data_handle]; - let common_data = &mut prim_data.common; - let image_data = &mut prim_data.kind; - let image_instance = &mut store.images[*image_instance_index]; - - // Update the template this instance references, which may refresh the GPU - // cache with any shared template data. - image_data.update( - common_data, - image_instance, - pic_context.surface_index, - prim_spatial_node_index, - frame_state, - frame_context, - &mut prim_instance.vis, - ); - - // common_data.opacity.is_opaque is computed in the above update call. - is_opaque = common_data.opacity.is_opaque; - - write_segment( - image_instance.segment_instance_index, - frame_state, - &mut scratch.segments, - &mut scratch.segment_instances, - |request| { - image_data.write_prim_gpu_blocks(request); - }, - ); - } - PrimitiveInstanceKind::LinearGradient { data_handle, ref mut visible_tiles_range, .. } => { - profile_scope!("LinearGradient"); - let prim_data = &mut data_stores.linear_grad[*data_handle]; - - // Update the template this instane references, which may refresh the GPU - // cache with any shared template data. - prim_data.update(frame_state, pic_context.surface_index); - - if prim_data.stretch_size.width >= prim_data.common.prim_rect.size.width && - prim_data.stretch_size.height >= prim_data.common.prim_rect.size.height { - - prim_data.common.may_need_repetition = false; - } - - if prim_data.tile_spacing != LayoutSize::zero() { - // We are performing the decomposition on the CPU here, no need to - // have it in the shader. - prim_data.common.may_need_repetition = false; - - *visible_tiles_range = decompose_repeated_gradient( - &prim_instance.vis, - &prim_data.common.prim_rect, - prim_spatial_node_index, - &prim_data.stretch_size, - &prim_data.tile_spacing, - frame_state, - &mut scratch.gradient_tiles, - &frame_context.spatial_tree, - Some(&mut |_, mut request| { - request.push([ - prim_data.start_point.x, - prim_data.start_point.y, - prim_data.end_point.x, - prim_data.end_point.y, - ]); - request.push([ - pack_as_float(prim_data.extend_mode as u32), - prim_data.stretch_size.width, - prim_data.stretch_size.height, - 0.0, - ]); - }), - ); - - if visible_tiles_range.is_empty() { - prim_instance.clear_visibility(); - } - } - - // TODO(gw): Consider whether it's worth doing segment building - // for gradient primitives. - } - PrimitiveInstanceKind::CachedLinearGradient { data_handle, ref mut visible_tiles_range, .. } => { - profile_scope!("CachedLinearGradient"); - let prim_data = &mut data_stores.linear_grad[*data_handle]; - prim_data.common.may_need_repetition = prim_data.stretch_size.width < prim_data.common.prim_rect.size.width - || prim_data.stretch_size.height < prim_data.common.prim_rect.size.height; - - // Update the template this instance references, which may refresh the GPU - // cache with any shared template data. - prim_data.update(frame_state, pic_context.surface_index); - - if prim_data.tile_spacing != LayoutSize::zero() { - prim_data.common.may_need_repetition = false; - - *visible_tiles_range = decompose_repeated_gradient( - &prim_instance.vis, - &prim_data.common.prim_rect, - prim_spatial_node_index, - &prim_data.stretch_size, - &prim_data.tile_spacing, - frame_state, - &mut scratch.gradient_tiles, - &frame_context.spatial_tree, - None, - ); - - if visible_tiles_range.is_empty() { - prim_instance.clear_visibility(); - } - } - } - PrimitiveInstanceKind::RadialGradient { data_handle, ref mut visible_tiles_range, .. } => { - profile_scope!("RadialGradient"); - let prim_data = &mut data_stores.radial_grad[*data_handle]; - - prim_data.common.may_need_repetition = prim_data.stretch_size.width < prim_data.common.prim_rect.size.width - || prim_data.stretch_size.height < prim_data.common.prim_rect.size.height; - - // Update the template this instane references, which may refresh the GPU - // cache with any shared template data. - prim_data.update(frame_state, pic_context.surface_index); - - if prim_data.tile_spacing != LayoutSize::zero() { - prim_data.common.may_need_repetition = false; - - *visible_tiles_range = decompose_repeated_gradient( - &prim_instance.vis, - &prim_data.common.prim_rect, - prim_spatial_node_index, - &prim_data.stretch_size, - &prim_data.tile_spacing, - frame_state, - &mut scratch.gradient_tiles, - &frame_context.spatial_tree, - None, - ); - - if visible_tiles_range.is_empty() { - prim_instance.clear_visibility(); - } - } - - // TODO(gw): Consider whether it's worth doing segment building - // for gradient primitives. - } - PrimitiveInstanceKind::ConicGradient { data_handle, ref mut visible_tiles_range, .. } => { - profile_scope!("ConicGradient"); - let prim_data = &mut data_stores.conic_grad[*data_handle]; - - prim_data.common.may_need_repetition = prim_data.stretch_size.width < prim_data.common.prim_rect.size.width - || prim_data.stretch_size.height < prim_data.common.prim_rect.size.height; - - // Update the template this instane references, which may refresh the GPU - // cache with any shared template data. - prim_data.update(frame_state, pic_context.surface_index); - - if prim_data.tile_spacing != LayoutSize::zero() { - prim_data.common.may_need_repetition = false; - - *visible_tiles_range = decompose_repeated_gradient( - &prim_instance.vis, - &prim_data.common.prim_rect, - prim_spatial_node_index, - &prim_data.stretch_size, - &prim_data.tile_spacing, - frame_state, - &mut scratch.gradient_tiles, - &frame_context.spatial_tree, - None, - ); - - if visible_tiles_range.is_empty() { - prim_instance.clear_visibility(); - } - } - - // TODO(gw): Consider whether it's worth doing segment building - // for gradient primitives. - } - PrimitiveInstanceKind::Picture { pic_index, segment_instance_index, .. } => { - profile_scope!("Picture"); - let pic = &mut store.pictures[pic_index.0]; - - if pic.prepare_for_render( - frame_context, - frame_state, - data_stores, - ) { - if let Some(ref mut splitter) = pic_state.plane_splitter { - PicturePrimitive::add_split_plane( - splitter, - frame_context.spatial_tree, - prim_spatial_node_index, - pic.precise_local_rect, - &prim_instance.vis.combined_local_clip_rect, - frame_state.current_dirty_region().combined, - plane_split_anchor, - ); - } - - // If this picture uses segments, ensure the GPU cache is - // up to date with segment local rects. - // TODO(gw): This entire match statement above can now be - // refactored into prepare_interned_prim_for_render. - if pic.can_use_segments() { - write_segment( - *segment_instance_index, - frame_state, - &mut scratch.segments, - &mut scratch.segment_instances, - |request| { - request.push(PremultipliedColorF::WHITE); - request.push(PremultipliedColorF::WHITE); - request.push([ - -1.0, // -ve means use prim rect for stretch size - 0.0, - 0.0, - 0.0, - ]); - } - ); - } - } else { - prim_instance.clear_visibility(); - } - } - PrimitiveInstanceKind::Backdrop { data_handle } => { - profile_scope!("Backdrop"); - let backdrop_pic_index = data_stores.backdrop[*data_handle].kind.pic_index; - - // Setup a dependency on the backdrop picture to ensure it is rendered prior to rendering this primitive. - let backdrop_surface_index = store.pictures[backdrop_pic_index.0].raster_config.as_ref().unwrap().surface_index; - if let Some(ref backdrop_tasks) = frame_state.surfaces[backdrop_surface_index.0].render_tasks { - // This is untidy / code duplication but matches existing behavior and will be - // removed in follow up patches to this bug to rework how backdrop-filter works. - let backdrop_task_id = match backdrop_tasks { - SurfaceRenderTasks::Tiled(..) => unreachable!(), - SurfaceRenderTasks::Simple(id) => *id, - SurfaceRenderTasks::Chained { port_task_id, .. } => *port_task_id, - }; - - frame_state.add_child_render_task( - pic_context.surface_index, - backdrop_task_id, - ); - } else { - if prim_instance.is_chased() { - println!("\tBackdrop primitive culled because backdrop task was not assigned render tasks"); - } - prim_instance.clear_visibility(); - } - } - }; - - // If the primitive is opaque, see if it can contribut to it's picture surface's opaque rect. - - is_opaque = is_opaque && { - let clip = prim_instance.vis.clip_task_index; - clip == ClipTaskIndex::INVALID - }; - - is_opaque = is_opaque && !frame_context.spatial_tree.is_relative_transform_complex( - prim_spatial_node_index, - pic_context.raster_spatial_node_index, - ); - - if is_opaque { - let prim_local_rect = data_stores.get_local_prim_rect( - prim_instance, - store, - ); - cluster.opaque_rect = crate::util::conservative_union_rect(&cluster.opaque_rect, &prim_local_rect); - } -} - - -fn write_segment<F>( - segment_instance_index: SegmentInstanceIndex, - frame_state: &mut FrameBuildingState, - segments: &mut SegmentStorage, - segment_instances: &mut SegmentInstanceStorage, - f: F, -) where F: Fn(&mut GpuDataRequest) { - debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID); - if segment_instance_index != SegmentInstanceIndex::UNUSED { - let segment_instance = &mut segment_instances[segment_instance_index]; - - if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) { - let segments = &segments[segment_instance.segments_range]; - - f(&mut request); - - for segment in segments { - request.write_segment( - segment.local_rect, - [0.0; 4], - ); - } - } - } -} - -fn decompose_repeated_gradient( - prim_vis: &PrimitiveVisibility, - prim_local_rect: &LayoutRect, - prim_spatial_node_index: SpatialNodeIndex, - stretch_size: &LayoutSize, - tile_spacing: &LayoutSize, - frame_state: &mut FrameBuildingState, - gradient_tiles: &mut GradientTileStorage, - spatial_tree: &SpatialTree, - mut callback: Option<&mut dyn FnMut(&LayoutRect, GpuDataRequest)>, -) -> GradientTileRange { - let mut visible_tiles = Vec::new(); - - // Tighten the clip rect because decomposing the repeated image can - // produce primitives that are partially covering the original image - // rect and we want to clip these extra parts out. - let tight_clip_rect = prim_vis - .combined_local_clip_rect - .intersection(prim_local_rect).unwrap(); - - let visible_rect = compute_conservative_visible_rect( - &prim_vis.clip_chain, - frame_state.current_dirty_region().combined, - prim_spatial_node_index, - spatial_tree, - ); - let stride = *stretch_size + *tile_spacing; - - let repetitions = image_tiling::repetitions(prim_local_rect, &visible_rect, stride); - for Repetition { origin, .. } in repetitions { - let mut handle = GpuCacheHandle::new(); - let rect = LayoutRect { - origin, - size: *stretch_size, - }; - - if let Some(callback) = &mut callback { - if let Some(request) = frame_state.gpu_cache.request(&mut handle) { - callback(&rect, request); - } - } - - visible_tiles.push(VisibleGradientTile { - local_rect: rect, - local_clip_rect: tight_clip_rect, - handle - }); - } - - // At this point if we don't have tiles to show it means we could probably - // have done a better a job at culling during an earlier stage. - // Clearing the screen rect has the effect of "culling out" the primitive - // from the point of view of the batch builder, and ensures we don't hit - // assertions later on because we didn't request any image. - if visible_tiles.is_empty() { - GradientTileRange::empty() - } else { - gradient_tiles.extend(visible_tiles) - } -} - - -fn update_clip_task_for_brush( - instance: &PrimitiveInstance, - prim_origin: &LayoutPoint, - prim_spatial_node_index: SpatialNodeIndex, - root_spatial_node_index: SpatialNodeIndex, - pic_context: &PictureContext, - pic_state: &mut PictureState, - frame_context: &FrameBuildingContext, - frame_state: &mut FrameBuildingState, - prim_store: &PrimitiveStore, - data_stores: &mut DataStores, - segments_store: &mut SegmentStorage, - segment_instances_store: &mut SegmentInstanceStorage, - clip_mask_instances: &mut Vec<ClipMaskKind>, - unclipped: &DeviceRect, - device_pixel_scale: DevicePixelScale, -) -> Option<ClipTaskIndex> { - let segments = match instance.kind { - PrimitiveInstanceKind::TextRun { .. } | - PrimitiveInstanceKind::Clear { .. } | - PrimitiveInstanceKind::LineDecoration { .. } | - PrimitiveInstanceKind::Backdrop { .. } => { - return None; - } - PrimitiveInstanceKind::Image { image_instance_index, .. } => { - let segment_instance_index = prim_store - .images[image_instance_index] - .segment_instance_index; - - if segment_instance_index == SegmentInstanceIndex::UNUSED { - return None; - } - - let segment_instance = &segment_instances_store[segment_instance_index]; - - &segments_store[segment_instance.segments_range] - } - PrimitiveInstanceKind::Picture { segment_instance_index, .. } => { - // Pictures may not support segment rendering at all (INVALID) - // or support segment rendering but choose not to due to size - // or some other factor (UNUSED). - if segment_instance_index == SegmentInstanceIndex::UNUSED || - segment_instance_index == SegmentInstanceIndex::INVALID { - return None; - } - - let segment_instance = &segment_instances_store[segment_instance_index]; - &segments_store[segment_instance.segments_range] - } - PrimitiveInstanceKind::YuvImage { segment_instance_index, .. } | - PrimitiveInstanceKind::Rectangle { segment_instance_index, .. } => { - debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID); - - if segment_instance_index == SegmentInstanceIndex::UNUSED { - return None; - } - - let segment_instance = &segment_instances_store[segment_instance_index]; - - &segments_store[segment_instance.segments_range] - } - PrimitiveInstanceKind::ImageBorder { data_handle, .. } => { - let border_data = &data_stores.image_border[data_handle].kind; - - // TODO: This is quite messy - once we remove legacy primitives we - // can change this to be a tuple match on (instance, template) - border_data.brush_segments.as_slice() - } - PrimitiveInstanceKind::NormalBorder { data_handle, .. } => { - let border_data = &data_stores.normal_border[data_handle].kind; - - // TODO: This is quite messy - once we remove legacy primitives we - // can change this to be a tuple match on (instance, template) - border_data.brush_segments.as_slice() - } - PrimitiveInstanceKind::LinearGradient { data_handle, .. } - | PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => { - let prim_data = &data_stores.linear_grad[data_handle]; - - // TODO: This is quite messy - once we remove legacy primitives we - // can change this to be a tuple match on (instance, template) - if prim_data.brush_segments.is_empty() { - return None; - } - - prim_data.brush_segments.as_slice() - } - PrimitiveInstanceKind::RadialGradient { data_handle, .. } => { - let prim_data = &data_stores.radial_grad[data_handle]; - - // TODO: This is quite messy - once we remove legacy primitives we - // can change this to be a tuple match on (instance, template) - if prim_data.brush_segments.is_empty() { - return None; - } - - prim_data.brush_segments.as_slice() - } - PrimitiveInstanceKind::ConicGradient { data_handle, .. } => { - let prim_data = &data_stores.conic_grad[data_handle]; - - // TODO: This is quite messy - once we remove legacy primitives we - // can change this to be a tuple match on (instance, template) - if prim_data.brush_segments.is_empty() { - return None; - } - - prim_data.brush_segments.as_slice() - } - }; - - // If there are no segments, early out to avoid setting a valid - // clip task instance location below. - if segments.is_empty() { - return None; - } - - // Set where in the clip mask instances array the clip mask info - // can be found for this primitive. Each segment will push the - // clip mask information for itself in update_clip_task below. - let clip_task_index = ClipTaskIndex(clip_mask_instances.len() as _); - - // If we only built 1 segment, there is no point in re-running - // the clip chain builder. Instead, just use the clip chain - // instance that was built for the main primitive. This is a - // significant optimization for the common case. - if segments.len() == 1 { - let clip_mask_kind = update_brush_segment_clip_task( - &segments[0], - Some(&instance.vis.clip_chain), - frame_state.current_dirty_region().combined, - root_spatial_node_index, - pic_context.surface_index, - pic_state, - frame_context, - frame_state, - &mut data_stores.clip, - unclipped, - device_pixel_scale, - ); - clip_mask_instances.push(clip_mask_kind); - } else { - let dirty_world_rect = frame_state.current_dirty_region().combined; - - for segment in segments { - // Build a clip chain for the smaller segment rect. This will - // often manage to eliminate most/all clips, and sometimes - // clip the segment completely. - frame_state.clip_store.set_active_clips_from_clip_chain( - &instance.vis.clip_chain, - prim_spatial_node_index, - &frame_context.spatial_tree, - ); - - let segment_clip_chain = frame_state - .clip_store - .build_clip_chain_instance( - segment.local_rect.translate(prim_origin.to_vector()), - &pic_state.map_local_to_pic, - &pic_state.map_pic_to_world, - &frame_context.spatial_tree, - frame_state.gpu_cache, - frame_state.resource_cache, - device_pixel_scale, - &dirty_world_rect, - &mut data_stores.clip, - false, - instance.is_chased(), - ); - - let clip_mask_kind = update_brush_segment_clip_task( - &segment, - segment_clip_chain.as_ref(), - frame_state.current_dirty_region().combined, - root_spatial_node_index, - pic_context.surface_index, - pic_state, - frame_context, - frame_state, - &mut data_stores.clip, - unclipped, - device_pixel_scale, - ); - clip_mask_instances.push(clip_mask_kind); - } - } - - Some(clip_task_index) -} - -pub fn update_clip_task( - instance: &mut PrimitiveInstance, - prim_origin: &LayoutPoint, - prim_spatial_node_index: SpatialNodeIndex, - root_spatial_node_index: SpatialNodeIndex, - pic_context: &PictureContext, - pic_state: &mut PictureState, - frame_context: &FrameBuildingContext, - frame_state: &mut FrameBuildingState, - prim_store: &mut PrimitiveStore, - data_stores: &mut DataStores, - scratch: &mut PrimitiveScratchBuffer, -) -> bool { - let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale; - - if instance.is_chased() { - println!("\tupdating clip task with pic rect {:?}", instance.vis.clip_chain.pic_clip_rect); - } - - // Get the device space rect for the primitive if it was unclipped. - let unclipped = match get_unclipped_device_rect( - instance.vis.clip_chain.pic_clip_rect, - &pic_state.map_pic_to_raster, - device_pixel_scale, - ) { - Some(rect) => rect, - None => return false, - }; - - build_segments_if_needed( - instance, - frame_state, - prim_store, - data_stores, - &mut scratch.segments, - &mut scratch.segment_instances, - ); - - // First try to render this primitive's mask using optimized brush rendering. - instance.vis.clip_task_index = if let Some(clip_task_index) = update_clip_task_for_brush( - instance, - prim_origin, - prim_spatial_node_index, - root_spatial_node_index, - pic_context, - pic_state, - frame_context, - frame_state, - prim_store, - data_stores, - &mut scratch.segments, - &mut scratch.segment_instances, - &mut scratch.clip_mask_instances, - &unclipped, - device_pixel_scale, - ) { - if instance.is_chased() { - println!("\tsegment tasks have been created for clipping: {:?}", clip_task_index); - } - clip_task_index - } else if instance.vis.clip_chain.needs_mask { - // Get a minimal device space rect, clipped to the screen that we - // need to allocate for the clip mask, as well as interpolated - // snap offsets. - let unadjusted_device_rect = match get_clipped_device_rect( - &unclipped, - &pic_state.map_raster_to_world, - frame_state.current_dirty_region().combined, - device_pixel_scale, - ) { - Some(device_rect) => device_rect, - None => return false, - }; - - let (device_rect, device_pixel_scale) = adjust_mask_scale_for_max_size( - unadjusted_device_rect, - device_pixel_scale, - ); - let clip_task_id = RenderTaskKind::new_mask( - device_rect, - instance.vis.clip_chain.clips_range, - root_spatial_node_index, - frame_state.clip_store, - frame_state.gpu_cache, - frame_state.resource_cache, - frame_state.rg_builder, - &mut data_stores.clip, - device_pixel_scale, - frame_context.fb_config, - frame_state.surfaces, - ); - if instance.is_chased() { - println!("\tcreated task {:?} with device rect {:?}", - clip_task_id, device_rect); - } - // Set the global clip mask instance for this primitive. - let clip_task_index = ClipTaskIndex(scratch.clip_mask_instances.len() as _); - scratch.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id)); - instance.vis.clip_task_index = clip_task_index; - frame_state.add_child_render_task( - pic_context.surface_index, - clip_task_id, - ); - clip_task_index - } else { - if instance.is_chased() { - println!("\tno mask is needed"); - } - ClipTaskIndex::INVALID - }; - - true -} - -/// Write out to the clip mask instances array the correct clip mask -/// config for this segment. -pub fn update_brush_segment_clip_task( - segment: &BrushSegment, - clip_chain: Option<&ClipChainInstance>, - world_clip_rect: WorldRect, - root_spatial_node_index: SpatialNodeIndex, - surface_index: SurfaceIndex, - pic_state: &mut PictureState, - frame_context: &FrameBuildingContext, - frame_state: &mut FrameBuildingState, - clip_data_store: &mut ClipDataStore, - unclipped: &DeviceRect, - device_pixel_scale: DevicePixelScale, -) -> ClipMaskKind { - let clip_chain = match clip_chain { - Some(chain) => chain, - None => return ClipMaskKind::Clipped, - }; - if !clip_chain.needs_mask || - (!segment.may_need_clip_mask && !clip_chain.has_non_local_clips) { - return ClipMaskKind::None; - } - - let segment_world_rect = match pic_state.map_pic_to_world.map(&clip_chain.pic_clip_rect) { - Some(rect) => rect, - None => return ClipMaskKind::Clipped, - }; - - let segment_world_rect = match segment_world_rect.intersection(&world_clip_rect) { - Some(rect) => rect, - None => return ClipMaskKind::Clipped, - }; - - // Get a minimal device space rect, clipped to the screen that we - // need to allocate for the clip mask, as well as interpolated - // snap offsets. - let device_rect = match get_clipped_device_rect( - unclipped, - &pic_state.map_raster_to_world, - segment_world_rect, - device_pixel_scale, - ) { - Some(info) => info, - None => { - return ClipMaskKind::Clipped; - } - }; - - let (device_rect, device_pixel_scale) = adjust_mask_scale_for_max_size(device_rect, device_pixel_scale); - - let clip_task_id = RenderTaskKind::new_mask( - device_rect, - clip_chain.clips_range, - root_spatial_node_index, - frame_state.clip_store, - frame_state.gpu_cache, - frame_state.resource_cache, - frame_state.rg_builder, - clip_data_store, - device_pixel_scale, - frame_context.fb_config, - frame_state.surfaces, - ); - - frame_state.add_child_render_task( - surface_index, - clip_task_id, - ); - ClipMaskKind::Mask(clip_task_id) -} - - -fn write_brush_segment_description( - prim_local_rect: LayoutRect, - prim_local_clip_rect: LayoutRect, - clip_chain: &ClipChainInstance, - segment_builder: &mut SegmentBuilder, - clip_store: &ClipStore, - data_stores: &DataStores, -) -> bool { - // If the brush is small, we want to skip building segments - // and just draw it as a single primitive with clip mask. - if prim_local_rect.size.area() < MIN_BRUSH_SPLIT_AREA { - return false; - } - - segment_builder.initialize( - prim_local_rect, - None, - prim_local_clip_rect - ); - - // Segment the primitive on all the local-space clip sources that we can. - for i in 0 .. clip_chain.clips_range.count { - let clip_instance = clip_store - .get_instance_from_range(&clip_chain.clips_range, i); - let clip_node = &data_stores.clip[clip_instance.handle]; - - // If this clip item is positioned by another positioning node, its relative position - // could change during scrolling. This means that we would need to resegment. Instead - // of doing that, only segment with clips that have the same positioning node. - // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only - // when necessary while scrolling. - if !clip_instance.flags.contains(ClipNodeFlags::SAME_SPATIAL_NODE) { - continue; - } - - let (local_clip_rect, radius, mode) = match clip_node.item.kind { - ClipItemKind::RoundedRectangle { rect, radius, mode } => { - (rect, Some(radius), mode) - } - ClipItemKind::Rectangle { rect, mode } => { - (rect, None, mode) - } - ClipItemKind::BoxShadow { ref source } => { - // For inset box shadows, we can clip out any - // pixels that are inside the shadow region - // and are beyond the inner rect, as they can't - // be affected by the blur radius. - let inner_clip_mode = match source.clip_mode { - BoxShadowClipMode::Outset => None, - BoxShadowClipMode::Inset => Some(ClipMode::ClipOut), - }; - - // Push a region into the segment builder where the - // box-shadow can have an effect on the result. This - // ensures clip-mask tasks get allocated for these - // pixel regions, even if no other clips affect them. - segment_builder.push_mask_region( - source.prim_shadow_rect, - source.prim_shadow_rect.inflate( - -0.5 * source.original_alloc_size.width, - -0.5 * source.original_alloc_size.height, - ), - inner_clip_mode, - ); - - continue; - } - ClipItemKind::Image { .. } => { - // If we encounter an image mask, bail out from segment building. - // It's not possible to know which parts of the primitive are affected - // by the mask (without inspecting the pixels). We could do something - // better here in the future if it ever shows up as a performance issue - // (for instance, at least segment based on the bounding rect of the - // image mask if it's non-repeating). - return false; - } - }; - - segment_builder.push_clip_rect(local_clip_rect, radius, mode); - } - - true -} - -fn build_segments_if_needed( - instance: &mut PrimitiveInstance, - frame_state: &mut FrameBuildingState, - prim_store: &mut PrimitiveStore, - data_stores: &DataStores, - segments_store: &mut SegmentStorage, - segment_instances_store: &mut SegmentInstanceStorage, -) { - let prim_clip_chain = &instance.vis.clip_chain; - - // Usually, the primitive rect can be found from information - // in the instance and primitive template. - let prim_local_rect = data_stores.get_local_prim_rect( - instance, - prim_store, - ); - - let segment_instance_index = match instance.kind { - PrimitiveInstanceKind::Rectangle { ref mut segment_instance_index, .. } | - PrimitiveInstanceKind::YuvImage { ref mut segment_instance_index, .. } => { - segment_instance_index - } - PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { - let image_data = &data_stores.image[data_handle].kind; - let image_instance = &mut prim_store.images[image_instance_index]; - //Note: tiled images don't support automatic segmentation, - // they strictly produce one segment per visible tile instead. - if frame_state - .resource_cache - .get_image_properties(image_data.key) - .and_then(|properties| properties.tiling) - .is_some() - { - image_instance.segment_instance_index = SegmentInstanceIndex::UNUSED; - return; - } - &mut image_instance.segment_instance_index - } - PrimitiveInstanceKind::Picture { ref mut segment_instance_index, pic_index, .. } => { - let pic = &mut prim_store.pictures[pic_index.0]; - - // If this picture supports segment rendering - if pic.can_use_segments() { - // If the segments have been invalidated, ensure the current - // index of segments is invalid. This ensures that the segment - // building logic below will be run. - if !pic.segments_are_valid { - *segment_instance_index = SegmentInstanceIndex::INVALID; - pic.segments_are_valid = true; - } - - segment_instance_index - } else { - return; - } - } - PrimitiveInstanceKind::TextRun { .. } | - PrimitiveInstanceKind::NormalBorder { .. } | - PrimitiveInstanceKind::ImageBorder { .. } | - PrimitiveInstanceKind::Clear { .. } | - PrimitiveInstanceKind::LinearGradient { .. } | - PrimitiveInstanceKind::CachedLinearGradient { .. } | - PrimitiveInstanceKind::RadialGradient { .. } | - PrimitiveInstanceKind::ConicGradient { .. } | - PrimitiveInstanceKind::LineDecoration { .. } | - PrimitiveInstanceKind::Backdrop { .. } => { - // These primitives don't support / need segments. - return; - } - }; - - if *segment_instance_index == SegmentInstanceIndex::INVALID { - let mut segments: SmallVec<[BrushSegment; 8]> = SmallVec::new(); - - if write_brush_segment_description( - prim_local_rect, - instance.clip_set.local_clip_rect, - prim_clip_chain, - &mut frame_state.segment_builder, - frame_state.clip_store, - data_stores, - ) { - frame_state.segment_builder.build(|segment| { - segments.push( - BrushSegment::new( - segment.rect.translate(-prim_local_rect.origin.to_vector()), - segment.has_mask, - segment.edge_flags, - [0.0; 4], - BrushFlags::PERSPECTIVE_INTERPOLATION, - ), - ); - }); - } - - // If only a single segment is produced, there is no benefit to writing - // a segment instance array. Instead, just use the main primitive rect - // written into the GPU cache. - // TODO(gw): This is (sortof) a bandaid - due to a limitation in the current - // brush encoding, we can only support a total of up to 2^16 segments. - // This should be (more than) enough for any real world case, so for - // now we can handle this by skipping cases where we were generating - // segments where there is no benefit. The long term / robust fix - // for this is to move the segment building to be done as a more - // limited nine-patch system during scene building, removing arbitrary - // segmentation during frame-building (see bug #1617491). - if segments.len() <= 1 { - *segment_instance_index = SegmentInstanceIndex::UNUSED; - } else { - let segments_range = segments_store.extend(segments); - - let instance = SegmentedInstance { - segments_range, - gpu_cache_handle: GpuCacheHandle::new(), - }; - - *segment_instance_index = segment_instances_store.push(instance); - }; - } -} - -/// Retrieve the exact unsnapped device space rectangle for a primitive. -fn get_unclipped_device_rect( - prim_rect: PictureRect, - map_to_raster: &SpaceMapper<PicturePixel, RasterPixel>, - device_pixel_scale: DevicePixelScale, -) -> Option<DeviceRect> { - let raster_rect = map_to_raster.map(&prim_rect)?; - let world_rect = raster_rect * Scale::new(1.0); - Some(world_rect * device_pixel_scale) -} - -/// Given an unclipped device rect, try to find a minimal device space -/// rect to allocate a clip mask for, by clipping to the screen. This -/// function is very similar to picture::get_raster_rects. It is far from -/// ideal, and should be refactored as part of the support for setting -/// scale per-raster-root. -fn get_clipped_device_rect( - unclipped: &DeviceRect, - map_to_world: &SpaceMapper<RasterPixel, WorldPixel>, - world_clip_rect: WorldRect, - device_pixel_scale: DevicePixelScale, -) -> Option<DeviceRect> { - let unclipped_raster_rect = { - let world_rect = *unclipped * Scale::new(1.0); - let raster_rect = world_rect * device_pixel_scale.inverse(); - - raster_rect.cast_unit() - }; - - let unclipped_world_rect = map_to_world.map(&unclipped_raster_rect)?; - - let clipped_world_rect = unclipped_world_rect.intersection(&world_clip_rect)?; - - let clipped_raster_rect = map_to_world.unmap(&clipped_world_rect)?; - - let clipped_raster_rect = clipped_raster_rect.intersection(&unclipped_raster_rect)?; - - // Ensure that we won't try to allocate a zero-sized clip render task. - if clipped_raster_rect.is_empty() { - return None; - } - - let clipped = raster_rect_to_device_pixels( - clipped_raster_rect, - device_pixel_scale, - ); - - Some(clipped) -} - -// Ensures that the size of mask render tasks are within MAX_MASK_SIZE. -fn adjust_mask_scale_for_max_size(device_rect: DeviceRect, device_pixel_scale: DevicePixelScale) -> (DeviceRect, DevicePixelScale) { - if device_rect.width() > MAX_MASK_SIZE || device_rect.height() > MAX_MASK_SIZE { - // round_out will grow by 1 integer pixel if origin is on a - // fractional position, so keep that margin for error with -1: - let scale = (MAX_MASK_SIZE - 1.0) / - f32::max(device_rect.width(), device_rect.height()); - let new_device_pixel_scale = device_pixel_scale * Scale::new(scale); - let new_device_rect = (device_rect.to_f32() * Scale::new(scale)) - .round_out(); - (new_device_rect, new_device_pixel_scale) - } else { - (device_rect, device_pixel_scale) - } -} - diff --git a/third_party/webrender/webrender/src/prim_store/backdrop.rs b/third_party/webrender/webrender/src/prim_store/backdrop.rs index c45bf78eef4..ea033574fb2 100644 --- a/third_party/webrender/webrender/src/prim_store/backdrop.rs +++ b/third_party/webrender/webrender/src/prim_store/backdrop.rs @@ -74,7 +74,6 @@ impl Internable for Backdrop { type Key = BackdropKey; type StoreData = BackdropTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_BACKDROPS; } impl InternablePrimitive for Backdrop { diff --git a/third_party/webrender/webrender/src/prim_store/borders.rs b/third_party/webrender/webrender/src/prim_store/borders.rs index 4be7d72b46d..084350c3357 100644 --- a/third_party/webrender/webrender/src/prim_store/borders.rs +++ b/third_party/webrender/webrender/src/prim_store/borders.rs @@ -2,13 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{NormalBorder, PremultipliedColorF, Shadow, RasterSpace}; +use api::{NormalBorder, PremultipliedColorF, Shadow}; use api::units::*; use crate::border::create_border_segments; use crate::border::NormalBorderAu; use crate::scene_building::{CreateShadow, IsVisible}; use crate::frame_builder::{FrameBuildingState}; -use crate::gpu_cache::GpuDataRequest; +use crate::gpu_cache::{GpuCache, GpuDataRequest}; use crate::intern; use crate::internal_types::LayoutPrimitiveInfo; use crate::prim_store::{ @@ -17,12 +17,8 @@ use crate::prim_store::{ PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveStore, InternablePrimitive, }; -use crate::resource_cache::ImageRequest; -use crate::render_task::RenderTask; -use crate::render_task_graph::RenderTaskId; -use crate::render_backend::FrameId; - -use super::storage; +use crate::resource_cache::{ImageRequest, ResourceCache}; +use crate::storage; #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -149,7 +145,6 @@ impl intern::Internable for NormalBorderPrim { type Key = NormalBorderKey; type StoreData = NormalBorderTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_NORMAL_BORDERS; } impl InternablePrimitive for NormalBorderPrim { @@ -171,18 +166,13 @@ impl InternablePrimitive for NormalBorderPrim { ) -> PrimitiveInstanceKind { PrimitiveInstanceKind::NormalBorder { data_handle, - render_task_ids: storage::Range::empty(), + cache_handles: storage::Range::empty(), } } } impl CreateShadow for NormalBorderPrim { - fn create_shadow( - &self, - shadow: &Shadow, - _: bool, - _: RasterSpace, - ) -> Self { + fn create_shadow(&self, shadow: &Shadow) -> Self { let border = self.border.with_color(shadow.color.into()); NormalBorderPrim { border, @@ -232,9 +222,6 @@ pub struct ImageBorderData { #[ignore_malloc_size_of = "Arc"] pub request: ImageRequest, pub brush_segments: Vec<BrushSegment>, - pub src_color: Option<RenderTaskId>, - pub frame_id: FrameId, - pub is_opaque: bool, } impl ImageBorderData { @@ -252,31 +239,28 @@ impl ImageBorderData { self.write_segment_gpu_blocks(request); } - let frame_id = frame_state.rg_builder.frame_id(); - if self.frame_id != frame_id { - self.frame_id = frame_id; - - let size = frame_state.resource_cache.request_image( - self.request, - frame_state.gpu_cache, - ); - - let task_id = frame_state.rg_builder.add().init( - RenderTask::new_image(size, self.request) - ); - - self.src_color = Some(task_id); - - let image_properties = frame_state - .resource_cache - .get_image_properties(self.request.key); + let image_properties = frame_state + .resource_cache + .get_image_properties(self.request.key); - self.is_opaque = image_properties - .map(|properties| properties.descriptor.is_opaque()) - .unwrap_or(true); + common.opacity = if let Some(image_properties) = image_properties { + PrimitiveOpacity { + is_opaque: image_properties.descriptor.is_opaque(), + } + } else { + PrimitiveOpacity::opaque() } + } - common.opacity = PrimitiveOpacity { is_opaque: self.is_opaque }; + pub fn request_resources( + &mut self, + resource_cache: &mut ResourceCache, + gpu_cache: &mut GpuCache, + ) { + resource_cache.request_image( + self.request, + gpu_cache, + ); } fn write_prim_gpu_blocks( @@ -323,9 +307,6 @@ impl From<ImageBorderKey> for ImageBorderTemplate { kind: ImageBorderData { request: key.kind.request, brush_segments, - src_color: None, - frame_id: FrameId::INVALID, - is_opaque: false, } } } @@ -337,7 +318,6 @@ impl intern::Internable for ImageBorder { type Key = ImageBorderKey; type StoreData = ImageBorderTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGE_BORDERS; } impl InternablePrimitive for ImageBorder { @@ -383,6 +363,6 @@ fn test_struct_sizes() { assert_eq!(mem::size_of::<NormalBorderTemplate>(), 216, "NormalBorderTemplate size changed"); assert_eq!(mem::size_of::<NormalBorderKey>(), 104, "NormalBorderKey size changed"); assert_eq!(mem::size_of::<ImageBorder>(), 84, "ImageBorder size changed"); - assert_eq!(mem::size_of::<ImageBorderTemplate>(), 96, "ImageBorderTemplate size changed"); + assert_eq!(mem::size_of::<ImageBorderTemplate>(), 80, "ImageBorderTemplate size changed"); assert_eq!(mem::size_of::<ImageBorderKey>(), 104, "ImageBorderKey size changed"); } diff --git a/third_party/webrender/webrender/src/prim_store/gradient.rs b/third_party/webrender/webrender/src/prim_store/gradient.rs new file mode 100644 index 00000000000..add65ad8cd8 --- /dev/null +++ b/third_party/webrender/webrender/src/prim_store/gradient.rs @@ -0,0 +1,1007 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use api::{ + ColorF, ColorU, ExtendMode, GradientStop, + PremultipliedColorF, LineOrientation, +}; +use api::units::{LayoutPoint, LayoutSize, LayoutVector2D}; +use crate::scene_building::IsVisible; +use euclid::approxeq::ApproxEq; +use crate::frame_builder::FrameBuildingState; +use crate::gpu_cache::{GpuCacheHandle, GpuDataRequest}; +use crate::intern::{Internable, InternDebug, Handle as InternHandle}; +use crate::internal_types::LayoutPrimitiveInfo; +use crate::prim_store::{BrushSegment, CachedGradientSegment, GradientTileRange, VectorKey}; +use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity}; +use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore}; +use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive}; +use std::{hash, ops::{Deref, DerefMut}}; +use crate::util::pack_as_float; +use crate::texture_cache::TEXTURE_REGION_DIMENSIONS; + +/// The maximum number of stops a gradient may have to use the fast path. +pub const GRADIENT_FP_STOPS: usize = 4; + +/// A hashable gradient stop that can be used in primitive keys. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)] +pub struct GradientStopKey { + pub offset: f32, + pub color: ColorU, +} + +impl GradientStopKey { + pub fn empty() -> Self { + GradientStopKey { + offset: 0.0, + color: ColorU::new(0, 0, 0, 0), + } + } +} + +impl Into<GradientStopKey> for GradientStop { + fn into(self) -> GradientStopKey { + GradientStopKey { + offset: self.offset, + color: self.color.into(), + } + } +} + +// Convert `stop_keys` into a vector of `GradientStop`s, which is a more +// convenient representation for the current gradient builder. Compute the +// minimum stop alpha along the way. +fn stops_and_min_alpha(stop_keys: &[GradientStopKey]) -> (Vec<GradientStop>, f32) { + let mut min_alpha: f32 = 1.0; + let stops = stop_keys.iter().map(|stop_key| { + let color: ColorF = stop_key.color.into(); + min_alpha = min_alpha.min(color.a); + + GradientStop { + offset: stop_key.offset, + color, + } + }).collect(); + + (stops, min_alpha) +} + +impl Eq for GradientStopKey {} + +impl hash::Hash for GradientStopKey { + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.offset.to_bits().hash(state); + self.color.hash(state); + } +} + +/// Identifying key for a linear gradient. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)] +pub struct LinearGradientKey { + pub common: PrimKeyCommonData, + pub extend_mode: ExtendMode, + pub start_point: PointKey, + pub end_point: PointKey, + pub stretch_size: SizeKey, + pub tile_spacing: SizeKey, + pub stops: Vec<GradientStopKey>, + pub reverse_stops: bool, + pub nine_patch: Option<Box<NinePatchDescriptor>>, +} + +impl LinearGradientKey { + pub fn new( + info: &LayoutPrimitiveInfo, + linear_grad: LinearGradient, + ) -> Self { + LinearGradientKey { + common: info.into(), + extend_mode: linear_grad.extend_mode, + start_point: linear_grad.start_point, + end_point: linear_grad.end_point, + stretch_size: linear_grad.stretch_size, + tile_spacing: linear_grad.tile_spacing, + stops: linear_grad.stops, + reverse_stops: linear_grad.reverse_stops, + nine_patch: linear_grad.nine_patch, + } + } +} + +impl InternDebug for LinearGradientKey {} + +#[derive(Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct GradientCacheKey { + pub orientation: LineOrientation, + pub start_stop_point: VectorKey, + pub stops: [GradientStopKey; GRADIENT_FP_STOPS], +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct LinearGradientTemplate { + pub common: PrimTemplateCommonData, + pub extend_mode: ExtendMode, + pub start_point: LayoutPoint, + pub end_point: LayoutPoint, + pub stretch_size: LayoutSize, + pub tile_spacing: LayoutSize, + pub stops_opacity: PrimitiveOpacity, + pub stops: Vec<GradientStop>, + pub brush_segments: Vec<BrushSegment>, + pub reverse_stops: bool, + pub stops_handle: GpuCacheHandle, + /// If true, this gradient can be drawn via the fast path + /// (cache gradient, and draw as image). + pub supports_caching: bool, +} + +impl Deref for LinearGradientTemplate { + type Target = PrimTemplateCommonData; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl DerefMut for LinearGradientTemplate { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.common + } +} + +impl From<LinearGradientKey> for LinearGradientTemplate { + fn from(item: LinearGradientKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(item.common); + + // Check if we can draw this gradient via a fast path by caching the + // gradient in a smaller task, and drawing as an image. + // TODO(gw): Aim to reduce the constraints on fast path gradients in future, + // although this catches the vast majority of gradients on real pages. + let mut supports_caching = + // Gradient must cover entire primitive + item.tile_spacing.w + item.stretch_size.w >= common.prim_rect.size.width && + item.tile_spacing.h + item.stretch_size.h >= common.prim_rect.size.height && + // Must be a vertical or horizontal gradient + (item.start_point.x.approx_eq(&item.end_point.x) || + item.start_point.y.approx_eq(&item.end_point.y)) && + // Fast path not supported on segmented (border-image) gradients. + item.nine_patch.is_none(); + + // if we support caching and the gradient uses repeat, we might potentially + // emit a lot of quads to cover the primitive. each quad will still cover + // the entire gradient along the other axis, so the effect is linear in + // display resolution, not quadratic (unlike say a tiny background image + // tiling the display). in addition, excessive minification may lead to + // texture trashing. so use the minification as a proxy heuristic for both + // cases. + // + // note that the actual number of quads may be further increased due to + // hard-stops and/or more than GRADIENT_FP_STOPS stops per gradient. + if supports_caching && item.extend_mode == ExtendMode::Repeat { + let single_repeat_size = + if item.start_point.x.approx_eq(&item.end_point.x) { + item.end_point.y - item.start_point.y + } else { + item.end_point.x - item.start_point.x + }; + let downscaling = single_repeat_size as f32 / TEXTURE_REGION_DIMENSIONS as f32; + if downscaling < 0.1 { + // if a single copy of the gradient is this small relative to its baked + // gradient cache, we have bad texture caching and/or too many quads. + supports_caching = false; + } + } + + let (stops, min_alpha) = stops_and_min_alpha(&item.stops); + + let mut brush_segments = Vec::new(); + + if let Some(ref nine_patch) = item.nine_patch { + brush_segments = nine_patch.create_segments(common.prim_rect.size); + } + + // Save opacity of the stops for use in + // selecting which pass this gradient + // should be drawn in. + let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha); + + LinearGradientTemplate { + common, + extend_mode: item.extend_mode, + start_point: item.start_point.into(), + end_point: item.end_point.into(), + stretch_size: item.stretch_size.into(), + tile_spacing: item.tile_spacing.into(), + stops_opacity, + stops, + brush_segments, + reverse_stops: item.reverse_stops, + stops_handle: GpuCacheHandle::new(), + supports_caching, + } + } +} + +impl LinearGradientTemplate { + /// Update the GPU cache for a given primitive template. This may be called multiple + /// times per frame, by each primitive reference that refers to this interned + /// template. The initial request call to the GPU cache ensures that work is only + /// done if the cache entry is invalid (due to first use or eviction). + pub fn update( + &mut self, + frame_state: &mut FrameBuildingState, + ) { + if let Some(mut request) = + frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { + // write_prim_gpu_blocks + request.push([ + self.start_point.x, + self.start_point.y, + self.end_point.x, + self.end_point.y, + ]); + request.push([ + pack_as_float(self.extend_mode as u32), + self.stretch_size.width, + self.stretch_size.height, + 0.0, + ]); + + // write_segment_gpu_blocks + for segment in &self.brush_segments { + // has to match VECS_PER_SEGMENT + request.write_segment( + segment.local_rect, + segment.extra_data, + ); + } + } + + if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { + GradientGpuBlockBuilder::build( + self.reverse_stops, + &mut request, + &self.stops, + ); + } + + self.opacity = { + // If the coverage of the gradient extends to or beyond + // the primitive rect, then the opacity can be determined + // by the colors of the stops. If we have tiling / spacing + // then we just assume the gradient is translucent for now. + // (In the future we could consider segmenting in some cases). + let stride = self.stretch_size + self.tile_spacing; + if stride.width >= self.common.prim_rect.size.width && + stride.height >= self.common.prim_rect.size.height { + self.stops_opacity + } else { + PrimitiveOpacity::translucent() + } + } + } +} + +pub type LinearGradientDataHandle = InternHandle<LinearGradient>; + +#[derive(Debug, MallocSizeOf)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct LinearGradient { + pub extend_mode: ExtendMode, + pub start_point: PointKey, + pub end_point: PointKey, + pub stretch_size: SizeKey, + pub tile_spacing: SizeKey, + pub stops: Vec<GradientStopKey>, + pub reverse_stops: bool, + pub nine_patch: Option<Box<NinePatchDescriptor>>, +} + +impl Internable for LinearGradient { + type Key = LinearGradientKey; + type StoreData = LinearGradientTemplate; + type InternData = (); +} + +impl InternablePrimitive for LinearGradient { + fn into_key( + self, + info: &LayoutPrimitiveInfo, + ) -> LinearGradientKey { + LinearGradientKey::new(info, self) + } + + fn make_instance_kind( + _key: LinearGradientKey, + data_handle: LinearGradientDataHandle, + prim_store: &mut PrimitiveStore, + _reference_frame_relative_offset: LayoutVector2D, + ) -> PrimitiveInstanceKind { + let gradient_index = prim_store.linear_gradients.push(LinearGradientPrimitive { + cache_segments: Vec::new(), + visible_tiles_range: GradientTileRange::empty(), + }); + + PrimitiveInstanceKind::LinearGradient { + data_handle, + gradient_index, + } + } +} + +impl IsVisible for LinearGradient { + fn is_visible(&self) -> bool { + true + } +} + +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +pub struct LinearGradientPrimitive { + pub cache_segments: Vec<CachedGradientSegment>, + pub visible_tiles_range: GradientTileRange, +} + +//////////////////////////////////////////////////////////////////////////////// + +/// Hashable radial gradient parameters, for use during prim interning. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, MallocSizeOf, PartialEq)] +pub struct RadialGradientParams { + pub start_radius: f32, + pub end_radius: f32, + pub ratio_xy: f32, +} + +impl Eq for RadialGradientParams {} + +impl hash::Hash for RadialGradientParams { + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.start_radius.to_bits().hash(state); + self.end_radius.to_bits().hash(state); + self.ratio_xy.to_bits().hash(state); + } +} + +/// Identifying key for a radial gradient. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)] +pub struct RadialGradientKey { + pub common: PrimKeyCommonData, + pub extend_mode: ExtendMode, + pub center: PointKey, + pub params: RadialGradientParams, + pub stretch_size: SizeKey, + pub stops: Vec<GradientStopKey>, + pub tile_spacing: SizeKey, + pub nine_patch: Option<Box<NinePatchDescriptor>>, +} + +impl RadialGradientKey { + pub fn new( + info: &LayoutPrimitiveInfo, + radial_grad: RadialGradient, + ) -> Self { + RadialGradientKey { + common: info.into(), + extend_mode: radial_grad.extend_mode, + center: radial_grad.center, + params: radial_grad.params, + stretch_size: radial_grad.stretch_size, + stops: radial_grad.stops, + tile_spacing: radial_grad.tile_spacing, + nine_patch: radial_grad.nine_patch, + } + } +} + +impl InternDebug for RadialGradientKey {} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct RadialGradientTemplate { + pub common: PrimTemplateCommonData, + pub extend_mode: ExtendMode, + pub center: LayoutPoint, + pub params: RadialGradientParams, + pub stretch_size: LayoutSize, + pub tile_spacing: LayoutSize, + pub brush_segments: Vec<BrushSegment>, + pub stops_opacity: PrimitiveOpacity, + pub stops: Vec<GradientStop>, + pub stops_handle: GpuCacheHandle, +} + +impl Deref for RadialGradientTemplate { + type Target = PrimTemplateCommonData; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl DerefMut for RadialGradientTemplate { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.common + } +} + +impl From<RadialGradientKey> for RadialGradientTemplate { + fn from(item: RadialGradientKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(item.common); + let mut brush_segments = Vec::new(); + + if let Some(ref nine_patch) = item.nine_patch { + brush_segments = nine_patch.create_segments(common.prim_rect.size); + } + + let (stops, min_alpha) = stops_and_min_alpha(&item.stops); + + // Save opacity of the stops for use in + // selecting which pass this gradient + // should be drawn in. + let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha); + + RadialGradientTemplate { + common, + center: item.center.into(), + extend_mode: item.extend_mode, + params: item.params, + stretch_size: item.stretch_size.into(), + tile_spacing: item.tile_spacing.into(), + brush_segments, + stops_opacity, + stops, + stops_handle: GpuCacheHandle::new(), + } + } +} + +impl RadialGradientTemplate { + /// Update the GPU cache for a given primitive template. This may be called multiple + /// times per frame, by each primitive reference that refers to this interned + /// template. The initial request call to the GPU cache ensures that work is only + /// done if the cache entry is invalid (due to first use or eviction). + pub fn update( + &mut self, + frame_state: &mut FrameBuildingState, + ) { + if let Some(mut request) = + frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { + // write_prim_gpu_blocks + request.push([ + self.center.x, + self.center.y, + self.params.start_radius, + self.params.end_radius, + ]); + request.push([ + self.params.ratio_xy, + pack_as_float(self.extend_mode as u32), + self.stretch_size.width, + self.stretch_size.height, + ]); + + // write_segment_gpu_blocks + for segment in &self.brush_segments { + // has to match VECS_PER_SEGMENT + request.write_segment( + segment.local_rect, + segment.extra_data, + ); + } + } + + if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { + GradientGpuBlockBuilder::build( + false, + &mut request, + &self.stops, + ); + } + + self.opacity = PrimitiveOpacity::translucent(); + } +} + +pub type RadialGradientDataHandle = InternHandle<RadialGradient>; + +#[derive(Debug, MallocSizeOf)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct RadialGradient { + pub extend_mode: ExtendMode, + pub center: PointKey, + pub params: RadialGradientParams, + pub stretch_size: SizeKey, + pub stops: Vec<GradientStopKey>, + pub tile_spacing: SizeKey, + pub nine_patch: Option<Box<NinePatchDescriptor>>, +} + +impl Internable for RadialGradient { + type Key = RadialGradientKey; + type StoreData = RadialGradientTemplate; + type InternData = (); +} + +impl InternablePrimitive for RadialGradient { + fn into_key( + self, + info: &LayoutPrimitiveInfo, + ) -> RadialGradientKey { + RadialGradientKey::new(info, self) + } + + fn make_instance_kind( + _key: RadialGradientKey, + data_handle: RadialGradientDataHandle, + _prim_store: &mut PrimitiveStore, + _reference_frame_relative_offset: LayoutVector2D, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::RadialGradient { + data_handle, + visible_tiles_range: GradientTileRange::empty(), + } + } +} + +impl IsVisible for RadialGradient { + fn is_visible(&self) -> bool { + true + } +} + +//////////////////////////////////////////////////////////////////////////////// + +/// Conic gradients + +/// Hashable conic gradient parameters, for use during prim interning. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, MallocSizeOf, PartialEq)] +pub struct ConicGradientParams { + pub angle: f32, // in radians + pub start_offset: f32, + pub end_offset: f32, +} + +impl Eq for ConicGradientParams {} + +impl hash::Hash for ConicGradientParams { + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.angle.to_bits().hash(state); + self.start_offset.to_bits().hash(state); + self.end_offset.to_bits().hash(state); + } +} + +/// Identifying key for a line decoration. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)] +pub struct ConicGradientKey { + pub common: PrimKeyCommonData, + pub extend_mode: ExtendMode, + pub center: PointKey, + pub params: ConicGradientParams, + pub stretch_size: SizeKey, + pub stops: Vec<GradientStopKey>, + pub tile_spacing: SizeKey, + pub nine_patch: Option<Box<NinePatchDescriptor>>, +} + +impl ConicGradientKey { + pub fn new( + info: &LayoutPrimitiveInfo, + conic_grad: ConicGradient, + ) -> Self { + ConicGradientKey { + common: info.into(), + extend_mode: conic_grad.extend_mode, + center: conic_grad.center, + params: conic_grad.params, + stretch_size: conic_grad.stretch_size, + stops: conic_grad.stops, + tile_spacing: conic_grad.tile_spacing, + nine_patch: conic_grad.nine_patch, + } + } +} + +impl InternDebug for ConicGradientKey {} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(MallocSizeOf)] +pub struct ConicGradientTemplate { + pub common: PrimTemplateCommonData, + pub extend_mode: ExtendMode, + pub center: LayoutPoint, + pub params: ConicGradientParams, + pub stretch_size: LayoutSize, + pub tile_spacing: LayoutSize, + pub brush_segments: Vec<BrushSegment>, + pub stops_opacity: PrimitiveOpacity, + pub stops: Vec<GradientStop>, + pub stops_handle: GpuCacheHandle, +} + +impl Deref for ConicGradientTemplate { + type Target = PrimTemplateCommonData; + fn deref(&self) -> &Self::Target { + &self.common + } +} + +impl DerefMut for ConicGradientTemplate { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.common + } +} + +impl From<ConicGradientKey> for ConicGradientTemplate { + fn from(item: ConicGradientKey) -> Self { + let common = PrimTemplateCommonData::with_key_common(item.common); + let mut brush_segments = Vec::new(); + + if let Some(ref nine_patch) = item.nine_patch { + brush_segments = nine_patch.create_segments(common.prim_rect.size); + } + + let (stops, min_alpha) = stops_and_min_alpha(&item.stops); + + // Save opacity of the stops for use in + // selecting which pass this gradient + // should be drawn in. + let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha); + + ConicGradientTemplate { + common, + center: item.center.into(), + extend_mode: item.extend_mode, + params: item.params, + stretch_size: item.stretch_size.into(), + tile_spacing: item.tile_spacing.into(), + brush_segments, + stops_opacity, + stops, + stops_handle: GpuCacheHandle::new(), + } + } +} + +impl ConicGradientTemplate { + /// Update the GPU cache for a given primitive template. This may be called multiple + /// times per frame, by each primitive reference that refers to this interned + /// template. The initial request call to the GPU cache ensures that work is only + /// done if the cache entry is invalid (due to first use or eviction). + pub fn update( + &mut self, + frame_state: &mut FrameBuildingState, + ) { + if let Some(mut request) = + frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { + // write_prim_gpu_blocks + request.push([ + self.center.x, + self.center.y, + self.params.start_offset, + self.params.end_offset, + ]); + request.push([ + self.params.angle, + pack_as_float(self.extend_mode as u32), + self.stretch_size.width, + self.stretch_size.height, + ]); + + // write_segment_gpu_blocks + for segment in &self.brush_segments { + // has to match VECS_PER_SEGMENT + request.write_segment( + segment.local_rect, + segment.extra_data, + ); + } + } + + if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { + GradientGpuBlockBuilder::build( + false, + &mut request, + &self.stops, + ); + } + + self.opacity = PrimitiveOpacity::translucent(); + } +} + +pub type ConicGradientDataHandle = InternHandle<ConicGradient>; + +#[derive(Debug, MallocSizeOf)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct ConicGradient { + pub extend_mode: ExtendMode, + pub center: PointKey, + pub params: ConicGradientParams, + pub stretch_size: SizeKey, + pub stops: Vec<GradientStopKey>, + pub tile_spacing: SizeKey, + pub nine_patch: Option<Box<NinePatchDescriptor>>, +} + +impl Internable for ConicGradient { + type Key = ConicGradientKey; + type StoreData = ConicGradientTemplate; + type InternData = (); +} + +impl InternablePrimitive for ConicGradient { + fn into_key( + self, + info: &LayoutPrimitiveInfo, + ) -> ConicGradientKey { + ConicGradientKey::new(info, self) + } + + fn make_instance_kind( + _key: ConicGradientKey, + data_handle: ConicGradientDataHandle, + _prim_store: &mut PrimitiveStore, + _reference_frame_relative_offset: LayoutVector2D, + ) -> PrimitiveInstanceKind { + PrimitiveInstanceKind::ConicGradient { + data_handle, + visible_tiles_range: GradientTileRange::empty(), + } + } +} + +impl IsVisible for ConicGradient { + fn is_visible(&self) -> bool { + true + } +} + +//////////////////////////////////////////////////////////////////////////////// + +// The gradient entry index for the first color stop +pub const GRADIENT_DATA_FIRST_STOP: usize = 0; +// The gradient entry index for the last color stop +pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1; + +// The start of the gradient data table +pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1; +// The exclusive bound of the gradient data table +pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP; +// The number of entries in the gradient data table. +pub const GRADIENT_DATA_TABLE_SIZE: usize = 128; + +// The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry +pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2; + +/// An entry in a gradient data table representing a segment of the gradient +/// color space. +#[derive(Debug, Copy, Clone)] +#[repr(C)] +struct GradientDataEntry { + start_color: PremultipliedColorF, + end_color: PremultipliedColorF, +} + +impl GradientDataEntry { + fn white() -> Self { + Self { + start_color: PremultipliedColorF::WHITE, + end_color: PremultipliedColorF::WHITE, + } + } +} + +// TODO(gw): Tidy this up to be a free function / module? +struct GradientGpuBlockBuilder {} + +impl GradientGpuBlockBuilder { + /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating + /// from start_color to end_color. + fn fill_colors( + start_idx: usize, + end_idx: usize, + start_color: &PremultipliedColorF, + end_color: &PremultipliedColorF, + entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE], + ) { + // Calculate the color difference for individual steps in the ramp. + let inv_steps = 1.0 / (end_idx - start_idx) as f32; + let step_r = (end_color.r - start_color.r) * inv_steps; + let step_g = (end_color.g - start_color.g) * inv_steps; + let step_b = (end_color.b - start_color.b) * inv_steps; + let step_a = (end_color.a - start_color.a) * inv_steps; + + let mut cur_color = *start_color; + + // Walk the ramp writing start and end colors for each entry. + for index in start_idx .. end_idx { + let entry = &mut entries[index]; + entry.start_color = cur_color; + cur_color.r += step_r; + cur_color.g += step_g; + cur_color.b += step_b; + cur_color.a += step_a; + entry.end_color = cur_color; + } + } + + /// Compute an index into the gradient entry table based on a gradient stop offset. This + /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END]. + #[inline] + fn get_index(offset: f32) -> usize { + (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 + + GRADIENT_DATA_TABLE_BEGIN as f32) + .round() as usize + } + + // Build the gradient data from the supplied stops, reversing them if necessary. + fn build( + reverse_stops: bool, + request: &mut GpuDataRequest, + src_stops: &[GradientStop], + ) { + // Preconditions (should be ensured by DisplayListBuilder): + // * we have at least two stops + // * first stop has offset 0.0 + // * last stop has offset 1.0 + let mut src_stops = src_stops.into_iter(); + let mut cur_color = match src_stops.next() { + Some(stop) => { + debug_assert_eq!(stop.offset, 0.0); + stop.color.premultiplied() + } + None => { + error!("Zero gradient stops found!"); + PremultipliedColorF::BLACK + } + }; + + // A table of gradient entries, with two colors per entry, that specify the start and end color + // within the segment of the gradient space represented by that entry. To lookup a gradient result, + // first the entry index is calculated to determine which two colors to interpolate between, then + // the offset within that entry bucket is used to interpolate between the two colors in that entry. + // This layout preserves hard stops, as the end color for a given entry can differ from the start + // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8 + // format for texture upload. This table requires the gradient color stops to be normalized to the + // range [0, 1]. The first and last entries hold the first and last color stop colors respectively, + // while the entries in between hold the interpolated color stop values for the range [0, 1]. + let mut entries = [GradientDataEntry::white(); GRADIENT_DATA_SIZE]; + + if reverse_stops { + // Fill in the first entry (for reversed stops) with the first color stop + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_LAST_STOP, + GRADIENT_DATA_LAST_STOP + 1, + &cur_color, + &cur_color, + &mut entries, + ); + + // Fill in the center of the gradient table, generating a color ramp between each consecutive pair + // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The + // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). + let mut cur_idx = GRADIENT_DATA_TABLE_END; + for next in src_stops { + let next_color = next.color.premultiplied(); + let next_idx = Self::get_index(1.0 - next.offset); + + if next_idx < cur_idx { + GradientGpuBlockBuilder::fill_colors( + next_idx, + cur_idx, + &next_color, + &cur_color, + &mut entries, + ); + cur_idx = next_idx; + } + + cur_color = next_color; + } + if cur_idx != GRADIENT_DATA_TABLE_BEGIN { + error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); + } + + // Fill in the last entry (for reversed stops) with the last color stop + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_FIRST_STOP, + GRADIENT_DATA_FIRST_STOP + 1, + &cur_color, + &cur_color, + &mut entries, + ); + } else { + // Fill in the first entry with the first color stop + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_FIRST_STOP, + GRADIENT_DATA_FIRST_STOP + 1, + &cur_color, + &cur_color, + &mut entries, + ); + + // Fill in the center of the gradient table, generating a color ramp between each consecutive pair + // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The + // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). + let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN; + for next in src_stops { + let next_color = next.color.premultiplied(); + let next_idx = Self::get_index(next.offset); + + if next_idx > cur_idx { + GradientGpuBlockBuilder::fill_colors( + cur_idx, + next_idx, + &cur_color, + &next_color, + &mut entries, + ); + cur_idx = next_idx; + } + + cur_color = next_color; + } + if cur_idx != GRADIENT_DATA_TABLE_END { + error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); + } + + // Fill in the last entry with the last color stop + GradientGpuBlockBuilder::fill_colors( + GRADIENT_DATA_LAST_STOP, + GRADIENT_DATA_LAST_STOP + 1, + &cur_color, + &cur_color, + &mut entries, + ); + } + + for entry in entries.iter() { + request.push(entry.start_color); + request.push(entry.end_color); + } + } +} + +#[test] +#[cfg(target_pointer_width = "64")] +fn test_struct_sizes() { + use std::mem; + // The sizes of these structures are critical for performance on a number of + // talos stress tests. If you get a failure here on CI, there's two possibilities: + // (a) You made a structure smaller than it currently is. Great work! Update the + // test expectations and move on. + // (b) You made a structure larger. This is not necessarily a problem, but should only + // be done with care, and after checking if talos performance regresses badly. + assert_eq!(mem::size_of::<LinearGradient>(), 72, "LinearGradient size changed"); + assert_eq!(mem::size_of::<LinearGradientTemplate>(), 120, "LinearGradientTemplate size changed"); + assert_eq!(mem::size_of::<LinearGradientKey>(), 88, "LinearGradientKey size changed"); + + assert_eq!(mem::size_of::<RadialGradient>(), 72, "RadialGradient size changed"); + assert_eq!(mem::size_of::<RadialGradientTemplate>(), 128, "RadialGradientTemplate size changed"); + assert_eq!(mem::size_of::<RadialGradientKey>(), 96, "RadialGradientKey size changed"); + + assert_eq!(mem::size_of::<ConicGradient>(), 72, "ConicGradient size changed"); + assert_eq!(mem::size_of::<ConicGradientTemplate>(), 128, "ConicGradientTemplate size changed"); + assert_eq!(mem::size_of::<ConicGradientKey>(), 96, "ConicGradientKey size changed"); +} diff --git a/third_party/webrender/webrender/src/prim_store/gradient/conic.rs b/third_party/webrender/webrender/src/prim_store/gradient/conic.rs deleted file mode 100644 index 34bcb976993..00000000000 --- a/third_party/webrender/webrender/src/prim_store/gradient/conic.rs +++ /dev/null @@ -1,363 +0,0 @@ -/* 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/. */ - -//! Conic gradients -//! -//! Specification: https://drafts.csswg.org/css-images-4/#conic-gradients -//! -//! Conic gradients are rendered via cached render tasks and composited with the image brush. - -use euclid::vec2; -use api::{ExtendMode, GradientStop, PremultipliedColorF}; -use api::units::*; -use crate::scene_building::IsVisible; -use crate::frame_builder::FrameBuildingState; -use crate::gpu_cache::{GpuCache, GpuCacheHandle}; -use crate::intern::{Internable, InternDebug, Handle as InternHandle}; -use crate::internal_types::LayoutPrimitiveInfo; -use crate::prim_store::{BrushSegment, GradientTileRange}; -use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, FloatKey}; -use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore}; -use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive}; -use crate::render_task::{RenderTask, RenderTaskKind}; -use crate::render_task_graph::RenderTaskId; -use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheKey, RenderTaskParent}; -use crate::picture::{SurfaceIndex}; - -use std::{hash, ops::{Deref, DerefMut}}; -use super::{stops_and_min_alpha, GradientStopKey, GradientGpuBlockBuilder}; - -/// Hashable conic gradient parameters, for use during prim interning. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Clone, MallocSizeOf, PartialEq)] -pub struct ConicGradientParams { - pub angle: f32, // in radians - pub start_offset: f32, - pub end_offset: f32, -} - -impl Eq for ConicGradientParams {} - -impl hash::Hash for ConicGradientParams { - fn hash<H: hash::Hasher>(&self, state: &mut H) { - self.angle.to_bits().hash(state); - self.start_offset.to_bits().hash(state); - self.end_offset.to_bits().hash(state); - } -} - -/// Identifying key for a line decoration. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)] -pub struct ConicGradientKey { - pub common: PrimKeyCommonData, - pub extend_mode: ExtendMode, - pub center: PointKey, - pub params: ConicGradientParams, - pub stretch_size: SizeKey, - pub stops: Vec<GradientStopKey>, - pub tile_spacing: SizeKey, - pub nine_patch: Option<Box<NinePatchDescriptor>>, -} - -impl ConicGradientKey { - pub fn new( - info: &LayoutPrimitiveInfo, - conic_grad: ConicGradient, - ) -> Self { - ConicGradientKey { - common: info.into(), - extend_mode: conic_grad.extend_mode, - center: conic_grad.center, - params: conic_grad.params, - stretch_size: conic_grad.stretch_size, - stops: conic_grad.stops, - tile_spacing: conic_grad.tile_spacing, - nine_patch: conic_grad.nine_patch, - } - } -} - -impl InternDebug for ConicGradientKey {} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] -pub struct ConicGradientTemplate { - pub common: PrimTemplateCommonData, - pub extend_mode: ExtendMode, - pub center: DevicePoint, - pub params: ConicGradientParams, - pub task_size: DeviceIntSize, - pub scale: DeviceVector2D, - pub stretch_size: LayoutSize, - pub tile_spacing: LayoutSize, - pub brush_segments: Vec<BrushSegment>, - pub stops_opacity: PrimitiveOpacity, - pub stops: Vec<GradientStop>, - pub stops_handle: GpuCacheHandle, - pub src_color: Option<RenderTaskId>, -} - -impl Deref for ConicGradientTemplate { - type Target = PrimTemplateCommonData; - fn deref(&self) -> &Self::Target { - &self.common - } -} - -impl DerefMut for ConicGradientTemplate { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.common - } -} - -impl From<ConicGradientKey> for ConicGradientTemplate { - fn from(item: ConicGradientKey) -> Self { - let common = PrimTemplateCommonData::with_key_common(item.common); - let mut brush_segments = Vec::new(); - - if let Some(ref nine_patch) = item.nine_patch { - brush_segments = nine_patch.create_segments(common.prim_rect.size); - } - - let (stops, min_alpha) = stops_and_min_alpha(&item.stops); - - // Save opacity of the stops for use in - // selecting which pass this gradient - // should be drawn in. - let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha); - - let mut stretch_size: LayoutSize = item.stretch_size.into(); - stretch_size.width = stretch_size.width.min(common.prim_rect.size.width); - stretch_size.height = stretch_size.height.min(common.prim_rect.size.height); - - // Avoid rendering enormous gradients. Radial gradients are mostly made of soft transitions, - // so it is unlikely that rendering at a higher resolution that 1024 would produce noticeable - // differences, especially with 8 bits per channel. - const MAX_SIZE: f32 = 1024.0; - let mut task_size: DeviceSize = stretch_size.cast_unit(); - let mut scale = vec2(1.0, 1.0); - if task_size.width > MAX_SIZE { - scale.x = task_size.width / MAX_SIZE; - task_size.width = MAX_SIZE; - } - if task_size.height > MAX_SIZE { - scale.y = task_size.height / MAX_SIZE; - task_size.height = MAX_SIZE; - } - - ConicGradientTemplate { - common, - center: DevicePoint::new(item.center.x, item.center.y), - extend_mode: item.extend_mode, - params: item.params, - stretch_size, - task_size: task_size.ceil().to_i32(), - scale, - tile_spacing: item.tile_spacing.into(), - brush_segments, - stops_opacity, - stops, - stops_handle: GpuCacheHandle::new(), - src_color: None, - } - } -} - -impl ConicGradientTemplate { - /// Update the GPU cache for a given primitive template. This may be called multiple - /// times per frame, by each primitive reference that refers to this interned - /// template. The initial request call to the GPU cache ensures that work is only - /// done if the cache entry is invalid (due to first use or eviction). - pub fn update( - &mut self, - frame_state: &mut FrameBuildingState, - parent_surface: SurfaceIndex, - ) { - if let Some(mut request) = - frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { - // write_prim_gpu_blocks - request.push(PremultipliedColorF::WHITE); - request.push(PremultipliedColorF::WHITE); - request.push([ - self.stretch_size.width, - self.stretch_size.height, - 0.0, - 0.0, - ]); - - // write_segment_gpu_blocks - for segment in &self.brush_segments { - // has to match VECS_PER_SEGMENT - request.write_segment( - segment.local_rect, - segment.extra_data, - ); - } - } - - if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { - GradientGpuBlockBuilder::build( - false, - &mut request, - &self.stops, - ); - } - - let cache_key = ConicGradientCacheKey { - size: self.task_size, - center: PointKey { x: self.center.x, y: self.center.y }, - scale: PointKey { x: self.scale.x, y: self.scale.y }, - start_offset: FloatKey(self.params.start_offset), - end_offset: FloatKey(self.params.end_offset), - angle: FloatKey(self.params.angle), - extend_mode: self.extend_mode, - stops: self.stops.iter().map(|stop| (*stop).into()).collect(), - }; - - let task_id = frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size: self.task_size, - kind: RenderTaskCacheKeyKind::ConicGradient(cache_key), - }, - frame_state.gpu_cache, - frame_state.rg_builder, - None, - false, - RenderTaskParent::Surface(parent_surface), - frame_state.surfaces, - |rg_builder| { - rg_builder.add().init(RenderTask::new_dynamic( - self.task_size, - RenderTaskKind::ConicGradient(ConicGradientTask { - extend_mode: self.extend_mode, - scale: self.scale, - center: self.center, - params: self.params.clone(), - stops: self.stops_handle, - }), - )) - } - ); - - self.src_color = Some(task_id); - - // Tile spacing is always handled by decomposing into separate draw calls so the - // primitive opacity is equivalent to stops opacity. This might change to being - // set to non-opaque in the presence of tile spacing if/when tile spacing is handled - // in the same way as with the image primitive. - self.opacity = self.stops_opacity; - } -} - -pub type ConicGradientDataHandle = InternHandle<ConicGradient>; - -#[derive(Debug, MallocSizeOf)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct ConicGradient { - pub extend_mode: ExtendMode, - pub center: PointKey, - pub params: ConicGradientParams, - pub stretch_size: SizeKey, - pub stops: Vec<GradientStopKey>, - pub tile_spacing: SizeKey, - pub nine_patch: Option<Box<NinePatchDescriptor>>, -} - -impl Internable for ConicGradient { - type Key = ConicGradientKey; - type StoreData = ConicGradientTemplate; - type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_CONIC_GRADIENTS; -} - -impl InternablePrimitive for ConicGradient { - fn into_key( - self, - info: &LayoutPrimitiveInfo, - ) -> ConicGradientKey { - ConicGradientKey::new(info, self) - } - - fn make_instance_kind( - _key: ConicGradientKey, - data_handle: ConicGradientDataHandle, - _prim_store: &mut PrimitiveStore, - _reference_frame_relative_offset: LayoutVector2D, - ) -> PrimitiveInstanceKind { - PrimitiveInstanceKind::ConicGradient { - data_handle, - visible_tiles_range: GradientTileRange::empty(), - } - } -} - -impl IsVisible for ConicGradient { - fn is_visible(&self) -> bool { - true - } -} - -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct ConicGradientTask { - pub extend_mode: ExtendMode, - pub center: DevicePoint, - pub scale: DeviceVector2D, - pub params: ConicGradientParams, - pub stops: GpuCacheHandle, -} - -impl ConicGradientTask { - pub fn to_instance(&self, target_rect: &DeviceIntRect, gpu_cache: &mut GpuCache) -> ConicGradientInstance { - ConicGradientInstance { - task_rect: target_rect.to_f32(), - center: self.center, - scale: self.scale, - start_offset: self.params.start_offset, - end_offset: self.params.end_offset, - angle: self.params.angle, - extend_mode: self.extend_mode as i32, - gradient_stops_address: self.stops.as_int(gpu_cache), - } - } -} - -/// The per-instance shader input of a radial gradient render task. -/// -/// Must match the RADIAL_GRADIENT instance description in renderer/vertex.rs. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -#[derive(Clone, Debug)] -pub struct ConicGradientInstance { - pub task_rect: DeviceRect, - pub center: DevicePoint, - pub scale: DeviceVector2D, - pub start_offset: f32, - pub end_offset: f32, - pub angle: f32, - pub extend_mode: i32, - pub gradient_stops_address: i32, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct ConicGradientCacheKey { - pub size: DeviceIntSize, - pub center: PointKey, - pub scale: PointKey, - pub start_offset: FloatKey, - pub end_offset: FloatKey, - pub angle: FloatKey, - pub extend_mode: ExtendMode, - pub stops: Vec<GradientStopKey>, -} - diff --git a/third_party/webrender/webrender/src/prim_store/gradient/linear.rs b/third_party/webrender/webrender/src/prim_store/gradient/linear.rs deleted file mode 100644 index e608cb0bae1..00000000000 --- a/third_party/webrender/webrender/src/prim_store/gradient/linear.rs +++ /dev/null @@ -1,723 +0,0 @@ -/* 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/. */ - -//! Linear gradients -//! -//! Specification: https://drafts.csswg.org/css-images-4/#linear-gradients -//! -//! Linear gradients are rendered via cached render tasks and composited with the image brush. - -use euclid::approxeq::ApproxEq; -use euclid::{point2, vec2, size2}; -use api::{ExtendMode, GradientStop, LineOrientation, PremultipliedColorF, ColorF, ColorU}; -use api::units::*; -use crate::scene_building::IsVisible; -use crate::frame_builder::FrameBuildingState; -use crate::gpu_cache::{GpuCache, GpuCacheHandle}; -use crate::intern::{Internable, InternDebug, Handle as InternHandle}; -use crate::internal_types::LayoutPrimitiveInfo; -use crate::image_tiling::simplify_repeated_primitive; -use crate::prim_store::{BrushSegment, GradientTileRange}; -use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity}; -use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore}; -use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, InternablePrimitive}; -use crate::render_task::{RenderTask, RenderTaskKind}; -use crate::render_task_graph::RenderTaskId; -use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheKey, RenderTaskParent}; -use crate::picture::{SurfaceIndex}; -use crate::util::pack_as_float; -use super::{stops_and_min_alpha, GradientStopKey, GradientGpuBlockBuilder, apply_gradient_local_clip}; -use std::ops::{Deref, DerefMut}; -use std::mem::swap; - -/// Identifying key for a linear gradient. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)] -pub struct LinearGradientKey { - pub common: PrimKeyCommonData, - pub extend_mode: ExtendMode, - pub start_point: PointKey, - pub end_point: PointKey, - pub stretch_size: SizeKey, - pub tile_spacing: SizeKey, - pub stops: Vec<GradientStopKey>, - pub reverse_stops: bool, - pub cached: bool, - pub nine_patch: Option<Box<NinePatchDescriptor>>, -} - -impl LinearGradientKey { - pub fn new( - info: &LayoutPrimitiveInfo, - linear_grad: LinearGradient, - ) -> Self { - LinearGradientKey { - common: info.into(), - extend_mode: linear_grad.extend_mode, - start_point: linear_grad.start_point, - end_point: linear_grad.end_point, - stretch_size: linear_grad.stretch_size, - tile_spacing: linear_grad.tile_spacing, - stops: linear_grad.stops, - reverse_stops: linear_grad.reverse_stops, - cached: linear_grad.cached, - nine_patch: linear_grad.nine_patch, - } - } -} - -impl InternDebug for LinearGradientKey {} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, MallocSizeOf)] -pub struct LinearGradientTemplate { - pub common: PrimTemplateCommonData, - pub extend_mode: ExtendMode, - pub start_point: DevicePoint, - pub end_point: DevicePoint, - pub task_size: DeviceIntSize, - pub scale: DeviceVector2D, - pub stretch_size: LayoutSize, - pub tile_spacing: LayoutSize, - pub stops_opacity: PrimitiveOpacity, - pub stops: Vec<GradientStop>, - pub brush_segments: Vec<BrushSegment>, - pub reverse_stops: bool, - pub is_fast_path: bool, - pub cached: bool, - pub stops_handle: GpuCacheHandle, - pub src_color: Option<RenderTaskId>, -} - -impl Deref for LinearGradientTemplate { - type Target = PrimTemplateCommonData; - fn deref(&self) -> &Self::Target { - &self.common - } -} - -impl DerefMut for LinearGradientTemplate { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.common - } -} - -/// Perform a few optimizations to the gradient that are relevant to scene building. -/// -/// Returns true if the gradient was decomposed into fast-path primitives, indicating -/// that we shouldn't emit a regular gradient primitive after this returns. -pub fn optimize_linear_gradient( - prim_rect: &mut LayoutRect, - tile_size: &mut LayoutSize, - mut tile_spacing: LayoutSize, - clip_rect: &LayoutRect, - start: &mut LayoutPoint, - end: &mut LayoutPoint, - extend_mode: ExtendMode, - stops: &mut [GradientStopKey], - // Callback called for each fast-path segment (rect, start end, stops). - callback: &mut dyn FnMut(&LayoutRect, LayoutPoint, LayoutPoint, &[GradientStopKey]) -) -> bool { - // First sanitize the gradient parameters. See if we can remove repetitions, - // tighten the primitive bounds, etc. - - // The size of gradient render tasks depends on the tile_size. No need to generate - // large stretch sizes that will be clipped to the bounds of the primitive. - tile_size.width = tile_size.width.min(prim_rect.size.width); - tile_size.height = tile_size.height.min(prim_rect.size.height); - - simplify_repeated_primitive(&tile_size, &mut tile_spacing, prim_rect); - - let vertical = start.x.approx_eq(&end.x); - let horizontal = start.y.approx_eq(&end.y); - - let mut horizontally_tiled = prim_rect.size.width > tile_size.width; - let mut vertically_tiled = prim_rect.size.height > tile_size.height; - - // Check whether the tiling is equivalent to stretching on either axis. - // Stretching the gradient is more efficient than repeating it. - if vertically_tiled && horizontal && tile_spacing.height == 0.0 { - tile_size.height = prim_rect.size.height; - vertically_tiled = false; - } - - if horizontally_tiled && vertical && tile_spacing.width == 0.0 { - tile_size.width = prim_rect.size.width; - horizontally_tiled = false; - } - - let offset = apply_gradient_local_clip( - prim_rect, - &tile_size, - &tile_spacing, - &clip_rect - ); - - *start += offset; - *end += offset; - - // Next, in the case of axis-aligned gradients, see if it is worth - // decomposing the gradient into multiple gradients with only two - // gradient stops per segment to get a faster shader. - - if extend_mode != ExtendMode::Clamp || stops.is_empty() { - return false; - } - - if !vertical && !horizontal { - return false; - } - - if vertical && horizontal { - return false; - } - - if !tile_spacing.is_empty() || vertically_tiled || horizontally_tiled { - return false; - } - - // If the gradient is small, no need to bother with decomposing it. - if (horizontal && tile_size.width < 256.0) - || (vertical && tile_size.height < 256.0) { - - return false; - } - - // Flip x and y if need be so that we only deal with the horizontal case. - - // From now on don't return false. We are going modifying the caller's - // variables and not bother to restore them. If the control flow changes, - // Make sure to to restore &mut parameters to sensible values before - // returning false. - - let adjust_rect = &mut |rect: &mut LayoutRect| { - if vertical { - swap(&mut rect.origin.x, &mut rect.origin.y); - swap(&mut rect.size.width, &mut rect.size.height); - } - }; - - let adjust_size = &mut |size: &mut LayoutSize| { - if vertical { swap(&mut size.width, &mut size.height); } - }; - - let adjust_point = &mut |p: &mut LayoutPoint| { - if vertical { swap(&mut p.x, &mut p.y); } - }; - - let clip_rect = match clip_rect.intersection(prim_rect) { - Some(clip) => clip, - None => { - return false; - } - }; - - adjust_rect(prim_rect); - adjust_point(start); - adjust_point(end); - adjust_size(tile_size); - - let length = (end.x - start.x).abs(); - - // Decompose the gradient into simple segments. This lets us: - // - separate opaque from semi-transparent segments, - // - compress long segments into small render tasks, - // - make sure hard stops stay so even if the primitive is large. - - let reverse_stops = start.x > end.x; - - // Handle reverse stops so we can assume stops are arranged in increasing x. - if reverse_stops { - stops.reverse(); - swap(start, end); - } - - // Use fake gradient stop to emulate the potential constant color sections - // before and after the gradient endpoints. - let mut prev = *stops.first().unwrap(); - let mut last = *stops.last().unwrap(); - - // Set the offsets of the fake stops to position them at the edges of the primitive. - prev.offset = -start.x / length; - last.offset = (tile_size.width - start.x) / length; - if reverse_stops { - prev.offset = 1.0 - prev.offset; - last.offset = 1.0 - last.offset; - } - - for stop in stops.iter().chain((&[last]).iter()) { - let prev_stop = prev; - prev = *stop; - - if prev_stop.color.a == 0 && stop.color.a == 0 { - continue; - } - - - let prev_offset = if reverse_stops { 1.0 - prev_stop.offset } else { prev_stop.offset }; - let offset = if reverse_stops { 1.0 - stop.offset } else { stop.offset }; - - // In layout space, relative to the primitive. - let segment_start = start.x + prev_offset * length; - let segment_end = start.x + offset * length; - let segment_length = segment_end - segment_start; - - if segment_length <= 0.0 { - continue; - } - - let mut segment_rect = *prim_rect; - segment_rect.origin.x += segment_start; - segment_rect.size.width = segment_length; - - let mut start = point2(0.0, 0.0); - let mut end = point2(segment_length, 0.0); - - adjust_point(&mut start); - adjust_point(&mut end); - adjust_rect(&mut segment_rect); - - let origin_before_clip = segment_rect.origin; - segment_rect = match segment_rect.intersection(&clip_rect) { - Some(rect) => rect, - None => { - continue; - } - }; - let offset = segment_rect.origin - origin_before_clip; - - // Account for the clipping since start and end are relative to the origin. - start -= offset; - end -= offset; - - callback( - &segment_rect, - start, - end, - &[ - GradientStopKey { offset: 0.0, .. prev_stop }, - GradientStopKey { offset: 1.0, .. *stop }, - ], - ); - } - - true -} - -impl From<LinearGradientKey> for LinearGradientTemplate { - fn from(item: LinearGradientKey) -> Self { - - let common = PrimTemplateCommonData::with_key_common(item.common); - - let (mut stops, min_alpha) = stops_and_min_alpha(&item.stops); - - let mut brush_segments = Vec::new(); - - if let Some(ref nine_patch) = item.nine_patch { - brush_segments = nine_patch.create_segments(common.prim_rect.size); - } - - // Save opacity of the stops for use in - // selecting which pass this gradient - // should be drawn in. - let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha); - - let start_point = DevicePoint::new(item.start_point.x, item.start_point.y); - let end_point = DevicePoint::new(item.end_point.x, item.end_point.y); - let tile_spacing: LayoutSize = item.tile_spacing.into(); - let stretch_size: LayoutSize = item.stretch_size.into(); - let mut task_size: DeviceSize = stretch_size.cast_unit(); - - let horizontal = start_point.y.approx_eq(&end_point.y); - let vertical = start_point.x.approx_eq(&end_point.x); - - if horizontal { - // Completely horizontal, we can stretch the gradient vertically. - task_size.height = 1.0; - } - - if vertical { - // Completely vertical, we can stretch the gradient horizontally. - task_size.width = 1.0; - } - - // See if we can render the gradient using a special fast-path shader. - // The fast path path only works with two gradient stops. - let mut is_fast_path = false; - if item.cached && stops.len() == 2 && brush_segments.is_empty() { - if horizontal - && stretch_size.width >= common.prim_rect.width() - && start_point.x.approx_eq(&0.0) - && end_point.x.approx_eq(&stretch_size.width) { - is_fast_path = true; - task_size.width = task_size.width.min(256.0); - } - if vertical - && stretch_size.height >= common.prim_rect.height() - && start_point.y.approx_eq(&0.0) - && end_point.y.approx_eq(&stretch_size.height) { - is_fast_path = true; - task_size.height = task_size.height.min(256.0); - } - - if stops[0].color == stops[1].color { - is_fast_path = true; - task_size = size2(1.0, 1.0); - } - - if is_fast_path && item.reverse_stops { - // The fast path doesn't use the gradient gpu blocks builder so handle - // reversed stops here. - stops.swap(0, 1); - } - } - - // Avoid rendering enormous gradients. Radial gradients are mostly made of soft transitions, - // so it is unlikely that rendering at a higher resolution than 1024 would produce noticeable - // differences, especially with 8 bits per channel. - const MAX_SIZE: f32 = 1024.0; - - let mut scale = vec2(1.0, 1.0); - - if task_size.width > MAX_SIZE { - scale.x = task_size.width / MAX_SIZE; - task_size.width = MAX_SIZE; - } - - if task_size.height > MAX_SIZE { - scale.y = task_size.height / MAX_SIZE; - task_size.height = MAX_SIZE; - } - - LinearGradientTemplate { - common, - extend_mode: item.extend_mode, - start_point, - end_point, - task_size: task_size.ceil().to_i32(), - scale, - stretch_size, - tile_spacing, - stops_opacity, - stops, - brush_segments, - reverse_stops: item.reverse_stops, - is_fast_path, - cached: item.cached, - stops_handle: GpuCacheHandle::new(), - src_color: None, - } - } -} - -impl LinearGradientTemplate { - /// Update the GPU cache for a given primitive template. This may be called multiple - /// times per frame, by each primitive reference that refers to this interned - /// template. The initial request call to the GPU cache ensures that work is only - /// done if the cache entry is invalid (due to first use or eviction). - pub fn update( - &mut self, - frame_state: &mut FrameBuildingState, - parent_surface: SurfaceIndex, - ) { - if let Some(mut request) = frame_state.gpu_cache.request( - &mut self.common.gpu_cache_handle - ) { - - // Write_prim_gpu_blocks - if self.cached { - // We are using the image brush. - request.push(PremultipliedColorF::WHITE); - request.push(PremultipliedColorF::WHITE); - request.push([ - self.stretch_size.width, - self.stretch_size.height, - 0.0, - 0.0, - ]); - } else { - // We are using the gradient brush. - request.push([ - self.start_point.x, - self.start_point.y, - self.end_point.x, - self.end_point.y, - ]); - request.push([ - pack_as_float(self.extend_mode as u32), - self.stretch_size.width, - self.stretch_size.height, - 0.0, - ]); - } - - // write_segment_gpu_blocks - for segment in &self.brush_segments { - // has to match VECS_PER_SEGMENT - request.write_segment( - segment.local_rect, - segment.extra_data, - ); - } - } - - // The fast path passes gradient stops as vertex attributes directly. - if !self.is_fast_path { - if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { - GradientGpuBlockBuilder::build( - self.reverse_stops, - &mut request, - &self.stops, - ); - } - } - - // Tile spacing is always handled by decomposing into separate draw calls so the - // primitive opacity is equivalent to stops opacity. This might change to being - // set to non-opaque in the presence of tile spacing if/when tile spacing is handled - // in the same way as with the image primitive. - self.opacity = self.stops_opacity; - - if !self.cached { - return; - } - - let task_id = if self.is_fast_path { - let orientation = if self.task_size.width > self.task_size.height { - LineOrientation::Horizontal - } else { - LineOrientation::Vertical - }; - - let gradient = FastLinearGradientTask { - color0: self.stops[0].color.into(), - color1: self.stops[1].color.into(), - orientation, - }; - - frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size: self.task_size, - kind: RenderTaskCacheKeyKind::FastLinearGradient(gradient), - }, - frame_state.gpu_cache, - frame_state.rg_builder, - None, - false, - RenderTaskParent::Surface(parent_surface), - frame_state.surfaces, - |rg_builder| { - rg_builder.add().init(RenderTask::new_dynamic( - self.task_size, - RenderTaskKind::FastLinearGradient(gradient), - )) - } - ) - } else { - let cache_key = LinearGradientCacheKey { - size: self.task_size, - start: PointKey { x: self.start_point.x, y: self.start_point.y }, - end: PointKey { x: self.end_point.x, y: self.end_point.y }, - scale: PointKey { x: self.scale.x, y: self.scale.y }, - extend_mode: self.extend_mode, - stops: self.stops.iter().map(|stop| (*stop).into()).collect(), - reversed_stops: self.reverse_stops, - }; - - frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size: self.task_size, - kind: RenderTaskCacheKeyKind::LinearGradient(cache_key), - }, - frame_state.gpu_cache, - frame_state.rg_builder, - None, - false, - RenderTaskParent::Surface(parent_surface), - frame_state.surfaces, - |rg_builder| { - rg_builder.add().init(RenderTask::new_dynamic( - self.task_size, - RenderTaskKind::LinearGradient(LinearGradientTask { - start: self.start_point, - end: self.end_point, - scale: self.scale, - extend_mode: self.extend_mode, - stops: self.stops_handle, - }), - )) - } - ) - }; - - self.src_color = Some(task_id); - } -} - -pub type LinearGradientDataHandle = InternHandle<LinearGradient>; - -#[derive(Debug, MallocSizeOf)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct LinearGradient { - pub extend_mode: ExtendMode, - pub start_point: PointKey, - pub end_point: PointKey, - pub stretch_size: SizeKey, - pub tile_spacing: SizeKey, - pub stops: Vec<GradientStopKey>, - pub reverse_stops: bool, - pub nine_patch: Option<Box<NinePatchDescriptor>>, - pub cached: bool, -} - -impl Internable for LinearGradient { - type Key = LinearGradientKey; - type StoreData = LinearGradientTemplate; - type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_LINEAR_GRADIENTS; -} - -impl InternablePrimitive for LinearGradient { - fn into_key( - self, - info: &LayoutPrimitiveInfo, - ) -> LinearGradientKey { - LinearGradientKey::new(info, self) - } - - fn make_instance_kind( - key: LinearGradientKey, - data_handle: LinearGradientDataHandle, - _prim_store: &mut PrimitiveStore, - _reference_frame_relative_offset: LayoutVector2D, - ) -> PrimitiveInstanceKind { - if key.cached { - PrimitiveInstanceKind::CachedLinearGradient { - data_handle, - visible_tiles_range: GradientTileRange::empty(), - } - } else { - PrimitiveInstanceKind::LinearGradient { - data_handle, - visible_tiles_range: GradientTileRange::empty(), - } - } - } -} - -impl IsVisible for LinearGradient { - fn is_visible(&self) -> bool { - true - } -} - -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -pub struct LinearGradientPrimitive { - pub cache_segments: Vec<CachedGradientSegment>, - pub visible_tiles_range: GradientTileRange, -} - -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -pub struct CachedGradientSegment { - pub render_task: RenderTaskId, - pub local_rect: LayoutRect, -} - - -#[derive(Copy, Clone, Debug, Hash, MallocSizeOf, PartialEq, Eq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct FastLinearGradientTask { - pub color0: ColorU, - pub color1: ColorU, - pub orientation: LineOrientation, -} - -impl FastLinearGradientTask { - pub fn to_instance(&self, target_rect: &DeviceIntRect) -> FastLinearGradientInstance { - FastLinearGradientInstance { - task_rect: target_rect.to_f32(), - color0: ColorF::from(self.color0).premultiplied(), - color1: ColorF::from(self.color1).premultiplied(), - axis_select: match self.orientation { - LineOrientation::Horizontal => 0.0, - LineOrientation::Vertical => 1.0, - }, - } - } -} - -pub type FastLinearGradientCacheKey = FastLinearGradientTask; - -/// The per-instance shader input of a fast-path linear gradient render task. -/// -/// Must match the FAST_LINEAR_GRADIENT instance description in renderer/vertex.rs. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -#[derive(Clone, Debug)] -pub struct FastLinearGradientInstance { - pub task_rect: DeviceRect, - pub color0: PremultipliedColorF, - pub color1: PremultipliedColorF, - pub axis_select: f32, -} - -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct LinearGradientTask { - pub start: DevicePoint, - pub end: DevicePoint, - pub scale: DeviceVector2D, - pub extend_mode: ExtendMode, - pub stops: GpuCacheHandle, -} - -impl LinearGradientTask { - pub fn to_instance(&self, target_rect: &DeviceIntRect, gpu_cache: &mut GpuCache) -> LinearGradientInstance { - LinearGradientInstance { - task_rect: target_rect.to_f32(), - start: self.start, - end: self.end, - scale: self.scale, - extend_mode: self.extend_mode as i32, - gradient_stops_address: self.stops.as_int(gpu_cache), - } - } -} - -/// The per-instance shader input of a linear gradient render task. -/// -/// Must match the LINEAR_GRADIENT instance description in renderer/vertex.rs. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -#[derive(Clone, Debug)] -pub struct LinearGradientInstance { - pub task_rect: DeviceRect, - pub start: DevicePoint, - pub end: DevicePoint, - pub scale: DeviceVector2D, - pub extend_mode: i32, - pub gradient_stops_address: i32, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct LinearGradientCacheKey { - pub size: DeviceIntSize, - pub start: PointKey, - pub end: PointKey, - pub scale: PointKey, - pub extend_mode: ExtendMode, - pub stops: Vec<GradientStopKey>, - pub reversed_stops: bool, -} diff --git a/third_party/webrender/webrender/src/prim_store/gradient/mod.rs b/third_party/webrender/webrender/src/prim_store/gradient/mod.rs deleted file mode 100644 index 3fea0bb543f..00000000000 --- a/third_party/webrender/webrender/src/prim_store/gradient/mod.rs +++ /dev/null @@ -1,388 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use api::{ColorF, ColorU, GradientStop, PremultipliedColorF}; -use api::units::{LayoutRect, LayoutSize, LayoutVector2D}; -use crate::gpu_cache::GpuDataRequest; -use std::hash; - -mod linear; -mod radial; -mod conic; - -pub use linear::*; -pub use radial::*; -pub use conic::*; - -/// A hashable gradient stop that can be used in primitive keys. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)] -pub struct GradientStopKey { - pub offset: f32, - pub color: ColorU, -} - -impl GradientStopKey { - pub fn empty() -> Self { - GradientStopKey { - offset: 0.0, - color: ColorU::new(0, 0, 0, 0), - } - } -} - -impl Into<GradientStopKey> for GradientStop { - fn into(self) -> GradientStopKey { - GradientStopKey { - offset: self.offset, - color: self.color.into(), - } - } -} - -// Convert `stop_keys` into a vector of `GradientStop`s, which is a more -// convenient representation for the current gradient builder. Compute the -// minimum stop alpha along the way. -fn stops_and_min_alpha(stop_keys: &[GradientStopKey]) -> (Vec<GradientStop>, f32) { - let mut min_alpha: f32 = 1.0; - let stops = stop_keys.iter().map(|stop_key| { - let color: ColorF = stop_key.color.into(); - min_alpha = min_alpha.min(color.a); - - GradientStop { - offset: stop_key.offset, - color, - } - }).collect(); - - (stops, min_alpha) -} - -impl Eq for GradientStopKey {} - -impl hash::Hash for GradientStopKey { - fn hash<H: hash::Hasher>(&self, state: &mut H) { - self.offset.to_bits().hash(state); - self.color.hash(state); - } -} - -// The gradient entry index for the first color stop -pub const GRADIENT_DATA_FIRST_STOP: usize = 0; -// The gradient entry index for the last color stop -pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1; - -// The start of the gradient data table -pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1; -// The exclusive bound of the gradient data table -pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP; -// The number of entries in the gradient data table. -pub const GRADIENT_DATA_TABLE_SIZE: usize = 128; - -// The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry -pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2; - -/// An entry in a gradient data table representing a segment of the gradient -/// color space. -#[derive(Debug, Copy, Clone)] -#[repr(C)] -struct GradientDataEntry { - start_color: PremultipliedColorF, - end_step: PremultipliedColorF, -} - -impl GradientDataEntry { - fn white() -> Self { - Self { - start_color: PremultipliedColorF::WHITE, - end_step: PremultipliedColorF::TRANSPARENT, - } - } -} - -// TODO(gw): Tidy this up to be a free function / module? -struct GradientGpuBlockBuilder {} - -impl GradientGpuBlockBuilder { - /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating - /// from start_color to end_color. - fn fill_colors( - start_idx: usize, - end_idx: usize, - start_color: &PremultipliedColorF, - end_color: &PremultipliedColorF, - entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE], - prev_step: &PremultipliedColorF, - ) -> PremultipliedColorF { - // Calculate the color difference for individual steps in the ramp. - let inv_steps = 1.0 / (end_idx - start_idx) as f32; - let mut step = PremultipliedColorF { - r: (end_color.r - start_color.r) * inv_steps, - g: (end_color.g - start_color.g) * inv_steps, - b: (end_color.b - start_color.b) * inv_steps, - a: (end_color.a - start_color.a) * inv_steps, - }; - // As a subtle form of compression, we ensure that the step values for - // each stop range are the same if and only if they belong to the same - // stop range. However, if two different stop ranges have the same step, - // we need to modify the steps so they compare unequally between ranges. - // This allows to quickly compare if two adjacent stops belong to the - // same range by comparing their steps. - if step == *prev_step { - // Modify the step alpha value as if by nextafter(). The difference - // here should be so small as to be unnoticeable, but yet allow it - // to compare differently. - step.a = f32::from_bits(if step.a == 0.0 { 1 } else { step.a.to_bits() + 1 }); - } - - let mut cur_color = *start_color; - - // Walk the ramp writing start and end colors for each entry. - for index in start_idx .. end_idx { - let entry = &mut entries[index]; - entry.start_color = cur_color; - cur_color.r += step.r; - cur_color.g += step.g; - cur_color.b += step.b; - cur_color.a += step.a; - entry.end_step = step; - } - - step - } - - /// Compute an index into the gradient entry table based on a gradient stop offset. This - /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END]. - #[inline] - fn get_index(offset: f32) -> usize { - (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 + - GRADIENT_DATA_TABLE_BEGIN as f32) - .round() as usize - } - - // Build the gradient data from the supplied stops, reversing them if necessary. - fn build( - reverse_stops: bool, - request: &mut GpuDataRequest, - src_stops: &[GradientStop], - ) { - // Preconditions (should be ensured by DisplayListBuilder): - // * we have at least two stops - // * first stop has offset 0.0 - // * last stop has offset 1.0 - let mut src_stops = src_stops.into_iter(); - let mut cur_color = match src_stops.next() { - Some(stop) => { - debug_assert_eq!(stop.offset, 0.0); - stop.color.premultiplied() - } - None => { - error!("Zero gradient stops found!"); - PremultipliedColorF::BLACK - } - }; - - // A table of gradient entries, with two colors per entry, that specify the start and end color - // within the segment of the gradient space represented by that entry. To lookup a gradient result, - // first the entry index is calculated to determine which two colors to interpolate between, then - // the offset within that entry bucket is used to interpolate between the two colors in that entry. - // This layout is motivated by the fact that if one naively tries to store a single color per entry - // and interpolate directly between entries, then hard stops will become softened because the end - // color of an entry actually differs from the start color of the next entry, even though they fall - // at the same edge offset in the gradient space. Instead, the two-color-per-entry layout preserves - // hard stops, as the end color for a given entry can differ from the start color for the following - // entry. - // Colors are stored in RGBA32F format (in the GPU cache). This table requires the gradient color - // stops to be normalized to the range [0, 1]. The first and last entries hold the first and last - // color stop colors respectively, while the entries in between hold the interpolated color stop - // values for the range [0, 1]. - // As a further optimization, rather than directly storing the end color, the difference of the end - // color from the start color is stored instead, so that an entry can be evaluated more cheaply - // with start+diff*offset instead of mix(start,end,offset). Further, the color difference in two - // adjacent entries will always be the same if they were generated from the same set of stops/run. - // To allow fast searching of the table, if two adjacent entries generated from different sets of - // stops (a boundary) have the same difference, the floating-point bits of the stop will be nudged - // so that they compare differently without perceptibly altering the interpolation result. This way, - // one can quickly scan the table and recover runs just by comparing the color differences of the - // current and next entry. - // For example, a table with 2 inside entries (startR,startG,startB):(diffR,diffG,diffB) might look - // like so: - // first | 0.0 | 0.5 | last - // (0,0,0):(0,0,0) | (1,0,0):(-1,1,0) | (0,0,1):(0,1,-1) | (1,1,1):(0,0,0) - // ^ solid black ^ red to green ^ blue to green ^ solid white - let mut entries = [GradientDataEntry::white(); GRADIENT_DATA_SIZE]; - let mut prev_step = cur_color; - if reverse_stops { - // Fill in the first entry (for reversed stops) with the first color stop - prev_step = GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_LAST_STOP, - GRADIENT_DATA_LAST_STOP + 1, - &cur_color, - &cur_color, - &mut entries, - &prev_step, - ); - - // Fill in the center of the gradient table, generating a color ramp between each consecutive pair - // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The - // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). - let mut cur_idx = GRADIENT_DATA_TABLE_END; - for next in src_stops { - let next_color = next.color.premultiplied(); - let next_idx = Self::get_index(1.0 - next.offset); - - if next_idx < cur_idx { - prev_step = GradientGpuBlockBuilder::fill_colors( - next_idx, - cur_idx, - &next_color, - &cur_color, - &mut entries, - &prev_step, - ); - cur_idx = next_idx; - } - - cur_color = next_color; - } - if cur_idx != GRADIENT_DATA_TABLE_BEGIN { - error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); - } - - // Fill in the last entry (for reversed stops) with the last color stop - GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_FIRST_STOP, - GRADIENT_DATA_FIRST_STOP + 1, - &cur_color, - &cur_color, - &mut entries, - &prev_step, - ); - } else { - // Fill in the first entry with the first color stop - prev_step = GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_FIRST_STOP, - GRADIENT_DATA_FIRST_STOP + 1, - &cur_color, - &cur_color, - &mut entries, - &prev_step, - ); - - // Fill in the center of the gradient table, generating a color ramp between each consecutive pair - // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The - // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END). - let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN; - for next in src_stops { - let next_color = next.color.premultiplied(); - let next_idx = Self::get_index(next.offset); - - if next_idx > cur_idx { - prev_step = GradientGpuBlockBuilder::fill_colors( - cur_idx, - next_idx, - &cur_color, - &next_color, - &mut entries, - &prev_step, - ); - cur_idx = next_idx; - } - - cur_color = next_color; - } - if cur_idx != GRADIENT_DATA_TABLE_END { - error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx); - } - - // Fill in the last entry with the last color stop - GradientGpuBlockBuilder::fill_colors( - GRADIENT_DATA_LAST_STOP, - GRADIENT_DATA_LAST_STOP + 1, - &cur_color, - &cur_color, - &mut entries, - &prev_step, - ); - } - - for entry in entries.iter() { - request.push(entry.start_color); - request.push(entry.end_step); - } - } -} - -// If the gradient is not tiled we know that any content outside of the clip will not -// be shown. Applying the clip early reduces how much of the gradient we -// render and cache. We do this optimization separately on each axis. -// Returns the offset between the new and old primitive rect origin, to apply to the -// gradient parameters that are relative to the primitive origin. -pub fn apply_gradient_local_clip( - prim_rect: &mut LayoutRect, - stretch_size: &LayoutSize, - tile_spacing: &LayoutSize, - clip_rect: &LayoutRect, -) -> LayoutVector2D { - let w = prim_rect.max_x().min(clip_rect.max_x()) - prim_rect.min_x(); - let h = prim_rect.max_y().min(clip_rect.max_y()) - prim_rect.min_y(); - let is_tiled_x = w > stretch_size.width + tile_spacing.width; - let is_tiled_y = h > stretch_size.height + tile_spacing.height; - - let mut offset = LayoutVector2D::new(0.0, 0.0); - - if !is_tiled_x { - let diff = (clip_rect.min_x() - prim_rect.min_x()).min(prim_rect.size.width); - if diff > 0.0 { - prim_rect.origin.x += diff; - prim_rect.size.width -= diff; - offset.x = -diff; - } - - let diff = prim_rect.max_x() - clip_rect.max_x(); - if diff > 0.0 { - prim_rect.size.width -= diff; - } - } - - if !is_tiled_y { - let diff = (clip_rect.min_y() - prim_rect.min_y()).min(prim_rect.size.height); - if diff > 0.0 { - prim_rect.origin.y += diff; - prim_rect.size.height -= diff; - offset.y = -diff; - } - - let diff = prim_rect.max_y() - clip_rect.max_y(); - if diff > 0.0 { - prim_rect.size.height -= diff; - } - } - - offset -} - -#[test] -#[cfg(target_pointer_width = "64")] -fn test_struct_sizes() { - use std::mem; - // The sizes of these structures are critical for performance on a number of - // talos stress tests. If you get a failure here on CI, there's two possibilities: - // (a) You made a structure smaller than it currently is. Great work! Update the - // test expectations and move on. - // (b) You made a structure larger. This is not necessarily a problem, but should only - // be done with care, and after checking if talos performance regresses badly. - assert_eq!(mem::size_of::<LinearGradient>(), 72, "LinearGradient size changed"); - assert_eq!(mem::size_of::<LinearGradientTemplate>(), 144, "LinearGradientTemplate size changed"); - assert_eq!(mem::size_of::<LinearGradientKey>(), 88, "LinearGradientKey size changed"); - - assert_eq!(mem::size_of::<RadialGradient>(), 72, "RadialGradient size changed"); - assert_eq!(mem::size_of::<RadialGradientTemplate>(), 144, "RadialGradientTemplate size changed"); - assert_eq!(mem::size_of::<RadialGradientKey>(), 96, "RadialGradientKey size changed"); - - assert_eq!(mem::size_of::<ConicGradient>(), 72, "ConicGradient size changed"); - assert_eq!(mem::size_of::<ConicGradientTemplate>(), 144, "ConicGradientTemplate size changed"); - assert_eq!(mem::size_of::<ConicGradientKey>(), 96, "ConicGradientKey size changed"); -} diff --git a/third_party/webrender/webrender/src/prim_store/gradient/radial.rs b/third_party/webrender/webrender/src/prim_store/gradient/radial.rs deleted file mode 100644 index 86051bddfa9..00000000000 --- a/third_party/webrender/webrender/src/prim_store/gradient/radial.rs +++ /dev/null @@ -1,536 +0,0 @@ -/* 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/. */ - -//! Radial gradients -//! -//! Specification: https://drafts.csswg.org/css-images-4/#radial-gradients -//! -//! Radial gradients are rendered via cached render tasks and composited with the image brush. - -use euclid::{vec2, size2}; -use api::{ExtendMode, GradientStop, PremultipliedColorF, ColorU}; -use api::units::*; -use crate::scene_building::IsVisible; -use crate::frame_builder::FrameBuildingState; -use crate::gpu_cache::{GpuCache, GpuCacheHandle}; -use crate::intern::{Internable, InternDebug, Handle as InternHandle}; -use crate::internal_types::LayoutPrimitiveInfo; -use crate::prim_store::{BrushSegment, GradientTileRange, InternablePrimitive}; -use crate::prim_store::{PrimitiveInstanceKind, PrimitiveOpacity}; -use crate::prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore}; -use crate::prim_store::{NinePatchDescriptor, PointKey, SizeKey, FloatKey}; -use crate::render_task::{RenderTask, RenderTaskKind}; -use crate::render_task_graph::RenderTaskId; -use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheKey, RenderTaskParent}; -use crate::picture::{SurfaceIndex}; - -use std::{hash, ops::{Deref, DerefMut}}; -use super::{ - stops_and_min_alpha, GradientStopKey, GradientGpuBlockBuilder, - apply_gradient_local_clip, -}; - -/// Hashable radial gradient parameters, for use during prim interning. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Clone, MallocSizeOf, PartialEq)] -pub struct RadialGradientParams { - pub start_radius: f32, - pub end_radius: f32, - pub ratio_xy: f32, -} - -impl Eq for RadialGradientParams {} - -impl hash::Hash for RadialGradientParams { - fn hash<H: hash::Hasher>(&self, state: &mut H) { - self.start_radius.to_bits().hash(state); - self.end_radius.to_bits().hash(state); - self.ratio_xy.to_bits().hash(state); - } -} - -/// Identifying key for a radial gradient. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)] -pub struct RadialGradientKey { - pub common: PrimKeyCommonData, - pub extend_mode: ExtendMode, - pub center: PointKey, - pub params: RadialGradientParams, - pub stretch_size: SizeKey, - pub stops: Vec<GradientStopKey>, - pub tile_spacing: SizeKey, - pub nine_patch: Option<Box<NinePatchDescriptor>>, -} - -impl RadialGradientKey { - pub fn new( - info: &LayoutPrimitiveInfo, - radial_grad: RadialGradient, - ) -> Self { - RadialGradientKey { - common: info.into(), - extend_mode: radial_grad.extend_mode, - center: radial_grad.center, - params: radial_grad.params, - stretch_size: radial_grad.stretch_size, - stops: radial_grad.stops, - tile_spacing: radial_grad.tile_spacing, - nine_patch: radial_grad.nine_patch, - } - } -} - -impl InternDebug for RadialGradientKey {} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(MallocSizeOf)] -#[derive(Debug)] -pub struct RadialGradientTemplate { - pub common: PrimTemplateCommonData, - pub extend_mode: ExtendMode, - pub params: RadialGradientParams, - pub center: DevicePoint, - pub task_size: DeviceIntSize, - pub scale: DeviceVector2D, - pub stretch_size: LayoutSize, - pub tile_spacing: LayoutSize, - pub brush_segments: Vec<BrushSegment>, - pub stops_opacity: PrimitiveOpacity, - pub stops: Vec<GradientStop>, - pub stops_handle: GpuCacheHandle, - pub src_color: Option<RenderTaskId>, -} - -impl Deref for RadialGradientTemplate { - type Target = PrimTemplateCommonData; - fn deref(&self) -> &Self::Target { - &self.common - } -} - -impl DerefMut for RadialGradientTemplate { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.common - } -} - -impl From<RadialGradientKey> for RadialGradientTemplate { - fn from(item: RadialGradientKey) -> Self { - let common = PrimTemplateCommonData::with_key_common(item.common); - let mut brush_segments = Vec::new(); - - if let Some(ref nine_patch) = item.nine_patch { - brush_segments = nine_patch.create_segments(common.prim_rect.size); - } - - let (stops, min_alpha) = stops_and_min_alpha(&item.stops); - - // Save opacity of the stops for use in - // selecting which pass this gradient - // should be drawn in. - let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha); - - let mut stretch_size: LayoutSize = item.stretch_size.into(); - stretch_size.width = stretch_size.width.min(common.prim_rect.size.width); - stretch_size.height = stretch_size.height.min(common.prim_rect.size.height); - - // Avoid rendering enormous gradients. Radial gradients are mostly made of soft transitions, - // so it is unlikely that rendering at a higher resolution that 1024 would produce noticeable - // differences, especially with 8 bits per channel. - const MAX_SIZE: f32 = 1024.0; - let mut task_size: DeviceSize = stretch_size.cast_unit(); - let mut scale = vec2(1.0, 1.0); - if task_size.width > MAX_SIZE { - scale.x = task_size.width/ MAX_SIZE; - task_size.width = MAX_SIZE; - } - if task_size.height > MAX_SIZE { - scale.y = task_size.height /MAX_SIZE; - task_size.height = MAX_SIZE; - } - - RadialGradientTemplate { - common, - center: DevicePoint::new(item.center.x, item.center.y), - extend_mode: item.extend_mode, - params: item.params, - stretch_size, - task_size: task_size.ceil().to_i32(), - scale, - tile_spacing: item.tile_spacing.into(), - brush_segments, - stops_opacity, - stops, - stops_handle: GpuCacheHandle::new(), - src_color: None, - } - } -} - -impl RadialGradientTemplate { - /// Update the GPU cache for a given primitive template. This may be called multiple - /// times per frame, by each primitive reference that refers to this interned - /// template. The initial request call to the GPU cache ensures that work is only - /// done if the cache entry is invalid (due to first use or eviction). - pub fn update( - &mut self, - frame_state: &mut FrameBuildingState, - parent_surface: SurfaceIndex, - ) { - if let Some(mut request) = - frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) { - // write_prim_gpu_blocks - request.push(PremultipliedColorF::WHITE); - request.push(PremultipliedColorF::WHITE); - request.push([ - self.stretch_size.width, - self.stretch_size.height, - 0.0, - 0.0, - ]); - - // write_segment_gpu_blocks - for segment in &self.brush_segments { - // has to match VECS_PER_SEGMENT - request.write_segment( - segment.local_rect, - segment.extra_data, - ); - } - } - - if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) { - GradientGpuBlockBuilder::build( - false, - &mut request, - &self.stops, - ); - } - - let task_size = self.task_size; - let cache_key = RadialGradientCacheKey { - size: task_size, - center: PointKey { x: self.center.x, y: self.center.y }, - scale: PointKey { x: self.scale.x, y: self.scale.y }, - start_radius: FloatKey(self.params.start_radius), - end_radius: FloatKey(self.params.end_radius), - ratio_xy: FloatKey(self.params.ratio_xy), - extend_mode: self.extend_mode, - stops: self.stops.iter().map(|stop| (*stop).into()).collect(), - }; - - let task_id = frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size: task_size, - kind: RenderTaskCacheKeyKind::RadialGradient(cache_key), - }, - frame_state.gpu_cache, - frame_state.rg_builder, - None, - false, - RenderTaskParent::Surface(parent_surface), - frame_state.surfaces, - |rg_builder| { - rg_builder.add().init(RenderTask::new_dynamic( - task_size, - RenderTaskKind::RadialGradient(RadialGradientTask { - extend_mode: self.extend_mode, - center: self.center, - scale: self.scale, - params: self.params.clone(), - stops: self.stops_handle, - }), - )) - } - ); - - self.src_color = Some(task_id); - - // Tile spacing is always handled by decomposing into separate draw calls so the - // primitive opacity is equivalent to stops opacity. This might change to being - // set to non-opaque in the presence of tile spacing if/when tile spacing is handled - // in the same way as with the image primitive. - self.opacity = self.stops_opacity; - } -} - -pub type RadialGradientDataHandle = InternHandle<RadialGradient>; - -#[derive(Debug, MallocSizeOf)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct RadialGradient { - pub extend_mode: ExtendMode, - pub center: PointKey, - pub params: RadialGradientParams, - pub stretch_size: SizeKey, - pub stops: Vec<GradientStopKey>, - pub tile_spacing: SizeKey, - pub nine_patch: Option<Box<NinePatchDescriptor>>, -} - -impl Internable for RadialGradient { - type Key = RadialGradientKey; - type StoreData = RadialGradientTemplate; - type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_RADIAL_GRADIENTS; -} - -impl InternablePrimitive for RadialGradient { - fn into_key( - self, - info: &LayoutPrimitiveInfo, - ) -> RadialGradientKey { - RadialGradientKey::new(info, self) - } - - fn make_instance_kind( - _key: RadialGradientKey, - data_handle: RadialGradientDataHandle, - _prim_store: &mut PrimitiveStore, - _reference_frame_relative_offset: LayoutVector2D, - ) -> PrimitiveInstanceKind { - PrimitiveInstanceKind::RadialGradient { - data_handle, - visible_tiles_range: GradientTileRange::empty(), - } - } -} - -impl IsVisible for RadialGradient { - fn is_visible(&self) -> bool { - true - } -} - -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct RadialGradientTask { - pub extend_mode: ExtendMode, - pub center: DevicePoint, - pub scale: DeviceVector2D, - pub params: RadialGradientParams, - pub stops: GpuCacheHandle, -} - -impl RadialGradientTask { - pub fn to_instance(&self, target_rect: &DeviceIntRect, gpu_cache: &mut GpuCache) -> RadialGradientInstance { - RadialGradientInstance { - task_rect: target_rect.to_f32(), - center: self.center, - scale: self.scale, - start_radius: self.params.start_radius, - end_radius: self.params.end_radius, - ratio_xy: self.params.ratio_xy, - extend_mode: self.extend_mode as i32, - gradient_stops_address: self.stops.as_int(gpu_cache), - } - } -} - -/// The per-instance shader input of a radial gradient render task. -/// -/// Must match the RADIAL_GRADIENT instance description in renderer/vertex.rs. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[repr(C)] -#[derive(Clone, Debug)] -pub struct RadialGradientInstance { - pub task_rect: DeviceRect, - pub center: DevicePoint, - pub scale: DeviceVector2D, - pub start_radius: f32, - pub end_radius: f32, - pub ratio_xy: f32, - pub extend_mode: i32, - pub gradient_stops_address: i32, -} - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct RadialGradientCacheKey { - pub size: DeviceIntSize, - pub center: PointKey, - pub scale: PointKey, - pub start_radius: FloatKey, - pub end_radius: FloatKey, - pub ratio_xy: FloatKey, - pub extend_mode: ExtendMode, - pub stops: Vec<GradientStopKey>, -} - -/// Avoid invoking the radial gradient shader on large areas where the color is -/// constant. -/// -/// If the extend mode is set to clamp, the "interesting" part -/// of the gradient is only in the bounds of the gradient's ellipse, and the rest -/// is the color of the last gradient stop. -/// -/// Sometimes we run into radial gradient with a small radius compared to the -/// primitive bounds, which means a large area of the primitive is a constant color -/// This function tries to detect that, potentially shrink the gradient primitive to only -/// the useful part and if needed insert solid color primitives around the gradient where -/// parts of it have been removed. -pub fn optimize_radial_gradient( - prim_rect: &mut LayoutRect, - stretch_size: &mut LayoutSize, - center: &mut LayoutPoint, - tile_spacing: &mut LayoutSize, - clip_rect: &LayoutRect, - radius: LayoutSize, - end_offset: f32, - extend_mode: ExtendMode, - stops: &[GradientStopKey], - solid_parts: &mut dyn FnMut(&LayoutRect, ColorU), -) { - let offset = apply_gradient_local_clip( - prim_rect, - stretch_size, - tile_spacing, - clip_rect - ); - - *center += offset; - - if extend_mode != ExtendMode::Clamp || stops.is_empty() { - return; - } - - // Bounding box of the "interesting" part of the gradient. - let min = prim_rect.origin + center.to_vector() - radius.to_vector() * end_offset; - let max = prim_rect.origin + center.to_vector() + radius.to_vector() * end_offset; - - // The (non-repeated) gradient primitive rect. - let gradient_rect = LayoutRect { - origin: prim_rect.origin, - size: *stretch_size, - }; - - // How much internal margin between the primitive bounds and the gradient's - // bounding rect (areas that are a constant color). - let mut l = (min.x - gradient_rect.min_x()).max(0.0).floor(); - let mut t = (min.y - gradient_rect.min_y()).max(0.0).floor(); - let mut r = (gradient_rect.max_x() - max.x).max(0.0).floor(); - let mut b = (gradient_rect.max_y() - max.y).max(0.0).floor(); - - let is_tiled = prim_rect.size.width > stretch_size.width + tile_spacing.width - || prim_rect.size.height > stretch_size.height + tile_spacing.height; - - let bg_color = stops.last().unwrap().color; - - if bg_color.a != 0 && is_tiled { - // If the primitive has repetitions, it's not enough to insert solid rects around it, - // so bail out. - return; - } - - // If the background is fully transparent, shrinking the primitive bounds as much as possible - // is always a win. If the background is not transparent, we have to insert solid rectangles - // around the shrunk parts. - // If the background is transparent and the primitive is tiled, the optimization may introduce - // tile spacing which forces the tiling to be manually decomposed. - // Either way, don't bother optimizing unless it saves a significant amount of pixels. - if bg_color.a != 0 || (is_tiled && tile_spacing.is_empty()) { - let threshold = 128.0; - if l < threshold { l = 0.0 } - if t < threshold { t = 0.0 } - if r < threshold { r = 0.0 } - if b < threshold { b = 0.0 } - } - - if l + t + r + b == 0.0 { - // No adjustment to make; - return; - } - - // Insert solid rectangles around the gradient, in the places where the primitive will be - // shrunk. - if bg_color.a != 0 { - if l != 0.0 && t != 0.0 { - let solid_rect = LayoutRect { - origin: gradient_rect.origin, - size: size2(l, t), - }; - solid_parts(&solid_rect, bg_color); - } - - if l != 0.0 && b != 0.0 { - let solid_rect = LayoutRect { - origin: gradient_rect.bottom_left() - vec2(0.0, b), - size: size2(l, b), - }; - solid_parts(&solid_rect, bg_color); - } - - if t != 0.0 && r != 0.0 { - let solid_rect = LayoutRect { - origin: gradient_rect.top_right() - vec2(r, 0.0), - size: size2(r, t), - }; - solid_parts(&solid_rect, bg_color); - } - - if r != 0.0 && b != 0.0 { - let solid_rect = LayoutRect { - origin: gradient_rect.bottom_right() - vec2(r, b), - size: size2(r, b), - }; - solid_parts(&solid_rect, bg_color); - } - - if l != 0.0 { - let solid_rect = LayoutRect { - origin: gradient_rect.origin + vec2(0.0, t), - size: size2(l, gradient_rect.size.height - t - b), - }; - solid_parts(&solid_rect, bg_color); - } - - if r != 0.0 { - let solid_rect = LayoutRect { - origin: gradient_rect.top_right() + vec2(-r, t), - size: size2(r, gradient_rect.size.height - t - b), - }; - solid_parts(&solid_rect, bg_color); - } - - if t != 0.0 { - let solid_rect = LayoutRect { - origin: gradient_rect.origin + vec2(l, 0.0), - size: size2(gradient_rect.size.width - l - r, t), - }; - solid_parts(&solid_rect, bg_color); - } - - if b != 0.0 { - let solid_rect = LayoutRect { - origin: gradient_rect.bottom_left() + vec2(l, -b), - size: size2(gradient_rect.size.width - l - r, b), - }; - solid_parts(&solid_rect, bg_color); - } - } - - // Shrink the gradient primitive. - - prim_rect.origin.x += l; - prim_rect.origin.y += t; - prim_rect.size.width -= l; - prim_rect.size.height -= t; - - stretch_size.width -= l + r; - stretch_size.height -= b + t; - - center.x -= l; - center.y -= t; - - tile_spacing.width += l + r; - tile_spacing.height += t + b; -} diff --git a/third_party/webrender/webrender/src/prim_store/image.rs b/third_party/webrender/webrender/src/prim_store/image.rs index 3e4ed4a896f..922bd5b80cd 100644 --- a/third_party/webrender/webrender/src/prim_store/image.rs +++ b/third_party/webrender/webrender/src/prim_store/image.rs @@ -3,40 +3,35 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{ - AlphaType, ColorDepth, ColorF, ColorU, ExternalImageData, ExternalImageType, - ImageKey as ApiImageKey, ImageBufferKind, ImageRendering, PremultipliedColorF, - RasterSpace, Shadow, YuvColorSpace, ColorRange, YuvFormat, + AlphaType, ColorDepth, ColorF, ColorU, + ImageKey as ApiImageKey, ImageRendering, + PremultipliedColorF, Shadow, YuvColorSpace, ColorRange, YuvFormat, }; use api::units::*; use crate::scene_building::{CreateShadow, IsVisible}; -use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, add_child_render_task}; +use crate::frame_builder::FrameBuildingState; use crate::gpu_cache::{GpuCache, GpuDataRequest}; use crate::intern::{Internable, InternDebug, Handle as InternHandle}; use crate::internal_types::{LayoutPrimitiveInfo}; -use crate::picture::SurfaceIndex; use crate::prim_store::{ - EdgeAaSegmentMask, PrimitiveInstanceKind, + EdgeAaSegmentMask, OpacityBindingIndex, PrimitiveInstanceKind, PrimitiveOpacity, PrimKey, PrimTemplate, PrimTemplateCommonData, PrimitiveStore, SegmentInstanceIndex, SizeKey, InternablePrimitive, }; use crate::render_target::RenderTargetKind; -use crate::render_task_graph::RenderTaskId; -use crate::render_task::RenderTask; +use crate::render_task::{BlitSource, RenderTask}; use crate::render_task_cache::{ - RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent + RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind }; -use crate::resource_cache::{ImageRequest, ImageProperties, ResourceCache}; +use crate::resource_cache::{ImageRequest, ResourceCache}; use crate::util::pack_as_float; -use crate::visibility::{PrimitiveVisibility, compute_conservative_visible_rect}; -use crate::spatial_tree::SpatialNodeIndex; -use crate::image_tiling; #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct VisibleImageTile { - pub src_color: RenderTaskId, + pub tile_offset: TileOffset, pub edge_flags: EdgeAaSegmentMask, pub local_rect: LayoutRect, pub local_clip_rect: LayoutRect, @@ -67,10 +62,10 @@ pub struct ImageCacheKey { #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] pub struct ImageInstance { + pub opacity_binding_index: OpacityBindingIndex, pub segment_instance_index: SegmentInstanceIndex, pub tight_local_clip_rect: LayoutRect, pub visible_tiles: Vec<VisibleImageTile>, - pub src_color: Option<RenderTaskId>, } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -101,6 +96,21 @@ impl ImageKey { impl InternDebug for ImageKey {} +// Where to find the texture data for an image primitive. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Debug, MallocSizeOf)] +pub enum ImageSource { + // A normal image - just reference the texture cache. + Default, + // An image that is pre-rendered into the texture cache + // via a render task. + Cache { + size: DeviceIntSize, + handle: Option<RenderTaskCacheEntryHandle>, + }, +} + #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Debug, MallocSizeOf)] @@ -109,6 +119,7 @@ pub struct ImageData { pub stretch_size: LayoutSize, pub tile_spacing: LayoutSize, pub color: ColorF, + pub source: ImageSource, pub image_rendering: ImageRendering, pub alpha_type: AlphaType, } @@ -120,6 +131,7 @@ impl From<Image> for ImageData { color: image.color.into(), stretch_size: image.stretch_size.into(), tile_spacing: image.tile_spacing.into(), + source: ImageSource::Default, image_rendering: image.image_rendering, alpha_type: image.alpha_type, } @@ -134,249 +146,113 @@ impl ImageData { pub fn update( &mut self, common: &mut PrimTemplateCommonData, - image_instance: &mut ImageInstance, - parent_surface: SurfaceIndex, - prim_spatial_node_index: SpatialNodeIndex, frame_state: &mut FrameBuildingState, - frame_context: &FrameBuildingContext, - visibility: &mut PrimitiveVisibility, ) { - - let image_properties = frame_state - .resource_cache - .get_image_properties(self.key); - - common.opacity = match &image_properties { - Some(properties) => { - if properties.descriptor.is_opaque() { - PrimitiveOpacity::from_alpha(self.color.a) - } else { - PrimitiveOpacity::translucent() - } - } - None => PrimitiveOpacity::opaque(), - }; - - if self.stretch_size.width >= common.prim_rect.size.width && - self.stretch_size.height >= common.prim_rect.size.height { - - common.may_need_repetition = false; + if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) { + self.write_prim_gpu_blocks(&mut request); } - let request = ImageRequest { - key: self.key, - rendering: self.image_rendering, - tile: None, - }; - - match image_properties { - // Non-tiled (most common) path. - Some(ImageProperties { tiling: None, ref descriptor, ref external_image, .. }) => { - let mut size = frame_state.resource_cache.request_image( - request, - frame_state.gpu_cache, - ); - - let orig_task_id = frame_state.rg_builder.add().init( - RenderTask::new_image(size, request) - ); - - // On some devices we cannot render from an ImageBufferKind::TextureExternal - // source using most shaders, so must peform a copy to a regular texture first. - let task_id = if frame_context.fb_config.external_images_require_copy - && matches!( - external_image, - Some(ExternalImageData { - image_type: ExternalImageType::TextureHandle( - ImageBufferKind::TextureExternal - ), - .. - }) - ) - { - let target_kind = if descriptor.format.bytes_per_pixel() == 1 { - RenderTargetKind::Alpha - } else { - RenderTargetKind::Color - }; - - let task_id = RenderTask::new_scaling( - orig_task_id, - frame_state.rg_builder, - target_kind, - size - ); - - add_child_render_task( - parent_surface, - task_id, - frame_state.surfaces, - frame_state.rg_builder, - ); - - task_id - } else { - orig_task_id - }; - - // Every frame, for cached items, we need to request the render - // task cache item. The closure will be invoked on the first - // time through, and any time the render task output has been - // evicted from the texture cache. - if self.tile_spacing == LayoutSize::zero() { - // Most common case. - image_instance.src_color = Some(task_id); - } else { - let padding = DeviceIntSideOffsets::new( - 0, - (self.tile_spacing.width * size.width as f32 / self.stretch_size.width) as i32, - (self.tile_spacing.height * size.height as f32 / self.stretch_size.height) as i32, - 0, - ); - - size.width += padding.horizontal(); - size.height += padding.vertical(); - - if padding != DeviceIntSideOffsets::zero() { - common.opacity = PrimitiveOpacity::translucent(); + common.opacity = { + let image_properties = frame_state + .resource_cache + .get_image_properties(self.key); + + match image_properties { + Some(image_properties) => { + let is_tiled = image_properties.tiling.is_some(); + + if self.tile_spacing != LayoutSize::zero() && !is_tiled { + self.source = ImageSource::Cache { + // Size in device-pixels we need to allocate in render task cache. + size: image_properties.descriptor.size.to_i32(), + handle: None, + }; } - let image_cache_key = ImageCacheKey { - request, - texel_rect: None, - }; - let target_kind = if descriptor.format.bytes_per_pixel() == 1 { - RenderTargetKind::Alpha - } else { - RenderTargetKind::Color + let mut is_opaque = image_properties.descriptor.is_opaque(); + let request = ImageRequest { + key: self.key, + rendering: self.image_rendering, + tile: None, }; - // Request a pre-rendered image task. - let cached_task_handle = frame_state.resource_cache.request_render_task( - RenderTaskCacheKey { - size, - kind: RenderTaskCacheKeyKind::Image(image_cache_key), - }, - frame_state.gpu_cache, - frame_state.rg_builder, - None, - descriptor.is_opaque(), - RenderTaskParent::Surface(parent_surface), - frame_state.surfaces, - |rg_builder| { - // Create a task to blit from the texture cache to - // a normal transient render task surface. - // TODO: figure out if/when we can do a blit instead. - let cache_to_target_task_id = RenderTask::new_scaling_with_padding( - task_id, - rg_builder, - target_kind, - size, - padding, + // Every frame, for cached items, we need to request the render + // task cache item. The closure will be invoked on the first + // time through, and any time the render task output has been + // evicted from the texture cache. + match self.source { + ImageSource::Cache { ref mut size, ref mut handle } => { + let padding = DeviceIntSideOffsets::new( + 0, + (self.tile_spacing.width * size.width as f32 / self.stretch_size.width) as i32, + (self.tile_spacing.height * size.height as f32 / self.stretch_size.height) as i32, + 0, ); - // Create a task to blit the rect from the child render - // task above back into the right spot in the persistent - // render target cache. - RenderTask::new_blit( - size, - cache_to_target_task_id, - rg_builder, - ) + size.width += padding.horizontal(); + size.height += padding.vertical(); + + is_opaque &= padding == DeviceIntSideOffsets::zero(); + + let image_cache_key = ImageCacheKey { + request, + texel_rect: None, + }; + let target_kind = if image_properties.descriptor.format.bytes_per_pixel() == 1 { + RenderTargetKind::Alpha + } else { + RenderTargetKind::Color + }; + + // Request a pre-rendered image task. + *handle = Some(frame_state.resource_cache.request_render_task( + RenderTaskCacheKey { + size: *size, + kind: RenderTaskCacheKeyKind::Image(image_cache_key), + }, + frame_state.gpu_cache, + frame_state.render_tasks, + None, + image_properties.descriptor.is_opaque(), + |render_tasks| { + // Create a task to blit from the texture cache to + // a normal transient render task surface. This will + // copy only the sub-rect, if specified. + // TODO: figure out if/when we can do a blit instead. + let cache_to_target_task_id = RenderTask::new_scaling_with_padding( + BlitSource::Image { key: image_cache_key }, + render_tasks, + target_kind, + *size, + padding, + ); + + // Create a task to blit the rect from the child render + // task above back into the right spot in the persistent + // render target cache. + render_tasks.add().init(RenderTask::new_blit( + *size, + BlitSource::RenderTask { + task_id: cache_to_target_task_id, + }, + )) + } + )); } - ); - - image_instance.src_color = Some(cached_task_handle); - } - } - // Tiled image path. - Some(ImageProperties { tiling: Some(tile_size), visible_rect, .. }) => { - // we'll have a source handle per visible tile instead. - image_instance.src_color = None; - - image_instance.visible_tiles.clear(); - // TODO: rename the blob's visible_rect into something that doesn't conflict - // with the terminology we use during culling since it's not really the same - // thing. - let active_rect = visible_rect; - - // Tighten the clip rect because decomposing the repeated image can - // produce primitives that are partially covering the original image - // rect and we want to clip these extra parts out. - let tight_clip_rect = visibility - .combined_local_clip_rect - .intersection(&common.prim_rect).unwrap(); - image_instance.tight_local_clip_rect = tight_clip_rect; - - let visible_rect = compute_conservative_visible_rect( - &visibility.clip_chain, - frame_state.current_dirty_region().combined, - prim_spatial_node_index, - frame_context.spatial_tree, - ); - - let base_edge_flags = edge_flags_for_tile_spacing(&self.tile_spacing); - - let stride = self.stretch_size + self.tile_spacing; - - // We are performing the decomposition on the CPU here, no need to - // have it in the shader. - common.may_need_repetition = false; - - let repetitions = image_tiling::repetitions( - &common.prim_rect, - &visible_rect, - stride, - ); - - for image_tiling::Repetition { origin, edge_flags } in repetitions { - let edge_flags = base_edge_flags | edge_flags; - - let layout_image_rect = LayoutRect { - origin, - size: self.stretch_size, - }; + ImageSource::Default => {} + } - let tiles = image_tiling::tiles( - &layout_image_rect, - &visible_rect, - &active_rect, - tile_size as i32, - ); - - for tile in tiles { - let request = request.with_tile(tile.offset); - let size = frame_state.resource_cache.request_image( - request, - frame_state.gpu_cache, - ); - - let task_id = frame_state.rg_builder.add().init( - RenderTask::new_image(size, request) - ); - - image_instance.visible_tiles.push(VisibleImageTile { - src_color: task_id, - edge_flags: tile.edge_flags & edge_flags, - local_rect: tile.rect, - local_clip_rect: tight_clip_rect, - }); + if is_opaque { + PrimitiveOpacity::from_alpha(self.color.a) + } else { + PrimitiveOpacity::translucent() } } - - if image_instance.visible_tiles.is_empty() { - // Mark as invisible - visibility.reset(); + None => { + PrimitiveOpacity::opaque() } } - None => { - image_instance.src_color = None; - } - } - - if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) { - self.write_prim_gpu_blocks(&mut request); - } + }; } pub fn write_prim_gpu_blocks(&self, request: &mut GpuDataRequest) { @@ -394,19 +270,6 @@ impl ImageData { } } -fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask { - let mut flags = EdgeAaSegmentMask::empty(); - - if tile_spacing.width > 0.0 { - flags |= EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT; - } - if tile_spacing.height > 0.0 { - flags |= EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM; - } - - flags -} - pub type ImageTemplate = PrimTemplate<ImageData>; impl From<ImageKey> for ImageTemplate { @@ -426,7 +289,6 @@ impl Internable for Image { type Key = ImageKey; type StoreData = ImageTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_IMAGES; } impl InternablePrimitive for Image { @@ -446,10 +308,10 @@ impl InternablePrimitive for Image { // TODO(gw): Refactor this to not need a separate image // instance (see ImageInstance struct). let image_instance_index = prim_store.images.push(ImageInstance { + opacity_binding_index: OpacityBindingIndex::INVALID, segment_instance_index: SegmentInstanceIndex::INVALID, tight_local_clip_rect: LayoutRect::zero(), visible_tiles: Vec::new(), - src_color: None, }); PrimitiveInstanceKind::Image { @@ -461,12 +323,7 @@ impl InternablePrimitive for Image { } impl CreateShadow for Image { - fn create_shadow( - &self, - shadow: &Shadow, - _: bool, - _: RasterSpace, - ) -> Self { + fn create_shadow(&self, shadow: &Shadow) -> Self { Image { tile_spacing: self.tile_spacing, stretch_size: self.stretch_size, @@ -520,7 +377,6 @@ impl InternDebug for YuvImageKey {} pub struct YuvImageData { pub color_depth: ColorDepth, pub yuv_key: [ApiImageKey; 3], - pub src_yuv: [Option<RenderTaskId>; 3], pub format: YuvFormat, pub color_space: YuvColorSpace, pub color_range: ColorRange, @@ -532,7 +388,6 @@ impl From<YuvImage> for YuvImageData { YuvImageData { color_depth: image.color_depth, yuv_key: image.yuv_key, - src_yuv: [None, None, None], format: image.format, color_space: image.color_space, color_range: image.color_range, @@ -551,30 +406,6 @@ impl YuvImageData { common: &mut PrimTemplateCommonData, frame_state: &mut FrameBuildingState, ) { - - self.src_yuv = [ None, None, None ]; - - let channel_num = self.format.get_plane_num(); - debug_assert!(channel_num <= 3); - for channel in 0 .. channel_num { - let request = ImageRequest { - key: self.yuv_key[channel], - rendering: self.image_rendering, - tile: None, - }; - - let size = frame_state.resource_cache.request_image( - request, - frame_state.gpu_cache, - ); - - let task_id = frame_state.rg_builder.add().init( - RenderTask::new_image(size, request) - ); - - self.src_yuv[channel] = Some(task_id); - } - if let Some(mut request) = frame_state.gpu_cache.request(&mut common.gpu_cache_handle) { self.write_prim_gpu_blocks(&mut request); }; @@ -631,7 +462,6 @@ impl Internable for YuvImage { type Key = YuvImageKey; type StoreData = YuvImageTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_YUV_IMAGES; } impl InternablePrimitive for YuvImage { @@ -673,9 +503,9 @@ fn test_struct_sizes() { // (b) You made a structure larger. This is not necessarily a problem, but should only // be done with care, and after checking if talos performance regresses badly. assert_eq!(mem::size_of::<Image>(), 32, "Image size changed"); - assert_eq!(mem::size_of::<ImageTemplate>(), 72, "ImageTemplate size changed"); + assert_eq!(mem::size_of::<ImageTemplate>(), 92, "ImageTemplate size changed"); assert_eq!(mem::size_of::<ImageKey>(), 52, "ImageKey size changed"); assert_eq!(mem::size_of::<YuvImage>(), 32, "YuvImage size changed"); - assert_eq!(mem::size_of::<YuvImageTemplate>(), 72, "YuvImageTemplate size changed"); + assert_eq!(mem::size_of::<YuvImageTemplate>(), 60, "YuvImageTemplate size changed"); assert_eq!(mem::size_of::<YuvImageKey>(), 52, "YuvImageKey size changed"); } diff --git a/third_party/webrender/webrender/src/prim_store/line_dec.rs b/third_party/webrender/webrender/src/prim_store/line_dec.rs index 496bab75691..84537454533 100644 --- a/third_party/webrender/webrender/src/prim_store/line_dec.rs +++ b/third_party/webrender/webrender/src/prim_store/line_dec.rs @@ -3,10 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{ - ColorF, ColorU, RasterSpace, + ColorF, ColorU, LineOrientation, LineStyle, PremultipliedColorF, Shadow, }; -use api::units::*; +use api::units::{Au, LayoutSizeAu, LayoutVector2D}; use crate::scene_building::{CreateShadow, IsVisible}; use crate::frame_builder::{FrameBuildingState}; use crate::gpu_cache::GpuDataRequest; @@ -126,7 +126,6 @@ impl intern::Internable for LineDecoration { type Key = LineDecorationKey; type StoreData = LineDecorationTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_LINE_DECORATIONS; } impl InternablePrimitive for LineDecoration { @@ -148,18 +147,13 @@ impl InternablePrimitive for LineDecoration { ) -> PrimitiveInstanceKind { PrimitiveInstanceKind::LineDecoration { data_handle, - render_task: None, + cache_handle: None, } } } impl CreateShadow for LineDecoration { - fn create_shadow( - &self, - shadow: &Shadow, - _: bool, - _: RasterSpace, - ) -> Self { + fn create_shadow(&self, shadow: &Shadow) -> Self { LineDecoration { color: shadow.color.into(), cache_key: self.cache_key.clone(), @@ -173,74 +167,6 @@ impl IsVisible for LineDecoration { } } -/// Choose the decoration mask tile size for a given line. -/// -/// Given a line with overall size `rect_size` and the given `orientation`, -/// return the dimensions of a single mask tile for the decoration pattern -/// described by `style` and `wavy_line_thickness`. -/// -/// If `style` is `Solid`, no mask tile is necessary; return `None`. The other -/// styles each have their own characteristic periods of repetition, so for each -/// one, this function returns a `LayoutSize` with the right aspect ratio and -/// whose specific size is convenient for the `cs_line_decoration.glsl` fragment -/// shader to work with. The shader uses a local coordinate space in which the -/// tile fills a rectangle with one corner at the origin, and with the size this -/// function returns. -/// -/// The returned size is not necessarily in pixels; device scaling and other -/// concerns can still affect the actual task size. -/// -/// Regardless of whether `orientation` is `Vertical` or `Horizontal`, the -/// `width` and `height` of the returned size are always horizontal and -/// vertical, respectively. -pub fn get_line_decoration_size( - rect_size: &LayoutSize, - orientation: LineOrientation, - style: LineStyle, - wavy_line_thickness: f32, -) -> Option<LayoutSize> { - let h = match orientation { - LineOrientation::Horizontal => rect_size.height, - LineOrientation::Vertical => rect_size.width, - }; - - // TODO(gw): The formulae below are based on the existing gecko and line - // shader code. They give reasonable results for most inputs, - // but could definitely do with a detailed pass to get better - // quality on a wider range of inputs! - // See nsCSSRendering::PaintDecorationLine in Gecko. - - let (parallel, perpendicular) = match style { - LineStyle::Solid => { - return None; - } - LineStyle::Dashed => { - let dash_length = (3.0 * h).min(64.0).max(1.0); - - (2.0 * dash_length, 4.0) - } - LineStyle::Dotted => { - let diameter = h.min(64.0).max(1.0); - let period = 2.0 * diameter; - - (period, diameter) - } - LineStyle::Wavy => { - let line_thickness = wavy_line_thickness.max(1.0); - let slope_length = h - line_thickness; - let flat_length = ((line_thickness - 1.0) * 2.0).max(1.0); - let approx_period = 2.0 * (slope_length + flat_length); - - (approx_period, h) - } - }; - - Some(match orientation { - LineOrientation::Horizontal => LayoutSize::new(parallel, perpendicular), - LineOrientation::Vertical => LayoutSize::new(perpendicular, parallel), - }) -} - #[test] #[cfg(target_pointer_width = "64")] fn test_struct_sizes() { diff --git a/third_party/webrender/webrender/src/prim_store/mod.rs b/third_party/webrender/webrender/src/prim_store/mod.rs index bc1db95d568..0a1514a9b35 100644 --- a/third_party/webrender/webrender/src/prim_store/mod.rs +++ b/third_party/webrender/webrender/src/prim_store/mod.rs @@ -2,35 +2,60 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BorderRadius, ClipMode, ColorF, ColorU, RasterSpace}; +use api::{BorderRadius, ClipMode, ColorF, ColorU}; use api::{ImageRendering, RepeatMode, PrimitiveFlags}; -use api::{PremultipliedColorF, PropertyBinding, Shadow}; -use api::{PrimitiveKeyKind, FillRule, POLYGON_CLIP_VERTEX_MAX}; +use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop}; +use api::{BoxShadowClipMode, LineStyle, LineOrientation, BorderStyle}; +use api::{PrimitiveKeyKind, ExtendMode, EdgeAaSegmentMask}; +use api::image_tiling::{self, Repetition}; use api::units::*; -use euclid::{SideOffsets2D, Size2D}; -use malloc_size_of::MallocSizeOf; -use crate::segment::EdgeAaSegmentMask; +use crate::border::{get_max_scale_for_border, build_border_instances}; use crate::border::BorderSegmentCacheKey; -use crate::clip::{ClipChainId, ClipSet}; -use crate::debug_item::DebugItem; +use crate::clip::{ClipStore}; +use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace}; +use crate::clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItemKind}; +use crate::debug_colors; +use crate::debug_render::DebugItem; use crate::scene_building::{CreateShadow, IsVisible}; -use crate::frame_builder::FrameBuildingState; +use euclid::{SideOffsets2D, Transform3D, Rect, Scale, Size2D, Point2D, Vector2D}; +use euclid::approxeq::ApproxEq; +use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState}; +use crate::frame_builder::{FrameVisibilityContext, FrameVisibilityState}; use crate::glyph_rasterizer::GlyphKey; -use crate::gpu_cache::{GpuCacheAddress, GpuCacheHandle, GpuDataRequest}; +use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks}; use crate::gpu_types::{BrushFlags}; use crate::intern; -use crate::picture::PicturePrimitive; +use crate::internal_types::PlaneSplitAnchor; +use malloc_size_of::MallocSizeOf; +use crate::picture::{PictureCompositeMode, PicturePrimitive, ClusterFlags, TileCacheLogger}; +use crate::picture::{PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedTiles, RasterConfig}; +use crate::prim_store::backdrop::BackdropDataHandle; +use crate::prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle}; +use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientCacheKey, GradientStopKey}; +use crate::prim_store::gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle, ConicGradientDataHandle}; +use crate::prim_store::image::{ImageDataHandle, ImageInstance, VisibleImageTile, YuvImageDataHandle}; +use crate::prim_store::line_dec::{LineDecorationDataHandle,MAX_LINE_DECORATION_RESOLUTION}; +use crate::prim_store::picture::PictureDataHandle; +use crate::prim_store::text_run::{TextRunDataHandle, TextRunPrimitive}; #[cfg(debug_assertions)] use crate::render_backend::{FrameId}; +use crate::render_backend::DataStores; use crate::render_task_graph::RenderTaskId; -use crate::resource_cache::ImageProperties; +use crate::render_task_cache::{RenderTaskCacheKeyKind, RenderTaskCacheEntryHandle, RenderTaskCacheKey, to_cache_size}; +use crate::render_task::RenderTask; +use crate::renderer::{MAX_VERTEX_TEXTURE_WIDTH}; +use crate::resource_cache::{ImageProperties, ImageRequest}; use crate::scene::SceneProperties; -use std::{hash, ops, u32, usize}; +use crate::segment::SegmentBuilder; +use std::{cmp, fmt, hash, ops, u32, usize, mem}; #[cfg(debug_assertions)] use std::sync::atomic::{AtomicUsize, Ordering}; -use crate::util::Recycler; -use crate::internal_types::LayoutPrimitiveInfo; -use crate::visibility::PrimitiveVisibility; +use crate::storage; +use crate::texture_cache::TEXTURE_REGION_DIMENSIONS; +use crate::util::{MatrixHelpers, MaxRect, Recycler, ScaleOffset, RectHelpers, PointHelpers}; +use crate::util::{clamp_to_scale_factor, pack_as_float, project_rect, raster_rect_to_device_pixels}; +use crate::internal_types::{LayoutPrimitiveInfo, Filter}; +use smallvec::SmallVec; pub mod backdrop; pub mod borders; @@ -41,18 +66,6 @@ pub mod picture; pub mod text_run; pub mod interned; -mod storage; - -use backdrop::BackdropDataHandle; -use borders::{ImageBorderDataHandle, NormalBorderDataHandle}; -use gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle, ConicGradientDataHandle}; -use image::{ImageDataHandle, ImageInstance, YuvImageDataHandle}; -use line_dec::LineDecorationDataHandle; -use picture::PictureDataHandle; -use text_run::{TextRunDataHandle, TextRunPrimitive}; - -pub const VECS_PER_SEGMENT: usize = 2; - /// Counter for unique primitive IDs for debug tracing. #[cfg(debug_assertions)] static NEXT_PRIM_ID: AtomicUsize = AtomicUsize::new(0); @@ -69,6 +82,11 @@ pub fn register_prim_chase_id(id: PrimitiveDebugId) { pub fn register_prim_chase_id(_: PrimitiveDebugId) { } +const MIN_BRUSH_SPLIT_AREA: f32 = 128.0 * 128.0; +pub const VECS_PER_SEGMENT: usize = 2; + +const MAX_MASK_SIZE: f32 = 4096.0; + #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Debug, Copy, Clone, MallocSizeOf)] @@ -98,6 +116,234 @@ impl PrimitiveOpacity { } } +#[derive(Clone, Debug)] +pub struct SpaceSnapper { + pub ref_spatial_node_index: SpatialNodeIndex, + current_target_spatial_node_index: SpatialNodeIndex, + snapping_transform: Option<ScaleOffset>, + pub device_pixel_scale: DevicePixelScale, +} + +impl SpaceSnapper { + pub fn new( + ref_spatial_node_index: SpatialNodeIndex, + device_pixel_scale: DevicePixelScale, + ) -> Self { + SpaceSnapper { + ref_spatial_node_index, + current_target_spatial_node_index: SpatialNodeIndex::INVALID, + snapping_transform: None, + device_pixel_scale, + } + } + + pub fn new_with_target( + ref_spatial_node_index: SpatialNodeIndex, + target_node_index: SpatialNodeIndex, + device_pixel_scale: DevicePixelScale, + spatial_tree: &SpatialTree, + ) -> Self { + let mut snapper = SpaceSnapper { + ref_spatial_node_index, + current_target_spatial_node_index: SpatialNodeIndex::INVALID, + snapping_transform: None, + device_pixel_scale, + }; + + snapper.set_target_spatial_node(target_node_index, spatial_tree); + snapper + } + + pub fn set_target_spatial_node( + &mut self, + target_node_index: SpatialNodeIndex, + spatial_tree: &SpatialTree, + ) { + if target_node_index == self.current_target_spatial_node_index { + return + } + + let ref_spatial_node = &spatial_tree.spatial_nodes[self.ref_spatial_node_index.0 as usize]; + let target_spatial_node = &spatial_tree.spatial_nodes[target_node_index.0 as usize]; + + self.current_target_spatial_node_index = target_node_index; + self.snapping_transform = match (ref_spatial_node.snapping_transform, target_spatial_node.snapping_transform) { + (Some(ref ref_scale_offset), Some(ref target_scale_offset)) => { + Some(ref_scale_offset + .inverse() + .accumulate(target_scale_offset) + .scale(self.device_pixel_scale.0)) + } + _ => None, + }; + } + + pub fn snap_rect<F>(&self, rect: &Rect<f32, F>) -> Rect<f32, F> where F: fmt::Debug { + debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID); + match self.snapping_transform { + Some(ref scale_offset) => { + let snapped_device_rect : DeviceRect = scale_offset.map_rect(rect).snap(); + scale_offset.unmap_rect(&snapped_device_rect) + } + None => *rect, + } + } + + pub fn snap_point<F>(&self, point: &Point2D<f32, F>) -> Point2D<f32, F> where F: fmt::Debug { + debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID); + match self.snapping_transform { + Some(ref scale_offset) => { + let snapped_device_vector : DevicePoint = scale_offset.map_point(point).snap(); + scale_offset.unmap_point(&snapped_device_vector) + } + None => *point, + } + } + + pub fn snap_size<F>(&self, size: &Size2D<f32, F>) -> Size2D<f32, F> where F: fmt::Debug { + debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID); + match self.snapping_transform { + Some(ref scale_offset) => { + let rect = Rect::<f32, F>::new(Point2D::<f32, F>::zero(), *size); + let snapped_device_rect : DeviceRect = scale_offset.map_rect(&rect).snap(); + scale_offset.unmap_rect(&snapped_device_rect).size + } + None => *size, + } + } +} + +#[derive(Debug, Clone)] +pub struct SpaceMapper<F, T> { + kind: CoordinateSpaceMapping<F, T>, + pub ref_spatial_node_index: SpatialNodeIndex, + pub current_target_spatial_node_index: SpatialNodeIndex, + pub bounds: Rect<f32, T>, + visible_face: VisibleFace, +} + +impl<F, T> SpaceMapper<F, T> where F: fmt::Debug { + pub fn new( + ref_spatial_node_index: SpatialNodeIndex, + bounds: Rect<f32, T>, + ) -> Self { + SpaceMapper { + kind: CoordinateSpaceMapping::Local, + ref_spatial_node_index, + current_target_spatial_node_index: ref_spatial_node_index, + bounds, + visible_face: VisibleFace::Front, + } + } + + pub fn new_with_target( + ref_spatial_node_index: SpatialNodeIndex, + target_node_index: SpatialNodeIndex, + bounds: Rect<f32, T>, + spatial_tree: &SpatialTree, + ) -> Self { + let mut mapper = Self::new(ref_spatial_node_index, bounds); + mapper.set_target_spatial_node(target_node_index, spatial_tree); + mapper + } + + pub fn set_target_spatial_node( + &mut self, + target_node_index: SpatialNodeIndex, + spatial_tree: &SpatialTree, + ) { + if target_node_index == self.current_target_spatial_node_index { + return + } + + let ref_spatial_node = &spatial_tree.spatial_nodes[self.ref_spatial_node_index.0 as usize]; + let target_spatial_node = &spatial_tree.spatial_nodes[target_node_index.0 as usize]; + + self.kind = if self.ref_spatial_node_index == target_node_index { + CoordinateSpaceMapping::Local + } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id { + let scale_offset = ref_spatial_node.content_transform + .inverse() + .accumulate(&target_spatial_node.content_transform); + CoordinateSpaceMapping::ScaleOffset(scale_offset) + } else { + let transform = spatial_tree + .get_relative_transform(target_node_index, self.ref_spatial_node_index) + .into_transform() + .with_source::<F>() + .with_destination::<T>(); + CoordinateSpaceMapping::Transform(transform) + }; + + self.visible_face = self.kind.visible_face(); + self.current_target_spatial_node_index = target_node_index; + } + + pub fn get_transform(&self) -> Transform3D<f32, F, T> { + match self.kind { + CoordinateSpaceMapping::Local => { + Transform3D::identity() + } + CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { + scale_offset.to_transform() + } + CoordinateSpaceMapping::Transform(transform) => { + transform + } + } + } + + pub fn unmap(&self, rect: &Rect<f32, T>) -> Option<Rect<f32, F>> { + match self.kind { + CoordinateSpaceMapping::Local => { + Some(rect.cast_unit()) + } + CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { + Some(scale_offset.unmap_rect(rect)) + } + CoordinateSpaceMapping::Transform(ref transform) => { + transform.inverse_rect_footprint(rect) + } + } + } + + pub fn map(&self, rect: &Rect<f32, F>) -> Option<Rect<f32, T>> { + match self.kind { + CoordinateSpaceMapping::Local => { + Some(rect.cast_unit()) + } + CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { + Some(scale_offset.map_rect(rect)) + } + CoordinateSpaceMapping::Transform(ref transform) => { + match project_rect(transform, rect, &self.bounds) { + Some(bounds) => { + Some(bounds) + } + None => { + warn!("parent relative transform can't transform the primitive rect for {:?}", rect); + None + } + } + } + } + } + + pub fn map_vector(&self, v: Vector2D<f32, F>) -> Vector2D<f32, T> { + match self.kind { + CoordinateSpaceMapping::Local => { + v.cast_unit() + } + CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { + scale_offset.map_vector(&v) + } + CoordinateSpaceMapping::Transform(ref transform) => { + transform.transform_vector2d(v) + } + } + } +} + /// For external images, it's not possible to know the /// UV coords of the image (or the image data itself) /// until the render thread receives the frame and issues @@ -129,6 +375,21 @@ impl ClipTaskIndex { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PictureIndex(pub usize); +impl GpuCacheHandle { + pub fn as_int(self, gpu_cache: &GpuCache) -> i32 { + gpu_cache.get_address(&self).as_int() + } +} + +impl GpuCacheAddress { + pub fn as_int(self) -> i32 { + // TODO(gw): Temporarily encode GPU Cache addresses as a single int. + // In the future, we can change the PrimitiveInstanceData struct + // to use 2x u16 for the vertex attribute instead of an i32. + self.v as i32 * MAX_VERTEX_TEXTURE_WIDTH as i32 + self.u as i32 + } +} + #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Copy, Debug, Clone, MallocSizeOf, PartialEq)] @@ -210,46 +471,6 @@ impl From<WorldRect> for RectangleKey { } } -/// To create a fixed-size representation of a polygon, we use a fixed -/// number of points. Our initialization method restricts us to values -/// <= 32. If our constant POLYGON_CLIP_VERTEX_MAX is > 32, the Rust -/// compiler will complain. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Copy, Debug, Clone, Hash, MallocSizeOf, PartialEq)] -pub struct PolygonKey { - pub point_count: u8, - pub points: [PointKey; POLYGON_CLIP_VERTEX_MAX], - pub fill_rule: FillRule, -} - -impl PolygonKey { - pub fn new( - points_layout: &Vec<LayoutPoint>, - fill_rule: FillRule, - ) -> Self { - // We have to fill fixed-size arrays with data from a Vec. - // We'll do this by initializing the arrays to known-good - // values then overwriting those values as long as our - // iterator provides values. - let mut points: [PointKey; POLYGON_CLIP_VERTEX_MAX] = [PointKey { x: 0.0, y: 0.0}; POLYGON_CLIP_VERTEX_MAX]; - - let mut point_count: u8 = 0; - for (src, dest) in points_layout.iter().zip(points.iter_mut()) { - *dest = (*src as LayoutPoint).into(); - point_count = point_count + 1; - } - - PolygonKey { - point_count, - points, - fill_rule, - } - } -} - -impl Eq for PolygonKey {} - /// A hashable SideOffset2D that can be used in primitive keys. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -426,21 +647,6 @@ impl From<WorldPoint> for PointKey { } } -/// A hashable float for using as a key during primitive interning. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Copy, Clone, MallocSizeOf, PartialEq)] -pub struct FloatKey(f32); - -impl Eq for FloatKey {} - -impl hash::Hash for FloatKey { - fn hash<H: hash::Hasher>(&self, state: &mut H) { - self.0.to_bits().hash(state); - } -} - - #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash)] @@ -500,25 +706,6 @@ pub enum PrimitiveTemplateKind { Clear, } -impl PrimitiveTemplateKind { - /// Write any GPU blocks for the primitive template to the given request object. - pub fn write_prim_gpu_blocks( - &self, - request: &mut GpuDataRequest, - scene_properties: &SceneProperties, - ) { - match *self { - PrimitiveTemplateKind::Clear => { - // Opaque black with operator dest out - request.push(PremultipliedColorF::BLACK); - } - PrimitiveTemplateKind::Rectangle { ref color, .. } => { - request.push(scene_properties.resolve_color(color).premultiplied()) - } - } - } -} - /// Construct the primitive template data from a primitive key. This /// is invoked when a primitive key is created and the interner /// doesn't currently contain a primitive with this key. @@ -540,7 +727,6 @@ impl From<PrimitiveKeyKind> for PrimitiveTemplateKind { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(MallocSizeOf)] -#[derive(Debug)] pub struct PrimTemplateCommonData { pub flags: PrimitiveFlags, pub may_need_repetition: bool, @@ -603,6 +789,25 @@ impl From<PrimitiveKey> for PrimitiveTemplate { } } +impl PrimitiveTemplateKind { + /// Write any GPU blocks for the primitive template to the given request object. + fn write_prim_gpu_blocks( + &self, + request: &mut GpuDataRequest, + scene_properties: &SceneProperties, + ) { + match *self { + PrimitiveTemplateKind::Clear => { + // Opaque black with operator dest out + request.push(PremultipliedColorF::BLACK); + } + PrimitiveTemplateKind::Rectangle { ref color, .. } => { + request.push(scene_properties.resolve_color(color).premultiplied()) + } + } + } +} + impl PrimitiveTemplate { /// Update the GPU cache for a given primitive template. This may be called multiple /// times per frame, by each primitive reference that refers to this interned @@ -634,7 +839,6 @@ impl intern::Internable for PrimitiveKeyKind { type Key = PrimitiveKey; type StoreData = PrimitiveTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_PRIMITIVES; } impl InternablePrimitive for PrimitiveKeyKind { @@ -666,6 +870,7 @@ impl InternablePrimitive for PrimitiveKeyKind { }; PrimitiveInstanceKind::Rectangle { data_handle, + opacity_binding_index: OpacityBindingIndex::INVALID, segment_instance_index: SegmentInstanceIndex::INVALID, color_binding_index, } @@ -674,6 +879,44 @@ impl InternablePrimitive for PrimitiveKeyKind { } } +// Maintains a list of opacity bindings that have been collapsed into +// the color of a single primitive. This is an important optimization +// that avoids allocating an intermediate surface for most common +// uses of opacity filters. +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +pub struct OpacityBinding { + pub bindings: Vec<PropertyBinding<f32>>, + pub current: f32, +} + +impl OpacityBinding { + pub fn new() -> OpacityBinding { + OpacityBinding { + bindings: Vec::new(), + current: 1.0, + } + } + + // Add a new opacity value / binding to the list + pub fn push(&mut self, binding: PropertyBinding<f32>) { + self.bindings.push(binding); + } + + // Resolve the current value of each opacity binding, and + // store that as a single combined opacity. + pub fn update(&mut self, scene_properties: &SceneProperties) { + let mut new_opacity = 1.0; + + for binding in &self.bindings { + let opacity = scene_properties.resolve_float(binding); + new_opacity = new_opacity * opacity; + } + + self.current = new_opacity; + } +} + #[derive(Debug, MallocSizeOf)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -690,6 +933,13 @@ pub struct VisibleGradientTile { pub local_clip_rect: LayoutRect, } +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +pub struct CachedGradientSegment { + pub handle: RenderTaskCacheEntryHandle, + pub local_rect: LayoutRect, +} + /// Information about how to cache a border segment, /// along with the current render task cache entry. #[cfg_attr(feature = "capture", derive(Serialize))] @@ -739,21 +989,92 @@ impl BrushSegment { brush_flags, } } + + /// Write out to the clip mask instances array the correct clip mask + /// config for this segment. + pub fn update_clip_task( + &self, + clip_chain: Option<&ClipChainInstance>, + prim_bounding_rect: WorldRect, + root_spatial_node_index: SpatialNodeIndex, + surface_index: SurfaceIndex, + pic_state: &mut PictureState, + frame_context: &FrameBuildingContext, + frame_state: &mut FrameBuildingState, + clip_data_store: &mut ClipDataStore, + unclipped: &DeviceRect, + device_pixel_scale: DevicePixelScale, + ) -> ClipMaskKind { + match clip_chain { + Some(clip_chain) => { + if !clip_chain.needs_mask || + (!self.may_need_clip_mask && !clip_chain.has_non_local_clips) { + return ClipMaskKind::None; + } + + let segment_world_rect = match pic_state.map_pic_to_world.map(&clip_chain.pic_clip_rect) { + Some(rect) => rect, + None => return ClipMaskKind::Clipped, + }; + + let segment_world_rect = match segment_world_rect.intersection(&prim_bounding_rect) { + Some(rect) => rect, + None => return ClipMaskKind::Clipped, + }; + + // Get a minimal device space rect, clipped to the screen that we + // need to allocate for the clip mask, as well as interpolated + // snap offsets. + let device_rect = match get_clipped_device_rect( + unclipped, + &pic_state.map_raster_to_world, + segment_world_rect, + device_pixel_scale, + ) { + Some(info) => info, + None => { + return ClipMaskKind::Clipped; + } + }; + + let (device_rect, device_pixel_scale) = adjust_mask_scale_for_max_size(device_rect, device_pixel_scale); + + let clip_task_id = RenderTask::new_mask( + device_rect.to_i32(), + clip_chain.clips_range, + root_spatial_node_index, + frame_state.clip_store, + frame_state.gpu_cache, + frame_state.resource_cache, + frame_state.render_tasks, + clip_data_store, + device_pixel_scale, + frame_context.fb_config, + ); + let port = frame_state + .surfaces[surface_index.0] + .render_tasks + .unwrap_or_else(|| panic!("bug: no task for surface {:?}", surface_index)) + .port; + frame_state.render_tasks.add_dependency(port, clip_task_id); + ClipMaskKind::Mask(clip_task_id) + } + None => { + ClipMaskKind::Clipped + } + } + } } -#[derive(Debug, Clone)] +#[derive(Debug)] #[repr(C)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] struct ClipRect { rect: LayoutRect, mode: f32, } -#[derive(Debug, Clone)] +#[derive(Debug)] #[repr(C)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] struct ClipCorner { rect: LayoutRect, outer_radius_x: f32, @@ -762,7 +1083,23 @@ struct ClipCorner { inner_radius_y: f32, } +impl ToGpuBlocks for ClipCorner { + fn write_gpu_blocks(&self, mut request: GpuDataRequest) { + self.write(&mut request) + } +} + impl ClipCorner { + fn write(&self, request: &mut GpuDataRequest) { + request.push(self.rect); + request.push([ + self.outer_radius_x, + self.outer_radius_y, + self.inner_radius_x, + self.inner_radius_y, + ]); + } + fn uniform(rect: LayoutRect, outer_radius: f32, inner_radius: f32) -> ClipCorner { ClipCorner { rect, @@ -774,10 +1111,25 @@ impl ClipCorner { } } -#[derive(Debug, Clone)] +#[derive(Debug)] #[repr(C)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct ImageMaskData { + /// The local size of the whole masked area. + pub local_mask_size: LayoutSize, +} + +impl ToGpuBlocks for ImageMaskData { + fn write_gpu_blocks(&self, mut request: GpuDataRequest) { + request.push([ + self.local_mask_size.width, + self.local_mask_size.height, + 0.0, + 0.0, + ]); + } +} + +#[derive(Debug)] pub struct ClipData { rect: ClipRect, top_left: ClipCorner, @@ -906,6 +1258,19 @@ impl ClipData { ), } } + + pub fn write(&self, request: &mut GpuDataRequest) { + request.push(self.rect.rect); + request.push([self.rect.mode, 0.0, 0.0, 0.0]); + for corner in &[ + &self.top_left, + &self.top_right, + &self.bottom_left, + &self.bottom_right, + ] { + corner.write(request); + } + } } /// A hashable descriptor for nine-patches, used by image and @@ -954,8 +1319,6 @@ impl CreateShadow for PrimitiveKeyKind { fn create_shadow( &self, shadow: &Shadow, - _: bool, - _: RasterSpace, ) -> PrimitiveKeyKind { match *self { PrimitiveKeyKind::Rectangle { .. } => { @@ -975,7 +1338,7 @@ impl CreateShadow for PrimitiveKeyKind { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct PrimitiveDebugId(pub usize); -#[derive(Debug)] +#[derive(Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] pub enum PrimitiveInstanceKind { /// Direct reference to a Picture @@ -1005,12 +1368,12 @@ pub enum PrimitiveInstanceKind { // the things we store here in the instance, and // use them directly. This will remove cache_handle, // but also the opacity, clip_task_id etc below. - render_task: Option<RenderTaskId>, + cache_handle: Option<RenderTaskCacheEntryHandle>, }, NormalBorder { /// Handle to the common interned data for this primitive. data_handle: NormalBorderDataHandle, - render_task_ids: storage::Range<RenderTaskId>, + cache_handles: storage::Range<RenderTaskCacheEntryHandle>, }, ImageBorder { /// Handle to the common interned data for this primitive. @@ -1019,6 +1382,7 @@ pub enum PrimitiveInstanceKind { Rectangle { /// Handle to the common interned data for this primitive. data_handle: PrimitiveDataHandle, + opacity_binding_index: OpacityBindingIndex, segment_instance_index: SegmentInstanceIndex, color_binding_index: ColorBindingIndex, }, @@ -1034,19 +1398,10 @@ pub enum PrimitiveInstanceKind { image_instance_index: ImageInstanceIndex, is_compositor_surface: bool, }, - /// Always rendered directly into the picture. This tends to be - /// faster with SWGL. LinearGradient { /// Handle to the common interned data for this primitive. data_handle: LinearGradientDataHandle, - visible_tiles_range: GradientTileRange, - }, - /// Always rendered via a cached render task. Usually faster with - /// a GPU. - CachedLinearGradient { - /// Handle to the common interned data for this primitive. - data_handle: LinearGradientDataHandle, - visible_tiles_range: GradientTileRange, + gradient_index: LinearGradientIndex, }, RadialGradient { /// Handle to the common interned data for this primitive. @@ -1069,7 +1424,108 @@ pub enum PrimitiveInstanceKind { }, } -#[derive(Debug)] +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +pub struct PrimitiveVisibilityIndex(pub u32); + +impl PrimitiveVisibilityIndex { + pub const INVALID: PrimitiveVisibilityIndex = PrimitiveVisibilityIndex(u32::MAX); +} + +/// A bit mask describing which dirty regions a primitive is visible in. +/// A value of 0 means not visible in any region, while a mask of 0xffff +/// would be considered visible in all regions. +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct PrimitiveVisibilityMask { + bits: u16, +} + +impl PrimitiveVisibilityMask { + /// Construct a default mask, where no regions are considered visible + pub fn empty() -> Self { + PrimitiveVisibilityMask { + bits: 0, + } + } + + pub fn all() -> Self { + PrimitiveVisibilityMask { + bits: !0, + } + } + + pub fn include(&mut self, other: PrimitiveVisibilityMask) { + self.bits |= other.bits; + } + + pub fn intersects(&self, other: PrimitiveVisibilityMask) -> bool { + (self.bits & other.bits) != 0 + } + + /// Mark a given region index as visible + pub fn set_visible(&mut self, region_index: usize) { + debug_assert!(region_index < PrimitiveVisibilityMask::MAX_DIRTY_REGIONS); + self.bits |= 1 << region_index; + } + + /// Returns true if there are no visible regions + pub fn is_empty(&self) -> bool { + self.bits == 0 + } + + /// The maximum number of supported dirty regions. + pub const MAX_DIRTY_REGIONS: usize = 8 * mem::size_of::<PrimitiveVisibilityMask>(); +} + +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))] +pub struct PrimitiveVisibility { + /// The clip chain instance that was built for this primitive. + pub clip_chain: ClipChainInstance, + + /// The current world rect, clipped to screen / dirty rect boundaries. + // TODO(gw): This is only used by a small number of primitives. + // It's probably faster to not store this and recalculate + // on demand in those cases? + pub clipped_world_rect: WorldRect, + + /// An index into the clip task instances array in the primitive + /// store. If this is ClipTaskIndex::INVALID, then the primitive + /// has no clip mask. Otherwise, it may store the offset of the + /// global clip mask task for this primitive, or the first of + /// 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, + + /// The current combined local clip for this primitive, from + /// the primitive local clip above and the current clip chain. + pub combined_local_clip_rect: LayoutRect, +} + +#[derive(Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] pub struct PrimitiveInstance { /// Identifies the kind of primitive this @@ -1078,6 +1534,9 @@ pub struct PrimitiveInstance { /// can be found. pub kind: PrimitiveInstanceKind, + /// Local space clip rect for this instance + pub local_clip_rect: LayoutRect, + #[cfg(debug_assertions)] pub id: PrimitiveDebugId, @@ -1086,14 +1545,12 @@ pub struct PrimitiveInstance { #[cfg(debug_assertions)] pub prepared_frame_id: FrameId, - /// All information and state related to clip(s) for this primitive - pub clip_set: ClipSet, + /// If this primitive is visible, an index into the instance + /// visibility scratch buffer. If not visible, INVALID. + pub visibility_info: PrimitiveVisibilityIndex, - /// Information related to the current visibility state of this - /// primitive. - // TODO(gw): Currently built each frame, but can be retained. - // TODO(gw): Remove clipped_world_rect (use tile bounds to determine vis flags) - pub vis: PrimitiveVisibility, + /// ID of the clip chain that this primitive is clipped by. + pub clip_chain_id: ClipChainId, } impl PrimitiveInstance { @@ -1103,26 +1560,20 @@ impl PrimitiveInstance { clip_chain_id: ClipChainId, ) -> Self { PrimitiveInstance { + local_clip_rect, kind, #[cfg(debug_assertions)] prepared_frame_id: FrameId::INVALID, #[cfg(debug_assertions)] id: PrimitiveDebugId(NEXT_PRIM_ID.fetch_add(1, Ordering::Relaxed)), - vis: PrimitiveVisibility::new(), - clip_set: ClipSet { - local_clip_rect, - clip_chain_id, - }, + visibility_info: PrimitiveVisibilityIndex::INVALID, + clip_chain_id, } } // Reset any pre-frame state for this primitive. pub fn reset(&mut self) { - self.vis.reset(); - } - - pub fn clear_visibility(&mut self) { - self.vis.reset(); + self.visibility_info = PrimitiveVisibilityIndex::INVALID; } #[cfg(debug_assertions)] @@ -1153,9 +1604,6 @@ impl PrimitiveInstance { PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { data_handle.uid() } - PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => { - data_handle.uid() - } PrimitiveInstanceKind::NormalBorder { data_handle, .. } => { data_handle.uid() } @@ -1191,9 +1639,11 @@ pub struct SegmentedInstance { pub type GlyphKeyStorage = storage::Storage<GlyphKey>; pub type TextRunIndex = storage::Index<TextRunPrimitive>; pub type TextRunStorage = storage::Storage<TextRunPrimitive>; +pub type OpacityBindingIndex = storage::Index<OpacityBinding>; +pub type OpacityBindingStorage = storage::Storage<OpacityBinding>; pub type ColorBindingIndex = storage::Index<PropertyBinding<ColorU>>; pub type ColorBindingStorage = storage::Storage<PropertyBinding<ColorU>>; -pub type BorderHandleStorage = storage::Storage<RenderTaskId>; +pub type BorderHandleStorage = storage::Storage<RenderTaskCacheEntryHandle>; pub type SegmentStorage = storage::Storage<BrushSegment>; pub type SegmentsRange = storage::Range<BrushSegment>; pub type SegmentInstanceStorage = storage::Storage<SegmentedInstance>; @@ -1202,6 +1652,7 @@ pub type ImageInstanceStorage = storage::Storage<ImageInstance>; pub type ImageInstanceIndex = storage::Index<ImageInstance>; pub type GradientTileStorage = storage::Storage<VisibleGradientTile>; pub type GradientTileRange = storage::Range<VisibleGradientTile>; +pub type LinearGradientIndex = storage::Index<LinearGradientPrimitive>; pub type LinearGradientStorage = storage::Storage<LinearGradientPrimitive>; /// Contains various vecs of data that is used only during frame building, @@ -1234,12 +1685,19 @@ pub struct PrimitiveScratchBuffer { /// per-tile information. pub gradient_tiles: GradientTileStorage, + /// List of the visibility information for currently visible primitives. + pub prim_info: Vec<PrimitiveVisibility>, + + /// List of dirty regions for the cached pictures in this document, used to + /// verify invalidation in wrench reftests. Only collected in testing. + pub recorded_dirty_regions: Vec<RecordedDirtyRegion>, + /// List of debug display items for rendering. pub debug_items: Vec<DebugItem>, } -impl Default for PrimitiveScratchBuffer { - fn default() -> Self { +impl PrimitiveScratchBuffer { + pub fn new() -> Self { PrimitiveScratchBuffer { clip_mask_instances: Vec::new(), glyph_keys: GlyphKeyStorage::new(0), @@ -1247,14 +1705,15 @@ impl Default for PrimitiveScratchBuffer { segments: SegmentStorage::new(0), segment_instances: SegmentInstanceStorage::new(0), gradient_tiles: GradientTileStorage::new(0), + recorded_dirty_regions: Vec::new(), debug_items: Vec::new(), + prim_info: Vec::new(), } } -} -impl PrimitiveScratchBuffer { pub fn recycle(&mut self, recycler: &mut Recycler) { recycler.recycle_vec(&mut self.clip_mask_instances); + recycler.recycle_vec(&mut self.prim_info); self.glyph_keys.recycle(recycler); self.border_cache_handles.recycle(recycler); self.segments.recycle(recycler); @@ -1278,7 +1737,11 @@ impl PrimitiveScratchBuffer { // should fix this in the future to retain handles. self.gradient_tiles.clear(); + self.prim_info.clear(); + self.debug_items.clear(); + + assert!(self.recorded_dirty_regions.is_empty(), "Should have sent to Renderer"); } #[allow(dead_code)] @@ -1316,6 +1779,7 @@ impl PrimitiveScratchBuffer { pub struct PrimitiveStoreStats { picture_count: usize, text_run_count: usize, + opacity_binding_count: usize, image_count: usize, linear_gradient_count: usize, color_binding_count: usize, @@ -1326,6 +1790,7 @@ impl PrimitiveStoreStats { PrimitiveStoreStats { picture_count: 0, text_run_count: 0, + opacity_binding_count: 0, image_count: 0, linear_gradient_count: 0, color_binding_count: 0, @@ -1344,6 +1809,8 @@ pub struct PrimitiveStore { /// for other types. pub images: ImageInstanceStorage, + /// List of animated opacity bindings for a primitive. + pub opacity_bindings: OpacityBindingStorage, /// animated color bindings for this primitive. pub color_bindings: ColorBindingStorage, } @@ -1354,6 +1821,7 @@ impl PrimitiveStore { pictures: Vec::with_capacity(stats.picture_count), text_runs: TextRunStorage::new(stats.text_run_count), images: ImageInstanceStorage::new(stats.image_count), + opacity_bindings: OpacityBindingStorage::new(stats.opacity_binding_count), color_bindings: ColorBindingStorage::new(stats.color_binding_count), linear_gradients: LinearGradientStorage::new(stats.linear_gradient_count), } @@ -1364,6 +1832,7 @@ impl PrimitiveStore { picture_count: self.pictures.len(), text_run_count: self.text_runs.len(), image_count: self.images.len(), + opacity_binding_count: self.opacity_bindings.len(), linear_gradient_count: self.linear_gradients.len(), color_binding_count: self.color_bindings.len(), } @@ -1376,14 +1845,2651 @@ impl PrimitiveStore { self.pictures[root.0].print(&self.pictures, root, &mut pt); } + /// Destroy an existing primitive store. This is called just before + /// a primitive store is replaced with a newly built scene. + pub fn destroy( + &mut self, + retained_tiles: &mut RetainedTiles, + ) { + for pic in &mut self.pictures { + pic.destroy( + retained_tiles, + ); + } + } + /// Returns the total count of primitive instances contained in pictures. pub fn prim_count(&self) -> usize { let mut prim_count = 0; for pic in &self.pictures { - prim_count += pic.prim_list.prim_instances.len(); + for cluster in &pic.prim_list.clusters { + prim_count += cluster.prim_instances.len(); + } } prim_count } + + /// Update visibility pass - update each primitive visibility struct, and + /// build the clip chain instance if appropriate. + pub fn update_visibility( + &mut self, + pic_index: PictureIndex, + parent_surface_index: SurfaceIndex, + world_culling_rect: &WorldRect, + frame_context: &FrameVisibilityContext, + frame_state: &mut FrameVisibilityState, + ) -> Option<PictureRect> { + profile_scope!("update_visibility"); + let (mut prim_list, surface_index, apply_local_clip_rect, world_culling_rect, is_composite) = { + let pic = &mut self.pictures[pic_index.0]; + let mut world_culling_rect = *world_culling_rect; + + let prim_list = mem::replace(&mut pic.prim_list, PrimitiveList::empty()); + let (surface_index, is_composite) = match pic.raster_config { + Some(ref raster_config) => (raster_config.surface_index, true), + None => (parent_surface_index, false) + }; + + match pic.raster_config { + Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { .. }, .. }) => { + let mut tile_cache = pic.tile_cache.take().unwrap(); + debug_assert!(frame_state.tile_cache.is_none()); + + // If we have a tile cache for this picture, see if any of the + // relative transforms have changed, which means we need to + // re-map the dependencies of any child primitives. + world_culling_rect = tile_cache.pre_update( + layout_rect_as_picture_rect(&pic.estimated_local_rect), + surface_index, + frame_context, + frame_state, + ); + + // Push a new surface, supplying the list of clips that should be + // ignored, since they are handled by clipping when drawing this surface. + frame_state.push_surface( + surface_index, + &tile_cache.shared_clips, + frame_context.spatial_tree, + ); + frame_state.tile_cache = Some(tile_cache); + } + _ => { + if is_composite { + frame_state.push_surface( + surface_index, + &[], + frame_context.spatial_tree, + ); + } + } + } + + (prim_list, surface_index, pic.apply_local_clip_rect, world_culling_rect, is_composite) + }; + + let surface = &frame_context.surfaces[surface_index.0 as usize]; + + let mut map_local_to_surface = surface + .map_local_to_surface + .clone(); + + let map_surface_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + surface.surface_spatial_node_index, + frame_context.global_screen_world_rect, + frame_context.spatial_tree, + ); + + let mut surface_rect = PictureRect::zero(); + + for cluster in &mut prim_list.clusters { + profile_scope!("cluster"); + // Get the cluster and see if is visible + if !cluster.flags.contains(ClusterFlags::IS_VISIBLE) { + // Each prim instance must have reset called each frame, to clear + // indices into various scratch buffers. If this doesn't occur, + // the primitive may incorrectly be considered visible, which can + // cause unexpected conditions to occur later during the frame. + // Primitive instances are normally reset in the main loop below, + // but we must also reset them in the rare case that the cluster + // visibility has changed (due to an invalid transform and/or + // backface visibility changing for this cluster). + // TODO(gw): This is difficult to test for in CI - as a follow up, + // we should add a debug flag that validates the prim + // instance is always reset every frame to catch similar + // issues in future. + for prim_instance in &mut cluster.prim_instances { + prim_instance.reset(); + } + continue; + } + + map_local_to_surface.set_target_spatial_node( + cluster.spatial_node_index, + frame_context.spatial_tree, + ); + + for prim_instance in &mut cluster.prim_instances { + prim_instance.reset(); + + if prim_instance.is_chased() { + #[cfg(debug_assertions)] // needed for ".id" part + println!("\tpreparing {:?} in {:?}", prim_instance.id, pic_index); + println!("\t{:?}", prim_instance.kind); + } + + let (is_passthrough, prim_local_rect, prim_shadowed_rect) = match prim_instance.kind { + PrimitiveInstanceKind::Picture { pic_index, .. } => { + if !self.pictures[pic_index.0].is_visible() { + continue; + } + + frame_state.clip_chain_stack.push_clip( + prim_instance.clip_chain_id, + frame_state.clip_store, + ); + + let pic_surface_rect = self.update_visibility( + pic_index, + surface_index, + &world_culling_rect, + frame_context, + frame_state, + ); + + frame_state.clip_chain_stack.pop_clip(); + + let pic = &self.pictures[pic_index.0]; + + if prim_instance.is_chased() && pic.estimated_local_rect != pic.precise_local_rect { + println!("\testimate {:?} adjusted to {:?}", pic.estimated_local_rect, pic.precise_local_rect); + } + + let mut shadow_rect = pic.precise_local_rect; + match pic.raster_config { + Some(ref rc) => match rc.composite_mode { + // If we have a drop shadow filter, we also need to include the shadow in + // our shadowed local rect for the purpose of calculating the size of the + // picture. + PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => { + for shadow in shadows { + shadow_rect = shadow_rect.union(&pic.precise_local_rect.translate(shadow.offset)); + } + } + _ => {} + } + None => { + // If the primitive does not have its own raster config, we need to + // propogate the surface rect calculation to the parent. + if let Some(ref rect) = pic_surface_rect { + surface_rect = surface_rect.union(rect); + } + } + } + + (pic.raster_config.is_none(), pic.precise_local_rect, shadow_rect) + } + _ => { + let prim_data = &frame_state.data_stores.as_common_data(&prim_instance); + + (false, prim_data.prim_rect, prim_data.prim_rect) + } + }; + + if is_passthrough { + let vis_index = PrimitiveVisibilityIndex(frame_state.scratch.prim_info.len() as u32); + + frame_state.scratch.prim_info.push( + PrimitiveVisibility { + clipped_world_rect: WorldRect::max_rect(), + clip_chain: ClipChainInstance::empty(), + clip_task_index: ClipTaskIndex::INVALID, + combined_local_clip_rect: LayoutRect::zero(), + visibility_mask: PrimitiveVisibilityMask::empty(), + flags: PrimitiveVisibilityFlags::empty(), + } + ); + + prim_instance.visibility_info = vis_index; + } else { + if prim_local_rect.size.width <= 0.0 || prim_local_rect.size.height <= 0.0 { + if prim_instance.is_chased() { + println!("\tculled for zero local rectangle"); + } + continue; + } + + // Inflate the local rect for this primitive by the inflation factor of + // the picture context and include the shadow offset. This ensures that + // even if the primitive itself is not visible, any effects from the + // blur radius or shadow will be correctly taken into account. + let inflation_factor = surface.inflation_factor; + let local_rect = prim_shadowed_rect + .inflate(inflation_factor, inflation_factor) + .intersection(&prim_instance.local_clip_rect); + let local_rect = match local_rect { + Some(local_rect) => local_rect, + None => { + if prim_instance.is_chased() { + println!("\tculled for being out of the local clip rectangle: {:?}", + prim_instance.local_clip_rect); + } + continue; + } + }; + + // Include the clip chain for this primitive in the current stack. + frame_state.clip_chain_stack.push_clip( + prim_instance.clip_chain_id, + frame_state.clip_store, + ); + + frame_state.clip_store.set_active_clips( + prim_instance.local_clip_rect, + cluster.spatial_node_index, + frame_state.clip_chain_stack.current_clips_array(), + &frame_context.spatial_tree, + &frame_state.data_stores.clip, + ); + + let clip_chain = frame_state + .clip_store + .build_clip_chain_instance( + local_rect, + &map_local_to_surface, + &map_surface_to_world, + &frame_context.spatial_tree, + frame_state.gpu_cache, + frame_state.resource_cache, + surface.device_pixel_scale, + &world_culling_rect, + &mut frame_state.data_stores.clip, + true, + 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. + match tile_cache.update_prim_dependencies( + prim_instance, + cluster.spatial_node_index, + clip_chain.as_ref(), + prim_local_rect, + frame_context, + frame_state.data_stores, + frame_state.clip_store, + &self.pictures, + frame_state.resource_cache, + &self.opacity_bindings, + &self.color_bindings, + &self.images, + &frame_state.surface_stack, + &mut frame_state.composite_state, + ) { + 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; + } + } + } + + // Ensure the primitive clip is popped + frame_state.clip_chain_stack.pop_clip(); + + let clip_chain = match clip_chain { + Some(clip_chain) => clip_chain, + None => { + if prim_instance.is_chased() { + println!("\tunable to build the clip chain, skipping"); + } + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + continue; + } + }; + + if prim_instance.is_chased() { + println!("\teffective clip chain from {:?} {}", + clip_chain.clips_range, + if apply_local_clip_rect { "(applied)" } else { "" }, + ); + println!("\tpicture rect {:?} @{:?}", + clip_chain.pic_clip_rect, + clip_chain.pic_spatial_node_index, + ); + } + + // Check if the clip bounding rect (in pic space) is visible on screen + // This includes both the prim bounding rect + local prim clip rect! + let world_rect = match map_surface_to_world.map(&clip_chain.pic_clip_rect) { + Some(world_rect) => world_rect, + None => { + continue; + } + }; + + let clipped_world_rect = match world_rect.intersection(&world_culling_rect) { + Some(rect) => rect, + None => { + continue; + } + }; + + let combined_local_clip_rect = if apply_local_clip_rect { + clip_chain.local_clip_rect + } else { + prim_instance.local_clip_rect + }; + + if combined_local_clip_rect.size.is_empty() { + debug_assert!(combined_local_clip_rect.size.width >= 0.0 && + combined_local_clip_rect.size.height >= 0.0); + if prim_instance.is_chased() { + println!("\tculled for zero local clip rectangle"); + } + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + continue; + } + + // Include the visible area for primitive, including any shadows, in + // the area affected by the surface. + match combined_local_clip_rect.intersection(&local_rect) { + Some(visible_rect) => { + if let Some(rect) = map_local_to_surface.map(&visible_rect) { + surface_rect = surface_rect.union(&rect); + } + } + None => { + if prim_instance.is_chased() { + println!("\tculled for zero visible rectangle"); + } + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + continue; + } + } + + // When the debug display is enabled, paint a colored rectangle around each + // primitive. + if frame_context.debug_flags.contains(::api::DebugFlags::PRIMITIVE_DBG) { + let debug_color = match prim_instance.kind { + PrimitiveInstanceKind::Picture { .. } => ColorF::TRANSPARENT, + PrimitiveInstanceKind::TextRun { .. } => debug_colors::RED, + PrimitiveInstanceKind::LineDecoration { .. } => debug_colors::PURPLE, + PrimitiveInstanceKind::NormalBorder { .. } | + PrimitiveInstanceKind::ImageBorder { .. } => debug_colors::ORANGE, + PrimitiveInstanceKind::Rectangle { .. } => ColorF { r: 0.8, g: 0.8, b: 0.8, a: 0.5 }, + PrimitiveInstanceKind::YuvImage { .. } => debug_colors::BLUE, + PrimitiveInstanceKind::Image { .. } => debug_colors::BLUE, + PrimitiveInstanceKind::LinearGradient { .. } => debug_colors::PINK, + PrimitiveInstanceKind::RadialGradient { .. } => debug_colors::PINK, + PrimitiveInstanceKind::ConicGradient { .. } => debug_colors::PINK, + PrimitiveInstanceKind::Clear { .. } => debug_colors::CYAN, + PrimitiveInstanceKind::Backdrop { .. } => debug_colors::MEDIUMAQUAMARINE, + }; + if debug_color.a != 0.0 { + let debug_rect = clipped_world_rect * frame_context.global_device_pixel_scale; + frame_state.scratch.push_debug_rect(debug_rect, debug_color, debug_color.scale_alpha(0.5)); + } + } else if frame_context.debug_flags.contains(::api::DebugFlags::OBSCURE_IMAGES) { + let is_image = matches!( + prim_instance.kind, + PrimitiveInstanceKind::Image { .. } | PrimitiveInstanceKind::YuvImage { .. } + ); + if is_image { + // We allow "small" images, since they're generally UI elements. + let rect = clipped_world_rect * frame_context.global_device_pixel_scale; + if rect.size.width > 70.0 && rect.size.height > 70.0 { + frame_state.scratch.push_debug_rect(rect, debug_colors::PURPLE, debug_colors::PURPLE); + } + } + } + + let vis_index = PrimitiveVisibilityIndex(frame_state.scratch.prim_info.len() as u32); + if prim_instance.is_chased() { + println!("\tvisible {:?} with {:?}", vis_index, combined_local_clip_rect); + } + + frame_state.scratch.prim_info.push( + PrimitiveVisibility { + clipped_world_rect, + clip_chain, + clip_task_index: ClipTaskIndex::INVALID, + combined_local_clip_rect, + visibility_mask: PrimitiveVisibilityMask::empty(), + flags: vis_flags, + } + ); + + prim_instance.visibility_info = vis_index; + + self.request_resources_for_prim( + prim_instance, + cluster.spatial_node_index, + clipped_world_rect, + frame_context, + frame_state, + ); + } + } + } + + // Similar to above, pop either the clip chain or root entry off the current clip stack. + if is_composite { + frame_state.pop_surface(); + } + + let pic = &mut self.pictures[pic_index.0]; + pic.prim_list = prim_list; + + // If the local rect changed (due to transforms in child primitives) then + // invalidate the GPU cache location to re-upload the new local rect + // and stretch size. Drop shadow filters also depend on the local rect + // size for the extra GPU cache data handle. + // TODO(gw): In future, if we support specifying a flag which gets the + // stretch size from the segment rect in the shaders, we can + // remove this invalidation here completely. + if let Some(ref rc) = pic.raster_config { + // Inflate the local bounding rect if required by the filter effect. + // This inflaction factor is to be applied to the surface itself. + if pic.options.inflate_if_required { + // The picture's local rect is calculated as the union of the + // snapped primitive rects, which should result in a snapped + // local rect, unless it was inflated. This is also done during + // surface configuration when calculating the picture's + // estimated local rect. + let snap_pic_to_raster = SpaceSnapper::new_with_target( + surface.raster_spatial_node_index, + pic.spatial_node_index, + surface.device_pixel_scale, + frame_context.spatial_tree, + ); + + surface_rect = rc.composite_mode.inflate_picture_rect(surface_rect, surface.scale_factors); + surface_rect = snap_pic_to_raster.snap_rect(&surface_rect); + } + + // Layout space for the picture is picture space from the + // perspective of its child primitives. + let pic_local_rect = surface_rect * Scale::new(1.0); + if pic.precise_local_rect != pic_local_rect { + match rc.composite_mode { + PictureCompositeMode::Filter(Filter::DropShadows(..)) => { + for handle in &pic.extra_gpu_data_handles { + frame_state.gpu_cache.invalidate(handle); + } + } + _ => {} + } + // Invalidate any segments built for this picture, since the local + // rect has changed. + pic.segments_are_valid = false; + pic.precise_local_rect = pic_local_rect; + } + + if let PictureCompositeMode::TileCache { .. } = rc.composite_mode { + let mut tile_cache = frame_state.tile_cache.take().unwrap(); + + // Build the dirty region(s) for this tile cache. + tile_cache.post_update( + frame_context, + frame_state, + ); + + pic.tile_cache = Some(tile_cache); + } + + None + } else { + let parent_surface = &frame_context.surfaces[parent_surface_index.0 as usize]; + let map_surface_to_parent_surface = SpaceMapper::new_with_target( + parent_surface.surface_spatial_node_index, + surface.surface_spatial_node_index, + PictureRect::max_rect(), + frame_context.spatial_tree, + ); + map_surface_to_parent_surface.map(&surface_rect) + } + } + + fn request_resources_for_prim( + &mut self, + prim_instance: &mut PrimitiveInstance, + prim_spatial_node_index: SpatialNodeIndex, + prim_world_rect: WorldRect, + frame_context: &FrameVisibilityContext, + frame_state: &mut FrameVisibilityState, + ) { + profile_scope!("request_resources_for_prim"); + match prim_instance.kind { + PrimitiveInstanceKind::TextRun { .. } => { + // Text runs can't request resources early here, as we don't + // know until TileCache::post_update() whether we are drawing + // on an opaque surface. + // TODO(gw): We might be able to detect simple cases of this earlier, + // during the picture traversal. But it's probably not worth it? + } + PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { + let prim_data = &mut frame_state.data_stores.image[data_handle]; + let common_data = &mut prim_data.common; + let image_data = &mut prim_data.kind; + let image_instance = &mut self.images[image_instance_index]; + + let image_properties = frame_state + .resource_cache + .get_image_properties(image_data.key); + + let request = ImageRequest { + key: image_data.key, + rendering: image_data.image_rendering, + tile: None, + }; + + match image_properties { + Some(ImageProperties { tiling: None, .. }) => { + + frame_state.resource_cache.request_image( + request, + frame_state.gpu_cache, + ); + } + Some(ImageProperties { tiling: Some(tile_size), visible_rect, .. }) => { + image_instance.visible_tiles.clear(); + // TODO: rename the blob's visible_rect into something that doesn't conflict + // with the terminology we use during culling since it's not really the same + // thing. + let active_rect = visible_rect; + + // Tighten the clip rect because decomposing the repeated image can + // produce primitives that are partially covering the original image + // rect and we want to clip these extra parts out. + let prim_info = &frame_state.scratch.prim_info[prim_instance.visibility_info.0 as usize]; + let tight_clip_rect = prim_info + .combined_local_clip_rect + .intersection(&common_data.prim_rect).unwrap(); + image_instance.tight_local_clip_rect = tight_clip_rect; + + let map_local_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + prim_spatial_node_index, + frame_context.global_screen_world_rect, + frame_context.spatial_tree, + ); + + let visible_rect = compute_conservative_visible_rect( + &tight_clip_rect, + prim_world_rect, + &map_local_to_world, + ); + + let base_edge_flags = edge_flags_for_tile_spacing(&image_data.tile_spacing); + + let stride = image_data.stretch_size + image_data.tile_spacing; + + // We are performing the decomposition on the CPU here, no need to + // have it in the shader. + common_data.may_need_repetition = false; + + let repetitions = image_tiling::repetitions( + &common_data.prim_rect, + &visible_rect, + stride, + ); + + for Repetition { origin, edge_flags } in repetitions { + let edge_flags = base_edge_flags | edge_flags; + + let layout_image_rect = LayoutRect { + origin, + size: image_data.stretch_size, + }; + + let tiles = image_tiling::tiles( + &layout_image_rect, + &visible_rect, + &active_rect, + tile_size as i32, + ); + + for tile in tiles { + frame_state.resource_cache.request_image( + request.with_tile(tile.offset), + frame_state.gpu_cache, + ); + + image_instance.visible_tiles.push(VisibleImageTile { + tile_offset: tile.offset, + edge_flags: tile.edge_flags & edge_flags, + local_rect: tile.rect, + local_clip_rect: tight_clip_rect, + }); + } + } + + if image_instance.visible_tiles.is_empty() { + // Mark as invisible + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + } + } + None => {} + } + } + PrimitiveInstanceKind::ImageBorder { data_handle, .. } => { + let prim_data = &mut frame_state.data_stores.image_border[data_handle]; + prim_data.kind.request_resources( + frame_state.resource_cache, + frame_state.gpu_cache, + ); + } + PrimitiveInstanceKind::YuvImage { data_handle, .. } => { + let prim_data = &mut frame_state.data_stores.yuv_image[data_handle]; + prim_data.kind.request_resources( + frame_state.resource_cache, + frame_state.gpu_cache, + ); + } + _ => {} + } + } + + pub fn get_opacity_binding( + &self, + opacity_binding_index: OpacityBindingIndex, + ) -> f32 { + if opacity_binding_index == OpacityBindingIndex::INVALID { + 1.0 + } else { + self.opacity_bindings[opacity_binding_index].current + } + } + + // Internal method that retrieves the primitive index of a primitive + // that can be the target for collapsing parent opacity filters into. + fn get_opacity_collapse_prim( + &self, + pic_index: PictureIndex, + ) -> Option<PictureIndex> { + let pic = &self.pictures[pic_index.0]; + + // We can only collapse opacity if there is a single primitive, otherwise + // the opacity needs to be applied to the primitives as a group. + if pic.prim_list.clusters.len() != 1 { + return None; + } + + let cluster = &pic.prim_list.clusters[0]; + if cluster.prim_instances.len() != 1 { + return None; + } + + let prim_instance = &cluster.prim_instances[0]; + + // For now, we only support opacity collapse on solid rects and images. + // This covers the most common types of opacity filters that can be + // handled by this optimization. In the future, we can easily extend + // this to other primitives, such as text runs and gradients. + match prim_instance.kind { + // If we find a single rect or image, we can use that + // as the primitive to collapse the opacity into. + PrimitiveInstanceKind::Rectangle { .. } | + PrimitiveInstanceKind::Image { .. } => { + return Some(pic_index); + } + PrimitiveInstanceKind::Clear { .. } | + PrimitiveInstanceKind::TextRun { .. } | + PrimitiveInstanceKind::NormalBorder { .. } | + PrimitiveInstanceKind::ImageBorder { .. } | + PrimitiveInstanceKind::YuvImage { .. } | + PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | + PrimitiveInstanceKind::ConicGradient { .. } | + PrimitiveInstanceKind::LineDecoration { .. } | + PrimitiveInstanceKind::Backdrop { .. } => { + // These prims don't support opacity collapse + } + PrimitiveInstanceKind::Picture { pic_index, .. } => { + let pic = &self.pictures[pic_index.0]; + + // If we encounter a picture that is a pass-through + // (i.e. no composite mode), then we can recurse into + // that to try and find a primitive to collapse to. + if pic.requested_composite_mode.is_none() { + return self.get_opacity_collapse_prim(pic_index); + } + } + } + + None + } + + // Apply any optimizations to drawing this picture. Currently, + // we just support collapsing pictures with an opacity filter + // by pushing that opacity value into the color of a primitive + // if that picture contains one compatible primitive. + pub fn optimize_picture_if_possible( + &mut self, + pic_index: PictureIndex, + ) { + // Only handle opacity filters for now. + let binding = match self.pictures[pic_index.0].requested_composite_mode { + Some(PictureCompositeMode::Filter(Filter::Opacity(binding, _))) => { + binding + } + _ => { + return; + } + }; + + // See if this picture contains a single primitive that supports + // opacity collapse. + match self.get_opacity_collapse_prim(pic_index) { + Some(pic_index) => { + let pic = &mut self.pictures[pic_index.0]; + let prim_instance = &mut pic.prim_list.clusters[0].prim_instances[0]; + match prim_instance.kind { + PrimitiveInstanceKind::Image { image_instance_index, .. } => { + let image_instance = &mut self.images[image_instance_index]; + // By this point, we know we should only have found a primitive + // that supports opacity collapse. + if image_instance.opacity_binding_index == OpacityBindingIndex::INVALID { + image_instance.opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new()); + } + let opacity_binding = &mut self.opacity_bindings[image_instance.opacity_binding_index]; + opacity_binding.push(binding); + } + PrimitiveInstanceKind::Rectangle { ref mut opacity_binding_index, .. } => { + // By this point, we know we should only have found a primitive + // that supports opacity collapse. + if *opacity_binding_index == OpacityBindingIndex::INVALID { + *opacity_binding_index = self.opacity_bindings.push(OpacityBinding::new()); + } + let opacity_binding = &mut self.opacity_bindings[*opacity_binding_index]; + opacity_binding.push(binding); + } + _ => { + unreachable!(); + } + } + } + None => { + return; + } + } + + // The opacity filter has been collapsed, so mark this picture + // as a pass though. This means it will no longer allocate an + // intermediate surface or incur an extra blend / blit. Instead, + // the collapsed primitive will be drawn directly into the + // parent picture. + self.pictures[pic_index.0].requested_composite_mode = None; + } + + fn prepare_prim_for_render( + &mut self, + prim_instance: &mut PrimitiveInstance, + prim_spatial_node_index: SpatialNodeIndex, + pic_context: &PictureContext, + pic_state: &mut PictureState, + frame_context: &FrameBuildingContext, + frame_state: &mut FrameBuildingState, + plane_split_anchor: PlaneSplitAnchor, + data_stores: &mut DataStores, + scratch: &mut PrimitiveScratchBuffer, + tile_cache_log: &mut TileCacheLogger, + ) -> bool { + profile_scope!("prepare_prim_for_render"); + // If we have dependencies, we need to prepare them first, in order + // to know the actual rect of this primitive. + // For example, scrolling may affect the location of an item in + // local space, which may force us to render this item on a larger + // picture target, if being composited. + let pic_info = { + match prim_instance.kind { + PrimitiveInstanceKind::Picture { pic_index ,.. } => { + let pic = &mut self.pictures[pic_index.0]; + + let clipped_prim_bounding_rect = scratch + .prim_info[prim_instance.visibility_info.0 as usize] + .clipped_world_rect; + + match pic.take_context( + pic_index, + clipped_prim_bounding_rect, + pic_context.surface_spatial_node_index, + pic_context.raster_spatial_node_index, + pic_context.surface_index, + &pic_context.subpixel_mode, + frame_state, + frame_context, + scratch, + tile_cache_log, + ) { + Some(info) => Some(info), + None => { + if prim_instance.is_chased() { + println!("\tculled for carrying an invisible composite filter"); + } + + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + + return false; + } + } + } + PrimitiveInstanceKind::TextRun { .. } | + PrimitiveInstanceKind::Rectangle { .. } | + PrimitiveInstanceKind::LineDecoration { .. } | + PrimitiveInstanceKind::NormalBorder { .. } | + PrimitiveInstanceKind::ImageBorder { .. } | + PrimitiveInstanceKind::YuvImage { .. } | + PrimitiveInstanceKind::Image { .. } | + PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | + PrimitiveInstanceKind::ConicGradient { .. } | + PrimitiveInstanceKind::Clear { .. } | + PrimitiveInstanceKind::Backdrop { .. } => { + None + } + } + }; + + let is_passthrough = match pic_info { + Some((pic_context_for_children, mut pic_state_for_children, mut prim_list)) => { + let is_passthrough = pic_context_for_children.is_passthrough; + + self.prepare_primitives( + &mut prim_list, + &pic_context_for_children, + &mut pic_state_for_children, + frame_context, + frame_state, + data_stores, + scratch, + tile_cache_log, + ); + + // Restore the dependencies (borrow check dance) + self.pictures[pic_context_for_children.pic_index.0] + .restore_context( + pic_context.surface_index, + prim_list, + pic_context_for_children, + pic_state_for_children, + frame_state, + ); + + is_passthrough + } + None => { + false + } + }; + + let prim_rect = data_stores.get_local_prim_rect( + prim_instance, + self, + ); + + if !is_passthrough { + prim_instance.update_clip_task( + &prim_rect.origin, + prim_spatial_node_index, + pic_context.raster_spatial_node_index, + pic_context, + pic_state, + frame_context, + frame_state, + self, + data_stores, + scratch, + ); + + if prim_instance.is_chased() { + println!("\tconsidered visible and ready with local pos {:?}", prim_rect.origin); + } + } + + #[cfg(debug_assertions)] + { + prim_instance.prepared_frame_id = frame_state.render_tasks.frame_id(); + } + + self.prepare_interned_prim_for_render( + prim_instance, + prim_spatial_node_index, + plane_split_anchor, + pic_context, + pic_state, + frame_context, + frame_state, + data_stores, + scratch, + ); + + true + } + + pub fn prepare_primitives( + &mut self, + prim_list: &mut PrimitiveList, + pic_context: &PictureContext, + pic_state: &mut PictureState, + frame_context: &FrameBuildingContext, + frame_state: &mut FrameBuildingState, + data_stores: &mut DataStores, + scratch: &mut PrimitiveScratchBuffer, + tile_cache_log: &mut TileCacheLogger, + ) { + profile_scope!("prepare_primitives"); + for (cluster_index, cluster) in prim_list.clusters.iter_mut().enumerate() { + profile_scope!("cluster"); + pic_state.map_local_to_pic.set_target_spatial_node( + cluster.spatial_node_index, + frame_context.spatial_tree, + ); + + for (prim_instance_index, prim_instance) in cluster.prim_instances.iter_mut().enumerate() { + if prim_instance.visibility_info == PrimitiveVisibilityIndex::INVALID { + continue; + } + + // The original clipped world rect was calculated during the initial visibility pass. + // However, it's possible that the dirty rect has got smaller, if tiles were not + // dirty. Intersecting with the dirty rect here eliminates preparing any primitives + // outside the dirty rect, and reduces the size of any off-screen surface allocations + // for clip masks / render tasks that we make. + { + let visibility_info = &mut scratch.prim_info[prim_instance.visibility_info.0 as usize]; + let dirty_region = frame_state.current_dirty_region(); + + for dirty_region in &dirty_region.dirty_rects { + if visibility_info.clipped_world_rect.intersects(&dirty_region.world_rect) { + visibility_info.visibility_mask.include(dirty_region.visibility_mask); + } + } + + if visibility_info.visibility_mask.is_empty() { + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + continue; + } + } + + let plane_split_anchor = PlaneSplitAnchor::new(cluster_index, prim_instance_index); + + if self.prepare_prim_for_render( + prim_instance, + cluster.spatial_node_index, + pic_context, + pic_state, + frame_context, + frame_state, + plane_split_anchor, + data_stores, + scratch, + tile_cache_log, + ) { + frame_state.profile_counters.visible_primitives.inc(); + } + } + } + } + + /// Prepare an interned primitive for rendering, by requesting + /// resources, render tasks etc. This is equivalent to the + /// prepare_prim_for_render_inner call for old style primitives. + fn prepare_interned_prim_for_render( + &mut self, + prim_instance: &mut PrimitiveInstance, + prim_spatial_node_index: SpatialNodeIndex, + plane_split_anchor: PlaneSplitAnchor, + pic_context: &PictureContext, + pic_state: &mut PictureState, + frame_context: &FrameBuildingContext, + frame_state: &mut FrameBuildingState, + data_stores: &mut DataStores, + scratch: &mut PrimitiveScratchBuffer, + ) { + let is_chased = prim_instance.is_chased(); + let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale; + + match &mut prim_instance.kind { + PrimitiveInstanceKind::LineDecoration { data_handle, ref mut cache_handle, .. } => { + profile_scope!("LineDecoration"); + let prim_data = &mut data_stores.line_decoration[*data_handle]; + let common_data = &mut prim_data.common; + let line_dec_data = &mut prim_data.kind; + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + line_dec_data.update(common_data, frame_state); + + // Work out the device pixel size to be used to cache this line decoration. + if is_chased { + println!("\tline decoration key={:?}", line_dec_data.cache_key); + } + + // If we have a cache key, it's a wavy / dashed / dotted line. Otherwise, it's + // a simple solid line. + if let Some(cache_key) = line_dec_data.cache_key.as_ref() { + // TODO(gw): Do we ever need / want to support scales for text decorations + // based on the current transform? + let scale_factor = Scale::new(1.0) * device_pixel_scale; + let mut task_size = (LayoutSize::from_au(cache_key.size) * scale_factor).ceil().to_i32(); + if task_size.width > MAX_LINE_DECORATION_RESOLUTION as i32 || + task_size.height > MAX_LINE_DECORATION_RESOLUTION as i32 { + let max_extent = cmp::max(task_size.width, task_size.height); + let task_scale_factor = Scale::new(MAX_LINE_DECORATION_RESOLUTION as f32 / max_extent as f32); + task_size = (LayoutSize::from_au(cache_key.size) * scale_factor * task_scale_factor) + .ceil().to_i32(); + } + + // Request a pre-rendered image task. + // TODO(gw): This match is a bit untidy, but it should disappear completely + // once the prepare_prims and batching are unified. When that + // happens, we can use the cache handle immediately, and not need + // to temporarily store it in the primitive instance. + *cache_handle = Some(frame_state.resource_cache.request_render_task( + RenderTaskCacheKey { + size: task_size, + kind: RenderTaskCacheKeyKind::LineDecoration(cache_key.clone()), + }, + frame_state.gpu_cache, + frame_state.render_tasks, + None, + false, + |render_tasks| { + render_tasks.add().init(RenderTask::new_line_decoration( + task_size, + cache_key.style, + cache_key.orientation, + cache_key.wavy_line_thickness.to_f32_px(), + LayoutSize::from_au(cache_key.size), + )) + } + )); + } + } + PrimitiveInstanceKind::TextRun { run_index, data_handle, .. } => { + profile_scope!("TextRun"); + let prim_data = &mut data_stores.text_run[*data_handle]; + let run = &mut self.text_runs[*run_index]; + + prim_data.common.may_need_repetition = false; + + // The glyph transform has to match `glyph_transform` in "ps_text_run" shader. + // It's relative to the rasterizing space of a glyph. + let transform = frame_context.spatial_tree + .get_relative_transform( + prim_spatial_node_index, + pic_context.raster_spatial_node_index, + ) + .into_fast_transform(); + let prim_offset = prim_data.common.prim_rect.origin.to_vector() - run.reference_frame_relative_offset; + + let pic = &self.pictures[pic_context.pic_index.0]; + let raster_space = pic.get_raster_space(frame_context.spatial_tree); + let surface = &frame_state.surfaces[pic_context.surface_index.0]; + let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; + let root_scaling_factor = match pic.raster_config { + Some(ref raster_config) => raster_config.root_scaling_factor, + None => 1.0 + }; + + run.request_resources( + prim_offset, + prim_info.clip_chain.pic_clip_rect, + &prim_data.font, + &prim_data.glyphs, + &transform.to_transform().with_destination::<_>(), + surface, + prim_spatial_node_index, + raster_space, + root_scaling_factor, + &pic_context.subpixel_mode, + frame_state.resource_cache, + frame_state.gpu_cache, + frame_state.render_tasks, + frame_context.spatial_tree, + scratch, + ); + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + prim_data.update(frame_state); + } + PrimitiveInstanceKind::Clear { data_handle, .. } => { + profile_scope!("Clear"); + let prim_data = &mut data_stores.prim[*data_handle]; + + prim_data.common.may_need_repetition = false; + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + prim_data.update(frame_state, frame_context.scene_properties); + } + PrimitiveInstanceKind::NormalBorder { data_handle, ref mut cache_handles, .. } => { + profile_scope!("NormalBorder"); + let prim_data = &mut data_stores.normal_border[*data_handle]; + let common_data = &mut prim_data.common; + let border_data = &mut prim_data.kind; + + common_data.may_need_repetition = + matches!(border_data.border.top.style, BorderStyle::Dotted | BorderStyle::Dashed) || + matches!(border_data.border.right.style, BorderStyle::Dotted | BorderStyle::Dashed) || + matches!(border_data.border.bottom.style, BorderStyle::Dotted | BorderStyle::Dashed) || + matches!(border_data.border.left.style, BorderStyle::Dotted | BorderStyle::Dashed); + + + // Update the template this instance references, which may refresh the GPU + // cache with any shared template data. + border_data.update(common_data, frame_state); + + // TODO(gw): For now, the scale factors to rasterize borders at are + // based on the true world transform of the primitive. When + // raster roots with local scale are supported in future, + // that will need to be accounted for here. + let scale = frame_context + .spatial_tree + .get_world_transform(prim_spatial_node_index) + .scale_factors(); + + // Scale factors are normalized to a power of 2 to reduce the number of + // resolution changes. + // For frames with a changing scale transform round scale factors up to + // nearest power-of-2 boundary so that we don't keep having to redraw + // the content as it scales up and down. Rounding up to nearest + // power-of-2 boundary ensures we never scale up, only down --- avoiding + // jaggies. It also ensures we never scale down by more than a factor of + // 2, avoiding bad downscaling quality. + let scale_width = clamp_to_scale_factor(scale.0, false); + let scale_height = clamp_to_scale_factor(scale.1, false); + // Pick the maximum dimension as scale + let world_scale = LayoutToWorldScale::new(scale_width.max(scale_height)); + let mut scale = world_scale * device_pixel_scale; + let max_scale = get_max_scale_for_border(border_data); + scale.0 = scale.0.min(max_scale.0); + + // For each edge and corner, request the render task by content key + // from the render task cache. This ensures that the render task for + // this segment will be available for batching later in the frame. + let mut handles: SmallVec<[RenderTaskCacheEntryHandle; 8]> = SmallVec::new(); + + for segment in &border_data.border_segments { + // Update the cache key device size based on requested scale. + let cache_size = to_cache_size(segment.local_task_size * scale); + let cache_key = RenderTaskCacheKey { + kind: RenderTaskCacheKeyKind::BorderSegment(segment.cache_key.clone()), + size: cache_size, + }; + + handles.push(frame_state.resource_cache.request_render_task( + cache_key, + frame_state.gpu_cache, + frame_state.render_tasks, + None, + false, // TODO(gw): We don't calculate opacity for borders yet! + |render_tasks| { + render_tasks.add().init(RenderTask::new_border_segment( + cache_size, + build_border_instances( + &segment.cache_key, + cache_size, + &border_data.border, + scale, + ), + )) + } + )); + } + + *cache_handles = scratch + .border_cache_handles + .extend(handles); + } + PrimitiveInstanceKind::ImageBorder { data_handle, .. } => { + profile_scope!("ImageBorder"); + let prim_data = &mut data_stores.image_border[*data_handle]; + + // TODO: get access to the ninepatch and to check whwther we need support + // for repetitions in the shader. + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + prim_data.kind.update(&mut prim_data.common, frame_state); + } + PrimitiveInstanceKind::Rectangle { data_handle, segment_instance_index, opacity_binding_index, color_binding_index, .. } => { + profile_scope!("Rectangle"); + let prim_data = &mut data_stores.prim[*data_handle]; + prim_data.common.may_need_repetition = false; + + if *color_binding_index != ColorBindingIndex::INVALID { + match self.color_bindings[*color_binding_index] { + PropertyBinding::Binding(..) => { + // We explicitly invalidate the gpu cache + // if the color is animating. + let gpu_cache_handle = + if *segment_instance_index == SegmentInstanceIndex::INVALID { + None + } else if *segment_instance_index == SegmentInstanceIndex::UNUSED { + Some(&prim_data.common.gpu_cache_handle) + } else { + Some(&scratch.segment_instances[*segment_instance_index].gpu_cache_handle) + }; + if let Some(gpu_cache_handle) = gpu_cache_handle { + frame_state.gpu_cache.invalidate(gpu_cache_handle); + } + } + PropertyBinding::Value(..) => {}, + } + } + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + prim_data.update( + frame_state, + frame_context.scene_properties, + ); + + update_opacity_binding( + &mut self.opacity_bindings, + *opacity_binding_index, + frame_context.scene_properties, + ); + + write_segment( + *segment_instance_index, + frame_state, + &mut scratch.segments, + &mut scratch.segment_instances, + |request| { + prim_data.kind.write_prim_gpu_blocks( + request, + frame_context.scene_properties, + ); + } + ); + } + PrimitiveInstanceKind::YuvImage { data_handle, segment_instance_index, .. } => { + profile_scope!("YuvImage"); + let prim_data = &mut data_stores.yuv_image[*data_handle]; + let common_data = &mut prim_data.common; + let yuv_image_data = &mut prim_data.kind; + + common_data.may_need_repetition = false; + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + yuv_image_data.update(common_data, frame_state); + + write_segment( + *segment_instance_index, + frame_state, + &mut scratch.segments, + &mut scratch.segment_instances, + |request| { + yuv_image_data.write_prim_gpu_blocks(request); + } + ); + } + PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { + profile_scope!("Image"); + let prim_data = &mut data_stores.image[*data_handle]; + let common_data = &mut prim_data.common; + let image_data = &mut prim_data.kind; + + if image_data.stretch_size.width >= common_data.prim_rect.size.width && + image_data.stretch_size.height >= common_data.prim_rect.size.height { + + common_data.may_need_repetition = false; + } + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + image_data.update(common_data, frame_state); + + let image_instance = &mut self.images[*image_instance_index]; + + update_opacity_binding( + &mut self.opacity_bindings, + image_instance.opacity_binding_index, + frame_context.scene_properties, + ); + + write_segment( + image_instance.segment_instance_index, + frame_state, + &mut scratch.segments, + &mut scratch.segment_instances, + |request| { + image_data.write_prim_gpu_blocks(request); + }, + ); + } + PrimitiveInstanceKind::LinearGradient { data_handle, gradient_index, .. } => { + profile_scope!("LinearGradient"); + let prim_data = &mut data_stores.linear_grad[*data_handle]; + let gradient = &mut self.linear_gradients[*gradient_index]; + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + prim_data.update(frame_state); + + if prim_data.stretch_size.width >= prim_data.common.prim_rect.size.width && + prim_data.stretch_size.height >= prim_data.common.prim_rect.size.height { + + prim_data.common.may_need_repetition = false; + } + + if prim_data.supports_caching { + let gradient_size = (prim_data.end_point - prim_data.start_point).to_size(); + + // Calculate what the range of the gradient is that covers this + // primitive. These values are included in the cache key. The + // size of the gradient task is the length of a texture cache + // region, for maximum accuracy, and a minimal size on the + // axis that doesn't matter. + let (size, orientation, prim_start_offset, prim_end_offset) = + if prim_data.start_point.x.approx_eq(&prim_data.end_point.x) { + let prim_start_offset = -prim_data.start_point.y / gradient_size.height; + let prim_end_offset = (prim_data.common.prim_rect.size.height - prim_data.start_point.y) + / gradient_size.height; + let size = DeviceIntSize::new(16, TEXTURE_REGION_DIMENSIONS); + (size, LineOrientation::Vertical, prim_start_offset, prim_end_offset) + } else { + let prim_start_offset = -prim_data.start_point.x / gradient_size.width; + let prim_end_offset = (prim_data.common.prim_rect.size.width - prim_data.start_point.x) + / gradient_size.width; + let size = DeviceIntSize::new(TEXTURE_REGION_DIMENSIONS, 16); + (size, LineOrientation::Horizontal, prim_start_offset, prim_end_offset) + }; + + // Build the cache key, including information about the stops. + let mut stops = vec![GradientStopKey::empty(); prim_data.stops.len()]; + + // Reverse the stops as required, same as the gradient builder does + // for the slow path. + if prim_data.reverse_stops { + for (src, dest) in prim_data.stops.iter().rev().zip(stops.iter_mut()) { + let stop = GradientStop { + offset: 1.0 - src.offset, + color: src.color, + }; + *dest = stop.into(); + } + } else { + for (src, dest) in prim_data.stops.iter().zip(stops.iter_mut()) { + *dest = (*src).into(); + } + } + + gradient.cache_segments.clear(); + + // emit render task caches and image rectangles to draw a gradient + // with offsets from start_offset to end_offset. + // + // the primitive is covered by a gradient that ranges from + // prim_start_offset to prim_end_offset. + // + // when clamping, these two pairs of offsets will always be the same. + // when repeating, however, we march across the primitive, blitting + // copies of the gradient along the way. each copy has a range from + // 0.0 to 1.0 (assuming it's fully visible), but where it appears on + // the primitive changes as we go. this position is also expressed + // as an offset: gradient_offset_base. that is, in terms of stops, + // we draw a gradient from start_offset to end_offset. its actual + // location on the primitive is at start_offset + gradient_offset_base. + // + // either way, we need a while-loop to draw the gradient as well + // because it might have more than 4 stops (the maximum of a cached + // segment) and/or hard stops. so we have a walk-within-the-walk from + // start_offset to end_offset caching up to GRADIENT_FP_STOPS stops at a + // time. + fn emit_segments(start_offset: f32, // start and end offset together are + end_offset: f32, // always a subrange of 0..1 + gradient_offset_base: f32, + prim_start_offset: f32, // the offsets of the entire gradient as it + prim_end_offset: f32, // covers the entire primitive. + prim_origin_in: LayoutPoint, + prim_size_in: LayoutSize, + task_size: DeviceIntSize, + is_opaque: bool, + stops: &[GradientStopKey], + orientation: LineOrientation, + frame_state: &mut FrameBuildingState, + gradient: &mut LinearGradientPrimitive) + { + // these prints are used to generate documentation examples, so + // leaving them in but commented out: + //println!("emit_segments call:"); + //println!("\tstart_offset: {}, end_offset: {}", start_offset, end_offset); + //println!("\tprim_start_offset: {}, prim_end_offset: {}", prim_start_offset, prim_end_offset); + //println!("\tgradient_offset_base: {}", gradient_offset_base); + let mut first_stop = 0; + // look for an inclusive range of stops [first_stop, last_stop]. + // once first_stop points at (or past) the last stop, we're done. + while first_stop < stops.len()-1 { + + // if the entire sub-gradient starts at an offset that's past the + // segment's end offset, we're done. + if stops[first_stop].offset > end_offset { + return; + } + + // accumulate stops until we have GRADIENT_FP_STOPS of them, or we hit + // a hard stop: + let mut last_stop = first_stop; + let mut hard_stop = false; // did we stop on a hard stop? + while last_stop < stops.len()-1 && + last_stop - first_stop + 1 < GRADIENT_FP_STOPS + { + if stops[last_stop+1].offset == stops[last_stop].offset { + hard_stop = true; + break; + } + + last_stop = last_stop + 1; + } + + let num_stops = last_stop - first_stop + 1; + + // repeated hard stops at the same offset, skip + if num_stops == 0 { + first_stop = last_stop + 1; + continue; + } + + // if the last_stop offset is before start_offset, the segment's not visible: + if stops[last_stop].offset < start_offset { + first_stop = if hard_stop { last_stop+1 } else { last_stop }; + continue; + } + + let segment_start_point = start_offset.max(stops[first_stop].offset); + let segment_end_point = end_offset .min(stops[last_stop ].offset); + + let mut segment_stops = [GradientStopKey::empty(); GRADIENT_FP_STOPS]; + for i in 0..num_stops { + segment_stops[i] = stops[first_stop + i]; + } + + let cache_key = GradientCacheKey { + orientation, + start_stop_point: VectorKey { + x: segment_start_point, + y: segment_end_point, + }, + stops: segment_stops, + }; + + let mut prim_origin = prim_origin_in; + let mut prim_size = prim_size_in; + + // the primitive is covered by a segment from overall_start to + // overall_end; scale and shift based on the length of the actual + // segment that we're drawing: + let inv_length = 1.0 / ( prim_end_offset - prim_start_offset ); + if orientation == LineOrientation::Horizontal { + prim_origin.x += ( segment_start_point + gradient_offset_base - prim_start_offset ) + * inv_length * prim_size.width; + prim_size.width *= ( segment_end_point - segment_start_point ) + * inv_length; // 2 gradient_offset_bases cancel out + } else { + prim_origin.y += ( segment_start_point + gradient_offset_base - prim_start_offset ) + * inv_length * prim_size.height; + prim_size.height *= ( segment_end_point - segment_start_point ) + * inv_length; // 2 gradient_offset_bases cancel out + } + + // <= 0 can happen if a hardstop lands exactly on an edge + if prim_size.area() > 0.0 { + let local_rect = LayoutRect::new( prim_origin, prim_size ); + + // documentation example traces: + //println!("\t\tcaching from offset {} to {}", segment_start_point, segment_end_point); + //println!("\t\tand blitting to {:?}", local_rect); + + // Request the render task each frame. + gradient.cache_segments.push( + CachedGradientSegment { + handle: frame_state.resource_cache.request_render_task( + RenderTaskCacheKey { + size: task_size, + kind: RenderTaskCacheKeyKind::Gradient(cache_key), + }, + frame_state.gpu_cache, + frame_state.render_tasks, + None, + is_opaque, + |render_tasks| { + render_tasks.add().init(RenderTask::new_gradient( + task_size, + segment_stops, + orientation, + segment_start_point, + segment_end_point, + )) + }), + local_rect: local_rect, + } + ); + } + + // if ending on a hardstop, skip past it for the start of the next run: + first_stop = if hard_stop { last_stop + 1 } else { last_stop }; + } + } + + if prim_data.extend_mode == ExtendMode::Clamp || + ( prim_start_offset >= 0.0 && prim_end_offset <= 1.0 ) // repeat doesn't matter + { + // To support clamping, we need to make sure that quads are emitted for the + // segments before and after the 0.0...1.0 range of offsets. emit_segments + // can handle that by duplicating the first and last point if necessary: + if prim_start_offset < 0.0 { + stops.insert(0, GradientStopKey { + offset: prim_start_offset, + color : stops[0].color + }); + } + + if prim_end_offset > 1.0 { + stops.push( GradientStopKey { + offset: prim_end_offset, + color : stops[stops.len()-1].color + }); + } + + emit_segments(prim_start_offset, prim_end_offset, + 0.0, + prim_start_offset, prim_end_offset, + prim_data.common.prim_rect.origin, + prim_data.common.prim_rect.size, + size, + prim_data.stops_opacity.is_opaque, + &stops, + orientation, + frame_state, + gradient); + } + else + { + let mut segment_start_point = prim_start_offset; + while segment_start_point < prim_end_offset { + + // gradient stops are expressed in the range 0.0 ... 1.0, so to blit + // a copy of the gradient, snap to the integer just before the offset + // we want ... + let gradient_offset_base = segment_start_point.floor(); + // .. and then draw from a start offset in range 0 to 1 ... + let repeat_start = segment_start_point - gradient_offset_base; + // .. up to the next integer, but clamped to the primitive's real + // end offset: + let repeat_end = (gradient_offset_base + 1.0).min(prim_end_offset) - gradient_offset_base; + + emit_segments(repeat_start, repeat_end, + gradient_offset_base, + prim_start_offset, prim_end_offset, + prim_data.common.prim_rect.origin, + prim_data.common.prim_rect.size, + size, + prim_data.stops_opacity.is_opaque, + &stops, + orientation, + frame_state, + gradient); + + segment_start_point = repeat_end + gradient_offset_base; + } + } + } + + if prim_data.tile_spacing != LayoutSize::zero() { + // We are performing the decomposition on the CPU here, no need to + // have it in the shader. + prim_data.common.may_need_repetition = false; + + let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; + + let map_local_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + prim_spatial_node_index, + frame_context.global_screen_world_rect, + frame_context.spatial_tree, + ); + + gradient.visible_tiles_range = decompose_repeated_primitive( + &prim_info.combined_local_clip_rect, + &prim_data.common.prim_rect, + prim_info.clipped_world_rect, + &prim_data.stretch_size, + &prim_data.tile_spacing, + frame_state, + &mut scratch.gradient_tiles, + &map_local_to_world, + &mut |_, mut request| { + request.push([ + prim_data.start_point.x, + prim_data.start_point.y, + prim_data.end_point.x, + prim_data.end_point.y, + ]); + request.push([ + pack_as_float(prim_data.extend_mode as u32), + prim_data.stretch_size.width, + prim_data.stretch_size.height, + 0.0, + ]); + } + ); + + if gradient.visible_tiles_range.is_empty() { + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + } + } + + // TODO(gw): Consider whether it's worth doing segment building + // for gradient primitives. + } + PrimitiveInstanceKind::RadialGradient { data_handle, ref mut visible_tiles_range, .. } => { + profile_scope!("RadialGradient"); + let prim_data = &mut data_stores.radial_grad[*data_handle]; + + if prim_data.stretch_size.width >= prim_data.common.prim_rect.size.width && + prim_data.stretch_size.height >= prim_data.common.prim_rect.size.height { + + // We are performing the decomposition on the CPU here, no need to + // have it in the shader. + prim_data.common.may_need_repetition = false; + } + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + prim_data.update(frame_state); + + if prim_data.tile_spacing != LayoutSize::zero() { + let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; + + let map_local_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + prim_spatial_node_index, + frame_context.global_screen_world_rect, + frame_context.spatial_tree, + ); + + prim_data.common.may_need_repetition = false; + + *visible_tiles_range = decompose_repeated_primitive( + &prim_info.combined_local_clip_rect, + &prim_data.common.prim_rect, + prim_info.clipped_world_rect, + &prim_data.stretch_size, + &prim_data.tile_spacing, + frame_state, + &mut scratch.gradient_tiles, + &map_local_to_world, + &mut |_, mut request| { + request.push([ + prim_data.center.x, + prim_data.center.y, + prim_data.params.start_radius, + prim_data.params.end_radius, + ]); + request.push([ + prim_data.params.ratio_xy, + pack_as_float(prim_data.extend_mode as u32), + prim_data.stretch_size.width, + prim_data.stretch_size.height, + ]); + }, + ); + + if visible_tiles_range.is_empty() { + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + } + } + + // TODO(gw): Consider whether it's worth doing segment building + // for gradient primitives. + } + PrimitiveInstanceKind::ConicGradient { data_handle, ref mut visible_tiles_range, .. } => { + profile_scope!("ConicGradient"); + let prim_data = &mut data_stores.conic_grad[*data_handle]; + + if prim_data.stretch_size.width >= prim_data.common.prim_rect.size.width && + prim_data.stretch_size.height >= prim_data.common.prim_rect.size.height { + + // We are performing the decomposition on the CPU here, no need to + // have it in the shader. + prim_data.common.may_need_repetition = false; + } + + // Update the template this instane references, which may refresh the GPU + // cache with any shared template data. + prim_data.update(frame_state); + + if prim_data.tile_spacing != LayoutSize::zero() { + let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; + + let map_local_to_world = SpaceMapper::new_with_target( + ROOT_SPATIAL_NODE_INDEX, + prim_spatial_node_index, + frame_context.global_screen_world_rect, + frame_context.spatial_tree, + ); + + prim_data.common.may_need_repetition = false; + + *visible_tiles_range = decompose_repeated_primitive( + &prim_info.combined_local_clip_rect, + &prim_data.common.prim_rect, + prim_info.clipped_world_rect, + &prim_data.stretch_size, + &prim_data.tile_spacing, + frame_state, + &mut scratch.gradient_tiles, + &map_local_to_world, + &mut |_, mut request| { + request.push([ + prim_data.center.x, + prim_data.center.y, + prim_data.params.start_offset, + prim_data.params.end_offset, + ]); + request.push([ + prim_data.params.angle, + pack_as_float(prim_data.extend_mode as u32), + prim_data.stretch_size.width, + prim_data.stretch_size.height, + ]); + }, + ); + + if visible_tiles_range.is_empty() { + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + } + } + + // TODO(gw): Consider whether it's worth doing segment building + // for gradient primitives. + } + PrimitiveInstanceKind::Picture { pic_index, segment_instance_index, .. } => { + profile_scope!("Picture"); + let pic = &mut self.pictures[pic_index.0]; + let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize]; + + if pic.prepare_for_render( + frame_context, + frame_state, + data_stores, + ) { + if let Some(ref mut splitter) = pic_state.plane_splitter { + PicturePrimitive::add_split_plane( + splitter, + frame_context.spatial_tree, + prim_spatial_node_index, + pic.precise_local_rect, + &prim_info.combined_local_clip_rect, + frame_state.current_dirty_region().combined, + plane_split_anchor, + ); + } + + // If this picture uses segments, ensure the GPU cache is + // up to date with segment local rects. + // TODO(gw): This entire match statement above can now be + // refactored into prepare_interned_prim_for_render. + if pic.can_use_segments() { + write_segment( + *segment_instance_index, + frame_state, + &mut scratch.segments, + &mut scratch.segment_instances, + |request| { + request.push(PremultipliedColorF::WHITE); + request.push(PremultipliedColorF::WHITE); + request.push([ + -1.0, // -ve means use prim rect for stretch size + 0.0, + 0.0, + 0.0, + ]); + } + ); + } + } else { + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + } + } + PrimitiveInstanceKind::Backdrop { data_handle } => { + profile_scope!("Backdrop"); + let backdrop_pic_index = data_stores.backdrop[*data_handle].kind.pic_index; + + // Setup a dependency on the backdrop picture to ensure it is rendered prior to rendering this primitive. + let backdrop_surface_index = self.pictures[backdrop_pic_index.0].raster_config.as_ref().unwrap().surface_index; + if let Some(backdrop_tasks) = frame_state.surfaces[backdrop_surface_index.0].render_tasks { + let picture_task_id = frame_state.surfaces[pic_context.surface_index.0].render_tasks.as_ref().unwrap().port; + frame_state.render_tasks.add_dependency(picture_task_id, backdrop_tasks.root); + } else { + if prim_instance.is_chased() { + println!("\tBackdrop primitive culled because backdrop task was not assigned render tasks"); + } + prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID; + } + } + }; + } +} + +fn write_segment<F>( + segment_instance_index: SegmentInstanceIndex, + frame_state: &mut FrameBuildingState, + segments: &mut SegmentStorage, + segment_instances: &mut SegmentInstanceStorage, + f: F, +) where F: Fn(&mut GpuDataRequest) { + debug_assert_ne!(segment_instance_index, SegmentInstanceIndex::INVALID); + if segment_instance_index != SegmentInstanceIndex::UNUSED { + let segment_instance = &mut segment_instances[segment_instance_index]; + + if let Some(mut request) = frame_state.gpu_cache.request(&mut segment_instance.gpu_cache_handle) { + let segments = &segments[segment_instance.segments_range]; + + f(&mut request); + + for segment in segments { + request.write_segment( + segment.local_rect, + [0.0; 4], + ); + } + } + } +} + +fn decompose_repeated_primitive( + combined_local_clip_rect: &LayoutRect, + prim_local_rect: &LayoutRect, + prim_world_rect: WorldRect, + stretch_size: &LayoutSize, + tile_spacing: &LayoutSize, + frame_state: &mut FrameBuildingState, + gradient_tiles: &mut GradientTileStorage, + map_local_to_world: &SpaceMapper<LayoutPixel, WorldPixel>, + callback: &mut dyn FnMut(&LayoutRect, GpuDataRequest), +) -> GradientTileRange { + let mut visible_tiles = Vec::new(); + + // Tighten the clip rect because decomposing the repeated image can + // produce primitives that are partially covering the original image + // rect and we want to clip these extra parts out. + let tight_clip_rect = combined_local_clip_rect + .intersection(prim_local_rect).unwrap(); + + let visible_rect = compute_conservative_visible_rect( + &tight_clip_rect, + prim_world_rect, + map_local_to_world, + ); + let stride = *stretch_size + *tile_spacing; + + let repetitions = image_tiling::repetitions(prim_local_rect, &visible_rect, stride); + for Repetition { origin, .. } in repetitions { + let mut handle = GpuCacheHandle::new(); + let rect = LayoutRect { + origin, + size: *stretch_size, + }; + + if let Some(request) = frame_state.gpu_cache.request(&mut handle) { + callback(&rect, request); + } + + visible_tiles.push(VisibleGradientTile { + local_rect: rect, + local_clip_rect: tight_clip_rect, + handle + }); + } + + // At this point if we don't have tiles to show it means we could probably + // have done a better a job at culling during an earlier stage. + // Clearing the screen rect has the effect of "culling out" the primitive + // from the point of view of the batch builder, and ensures we don't hit + // assertions later on because we didn't request any image. + if visible_tiles.is_empty() { + GradientTileRange::empty() + } else { + gradient_tiles.extend(visible_tiles) + } +} + +fn compute_conservative_visible_rect( + local_clip_rect: &LayoutRect, + world_culling_rect: WorldRect, + map_local_to_world: &SpaceMapper<LayoutPixel, WorldPixel>, +) -> LayoutRect { + if let Some(local_bounds) = map_local_to_world.unmap(&world_culling_rect) { + return local_clip_rect.intersection(&local_bounds).unwrap_or_else(LayoutRect::zero) + } + + *local_clip_rect +} + +fn edge_flags_for_tile_spacing(tile_spacing: &LayoutSize) -> EdgeAaSegmentMask { + let mut flags = EdgeAaSegmentMask::empty(); + + if tile_spacing.width > 0.0 { + flags |= EdgeAaSegmentMask::LEFT | EdgeAaSegmentMask::RIGHT; + } + if tile_spacing.height > 0.0 { + flags |= EdgeAaSegmentMask::TOP | EdgeAaSegmentMask::BOTTOM; + } + + flags +} + +impl<'a> GpuDataRequest<'a> { + // Write the GPU cache data for an individual segment. + fn write_segment( + &mut self, + local_rect: LayoutRect, + extra_data: [f32; 4], + ) { + let _ = VECS_PER_SEGMENT; + self.push(local_rect); + self.push(extra_data); + } +} + + fn write_brush_segment_description( + prim_local_rect: LayoutRect, + prim_local_clip_rect: LayoutRect, + clip_chain: &ClipChainInstance, + segment_builder: &mut SegmentBuilder, + clip_store: &ClipStore, + data_stores: &DataStores, + ) -> bool { + // If the brush is small, we want to skip building segments + // and just draw it as a single primitive with clip mask. + if prim_local_rect.size.area() < MIN_BRUSH_SPLIT_AREA { + return false; + } + + segment_builder.initialize( + prim_local_rect, + None, + prim_local_clip_rect + ); + + // Segment the primitive on all the local-space clip sources that we can. + for i in 0 .. clip_chain.clips_range.count { + let clip_instance = clip_store + .get_instance_from_range(&clip_chain.clips_range, i); + let clip_node = &data_stores.clip[clip_instance.handle]; + + // If this clip item is positioned by another positioning node, its relative position + // could change during scrolling. This means that we would need to resegment. Instead + // of doing that, only segment with clips that have the same positioning node. + // TODO(mrobinson, #2858): It may make sense to include these nodes, resegmenting only + // when necessary while scrolling. + if !clip_instance.flags.contains(ClipNodeFlags::SAME_SPATIAL_NODE) { + continue; + } + + let (local_clip_rect, radius, mode) = match clip_node.item.kind { + ClipItemKind::RoundedRectangle { rect, radius, mode } => { + (rect, Some(radius), mode) + } + ClipItemKind::Rectangle { rect, mode } => { + (rect, None, mode) + } + ClipItemKind::BoxShadow { ref source } => { + // For inset box shadows, we can clip out any + // pixels that are inside the shadow region + // and are beyond the inner rect, as they can't + // be affected by the blur radius. + let inner_clip_mode = match source.clip_mode { + BoxShadowClipMode::Outset => None, + BoxShadowClipMode::Inset => Some(ClipMode::ClipOut), + }; + + // Push a region into the segment builder where the + // box-shadow can have an effect on the result. This + // ensures clip-mask tasks get allocated for these + // pixel regions, even if no other clips affect them. + segment_builder.push_mask_region( + source.prim_shadow_rect, + source.prim_shadow_rect.inflate( + -0.5 * source.original_alloc_size.width, + -0.5 * source.original_alloc_size.height, + ), + inner_clip_mode, + ); + + continue; + } + ClipItemKind::Image { .. } => { + // If we encounter an image mask, bail out from segment building. + // It's not possible to know which parts of the primitive are affected + // by the mask (without inspecting the pixels). We could do something + // better here in the future if it ever shows up as a performance issue + // (for instance, at least segment based on the bounding rect of the + // image mask if it's non-repeating). + return false; + } + }; + + segment_builder.push_clip_rect(local_clip_rect, radius, mode); + } + + true + } + +impl PrimitiveInstance { + fn build_segments_if_needed( + &mut self, + prim_info: &PrimitiveVisibility, + frame_state: &mut FrameBuildingState, + prim_store: &mut PrimitiveStore, + data_stores: &DataStores, + segments_store: &mut SegmentStorage, + segment_instances_store: &mut SegmentInstanceStorage, + ) { + let prim_clip_chain = &prim_info.clip_chain; + + // Usually, the primitive rect can be found from information + // in the instance and primitive template. + let prim_local_rect = data_stores.get_local_prim_rect( + self, + prim_store, + ); + + let segment_instance_index = match self.kind { + PrimitiveInstanceKind::Rectangle { ref mut segment_instance_index, .. } | + PrimitiveInstanceKind::YuvImage { ref mut segment_instance_index, .. } => { + segment_instance_index + } + PrimitiveInstanceKind::Image { data_handle, image_instance_index, .. } => { + let image_data = &data_stores.image[data_handle].kind; + let image_instance = &mut prim_store.images[image_instance_index]; + //Note: tiled images don't support automatic segmentation, + // they strictly produce one segment per visible tile instead. + if frame_state + .resource_cache + .get_image_properties(image_data.key) + .and_then(|properties| properties.tiling) + .is_some() + { + image_instance.segment_instance_index = SegmentInstanceIndex::UNUSED; + return; + } + &mut image_instance.segment_instance_index + } + PrimitiveInstanceKind::Picture { ref mut segment_instance_index, pic_index, .. } => { + let pic = &mut prim_store.pictures[pic_index.0]; + + // If this picture supports segment rendering + if pic.can_use_segments() { + // If the segments have been invalidated, ensure the current + // index of segments is invalid. This ensures that the segment + // building logic below will be run. + if !pic.segments_are_valid { + *segment_instance_index = SegmentInstanceIndex::INVALID; + pic.segments_are_valid = true; + } + + segment_instance_index + } else { + return; + } + } + PrimitiveInstanceKind::TextRun { .. } | + PrimitiveInstanceKind::NormalBorder { .. } | + PrimitiveInstanceKind::ImageBorder { .. } | + PrimitiveInstanceKind::Clear { .. } | + PrimitiveInstanceKind::LinearGradient { .. } | + PrimitiveInstanceKind::RadialGradient { .. } | + PrimitiveInstanceKind::ConicGradient { .. } | + PrimitiveInstanceKind::LineDecoration { .. } | + PrimitiveInstanceKind::Backdrop { .. } => { + // These primitives don't support / need segments. + return; + } + }; + + if *segment_instance_index == SegmentInstanceIndex::INVALID { + let mut segments: SmallVec<[BrushSegment; 8]> = SmallVec::new(); + + if write_brush_segment_description( + prim_local_rect, + self.local_clip_rect, + prim_clip_chain, + &mut frame_state.segment_builder, + frame_state.clip_store, + data_stores, + ) { + frame_state.segment_builder.build(|segment| { + segments.push( + BrushSegment::new( + segment.rect.translate(-prim_local_rect.origin.to_vector()), + segment.has_mask, + segment.edge_flags, + [0.0; 4], + BrushFlags::PERSPECTIVE_INTERPOLATION, + ), + ); + }); + } + + // If only a single segment is produced, there is no benefit to writing + // a segment instance array. Instead, just use the main primitive rect + // written into the GPU cache. + // TODO(gw): This is (sortof) a bandaid - due to a limitation in the current + // brush encoding, we can only support a total of up to 2^16 segments. + // This should be (more than) enough for any real world case, so for + // now we can handle this by skipping cases where we were generating + // segments where there is no benefit. The long term / robust fix + // for this is to move the segment building to be done as a more + // limited nine-patch system during scene building, removing arbitrary + // segmentation during frame-building (see bug #1617491). + if segments.len() <= 1 { + *segment_instance_index = SegmentInstanceIndex::UNUSED; + } else { + let segments_range = segments_store.extend(segments); + + let instance = SegmentedInstance { + segments_range, + gpu_cache_handle: GpuCacheHandle::new(), + }; + + *segment_instance_index = segment_instances_store.push(instance); + }; + } + } + + fn update_clip_task_for_brush( + &self, + prim_origin: &LayoutPoint, + prim_info: &mut PrimitiveVisibility, + prim_spatial_node_index: SpatialNodeIndex, + root_spatial_node_index: SpatialNodeIndex, + pic_context: &PictureContext, + pic_state: &mut PictureState, + frame_context: &FrameBuildingContext, + frame_state: &mut FrameBuildingState, + prim_store: &PrimitiveStore, + data_stores: &mut DataStores, + segments_store: &mut SegmentStorage, + segment_instances_store: &mut SegmentInstanceStorage, + clip_mask_instances: &mut Vec<ClipMaskKind>, + unclipped: &DeviceRect, + device_pixel_scale: DevicePixelScale, + ) -> bool { + let segments = match self.kind { + PrimitiveInstanceKind::TextRun { .. } | + PrimitiveInstanceKind::Clear { .. } | + PrimitiveInstanceKind::LineDecoration { .. } | + PrimitiveInstanceKind::Backdrop { .. } => { + return false; + } + PrimitiveInstanceKind::Image { image_instance_index, .. } => { + let segment_instance_index = prim_store + .images[image_instance_index] + .segment_instance_index; + + if segment_instance_index == SegmentInstanceIndex::UNUSED { + return false; + } + + let segment_instance = &segment_instances_store[segment_instance_index]; + + &segments_store[segment_instance.segments_range] + } + PrimitiveInstanceKind::Picture { segment_instance_index, .. } => { + // Pictures may not support segment rendering at all (INVALID) + // or support segment rendering but choose not to due to size + // or some other factor (UNUSED). + if segment_instance_index == SegmentInstanceIndex::UNUSED || + segment_instance_index == SegmentInstanceIndex::INVALID { + return false; + } + + let segment_instance = &segment_instances_store[segment_instance_index]; + &segments_store[segment_instance.segments_range] + } + PrimitiveInstanceKind::YuvImage { segment_instance_index, .. } | + PrimitiveInstanceKind::Rectangle { segment_instance_index, .. } => { + debug_assert!(segment_instance_index != SegmentInstanceIndex::INVALID); + + if segment_instance_index == SegmentInstanceIndex::UNUSED { + return false; + } + + let segment_instance = &segment_instances_store[segment_instance_index]; + + &segments_store[segment_instance.segments_range] + } + PrimitiveInstanceKind::ImageBorder { data_handle, .. } => { + let border_data = &data_stores.image_border[data_handle].kind; + + // TODO: This is quite messy - once we remove legacy primitives we + // can change this to be a tuple match on (instance, template) + border_data.brush_segments.as_slice() + } + PrimitiveInstanceKind::NormalBorder { data_handle, .. } => { + let border_data = &data_stores.normal_border[data_handle].kind; + + // TODO: This is quite messy - once we remove legacy primitives we + // can change this to be a tuple match on (instance, template) + border_data.brush_segments.as_slice() + } + PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { + let prim_data = &data_stores.linear_grad[data_handle]; + + // TODO: This is quite messy - once we remove legacy primitives we + // can change this to be a tuple match on (instance, template) + if prim_data.brush_segments.is_empty() { + return false; + } + + prim_data.brush_segments.as_slice() + } + PrimitiveInstanceKind::RadialGradient { data_handle, .. } => { + let prim_data = &data_stores.radial_grad[data_handle]; + + // TODO: This is quite messy - once we remove legacy primitives we + // can change this to be a tuple match on (instance, template) + if prim_data.brush_segments.is_empty() { + return false; + } + + prim_data.brush_segments.as_slice() + } + PrimitiveInstanceKind::ConicGradient { data_handle, .. } => { + let prim_data = &data_stores.conic_grad[data_handle]; + + // TODO: This is quite messy - once we remove legacy primitives we + // can change this to be a tuple match on (instance, template) + if prim_data.brush_segments.is_empty() { + return false; + } + + prim_data.brush_segments.as_slice() + } + }; + + // If there are no segments, early out to avoid setting a valid + // clip task instance location below. + if segments.is_empty() { + return true; + } + + // Set where in the clip mask instances array the clip mask info + // can be found for this primitive. Each segment will push the + // clip mask information for itself in update_clip_task below. + prim_info.clip_task_index = ClipTaskIndex(clip_mask_instances.len() as _); + + // If we only built 1 segment, there is no point in re-running + // the clip chain builder. Instead, just use the clip chain + // instance that was built for the main primitive. This is a + // significant optimization for the common case. + if segments.len() == 1 { + let clip_mask_kind = segments[0].update_clip_task( + Some(&prim_info.clip_chain), + prim_info.clipped_world_rect, + root_spatial_node_index, + pic_context.surface_index, + pic_state, + frame_context, + frame_state, + &mut data_stores.clip, + unclipped, + device_pixel_scale, + ); + clip_mask_instances.push(clip_mask_kind); + } else { + let dirty_world_rect = frame_state.current_dirty_region().combined; + + for segment in segments { + // Build a clip chain for the smaller segment rect. This will + // often manage to eliminate most/all clips, and sometimes + // clip the segment completely. + frame_state.clip_store.set_active_clips_from_clip_chain( + &prim_info.clip_chain, + prim_spatial_node_index, + &frame_context.spatial_tree, + ); + + let segment_clip_chain = frame_state + .clip_store + .build_clip_chain_instance( + segment.local_rect.translate(prim_origin.to_vector()), + &pic_state.map_local_to_pic, + &pic_state.map_pic_to_world, + &frame_context.spatial_tree, + frame_state.gpu_cache, + frame_state.resource_cache, + device_pixel_scale, + &dirty_world_rect, + &mut data_stores.clip, + false, + self.is_chased(), + ); + + let clip_mask_kind = segment.update_clip_task( + segment_clip_chain.as_ref(), + prim_info.clipped_world_rect, + root_spatial_node_index, + pic_context.surface_index, + pic_state, + frame_context, + frame_state, + &mut data_stores.clip, + unclipped, + device_pixel_scale, + ); + clip_mask_instances.push(clip_mask_kind); + } + } + + true + } + + fn update_clip_task( + &mut self, + prim_origin: &LayoutPoint, + prim_spatial_node_index: SpatialNodeIndex, + root_spatial_node_index: SpatialNodeIndex, + pic_context: &PictureContext, + pic_state: &mut PictureState, + frame_context: &FrameBuildingContext, + frame_state: &mut FrameBuildingState, + prim_store: &mut PrimitiveStore, + data_stores: &mut DataStores, + scratch: &mut PrimitiveScratchBuffer, + ) { + let prim_info = &mut scratch.prim_info[self.visibility_info.0 as usize]; + let device_pixel_scale = frame_state.surfaces[pic_context.surface_index.0].device_pixel_scale; + + if self.is_chased() { + println!("\tupdating clip task with pic rect {:?}", prim_info.clip_chain.pic_clip_rect); + } + + // Get the device space rect for the primitive if it was unclipped. + let unclipped = match get_unclipped_device_rect( + prim_info.clip_chain.pic_clip_rect, + &pic_state.map_pic_to_raster, + device_pixel_scale, + ) { + Some(rect) => rect, + None => return, + }; + + self.build_segments_if_needed( + &prim_info, + frame_state, + prim_store, + data_stores, + &mut scratch.segments, + &mut scratch.segment_instances, + ); + + // First try to render this primitive's mask using optimized brush rendering. + if self.update_clip_task_for_brush( + prim_origin, + prim_info, + prim_spatial_node_index, + root_spatial_node_index, + pic_context, + pic_state, + frame_context, + frame_state, + prim_store, + data_stores, + &mut scratch.segments, + &mut scratch.segment_instances, + &mut scratch.clip_mask_instances, + &unclipped, + device_pixel_scale, + ) { + if self.is_chased() { + println!("\tsegment tasks have been created for clipping"); + } + return; + } + + if prim_info.clip_chain.needs_mask { + // Get a minimal device space rect, clipped to the screen that we + // need to allocate for the clip mask, as well as interpolated + // snap offsets. + if let Some(device_rect) = get_clipped_device_rect( + &unclipped, + &pic_state.map_raster_to_world, + prim_info.clipped_world_rect, + device_pixel_scale, + ) { + let (device_rect, device_pixel_scale) = adjust_mask_scale_for_max_size(device_rect, device_pixel_scale); + + let clip_task_id = RenderTask::new_mask( + device_rect, + prim_info.clip_chain.clips_range, + root_spatial_node_index, + frame_state.clip_store, + frame_state.gpu_cache, + frame_state.resource_cache, + frame_state.render_tasks, + &mut data_stores.clip, + device_pixel_scale, + frame_context.fb_config, + ); + if self.is_chased() { + println!("\tcreated task {:?} with device rect {:?}", + clip_task_id, device_rect); + } + // Set the global clip mask instance for this primitive. + let clip_task_index = ClipTaskIndex(scratch.clip_mask_instances.len() as _); + scratch.clip_mask_instances.push(ClipMaskKind::Mask(clip_task_id)); + prim_info.clip_task_index = clip_task_index; + frame_state.render_tasks.add_dependency( + frame_state.surfaces[pic_context.surface_index.0].render_tasks.unwrap().port, + clip_task_id, + ); + } + } + } +} + +// Ensures that the size of mask render tasks are within MAX_MASK_SIZE. +fn adjust_mask_scale_for_max_size(device_rect: DeviceRect, device_pixel_scale: DevicePixelScale) -> (DeviceIntRect, DevicePixelScale) { + if device_rect.width() > MAX_MASK_SIZE || device_rect.height() > MAX_MASK_SIZE { + // round_out will grow by 1 integer pixel if origin is on a + // fractional position, so keep that margin for error with -1: + let scale = (MAX_MASK_SIZE - 1.0) / + f32::max(device_rect.width(), device_rect.height()); + let new_device_pixel_scale = device_pixel_scale * Scale::new(scale); + let new_device_rect = (device_rect.to_f32() * Scale::new(scale)) + .round_out() + .to_i32(); + (new_device_rect, new_device_pixel_scale) + } else { + (device_rect.to_i32(), device_pixel_scale) + } +} + +/// Retrieve the exact unsnapped device space rectangle for a primitive. +fn get_unclipped_device_rect( + prim_rect: PictureRect, + map_to_raster: &SpaceMapper<PicturePixel, RasterPixel>, + device_pixel_scale: DevicePixelScale, +) -> Option<DeviceRect> { + let raster_rect = map_to_raster.map(&prim_rect)?; + let world_rect = raster_rect * Scale::new(1.0); + Some(world_rect * device_pixel_scale) +} + +/// Given an unclipped device rect, try to find a minimal device space +/// rect to allocate a clip mask for, by clipping to the screen. This +/// function is very similar to get_raster_rects below. It is far from +/// ideal, and should be refactored as part of the support for setting +/// scale per-raster-root. +fn get_clipped_device_rect( + unclipped: &DeviceRect, + map_to_world: &SpaceMapper<RasterPixel, WorldPixel>, + prim_bounding_rect: WorldRect, + device_pixel_scale: DevicePixelScale, +) -> Option<DeviceRect> { + let unclipped_raster_rect = { + let world_rect = *unclipped * Scale::new(1.0); + let raster_rect = world_rect * device_pixel_scale.inverse(); + + raster_rect.cast_unit() + }; + + let unclipped_world_rect = map_to_world.map(&unclipped_raster_rect)?; + + let clipped_world_rect = unclipped_world_rect.intersection(&prim_bounding_rect)?; + + let clipped_raster_rect = map_to_world.unmap(&clipped_world_rect)?; + + let clipped_raster_rect = clipped_raster_rect.intersection(&unclipped_raster_rect)?; + + // Ensure that we won't try to allocate a zero-sized clip render task. + if clipped_raster_rect.is_empty() { + return None; + } + + let clipped = raster_rect_to_device_pixels( + clipped_raster_rect, + device_pixel_scale, + ); + + Some(clipped) +} + +pub fn get_raster_rects( + pic_rect: PictureRect, + map_to_raster: &SpaceMapper<PicturePixel, RasterPixel>, + map_to_world: &SpaceMapper<RasterPixel, WorldPixel>, + prim_bounding_rect: WorldRect, + device_pixel_scale: DevicePixelScale, +) -> Option<(DeviceRect, DeviceRect)> { + let unclipped_raster_rect = map_to_raster.map(&pic_rect)?; + + let unclipped = raster_rect_to_device_pixels( + unclipped_raster_rect, + device_pixel_scale, + ); + + let unclipped_world_rect = map_to_world.map(&unclipped_raster_rect)?; + + let clipped_world_rect = unclipped_world_rect.intersection(&prim_bounding_rect)?; + + let clipped_raster_rect = map_to_world.unmap(&clipped_world_rect)?; + + let clipped_raster_rect = clipped_raster_rect.intersection(&unclipped_raster_rect)?; + + let clipped = raster_rect_to_device_pixels( + clipped_raster_rect, + device_pixel_scale, + ); + + // Ensure that we won't try to allocate a zero-sized clip render task. + if clipped.is_empty() { + return None; + } + + Some((clipped, unclipped)) +} + +/// Choose the decoration mask tile size for a given line. +/// +/// Given a line with overall size `rect_size` and the given `orientation`, +/// return the dimensions of a single mask tile for the decoration pattern +/// described by `style` and `wavy_line_thickness`. +/// +/// If `style` is `Solid`, no mask tile is necessary; return `None`. The other +/// styles each have their own characteristic periods of repetition, so for each +/// one, this function returns a `LayoutSize` with the right aspect ratio and +/// whose specific size is convenient for the `cs_line_decoration.glsl` fragment +/// shader to work with. The shader uses a local coordinate space in which the +/// tile fills a rectangle with one corner at the origin, and with the size this +/// function returns. +/// +/// The returned size is not necessarily in pixels; device scaling and other +/// concerns can still affect the actual task size. +/// +/// Regardless of whether `orientation` is `Vertical` or `Horizontal`, the +/// `width` and `height` of the returned size are always horizontal and +/// vertical, respectively. +pub fn get_line_decoration_size( + rect_size: &LayoutSize, + orientation: LineOrientation, + style: LineStyle, + wavy_line_thickness: f32, +) -> Option<LayoutSize> { + let h = match orientation { + LineOrientation::Horizontal => rect_size.height, + LineOrientation::Vertical => rect_size.width, + }; + + // TODO(gw): The formulae below are based on the existing gecko and line + // shader code. They give reasonable results for most inputs, + // but could definitely do with a detailed pass to get better + // quality on a wider range of inputs! + // See nsCSSRendering::PaintDecorationLine in Gecko. + + let (parallel, perpendicular) = match style { + LineStyle::Solid => { + return None; + } + LineStyle::Dashed => { + let dash_length = (3.0 * h).min(64.0).max(1.0); + + (2.0 * dash_length, 4.0) + } + LineStyle::Dotted => { + let diameter = h.min(64.0).max(1.0); + let period = 2.0 * diameter; + + (period, diameter) + } + LineStyle::Wavy => { + let line_thickness = wavy_line_thickness.max(1.0); + let slope_length = h - line_thickness; + let flat_length = ((line_thickness - 1.0) * 2.0).max(1.0); + let approx_period = 2.0 * (slope_length + flat_length); + + (approx_period, h) + } + }; + + Some(match orientation { + LineOrientation::Horizontal => LayoutSize::new(parallel, perpendicular), + LineOrientation::Vertical => LayoutSize::new(perpendicular, parallel), + }) +} + +fn update_opacity_binding( + opacity_bindings: &mut OpacityBindingStorage, + opacity_binding_index: OpacityBindingIndex, + scene_properties: &SceneProperties, +) { + if opacity_binding_index != OpacityBindingIndex::INVALID { + let binding = &mut opacity_bindings[opacity_binding_index]; + binding.update(scene_properties); + } } /// Trait for primitives that are directly internable. @@ -1414,8 +4520,8 @@ fn test_struct_sizes() { // test expectations and move on. // (b) You made a structure larger. This is not necessarily a problem, but should only // be done with care, and after checking if talos performance regresses badly. - assert_eq!(mem::size_of::<PrimitiveInstance>(), 152, "PrimitiveInstance size changed"); - assert_eq!(mem::size_of::<PrimitiveInstanceKind>(), 24, "PrimitiveInstanceKind size changed"); + assert_eq!(mem::size_of::<PrimitiveInstance>(), 80, "PrimitiveInstance size changed"); + assert_eq!(mem::size_of::<PrimitiveInstanceKind>(), 40, "PrimitiveInstanceKind size changed"); assert_eq!(mem::size_of::<PrimitiveTemplate>(), 56, "PrimitiveTemplate size changed"); assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 28, "PrimitiveTemplateKind size changed"); assert_eq!(mem::size_of::<PrimitiveKey>(), 36, "PrimitiveKey size changed"); diff --git a/third_party/webrender/webrender/src/prim_store/picture.rs b/third_party/webrender/webrender/src/prim_store/picture.rs index d0815cdac87..46a52e0a807 100644 --- a/third_party/webrender/webrender/src/prim_store/picture.rs +++ b/third_party/webrender/webrender/src/prim_store/picture.rs @@ -60,7 +60,7 @@ pub enum FilterPrimitiveKey { Identity(ColorSpace, FilterPrimitiveInput), Flood(ColorSpace, ColorU), Blend(ColorSpace, MixBlendMode, FilterPrimitiveInput, FilterPrimitiveInput), - Blur(ColorSpace, Au, Au, FilterPrimitiveInput), + Blur(ColorSpace, Au, FilterPrimitiveInput), Opacity(ColorSpace, Au, FilterPrimitiveInput), ColorMatrix(ColorSpace, [Au; 20], FilterPrimitiveInput), DropShadow(ColorSpace, (VectorKey, Au, ColorU), FilterPrimitiveInput), @@ -79,7 +79,7 @@ pub enum PictureCompositeKey { Identity, // FilterOp - Blur(Au, Au), + Blur(Au), Brightness(Au), Contrast(Au), Grayscale(Au), @@ -140,8 +140,7 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey { } Some(PictureCompositeMode::Filter(op)) => { match op { - Filter::Blur(width, height) => - PictureCompositeKey::Blur(Au::from_f32_px(width), Au::from_f32_px(height)), + Filter::Blur(value) => PictureCompositeKey::Blur(Au::from_f32_px(value)), Filter::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)), Filter::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)), Filter::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)), @@ -189,8 +188,7 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey { FilterPrimitiveKind::Identity(identity) => FilterPrimitiveKey::Identity(primitive.color_space, identity.input), FilterPrimitiveKind::Blend(blend) => FilterPrimitiveKey::Blend(primitive.color_space, blend.mode, blend.input1, blend.input2), FilterPrimitiveKind::Flood(flood) => FilterPrimitiveKey::Flood(primitive.color_space, flood.color.into()), - FilterPrimitiveKind::Blur(blur) => - FilterPrimitiveKey::Blur(primitive.color_space, Au::from_f32_px(blur.width), Au::from_f32_px(blur.height), blur.input), + FilterPrimitiveKind::Blur(blur) => FilterPrimitiveKey::Blur(primitive.color_space, Au::from_f32_px(blur.radius), blur.input), FilterPrimitiveKind::Opacity(opacity) => FilterPrimitiveKey::Opacity(primitive.color_space, Au::from_f32_px(opacity.opacity), opacity.input), FilterPrimitiveKind::ColorMatrix(color_matrix) => { @@ -277,7 +275,6 @@ impl Internable for Picture { type Key = PictureKey; type StoreData = PictureTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_PICTURES; } impl InternablePrimitive for Picture { diff --git a/third_party/webrender/webrender/src/prim_store/text_run.rs b/third_party/webrender/webrender/src/prim_store/text_run.rs index 2affe657b11..a5c96e3c11d 100644 --- a/third_party/webrender/webrender/src/prim_store/text_run.rs +++ b/third_party/webrender/webrender/src/prim_store/text_run.rs @@ -3,28 +3,26 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{ColorF, FontInstanceFlags, GlyphInstance, RasterSpace, Shadow}; -use api::units::{LayoutToWorldTransform, LayoutVector2D}; +use api::units::{LayoutToWorldTransform, LayoutVector2D, PictureRect}; use crate::scene_building::{CreateShadow, IsVisible}; use crate::frame_builder::FrameBuildingState; use crate::glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT}; use crate::gpu_cache::GpuCache; use crate::intern; use crate::internal_types::LayoutPrimitiveInfo; -use crate::picture::SurfaceInfo; +use crate::picture::{SubpixelMode, SurfaceInfo}; use crate::prim_store::{PrimitiveOpacity, PrimitiveScratchBuffer}; use crate::prim_store::{PrimitiveStore, PrimKeyCommonData, PrimTemplateCommonData}; +use crate::render_task_graph::RenderTaskGraph; use crate::renderer::{MAX_VERTEX_TEXTURE_WIDTH}; use crate::resource_cache::{ResourceCache}; use crate::util::{MatrixHelpers}; -use crate::prim_store::{InternablePrimitive, PrimitiveInstanceKind}; -use crate::spatial_tree::{SpatialTree, SpatialNodeIndex, ROOT_SPATIAL_NODE_INDEX}; -use crate::space::SpaceSnapper; -use crate::util::PrimaryArc; - +use crate::prim_store::{InternablePrimitive, PrimitiveInstanceKind, SpaceSnapper}; +use crate::spatial_tree::{SpatialTree, SpatialNodeIndex}; use std::ops; use std::sync::Arc; - -use super::storage; +use crate::storage; +use crate::util::PrimaryArc; /// A run of glyphs, with associated font information. #[cfg_attr(feature = "capture", derive(Serialize))] @@ -35,7 +33,6 @@ pub struct TextRunKey { pub font: FontInstance, pub glyphs: PrimaryArc<Vec<GlyphInstance>>, pub shadow: bool, - pub requested_raster_space: RasterSpace, } impl TextRunKey { @@ -48,7 +45,6 @@ impl TextRunKey { font: text_run.font, glyphs: PrimaryArc(text_run.glyphs), shadow: text_run.shadow, - requested_raster_space: text_run.requested_raster_space, } } } @@ -148,14 +144,12 @@ pub struct TextRun { #[ignore_malloc_size_of = "Measured via PrimaryArc"] pub glyphs: Arc<Vec<GlyphInstance>>, pub shadow: bool, - pub requested_raster_space: RasterSpace, } impl intern::Internable for TextRun { type Key = TextRunKey; type StoreData = TextRunTemplate; type InternData = (); - const PROFILE_COUNTER: usize = crate::profiler::INTERNED_TEXT_RUNS; } impl InternablePrimitive for TextRun { @@ -181,8 +175,7 @@ impl InternablePrimitive for TextRun { reference_frame_relative_offset, snapped_reference_frame_relative_offset: reference_frame_relative_offset, shadow: key.shadow, - raster_scale: 1.0, - requested_raster_space: key.requested_raster_space, + raster_space: RasterSpace::Screen, }); PrimitiveInstanceKind::TextRun{ data_handle, run_index } @@ -190,12 +183,7 @@ impl InternablePrimitive for TextRun { } impl CreateShadow for TextRun { - fn create_shadow( - &self, - shadow: &Shadow, - blur_is_noop: bool, - current_raster_space: RasterSpace, - ) -> Self { + fn create_shadow(&self, shadow: &Shadow) -> Self { let mut font = FontInstance { color: shadow.color.into(), ..self.font.clone() @@ -204,17 +192,10 @@ impl CreateShadow for TextRun { font.disable_subpixel_aa(); } - let requested_raster_space = if blur_is_noop { - current_raster_space - } else { - RasterSpace::Local(1.0) - }; - TextRun { font, glyphs: self.glyphs.clone(), shadow: true, - requested_raster_space, } } } @@ -233,8 +214,7 @@ pub struct TextRunPrimitive { pub reference_frame_relative_offset: LayoutVector2D, pub snapped_reference_frame_relative_offset: LayoutVector2D, pub shadow: bool, - pub raster_scale: f32, - pub requested_raster_space: RasterSpace, + pub raster_space: RasterSpace, } impl TextRunPrimitive { @@ -244,8 +224,9 @@ impl TextRunPrimitive { surface: &SurfaceInfo, spatial_node_index: SpatialNodeIndex, transform: &LayoutToWorldTransform, - mut allow_subpixel: bool, + subpixel_mode: &SubpixelMode, raster_space: RasterSpace, + prim_rect: PictureRect, root_scaling_factor: f32, spatial_tree: &SpatialTree, ) -> bool { @@ -255,8 +236,8 @@ impl TextRunPrimitive { // will implicitly be part of the device pixel ratio for // the (cached) local space surface, and so this code // will no longer be required. - let raster_scale = raster_space.local_scale().unwrap_or(1.0).max(0.001); + let raster_scale = raster_space.local_scale().unwrap_or(1.0).max(0.001); // root_scaling_factor is used to scale very large pictures that establish // a raster root back to something sane, thus scale the device size accordingly. // to the shader it looks like a change in DPI which it already supports. @@ -282,7 +263,7 @@ impl TextRunPrimitive { let font_transform = if transform_glyphs { // Get the font transform matrix (skew / scale) from the complete transform. // Fold in the device pixel scale. - self.raster_scale = 1.0; + self.raster_space = RasterSpace::Screen; FontTransform::from(transform) } else { if oversized { @@ -294,12 +275,12 @@ impl TextRunPrimitive { // Record the raster space the text needs to be snapped in. The original raster // scale would have been too big. - self.raster_scale = limited_raster_scale; + self.raster_space = RasterSpace::Local(limited_raster_scale); } else { // Record the raster space the text needs to be snapped in. We may have changed // from RasterSpace::Screen due to a transform with perspective or without a 2d // inverse, or it may have been RasterSpace::Local all along. - self.raster_scale = raster_scale; + self.raster_space = RasterSpace::Local(raster_scale); } // Rasterize the glyph without any transform @@ -353,6 +334,21 @@ impl TextRunPrimitive { ..specified_font.clone() }; + // If subpixel AA is disabled due to the backing surface the glyphs + // are being drawn onto, disable it (unless we are using the + // specifial subpixel mode that estimates background color). + let mut allow_subpixel = match subpixel_mode { + SubpixelMode::Allow => true, + SubpixelMode::Deny => false, + SubpixelMode::Conditional { allowed_rect, excluded_rects } => { + // Conditional mode allows subpixel AA to be enabled for this + // text run, so long as it doesn't intersect with any of the + // cutout rectangles in the list, and it's inside the allowed rect. + allowed_rect.contains_rect(&prim_rect) && + excluded_rects.iter().all(|rect| !rect.intersects(&prim_rect)) + } + }; + // If we are using special estimated background subpixel blending, then // we can allow it regardless of what the surface says. allow_subpixel |= self.used_font.bg_color.a != 0; @@ -374,59 +370,32 @@ impl TextRunPrimitive { cache_dirty } - /// Gets the raster space to use when rendering this primitive. - /// Usually this would be the requested raster space. However, if - /// the primitive's spatial node or one of its ancestors is being pinch zoomed - /// then we round it. This prevents us rasterizing glyphs for every minor - /// change in zoom level, as that would be too expensive. - fn get_raster_space_for_prim( - &self, - prim_spatial_node_index: SpatialNodeIndex, - spatial_tree: &SpatialTree, - ) -> RasterSpace { - let prim_spatial_node = &spatial_tree.spatial_nodes[prim_spatial_node_index.0 as usize]; - if prim_spatial_node.is_ancestor_or_self_zooming { - let scale_factors = spatial_tree - .get_relative_transform(prim_spatial_node_index, ROOT_SPATIAL_NODE_INDEX) - .scale_factors(); - - // Round the scale up to the nearest power of 2, but don't exceed 8. - let scale = scale_factors.0.max(scale_factors.1).min(8.0); - let rounded_up = 2.0f32.powf(scale.log2().ceil()); - - RasterSpace::Local(rounded_up) - } else { - self.requested_raster_space - } - } - pub fn request_resources( &mut self, prim_offset: LayoutVector2D, + prim_rect: PictureRect, specified_font: &FontInstance, glyphs: &[GlyphInstance], transform: &LayoutToWorldTransform, surface: &SurfaceInfo, spatial_node_index: SpatialNodeIndex, + raster_space: RasterSpace, root_scaling_factor: f32, - allow_subpixel: bool, + subpixel_mode: &SubpixelMode, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, + render_tasks: &mut RenderTaskGraph, spatial_tree: &SpatialTree, scratch: &mut PrimitiveScratchBuffer, ) { - let raster_space = self.get_raster_space_for_prim( - spatial_node_index, - spatial_tree, - ); - let cache_dirty = self.update_font_instance( specified_font, surface, spatial_node_index, transform, - allow_subpixel, + subpixel_mode, raster_space, + prim_rect, root_scaling_factor, spatial_tree, ); @@ -435,7 +404,7 @@ impl TextRunPrimitive { let subpx_dir = self.used_font.get_subpx_dir(); let dps = surface.device_pixel_scale.0 * root_scaling_factor; - let transform = match raster_space { + let transform = match self.raster_space { RasterSpace::Local(scale) => FontTransform::new(scale * dps, 0.0, 0.0, scale * dps), RasterSpace::Screen => self.used_font.transform.scale(dps), }; @@ -452,6 +421,7 @@ impl TextRunPrimitive { self.used_font.clone(), &scratch.glyph_keys[self.glyph_keys_range], gpu_cache, + render_tasks, ); } } @@ -467,8 +437,8 @@ fn test_struct_sizes() { // test expectations and move on. // (b) You made a structure larger. This is not necessarily a problem, but should only // be done with care, and after checking if talos performance regresses badly. - assert_eq!(mem::size_of::<TextRun>(), 64, "TextRun size changed"); + assert_eq!(mem::size_of::<TextRun>(), 56, "TextRun size changed"); assert_eq!(mem::size_of::<TextRunTemplate>(), 80, "TextRunTemplate size changed"); - assert_eq!(mem::size_of::<TextRunKey>(), 80, "TextRunKey size changed"); + assert_eq!(mem::size_of::<TextRunKey>(), 72, "TextRunKey size changed"); assert_eq!(mem::size_of::<TextRunPrimitive>(), 80, "TextRunPrimitive size changed"); } diff --git a/third_party/webrender/webrender/src/profiler.rs b/third_party/webrender/webrender/src/profiler.rs index 702fa634ffb..cd4d6f5069c 100644 --- a/third_party/webrender/webrender/src/profiler.rs +++ b/third_party/webrender/webrender/src/profiler.rs @@ -2,772 +2,1093 @@ * 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/. */ -//! # Overlay profiler -//! -//! ## Profiler UI string syntax -//! -//! Comma-separated list of of tokens with trailing and leading spaces trimmed. -//! Each tokens can be: -//! - A counter name with an optional prefix. The name corresponds to the displayed name (see the -//! counters vector below. -//! - By default (no prefix) the counter is shown as average + max over half a second. -//! - With a '#' prefix the counter is shown as a graph. -//! - With a '*' prefix the counter is shown as a change indicator. -//! - Some special counters such as GPU time queries have specific visualizations ignoring prefixes. -//! - A preset name to append the preset to the UI (see PROFILER_PRESETS). -//! - An empty token to insert a bit of vertical space. -//! - A '|' token to start a new column. -//! - A '_' token to start a new row. - use api::{ColorF, ColorU}; -use crate::renderer::DebugRenderer; -use crate::device::query::GpuTimer; +use crate::debug_render::DebugRenderer; +use crate::device::query::{GpuSampler, GpuTimer, NamedTag}; use euclid::{Point2D, Rect, Size2D, vec2, default}; use crate::internal_types::FastHashMap; -use crate::renderer::{FullFrameStats, MAX_VERTEX_TEXTURE_WIDTH, wr_has_been_initialized}; -use api::units::DeviceIntSize; +use crate::renderer::{MAX_VERTEX_TEXTURE_WIDTH, wr_has_been_initialized}; use std::collections::vec_deque::VecDeque; -use std::fmt::{Write, Debug}; -use std::f32; +use std::{f32, mem}; use std::ffi::CStr; use std::ops::Range; use std::time::Duration; use time::precise_time_ns; -macro_rules! set_text { - ($dst:expr, $($arg:tt)*) => { - $dst.clear(); - write!($dst, $($arg)*).unwrap(); - }; +pub mod expected { + use std::ops::Range; + pub const AVG_BACKEND_CPU_TIME: Range<f64> = 0.0..3.0; + pub const MAX_BACKEND_CPU_TIME: Range<f64> = 0.0..6.0; + pub const AVG_RENDERER_CPU_TIME: Range<f64> = 0.0..5.0; + pub const MAX_RENDERER_CPU_TIME: Range<f64> = 0.0..10.0; + pub const AVG_IPC_TIME: Range<f64> = 0.0..2.0; + pub const MAX_IPC_TIME: Range<f64> = 0.0..4.0; + pub const AVG_GPU_TIME: Range<f64> = 0.0..8.0; + pub const MAX_GPU_TIME: Range<f64> = 0.0..15.0; + pub const DRAW_CALLS: Range<u64> = 1..100; + pub const VERTICES: Range<u64> = 10..25_000; + pub const TOTAL_PRIMITIVES: Range<u64> = 1..5000; + pub const VISIBLE_PRIMITIVES: Range<u64> = 1..5000; + pub const USED_TARGETS: Range<u64> = 1..4; + pub const COLOR_PASSES: Range<u64> = 1..4; + pub const ALPHA_PASSES: Range<u64> = 0..3; + pub const RENDERED_PICTURE_CACHE_TILES: Range<u64> = 0..5; + pub const TOTAL_PICTURE_CACHE_TILES: Range<u64> = 0..15; + pub const CREATED_TARGETS: Range<u64> = 0..3; + pub const CHANGED_TARGETS: Range<u64> = 0..3; + pub const TEXTURE_DATA_UPLOADED: Range<u64> = 0..10; + pub const GPU_CACHE_ROWS_TOTAL: Range<u64> = 1..50; + pub const GPU_CACHE_ROWS_UPDATED: Range<u64> = 0..25; + pub const GPU_CACHE_BLOCKS_TOTAL: Range<u64> = 1..65_000; + pub const GPU_CACHE_BLOCKS_UPDATED: Range<u64> = 0..1000; + pub const GPU_CACHE_BLOCKS_SAVED: Range<u64> = 0..50_000; + pub const DISPLAY_LIST_BUILD_TIME: Range<f64> = 0.0..3.0; + pub const MAX_SCENE_BUILD_TIME: Range<f64> = 0.0..3.0; + pub const DISPLAY_LIST_SEND_TIME: Range<f64> = 0.0..1.0; + pub const DISPLAY_LIST_TOTAL_TIME: Range<f64> = 0.0..4.0; + pub const NUM_FONT_TEMPLATES: Range<usize> = 0..50; + pub const FONT_TEMPLATES_MB: Range<f32> = 0.0..40.0; + pub const NUM_IMAGE_TEMPLATES: Range<usize> = 0..20; + pub const IMAGE_TEMPLATES_MB: Range<f32> = 0.0..10.0; + pub const DISPLAY_LIST_MB: Range<f32> = 0.0..0.2; + pub const NUM_RASTERIZED_BLOBS: Range<usize> = 0..25; // in tiles + pub const RASTERIZED_BLOBS_MB: Range<f32> = 0.0..4.0; } const GRAPH_WIDTH: f32 = 1024.0; const GRAPH_HEIGHT: f32 = 320.0; const GRAPH_PADDING: f32 = 8.0; const GRAPH_FRAME_HEIGHT: f32 = 16.0; -const PROFILE_SPACING: f32 = 15.0; -const PROFILE_PADDING: f32 = 10.0; -const BACKGROUND_COLOR: ColorU = ColorU { r: 20, g: 20, b: 20, a: 220 }; - -const ONE_SECOND_NS: u64 = 1_000_000_000; - -/// Profiler UI string presets. Defined in the profiler UI string syntax, can contain other presets. -static PROFILER_PRESETS: &'static[(&'static str, &'static str)] = &[ - // Default view, doesn't show everything, but still shows quite a bit. - (&"Default", &"FPS,|,Slow indicators,_,Time graphs,|,Frame times, ,Transaction times, ,Frame stats, ,Memory, ,Interners,_,GPU time queries,_,Paint phase graph"), - // Smaller, less intrusive overview - (&"Compact", &"FPS, ,Frame times, ,Frame stats"), - // Even less intrusive, only slow transactions and frame indicators. - (&"Slow indicators", &"*Slow transaction,*Slow frame"), - - // Counters: - - // Timing information for per layout transaction stages. - (&"Transaction times", &"DisplayList,Scene building,Content send,API send"), - // Timing information for per-frame stages. - (&"Frame times", &"Frame CPU total,Frame building,Visibility,Prepare,Batching,Glyph resolve,Texture cache update,Renderer,GPU"), - // Stats about the content of the frame. - (&"Frame stats", &"Primitives,Visible primitives,Draw calls,Vertices,Color passes,Alpha passes,Rendered picture tiles,Rasterized glyphs"), - // Texture cache allocation stats. - (&"Texture cache stats", &"Texture cache RGBA8 linear textures, Texture cache RGBA8 linear pixels, Texture cache RGBA8 linear pressure, - , ,Texture cache RGBA8 glyphs textures, Texture cache RGBA8 glyphs pixels, Texture cache RGBA8 glyphs pressure, - , ,Texture cache A8 glyphs textures, Texture cache A8 glyphs pixels, Texture cache A8 glyphs pressure, - , ,Texture cache A8 textures, Texture cache A8 pixels, Texture cache A8 pressure, - , ,Texture cache A16 textures, Texture cache A16 pixels, Texture cache A16 pressure, - , ,Texture cache RGBA8 nearest textures, Texture cache RGBA8 nearest pixels, Texture cache RGBA8 nearest pressure, - , ,Texture cache shared mem, Texture cache standalone mem, Texture cache standalone pressure, - , ,Texture cache eviction count, Texture cache youngest evicted" - ), - // Graphs to investigate driver overhead of texture cache updates. - (&"Texture upload perf", &"#Texture cache update,#Texture cache upload, ,#Staging CPU allocation,#Staging GPU allocation,#Staging CPU copy,#Staging GPU copy,#Upload time, ,#Upload copy batches,#Rasterized glyphs, ,#Cache texture creation,#Cache texture deletion"), - - // Graphs: - - // Graph overview of time spent in WebRender's main stages. - (&"Time graphs", &"#DisplayList,#Scene building,#Blob rasterization, ,#Frame CPU total,#Frame building,#Renderer,#Texture cache update, ,#GPU,"), - // Useful when investigating render backend bottlenecks. - (&"Backend graphs", &"#Frame building, #Visibility, #Prepare, #Batching, #Glyph resolve"), - // Useful when investigating renderer bottlenecks. - (&"Renderer graphs", &"#Rendered picture tiles,#Draw calls,#Rasterized glyphs,#Texture uploads,#Texture uploads mem, ,#Texture cache update,#Renderer,"), - - // Misc: - - (&"Memory", &"Image templates,Image templates mem,Font templates,Font templates mem,DisplayList mem,Picture tiles mem"), - (&"Interners", "Interned primitives,Interned clips,Interned pictures,Interned text runs,Interned normal borders,Interned image borders,Interned images,Interned YUV images,Interned line decorations,Interned linear gradients,Interned radial gradients,Interned conic gradients,Interned filter data,Interned backdrops"), - // Gpu sampler queries (need the pref gfx.webrender.debug.gpu-sampler-queries). - (&"GPU samplers", &"Alpha targets samplers,Transparent pass samplers,Opaque pass samplers,Total samplers"), -]; - -fn find_preset(name: &str) -> Option<&'static str> { - for preset in PROFILER_PRESETS { - if preset.0 == name { - return Some(preset.1); - } - } +const PROFILE_PADDING: f32 = 8.0; - None +const ONE_SECOND_NS: u64 = 1000000000; +const AVERAGE_OVER_NS: u64 = ONE_SECOND_NS / 2; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum ProfileStyle { + Full, + Compact, + Smart, } -// The indices here must match the PROFILE_COUNTERS array (checked at runtime). -pub const FRAME_BUILDING_TIME: usize = 0; -pub const FRAME_VISIBILITY_TIME: usize = 1; -pub const FRAME_PREPARE_TIME: usize = 2; -pub const FRAME_BATCHING_TIME: usize = 3; - -pub const RENDERER_TIME: usize = 4; -pub const TOTAL_FRAME_CPU_TIME: usize = 5; -pub const GPU_TIME: usize = 6; - -pub const CONTENT_SEND_TIME: usize = 7; -pub const API_SEND_TIME: usize = 8; - -pub const DISPLAY_LIST_BUILD_TIME: usize = 9; -pub const DISPLAY_LIST_MEM: usize = 10; - -pub const SCENE_BUILD_TIME: usize = 11; - -pub const RASTERIZED_BLOBS: usize = 12; -pub const RASTERIZED_BLOB_TILES: usize = 13; -pub const RASTERIZED_BLOBS_PX: usize = 14; -pub const BLOB_RASTERIZATION_TIME: usize = 15; - -pub const RASTERIZED_GLYPHS: usize = 16; -pub const GLYPH_RESOLVE_TIME: usize = 17; - -pub const DRAW_CALLS: usize = 18; -pub const VERTICES: usize = 19; -pub const PRIMITIVES: usize = 20; -pub const VISIBLE_PRIMITIVES: usize = 21; - -pub const USED_TARGETS: usize = 22; -pub const CREATED_TARGETS: usize = 23; -pub const PICTURE_CACHE_SLICES: usize = 24; - -pub const COLOR_PASSES: usize = 25; -pub const ALPHA_PASSES: usize = 26; -pub const PICTURE_TILES: usize = 27; -pub const PICTURE_TILES_MEM: usize = 28; -pub const RENDERED_PICTURE_TILES: usize = 29; -pub const TEXTURE_UPLOADS: usize = 30; -pub const TEXTURE_UPLOADS_MEM: usize = 31; - -pub const FONT_TEMPLATES: usize = 32; -pub const FONT_TEMPLATES_MEM: usize = 33; -pub const IMAGE_TEMPLATES: usize = 34; -pub const IMAGE_TEMPLATES_MEM: usize = 35; - -pub const GPU_CACHE_ROWS_TOTAL: usize = 36; -pub const GPU_CACHE_ROWS_UPDATED: usize = 37; -pub const GPU_CACHE_BLOCKS_TOTAL: usize = 38; -pub const GPU_CACHE_BLOCKS_UPDATED: usize = 39; -pub const GPU_CACHE_BLOCKS_SAVED: usize = 40; - -pub const TEXTURE_CACHE_A8_PIXELS: usize = 41; -pub const TEXTURE_CACHE_A8_TEXTURES: usize = 42; -pub const TEXTURE_CACHE_A16_PIXELS: usize = 43; -pub const TEXTURE_CACHE_A16_TEXTURES: usize = 44; -pub const TEXTURE_CACHE_RGBA8_LINEAR_PIXELS: usize = 45; -pub const TEXTURE_CACHE_RGBA8_LINEAR_TEXTURES: usize = 46; -pub const TEXTURE_CACHE_RGBA8_NEAREST_PIXELS: usize = 47; -pub const TEXTURE_CACHE_RGBA8_NEAREST_TEXTURES: usize = 48; -pub const TEXTURE_CACHE_SHARED_MEM: usize = 49; -pub const TEXTURE_CACHE_STANDALONE_MEM: usize = 50; - -pub const SLOW_FRAME: usize = 51; -pub const SLOW_TXN: usize = 52; - -pub const GPU_CACHE_UPLOAD_TIME: usize = 53; -pub const TEXTURE_CACHE_UPDATE_TIME: usize = 54; - -pub const FRAME_TIME: usize = 55; - -pub const ALPHA_TARGETS_SAMPLERS: usize = 56; -pub const TRANSPARENT_PASS_SAMPLERS: usize = 57; -pub const OPAQUE_PASS_SAMPLERS: usize = 58; -pub const TOTAL_SAMPLERS: usize = 59; - -pub const INTERNED_PRIMITIVES: usize = 60; -pub const INTERNED_CLIPS: usize = 61; -pub const INTERNED_TEXT_RUNS: usize = 62; -pub const INTERNED_NORMAL_BORDERS: usize = 63; -pub const INTERNED_IMAGE_BORDERS: usize = 64; -pub const INTERNED_IMAGES: usize = 65; -pub const INTERNED_YUV_IMAGES: usize = 66; -pub const INTERNED_LINE_DECORATIONS: usize = 67; -pub const INTERNED_LINEAR_GRADIENTS: usize = 68; -pub const INTERNED_RADIAL_GRADIENTS: usize = 69; -pub const INTERNED_CONIC_GRADIENTS: usize = 70; -pub const INTERNED_PICTURES: usize = 71; -pub const INTERNED_FILTER_DATA: usize = 72; -pub const INTERNED_BACKDROPS: usize = 73; -pub const INTERNED_POLYGONS: usize = 74; - -pub const TEXTURE_CACHE_RGBA8_GLYPHS_PIXELS: usize = 75; -pub const TEXTURE_CACHE_RGBA8_GLYPHS_TEXTURES: usize = 76; -pub const TEXTURE_CACHE_A8_GLYPHS_PIXELS: usize = 77; -pub const TEXTURE_CACHE_A8_GLYPHS_TEXTURES: usize = 78; - -pub const CPU_TEXTURE_ALLOCATION_TIME: usize = 79; -pub const STAGING_TEXTURE_ALLOCATION_TIME: usize = 80; -pub const UPLOAD_CPU_COPY_TIME: usize = 81; -pub const UPLOAD_GPU_COPY_TIME: usize = 82; -pub const UPLOAD_TIME: usize = 83; -pub const UPLOAD_NUM_COPY_BATCHES: usize = 84; -pub const TOTAL_UPLOAD_TIME: usize = 85; -pub const CREATE_CACHE_TEXTURE_TIME: usize = 86; -pub const DELETE_CACHE_TEXTURE_TIME: usize = 87; - -pub const TEXTURE_CACHE_COLOR8_LINEAR_PRESSURE: usize = 88; -pub const TEXTURE_CACHE_COLOR8_NEAREST_PRESSURE: usize = 89; -pub const TEXTURE_CACHE_COLOR8_GLYPHS_PRESSURE: usize = 90; -pub const TEXTURE_CACHE_ALPHA8_PRESSURE: usize = 91; -pub const TEXTURE_CACHE_ALPHA8_GLYPHS_PRESSURE: usize = 92; -pub const TEXTURE_CACHE_ALPHA16_PRESSURE: usize = 93; -pub const TEXTURE_CACHE_STANDALONE_PRESSURE: usize = 94; -pub const TEXTURE_CACHE_EVICTION_COUNT: usize = 95; -pub const TEXTURE_CACHE_YOUNGEST_EVICTION: usize = 96; - -pub const NUM_PROFILER_EVENTS: usize = 97; +/// Defines the interface for hooking up an external profiler to WR. +pub trait ProfilerHooks : Send + Sync { + /// Called at the beginning of a profile scope. The label must + /// be a C string (null terminated). + fn begin_marker(&self, label: &CStr); -pub struct Profiler { - counters: Vec<Counter>, - gpu_frames: ProfilerFrameCollection, - frame_stats: ProfilerFrameCollection, + /// Called at the end of a profile scope. The label must + /// be a C string (null terminated). + fn end_marker(&self, label: &CStr); - start: u64, - avg_over_period: u64, - num_graph_samples: usize, + /// Called to mark an event happening. The label must + /// be a C string (null terminated). + fn event_marker(&self, label: &CStr); - // For FPS computation. Updated in update(). - frame_timestamps_within_last_second: Vec<u64>, + /// 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. + /// + /// This variant is also useful when the caller only wants to record events + /// longer than a certain threshold, and thus they don't know in advance + /// whether the event will qualify. + fn add_text_marker(&self, label: &CStr, text: &str, duration: Duration); - ui: Vec<Item>, + /// Returns true if the current thread is being profiled. + fn thread_is_being_profiled(&self) -> bool; } -impl Profiler { - pub fn new() -> Self { +/// The current global profiler callbacks, if set by embedder. +pub static mut PROFILER_HOOKS: Option<&'static dyn ProfilerHooks> = None; - fn float(name: &'static str, unit: &'static str, index: usize, expected: Expected<f64>) -> CounterDescriptor { - CounterDescriptor { name, unit, show_as: ShowAs::Float, index, expected } +/// Set the profiler callbacks, or None to disable the profiler. +/// This function must only ever be called before any WR instances +/// have been created, or the hooks will not be set. +pub fn set_profiler_hooks(hooks: Option<&'static dyn ProfilerHooks>) { + if !wr_has_been_initialized() { + unsafe { + PROFILER_HOOKS = hooks; } + } +} - fn int(name: &'static str, unit: &'static str, index: usize, expected: Expected<i64>) -> CounterDescriptor { - CounterDescriptor { name, unit, show_as: ShowAs::Int, index, expected: expected.into_float() } +/// A simple RAII style struct to manage a profile scope. +pub struct ProfileScope { + name: &'static CStr, +} + +/// Records a marker of the given duration that just ended. +pub fn add_text_marker(label: &CStr, text: &str, duration: Duration) { + unsafe { + if let Some(ref hooks) = PROFILER_HOOKS { + hooks.add_text_marker(label, text, duration); } + } +} - // Not in the list below: - // - "GPU time queries" shows the details of the GPU time queries if selected as a graph. - // - "GPU cache bars" shows some info about the GPU cache. - - // TODO: This should be a global variable but to keep things readable we need to be able to - // use match in const fn which isn't supported by the current rustc version in gecko's build - // system. - let profile_counters = &[ - float("Frame building", "ms", FRAME_BUILDING_TIME, expected(0.0..6.0).avg(0.0..3.0)), - - float("Visibility", "ms", FRAME_VISIBILITY_TIME, expected(0.0..3.0).avg(0.0..2.0)), - float("Prepare", "ms", FRAME_PREPARE_TIME, expected(0.0..3.0).avg(0.0..2.0)), - float("Batching", "ms", FRAME_BATCHING_TIME, expected(0.0..3.0).avg(0.0..2.0)), - - float("Renderer", "ms", RENDERER_TIME, expected(0.0..8.0).avg(0.0..5.0)), - float("Frame CPU total", "ms", TOTAL_FRAME_CPU_TIME, expected(0.0..15.0).avg(0.0..6.0)), - float("GPU", "ms", GPU_TIME, expected(0.0..15.0).avg(0.0..8.0)), - - float("Content send", "ms", CONTENT_SEND_TIME, expected(0.0..1.0).avg(0.0..1.0)), - float("API send", "ms", API_SEND_TIME, expected(0.0..1.0).avg(0.0..0.4)), - float("DisplayList", "ms", DISPLAY_LIST_BUILD_TIME, expected(0.0..5.0).avg(0.0..3.0)), - float("DisplayList mem", "MB", DISPLAY_LIST_MEM, expected(0.0..20.0)), - float("Scene building", "ms", SCENE_BUILD_TIME, expected(0.0..4.0).avg(0.0..3.0)), - - int("Rasterized blobs", "", RASTERIZED_BLOBS, expected(0..15)), - int("Rasterized blob tiles", "", RASTERIZED_BLOB_TILES, expected(0..15)), - int("Rasterized blob pixels", "px", RASTERIZED_BLOBS_PX, expected(0..300_000)), - float("Blob rasterization", "ms", BLOB_RASTERIZATION_TIME, expected(0.0..8.0)), - - int("Rasterized glyphs", "", RASTERIZED_GLYPHS, expected(0..15)), - float("Glyph resolve", "ms", GLYPH_RESOLVE_TIME, expected(0.0..4.0)), - - int("Draw calls", "", DRAW_CALLS, expected(1..120).avg(1..90)), - int("Vertices", "", VERTICES, expected(10..5000)), - int("Primitives", "", PRIMITIVES, expected(10..5000)), - int("Visible primitives", "", VISIBLE_PRIMITIVES, expected(1..5000)), - - int("Used targets", "", USED_TARGETS, expected(1..4)), - int("Created targets", "", CREATED_TARGETS, expected(0..3)), - int("Picture cache slices", "", PICTURE_CACHE_SLICES, expected(0..5)), - - int("Color passes", "", COLOR_PASSES, expected(1..4)), - int("Alpha passes", "", ALPHA_PASSES, expected(0..3)), - int("Picture tiles", "", PICTURE_TILES, expected(0..15)), - float("Picture tiles mem", "MB", PICTURE_TILES_MEM, expected(0.0..150.0)), - int("Rendered picture tiles", "", RENDERED_PICTURE_TILES, expected(0..5)), - int("Texture uploads", "", TEXTURE_UPLOADS, expected(0..10)), - float("Texture uploads mem", "MB", TEXTURE_UPLOADS_MEM, expected(0.0..10.0)), - - int("Font templates", "", FONT_TEMPLATES, expected(0..40)), - float("Font templates mem", "MB", FONT_TEMPLATES_MEM, expected(0.0..20.0)), - int("Image templates", "", IMAGE_TEMPLATES, expected(0..100)), - float("Image templates mem", "MB", IMAGE_TEMPLATES_MEM, expected(0.0..50.0)), - - int("GPU cache rows total", "", GPU_CACHE_ROWS_TOTAL, expected(1..50)), - int("GPU cache rows updated", "", GPU_CACHE_ROWS_UPDATED, expected(0..25)), - int("GPU blocks total", "", GPU_CACHE_BLOCKS_TOTAL, expected(1..65_000)), - int("GPU blocks updated", "", GPU_CACHE_BLOCKS_UPDATED, expected(0..1000)), - int("GPU blocks saved", "", GPU_CACHE_BLOCKS_SAVED, expected(0..50_000)), - - int("Texture cache A8 pixels", "px", TEXTURE_CACHE_A8_PIXELS, expected(0..1_000_000)), - int("Texture cache A8 textures", "", TEXTURE_CACHE_A8_TEXTURES, expected(0..2)), - int("Texture cache A16 pixels", "px", TEXTURE_CACHE_A16_PIXELS, expected(0..260_000)), - int("Texture cache A16 textures", "", TEXTURE_CACHE_A16_TEXTURES, expected(0..2)), - int("Texture cache RGBA8 linear pixels", "px", TEXTURE_CACHE_RGBA8_LINEAR_PIXELS, expected(0..8_000_000)), - int("Texture cache RGBA8 linear textures", "", TEXTURE_CACHE_RGBA8_LINEAR_TEXTURES, expected(0..3)), - int("Texture cache RGBA8 nearest pixels", "px", TEXTURE_CACHE_RGBA8_NEAREST_PIXELS, expected(0..260_000)), - int("Texture cache RGBA8 nearest textures", "", TEXTURE_CACHE_RGBA8_NEAREST_TEXTURES, expected(0..2)), - float("Texture cache shared mem", "MB", TEXTURE_CACHE_SHARED_MEM, expected(0.0..100.0)), - float("Texture cache standalone mem", "MB", TEXTURE_CACHE_STANDALONE_MEM, expected(0.0..100.0)), - - - float("Slow frame", "", SLOW_FRAME, expected(0.0..0.0)), - float("Slow transaction", "", SLOW_TXN, expected(0.0..0.0)), - - float("GPU cache upload", "ms", GPU_CACHE_UPLOAD_TIME, expected(0.0..2.0)), - float("Texture cache update", "ms", TEXTURE_CACHE_UPDATE_TIME, expected(0.0..3.0)), - - float("Frame", "ms", FRAME_TIME, Expected::none()), - - float("Alpha targets samplers", "%", ALPHA_TARGETS_SAMPLERS, Expected::none()), - float("Transparent pass samplers", "%", TRANSPARENT_PASS_SAMPLERS, Expected::none()), - float("Opaque pass samplers", "%", OPAQUE_PASS_SAMPLERS, Expected::none()), - float("Total samplers", "%", TOTAL_SAMPLERS, Expected::none()), - - int("Interned primitives", "", INTERNED_PRIMITIVES, Expected::none()), - int("Interned clips", "", INTERNED_CLIPS, Expected::none()), - int("Interned text runs", "", INTERNED_TEXT_RUNS, Expected::none()), - int("Interned normal borders", "", INTERNED_NORMAL_BORDERS, Expected::none()), - int("Interned image borders", "", INTERNED_IMAGE_BORDERS, Expected::none()), - int("Interned images", "", INTERNED_IMAGES, Expected::none()), - int("Interned YUV images", "", INTERNED_YUV_IMAGES, Expected::none()), - int("Interned line decorations", "", INTERNED_LINE_DECORATIONS, Expected::none()), - int("Interned linear gradients", "", INTERNED_LINEAR_GRADIENTS, Expected::none()), - int("Interned radial gradients", "", INTERNED_RADIAL_GRADIENTS, Expected::none()), - int("Interned conic gradients", "", INTERNED_CONIC_GRADIENTS, Expected::none()), - int("Interned pictures", "", INTERNED_PICTURES, Expected::none()), - int("Interned filter data", "", INTERNED_FILTER_DATA, Expected::none()), - int("Interned backdrops", "", INTERNED_BACKDROPS, Expected::none()), - int("Interned polygons", "", INTERNED_POLYGONS, Expected::none()), - - int("Texture cache RGBA8 glyphs pixels", "px", TEXTURE_CACHE_RGBA8_GLYPHS_PIXELS, expected(0..4_000_000)), - int("Texture cache RGBA8 glyphs textures", "", TEXTURE_CACHE_RGBA8_GLYPHS_TEXTURES, expected(0..2)), - int("Texture cache A8 glyphs pixels", "px", TEXTURE_CACHE_A8_GLYPHS_PIXELS, expected(0..4_000_000)), - int("Texture cache A8 glyphs textures", "", TEXTURE_CACHE_A8_GLYPHS_TEXTURES, expected(0..2)), - - float("Staging CPU allocation", "ms", CPU_TEXTURE_ALLOCATION_TIME, Expected::none()), - float("Staging GPU allocation", "ms", STAGING_TEXTURE_ALLOCATION_TIME, Expected::none()), - float("Staging CPU copy", "ms", UPLOAD_CPU_COPY_TIME, Expected::none()), - float("Staging GPU copy", "ms", UPLOAD_GPU_COPY_TIME, Expected::none()), - float("Upload time", "ms", UPLOAD_TIME, Expected::none()), - int("Upload copy batches", "", UPLOAD_NUM_COPY_BATCHES, Expected::none()), - float("Texture cache upload", "ms", TOTAL_UPLOAD_TIME, expected(0.0..5.0)), - float("Cache texture creation", "ms", CREATE_CACHE_TEXTURE_TIME, expected(0.0..2.0)), - float("Cache texture deletion", "ms", DELETE_CACHE_TEXTURE_TIME, expected(0.0..1.0)), - - float("Texture cache RGBA8 linear pressure", "", TEXTURE_CACHE_COLOR8_LINEAR_PRESSURE, expected(0.0..1.0)), - float("Texture cache RGBA8 nearest pressure", "", TEXTURE_CACHE_COLOR8_NEAREST_PRESSURE, expected(0.0..1.0)), - float("Texture cache RGBA8 glyphs pressure", "", TEXTURE_CACHE_COLOR8_GLYPHS_PRESSURE, expected(0.0..1.0)), - float("Texture cache A8 pressure", "", TEXTURE_CACHE_ALPHA8_PRESSURE, expected(0.0..1.0)), - float("Texture cache A8 glyphs pressure", "", TEXTURE_CACHE_ALPHA8_GLYPHS_PRESSURE, expected(0.0..1.0)), - float("Texture cache A16 pressure", "", TEXTURE_CACHE_ALPHA16_PRESSURE, expected(0.0..1.0)), - float("Texture cache standalone pressure", "", TEXTURE_CACHE_STANDALONE_PRESSURE, expected(0.0..1.0)), - int("Texture cache eviction count", "items", TEXTURE_CACHE_EVICTION_COUNT, Expected::none()), - int("Texture cache youngest evicted", "frames", TEXTURE_CACHE_YOUNGEST_EVICTION, Expected::none()), - ]; +/// 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); + } + } +} - let mut counters = Vec::with_capacity(profile_counters.len()); +/// Returns true if the current thread is being profiled. +pub fn thread_is_being_profiled() -> bool { + unsafe { + PROFILER_HOOKS.map_or(false, |h| h.thread_is_being_profiled()) + } +} - for (idx, descriptor) in profile_counters.iter().enumerate() { - debug_assert_eq!(descriptor.index, idx); - counters.push(Counter::new(descriptor)); +impl ProfileScope { + /// Begin a new profile scope + pub fn new(name: &'static CStr) -> Self { + unsafe { + if let Some(ref hooks) = PROFILER_HOOKS { + hooks.begin_marker(name); + } } - Profiler { - gpu_frames: ProfilerFrameCollection::new(), - frame_stats: ProfilerFrameCollection::new(), + ProfileScope { + name, + } + } +} - counters, - start: precise_time_ns(), - avg_over_period: ONE_SECOND_NS / 2, +impl Drop for ProfileScope { + fn drop(&mut self) { + unsafe { + if let Some(ref hooks) = PROFILER_HOOKS { + hooks.end_marker(self.name); + } + } + } +} - num_graph_samples: 500, // Would it be useful to control this via a pref? - frame_timestamps_within_last_second: Vec::new(), - ui: Vec::new(), +/// A helper macro to define profile scopes. +macro_rules! profile_marker { + ($string:expr) => { + let _scope = $crate::profiler::ProfileScope::new(cstr!($string)); + }; +} + +#[derive(Debug, Clone)] +pub struct GpuProfileTag { + pub label: &'static str, + pub color: ColorF, +} + +impl NamedTag for GpuProfileTag { + fn get_label(&self) -> &str { + self.label + } +} + +trait ProfileCounter { + fn description(&self) -> &'static str; + fn value(&self) -> String; + fn is_expected(&self) -> bool; +} + +#[derive(Clone)] +pub struct IntProfileCounter { + description: &'static str, + value: usize, + expect: Option<Range<u64>>, +} + +impl IntProfileCounter { + fn new(description: &'static str, expect: Option<Range<u64>>) -> Self { + IntProfileCounter { + description, + value: 0, + expect, } } - /// Sum a few counters and if the total amount is larger than a threshold, update - /// a specific counter. - /// - /// This is useful to monitor slow frame and slow transactions. - fn update_slow_event(&mut self, dst_counter: usize, counters: &[usize], threshold: f64) { - let mut total = 0.0; - for &counter in counters { - if self.counters[counter].value.is_finite() { - total += self.counters[counter].value; - } + #[inline(always)] + pub fn inc(&mut self) { + self.value += 1; + } + + pub fn set(&mut self, value: usize) { + self.value = value; + } +} + +impl ProfileCounter for IntProfileCounter { + fn description(&self) -> &'static str { + self.description + } + + fn value(&self) -> String { + format!("{}", self.value) + } + + fn is_expected(&self) -> bool { + self.expect.as_ref().map(|range| range.contains(&(self.value as u64))).unwrap_or(true) + } +} + +/// A profile counter recording average and maximum integer values over time slices +/// of half a second. +#[derive(Clone)] +pub struct AverageIntProfileCounter { + description: &'static str, + /// Start of the current time slice. + start_ns: u64, + /// Sum of the values recorded during the current time slice. + sum: u64, + /// Number of samples in the current time slice. + num_samples: u64, + /// The max value in in-progress time slice. + next_max: u64, + /// The max value of the previous time slice (displayed). + max: u64, + /// The average value of the previous time slice (displayed). + avg: u64, + /// Intermediate accumulator for `add` and `inc`. + accum: u64, + /// Expected average range of values, if any. + expect_avg: Option<Range<u64>>, + /// Expected maximum range of values, if any. + expect_max: Option<Range<u64>>, +} + +impl AverageIntProfileCounter { + pub fn new( + description: &'static str, + expect_avg: Option<Range<u64>>, + expect_max: Option<Range<u64>>, + ) -> Self { + AverageIntProfileCounter { + description, + start_ns: precise_time_ns(), + sum: 0, + num_samples: 0, + next_max: 0, + max: 0, + avg: 0, + accum: 0, + expect_avg, + expect_max, } + } - if total > threshold { - self.counters[dst_counter].set(total); + pub fn reset(&mut self) { + if self.accum > 0 { + self.set_u64(self.accum); + self.accum = 0; } } - // Call at the end of every frame, after setting the counter values and before drawing the counters. - pub fn update(&mut self) { + pub fn set(&mut self, val: usize) { + self.set_u64(val as u64); + } + + pub fn set_u64(&mut self, val: u64) { let now = precise_time_ns(); - let update_avg = (now - self.start) > self.avg_over_period; - if update_avg { - self.start = now; + if (now - self.start_ns) > AVERAGE_OVER_NS && self.num_samples > 0 { + self.avg = self.sum / self.num_samples; + self.max = self.next_max; + self.start_ns = now; + self.sum = 0; + self.num_samples = 0; + self.next_max = 0; } - let one_second_ago = now - ONE_SECOND_NS; - self.frame_timestamps_within_last_second.retain(|t| *t > one_second_ago); - self.frame_timestamps_within_last_second.push(now); - - self.update_slow_event( - SLOW_FRAME, - &[TOTAL_FRAME_CPU_TIME], - 15.0, - ); - self.update_slow_event( - SLOW_TXN, - &[DISPLAY_LIST_BUILD_TIME, CONTENT_SEND_TIME, SCENE_BUILD_TIME], - 80.0 - ); + self.next_max = self.next_max.max(val); + self.sum += val; + self.num_samples += 1; + self.accum = 0; + } - for counter in &mut self.counters { - counter.update(update_avg); - } + pub fn add(&mut self, val: usize) { + self.accum += val as u64; + } + + pub fn inc(&mut self) { + self.accum += 1; + } + + pub fn get_accum(&mut self) -> u64{ + self.accum + } + + /// Returns either the most up to date value if the counter is updated + /// with add add inc, or the average over the previous time slice. + pub fn get(&self) -> usize { + let result = if self.accum != 0 { + self.accum + } else { + self.avg + }; + + result as usize } +} - pub fn update_frame_stats(&mut self, stats: FullFrameStats) { - if stats.gecko_display_list_time != 0.0 { - self.frame_stats.push(stats.into()); +impl ProfileCounter for AverageIntProfileCounter { + fn description(&self) -> &'static str { + self.description + } + + fn value(&self) -> String { + format!("{:.2} (max {:.2})", self.avg, self.max) + } + + fn is_expected(&self) -> bool { + self.expect_avg.as_ref().map(|range| range.contains(&self.avg)).unwrap_or(true) + && self.expect_max.as_ref().map(|range| range.contains(&self.max)).unwrap_or(true) + } +} + +pub struct PercentageProfileCounter { + description: &'static str, + value: f32, +} + +impl ProfileCounter for PercentageProfileCounter { + fn description(&self) -> &'static str { + self.description + } + + fn value(&self) -> String { + format!("{:.2}%", self.value * 100.0) + } + + fn is_expected(&self) -> bool { true } +} + +#[derive(Clone)] +pub struct ResourceProfileCounter { + description: &'static str, + value: usize, + // in bytes. + size: usize, + expected_count: Option<Range<usize>>, + // in MB + expected_size: Option<Range<f32>>, +} + +impl ResourceProfileCounter { + fn new( + description: &'static str, + expected_count: Option<Range<usize>>, + expected_size: Option<Range<f32>> + ) -> Self { + ResourceProfileCounter { + description, + value: 0, + size: 0, + expected_count, + expected_size, } } - pub fn set_gpu_time_queries(&mut self, gpu_queries: Vec<GpuTimer>) { - let mut gpu_time_ns = 0; - for sample in &gpu_queries { - gpu_time_ns += sample.time_ns; + #[allow(dead_code)] + fn reset(&mut self) { + self.value = 0; + self.size = 0; + } + + #[inline(always)] + pub fn inc(&mut self, size: usize) { + self.value += 1; + self.size += size; + } + + pub fn set(&mut self, count: usize, size: usize) { + self.value = count; + self.size = size; + } + + pub fn size_mb(&self) -> f32 { + self.size as f32 / (1024.0 * 1024.0) + } +} + +impl ProfileCounter for ResourceProfileCounter { + fn description(&self) -> &'static str { + self.description + } + + fn value(&self) -> String { + format!("{} ({:.2} MB)", self.value, self.size_mb()) + } + + fn is_expected(&self) -> bool { + self.expected_count.as_ref().map(|range| range.contains(&self.value)).unwrap_or(true) + && self.expected_size.as_ref().map(|range| range.contains(&self.size_mb())).unwrap_or(true) + } +} + +#[derive(Clone)] +pub struct TimeProfileCounter { + description: &'static str, + nanoseconds: u64, + invert: bool, + expect_ms: Option<Range<f64>>, +} + +pub struct Timer<'a> { + start: u64, + result: &'a mut u64, +} + +impl<'a> Drop for Timer<'a> { + fn drop(&mut self) { + let end = precise_time_ns(); + *self.result += end - self.start; + } +} + +impl TimeProfileCounter { + pub fn new(description: &'static str, invert: bool, expect_ms: Option<Range<f64>>) -> Self { + TimeProfileCounter { + description, + nanoseconds: 0, + invert, + expect_ms, } + } - self.gpu_frames.push(ProfilerFrame { - total_time: gpu_time_ns, - samples: gpu_queries - }); + fn reset(&mut self) { + self.nanoseconds = 0; + } + + #[allow(dead_code)] + pub fn set(&mut self, ns: u64) { + self.nanoseconds = ns; + } + + pub fn profile<T, F>(&mut self, callback: F) -> T + where + F: FnOnce() -> T, + { + let t0 = precise_time_ns(); + let val = callback(); + let t1 = precise_time_ns(); + let ns = t1 - t0; + self.nanoseconds += ns; + val + } - self.counters[GPU_TIME].set_f64(ns_to_ms(gpu_time_ns)); + pub fn timer(&mut self) -> Timer { + Timer { + start: precise_time_ns(), + result: &mut self.nanoseconds, + } + } + + pub fn inc(&mut self, ns: u64) { + self.nanoseconds += ns; } - // Find the index of a counter by its name. - pub fn index_of(&self, name: &str) -> Option<usize> { - self.counters.iter().position(|counter| counter.name == name) + pub fn get(&self) -> u64 { + self.nanoseconds } - // Define the profiler UI, see comment about the syntax at the top of this file. - pub fn set_ui(&mut self, names: &str) { - let mut selection = Vec::new(); + pub fn get_ms(&self) -> f64 { + self.nanoseconds as f64 / 1000000.0 + } +} - self.append_to_ui(&mut selection, names); +impl ProfileCounter for TimeProfileCounter { + fn description(&self) -> &'static str { + self.description + } - if selection == self.ui { - return; + fn value(&self) -> String { + if self.invert { + format!("{:.2} fps", 1000000000.0 / self.nanoseconds as f64) + } else { + format!("{:.2} ms", self.get_ms()) } + } + + fn is_expected(&self) -> bool { + self.expect_ms.as_ref() + .map(|range| range.contains(&(self.nanoseconds as f64 / 1000000.0))) + .unwrap_or(true) + } +} - for counter in &mut self.counters { - counter.disable_graph(); +#[derive(Clone)] +pub struct AverageTimeProfileCounter { + counter: AverageIntProfileCounter, + invert: bool, +} + +impl AverageTimeProfileCounter { + pub fn new( + description: &'static str, + invert: bool, + expect_avg: Option<Range<f64>>, + expect_max: Option<Range<f64>>, + ) -> Self { + let expect_avg_ns = expect_avg.map( + |range| (range.start * 1000000.0) as u64 .. (range.end * 1000000.0) as u64 + ); + let expect_max_ns = expect_max.map( + |range| (range.start * 1000000.0) as u64 .. (range.end * 1000000.0) as u64 + ); + + AverageTimeProfileCounter { + counter: AverageIntProfileCounter::new( + description, + expect_avg_ns, + expect_max_ns, + ), + invert, } + } - for item in &selection { - if let Item::Graph(idx) = item { - self.counters[*idx].enable_graph(self.num_graph_samples); - } + pub fn set(&mut self, ns: u64) { + self.counter.set_u64(ns); + } + + #[allow(dead_code)] + pub fn profile<T, F>(&mut self, callback: F) -> T + where + F: FnOnce() -> T, + { + let t0 = precise_time_ns(); + let val = callback(); + let t1 = precise_time_ns(); + self.counter.set_u64(t1 - t0); + val + } + + pub fn avg_ms(&self) -> f64 { self.counter.avg as f64 / 1000000.0 } + + pub fn max_ms(&self) -> f64 { self.counter.max as f64 / 1000000.0 } +} + +impl ProfileCounter for AverageTimeProfileCounter { + fn description(&self) -> &'static str { + self.counter.description + } + + fn value(&self) -> String { + if self.invert { + format!("{:.2} fps", 1000000000.0 / self.counter.avg as f64) + } else { + format!("{:.2} ms (max {:.2} ms)", self.avg_ms(), self.max_ms()) } + } - self.ui = selection; + fn is_expected(&self) -> bool { + self.counter.is_expected() } +} - fn append_to_ui(&mut self, selection: &mut Vec<Item>, names: &str) { - // Group successive counters together. - fn flush_counters(counters: &mut Vec<usize>, selection: &mut Vec<Item>) { - if !counters.is_empty() { - selection.push(Item::Counters(std::mem::take(counters))) - } + +#[derive(Clone)] +pub struct FrameProfileCounters { + pub total_primitives: AverageIntProfileCounter, + pub visible_primitives: AverageIntProfileCounter, + pub targets_used: AverageIntProfileCounter, + pub targets_changed: AverageIntProfileCounter, + pub targets_created: AverageIntProfileCounter, +} + +impl FrameProfileCounters { + pub fn new() -> Self { + FrameProfileCounters { + total_primitives: AverageIntProfileCounter::new( + "Total Primitives", + None, Some(expected::TOTAL_PRIMITIVES), + ), + visible_primitives: AverageIntProfileCounter::new( + "Visible Primitives", + None, Some(expected::VISIBLE_PRIMITIVES), + ), + targets_used: AverageIntProfileCounter::new( + "Used targets", + None, Some(expected::USED_TARGETS), + ), + targets_changed: AverageIntProfileCounter::new( + "Changed targets", + None, Some(expected::CHANGED_TARGETS), + ), + targets_created: AverageIntProfileCounter::new( + "Created targets", + None, Some(expected::CREATED_TARGETS), + ), } + } - let mut counters = Vec::new(); + pub fn reset_targets(&mut self) { + self.targets_used.reset(); + self.targets_changed.reset(); + self.targets_created.reset(); + } +} - for name in names.split(",") { - let name = name.trim(); - let is_graph = name.starts_with("#"); - let is_indicator = name.starts_with("*"); - let name = if is_graph || is_indicator { - &name[1..] - } else { - name - }; - // See comment about the ui string syntax at the top of this file. - match name { - "" => { - flush_counters(&mut counters, selection); - selection.push(Item::Space); - } - "|" => { - flush_counters(&mut counters, selection); - selection.push(Item::Column); - } - "_" => { - flush_counters(&mut counters, selection); - selection.push(Item::Row); - } - "FPS" => { - flush_counters(&mut counters, selection); - selection.push(Item::Fps); - } - "GPU time queries" => { - flush_counters(&mut counters, selection); - selection.push(Item::GpuTimeQueries); - } - "GPU cache bars" => { - flush_counters(&mut counters, selection); - selection.push(Item::GpuCacheBars); - } - "Paint phase graph" => { - flush_counters(&mut counters, selection); - selection.push(Item::PaintPhaseGraph); - } - _ => { - if let Some(idx) = self.index_of(name) { - if is_graph { - flush_counters(&mut counters, selection); - selection.push(Item::Graph(idx)); - } else if is_indicator { - flush_counters(&mut counters, selection); - selection.push(Item::ChangeIndicator(idx)); - } else { - counters.push(idx); - } - } else if let Some(preset_str) = find_preset(name) { - flush_counters(&mut counters, selection); - self.append_to_ui(selection, preset_str); - } else { - selection.push(Item::Text(format!("Unknonw counter: {}", name))); - } - } - } +#[derive(Clone)] +pub struct TextureCacheProfileCounters { + pub pages_alpha8_linear: ResourceProfileCounter, + pub pages_alpha16_linear: ResourceProfileCounter, + pub pages_color8_linear: ResourceProfileCounter, + pub pages_color8_nearest: ResourceProfileCounter, + pub pages_picture: ResourceProfileCounter, + pub rasterized_blob_pixels: ResourceProfileCounter, + pub standalone_bytes: IntProfileCounter, + pub shared_bytes: IntProfileCounter, +} + +impl TextureCacheProfileCounters { + pub fn new() -> Self { + TextureCacheProfileCounters { + pages_alpha8_linear: ResourceProfileCounter::new("Texture A8 cached pages", None, None), + pages_alpha16_linear: ResourceProfileCounter::new("Texture A16 cached pages", None, None), + pages_color8_linear: ResourceProfileCounter::new("Texture RGBA8 cached pages (L)", None, None), + pages_color8_nearest: ResourceProfileCounter::new("Texture RGBA8 cached pages (N)", None, None), + pages_picture: ResourceProfileCounter::new("Picture cached pages", None, None), + rasterized_blob_pixels: ResourceProfileCounter::new( + "Rasterized Blob Pixels", + Some(expected::NUM_RASTERIZED_BLOBS), + Some(expected::RASTERIZED_BLOBS_MB), + ), + standalone_bytes: IntProfileCounter::new("Standalone", None), + shared_bytes: IntProfileCounter::new("Shared", None), } + } +} + +#[derive(Clone)] +pub struct GpuCacheProfileCounters { + pub allocated_rows: AverageIntProfileCounter, + pub allocated_blocks: AverageIntProfileCounter, + pub updated_rows: AverageIntProfileCounter, + pub updated_blocks: AverageIntProfileCounter, + pub saved_blocks: AverageIntProfileCounter, +} - flush_counters(&mut counters, selection); +impl GpuCacheProfileCounters { + pub fn new() -> Self { + GpuCacheProfileCounters { + allocated_rows: AverageIntProfileCounter::new( + "GPU cache rows: total", + None, Some(expected::GPU_CACHE_ROWS_TOTAL), + ), + updated_rows: AverageIntProfileCounter::new( + "GPU cache rows: updated", + None, Some(expected::GPU_CACHE_ROWS_UPDATED), + ), + allocated_blocks: AverageIntProfileCounter::new( + "GPU cache blocks: total", + None, Some(expected::GPU_CACHE_BLOCKS_TOTAL), + ), + updated_blocks: AverageIntProfileCounter::new( + "GPU cache blocks: updated", + None, Some(expected::GPU_CACHE_BLOCKS_UPDATED), + ), + saved_blocks: AverageIntProfileCounter::new( + "GPU cache blocks: saved", + None, Some(expected::GPU_CACHE_BLOCKS_SAVED), + ), + } } +} + +#[derive(Clone)] +pub struct BackendProfileCounters { + pub total_time: TimeProfileCounter, + pub resources: ResourceProfileCounters, + pub txn: TransactionProfileCounters, + pub intern: InternProfileCounters, + pub scene_changed: bool, +} + +#[derive(Clone)] +pub struct ResourceProfileCounters { + pub font_templates: ResourceProfileCounter, + pub image_templates: ResourceProfileCounter, + pub texture_cache: TextureCacheProfileCounters, + pub gpu_cache: GpuCacheProfileCounters, + pub content_slices: IntProfileCounter, +} - pub fn set_counters(&mut self, counters: &mut TransactionProfile) { - for (id, evt) in counters.events.iter_mut().enumerate() { - if let Event::Value(val) = *evt { - self.counters[id].set(val); +#[derive(Clone)] +pub struct TransactionProfileCounters { + pub display_list_build_time: TimeProfileCounter, + pub scene_build_time: TimeProfileCounter, + /// Time between when the display list is built and when it is sent by the API. + pub content_send_time: TimeProfileCounter, + /// Time between sending the SetDisplayList from the API and picking it up on + /// the render scene builder thread. + pub api_send_time: TimeProfileCounter, + /// Sum of content_send_time and api_send_time. + pub total_send_time: TimeProfileCounter, + pub display_lists: ResourceProfileCounter, +} + +macro_rules! declare_intern_profile_counters { + ( $( $name:ident : $ty:ty, )+ ) => { + #[derive(Clone)] + pub struct InternProfileCounters { + $( + pub $name: ResourceProfileCounter, + )+ + } + + impl InternProfileCounters { + fn draw( + &self, + debug_renderer: &mut DebugRenderer, + draw_state: &mut DrawState, + ) { + Profiler::draw_counters( + &[ + $( + &self.$name, + )+ + ], + None, + debug_renderer, + false, + draw_state, + ); } - *evt = Event::None; } } +} + +enumerate_interners!(declare_intern_profile_counters); - pub fn get(&self, id: usize) -> Option<f64> { - self.counters[id].get() +impl TransactionProfileCounters { + pub fn set( + &mut self, + dl_build_start: u64, + dl_build_end: u64, + send_start: u64, + scene_build_start: u64, + scene_build_end: u64, + display_len: usize, + ) { + self.display_list_build_time.reset(); + self.content_send_time.reset(); + self.api_send_time.reset(); + self.total_send_time.reset(); + self.scene_build_time.reset(); + self.display_lists.reset(); + + let dl_build_time = dl_build_end - dl_build_start; + let scene_build_time = scene_build_end - scene_build_start; + let content_send_time = send_start - dl_build_end; + let api_send_time = scene_build_start - send_start; + self.display_list_build_time.inc(dl_build_time); + self.scene_build_time.inc(scene_build_time); + self.content_send_time.inc(content_send_time); + self.api_send_time.inc(api_send_time); + self.total_send_time.inc(content_send_time + api_send_time); + self.display_lists.inc(display_len); } +} - fn draw_counters( - counters: &[Counter], - selected: &[usize], - mut x: f32, mut y: f32, - text_buffer: &mut String, - debug_renderer: &mut DebugRenderer, - ) -> default::Rect<f32> { - let line_height = debug_renderer.line_height(); +impl BackendProfileCounters { + pub fn new() -> Self { + BackendProfileCounters { + total_time: TimeProfileCounter::new( + "Backend CPU Time", false, + Some(expected::MAX_BACKEND_CPU_TIME), + ), + resources: ResourceProfileCounters { + font_templates: ResourceProfileCounter::new( + "Font Templates", + Some(expected::NUM_FONT_TEMPLATES), + Some(expected::FONT_TEMPLATES_MB), + ), + image_templates: ResourceProfileCounter::new( + "Image Templates", + Some(expected::NUM_IMAGE_TEMPLATES), + Some(expected::IMAGE_TEMPLATES_MB), + ), + content_slices: IntProfileCounter::new( + "Content Slices", + None, + ), + texture_cache: TextureCacheProfileCounters::new(), + gpu_cache: GpuCacheProfileCounters::new(), + }, + txn: TransactionProfileCounters { + display_list_build_time: TimeProfileCounter::new( + "DisplayList Build Time", false, + Some(expected::DISPLAY_LIST_BUILD_TIME) + ), + scene_build_time: TimeProfileCounter::new( + "Scene build time", false, + Some(expected::MAX_SCENE_BUILD_TIME), + ), + content_send_time: TimeProfileCounter::new( + "Content Send Time", false, + Some(expected::DISPLAY_LIST_SEND_TIME), + ), + api_send_time: TimeProfileCounter::new( + "API Send Time", false, + Some(expected::DISPLAY_LIST_SEND_TIME), + ), + total_send_time: TimeProfileCounter::new( + "Total IPC Time", false, + Some(expected::DISPLAY_LIST_TOTAL_TIME), + ), + display_lists: ResourceProfileCounter::new( + "DisplayLists Sent", + None, Some(expected::DISPLAY_LIST_MB), + ), + }, + //TODO: generate this by a macro + intern: InternProfileCounters { + prim: ResourceProfileCounter::new("Interned primitives", None, None), + conic_grad: ResourceProfileCounter::new("Interned conic gradients", None, None), + image: ResourceProfileCounter::new("Interned images", None, None), + image_border: ResourceProfileCounter::new("Interned image borders", None, None), + line_decoration: ResourceProfileCounter::new("Interned line decorations", None, None), + linear_grad: ResourceProfileCounter::new("Interned linear gradients", None, None), + normal_border: ResourceProfileCounter::new("Interned normal borders", None, None), + picture: ResourceProfileCounter::new("Interned pictures", None, None), + radial_grad: ResourceProfileCounter::new("Interned radial gradients", None, None), + text_run: ResourceProfileCounter::new("Interned text runs", None, None), + yuv_image: ResourceProfileCounter::new("Interned YUV images", None, None), + clip: ResourceProfileCounter::new("Interned clips", None, None), + filter_data: ResourceProfileCounter::new("Interned filter data", None, None), + backdrop: ResourceProfileCounter::new("Interned backdrops", None, None), + }, + scene_changed: false, + } + } - x += PROFILE_PADDING; - y += PROFILE_PADDING; - let origin = default::Point2D::new(x, y); - y += line_height * 0.5; + pub fn reset(&mut self) { + self.total_time.reset(); + self.resources.texture_cache.rasterized_blob_pixels.reset(); + self.scene_changed = false; + } +} - let mut total_rect = Rect::zero(); +pub struct RendererProfileCounters { + pub frame_counter: IntProfileCounter, + pub frame_time: AverageTimeProfileCounter, + pub draw_calls: AverageIntProfileCounter, + pub vertices: AverageIntProfileCounter, + pub vao_count_and_size: ResourceProfileCounter, + pub color_passes: AverageIntProfileCounter, + pub alpha_passes: AverageIntProfileCounter, + pub texture_data_uploaded: AverageIntProfileCounter, + pub rendered_picture_cache_tiles: AverageIntProfileCounter, + pub total_picture_cache_tiles: AverageIntProfileCounter, +} - let mut color_index = 0; - let colors = [ - // Regular values, - ColorU::new(255, 255, 255, 255), - ColorU::new(255, 255, 0, 255), - // Unexpected values, - ColorU::new(255, 80, 0, 255), - ColorU::new(255, 0, 0, 255), - ]; +pub struct RendererProfileTimers { + pub cpu_time: TimeProfileCounter, + pub gpu_graph: TimeProfileCounter, + pub gpu_samples: Vec<GpuTimer<GpuProfileTag>>, +} - for idx in selected { - // If The index is invalid, add some vertical space. - let counter = &counters[*idx]; +impl RendererProfileCounters { + pub fn new() -> Self { + RendererProfileCounters { + frame_counter: IntProfileCounter::new("Frame", None), + frame_time: AverageTimeProfileCounter::new( + "FPS", true, None, None, + ), + draw_calls: AverageIntProfileCounter::new( + "Draw Calls", + None, Some(expected::DRAW_CALLS), + ), + vertices: AverageIntProfileCounter::new( + "Vertices", + None, Some(expected::VERTICES), + ), + vao_count_and_size: ResourceProfileCounter::new("VAO", None, None), + color_passes: AverageIntProfileCounter::new( + "Color passes", + None, Some(expected::COLOR_PASSES), + ), + alpha_passes: AverageIntProfileCounter::new( + "Alpha passes", + None, Some(expected::ALPHA_PASSES), + ), + texture_data_uploaded: AverageIntProfileCounter::new( + "Texture data, kb", + None, Some(expected::TEXTURE_DATA_UPLOADED), + ), + rendered_picture_cache_tiles: AverageIntProfileCounter::new( + "Rendered tiles", + None, Some(expected::RENDERED_PICTURE_CACHE_TILES), + ), + total_picture_cache_tiles: AverageIntProfileCounter::new( + "Total tiles", + None, Some(expected::TOTAL_PICTURE_CACHE_TILES), + ), + } + } - let rect = debug_renderer.add_text( - x, y, - counter.name, - colors[color_index], - None, - ); - color_index = (color_index + 1) % 2; + pub fn reset(&mut self) { + self.draw_calls.reset(); + self.vertices.reset(); + self.color_passes.reset(); + self.alpha_passes.reset(); + self.texture_data_uploaded.reset(); + self.rendered_picture_cache_tiles.reset(); + self.total_picture_cache_tiles.reset(); + } +} - total_rect = total_rect.union(&rect); - y += line_height; +impl RendererProfileTimers { + pub fn new() -> Self { + RendererProfileTimers { + cpu_time: TimeProfileCounter::new("Renderer CPU Time", false, None), + gpu_samples: Vec::new(), + gpu_graph: TimeProfileCounter::new("GPU Time", false, None), } + } +} - color_index = 0; - x = total_rect.max_x() + 60.0; - y = origin.y + line_height * 0.5; - - for idx in selected { - let counter = &counters[*idx]; - let expected_offset = if counter.has_unexpected_avg_max() { 2 } else { 0 }; +struct GraphStats { + min_value: f32, + mean_value: f32, + max_value: f32, +} - counter.write_value(text_buffer); +struct ProfileGraph { + max_samples: usize, + scale: f32, + values: VecDeque<f32>, + short_description: &'static str, + unit_description: &'static str, +} - let rect = debug_renderer.add_text( - x, - y, - &text_buffer, - colors[color_index + expected_offset], - None, - ); - color_index = (color_index + 1) % 2; +impl ProfileGraph { + fn new( + max_samples: usize, + scale: f32, + short_description: &'static str, + unit_description: &'static str, + ) -> Self { + ProfileGraph { + max_samples, + scale, + values: VecDeque::new(), + short_description, + unit_description, + } + } - total_rect = total_rect.union(&rect); - y += line_height; + fn push(&mut self, ns: u64) { + let val = ns as f64 * self.scale as f64; + if self.values.len() == self.max_samples { + self.values.pop_back(); } + self.values.push_front(val as f32); + } - total_rect = total_rect - .union(&Rect { origin, size: Size2D::new(1.0, 1.0) }) - .inflate(PROFILE_PADDING, PROFILE_PADDING); + fn stats(&self) -> GraphStats { + let mut stats = GraphStats { + min_value: f32::MAX, + mean_value: 0.0, + max_value: -f32::MAX, + }; - debug_renderer.add_quad( - total_rect.min_x(), - total_rect.min_y(), - total_rect.max_x(), - total_rect.max_y(), - BACKGROUND_COLOR, - BACKGROUND_COLOR, - ); + for value in &self.values { + stats.min_value = stats.min_value.min(*value); + stats.mean_value += *value; + stats.max_value = stats.max_value.max(*value); + } + + if !self.values.is_empty() { + stats.mean_value /= self.values.len() as f32; + } - total_rect + stats } fn draw_graph( - counter: &Counter, + &self, x: f32, y: f32, - text_buffer: &mut String, + description: &'static str, debug_renderer: &mut DebugRenderer, ) -> default::Rect<f32> { - let graph = counter.graph.as_ref().unwrap(); - - let max_samples = graph.values.capacity() as f32; - - let size = Size2D::new(max_samples, 100.0); + let size = Size2D::new(600.0, 100.0); let line_height = debug_renderer.line_height(); - let graph_rect = Rect::new(Point2D::new(x + PROFILE_PADDING, y + PROFILE_PADDING), size); - let mut rect = graph_rect.inflate(PROFILE_PADDING, PROFILE_PADDING); + let graph_rect = Rect::new(Point2D::new(x, y), size); + let mut rect = graph_rect.inflate(10.0, 10.0); - let stats = graph.stats(); + let stats = self.stats(); let text_color = ColorU::new(255, 255, 0, 255); - let text_origin = rect.origin + vec2(rect.size.width, 25.0); - set_text!(text_buffer, "{} ({})", counter.name, counter.unit); + let text_origin = rect.origin + vec2(rect.size.width, 20.0); debug_renderer.add_text( text_origin.x, text_origin.y, - if counter.unit == "" { counter.name } else { text_buffer }, + description, ColorU::new(0, 255, 0, 255), None, ); - - set_text!(text_buffer, "Samples: {}", stats.samples); - debug_renderer.add_text( text_origin.x, text_origin.y + line_height, - text_buffer, + &format!("Min: {:.2} {}", stats.min_value, self.unit_description), + text_color, + None, + ); + debug_renderer.add_text( + text_origin.x, + text_origin.y + line_height * 2.0, + &format!("Mean: {:.2} {}", stats.mean_value, self.unit_description), + text_color, + None, + ); + debug_renderer.add_text( + text_origin.x, + text_origin.y + line_height * 3.0, + &format!("Max: {:.2} {}", stats.max_value, self.unit_description), text_color, None, ); - if stats.samples > 0 { - set_text!(text_buffer, "Min: {:.2} {}", stats.min, counter.unit); - debug_renderer.add_text( - text_origin.x, - text_origin.y + line_height * 2.0, - text_buffer, - text_color, - None, - ); - - set_text!(text_buffer, "Avg: {:.2} {}", stats.avg, counter.unit); - debug_renderer.add_text( - text_origin.x, - text_origin.y + line_height * 3.0, - text_buffer, - text_color, - None, - ); - - set_text!(text_buffer, "Max: {:.2} {}", stats.max, counter.unit); - debug_renderer.add_text( - text_origin.x, - text_origin.y + line_height * 4.0, - text_buffer, - text_color, - None, - ); - } - - rect.size.width += 220.0; + rect.size.width += 140.0; debug_renderer.add_quad( - rect.min_x(), - rect.min_y(), - rect.max_x(), - rect.max_y(), - BACKGROUND_COLOR, - BACKGROUND_COLOR, + rect.origin.x, + rect.origin.y, + rect.origin.x + rect.size.width + 10.0, + rect.origin.y + rect.size.height, + ColorU::new(25, 25, 25, 200), + ColorU::new(51, 51, 51, 200), ); let bx1 = graph_rect.max_x(); let by1 = graph_rect.max_y(); - let w = graph_rect.size.width / max_samples; + let w = graph_rect.size.width / self.max_samples as f32; let h = graph_rect.size.height; let color_t0 = ColorU::new(0, 255, 0, 255); let color_b0 = ColorU::new(0, 180, 0, 255); + let color_t1 = ColorU::new(0, 255, 0, 255); + let color_b1 = ColorU::new(0, 180, 0, 255); + let color_t2 = ColorU::new(255, 0, 0, 255); let color_b2 = ColorU::new(180, 0, 0, 255); - for (index, sample) in graph.values.iter().enumerate() { - if !sample.is_finite() { - // NAN means no sample this frame. - continue; - } - let sample = *sample as f32; + for (index, sample) in self.values.iter().enumerate() { + let sample = *sample; let x1 = bx1 - index as f32 * w; let x0 = x1 - w; - let y0 = by1 - (sample / stats.max as f32) as f32 * h; + let y0 = by1 - (sample / stats.max_value) as f32 * h; let y1 = by1; - let (color_top, color_bottom) = if counter.is_unexpected_value(sample as f64) { - (color_t2, color_b2) - } else { + let (color_top, color_bottom) = if sample < 1000.0 / 60.0 { (color_t0, color_b0) + } else if sample < 1000.0 / 30.0 { + (color_t1, color_b1) + } else { + (color_t2, color_b2) }; debug_renderer.add_quad(x0, y0, x1, y1, color_top, color_bottom); @@ -775,164 +1096,51 @@ impl Profiler { rect } +} - - fn draw_change_indicator( - counter: &Counter, - x: f32, y: f32, - debug_renderer: &mut DebugRenderer - ) -> default::Rect<f32> { - let height = 10.0; - let width = 20.0; - - // Draw the indicator red instead of blue if is is not within expected ranges. - let color = if counter.has_unexpected_value() || counter.has_unexpected_avg_max() { - ColorU::new(255, 20, 20, 255) - } else { - ColorU::new(0, 100, 250, 255) - }; - - let tx = counter.change_indicator as f32 * width; - debug_renderer.add_quad( - x, - y, - x + 15.0 * width, - y + height, - ColorU::new(0, 0, 0, 150), - ColorU::new(0, 0, 0, 150), - ); - - debug_renderer.add_quad( - x + tx, - y, - x + tx + width, - y + height, - color, - ColorU::new(25, 25, 25, 255), - ); - - Rect { - origin: Point2D::new(x, y), - size: Size2D::new(15.0 * width + 20.0, height), - } +impl ProfileCounter for ProfileGraph { + fn description(&self) -> &'static str { + self.short_description } - fn draw_bar( - label: &str, - label_color: ColorU, - counters: &[(ColorU, usize)], - x: f32, y: f32, - debug_renderer: &mut DebugRenderer, - ) -> default::Rect<f32> { - let x = x + 8.0; - let y = y + 24.0; - let text_rect = debug_renderer.add_text( - x, y, - label, - label_color, - None, - ); - - let x_base = text_rect.max_x() + 10.0; - let width = 300.0; - let total_value = counters.last().unwrap().1; - let scale = width / total_value as f32; - let mut x_current = x_base; - - for &(color, counter) in counters { - let x_stop = x_base + counter as f32 * scale; - debug_renderer.add_quad( - x_current, - text_rect.origin.y, - x_stop, - text_rect.max_y(), - color, - color, - ); - x_current = x_stop; - - } - - let mut total_rect = text_rect; - total_rect.size.width += width + 10.0; - - total_rect + fn value(&self) -> String { + format!("{:.2}ms", self.stats().mean_value) } - fn draw_gpu_cache_bars(&self, x: f32, mut y: f32, text_buffer: &mut String, debug_renderer: &mut DebugRenderer) -> default::Rect<f32> { - let color_updated = ColorU::new(0xFF, 0, 0, 0xFF); - let color_free = ColorU::new(0, 0, 0xFF, 0xFF); - let color_saved = ColorU::new(0, 0xFF, 0, 0xFF); - - let updated_blocks = self.get(GPU_CACHE_BLOCKS_UPDATED).unwrap_or(0.0) as usize; - let saved_blocks = self.get(GPU_CACHE_BLOCKS_SAVED).unwrap_or(0.0) as usize; - let allocated_blocks = self.get(GPU_CACHE_BLOCKS_TOTAL).unwrap_or(0.0) as usize; - let allocated_rows = self.get(GPU_CACHE_ROWS_TOTAL).unwrap_or(0.0) as usize; - let updated_rows = self.get(GPU_CACHE_ROWS_UPDATED).unwrap_or(0.0) as usize; - let requested_blocks = updated_blocks + saved_blocks; - let total_blocks = allocated_rows * MAX_VERTEX_TEXTURE_WIDTH; - - set_text!(text_buffer, "GPU cache rows ({}):", allocated_rows); - - let rect0 = Profiler::draw_bar( - text_buffer, - ColorU::new(0xFF, 0xFF, 0xFF, 0xFF), - &[ - (color_updated, updated_rows), - (color_free, allocated_rows), - ], - x, y, - debug_renderer, - ); - - y = rect0.max_y(); - - let rect1 = Profiler::draw_bar( - "GPU cache blocks", - ColorU::new(0xFF, 0xFF, 0, 0xFF), - &[ - (color_updated, updated_blocks), - (color_saved, requested_blocks), - (color_free, allocated_blocks), - (ColorU::new(0, 0, 0, 0xFF), total_blocks), - ], - x, y, - debug_renderer, - ); + fn is_expected(&self) -> bool { true } +} - let total_rect = rect0.union(&rect1).inflate(10.0, 10.0); - debug_renderer.add_quad( - total_rect.origin.x, - total_rect.origin.y, - total_rect.origin.x + total_rect.size.width, - total_rect.origin.y + total_rect.size.height, - ColorF::new(0.1, 0.1, 0.1, 0.8).into(), - ColorF::new(0.2, 0.2, 0.2, 0.8).into(), - ); +struct GpuFrame { + total_time: u64, + samples: Vec<GpuTimer<GpuProfileTag>>, +} - total_rect - } +struct GpuFrameCollection { + frames: VecDeque<GpuFrame>, +} - // Draws a frame graph for a given frame collection. - fn draw_frame_graph( - frame_collection: &ProfilerFrameCollection, - x: f32, y: f32, - debug_renderer: &mut DebugRenderer, - ) -> default::Rect<f32> { - let mut has_data = false; - for frame in &frame_collection.frames { - if !frame.samples.is_empty() { - has_data = true; - break; - } +impl GpuFrameCollection { + fn new() -> Self { + GpuFrameCollection { + frames: VecDeque::new(), } + } - if !has_data { - return Rect::zero(); + fn push(&mut self, total_time: u64, samples: Vec<GpuTimer<GpuProfileTag>>) { + if self.frames.len() == 20 { + self.frames.pop_back(); } + self.frames.push_front(GpuFrame { + total_time, + samples, + }); + } +} +impl GpuFrameCollection { + fn draw(&self, x: f32, y: f32, debug_renderer: &mut DebugRenderer) -> default::Rect<f32> { let graph_rect = Rect::new( - Point2D::new(x + GRAPH_PADDING, y + GRAPH_PADDING), + Point2D::new(x, y), Size2D::new(GRAPH_WIDTH, GRAPH_HEIGHT), ); let bounding_rect = graph_rect.inflate(GRAPH_PADDING, GRAPH_PADDING); @@ -942,27 +1150,22 @@ impl Profiler { bounding_rect.origin.y, bounding_rect.origin.x + bounding_rect.size.width, bounding_rect.origin.y + bounding_rect.size.height, - BACKGROUND_COLOR, - BACKGROUND_COLOR, + ColorU::new(25, 25, 25, 200), + ColorU::new(51, 51, 51, 200), ); let w = graph_rect.size.width; let mut y0 = graph_rect.origin.y; - let mut max_time = frame_collection.frames + let max_time = self.frames .iter() .max_by_key(|f| f.total_time) .unwrap() .total_time as f32; - // If the max time is lower than 16ms, fix the scale - // at 16ms so that the graph is easier to interpret. - let baseline_ns = 16_000_000.0; // 16ms - max_time = max_time.max(baseline_ns); - let mut tags_present = FastHashMap::default(); - for frame in &frame_collection.frames { + for frame in &self.frames { let y1 = y0 + GRAPH_FRAME_HEIGHT; let mut current_ns = 0; @@ -988,23 +1191,6 @@ impl Profiler { y0 = y1; } - // If the max time is higher than 16ms, show a vertical line at the - // 16ms mark. - if max_time > baseline_ns { - let x = graph_rect.origin.x + w * baseline_ns as f32 / max_time; - let height = frame_collection.frames.len() as f32 * GRAPH_FRAME_HEIGHT; - - debug_renderer.add_quad( - x, - graph_rect.origin.y, - x + 4.0, - graph_rect.origin.y + height, - ColorU::new(120, 00, 00, 150), - ColorU::new(120, 00, 00, 100), - ); - } - - // Add a legend to see which color correspond to what primitive. const LEGEND_SIZE: f32 = 20.0; const PADDED_LEGEND_SIZE: f32 = 25.0; @@ -1014,8 +1200,8 @@ impl Profiler { bounding_rect.origin.y, bounding_rect.max_x() + GRAPH_PADDING + 200.0, bounding_rect.origin.y + tags_present.len() as f32 * PADDED_LEGEND_SIZE + GRAPH_PADDING, - BACKGROUND_COLOR, - BACKGROUND_COLOR, + ColorU::new(25, 25, 25, 200), + ColorU::new(51, 51, 51, 200), ); } @@ -1040,719 +1226,676 @@ impl Profiler { bounding_rect } - - pub fn draw_profile( - &mut self, - _frame_index: u64, - debug_renderer: &mut DebugRenderer, - device_size: DeviceIntSize, - ) { - let x_start = 20.0; - let mut y_start = 150.0; - let default_column_width = 400.0; - - // set_text!(..) into this string instead of using format!(..) to avoid - // unnecessary allocations. - let mut text_buffer = String::with_capacity(32); - - let mut column_width = default_column_width; - let mut max_y = y_start; - - let mut x = x_start; - let mut y = y_start; - - for elt in &self.ui { - let rect = match elt { - Item::Counters(indices) => { - Profiler::draw_counters(&self.counters, &indices, x, y, &mut text_buffer, debug_renderer) - } - Item::Graph(idx) => { - Profiler::draw_graph(&self.counters[*idx], x, y, &mut text_buffer, debug_renderer) - } - Item::ChangeIndicator(idx) => { - Profiler::draw_change_indicator(&self.counters[*idx], x, y, debug_renderer) - } - Item::GpuTimeQueries => { - Profiler::draw_frame_graph(&self.gpu_frames, x, y, debug_renderer) - } - Item::GpuCacheBars => { - self.draw_gpu_cache_bars(x, y, &mut text_buffer, debug_renderer) - } - Item::PaintPhaseGraph => { - Profiler::draw_frame_graph(&self.frame_stats, x, y, debug_renderer) - } - Item::Text(text) => { - let p = 10.0; - let mut rect = debug_renderer.add_text( - x + p, - y + p, - &text, - ColorU::new(255, 255, 255, 255), - None, - ); - rect = rect.inflate(p, p); - - debug_renderer.add_quad( - rect.origin.x, - rect.origin.y, - rect.max_x(), - rect.max_y(), - BACKGROUND_COLOR, - BACKGROUND_COLOR, - ); - - rect - } - Item::Fps => { - let fps = self.frame_timestamps_within_last_second.len(); - set_text!(&mut text_buffer, "{} fps", fps); - let mut rect = debug_renderer.add_text( - x + PROFILE_PADDING, - y + PROFILE_PADDING + 5.0, - &text_buffer, - ColorU::new(255, 255, 255, 255), - None, - ); - rect = rect.inflate(PROFILE_PADDING, PROFILE_PADDING); - - debug_renderer.add_quad( - rect.min_x(), - rect.min_y(), - rect.max_x(), - rect.max_y(), - BACKGROUND_COLOR, - BACKGROUND_COLOR, - ); - - rect - } - Item::Space => { - Rect { origin: Point2D::new(x, y), size: Size2D::new(0.0, PROFILE_SPACING) } - } - Item::Column => { - max_y = max_y.max(y); - x += column_width + PROFILE_SPACING; - y = y_start; - column_width = default_column_width; - - continue; - } - Item::Row => { - max_y = max_y.max(y); - y_start = max_y + PROFILE_SPACING; - y = y_start; - x = x_start; - column_width = default_column_width; - - continue; - } - }; - - column_width = column_width.max(rect.size.width); - y = rect.max_y(); - - if y > device_size.height as f32 - 100.0 { - max_y = max_y.max(y); - x += column_width + PROFILE_SPACING; - y = y_start; - column_width = default_column_width; - } - } - } - - #[cfg(feature = "capture")] - pub fn dump_stats(&self, sink: &mut dyn std::io::Write) -> std::io::Result<()> { - for counter in &self.counters { - if counter.value.is_finite() { - writeln!(sink, "{} {:?}{}", counter.name, counter.value, counter.unit)?; - } - } - - Ok(()) - } -} - -/// Defines the interface for hooking up an external profiler to WR. -pub trait ProfilerHooks : Send + Sync { - /// Called at the beginning of a profile scope. The label must - /// be a C string (null terminated). - fn begin_marker(&self, label: &CStr); - - /// Called at the end of a profile scope. The label must - /// 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. - /// - /// This variant is also useful when the caller only wants to record events - /// longer than a certain threshold, and thus they don't know in advance - /// whether the event will qualify. - fn add_text_marker(&self, label: &CStr, text: &str, duration: Duration); - - /// Returns true if the current thread is being profiled. - fn thread_is_being_profiled(&self) -> bool; } -/// The current global profiler callbacks, if set by embedder. -pub static mut PROFILER_HOOKS: Option<&'static dyn ProfilerHooks> = None; - -/// Set the profiler callbacks, or None to disable the profiler. -/// This function must only ever be called before any WR instances -/// have been created, or the hooks will not be set. -pub fn set_profiler_hooks(hooks: Option<&'static dyn ProfilerHooks>) { - if !wr_has_been_initialized() { - unsafe { - PROFILER_HOOKS = hooks; - } - } +struct DrawState { + x_left: f32, + y_left: f32, + x_right: f32, + y_right: f32, } -/// A simple RAII style struct to manage a profile scope. -pub struct ProfileScope { - name: &'static CStr, +pub struct Profiler { + draw_state: DrawState, + backend_graph: ProfileGraph, + renderer_graph: ProfileGraph, + gpu_graph: ProfileGraph, + ipc_graph: ProfileGraph, + display_list_build_graph: ProfileGraph, + scene_build_graph: ProfileGraph, + blob_raster_graph: ProfileGraph, + backend_time: AverageTimeProfileCounter, + renderer_time: AverageTimeProfileCounter, + gpu_time: AverageTimeProfileCounter, + ipc_time: AverageTimeProfileCounter, + gpu_frames: GpuFrameCollection, + cooldowns: Vec<i32>, } -/// Records a marker of the given duration that just ended. -pub fn add_text_marker(label: &CStr, text: &str, duration: Duration) { - unsafe { - if let Some(ref hooks) = PROFILER_HOOKS { - hooks.add_text_marker(label, text, duration); +impl Profiler { + pub fn new() -> Self { + let to_ms_scale = 1.0 / 1000000.0; + Profiler { + draw_state: DrawState { + x_left: 0.0, + y_left: 0.0, + x_right: 0.0, + y_right: 0.0, + }, + backend_graph: ProfileGraph::new(600, to_ms_scale, "Backend:", "ms"), + renderer_graph: ProfileGraph::new(600, to_ms_scale, "Renderer:", "ms"), + gpu_graph: ProfileGraph::new(600, to_ms_scale, "GPU:", "ms"), + ipc_graph: ProfileGraph::new(600, to_ms_scale, "IPC:", "ms"), + display_list_build_graph: ProfileGraph::new(600, to_ms_scale, "DisplayList build", "ms"), + scene_build_graph: ProfileGraph::new(600, to_ms_scale, "Scene build:", "ms"), + blob_raster_graph: ProfileGraph::new(600, 1.0, "Rasterized blob pixels:", "px"), + gpu_frames: GpuFrameCollection::new(), + backend_time: AverageTimeProfileCounter::new( + "Backend:", false, + Some(expected::AVG_BACKEND_CPU_TIME), + Some(expected::MAX_BACKEND_CPU_TIME), + ), + renderer_time: AverageTimeProfileCounter::new( + "Renderer:", false, + Some(expected::AVG_RENDERER_CPU_TIME), + Some(expected::MAX_RENDERER_CPU_TIME), + ), + ipc_time: AverageTimeProfileCounter::new( + "IPC:", false, + Some(expected::AVG_IPC_TIME), + Some(expected::MAX_IPC_TIME), + ), + gpu_time: AverageTimeProfileCounter::new( + "GPU:", false, + Some(expected::AVG_GPU_TIME), + Some(expected::MAX_GPU_TIME), + ), + cooldowns: Vec::new(), } } -} -/// 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); - } - } -} + // If we have an array of "cooldown" counters, then only display profiles that + // are out of the ordinary and keep displaying them until the cooldown is over. + fn draw_counters<T: ProfileCounter + ?Sized>( + counters: &[&T], + mut cooldowns: Option<&mut [i32]>, + debug_renderer: &mut DebugRenderer, + left: bool, + draw_state: &mut DrawState, + ) { + let mut label_rect = Rect::zero(); + let mut value_rect = Rect::zero(); + let (mut current_x, mut current_y) = if left { + (draw_state.x_left, draw_state.y_left) + } else { + (draw_state.x_right, draw_state.y_right) + }; + let mut color_index = 0; + let line_height = debug_renderer.line_height(); -/// Returns true if the current thread is being profiled. -pub fn thread_is_being_profiled() -> bool { - unsafe { - PROFILER_HOOKS.map_or(false, |h| h.thread_is_being_profiled()) - } -} + let colors = [ + // Regular values, + ColorU::new(255, 255, 255, 255), + ColorU::new(255, 255, 0, 255), + // Unexpected values, + ColorU::new(255, 80, 0, 255), + ColorU::new(255, 0, 0, 255), + ]; -impl ProfileScope { - /// Begin a new profile scope - pub fn new(name: &'static CStr) -> Self { - unsafe { - if let Some(ref hooks) = PROFILER_HOOKS { - hooks.begin_marker(name); + for (idx, counter) in counters.iter().enumerate() { + if let Some(cooldowns) = cooldowns.as_mut() { + if !counter.is_expected() { + cooldowns[idx] = 40; + } + if cooldowns[idx] == 0 { + continue; + } } - } + let rect = debug_renderer.add_text( + current_x, + current_y, + counter.description(), + colors[color_index], + None, + ); + color_index = (color_index + 1) % 2; - ProfileScope { - name, + label_rect = label_rect.union(&rect); + current_y += line_height; } - } -} -impl Drop for ProfileScope { - fn drop(&mut self) { - unsafe { - if let Some(ref hooks) = PROFILER_HOOKS { - hooks.end_marker(self.name); + color_index = 0; + current_x = label_rect.origin.x + label_rect.size.width + 60.0; + current_y = if left { draw_state.y_left } else { draw_state.y_right }; + + for (idx, counter) in counters.iter().enumerate() { + let expected_offset = if counter.is_expected() || cooldowns.is_some() { 0 } else { 2 }; + if let Some(cooldowns) = cooldowns.as_mut() { + if cooldowns[idx] > 0 { + cooldowns[idx] -= 1; + } else { + continue; + } } - } - } -} - -/// A helper macro to define profile scopes. -macro_rules! profile_marker { - ($string:expr) => { - let _scope = $crate::profiler::ProfileScope::new(cstr!($string)); - }; -} - -#[derive(Debug, Clone)] -pub struct GpuProfileTag { - pub label: &'static str, - pub color: ColorF, -} - -/// Ranges of expected value for a profile counter. -#[derive(Clone, Debug)] -pub struct Expected<T> { - pub range: Option<Range<T>>, - pub avg: Option<Range<T>>, -} - -impl<T> Expected<T> { - const fn none() -> Self { - Expected { - range: None, - avg: None, - } - } -} - -const fn expected<T>(range: Range<T>) -> Expected<T> { - Expected { - range: Some(range), - avg: None, - } -} - -impl Expected<f64> { - const fn avg(mut self, avg: Range<f64>) -> Self { - self.avg = Some(avg); - self - } -} - -impl Expected<i64> { - const fn avg(mut self, avg: Range<i64>) -> Self { - self.avg = Some(avg); - self - } - - fn into_float(self) -> Expected<f64> { - Expected { - range: match self.range { - Some(r) => Some(r.start as f64 .. r.end as f64), - None => None, - }, - avg: match self.avg { - Some(r) => Some(r.start as f64 .. r.end as f64), - None => None, - }, - } - } -} - -pub struct CounterDescriptor { - pub name: &'static str, - pub unit: &'static str, - pub index: usize, - pub show_as: ShowAs, - pub expected: Expected<f64>, -} - -#[derive(Debug)] -pub struct Counter { - pub name: &'static str, - pub unit: &'static str, - pub show_as: ShowAs, - pub expected: Expected<f64>, - - /// - value: f64, - /// Number of samples in the current time slice. - num_samples: u64, - /// Sum of the values recorded during the current time slice. - sum: f64, - /// The max value in in-progress time slice. - next_max: f64, - /// The max value of the previous time slice (displayed). - max: f64, - /// The average value of the previous time slice (displayed). - avg: f64, - /// Incremented when the counter changes. - change_indicator: u8, - - /// Only used to check that the constants match the real index. - #[allow(dead_code)] - index: usize, - - graph: Option<Graph>, -} + let rect = debug_renderer.add_text( + current_x, + current_y, + &counter.value(), + colors[color_index + expected_offset], + None, + ); + color_index = (color_index + 1) % 2; -impl Counter { - pub fn new(descriptor: &CounterDescriptor) -> Self { - Counter { - name: descriptor.name, - unit: descriptor.unit, - show_as: descriptor.show_as, - expected: descriptor.expected.clone(), - index: descriptor.index, - value: std::f64::NAN, - num_samples: 0, - sum: 0.0, - next_max: 0.0, - max: 0.0, - avg: 0.0, - change_indicator: 0, - graph: None, + value_rect = value_rect.union(&rect); + current_y += line_height; } - } - pub fn set_f64(&mut self, val: f64) { - self.value = val; - } - - pub fn set<T>(&mut self, val: T) where T: Into<f64> { - self.set_f64(val.into()); - } - pub fn get(&self) -> Option<f64> { - if self.value.is_finite() { - Some(self.value) + let total_rect = label_rect.union(&value_rect).inflate(10.0, 10.0); + debug_renderer.add_quad( + total_rect.origin.x, + total_rect.origin.y, + total_rect.origin.x + total_rect.size.width, + total_rect.origin.y + total_rect.size.height, + ColorF::new(0.1, 0.1, 0.1, 0.8).into(), + ColorF::new(0.2, 0.2, 0.2, 0.8).into(), + ); + let new_y = total_rect.origin.y + total_rect.size.height + 30.0; + if left { + draw_state.y_left = new_y; } else { - None + draw_state.y_right = new_y; } } - pub fn write_value(&self, output: &mut String) { - match self.show_as { - ShowAs::Float => { - set_text!(output, "{:.2} {} (max: {:.2})", self.avg, self.unit, self.max); - } - ShowAs::Int => { - set_text!(output, "{:.0} {} (max: {:.0})", self.avg.round(), self.unit, self.max.round()); - } - } - } - - pub fn enable_graph(&mut self, max_samples: usize) { - if self.graph.is_some() { - return; - } - - self.graph = Some(Graph::new(max_samples)); - } - - pub fn disable_graph(&mut self) { - self.graph = None; - } - - pub fn is_unexpected_value(&self, value: f64) -> bool { - if let Some(range) = &self.expected.range { - return value.is_finite() && value >= range.end; - } - - false - } + fn draw_bar( + &mut self, + label: &str, + label_color: ColorU, + counters: &[(ColorU, &AverageIntProfileCounter)], + debug_renderer: &mut DebugRenderer, + ) -> default::Rect<f32> { + let mut rect = debug_renderer.add_text( + self.draw_state.x_left, + self.draw_state.y_left, + label, + label_color, + None, + ); - pub fn has_unexpected_value(&self) -> bool { - self.is_unexpected_value(self.value) - } + let x_base = rect.origin.x + rect.size.width + 10.0; + let height = debug_renderer.line_height(); + let width = (self.draw_state.x_right - 30.0 - x_base).max(0.0); + let total_value = counters.last().unwrap().1.get(); + let scale = width / total_value as f32; + let mut x_current = x_base; - pub fn has_unexpected_avg_max(&self) -> bool { - if let Some(range) = &self.expected.range { - if self.max.is_finite() && self.max >= range.end { - return true; - } + for &(color, counter) in counters { + let x_stop = x_base + counter.get() as f32 * scale; + debug_renderer.add_quad( + x_current, + rect.origin.y, + x_stop, + rect.origin.y + height, + color, + color, + ); + x_current = x_stop; } - if let Some(range) = &self.expected.avg { - if self.avg < range.start || self.avg >= range.end { - return true; - } - } + self.draw_state.y_left += height; - false + rect.size.width += width + 10.0; + rect } - fn update(&mut self, update_avg: bool) { - let updated = self.value.is_finite(); - if updated { - self.next_max = self.next_max.max(self.value); - self.sum += self.value; - self.num_samples += 1; - self.change_indicator = (self.change_indicator + 1) % 15; - } - - if let Some(graph) = &mut self.graph { - graph.set(self.value); - } - - self.value = std::f64::NAN; - - if update_avg && self.num_samples > 0 { - self.avg = self.sum / self.num_samples as f64; - self.max = self.next_max; - self.sum = 0.0; - self.num_samples = 0; - self.next_max = std::f64::MIN; - } - } -} + fn draw_gpu_cache_bars( + &mut self, + counters: &GpuCacheProfileCounters, + debug_renderer: &mut DebugRenderer, + ) { + let color_updated = ColorU::new(0xFF, 0, 0, 0xFF); + let color_free = ColorU::new(0, 0, 0xFF, 0xFF); + let color_saved = ColorU::new(0, 0xFF, 0, 0xFF); -#[derive(Copy, Clone, Debug)] -pub enum Event { - Start(f64), - Value(f64), - None, -} + let mut requested_blocks = AverageIntProfileCounter::new("", None, None); + requested_blocks.set(counters.updated_blocks.get() + counters.saved_blocks.get()); -// std::convert::From/TryFrom can't deal with integer to f64 so we roll our own... -pub trait EventValue { - fn into_f64(self) -> f64; -} + let mut total_blocks = AverageIntProfileCounter::new("", None, None); + total_blocks.set(counters.allocated_rows.get() * MAX_VERTEX_TEXTURE_WIDTH); -impl EventValue for f64 { fn into_f64(self) -> f64 { self } } -impl EventValue for f32 { fn into_f64(self) -> f64 { self as f64 } } -impl EventValue for u32 { fn into_f64(self) -> f64 { self as f64 } } -impl EventValue for i32 { fn into_f64(self) -> f64 { self as f64 } } -impl EventValue for u64 { fn into_f64(self) -> f64 { self as f64 } } -impl EventValue for usize { fn into_f64(self) -> f64 { self as f64 } } - -/// A container for profiling information that moves along the rendering pipeline -/// and is handed off to the profiler at the end. -pub struct TransactionProfile { - pub events: Vec<Event>, -} + let rect0 = self.draw_bar( + &format!("GPU cache rows ({}):", counters.allocated_rows.get()), + ColorU::new(0xFF, 0xFF, 0xFF, 0xFF), + &[ + (color_updated, &counters.updated_rows), + (color_free, &counters.allocated_rows), + ], + debug_renderer, + ); -impl TransactionProfile { - pub fn new() -> Self { - TransactionProfile { - events: vec![Event::None; NUM_PROFILER_EVENTS], - } - } + let rect1 = self.draw_bar( + "GPU cache blocks", + ColorU::new(0xFF, 0xFF, 0, 0xFF), + &[ + (color_updated, &counters.updated_blocks), + (color_saved, &requested_blocks), + (color_free, &counters.allocated_blocks), + (ColorU::new(0, 0, 0, 0xFF), &total_blocks), + ], + debug_renderer, + ); - pub fn start_time(&mut self, id: usize) { - let ms = ns_to_ms(precise_time_ns()); - self.events[id] = Event::Start(ms); - } + let total_rect = rect0.union(&rect1).inflate(10.0, 10.0); + debug_renderer.add_quad( + total_rect.origin.x, + total_rect.origin.y, + total_rect.origin.x + total_rect.size.width, + total_rect.origin.y + total_rect.size.height, + ColorF::new(0.1, 0.1, 0.1, 0.8).into(), + ColorF::new(0.2, 0.2, 0.2, 0.8).into(), + ); - pub fn end_time(&mut self, id: usize) -> f64 { - self.end_time_if_started(id).unwrap() + self.draw_state.y_left = total_rect.origin.y + total_rect.size.height + 30.0; } - /// Similar to end_time, but doesn't panic if not matched with start_time. - pub fn end_time_if_started(&mut self, id: usize) -> Option<f64> { - if let Event::Start(start) = self.events[id] { - let time = ns_to_ms(precise_time_ns()) - start; - self.events[id] = Event::Value(time); - - Some(time) - } else { - None - } - } + fn draw_frame_bars( + &mut self, + counters: &FrameProfileCounters, + debug_renderer: &mut DebugRenderer, + ) { + let rect0 = self.draw_bar( + &format!("primitives ({}):", counters.total_primitives.get()), + ColorU::new(0xFF, 0xFF, 0xFF, 0xFF), + &[ + (ColorU::new(0, 0, 0xFF, 0xFF), &counters.visible_primitives), + (ColorU::new(0, 0, 0, 0xFF), &counters.total_primitives), + ], + debug_renderer, + ); - pub fn set<T>(&mut self, id: usize, value: T) where T: EventValue { - self.set_f64(id, value.into_f64()); - } + let rect1 = self.draw_bar( + &format!("GPU targets ({}):", &counters.targets_used.get()), + ColorU::new(0xFF, 0xFF, 0, 0xFF), + &[ + (ColorU::new(0, 0, 0xFF, 0xFF), &counters.targets_created), + (ColorU::new(0xFF, 0, 0, 0xFF), &counters.targets_changed), + (ColorU::new(0, 0xFF, 0, 0xFF), &counters.targets_used), + ], + debug_renderer, + ); + let total_rect = rect0.union(&rect1).inflate(10.0, 10.0); + debug_renderer.add_quad( + total_rect.origin.x, + total_rect.origin.y, + total_rect.origin.x + total_rect.size.width, + total_rect.origin.y + total_rect.size.height, + ColorF::new(0.1, 0.1, 0.1, 0.8).into(), + ColorF::new(0.2, 0.2, 0.2, 0.8).into(), + ); - pub fn set_f64(&mut self, id: usize, value: f64) { - self.events[id] = Event::Value(value); + self.draw_state.y_left = total_rect.origin.y + total_rect.size.height + 30.0; } - pub fn get(&self, id: usize) -> Option<f64> { - if let Event::Value(val) = self.events[id] { - Some(val) - } else { - None - } + fn draw_compact_profile( + &mut self, + backend_profile: &BackendProfileCounters, + renderer_profile: &RendererProfileCounters, + debug_renderer: &mut DebugRenderer, + ) { + Profiler::draw_counters( + &[ + &renderer_profile.frame_time as &dyn ProfileCounter, + &renderer_profile.color_passes, + &renderer_profile.alpha_passes, + &renderer_profile.draw_calls, + &renderer_profile.vertices, + &renderer_profile.rendered_picture_cache_tiles, + &renderer_profile.texture_data_uploaded, + &backend_profile.resources.content_slices, + &self.ipc_time, + &self.backend_time, + &self.renderer_time, + &self.gpu_time, + ], + None, + debug_renderer, + true, + &mut self.draw_state, + ); } - pub fn get_or(&self, id: usize, or: f64) -> f64 { - self.get(id).unwrap_or(or) - } + fn draw_full_profile( + &mut self, + frame_profiles: &[FrameProfileCounters], + backend_profile: &BackendProfileCounters, + renderer_profile: &RendererProfileCounters, + renderer_timers: &mut RendererProfileTimers, + gpu_samplers: &[GpuSampler<GpuProfileTag>], + screen_fraction: f32, + debug_renderer: &mut DebugRenderer, + ) { + Profiler::draw_counters( + &[ + &renderer_profile.frame_time as &dyn ProfileCounter, + &renderer_profile.frame_counter, + &renderer_profile.color_passes, + &renderer_profile.alpha_passes, + &renderer_profile.rendered_picture_cache_tiles, + &renderer_profile.total_picture_cache_tiles, + &renderer_profile.texture_data_uploaded, + &backend_profile.resources.content_slices, + &backend_profile.resources.texture_cache.shared_bytes, + &backend_profile.resources.texture_cache.standalone_bytes, + ], + None, + debug_renderer, + true, + &mut self.draw_state + ); - pub fn add<T>(&mut self, id: usize, n: T) where T: EventValue { - let n = n.into_f64(); + self.draw_gpu_cache_bars( + &backend_profile.resources.gpu_cache, + debug_renderer, + ); - let evt = &mut self.events[id]; + Profiler::draw_counters( + &[ + &backend_profile.resources.font_templates, + &backend_profile.resources.image_templates, + ], + None, + debug_renderer, + true, + &mut self.draw_state + ); - let val = match *evt { - Event::Value(v) => v + n, - Event::None => n, - Event::Start(..) => { panic!(); } - }; + backend_profile.intern.draw(debug_renderer, &mut self.draw_state); - *evt = Event::Value(val); - } + Profiler::draw_counters( + &[ + &backend_profile.resources.texture_cache.pages_alpha8_linear, + &backend_profile.resources.texture_cache.pages_color8_linear, + &backend_profile.resources.texture_cache.pages_color8_nearest, + &backend_profile.txn.display_lists, + ], + None, + debug_renderer, + true, + &mut self.draw_state + ); - pub fn inc(&mut self, id: usize) { - self.add(id, 1.0); - } + Profiler::draw_counters( + &[ + &backend_profile.txn.display_list_build_time, + &backend_profile.txn.scene_build_time, + &backend_profile.txn.content_send_time, + &backend_profile.txn.api_send_time, + &backend_profile.txn.total_send_time, + ], + None, + debug_renderer, + true, + &mut self.draw_state + ); - pub fn take(&mut self) -> Self { - TransactionProfile { - events: std::mem::take(&mut self.events), + for frame_profile in frame_profiles { + self.draw_frame_bars(frame_profile, debug_renderer); } - } - pub fn take_and_reset(&mut self) -> Self { - let events = std::mem::take(&mut self.events); - - *self = TransactionProfile::new(); + Profiler::draw_counters( + &[&renderer_profile.draw_calls, &renderer_profile.vertices], + None, + debug_renderer, + true, + &mut self.draw_state + ); - TransactionProfile { events } - } + Profiler::draw_counters( + &[ + &backend_profile.total_time, + &renderer_timers.cpu_time, + &renderer_timers.gpu_graph, + ], + None, + debug_renderer, + false, + &mut self.draw_state + ); - pub fn merge(&mut self, other: &mut Self) { - for i in 0..self.events.len() { - match (self.events[i], other.events[i]) { - (Event::Value(v1), Event::Value(v2)) => { - self.events[i] = Event::Value(v1.max(v2)); - } - (Event::Value(_), _) => {} - (_, Event::Value(v2)) => { - self.events[i] = Event::Value(v2); - } - (Event::None, evt) => { - self.events[i] = evt; + if !gpu_samplers.is_empty() { + let mut samplers = Vec::<PercentageProfileCounter>::new(); + // Gathering unique GPU samplers. This has O(N^2) complexity, + // but we only have a few samplers per target. + let mut total = 0.0; + for sampler in gpu_samplers { + let value = sampler.count as f32 * screen_fraction; + total += value; + match samplers.iter().position(|s| { + s.description as *const _ == sampler.tag.label as *const _ + }) { + Some(pos) => samplers[pos].value += value, + None => samplers.push(PercentageProfileCounter { + description: sampler.tag.label, + value, + }), } - (Event::Start(..), Event::Start(s)) => { - self.events[i] = Event::Start(s); - } - _=> {} } - other.events[i] = Event::None; + samplers.push(PercentageProfileCounter { + description: "Total", + value: total, + }); + let samplers: Vec<&dyn ProfileCounter> = samplers.iter().map(|sampler| { + sampler as &dyn ProfileCounter + }).collect(); + Profiler::draw_counters( + &samplers, + None, + debug_renderer, + false, + &mut self.draw_state, + ); } - } - pub fn clear(&mut self) { - for evt in &mut self.events { - *evt = Event::None; - } + let rect = + self.backend_graph + .draw_graph(self.draw_state.x_right, self.draw_state.y_right, "CPU (backend)", debug_renderer); + self.draw_state.y_right += rect.size.height + PROFILE_PADDING; + let rect = self.renderer_graph.draw_graph( + self.draw_state.x_right, + self.draw_state.y_right, + "CPU (renderer)", + debug_renderer, + ); + self.draw_state.y_right += rect.size.height + PROFILE_PADDING; + let rect = + self.ipc_graph + .draw_graph(self.draw_state.x_right, self.draw_state.y_right, "DisplayList IPC", debug_renderer); + self.draw_state.y_right += rect.size.height + PROFILE_PADDING; + + let rect = self.display_list_build_graph + .draw_graph(self.draw_state.x_right, self.draw_state.y_right, "DisplayList build", debug_renderer); + self.draw_state.y_right += rect.size.height + PROFILE_PADDING; + + let rect = self.scene_build_graph + .draw_graph(self.draw_state.x_right, self.draw_state.y_right, "Scene build", debug_renderer); + self.draw_state.y_right += rect.size.height + PROFILE_PADDING; + + let rect = self.gpu_graph + .draw_graph(self.draw_state.x_right, self.draw_state.y_right, "GPU", debug_renderer); + self.draw_state.y_right += rect.size.height + PROFILE_PADDING; + + let rect = self.blob_raster_graph + .draw_graph(self.draw_state.x_right, self.draw_state.y_right, "Blob pixels", debug_renderer); + self.draw_state.y_right += rect.size.height + PROFILE_PADDING; + + let rect = self.gpu_frames + .draw(self.draw_state.x_left, f32::max(self.draw_state.y_left, self.draw_state.y_right), debug_renderer); + self.draw_state.y_right += rect.size.height + PROFILE_PADDING; } -} -#[derive(Debug)] -pub struct GraphStats { - pub min: f64, - pub avg: f64, - pub max: f64, - pub sum: f64, - pub samples: usize, -} - -#[derive(Debug)] -pub struct Graph { - values: VecDeque<f64>, -} + fn draw_smart_profile( + &mut self, + backend_profile: &BackendProfileCounters, + renderer_profile: &RendererProfileCounters, + debug_renderer: &mut DebugRenderer, + ) { + while self.cooldowns.len() < 18 { + self.cooldowns.push(0); + } -impl Graph { - fn new(max_samples: usize) -> Self { - let mut values = VecDeque::new(); - values.reserve(max_samples); + // Always show the fps counter. + Profiler::draw_counters( + &[ + &renderer_profile.frame_time, + ], + None, + debug_renderer, + true, + &mut self.draw_state, + ); - Graph { values } - } + let mut start = 0; + let counters: &[&[&dyn ProfileCounter]] = &[ + &[ + &self.backend_time, + &self.renderer_time, + &self.gpu_time, + ], + &[ + &renderer_profile.color_passes, + &renderer_profile.alpha_passes, + &renderer_profile.draw_calls, + &renderer_profile.vertices, + &renderer_profile.rendered_picture_cache_tiles, + &renderer_profile.total_picture_cache_tiles, + ], + &[ + &backend_profile.resources.gpu_cache.allocated_rows, + &backend_profile.resources.gpu_cache.updated_rows, + &backend_profile.resources.gpu_cache.allocated_blocks, + &backend_profile.resources.gpu_cache.updated_blocks, + &backend_profile.resources.gpu_cache.saved_blocks, + ], + &[ + &backend_profile.resources.image_templates, + &backend_profile.resources.font_templates, + &backend_profile.resources.texture_cache.rasterized_blob_pixels, + &backend_profile.txn.display_lists, + ], + ]; - fn set(&mut self, val: f64) { - if self.values.len() == self.values.capacity() { - self.values.pop_back(); + for group in counters { + let end = start + group.len(); + Profiler::draw_counters( + &group[..], + Some(&mut self.cooldowns[start..end]), + debug_renderer, + true, + &mut self.draw_state, + ); + start = end; } - self.values.push_front(val); } - pub fn stats(&self) -> GraphStats { - let mut stats = GraphStats { - min: f64::MAX, - avg: 0.0, - max: -f64::MAX, - sum: 0.0, - samples: 0, - }; - - let mut samples = 0; - for value in &self.values { - if value.is_finite() { - stats.min = stats.min.min(*value); - stats.max = stats.max.max(*value); - stats.sum += *value; - samples += 1; - } + pub fn draw_profile( + &mut self, + frame_profiles: &[FrameProfileCounters], + backend_profile: &BackendProfileCounters, + renderer_profile: &RendererProfileCounters, + renderer_timers: &mut RendererProfileTimers, + gpu_samplers: &[GpuSampler<GpuProfileTag>], + screen_fraction: f32, + debug_renderer: &mut DebugRenderer, + style: ProfileStyle, + ) { + self.draw_state.x_left = 20.0; + self.draw_state.y_left = 50.0; + self.draw_state.x_right = 450.0; + self.draw_state.y_right = 40.0; + + let mut gpu_graph = 0; + let gpu_graphrs = mem::replace(&mut renderer_timers.gpu_samples, Vec::new()); + for sample in &gpu_graphrs { + gpu_graph += sample.time_ns; } - - if samples > 0 { - stats.avg = stats.sum / samples as f64; - stats.samples = samples; + renderer_timers.gpu_graph.set(gpu_graph); + + self.backend_graph + .push(backend_profile.total_time.nanoseconds); + self.backend_time.set(backend_profile.total_time.nanoseconds); + self.renderer_graph + .push(renderer_timers.cpu_time.nanoseconds); + self.renderer_time.set(renderer_timers.cpu_time.nanoseconds); + self.ipc_graph + .push(backend_profile.txn.total_send_time.nanoseconds); + self.display_list_build_graph + .push(backend_profile.txn.display_list_build_time.nanoseconds); + self.scene_build_graph + .push(backend_profile.txn.scene_build_time.nanoseconds); + self.blob_raster_graph + .push(backend_profile.resources.texture_cache.rasterized_blob_pixels.size as u64); + self.ipc_time.set(backend_profile.txn.total_send_time.nanoseconds); + self.gpu_graph.push(gpu_graph); + self.gpu_time.set(gpu_graph); + self.gpu_frames.push(gpu_graph, gpu_graphrs); + + match style { + ProfileStyle::Full => { + self.draw_full_profile( + frame_profiles, + backend_profile, + renderer_profile, + renderer_timers, + gpu_samplers, + screen_fraction, + debug_renderer, + ); + } + ProfileStyle::Compact => { + self.draw_compact_profile( + backend_profile, + renderer_profile, + debug_renderer, + ); + } + ProfileStyle::Smart => { + self.draw_smart_profile( + backend_profile, + renderer_profile, + debug_renderer, + ); + } } - - stats } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ShowAs { - Float, - Int, -} - -struct ProfilerFrame { - total_time: u64, - samples: Vec<GpuTimer>, -} - -struct ProfilerFrameCollection { - frames: VecDeque<ProfilerFrame>, +pub struct ChangeIndicator { + counter: u32, } -impl ProfilerFrameCollection { - fn new() -> Self { - ProfilerFrameCollection { - frames: VecDeque::new(), +impl ChangeIndicator { + pub fn new() -> Self { + ChangeIndicator { + counter: 0 } } - fn push(&mut self, frame: ProfilerFrame) { - if self.frames.len() == 20 { - self.frames.pop_back(); - } - self.frames.push_front(frame); + pub fn changed(&mut self) { + self.counter = (self.counter + 1) % 15; } -} - -impl From<FullFrameStats> for ProfilerFrame { - fn from(stats: FullFrameStats) -> ProfilerFrame { - let new_sample = |time, label, color| -> GpuTimer { - let tag = GpuProfileTag { - label, - color - }; - - let time_ns = ms_to_ns(time); - - GpuTimer { - tag, time_ns - } - }; - let samples = vec![ - new_sample(stats.gecko_display_list_time, "Gecko DL", ColorF { r: 0.0, g: 1.0, b: 0.0, a: 1.0 }), - new_sample(stats.wr_display_list_time, "WR DL", ColorF { r: 0.0, g: 1.0, b: 1.0, a: 1.0 }), - new_sample(stats.scene_build_time, "Scene Build", ColorF { r: 1.0, g: 0.0, b: 1.0, a: 1.0 }), - new_sample(stats.frame_build_time, "Frame Build", ColorF { r: 1.0, g: 0.0, b: 0.0, a: 1.0 }), - ]; + const WIDTH : f32 = 20.0; + const HEIGHT: f32 = 10.0; - ProfilerFrame { - total_time: ms_to_ns(stats.total()), - samples + pub fn width() -> f32 { + ChangeIndicator::WIDTH * 16.0 } - } -} - -pub fn ns_to_ms(ns: u64) -> f64 { - ns as f64 / 1_000_000.0 -} -pub fn ms_to_ns(ms: f64) -> u64 { - (ms * 1_000_000.0) as u64 -} - -pub fn bytes_to_mb(bytes: usize) -> f64 { - bytes as f64 / 1_000_000.0 -} + pub fn draw( + &self, + x: f32, y: f32, + color: ColorU, + debug_renderer: &mut DebugRenderer + ) { + let margin = 0.0; + let tx = self.counter as f32 * ChangeIndicator::WIDTH; + debug_renderer.add_quad( + x - margin, + y - margin, + x + 15.0 * ChangeIndicator::WIDTH + margin, + y + ChangeIndicator::HEIGHT + margin, + ColorU::new(0, 0, 0, 150), + ColorU::new(0, 0, 0, 150), + ); -#[derive(Debug, PartialEq)] -enum Item { - Counters(Vec<usize>), - Graph(usize), - ChangeIndicator(usize), - Fps, - GpuTimeQueries, - GpuCacheBars, - PaintPhaseGraph, - Text(String), - Space, - Column, - Row, + debug_renderer.add_quad( + x + tx, + y, + x + tx + ChangeIndicator::WIDTH, + y + ChangeIndicator::HEIGHT, + color, + ColorU::new(25, 25, 25, 255), + ); + } } - diff --git a/third_party/webrender/webrender/src/rectangle_occlusion.rs b/third_party/webrender/webrender/src/rectangle_occlusion.rs deleted file mode 100644 index a79e4ba0261..00000000000 --- a/third_party/webrender/webrender/src/rectangle_occlusion.rs +++ /dev/null @@ -1,208 +0,0 @@ -/* 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/. */ - -//! A simple occlusion culling algorithm for axis-aligned rectangles. -//! -//! ## Output -//! -//! Occlusion culling results in two lists of rectangles: -//! -//! - The opaque list should be rendered first. None of its rectangles overlap so order doesn't matter -//! within the opaque pass. -//! - The non-opaque list (or alpha list) which should be rendered in back-to-front order after the opaque pass. -//! -//! The output has minimal overdraw (no overdraw at all for opaque items and as little as possible for alpha ones). -//! -//! ## Algorithm overview -//! -//! The occlusion culling algorithm works in front-to-back order, accumulating rectangle in opaque and non-opaque lists. -//! Each time a rectangle is added, it is first tested against existing opaque rectangles and potentially split into visible -//! sub-rectangles, or even discarded completely. The front-to-back order ensures that once a rectangle is added it does not -//! have to be modified again, making the underlying data structure trivial (append-only). -//! -//! ## splitting -//! -//! Partially visible rectangles are split into up to 4 visible sub-rectangles by each intersecting occluder. -//! -//! ```ascii -//! +----------------------+ +----------------------+ -//! | rectangle | | | -//! | | | | -//! | +-----------+ | +--+-----------+-------+ -//! | |occluder | | --> | |\\\\\\\\\\\| | -//! | +-----------+ | +--+-----------+-------+ -//! | | | | -//! +----------------------+ +----------------------+ -//! ``` -//! -//! In the example above the rectangle is split into 4 visible parts with the central occluded part left out. -//! -//! This implementation favors longer horizontal bands instead creating nine-patches to deal with the corners. -//! The advantage is that it produces less rectangles which is good for the performance of the algorithm and -//! for SWGL which likes long horizontal spans, however it would cause artifacts if the resulting rectangles -//! were to be drawn with a non-axis-aligned transformation. -//! -//! ## Performance -//! -//! The cost of the algorithm grows with the number of opaque rectangle as each new rectangle is tested against -//! all previously added opaque rectangles. -//! -//! Note that opaque rectangles can either be added as opaque or non-opaque. This means a trade-off between -//! overdraw and number of rectangles can be explored to adjust performance: Small opaque rectangles, especially -//! towards the front of the scene, could be added as non-opaque to avoid causing many splits while adding only -//! a small amount of overdraw. -//! -//! This implementation is intended to be used with a small number of (opaque) items. A similar implementation -//! could use a spatial acceleration structure for opaque rectangles to perform better with a large amount of -//! occluders. -//! - -use euclid::point2; -use smallvec::SmallVec; -use api::units::*; - -/// A visible part of a rectangle after occlusion culling. -#[derive(Debug, PartialEq)] -pub struct Item { - pub rectangle: DeviceBox2D, - pub key: usize, -} - -/// A builder that applies occlusion culling with rectangles provided in front-to-back order. -pub struct FrontToBackBuilder { - opaque_items: Vec<Item>, - alpha_items: Vec<Item>, -} - -impl FrontToBackBuilder { - - /// Pre-allocating constructor. - pub fn with_capacity(opaque: usize, alpha: usize) -> Self { - FrontToBackBuilder { - opaque_items: Vec::with_capacity(opaque), - alpha_items: Vec::with_capacity(alpha), - } - } - - /// Add a rectangle, potentially splitting it and discarding the occluded parts if any. - /// - /// Returns true the rectangle is at least partially visible. - pub fn add(&mut self, rect: &DeviceBox2D, is_opaque: bool, key: usize) -> bool { - let mut fragments: SmallVec<[DeviceBox2D; 16]> = SmallVec::new(); - fragments.push(*rect); - - for item in &self.opaque_items { - if fragments.is_empty() { - break; - } - if item.rectangle.intersects(rect) { - apply_occluder(&item.rectangle, &mut fragments); - } - } - - let list = if is_opaque { - &mut self.opaque_items - } else { - &mut self.alpha_items - }; - - for rect in &fragments { - list.push(Item { - rectangle: *rect, - key, - }); - } - - !fragments.is_empty() - } - - /// Returns true if the provided rect is at least partially visible, without adding it. - pub fn test(&self, rect: &DeviceBox2D) -> bool { - let mut fragments: SmallVec<[DeviceBox2D; 16]> = SmallVec::new(); - fragments.push(*rect); - - for item in &self.opaque_items { - if item.rectangle.intersects(rect) { - apply_occluder(&item.rectangle, &mut fragments); - } - } - - !fragments.is_empty() - } - - /// The visible opaque rectangles (front-to-back order). - pub fn opaque_items(&self) -> &[Item] { - &self.opaque_items - } - - /// The visible non-opaque rectangles (front-to-back order). - pub fn alpha_items(&self) -> &[Item] { - &self.alpha_items - } -} - - -// Split out the parts of the rects in the provided vector -fn apply_occluder(occluder: &DeviceBox2D, rects: &mut SmallVec<[DeviceBox2D; 16]>) { - // Iterate in reverse order so that we can push new rects at the back without - // visiting them; - let mut i = rects.len() - 1; - loop { - let r = rects[i]; - - if r.intersects(occluder) { - let top = r.min.y < occluder.min.y; - let bottom = r.max.y > occluder.max.y; - let left = r.min.x < occluder.min.x; - let right = r.max.x > occluder.max.x; - - if top { - rects.push(DeviceBox2D { - min: r.min, - max: point2(r.max.x, occluder.min.y), - }); - } - - if bottom { - rects.push(DeviceBox2D { - min: point2(r.min.x, occluder.max.y), - max: r.max, - }); - } - - if left { - let min_y = r.min.y.max(occluder.min.y); - let max_y = r.max.y.min(occluder.max.y); - rects.push(DeviceBox2D { - min: point2(r.min.x, min_y), - max: point2(occluder.min.x, max_y), - }); - } - - if right { - let min_y = r.min.y.max(occluder.min.y); - let max_y = r.max.y.min(occluder.max.y); - rects.push(DeviceBox2D { - min: point2(occluder.max.x, min_y), - max: point2(r.max.x, max_y), - }); - } - - // Remove the original rectangle, replacing it with - // one of the new ones we just added, or popping it - // if it is the last item. - if i == rects.len() { - rects.pop(); - } else { - rects.swap_remove(i); - } - } - - if i == 0 { - break; - } - - i -= 1; - } -} diff --git a/third_party/webrender/webrender/src/render_backend.rs b/third_party/webrender/webrender/src/render_backend.rs index 825e981b5cf..e22596a1bab 100644 --- a/third_party/webrender/webrender/src/render_backend.rs +++ b/third_party/webrender/webrender/src/render_backend.rs @@ -8,39 +8,37 @@ //! See the comment at the top of the `renderer` module for a description of //! how these two pieces interact. -use api::{DebugFlags, BlobImageHandler}; -use api::{DocumentId, ExternalScrollId, HitTestResult}; -use api::{IdNamespace, PipelineId, RenderNotifier, ScrollClamping}; +use api::{ApiMsg, ClearCache, DebugCommand, DebugFlags, BlobImageHandler}; +use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestFlags, HitTestResult}; +use api::{IdNamespace, MemoryReport, PipelineId, RenderNotifier, ScrollClamping}; +use api::{ScrollLocation, TransactionMsg, ResourceUpdate}; use api::{NotificationRequest, Checkpoint, QualitySettings}; -use api::{PrimitiveKeyKind}; +use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind}; use api::units::*; -use api::channel::{single_msg_channel, Sender, Receiver}; #[cfg(any(feature = "capture", feature = "replay"))] -use crate::render_api::CaptureBits; +use api::CaptureBits; #[cfg(feature = "replay")] -use crate::render_api::CapturedDocument; -use crate::render_api::{MemoryReport, TransactionMsg, ResourceUpdate, ApiMsg, FrameMsg, ClearCache, DebugCommand}; -use crate::clip::{ClipIntern, PolygonIntern, ClipStoreScratchBuffer}; -use crate::filterdata::FilterDataIntern; +use api::CapturedDocument; +use crate::spatial_tree::SpatialNodeIndex; #[cfg(any(feature = "capture", feature = "replay"))] use crate::capture::CaptureConfig; use crate::composite::{CompositorKind, CompositeDescriptor}; -use crate::frame_builder::{FrameBuilder, FrameBuilderConfig, FrameScratchBuffer}; +#[cfg(feature = "debugger")] +use crate::debug_server; +use crate::frame_builder::{FrameBuilder, FrameBuilderConfig}; use crate::glyph_rasterizer::{FontInstance}; use crate::gpu_cache::GpuCache; use crate::hit_test::{HitTest, HitTester, SharedHitTester}; use crate::intern::DataStore; -#[cfg(any(feature = "capture", feature = "replay"))] -use crate::internal_types::DebugOutput; -use crate::internal_types::{FastHashMap, RenderedDocument, ResultMsg}; +use crate::internal_types::{DebugOutput, FastHashMap, RenderedDocument, ResultMsg}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use crate::picture::{TileCacheLogger, PictureScratchBuffer, SliceId, TileCacheInstance, TileCacheParams}; +use crate::picture::{RetainedTiles, TileCacheLogger}; use crate::prim_store::{PrimitiveScratchBuffer, PrimitiveInstance}; use crate::prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData, PrimitiveStore}; use crate::prim_store::interned::*; -use crate::profiler::{self, TransactionProfile}; -use crate::render_task_graph::RenderTaskGraphBuilder; -use crate::renderer::{AsyncPropertySampler, FullFrameStats, PipelineInfo}; +use crate::profiler::{BackendProfileCounters, ResourceProfileCounters}; +use crate::render_task_graph::RenderTaskGraphCounters; +use crate::renderer::{AsyncPropertySampler, PipelineInfo}; use crate::resource_cache::ResourceCache; #[cfg(feature = "replay")] use crate::resource_cache::PlainCacheOwn; @@ -52,12 +50,15 @@ use crate::scene::{BuiltScene, SceneProperties}; use crate::scene_builder_thread::*; #[cfg(feature = "serialize")] use serde::{Serialize, Deserialize}; +#[cfg(feature = "debugger")] +use serde_json; #[cfg(feature = "replay")] use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::mpsc::{channel, Sender, Receiver}; use std::time::{UNIX_EPOCH, SystemTime}; -use std::{mem, u32}; +use std::u32; #[cfg(feature = "capture")] use std::path::PathBuf; #[cfg(feature = "replay")] @@ -79,6 +80,7 @@ pub struct DocumentView { #[derive(Copy, Clone)] pub struct SceneView { pub device_rect: DeviceIntRect, + pub layer: DocumentLayer, pub device_pixel_ratio: f32, pub page_zoom_factor: f32, pub quality_settings: QualitySettings, @@ -164,9 +166,9 @@ impl ::std::ops::Sub<usize> for FrameId { FrameId(self.0 - other) } } + enum RenderBackendStatus { Continue, - StopRenderBackend, ShutDown(Option<Sender<()>>), } @@ -272,12 +274,12 @@ macro_rules! declare_data_stores { fn apply_updates( &mut self, updates: InternerUpdates, - profile: &mut TransactionProfile, + profile_counters: &mut BackendProfileCounters, ) { $( self.$name.apply_updates( updates.$name, - profile, + &mut profile_counters.intern.$name, ); )+ } @@ -285,7 +287,7 @@ macro_rules! declare_data_stores { } } -crate::enumerate_interners!(declare_data_stores); +enumerate_interners!(declare_data_stores); impl DataStores { /// Returns the local rect for a primitive. For most primitives, this is @@ -346,8 +348,7 @@ impl DataStores { let prim_data = &self.line_decoration[data_handle]; &prim_data.common } - PrimitiveInstanceKind::LinearGradient { data_handle, .. } - | PrimitiveInstanceKind::CachedLinearGradient { data_handle, .. } => { + PrimitiveInstanceKind::LinearGradient { data_handle, .. } => { let prim_data = &self.linear_grad[data_handle]; &prim_data.common } @@ -382,36 +383,6 @@ impl DataStores { } } -#[derive(Default)] -pub struct ScratchBuffer { - pub primitive: PrimitiveScratchBuffer, - pub picture: PictureScratchBuffer, - pub frame: FrameScratchBuffer, - pub clip_store: ClipStoreScratchBuffer, -} - -impl ScratchBuffer { - pub fn begin_frame(&mut self) { - self.primitive.begin_frame(); - self.picture.begin_frame(); - self.frame.begin_frame(); - } - - pub fn recycle(&mut self, recycler: &mut Recycler) { - self.primitive.recycle(recycler); - self.picture.recycle(recycler); - self.frame.recycle(recycler); - } - - pub fn memory_pressure(&mut self) { - // TODO: causes browser chrome test crashes on windows. - //self.primitive = Default::default(); - self.picture = Default::default(); - self.frame = Default::default(); - self.clip_store = Default::default(); - } -} - struct Document { /// The id of this document id: DocumentId, @@ -432,9 +403,6 @@ struct Document { /// The builder object that prodces frames, kept around to preserve some retained state. frame_builder: FrameBuilder, - /// Allows graphs of render tasks to be created, and then built into an immutable graph output. - rg_builder: RenderTaskGraphBuilder, - /// A data structure to allow hit testing against rendered frames. This is updated /// every time we produce a fully rendered frame. hit_tester: Option<Arc<HitTester>>, @@ -460,26 +428,23 @@ struct Document { /// Contains various vecs of data that is used only during frame building, /// where we want to recycle the memory each new display list, to avoid constantly /// re-allocating and moving memory around. - scratch: ScratchBuffer, + scratch: PrimitiveScratchBuffer, + /// Keep track of the size of render task graph to pre-allocate memory up-front + /// the next frame. + render_task_counters: RenderTaskGraphCounters, #[cfg(feature = "replay")] loaded_scene: Scene, /// Tracks the state of the picture cache tiles that were composited on the previous frame. prev_composite_descriptor: CompositeDescriptor, - - /// Tracks if we need to invalidate dirty rects for this document, due to the picture - /// cache slice configuration having changed when a new scene is swapped in. - dirty_rects_are_valid: bool, - - profile: TransactionProfile, - frame_stats: Option<FullFrameStats>, } impl Document { pub fn new( id: DocumentId, size: DeviceIntSize, + layer: DocumentLayer, default_device_pixel_ratio: f32, ) -> Self { Document { @@ -488,6 +453,7 @@ impl Document { view: DocumentView { scene: SceneView { device_rect: size.into(), + layer, page_zoom_factor: 1.0, device_pixel_ratio: default_device_pixel_ratio, quality_settings: QualitySettings::default(), @@ -508,14 +474,11 @@ impl Document { rendered_frame_is_valid: false, has_built_scene: false, data_stores: DataStores::default(), - scratch: ScratchBuffer::default(), + scratch: PrimitiveScratchBuffer::new(), + render_task_counters: RenderTaskGraphCounters::new(), #[cfg(feature = "replay")] loaded_scene: Scene::new(), prev_composite_descriptor: CompositeDescriptor::empty(), - dirty_rects_are_valid: true, - profile: TransactionProfile::new(), - rg_builder: RenderTaskGraphBuilder::new(), - frame_stats: None, } } @@ -535,6 +498,34 @@ impl Document { FrameMsg::UpdateEpoch(pipeline_id, epoch) => { self.scene.pipeline_epochs.insert(pipeline_id, epoch); } + FrameMsg::Scroll(delta, cursor) => { + profile_scope!("Scroll"); + + let node_index = match self.hit_tester { + Some(ref hit_tester) => { + // Ideally we would call self.scroll_nearest_scrolling_ancestor here, but + // we need have to avoid a double-borrow. + let test = HitTest::new(None, cursor, HitTestFlags::empty()); + hit_tester.find_node_under_point(test) + } + None => { + None + } + }; + + if self.hit_tester.is_some() + && self.scroll_nearest_scrolling_ancestor(delta, node_index) { + self.hit_tester_is_valid = false; + self.frame_is_valid = false; + } + + return DocumentOps { + // TODO: Does it make sense to track this as a scrolling even if we + // ended up not scrolling anything? + scroll: true, + ..DocumentOps::nop() + }; + } FrameMsg::HitTest(pipeline_id, point, flags, tx) => { if !self.hit_tester_is_valid { self.rebuild_hit_tester(); @@ -607,13 +598,10 @@ impl Document { &mut self, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, + resource_profile: &mut ResourceProfileCounters, debug_flags: DebugFlags, tile_cache_logger: &mut TileCacheLogger, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, - frame_stats: Option<FullFrameStats> ) -> RenderedDocument { - let frame_build_start_time = precise_time_ns(); - let accumulated_scale_factor = self.view.accumulated_scale_factor(); let pan = self.view.frame.pan.to_f32() / accumulated_scale_factor; @@ -628,44 +616,31 @@ impl Document { &mut self.scene, resource_cache, gpu_cache, - &mut self.rg_builder, self.stamp, accumulated_scale_factor, + self.view.scene.layer, self.view.scene.device_rect.origin, pan, + resource_profile, &self.dynamic_properties, &mut self.data_stores, &mut self.scratch, + &mut self.render_task_counters, debug_flags, tile_cache_logger, - tile_caches, - self.dirty_rects_are_valid, - &mut self.profile, ); frame }; self.frame_is_valid = true; - self.dirty_rects_are_valid = true; let is_new_scene = self.has_built_scene; self.has_built_scene = false; - let frame_build_time_ms = - profiler::ns_to_ms(precise_time_ns() - frame_build_start_time); - self.profile.set(profiler::FRAME_BUILDING_TIME, frame_build_time_ms); - - let frame_stats = frame_stats.map(|mut stats| { - stats.frame_build_time += frame_build_time_ms; - stats - }); - RenderedDocument { frame, is_new_scene, - profile: self.profile.take_and_reset(), - frame_stats: frame_stats } } @@ -679,7 +654,7 @@ impl Document { &self.dynamic_properties, ); - let hit_tester = Arc::new(self.scene.create_hit_tester()); + let hit_tester = Arc::new(self.scene.create_hit_tester(&self.data_stores.clip)); self.hit_tester = Some(Arc::clone(&hit_tester)); self.shared_hit_tester.update(hit_tester); self.hit_tester_is_valid = true; @@ -694,6 +669,15 @@ impl Document { } } + /// Returns true if any nodes actually changed position or false otherwise. + pub fn scroll_nearest_scrolling_ancestor( + &mut self, + scroll_location: ScrollLocation, + scroll_node_index: Option<SpatialNodeIndex>, + ) -> bool { + self.scene.spatial_tree.scroll_nearest_scrolling_ancestor(scroll_location, scroll_node_index) + } + /// Returns true if the node actually changed position or false otherwise. pub fn scroll_node( &mut self, @@ -704,77 +688,33 @@ impl Document { self.scene.spatial_tree.scroll_node(origin, id, clamp) } - /// Update the state of tile caches when a new scene is being swapped in to - /// the render backend. Retain / reuse existing caches if possible, and - /// destroy any now unused caches. - fn update_tile_caches_for_new_scene( - &mut self, - mut requested_tile_caches: FastHashMap<SliceId, TileCacheParams>, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, - resource_cache: &mut ResourceCache, - ) { - let mut new_tile_caches = FastHashMap::default(); - new_tile_caches.reserve(requested_tile_caches.len()); - - // Step through the tile caches that are needed for the new scene, and see - // if we have an existing cache that can be reused. - for (slice_id, params) in requested_tile_caches.drain() { - let tile_cache = match tile_caches.remove(&slice_id) { - Some(mut existing_tile_cache) => { - // Found an existing cache - update the cache params and reuse it - existing_tile_cache.prepare_for_new_scene( - params, - resource_cache, - ); - existing_tile_cache - } - None => { - // No cache exists so create a new one - Box::new(TileCacheInstance::new(params)) - } - }; - - new_tile_caches.insert(slice_id, tile_cache); - } - - // Replace current tile cache map, and return what was left over, - // which are now unused. - let unused_tile_caches = mem::replace( - tile_caches, - new_tile_caches, - ); - - if !unused_tile_caches.is_empty() { - // If the slice configuration changed, assume we can't rely on the - // current dirty rects for next composite - self.dirty_rects_are_valid = false; - - // Destroy any native surfaces allocated by these unused caches - for (_, tile_cache) in unused_tile_caches { - tile_cache.destroy(resource_cache); - } - } - } - pub fn new_async_scene_ready( &mut self, - mut built_scene: BuiltScene, + built_scene: BuiltScene, recycler: &mut Recycler, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, - resource_cache: &mut ResourceCache, ) { self.frame_is_valid = false; self.hit_tester_is_valid = false; - self.update_tile_caches_for_new_scene( - mem::replace(&mut built_scene.tile_cache_config.tile_caches, FastHashMap::default()), - tile_caches, - resource_cache, - ); - + // Give the old scene a chance to destroy any resources. + // Right now, all this does is build a hash map of any cached + // surface tiles, that can be provided to the next scene. + // TODO(nical) - It's a bit awkward how these retained tiles live + // in the scene's prim store then temporarily in the frame builder + // and then presumably back in the prim store during the next frame + // build. + let mut retained_tiles = RetainedTiles::new(); + self.scene.prim_store.destroy(&mut retained_tiles); let old_scrolling_states = self.scene.spatial_tree.drain(); + self.scene = built_scene; + + // Provide any cached tiles from the previous scene to + // the newly built one. + self.frame_builder.set_retained_resources(retained_tiles); + self.scratch.recycle(recycler); + self.scene.spatial_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states); } } @@ -813,6 +753,9 @@ pub struct RenderBackend { api_rx: Receiver<ApiMsg>, result_tx: Sender<ResultMsg>, scene_tx: Sender<SceneBuilderRequest>, + low_priority_scene_tx: Sender<SceneBuilderRequest>, + backend_scene_tx: Sender<BackendSceneBuilderRequest>, + scene_rx: Receiver<SceneBuilderResult>, default_device_pixel_ratio: f32, @@ -836,19 +779,10 @@ pub struct RenderBackend { blob_image_handler: Option<Box<dyn BlobImageHandler>>, recycler: Recycler, - #[cfg(feature = "capture")] - /// If `Some`, do 'sequence capture' logging, recording updated documents, - /// frames, etc. This is set only through messages from the scene builder, - /// so all control of sequence capture goes through there. capture_config: Option<CaptureConfig>, - #[cfg(feature = "replay")] loaded_resource_sequence_id: u32, - - /// A map of tile caches. These are stored in the backend as they are - /// persisted between both frame and scenes. - tile_caches: FastHashMap<SliceId, Box<TileCacheInstance>>, } impl RenderBackend { @@ -856,6 +790,9 @@ impl RenderBackend { api_rx: Receiver<ApiMsg>, result_tx: Sender<ResultMsg>, scene_tx: Sender<SceneBuilderRequest>, + low_priority_scene_tx: Sender<SceneBuilderRequest>, + backend_scene_tx: Sender<BackendSceneBuilderRequest>, + scene_rx: Receiver<SceneBuilderResult>, default_device_pixel_ratio: f32, resource_cache: ResourceCache, notifier: Box<dyn RenderNotifier>, @@ -870,6 +807,9 @@ impl RenderBackend { api_rx, result_tx, scene_tx, + low_priority_scene_tx, + backend_scene_tx, + scene_rx, default_device_pixel_ratio, resource_cache, gpu_cache: GpuCache::new(), @@ -888,7 +828,6 @@ impl RenderBackend { capture_config: None, #[cfg(feature = "replay")] loaded_resource_sequence_id: 0, - tile_caches: FastHashMap::default(), } } @@ -896,7 +835,7 @@ impl RenderBackend { IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32) } - pub fn run(&mut self) { + pub fn run(&mut self, mut profile_counters: BackendProfileCounters) { let mut frame_counter: u32 = 0; let mut status = RenderBackendStatus::Continue; @@ -905,49 +844,109 @@ impl RenderBackend { } while let RenderBackendStatus::Continue = status { - status = match self.api_rx.recv() { - Ok(msg) => { - self.process_api_msg(msg, &mut frame_counter) - } - Err(..) => { RenderBackendStatus::ShutDown(None) } - }; - } + while let Ok(msg) = self.scene_rx.try_recv() { + profile_scope!("rb_msg"); - if let RenderBackendStatus::StopRenderBackend = status { - while let Ok(msg) = self.api_rx.recv() { match msg { - ApiMsg::SceneBuilderResult(SceneBuilderResult::ExternalEvent(evt)) => { - self.notifier.external_event(evt); + SceneBuilderResult::Transactions(txns, result_tx) => { + self.process_transaction( + txns, + result_tx, + &mut frame_counter, + &mut profile_counters, + ); + self.bookkeep_after_frames(); + }, + #[cfg(feature = "capture")] + SceneBuilderResult::CapturedTransactions(txns, capture_config, result_tx) => { + if let Some(ref mut old_config) = self.capture_config { + assert!(old_config.scene_id <= capture_config.scene_id); + if old_config.scene_id < capture_config.scene_id { + old_config.scene_id = capture_config.scene_id; + old_config.frame_id = 0; + } + } else { + self.capture_config = Some(capture_config); + } + + let built_frame = self.process_transaction( + txns, + result_tx, + &mut frame_counter, + &mut profile_counters, + ); + + if built_frame { + self.save_capture_sequence(); + } + + self.bookkeep_after_frames(); + }, + SceneBuilderResult::GetGlyphDimensions(request) => { + let mut glyph_dimensions = Vec::with_capacity(request.glyph_indices.len()); + if let Some(base) = self.resource_cache.get_font_instance(request.key) { + let font = FontInstance::from_base(Arc::clone(&base)); + for glyph_index in &request.glyph_indices { + let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index); + glyph_dimensions.push(glyph_dim); + } + } + request.sender.send(glyph_dimensions).unwrap(); + } + SceneBuilderResult::GetGlyphIndices(request) => { + let mut glyph_indices = Vec::with_capacity(request.text.len()); + for ch in request.text.chars() { + let index = self.resource_cache.get_glyph_index(request.key, ch); + glyph_indices.push(index); + } + request.sender.send(glyph_indices).unwrap(); } - ApiMsg::SceneBuilderResult(SceneBuilderResult::FlushComplete(tx)) => { - // If somebody's blocked waiting for a flush, how did they - // trigger the RB thread to shut down? This shouldn't happen - // but handle it gracefully anyway. - debug_assert!(false); + SceneBuilderResult::FlushComplete(tx) => { tx.send(()).ok(); } - ApiMsg::SceneBuilderResult(SceneBuilderResult::ShutDown(sender)) => { - info!("Recycling stats: {:?}", self.recycler); - status = RenderBackendStatus::ShutDown(sender); - break; - } - _ => {}, + SceneBuilderResult::ExternalEvent(evt) => { + self.notifier.external_event(evt); + } + SceneBuilderResult::ClearNamespace(id) => { + self.resource_cache.clear_namespace(id); + self.documents.retain(|doc_id, _doc| doc_id.namespace_id != id); + if let Some(handler) = &mut self.blob_image_handler { + handler.clear_namespace(id); + } + } + SceneBuilderResult::Stopped => { + panic!("We haven't sent a Stop yet, how did we get a Stopped back?"); + } + SceneBuilderResult::DocumentsForDebugger(json) => { + let msg = ResultMsg::DebugOutput(DebugOutput::FetchDocuments(json)); + self.result_tx.send(msg).unwrap(); + self.notifier.wake_up(); + } } } + + status = match self.api_rx.recv() { + Ok(msg) => { + self.process_api_msg(msg, &mut profile_counters, &mut frame_counter) + } + Err(..) => { RenderBackendStatus::ShutDown(None) } + }; } + let _ = self.low_priority_scene_tx.send(SceneBuilderRequest::Stop); // Ensure we read everything the scene builder is sending us from // inflight messages, otherwise the scene builder might panic. - while let Ok(msg) = self.api_rx.try_recv() { + while let Ok(msg) = self.scene_rx.recv() { match msg { - ApiMsg::SceneBuilderResult(SceneBuilderResult::FlushComplete(tx)) => { + SceneBuilderResult::FlushComplete(tx) => { // If somebody's blocked waiting for a flush, how did they // trigger the RB thread to shut down? This shouldn't happen // but handle it gracefully anyway. debug_assert!(false); tx.send(()).ok(); } - _ => {}, + SceneBuilderResult::Stopped => break, + _ => continue, } } @@ -970,33 +969,41 @@ impl RenderBackend { mut txns: Vec<Box<BuiltTransaction>>, result_tx: Option<Sender<SceneSwapResult>>, frame_counter: &mut u32, + profile_counters: &mut BackendProfileCounters, ) -> bool { self.prepare_for_frames(); self.maybe_force_nop_documents( frame_counter, + profile_counters, |document_id| txns.iter().any(|txn| txn.document_id == document_id)); let mut built_frame = false; for mut txn in txns.drain(..) { let has_built_scene = txn.built_scene.is_some(); + if let Some(timings) = txn.timings { + if has_built_scene { + profile_counters.scene_changed = true; + } + + profile_counters.txn.set( + timings.builder_start_time_ns, + timings.builder_end_time_ns, + timings.send_time_ns, + timings.scene_build_start_time_ns, + timings.scene_build_end_time_ns, + timings.display_list_len, + ); + } + if let Some(doc) = self.documents.get_mut(&txn.document_id) { doc.removed_pipelines.append(&mut txn.removed_pipelines); doc.view.scene = txn.view; - doc.profile.merge(&mut txn.profile); - - doc.frame_stats = if let Some(stats) = &doc.frame_stats { - Some(stats.merge(&txn.frame_stats)) - } else { - Some(txn.frame_stats) - }; if let Some(built_scene) = txn.built_scene.take() { doc.new_async_scene_ready( built_scene, &mut self.recycler, - &mut self.tile_caches, - &mut self.resource_cache, ); } @@ -1010,7 +1017,7 @@ impl RenderBackend { self.tile_cache_logger.serialize_updates(&updates); } } - doc.data_stores.apply_updates(updates, &mut doc.profile); + doc.data_stores.apply_updates(updates, profile_counters); } // Build the hit tester while the APZ lock is held so that its content @@ -1020,7 +1027,7 @@ impl RenderBackend { } if let Some(ref tx) = result_tx { - let (resume_tx, resume_rx) = single_msg_channel(); + let (resume_tx, resume_rx) = channel(); tx.send(SceneSwapResult::Complete(resume_tx)).unwrap(); // Block until the post-swap hook has completed on // the scene builder thread. We need to do this before @@ -1034,12 +1041,6 @@ impl RenderBackend { .spatial_tree .discard_frame_state_for_pipeline(*pipeline_id); } - - self.resource_cache.add_rasterized_blob_images( - txn.rasterized_blobs.take(), - &mut doc.profile, - ); - } else { // The document was removed while we were building it, skip it. // TODO: we might want to just ensure that removed documents are @@ -1050,15 +1051,20 @@ impl RenderBackend { continue; } + self.resource_cache.add_rasterized_blob_images( + txn.rasterized_blobs.take(), + &mut profile_counters.resources.texture_cache, + ); + built_frame |= self.update_document( txn.document_id, txn.resource_updates.take(), txn.frame_ops.take(), txn.notifications.take(), txn.render_frame, - None, txn.invalidate_rendered_frame, frame_counter, + profile_counters, has_built_scene, ); } @@ -1069,9 +1075,23 @@ impl RenderBackend { fn process_api_msg( &mut self, msg: ApiMsg, + profile_counters: &mut BackendProfileCounters, frame_counter: &mut u32, ) -> RenderBackendStatus { match msg { + ApiMsg::WakeUp => {} + ApiMsg::WakeSceneBuilder => { + self.scene_tx.send(SceneBuilderRequest::WakeUp).unwrap(); + } + ApiMsg::FlushSceneBuilder(tx) => { + self.low_priority_scene_tx.send(SceneBuilderRequest::Flush(tx)).unwrap(); + } + ApiMsg::GetGlyphDimensions(request) => { + self.scene_tx.send(SceneBuilderRequest::GetGlyphDimensions(request)).unwrap(); + } + ApiMsg::GetGlyphIndices(request) => { + self.scene_tx.send(SceneBuilderRequest::GetGlyphIndices(request)).unwrap(); + } ApiMsg::CloneApi(sender) => { assert!(!self.namespace_alloc_by_client); sender.send(self.next_namespace_id()).unwrap(); @@ -1080,14 +1100,32 @@ impl RenderBackend { assert!(self.namespace_alloc_by_client); debug_assert!(!self.documents.iter().any(|(did, _doc)| did.namespace_id == namespace_id)); } - ApiMsg::AddDocument(document_id, initial_size) => { + ApiMsg::AddDocument(document_id, initial_size, layer) => { let document = Document::new( document_id, initial_size, + layer, self.default_device_pixel_ratio, ); let old = self.documents.insert(document_id, document); debug_assert!(old.is_none()); + + self.scene_tx.send( + SceneBuilderRequest::AddDocument(document_id, initial_size, layer) + ).unwrap(); + + } + ApiMsg::DeleteDocument(document_id) => { + self.documents.remove(&document_id); + self.low_priority_scene_tx.send( + SceneBuilderRequest::DeleteDocument(document_id) + ).unwrap(); + } + ApiMsg::ExternalEvent(evt) => { + self.low_priority_scene_tx.send(SceneBuilderRequest::ExternalEvent(evt)).unwrap(); + } + ApiMsg::ClearNamespace(id) => { + self.low_priority_scene_tx.send(SceneBuilderRequest::ClearNamespace(id)).unwrap(); } ApiMsg::MemoryPressure => { // This is drastic. It will basically flush everything out of the cache, @@ -1102,17 +1140,13 @@ impl RenderBackend { self.gpu_cache.clear(); - for (_, doc) in &mut self.documents { - doc.scratch.memory_pressure(); - } - let resource_updates = self.resource_cache.pending_updates(); let msg = ResultMsg::UpdateResources { resource_updates, memory_pressure: true, }; self.result_tx.send(msg).unwrap(); - self.notifier.wake_up(false); + self.notifier.wake_up(); } ApiMsg::ReportMemory(tx) => { self.report_memory(tx); @@ -1135,9 +1169,19 @@ impl RenderBackend { return RenderBackendStatus::Continue; } + DebugCommand::FetchDocuments => { + // Ask SceneBuilderThread to send JSON presentation of the documents, + // that will be forwarded to Renderer. + self.send_backend_message(BackendSceneBuilderRequest::DocumentsForDebugger); + return RenderBackendStatus::Continue; + } + DebugCommand::FetchClipScrollTree => { + let json = self.get_spatial_tree_for_debugger(); + ResultMsg::DebugOutput(DebugOutput::FetchClipScrollTree(json)) + } #[cfg(feature = "capture")] DebugCommand::SaveCapture(root, bits) => { - let output = self.save_capture(root, bits); + let output = self.save_capture(root, bits, profile_counters); ResultMsg::DebugOutput(output) }, #[cfg(feature = "capture")] @@ -1161,7 +1205,7 @@ impl RenderBackend { config.frame_id = frame_id; } - self.load_capture(config); + self.load_capture(config, profile_counters); for (id, doc) in &self.documents { let captured = CapturedDocument { @@ -1213,19 +1257,19 @@ impl RenderBackend { return RenderBackendStatus::Continue; } DebugCommand::SimulateLongSceneBuild(time_ms) => { - let _ = self.scene_tx.send(SceneBuilderRequest::SimulateLongSceneBuild(time_ms)); + self.scene_tx.send(SceneBuilderRequest::SimulateLongSceneBuild(time_ms)).unwrap(); + return RenderBackendStatus::Continue; + } + DebugCommand::SimulateLongLowPrioritySceneBuild(time_ms) => { + self.low_priority_scene_tx.send( + SceneBuilderRequest::SimulateLongLowPrioritySceneBuild(time_ms) + ).unwrap(); return RenderBackendStatus::Continue; } DebugCommand::SetFlags(flags) => { self.resource_cache.set_debug_flags(flags); self.gpu_cache.set_debug_flags(flags); - let force_invalidation = flags.contains(DebugFlags::FORCE_PICTURE_INVALIDATION); - if self.frame_config.force_invalidation != force_invalidation { - self.frame_config.force_invalidation = force_invalidation; - self.update_frame_builder_config(); - } - // If we're toggling on the GPU cache debug display, we // need to blow away the cache. This is because we only // send allocation/free notifications to the renderer @@ -1246,108 +1290,19 @@ impl RenderBackend { _ => ResultMsg::DebugCommand(option), }; self.result_tx.send(msg).unwrap(); - self.notifier.wake_up(true); + self.notifier.wake_up(); + } + ApiMsg::ShutDown(sender) => { + info!("Recycling stats: {:?}", self.recycler); + return RenderBackendStatus::ShutDown(sender); } ApiMsg::UpdateDocuments(transaction_msgs) => { self.prepare_transactions( transaction_msgs, frame_counter, + profile_counters, ); } - ApiMsg::SceneBuilderResult(msg) => { - return self.process_scene_builder_result(msg, frame_counter); - } - } - - RenderBackendStatus::Continue - } - - fn process_scene_builder_result( - &mut self, - msg: SceneBuilderResult, - frame_counter: &mut u32, - ) -> RenderBackendStatus { - profile_scope!("sb_msg"); - - match msg { - SceneBuilderResult::Transactions(txns, result_tx) => { - self.process_transaction( - txns, - result_tx, - frame_counter, - ); - self.bookkeep_after_frames(); - }, - #[cfg(feature = "capture")] - SceneBuilderResult::CapturedTransactions(txns, capture_config, result_tx) => { - if let Some(ref mut old_config) = self.capture_config { - assert!(old_config.scene_id <= capture_config.scene_id); - if old_config.scene_id < capture_config.scene_id { - old_config.scene_id = capture_config.scene_id; - old_config.frame_id = 0; - } - } else { - self.capture_config = Some(capture_config); - } - - let built_frame = self.process_transaction( - txns, - result_tx, - frame_counter, - ); - - if built_frame { - self.save_capture_sequence(); - } - - self.bookkeep_after_frames(); - }, - #[cfg(feature = "capture")] - SceneBuilderResult::StopCaptureSequence => { - self.capture_config = None; - } - SceneBuilderResult::GetGlyphDimensions(request) => { - let mut glyph_dimensions = Vec::with_capacity(request.glyph_indices.len()); - if let Some(base) = self.resource_cache.get_font_instance(request.key) { - let font = FontInstance::from_base(Arc::clone(&base)); - for glyph_index in &request.glyph_indices { - let glyph_dim = self.resource_cache.get_glyph_dimensions(&font, *glyph_index); - glyph_dimensions.push(glyph_dim); - } - } - request.sender.send(glyph_dimensions).unwrap(); - } - SceneBuilderResult::GetGlyphIndices(request) => { - let mut glyph_indices = Vec::with_capacity(request.text.len()); - for ch in request.text.chars() { - let index = self.resource_cache.get_glyph_index(request.key, ch); - glyph_indices.push(index); - } - request.sender.send(glyph_indices).unwrap(); - } - SceneBuilderResult::FlushComplete(tx) => { - tx.send(()).ok(); - } - SceneBuilderResult::ExternalEvent(evt) => { - self.notifier.external_event(evt); - } - SceneBuilderResult::ClearNamespace(id) => { - self.resource_cache.clear_namespace(id); - self.documents.retain(|doc_id, _doc| doc_id.namespace_id != id); - if let Some(handler) = &mut self.blob_image_handler { - handler.clear_namespace(id); - } - } - SceneBuilderResult::DeleteDocument(document_id) => { - self.documents.remove(&document_id); - } - SceneBuilderResult::StopRenderBackend => { - return RenderBackendStatus::StopRenderBackend; - } - SceneBuilderResult::ShutDown(sender) => { - info!("Recycling stats: {:?}", self.recycler); - return RenderBackendStatus::ShutDown(sender); - } } RenderBackendStatus::Continue @@ -1355,7 +1310,7 @@ impl RenderBackend { fn update_frame_builder_config(&self) { self.send_backend_message( - SceneBuilderRequest::SetFrameBuilderConfig( + BackendSceneBuilderRequest::SetFrameBuilderConfig( self.frame_config.clone() ) ); @@ -1377,37 +1332,55 @@ impl RenderBackend { &mut self, txns: Vec<Box<TransactionMsg>>, frame_counter: &mut u32, + profile_counters: &mut BackendProfileCounters, ) { - self.prepare_for_frames(); - self.maybe_force_nop_documents( - frame_counter, - |document_id| txns.iter().any(|txn| txn.document_id == document_id)); + let mut use_scene_builder = txns.iter() + .any(|transaction_msg| transaction_msg.use_scene_builder_thread); + let use_high_priority = txns.iter() + .any(|transaction_msg| !transaction_msg.low_priority); + + use_scene_builder = use_scene_builder || txns.iter().any(|txn| { + !txn.scene_ops.is_empty() + || !txn.blob_requests.is_empty() + || txn.blob_rasterizer.is_some() + }); - let mut built_frame = false; - for mut txn in txns { - if txn.generate_frame.as_bool() { - txn.profile.end_time(profiler::API_SEND_TIME); + if !use_scene_builder { + self.prepare_for_frames(); + self.maybe_force_nop_documents( + frame_counter, + profile_counters, + |document_id| txns.iter().any(|txn| txn.document_id == document_id)); + + let mut built_frame = false; + for mut txn in txns { + built_frame |= self.update_document( + txn.document_id, + txn.resource_updates.take(), + txn.frame_ops.take(), + txn.notifications.take(), + txn.generate_frame, + txn.invalidate_rendered_frame, + frame_counter, + profile_counters, + false + ); + } + if built_frame { + #[cfg(feature = "capture")] + self.save_capture_sequence(); } + self.bookkeep_after_frames(); + return; + } - self.documents.get_mut(&txn.document_id).unwrap().profile.merge(&mut txn.profile); + let tx = if use_high_priority { + &self.scene_tx + } else { + &self.low_priority_scene_tx + }; - built_frame |= self.update_document( - txn.document_id, - txn.resource_updates.take(), - txn.frame_ops.take(), - txn.notifications.take(), - txn.generate_frame.as_bool(), - txn.generate_frame.id(), - txn.invalidate_rendered_frame, - frame_counter, - false - ); - } - if built_frame { - #[cfg(feature = "capture")] - self.save_capture_sequence(); - } - self.bookkeep_after_frames(); + tx.send(SceneBuilderRequest::Transactions(txns)).unwrap(); } /// In certain cases, resources shared by multiple documents have to run @@ -1418,6 +1391,7 @@ impl RenderBackend { /// to force a frame build. fn maybe_force_nop_documents<F>(&mut self, frame_counter: &mut u32, + profile_counters: &mut BackendProfileCounters, document_already_present: F) where F: Fn(DocumentId) -> bool { if self.requires_frame_build() { @@ -1434,9 +1408,9 @@ impl RenderBackend { Vec::default(), Vec::default(), false, - None, false, frame_counter, + profile_counters, false); } #[cfg(feature = "capture")] @@ -1454,24 +1428,24 @@ impl RenderBackend { mut frame_ops: Vec<FrameMsg>, mut notifications: Vec<NotificationRequest>, mut render_frame: bool, - generated_frame_id: Option<u64>, invalidate_rendered_frame: bool, frame_counter: &mut u32, + profile_counters: &mut BackendProfileCounters, has_built_scene: bool, ) -> bool { 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 // before rendering. This is useful for rendering with the latest // async transforms. - if requested_frame { + if requested_frame || has_built_scene { if let Some(ref sampler) = self.sampler { - frame_ops.append(&mut sampler.sample(document_id, generated_frame_id)); + frame_ops.append(&mut sampler.sample(document_id, + &doc.scene.pipeline_epochs)); } } @@ -1481,6 +1455,7 @@ impl RenderBackend { // for something wrench specific and we should remove it. let mut scroll = false; for frame_msg in frame_ops { + let _timer = profile_counters.total_time.timer(); let op = doc.process_frame_msg(frame_msg); scroll |= op.scroll; } @@ -1493,7 +1468,7 @@ impl RenderBackend { self.resource_cache.post_scene_building_update( resource_updates, - &mut doc.profile, + &mut profile_counters.resources, ); if doc.dynamic_properties.flush_pending_updates() { @@ -1521,9 +1496,13 @@ impl RenderBackend { // external image with NativeTexture or when platform requested to composite frame. if invalidate_rendered_frame { doc.rendered_frame_is_valid = false; - if doc.scene.config.compositor_kind.should_redraw_on_invalidation() { - let msg = ResultMsg::ForceRedraw; - self.result_tx.send(msg).unwrap(); + if let CompositorKind::Draw { max_partial_present_rects, .. } = doc.scene.config.compositor_kind { + + // When partial present is enabled, we need to force redraw. + if max_partial_present_rects > 0 { + let msg = ResultMsg::ForceRedraw; + self.result_tx.send(msg).unwrap(); + } } } @@ -1535,17 +1514,15 @@ impl RenderBackend { // borrow ck hack for profile_counters let (pending_update, rendered_document) = { + let _timer = profile_counters.total_time.timer(); let frame_build_start_time = precise_time_ns(); - let frame_stats = doc.frame_stats.take(); - let rendered_document = doc.build_frame( &mut self.resource_cache, &mut self.gpu_cache, + &mut profile_counters.resources, self.debug_flags, &mut self.tile_cache_logger, - &mut self.tile_caches, - frame_stats ); debug!("generated frame for document {:?} with {} passes", @@ -1605,8 +1582,10 @@ impl RenderBackend { document_id, rendered_document, pending_update, + profile_counters.clone() ); self.result_tx.send(msg).unwrap(); + profile_counters.reset(); } else if requested_frame { // WR-internal optimization to avoid doing a bunch of render work if // there's no pixels. We still want to pretend to render and request @@ -1646,8 +1625,32 @@ impl RenderBackend { build_frame } - fn send_backend_message(&self, msg: SceneBuilderRequest) { - self.scene_tx.send(msg).unwrap(); + fn send_backend_message(&self, msg: BackendSceneBuilderRequest) { + self.backend_scene_tx.send(msg).unwrap(); + self.low_priority_scene_tx.send(SceneBuilderRequest::BackendMessage).unwrap(); + } + + #[cfg(not(feature = "debugger"))] + fn get_spatial_tree_for_debugger(&self) -> String { + String::new() + } + + #[cfg(feature = "debugger")] + fn get_spatial_tree_for_debugger(&self) -> String { + use crate::print_tree::PrintableTree; + + let mut debug_root = debug_server::SpatialTreeList::new(); + + for (_, doc) in &self.documents { + let debug_node = debug_server::TreeNode::new("document spatial tree"); + let mut builder = debug_server::TreeNodeBuilder::new(debug_node); + + doc.scene.spatial_tree.print_with(&mut builder); + + debug_root.add(builder.build()); + } + + serde_json::to_string(&debug_root).unwrap() } fn report_memory(&mut self, tx: Sender<Box<MemoryReport>>) { @@ -1666,15 +1669,12 @@ impl RenderBackend { } (*report) += self.resource_cache.report_memory(op); - report.texture_cache_structures = self.resource_cache - .texture_cache - .report_memory(ops); // Send a message to report memory on the scene-builder thread, which // will add its report to this one and send the result back to the original // thread waiting on the request. self.send_backend_message( - SceneBuilderRequest::ReportMemory(report, tx) + BackendSceneBuilderRequest::ReportMemory(report, tx) ); } @@ -1709,6 +1709,7 @@ impl RenderBackend { &mut self, root: PathBuf, bits: CaptureBits, + profile_counters: &mut BackendProfileCounters, ) -> DebugOutput { use std::fs; use crate::render_task_graph::dump_render_tasks_as_svg; @@ -1731,10 +1732,9 @@ impl RenderBackend { let rendered_document = doc.build_frame( &mut self.resource_cache, &mut self.gpu_cache, + &mut profile_counters.resources, self.debug_flags, &mut self.tile_cache_logger, - &mut self.tile_caches, - None, ); // After we rendered the frames, there are pending updates to both // GPU cache and resources. Instead of serializing them, we are going to make sure @@ -1753,34 +1753,15 @@ impl RenderBackend { let file_name = format!("built-clips-{}-{}", id.namespace_id.0, id.id); config.serialize_for_frame(&doc.scene.clip_store, file_name); let file_name = format!("scratch-{}-{}", id.namespace_id.0, id.id); - config.serialize_for_frame(&doc.scratch.primitive, file_name); + config.serialize_for_frame(&doc.scratch, file_name); let file_name = format!("render-tasks-{}-{}.svg", id.namespace_id.0, id.id); - let mut render_tasks_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) + let mut svg_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) .expect("Failed to open the SVG file."); dump_render_tasks_as_svg( &rendered_document.frame.render_tasks, - &mut render_tasks_file + &rendered_document.frame.passes, + &mut svg_file ).unwrap(); - - let file_name = format!("texture-cache-color-linear-{}-{}.svg", id.namespace_id.0, id.id); - let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) - .expect("Failed to open the SVG file."); - self.resource_cache.texture_cache.dump_color8_linear_as_svg(&mut texture_file).unwrap(); - - let file_name = format!("texture-cache-color8-glyphs-{}-{}.svg", id.namespace_id.0, id.id); - let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) - .expect("Failed to open the SVG file."); - self.resource_cache.texture_cache.dump_color8_glyphs_as_svg(&mut texture_file).unwrap(); - - let file_name = format!("texture-cache-alpha8-glyphs-{}-{}.svg", id.namespace_id.0, id.id); - let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) - .expect("Failed to open the SVG file."); - self.resource_cache.texture_cache.dump_alpha8_glyphs_as_svg(&mut texture_file).unwrap(); - - let file_name = format!("texture-cache-alpha8-linear-{}-{}.svg", id.namespace_id.0, id.id); - let mut texture_file = fs::File::create(&config.file_path_for_frame(file_name, "svg")) - .expect("Failed to open the SVG file."); - self.resource_cache.texture_cache.dump_alpha8_linear_as_svg(&mut texture_file).unwrap(); } let data_stores_name = format!("data-stores-{}-{}", id.namespace_id.0, id.id); @@ -1800,7 +1781,7 @@ impl RenderBackend { debug!("\tscene builder"); self.send_backend_message( - SceneBuilderRequest::SaveScene(config.clone()) + BackendSceneBuilderRequest::SaveScene(config.clone()) ); debug!("\tresource cache"); @@ -1849,7 +1830,7 @@ impl RenderBackend { bits: CaptureBits, ) { self.send_backend_message( - SceneBuilderRequest::StartCaptureSequence(CaptureConfig::new(root, bits)) + BackendSceneBuilderRequest::StartCaptureSequence(CaptureConfig::new(root, bits)) ); } @@ -1858,7 +1839,7 @@ impl RenderBackend { &mut self, ) { self.send_backend_message( - SceneBuilderRequest::StopCaptureSequence + BackendSceneBuilderRequest::StopCaptureSequence ); } @@ -1866,6 +1847,7 @@ impl RenderBackend { fn load_capture( &mut self, mut config: CaptureConfig, + profile_counters: &mut BackendProfileCounters, ) { debug!("capture: loading {:?}", config.frame_root()); let backend = config.deserialize_for_frame::<PlainRenderBackend, _>("backend") @@ -1971,13 +1953,10 @@ impl RenderBackend { rendered_frame_is_valid: false, has_built_scene: false, data_stores, - scratch: ScratchBuffer::default(), + scratch: PrimitiveScratchBuffer::new(), + render_task_counters: RenderTaskGraphCounters::new(), loaded_scene: scene.clone(), prev_composite_descriptor: CompositeDescriptor::empty(), - dirty_rects_are_valid: false, - profile: TransactionProfile::new(), - rg_builder: RenderTaskGraphBuilder::new(), - frame_stats: None, }; entry.insert(doc); } @@ -1994,10 +1973,12 @@ impl RenderBackend { let msg_publish = ResultMsg::PublishDocument( id, - RenderedDocument { frame, is_new_scene: true, profile: TransactionProfile::new(), frame_stats: None }, + RenderedDocument { frame, is_new_scene: true }, self.resource_cache.pending_updates(), + profile_counters.clone(), ); self.result_tx.send(msg_publish).unwrap(); + profile_counters.reset(); self.notifier.new_frame_ready(id, false, true, None); @@ -2021,7 +2002,7 @@ impl RenderBackend { if !scenes_to_build.is_empty() { self.send_backend_message( - SceneBuilderRequest::LoadScenes(scenes_to_build) + BackendSceneBuilderRequest::LoadScenes(scenes_to_build) ); } } diff --git a/third_party/webrender/webrender/src/render_target.rs b/third_party/webrender/webrender/src/render_target.rs index 301ce4ec4d8..9c62297c929 100644 --- a/third_party/webrender/webrender/src/render_target.rs +++ b/third_party/webrender/webrender/src/render_target.rs @@ -4,32 +4,42 @@ use api::units::*; -use api::{ColorF, ImageFormat, LineOrientation, BorderStyle}; -use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures}; +use api::{ColorF, PremultipliedColorF, ImageFormat, LineOrientation, BorderStyle, PipelineId}; +use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures, resolve_image}; use crate::batch::{ClipBatcher, BatchBuilder}; use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX}; use crate::clip::ClipStore; use crate::composite::CompositeState; -use crate::frame_builder::FrameGlobalResources; +use crate::device::Texture; +use crate::frame_builder::{FrameGlobalResources}; use crate::gpu_cache::{GpuCache, GpuCacheAddress}; use crate::gpu_types::{BorderInstance, SvgFilterInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance}; use crate::gpu_types::{TransformPalette, ZBufferIdGenerator}; -use crate::internal_types::{FastHashMap, TextureSource, CacheTextureId}; -use crate::picture::{SliceId, SurfaceInfo, ResolvedSurfaceTexture, TileCacheInstance}; -use crate::prim_store::{PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer}; -use crate::prim_store::gradient::{ - FastLinearGradientInstance, LinearGradientInstance, RadialGradientInstance, - ConicGradientInstance, -}; +use crate::internal_types::{FastHashMap, TextureSource, LayerIndex, Swizzle, SavedTargetIndex}; +use crate::picture::{SurfaceInfo, ResolvedSurfaceTexture}; +use crate::prim_store::{PrimitiveStore, DeferredResolve, PrimitiveScratchBuffer, PrimitiveVisibilityMask}; +use crate::prim_store::gradient::GRADIENT_FP_STOPS; use crate::render_backend::DataStores; -use crate::render_task::{RenderTaskKind, RenderTaskAddress}; +use crate::render_task::{RenderTaskKind, RenderTaskAddress, ClearMode, BlitSource}; use crate::render_task::{RenderTask, ScalingTask, SvgFilterInfo}; use crate::render_task_graph::{RenderTaskGraph, RenderTaskId}; use crate::resource_cache::ResourceCache; +use crate::texture_allocator::{ArrayAllocationTracker, FreeRectSlice}; +use std::{cmp, mem}; + const STYLE_SOLID: i32 = ((BorderStyle::Solid as i32) << 8) | ((BorderStyle::Solid as i32) << 16); const STYLE_MASK: i32 = 0x00FF_FF00; +/// According to apitrace, textures larger than 2048 break fast clear +/// optimizations on some intel drivers. We sometimes need to go larger, but +/// we try to avoid it. This can go away when proper tiling support lands, +/// since we can then split large primitives across multiple textures. +const IDEAL_MAX_TEXTURE_DIMENSION: i32 = 2048; +/// If we ever need a larger texture than the ideal, we better round it up to a +/// reasonable number in order to have a bit of leeway in placing things inside. +const TEXTURE_DIMENSION_MASK: i32 = 0xFF; + /// A tag used to identify the output format of a `RenderTarget`. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] #[cfg_attr(feature = "capture", derive(Serialize))] @@ -59,7 +69,6 @@ pub struct RenderTargetContext<'a, 'rc> { pub scratch: &'a PrimitiveScratchBuffer, pub screen_world_rect: WorldRect, pub globals: &'a FrameGlobalResources, - pub tile_caches: &'a FastHashMap<SliceId, Box<TileCacheInstance>>, } /// Represents a number of rendering operations on a surface. @@ -78,10 +87,8 @@ pub struct RenderTargetContext<'a, 'rc> { pub trait RenderTarget { /// Creates a new RenderTarget of the given type. fn new( - texture_id: CacheTextureId, screen_size: DeviceIntSize, gpu_supports_fast_clears: bool, - used_rect: DeviceIntRect, ) -> Self; /// Optional hook to provide additional processing for the target at the @@ -90,7 +97,7 @@ pub trait RenderTarget { &mut self, _ctx: &mut RenderTargetContext, _gpu_cache: &mut GpuCache, - _render_tasks: &RenderTaskGraph, + _render_tasks: &mut RenderTaskGraph, _deferred_resolves: &mut Vec<DeferredResolve>, _prim_headers: &mut PrimitiveHeaders, _transforms: &mut TransformPalette, @@ -116,10 +123,13 @@ pub trait RenderTarget { render_tasks: &RenderTaskGraph, clip_store: &ClipStore, transforms: &mut TransformPalette, + deferred_resolves: &mut Vec<DeferredResolve>, ); fn needs_depth(&self) -> bool; - fn texture_id(&self) -> CacheTextureId; + + fn used_rect(&self) -> DeviceIntRect; + fn add_used(&mut self, rect: DeviceIntRect); } /// A series of `RenderTarget` instances, serving as the high-level container @@ -146,21 +156,40 @@ pub trait RenderTarget { /// previous pass it depends on. /// /// Note that in some cases (like drop-shadows), we can depend on the output of -/// a pass earlier than the immediately-preceding pass. +/// a pass earlier than the immediately-preceding pass. See `SavedTargetIndex`. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct RenderTargetList<T> { + screen_size: DeviceIntSize, pub format: ImageFormat, + /// The maximum width and height of any single primitive we've encountered + /// that will be drawn to a dynamic location. + /// + /// We initially create our per-slice allocators with a width and height of + /// IDEAL_MAX_TEXTURE_DIMENSION. If we encounter a larger primitive, the + /// allocation will fail, but we'll bump max_dynamic_size, which will cause the + /// allocator for the next slice to be just large enough to accomodate it. + pub max_dynamic_size: DeviceIntSize, pub targets: Vec<T>, + pub saved_index: Option<SavedTargetIndex>, + pub alloc_tracker: ArrayAllocationTracker, + gpu_supports_fast_clears: bool, } impl<T: RenderTarget> RenderTargetList<T> { pub fn new( + screen_size: DeviceIntSize, format: ImageFormat, + gpu_supports_fast_clears: bool, ) -> Self { RenderTargetList { + screen_size, format, + max_dynamic_size: DeviceIntSize::new(0, 0), targets: Vec::new(), + saved_index: None, + alloc_tracker: ArrayAllocationTracker::new(), + gpu_supports_fast_clears, } } @@ -168,16 +197,16 @@ impl<T: RenderTarget> RenderTargetList<T> { &mut self, ctx: &mut RenderTargetContext, gpu_cache: &mut GpuCache, - render_tasks: &RenderTaskGraph, + render_tasks: &mut RenderTaskGraph, deferred_resolves: &mut Vec<DeferredResolve>, + saved_index: Option<SavedTargetIndex>, prim_headers: &mut PrimitiveHeaders, transforms: &mut TransformPalette, z_generator: &mut ZBufferIdGenerator, composite_state: &mut CompositeState, ) { - if self.targets.is_empty() { - return; - } + debug_assert_eq!(None, self.saved_index); + self.saved_index = saved_index; for target in &mut self.targets { target.build( @@ -193,9 +222,63 @@ impl<T: RenderTarget> RenderTargetList<T> { } } + pub fn allocate( + &mut self, + alloc_size: DeviceIntSize, + ) -> (RenderTargetIndex, DeviceIntPoint) { + let (free_rect_slice, origin) = match self.alloc_tracker.allocate(&alloc_size) { + Some(allocation) => allocation, + None => { + // Have the allocator restrict slice sizes to our max ideal + // dimensions, unless we've already gone bigger on a previous + // slice. + let rounded_dimensions = DeviceIntSize::new( + (self.max_dynamic_size.width + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK, + (self.max_dynamic_size.height + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK, + ); + let allocator_dimensions = DeviceIntSize::new( + cmp::max(IDEAL_MAX_TEXTURE_DIMENSION, rounded_dimensions.width), + cmp::max(IDEAL_MAX_TEXTURE_DIMENSION, rounded_dimensions.height), + ); + + assert!(alloc_size.width <= allocator_dimensions.width && + alloc_size.height <= allocator_dimensions.height); + let slice = FreeRectSlice(self.targets.len() as u32); + self.targets.push(T::new(self.screen_size, self.gpu_supports_fast_clears)); + + self.alloc_tracker.extend( + slice, + allocator_dimensions, + alloc_size, + ); + + (slice, DeviceIntPoint::zero()) + } + }; + + if alloc_size.is_empty() && self.targets.is_empty() { + // push an unused target here, only if we don't have any + self.targets.push(T::new(self.screen_size, self.gpu_supports_fast_clears)); + } + + self.targets[free_rect_slice.0 as usize] + .add_used(DeviceIntRect::new(origin, alloc_size)); + + (RenderTargetIndex(free_rect_slice.0 as usize), origin) + } + pub fn needs_depth(&self) -> bool { self.targets.iter().any(|target| target.needs_depth()) } + + pub fn check_ready(&self, t: &Texture) { + let dimensions = t.get_dimensions(); + assert!(dimensions.width >= self.max_dynamic_size.width); + assert!(dimensions.height >= self.max_dynamic_size.height); + assert_eq!(t.get_format(), self.format); + assert_eq!(t.get_layer_count() as usize, self.targets.len()); + assert!(t.supports_depth() >= self.needs_depth()); + } } @@ -208,14 +291,16 @@ impl<T: RenderTarget> RenderTargetList<T> { pub struct ColorRenderTarget { pub alpha_batch_containers: Vec<AlphaBatchContainer>, // List of blur operations to apply for this render target. - pub vertical_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>, - pub horizontal_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>, + pub vertical_blurs: Vec<BlurInstance>, + pub horizontal_blurs: Vec<BlurInstance>, + pub readbacks: Vec<DeviceIntRect>, pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>, pub svg_filters: Vec<(BatchTextures, Vec<SvgFilterInstance>)>, pub blits: Vec<BlitJob>, + // List of frame buffer outputs for this render target. + pub outputs: Vec<FrameOutput>, alpha_tasks: Vec<RenderTaskId>, screen_size: DeviceIntSize, - pub texture_id: CacheTextureId, // Track the used rect of the render target, so that // we can set a scissor rect and only clear to the // used portion of the target as an optimization. @@ -224,22 +309,21 @@ pub struct ColorRenderTarget { impl RenderTarget for ColorRenderTarget { fn new( - texture_id: CacheTextureId, screen_size: DeviceIntSize, _: bool, - used_rect: DeviceIntRect, ) -> Self { ColorRenderTarget { alpha_batch_containers: Vec::new(), - vertical_blurs: FastHashMap::default(), - horizontal_blurs: FastHashMap::default(), + vertical_blurs: Vec::new(), + horizontal_blurs: Vec::new(), + readbacks: Vec::new(), scalings: FastHashMap::default(), svg_filters: Vec::new(), blits: Vec::new(), + outputs: Vec::new(), alpha_tasks: Vec::new(), screen_size, - texture_id, - used_rect, + used_rect: DeviceIntRect::zero(), } } @@ -247,7 +331,7 @@ impl RenderTarget for ColorRenderTarget { &mut self, ctx: &mut RenderTargetContext, gpu_cache: &mut GpuCache, - render_tasks: &RenderTaskGraph, + render_tasks: &mut RenderTaskGraph, deferred_resolves: &mut Vec<DeferredResolve>, prim_headers: &mut PrimitiveHeaders, transforms: &mut TransformPalette, @@ -261,6 +345,15 @@ impl RenderTarget for ColorRenderTarget { profile_scope!("alpha_task"); let task = &render_tasks[*task_id]; + match task.clear_mode { + ClearMode::One | + ClearMode::Zero => { + panic!("bug: invalid clear mode for color task"); + } + ClearMode::DontCare | + ClearMode::Transparent => {} + } + match task.kind { RenderTaskKind::Picture(ref pic_task) => { let pic = &ctx.prim_store.pictures[pic_task.pic_index.0]; @@ -276,7 +369,7 @@ impl RenderTarget for ColorRenderTarget { } }; - let target_rect = task.get_target_rect(); + let (target_rect, _) = task.get_target_rect(); let scissor_rect = if pic_task.can_merge { None @@ -284,18 +377,6 @@ impl RenderTarget for ColorRenderTarget { Some(target_rect) }; - // Typical workloads have a single or a few batch builders with a - // large number of batches (regular pictres) and a higher number - // of batch builders with only a single or two batches (for example - // rendering isolated primitives to compute their shadows). - // We can easily guess which category we are in for each picture - // by checking whether it has multiple clusters. - let prealloc_batch_count = if pic.prim_list.clusters.len() > 1 { - 128 - } else { - 0 - }; - // TODO(gw): The type names of AlphaBatchBuilder and BatchBuilder // are still confusing. Once more of the picture caching // improvement code lands, the AlphaBatchBuilder and @@ -306,9 +387,8 @@ impl RenderTarget for ColorRenderTarget { ctx.break_advanced_blend_batches, ctx.batch_lookback_count, *task_id, - (*task_id).into(), - None, - prealloc_batch_count, + render_tasks.get_task_address(*task_id), + PrimitiveVisibilityMask::all(), ); let mut batch_builder = BatchBuilder::new( @@ -351,18 +431,15 @@ impl RenderTarget for ColorRenderTarget { } } - fn texture_id(&self) -> CacheTextureId { - self.texture_id - } - fn add_task( &mut self, task_id: RenderTaskId, - _ctx: &RenderTargetContext, + ctx: &RenderTargetContext, gpu_cache: &mut GpuCache, render_tasks: &RenderTaskGraph, _: &ClipStore, _: &mut TransformPalette, + deferred_resolves: &mut Vec<DeferredResolve>, ) { profile_scope!("add_task"); let task = &render_tasks[task_id]; @@ -372,22 +449,30 @@ impl RenderTarget for ColorRenderTarget { add_blur_instances( &mut self.vertical_blurs, BlurDirection::Vertical, - task_id.into(), - task.children[0], - render_tasks, + render_tasks.get_task_address(task_id), + render_tasks.get_task_address(task.children[0]), ); } RenderTaskKind::HorizontalBlur(..) => { add_blur_instances( &mut self.horizontal_blurs, BlurDirection::Horizontal, - task_id.into(), - task.children[0], - render_tasks, + render_tasks.get_task_address(task_id), + render_tasks.get_task_address(task.children[0]), ); } - RenderTaskKind::Picture(..) => { + RenderTaskKind::Picture(ref task_info) => { + let pic = &ctx.prim_store.pictures[task_info.pic_index.0]; self.alpha_tasks.push(task_id); + + // If this pipeline is registered as a frame output + // store the information necessary to do the copy. + if let Some(pipeline_id) = pic.frame_output_pipeline_id { + self.outputs.push(FrameOutput { + pipeline_id, + task_id, + }); + } } RenderTaskKind::SvgFilter(ref task_info) => { add_svg_filter_instances( @@ -400,32 +485,69 @@ impl RenderTarget for ColorRenderTarget { task_info.extra_gpu_cache_handle.map(|handle| gpu_cache.get_address(&handle)), ) } - RenderTaskKind::Image(..) | - RenderTaskKind::Cached(..) | RenderTaskKind::ClipRegion(..) | RenderTaskKind::Border(..) | RenderTaskKind::CacheMask(..) | - RenderTaskKind::FastLinearGradient(..) | - RenderTaskKind::LinearGradient(..) | - RenderTaskKind::RadialGradient(..) | - RenderTaskKind::ConicGradient(..) | + RenderTaskKind::Gradient(..) | RenderTaskKind::LineDecoration(..) => { panic!("Should not be added to color target!"); } - RenderTaskKind::Readback(..) => {} + RenderTaskKind::Readback(device_rect) => { + self.readbacks.push(device_rect); + } RenderTaskKind::Scaling(ref info) => { add_scaling_instances( info, &mut self.scalings, task, task.children.first().map(|&child| &render_tasks[child]), + ctx.resource_cache, + gpu_cache, + deferred_resolves, ); } RenderTaskKind::Blit(ref task_info) => { + let source = match task_info.source { + BlitSource::Image { key } => { + // Get the cache item for the source texture. + let cache_item = resolve_image( + key.request, + ctx.resource_cache, + gpu_cache, + deferred_resolves, + ); + + // Work out a source rect to copy from the texture, depending on whether + // a sub-rect is present or not. + let source_rect = key.texel_rect.map_or(cache_item.uv_rect.to_i32(), |sub_rect| { + DeviceIntRect::new( + DeviceIntPoint::new( + cache_item.uv_rect.origin.x as i32 + sub_rect.origin.x, + cache_item.uv_rect.origin.y as i32 + sub_rect.origin.y, + ), + sub_rect.size, + ) + }); + + // Store the blit job for the renderer to execute, including + // the allocated destination rect within this target. + BlitJobSource::Texture( + cache_item.texture_id, + cache_item.texture_layer, + source_rect, + ) + } + BlitSource::RenderTask { task_id } => { + BlitJobSource::RenderTask(task_id) + } + }; + let target_rect = task - .get_target_rect(); + .get_target_rect() + .0 + .inner_rect(task_info.padding); self.blits.push(BlitJob { - source: task_info.source, + source, target_rect, }); } @@ -439,6 +561,14 @@ impl RenderTarget for ColorRenderTarget { !ab.opaque_batches.is_empty() }) } + + fn used_rect(&self) -> DeviceIntRect { + self.used_rect + } + + fn add_used(&mut self, rect: DeviceIntRect) { + self.used_rect = self.used_rect.union(&rect); + } } /// Contains the work (in the form of instance arrays) needed to fill an alpha @@ -450,36 +580,33 @@ impl RenderTarget for ColorRenderTarget { pub struct AlphaRenderTarget { pub clip_batcher: ClipBatcher, // List of blur operations to apply for this render target. - pub vertical_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>, - pub horizontal_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>, + pub vertical_blurs: Vec<BlurInstance>, + pub horizontal_blurs: Vec<BlurInstance>, pub scalings: FastHashMap<TextureSource, Vec<ScalingInstance>>, pub zero_clears: Vec<RenderTaskId>, pub one_clears: Vec<RenderTaskId>, - pub texture_id: CacheTextureId, + // Track the used rect of the render target, so that + // we can set a scissor rect and only clear to the + // used portion of the target as an optimization. + pub used_rect: DeviceIntRect, } impl RenderTarget for AlphaRenderTarget { fn new( - texture_id: CacheTextureId, _: DeviceIntSize, gpu_supports_fast_clears: bool, - _: DeviceIntRect, ) -> Self { AlphaRenderTarget { clip_batcher: ClipBatcher::new(gpu_supports_fast_clears), - vertical_blurs: FastHashMap::default(), - horizontal_blurs: FastHashMap::default(), + vertical_blurs: Vec::new(), + horizontal_blurs: Vec::new(), scalings: FastHashMap::default(), zero_clears: Vec::new(), one_clears: Vec::new(), - texture_id, + used_rect: DeviceIntRect::zero(), } } - fn texture_id(&self) -> CacheTextureId { - self.texture_id - } - fn add_task( &mut self, task_id: RenderTaskId, @@ -488,51 +615,55 @@ impl RenderTarget for AlphaRenderTarget { render_tasks: &RenderTaskGraph, clip_store: &ClipStore, transforms: &mut TransformPalette, + deferred_resolves: &mut Vec<DeferredResolve>, ) { profile_scope!("add_task"); let task = &render_tasks[task_id]; - let target_rect = task.get_target_rect(); + let (target_rect, _) = task.get_target_rect(); + + match task.clear_mode { + ClearMode::Zero => { + self.zero_clears.push(task_id); + } + ClearMode::One => { + self.one_clears.push(task_id); + } + ClearMode::DontCare => {} + ClearMode::Transparent => { + panic!("bug: invalid clear mode for alpha task"); + } + } match task.kind { - RenderTaskKind::Image(..) | - RenderTaskKind::Cached(..) | RenderTaskKind::Readback(..) | RenderTaskKind::Picture(..) | RenderTaskKind::Blit(..) | RenderTaskKind::Border(..) | RenderTaskKind::LineDecoration(..) | - RenderTaskKind::FastLinearGradient(..) | - RenderTaskKind::LinearGradient(..) | - RenderTaskKind::RadialGradient(..) | - RenderTaskKind::ConicGradient(..) | + RenderTaskKind::Gradient(..) | RenderTaskKind::SvgFilter(..) => { panic!("BUG: should not be added to alpha target!"); } RenderTaskKind::VerticalBlur(..) => { - self.zero_clears.push(task_id); add_blur_instances( &mut self.vertical_blurs, BlurDirection::Vertical, - task_id.into(), - task.children[0], - render_tasks, + render_tasks.get_task_address(task_id), + render_tasks.get_task_address(task.children[0]), ); } RenderTaskKind::HorizontalBlur(..) => { - self.zero_clears.push(task_id); add_blur_instances( &mut self.horizontal_blurs, BlurDirection::Horizontal, - task_id.into(), - task.children[0], - render_tasks, + render_tasks.get_task_address(task_id), + render_tasks.get_task_address(task.children[0]), ); } RenderTaskKind::CacheMask(ref task_info) => { - let clear_to_one = self.clip_batcher.add( + self.clip_batcher.add( task_info.clip_node_range, task_info.root_spatial_node_index, - render_tasks, ctx.resource_cache, gpu_cache, clip_store, @@ -542,26 +673,19 @@ impl RenderTarget for AlphaRenderTarget { task_info.actual_rect, &ctx.screen_world_rect, task_info.device_pixel_scale, - ctx.global_device_pixel_scale, target_rect.origin.to_f32(), - task_info.actual_rect.origin, + task_info.actual_rect.origin.to_f32(), ); - if task_info.clear_to_one || clear_to_one { - self.one_clears.push(task_id); - } } RenderTaskKind::ClipRegion(ref region_task) => { - if region_task.clear_to_one { - self.one_clears.push(task_id); - } let device_rect = DeviceRect::new( DevicePoint::zero(), target_rect.size.to_f32(), ); self.clip_batcher.add_clip_region( + region_task.clip_data_address, region_task.local_pos, device_rect, - region_task.clip_data.clone(), target_rect.origin.to_f32(), DevicePoint::zero(), region_task.device_pixel_scale.0, @@ -573,6 +697,9 @@ impl RenderTarget for AlphaRenderTarget { &mut self.scalings, task, task.children.first().map(|&child| &render_tasks[child]), + ctx.resource_cache, + gpu_cache, + deferred_resolves, ); } #[cfg(test)] @@ -583,6 +710,14 @@ impl RenderTarget for AlphaRenderTarget { fn needs_depth(&self) -> bool { false } + + fn used_rect(&self) -> DeviceIntRect { + self.used_rect + } + + fn add_used(&mut self, rect: DeviceIntRect) { + self.used_rect = self.used_rect.union(&rect); + } } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -599,53 +734,49 @@ pub struct PictureCacheTarget { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct TextureCacheRenderTarget { pub target_kind: RenderTargetKind, - pub horizontal_blurs: FastHashMap<TextureSource, Vec<BlurInstance>>, + pub horizontal_blurs: Vec<BlurInstance>, pub blits: Vec<BlitJob>, pub border_segments_complex: Vec<BorderInstance>, pub border_segments_solid: Vec<BorderInstance>, pub clears: Vec<DeviceIntRect>, pub line_decorations: Vec<LineDecorationJob>, - pub fast_linear_gradients: Vec<FastLinearGradientInstance>, - pub linear_gradients: Vec<LinearGradientInstance>, - pub radial_gradients: Vec<RadialGradientInstance>, - pub conic_gradients: Vec<ConicGradientInstance>, + pub gradients: Vec<GradientJob>, } impl TextureCacheRenderTarget { pub fn new(target_kind: RenderTargetKind) -> Self { TextureCacheRenderTarget { target_kind, - horizontal_blurs: FastHashMap::default(), + horizontal_blurs: vec![], blits: vec![], border_segments_complex: vec![], border_segments_solid: vec![], clears: vec![], line_decorations: vec![], - fast_linear_gradients: vec![], - linear_gradients: vec![], - radial_gradients: vec![], - conic_gradients: vec![], + gradients: vec![], } } pub fn add_task( &mut self, task_id: RenderTaskId, - render_tasks: &RenderTaskGraph, - gpu_cache: &mut GpuCache, + render_tasks: &mut RenderTaskGraph, ) { profile_scope!("add_task"); - let task_address = task_id.into(); + let task_address = render_tasks.get_task_address(task_id); + let src_task_address = render_tasks[task_id].children.get(0).map(|src_task_id| { + render_tasks.get_task_address(*src_task_id) + }); - let task = &render_tasks[task_id]; + let task = &mut render_tasks[task_id]; let target_rect = task.get_target_rect(); match task.kind { RenderTaskKind::LineDecoration(ref info) => { - self.clears.push(target_rect); + self.clears.push(target_rect.0); self.line_decorations.push(LineDecorationJob { - task_rect: target_rect.to_f32(), + task_rect: target_rect.0.to_f32(), local_size: info.local_size, style: info.style as i32, axis_select: match info.orientation { @@ -660,27 +791,31 @@ impl TextureCacheRenderTarget { &mut self.horizontal_blurs, BlurDirection::Horizontal, task_address, - task.children[0], - render_tasks, + src_task_address.unwrap(), ); } RenderTaskKind::Blit(ref task_info) => { - // Add a blit job to copy from an existing render - // task to this target. - self.blits.push(BlitJob { - source: task_info.source, - target_rect, - }); + match task_info.source { + BlitSource::Image { .. } => { + // reading/writing from the texture cache at the same time + // is undefined behavior. + panic!("bug: a single blit cannot be to/from texture cache"); + } + BlitSource::RenderTask { task_id } => { + // Add a blit job to copy from an existing render + // task to this target. + self.blits.push(BlitJob { + source: BlitJobSource::RenderTask(task_id), + target_rect: target_rect.0.inner_rect(task_info.padding), + }); + } + } } - RenderTaskKind::Border(ref task_info) => { - self.clears.push(target_rect); - - let task_origin = target_rect.origin.to_f32(); - // TODO(gw): Clone here instead of a move of this vec, since the frame - // graph is immutable by this point. It's rare that borders - // are drawn since they are persisted in the texture cache, - // but perhaps this could be improved in future. - let instances = task_info.instances.clone(); + RenderTaskKind::Border(ref mut task_info) => { + self.clears.push(target_rect.0); + + let task_origin = target_rect.0.origin.to_f32(); + let instances = mem::replace(&mut task_info.instances, Vec::new()); for mut instance in instances { // TODO(gw): It may be better to store the task origin in // the render task data instead of per instance. @@ -692,20 +827,28 @@ impl TextureCacheRenderTarget { } } } - RenderTaskKind::FastLinearGradient(ref task_info) => { - self.fast_linear_gradients.push(task_info.to_instance(&target_rect)); - } - RenderTaskKind::LinearGradient(ref task_info) => { - self.linear_gradients.push(task_info.to_instance(&target_rect, gpu_cache)); - } - RenderTaskKind::RadialGradient(ref task_info) => { - self.radial_gradients.push(task_info.to_instance(&target_rect, gpu_cache)); - } - RenderTaskKind::ConicGradient(ref task_info) => { - self.conic_gradients.push(task_info.to_instance(&target_rect, gpu_cache)); + RenderTaskKind::Gradient(ref task_info) => { + let mut stops = [0.0; 4]; + let mut colors = [PremultipliedColorF::BLACK; 4]; + + let axis_select = match task_info.orientation { + LineOrientation::Horizontal => 0.0, + LineOrientation::Vertical => 1.0, + }; + + for (stop, (offset, color)) in task_info.stops.iter().zip(stops.iter_mut().zip(colors.iter_mut())) { + *offset = stop.offset; + *color = ColorF::from(stop.color).premultiplied(); + } + + self.gradients.push(GradientJob { + task_rect: target_rect.0.to_f32(), + axis_select, + stops, + colors, + start_stop: [task_info.start_point, task_info.end_point], + }); } - RenderTaskKind::Image(..) | - RenderTaskKind::Cached(..) | RenderTaskKind::VerticalBlur(..) | RenderTaskKind::Picture(..) | RenderTaskKind::ClipRegion(..) | @@ -722,24 +865,18 @@ impl TextureCacheRenderTarget { } fn add_blur_instances( - instances: &mut FastHashMap<TextureSource, Vec<BlurInstance>>, + instances: &mut Vec<BlurInstance>, blur_direction: BlurDirection, task_address: RenderTaskAddress, - src_task_id: RenderTaskId, - render_tasks: &RenderTaskGraph, + src_task_address: RenderTaskAddress, ) { - let source = render_tasks[src_task_id].get_texture_source(); - let instance = BlurInstance { task_address, - src_task_address: src_task_id.into(), + src_task_address, blur_direction, }; - instances - .entry(source) - .or_insert(Vec::new()) - .push(instance); + instances.push(instance); } fn add_scaling_instances( @@ -747,15 +884,55 @@ fn add_scaling_instances( instances: &mut FastHashMap<TextureSource, Vec<ScalingInstance>>, target_task: &RenderTask, source_task: Option<&RenderTask>, + resource_cache: &ResourceCache, + gpu_cache: &mut GpuCache, + deferred_resolves: &mut Vec<DeferredResolve>, ) { let target_rect = target_task .get_target_rect() + .0 .inner_rect(task.padding) .to_f32(); - let source = source_task.unwrap().get_texture_source(); + let (source, (source_rect, source_layer)) = match task.image { + Some(key) => { + assert!(source_task.is_none()); + + // Get the cache item for the source texture. + let cache_item = resolve_image( + key.request, + resource_cache, + gpu_cache, + deferred_resolves, + ); + + // Work out a source rect to copy from the texture, depending on whether + // a sub-rect is present or not. + let source_rect = key.texel_rect.map_or(cache_item.uv_rect, |sub_rect| { + DeviceIntRect::new( + DeviceIntPoint::new( + cache_item.uv_rect.origin.x + sub_rect.origin.x, + cache_item.uv_rect.origin.y + sub_rect.origin.y, + ), + sub_rect.size, + ) + }); - let source_rect = source_task.unwrap().get_target_rect().to_f32(); + ( + cache_item.texture_id, + (source_rect, cache_item.texture_layer as LayerIndex), + ) + } + None => { + ( + match task.target_kind { + RenderTargetKind::Color => TextureSource::PrevPassColor, + RenderTargetKind::Alpha => TextureSource::PrevPassAlpha, + }, + source_task.unwrap().location.to_source_rect(), + ) + } + }; instances .entry(source) @@ -763,6 +940,7 @@ fn add_scaling_instances( .push(ScalingInstance { target_rect, source_rect, + source_layer: source_layer as i32, }); } @@ -775,14 +953,20 @@ fn add_svg_filter_instances( input_2_task: Option<RenderTaskId>, extra_data_address: Option<GpuCacheAddress>, ) { - let mut textures = BatchTextures::empty(); + let mut textures = BatchTextures::no_texture(); - if let Some(id) = input_1_task { - textures.input.colors[0] = render_tasks[id].get_texture_source(); + if let Some(saved_index) = input_1_task.map(|id| &render_tasks[id].saved_index) { + textures.colors[0] = match saved_index { + Some(saved_index) => TextureSource::RenderTaskCache(*saved_index, Swizzle::default()), + None => TextureSource::PrevPassColor, + }; } - if let Some(id) = input_2_task { - textures.input.colors[1] = render_tasks[id].get_texture_source(); + if let Some(saved_index) = input_2_task.map(|id| &render_tasks[id].saved_index) { + textures.colors[1] = match saved_index { + Some(saved_index) => TextureSource::RenderTaskCache(*saved_index, Swizzle::default()), + None => TextureSource::PrevPassColor, + }; } let kind = match filter { @@ -836,9 +1020,9 @@ fn add_svg_filter_instances( }; let instance = SvgFilterInstance { - task_address: task_id.into(), - input_1_task_address: input_1_task.map(|id| id.into()).unwrap_or(RenderTaskAddress(0)), - input_2_task_address: input_2_task.map(|id| id.into()).unwrap_or(RenderTaskAddress(0)), + task_address: render_tasks.get_task_address(task_id), + input_1_task_address: input_1_task.map(|id| render_tasks.get_task_address(id)).unwrap_or(RenderTaskAddress(0)), + input_2_task_address: input_2_task.map(|id| render_tasks.get_task_address(id)).unwrap_or(RenderTaskAddress(0)), kind, input_count, generic_int, @@ -857,17 +1041,25 @@ fn add_svg_filter_instances( instances.push((textures, vec![instance])); } +// Defines where the source data for a blit job can be found. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum BlitJobSource { + Texture(TextureSource, i32, DeviceIntRect), + RenderTask(RenderTaskId), +} + // Information required to do a blit from a source to a target. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct BlitJob { - pub source: RenderTaskId, + pub source: BlitJobSource, pub target_rect: DeviceIntRect, } #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct LineDecorationJob { pub task_rect: DeviceRect, pub local_size: LayoutSize, @@ -875,3 +1067,25 @@ pub struct LineDecorationJob { pub style: i32, pub axis_select: f32, } + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[repr(C)] +pub struct GradientJob { + pub task_rect: DeviceRect, + pub stops: [f32; GRADIENT_FP_STOPS], + pub colors: [PremultipliedColorF; GRADIENT_FP_STOPS], + pub axis_select: f32, + pub start_stop: [f32; 2], +} + +/// Frame output information for a given pipeline ID. +/// Storing the task ID allows the renderer to find +/// the target rect within the render target that this +/// pipeline exists at. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct FrameOutput { + pub task_id: RenderTaskId, + pub pipeline_id: PipelineId, +} diff --git a/third_party/webrender/webrender/src/render_task.rs b/third_party/webrender/webrender/src/render_task.rs index ad8bdbe4975..acfae0ebae6 100644 --- a/third_party/webrender/webrender/src/render_task.rs +++ b/third_party/webrender/webrender/src/render_task.rs @@ -5,35 +5,34 @@ use api::{CompositeOperator, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind}; use api::{LineStyle, LineOrientation, ClipMode, MixBlendMode, ColorF, ColorSpace}; use api::units::*; -use crate::batch::BatchFilter; -use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange}; +use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange, ClipNodeFlags}; use crate::spatial_tree::SpatialNodeIndex; use crate::filterdata::SFilterData; use crate::frame_builder::FrameBuilderConfig; use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use crate::gpu_types::{BorderInstance, ImageSource, UvRectKind}; -use crate::internal_types::{CacheTextureId, FastHashMap, TextureSource, Swizzle}; -use crate::picture::{ResolvedSurfaceTexture, SurfaceInfo}; -use crate::prim_store::{ClipData, PictureIndex}; -use crate::prim_store::gradient::{ - FastLinearGradientTask, RadialGradientTask, - ConicGradientTask, LinearGradientTask, -}; -use crate::resource_cache::{ResourceCache, ImageRequest}; +use crate::internal_types::{CacheTextureId, FastHashMap, LayerIndex, SavedTargetIndex}; +use crate::picture::ResolvedSurfaceTexture; +use crate::prim_store::{PictureIndex, PrimitiveVisibilityMask}; +use crate::prim_store::image::ImageCacheKey; +use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientStopKey}; +#[cfg(feature = "debugger")] +use crate::print_tree::{PrintTreePrinter}; +use crate::resource_cache::ResourceCache; use std::{usize, f32, i32, u32}; -use crate::render_target::RenderTargetKind; -use crate::render_task_graph::{PassId, RenderTaskId, RenderTaskGraphBuilder}; -use crate::render_task_cache::{RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent}; +use crate::render_target::{RenderTargetIndex, RenderTargetKind}; +use crate::render_task_graph::{RenderTaskGraph, RenderTaskId}; +use crate::render_task_cache::{RenderTaskCacheKey, RenderTaskCacheKeyKind}; use smallvec::SmallVec; +const RENDER_TASK_SIZE_SANITY_CHECK: i32 = 16000; const FLOATS_PER_RENDER_TASK_INFO: usize = 8; -pub const MAX_RENDER_TASK_SIZE: i32 = 16384; pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0; pub const MIN_DOWNSCALING_RT_SIZE: i32 = 8; fn render_task_sanity_check(size: &DeviceIntSize) { - if size.width > MAX_RENDER_TASK_SIZE || - size.height > MAX_RENDER_TASK_SIZE { + if size.width > RENDER_TASK_SIZE_SANITY_CHECK || + size.height > RENDER_TASK_SIZE_SANITY_CHECK { error!("Attempting to create a render task of size {}x{}", size.width, size.height); panic!(); } @@ -45,122 +44,93 @@ fn render_task_sanity_check(size: &DeviceIntSize) { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct RenderTaskAddress(pub u16); -impl Into<RenderTaskAddress> for RenderTaskId { - fn into(self) -> RenderTaskAddress { - RenderTaskAddress(self.index as u16) - } -} - -/// A render task location that targets a persistent output buffer which -/// will be retained over multiple frames. -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +/// Identifies the output buffer location for a given `RenderTask`. +#[derive(Clone, Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum StaticRenderTaskSurface { +pub enum RenderTaskLocation { + /// The `RenderTask` should be drawn to a fixed region in a specific render + /// target. This is used for the root `RenderTask`, where the main + /// framebuffer is used as the render target. + Fixed(DeviceIntRect), + /// The `RenderTask` should be drawn to a target provided by the atlas + /// allocator. This is the most common case. + /// + /// The second member specifies the width and height of the task + /// output, and the first member is initially left as `None`. During the + /// build phase, we invoke `RenderTargetList::alloc()` and store the + /// resulting location in the first member. That location identifies the + /// render target and the offset of the allocated region within that target. + Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize), /// The output of the `RenderTask` will be persisted beyond this frame, and /// thus should be drawn into the `TextureCache`. TextureCache { /// Which texture in the texture cache should be drawn into. texture: CacheTextureId, - /// What format this texture cache surface is - target_kind: RenderTargetKind, - }, - /// Only used as a source for render tasks, can be any texture including an - /// external one. - ReadOnly { - source: TextureSource, + /// The target layer in the above texture. + layer: LayerIndex, + /// The target region within the above layer. + rect: DeviceIntRect, + }, /// This render task will be drawn to a picture cache texture that is /// persisted between both frames and scenes, if the content remains valid. PictureCache { /// Describes either a WR texture or a native OS compositor target surface: ResolvedSurfaceTexture, - }, -} - -/// Identifies the output buffer location for a given `RenderTask`. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum RenderTaskLocation { - // Towards the beginning of the frame, most task locations are typically not - // known yet, in which case they are set to one of the following variants: - - /// A dynamic task that has not yet been allocated a texture and rect. - Unallocated { - /// Requested size of this render task - size: DeviceIntSize, - }, - /// Will be replaced by a Static location after the texture cache update. - CacheRequest { + /// Size in device pixels of this picture cache tile. size: DeviceIntSize, }, - - // Before batching begins, we expect that locations have been resolved to - // one of the following variants: - - /// The `RenderTask` should be drawn to a target provided by the atlas - /// allocator. This is the most common case. - Dynamic { - /// Texture that this task was allocated to render on - texture_id: CacheTextureId, - /// Rectangle in the texture this task occupies - rect: DeviceIntRect, - }, - /// A task that is output to a persistent / retained target. - Static { - /// Target to draw to - surface: StaticRenderTaskSurface, - /// Rectangle in the texture this task occupies - rect: DeviceIntRect, - }, } impl RenderTaskLocation { /// Returns true if this is a dynamic location. pub fn is_dynamic(&self) -> bool { match *self { - RenderTaskLocation::Dynamic { .. } => true, + RenderTaskLocation::Dynamic(..) => true, _ => false, } } pub fn size(&self) -> DeviceIntSize { match self { - RenderTaskLocation::Unallocated { size } => *size, - RenderTaskLocation::Dynamic { rect, .. } => rect.size, - RenderTaskLocation::Static { rect, .. } => rect.size, - RenderTaskLocation::CacheRequest { size } => *size, + RenderTaskLocation::Fixed(rect) => rect.size, + RenderTaskLocation::Dynamic(_, size) => *size, + RenderTaskLocation::TextureCache { rect, .. } => rect.size, + RenderTaskLocation::PictureCache { size, .. } => *size, } } -} -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct CachedTask { - pub target_kind: RenderTargetKind, + pub fn to_source_rect(&self) -> (DeviceIntRect, LayerIndex) { + match *self { + RenderTaskLocation::Fixed(rect) => (rect, 0), + RenderTaskLocation::Dynamic(None, _) => panic!("Expected position to be set for the task!"), + RenderTaskLocation::Dynamic(Some((origin, layer)), size) => (DeviceIntRect::new(origin, size), layer.0 as LayerIndex), + RenderTaskLocation::TextureCache { rect, layer, .. } => (rect, layer), + RenderTaskLocation::PictureCache { .. } => { + panic!("bug: picture cache tasks should never be a source!"); + } + } + } } #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CacheMaskTask { - pub actual_rect: DeviceRect, + pub actual_rect: DeviceIntRect, pub root_spatial_node_index: SpatialNodeIndex, pub clip_node_range: ClipNodeRange, pub device_pixel_scale: DevicePixelScale, - pub clear_to_one: bool, } #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ClipRegionTask { + pub clip_data_address: GpuCacheAddress, pub local_pos: LayoutPoint, pub device_pixel_scale: DevicePixelScale, - pub clip_data: ClipData, - pub clear_to_one: bool, } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -168,10 +138,14 @@ pub struct ClipRegionTask { pub struct PictureTask { pub pic_index: PictureIndex, pub can_merge: bool, - pub content_origin: DevicePoint, + pub content_origin: DeviceIntPoint, + pub uv_rect_handle: GpuCacheHandle, pub surface_spatial_node_index: SpatialNodeIndex, + uv_rect_kind: UvRectKind, pub device_pixel_scale: DevicePixelScale, - pub batch_filter: Option<BatchFilter>, + /// A bitfield that describes which dirty regions should be included + /// in batches built for this picture task. + pub vis_mask: PrimitiveVisibilityMask, pub scissor_rect: Option<DeviceIntRect>, pub valid_rect: Option<DeviceIntRect>, } @@ -182,27 +156,16 @@ pub struct PictureTask { pub struct BlurTask { pub blur_std_deviation: f32, pub target_kind: RenderTargetKind, + pub uv_rect_handle: GpuCacheHandle, pub blur_region: DeviceIntSize, + uv_rect_kind: UvRectKind, } impl BlurTask { - // In order to do the blur down-scaling passes without introducing errors, we need the - // source of each down-scale pass to be a multuple of two. If need be, this inflates - // the source size so that each down-scale pass will sample correctly. - pub fn adjusted_blur_source_size(original_size: DeviceSize, mut std_dev: DeviceSize) -> DeviceSize { - let mut adjusted_size = original_size; - let mut scale_factor = 1.0; - while std_dev.width > MAX_BLUR_STD_DEVIATION && std_dev.height > MAX_BLUR_STD_DEVIATION { - if adjusted_size.width < MIN_DOWNSCALING_RT_SIZE as f32 || - adjusted_size.height < MIN_DOWNSCALING_RT_SIZE as f32 { - break; - } - std_dev = std_dev * 0.5; - scale_factor *= 2.0; - adjusted_size = (original_size.to_f32() / scale_factor).ceil(); - } - - adjusted_size * scale_factor + #[cfg(feature = "debugger")] + fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) { + pt.add_item(format!("std deviation: {}", self.blur_std_deviation)); + pt.add_item(format!("target: {:?}", self.target_kind)); } } @@ -211,9 +174,24 @@ impl BlurTask { #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct ScalingTask { pub target_kind: RenderTargetKind, + pub image: Option<ImageCacheKey>, + uv_rect_kind: UvRectKind, pub padding: DeviceIntSideOffsets, } +// Where the source data for a blit task can be found. +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum BlitSource { + Image { + key: ImageCacheKey, + }, + RenderTask { + task_id: RenderTaskId, + }, +} + #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] @@ -225,7 +203,18 @@ pub struct BorderTask { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct BlitTask { - pub source: RenderTaskId, + pub source: BlitSource, + pub padding: DeviceIntSideOffsets, +} + +#[derive(Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct GradientTask { + pub stops: [GradientStopKey; GRADIENT_FP_STOPS], + pub orientation: LineOrientation, + pub start_point: f32, + pub end_point: f32, } #[derive(Debug)] @@ -262,16 +251,8 @@ pub enum SvgFilterInfo { pub struct SvgFilterTask { pub info: SvgFilterInfo, pub extra_gpu_cache_handle: Option<GpuCacheHandle>, -} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct ReadbackTask { - // The offset of the rect that needs to be read back, in the - // device space of the surface that will be read back from. - // If this is None, there is no readback surface available - // and this is a dummy (empty) readback. - pub readback_origin: Option<DevicePoint>, + pub uv_rect_handle: GpuCacheHandle, + uv_rect_kind: UvRectKind, } #[derive(Debug)] @@ -284,40 +265,25 @@ pub struct RenderTaskData { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub enum RenderTaskKind { - Image(ImageRequest), - Cached(CachedTask), Picture(PictureTask), CacheMask(CacheMaskTask), ClipRegion(ClipRegionTask), VerticalBlur(BlurTask), HorizontalBlur(BlurTask), - Readback(ReadbackTask), + Readback(DeviceIntRect), Scaling(ScalingTask), Blit(BlitTask), Border(BorderTask), LineDecoration(LineDecorationTask), - FastLinearGradient(FastLinearGradientTask), - LinearGradient(LinearGradientTask), - RadialGradient(RadialGradientTask), - ConicGradient(ConicGradientTask), + Gradient(GradientTask), SvgFilter(SvgFilterTask), #[cfg(test)] Test(RenderTargetKind), } impl RenderTaskKind { - pub fn is_a_rendering_operation(&self) -> bool { - match self { - &RenderTaskKind::Image(..) => false, - &RenderTaskKind::Cached(..) => false, - _ => true, - } - } - pub fn as_str(&self) -> &'static str { match *self { - RenderTaskKind::Image(..) => "Image", - RenderTaskKind::Cached(..) => "Cached", RenderTaskKind::Picture(..) => "Picture", RenderTaskKind::CacheMask(..) => "CacheMask", RenderTaskKind::ClipRegion(..) => "ClipRegion", @@ -328,141 +294,241 @@ impl RenderTaskKind { RenderTaskKind::Blit(..) => "Blit", RenderTaskKind::Border(..) => "Border", RenderTaskKind::LineDecoration(..) => "LineDecoration", - RenderTaskKind::FastLinearGradient(..) => "FastLinearGradient", - RenderTaskKind::LinearGradient(..) => "LinearGradient", - RenderTaskKind::RadialGradient(..) => "RadialGradient", - RenderTaskKind::ConicGradient(..) => "ConicGradient", + RenderTaskKind::Gradient(..) => "Gradient", RenderTaskKind::SvgFilter(..) => "SvgFilter", #[cfg(test)] RenderTaskKind::Test(..) => "Test", } } +} - pub fn target_kind(&self) -> RenderTargetKind { - match *self { - RenderTaskKind::Image(..) | - RenderTaskKind::LineDecoration(..) | - RenderTaskKind::Readback(..) | - RenderTaskKind::Border(..) | - RenderTaskKind::FastLinearGradient(..) | - RenderTaskKind::LinearGradient(..) | - RenderTaskKind::RadialGradient(..) | - RenderTaskKind::ConicGradient(..) | - RenderTaskKind::Picture(..) | - RenderTaskKind::Blit(..) | - RenderTaskKind::SvgFilter(..) => { - RenderTargetKind::Color - } +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum ClearMode { + // Applicable to color and alpha targets. + Zero, + One, + /// This task doesn't care what it is cleared to - it will completely overwrite it. + DontCare, + + // Applicable to color targets only. + Transparent, +} - RenderTaskKind::ClipRegion(..) | - RenderTaskKind::CacheMask(..) => { - RenderTargetKind::Alpha - } +/// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs, +/// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive +/// in a single frame. +pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>; - RenderTaskKind::VerticalBlur(ref task_info) | - RenderTaskKind::HorizontalBlur(ref task_info) => { - task_info.target_kind - } +/// Since we only use it within a single primitive, the key only needs to contain the down-scaling level +/// and the blur std deviation. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum BlurTaskKey { + DownScale(u32), + Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 }, +} - RenderTaskKind::Scaling(ref task_info) => { - task_info.target_kind - } +impl BlurTaskKey { + fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self { + // Quantise the std deviations and store it as integers to work around + // Eq and Hash's f32 allergy. + // The blur radius is rounded before RenderTask::new_blur so we don't need + // a lot of precision. + const QUANTIZATION_FACTOR: f32 = 1024.0; + let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32; + let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32; + BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y } + } +} - RenderTaskKind::Cached(ref task_info) => { - task_info.target_kind - } +// The majority of render tasks have 0, 1 or 2 dependencies, except for pictures that +// typically have dozens to hundreds of dependencies. SmallVec with 2 inline elements +// avoids many tiny heap allocations in pages with a lot of text shadows and other +// types of render tasks. +pub type TaskDependencies = SmallVec<[RenderTaskId;2]>; - #[cfg(test)] - RenderTaskKind::Test(kind) => kind, +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct RenderTask { + pub location: RenderTaskLocation, + pub children: TaskDependencies, + pub kind: RenderTaskKind, + pub clear_mode: ClearMode, + pub saved_index: Option<SavedTargetIndex>, +} + +impl RenderTask { + #[inline] + pub fn with_dynamic_location( + size: DeviceIntSize, + children: TaskDependencies, + kind: RenderTaskKind, + clear_mode: ClearMode, + ) -> Self { + render_task_sanity_check(&size); + + RenderTask { + location: RenderTaskLocation::Dynamic(None, size), + children, + kind, + clear_mode, + saved_index: None, + } + } + + #[cfg(test)] + pub fn new_test( + target: RenderTargetKind, + location: RenderTaskLocation, + children: TaskDependencies, + ) -> Self { + RenderTask { + location, + children, + kind: RenderTaskKind::Test(target), + clear_mode: ClearMode::Transparent, + saved_index: None, } } pub fn new_picture( - size: DeviceIntSize, + location: RenderTaskLocation, unclipped_size: DeviceSize, pic_index: PictureIndex, - content_origin: DevicePoint, + content_origin: DeviceIntPoint, + uv_rect_kind: UvRectKind, surface_spatial_node_index: SpatialNodeIndex, device_pixel_scale: DevicePixelScale, - batch_filter: Option<BatchFilter>, + vis_mask: PrimitiveVisibilityMask, scissor_rect: Option<DeviceIntRect>, valid_rect: Option<DeviceIntRect>, ) -> Self { + let size = match location { + RenderTaskLocation::Dynamic(_, size) => size, + RenderTaskLocation::Fixed(rect) => rect.size, + RenderTaskLocation::TextureCache { rect, .. } => rect.size, + RenderTaskLocation::PictureCache { size, .. } => size, + }; + render_task_sanity_check(&size); let can_merge = size.width as f32 >= unclipped_size.width && size.height as f32 >= unclipped_size.height; - RenderTaskKind::Picture(PictureTask { - pic_index, - content_origin, - can_merge, - surface_spatial_node_index, - device_pixel_scale, - batch_filter, - scissor_rect, - valid_rect, - }) + RenderTask { + location, + children: TaskDependencies::new(), + kind: RenderTaskKind::Picture(PictureTask { + pic_index, + content_origin, + can_merge, + uv_rect_handle: GpuCacheHandle::new(), + uv_rect_kind, + surface_spatial_node_index, + device_pixel_scale, + vis_mask, + scissor_rect, + valid_rect, + }), + clear_mode: ClearMode::Transparent, + saved_index: None, + } } - pub fn new_readback( - readback_origin: Option<DevicePoint>, + pub fn new_gradient( + size: DeviceIntSize, + stops: [GradientStopKey; GRADIENT_FP_STOPS], + orientation: LineOrientation, + start_point: f32, + end_point: f32, ) -> Self { - RenderTaskKind::Readback( - ReadbackTask { - readback_origin, - } + RenderTask::with_dynamic_location( + size, + TaskDependencies::new(), + RenderTaskKind::Gradient(GradientTask { + stops, + orientation, + start_point, + end_point, + }), + ClearMode::DontCare, ) } - pub fn new_line_decoration( - style: LineStyle, - orientation: LineOrientation, - wavy_line_thickness: f32, - local_size: LayoutSize, + pub fn new_readback(screen_rect: DeviceIntRect) -> Self { + RenderTask::with_dynamic_location( + screen_rect.size, + TaskDependencies::new(), + RenderTaskKind::Readback(screen_rect), + ClearMode::Transparent, + ) + } + + pub fn new_blit( + size: DeviceIntSize, + source: BlitSource, ) -> Self { - RenderTaskKind::LineDecoration(LineDecorationTask { - style, - orientation, - wavy_line_thickness, - local_size, - }) + RenderTask::new_blit_with_padding(size, DeviceIntSideOffsets::zero(), source) } - pub fn new_border_segment( - instances: Vec<BorderInstance>, + pub fn new_blit_with_padding( + padded_size: DeviceIntSize, + padding: DeviceIntSideOffsets, + source: BlitSource, ) -> Self { - RenderTaskKind::Border(BorderTask { - instances, - }) + // If this blit uses a render task as a source, + // ensure it's added as a child task. This will + // ensure it gets allocated in the correct pass + // and made available as an input when this task + // executes. + let children = match source { + BlitSource::RenderTask { task_id } => smallvec![task_id], + BlitSource::Image { .. } => smallvec![], + }; + + RenderTask::with_dynamic_location( + padded_size, + children, + RenderTaskKind::Blit(BlitTask { + source, + padding, + }), + ClearMode::Transparent, + ) } - pub fn new_rounded_rect_mask( - local_pos: LayoutPoint, - clip_data: ClipData, - device_pixel_scale: DevicePixelScale, - fb_config: &FrameBuilderConfig, + pub fn new_line_decoration( + size: DeviceIntSize, + style: LineStyle, + orientation: LineOrientation, + wavy_line_thickness: f32, + local_size: LayoutSize, ) -> Self { - RenderTaskKind::ClipRegion(ClipRegionTask { - local_pos, - device_pixel_scale, - clip_data, - clear_to_one: fb_config.gpu_supports_fast_clears, - }) + RenderTask::with_dynamic_location( + size, + TaskDependencies::new(), + RenderTaskKind::LineDecoration(LineDecorationTask { + style, + orientation, + wavy_line_thickness, + local_size, + }), + ClearMode::Transparent, + ) } pub fn new_mask( - outer_rect: DeviceRect, + outer_rect: DeviceIntRect, clip_node_range: ClipNodeRange, root_spatial_node_index: SpatialNodeIndex, clip_store: &mut ClipStore, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, - rg_builder: &mut RenderTaskGraphBuilder, + render_tasks: &mut RenderTaskGraph, clip_data_store: &mut ClipDataStore, device_pixel_scale: DevicePixelScale, fb_config: &FrameBuilderConfig, - surfaces: &[SurfaceInfo], ) -> RenderTaskId { // Step through the clip sources that make up this mask. If we find // any box-shadow clip sources, request that image from the render @@ -473,24 +539,7 @@ impl RenderTaskKind { // TODO(gw): If this ever shows up in a profile, we could pre-calculate // whether a ClipSources contains any box-shadows and skip // this iteration for the majority of cases. - let task_size = outer_rect.size.to_i32(); - - // If we have a potentially tiled clip mask, clear the mask area first. Otherwise, - // the first (primary) clip mask will overwrite all the clip mask pixels with - // blending disabled to set to the initial value. - - let clip_task_id = rg_builder.add().init( - RenderTask::new_dynamic( - task_size, - RenderTaskKind::CacheMask(CacheMaskTask { - actual_rect: outer_rect, - clip_node_range, - root_spatial_node_index, - device_pixel_scale, - clear_to_one: fb_config.gpu_supports_fast_clears, - }), - ) - ); + let mut needs_clear = fb_config.gpu_supports_fast_clears; for i in 0 .. clip_node_range.count { let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i); @@ -502,342 +551,123 @@ impl RenderTaskKind { .expect("bug: no cache key set") .clone(); let blur_radius_dp = cache_key.blur_radius_dp as f32; - let device_pixel_scale = DevicePixelScale::new(cache_key.device_pixel_scale.to_f32_px()); + let clip_data_address = gpu_cache.get_address(&source.clip_data_handle); // Request a cacheable render task with a blurred, minimal // sized box-shadow rect. - source.render_task = Some(resource_cache.request_render_task( + source.cache_handle = Some(resource_cache.request_render_task( RenderTaskCacheKey { size: cache_size, kind: RenderTaskCacheKeyKind::BoxShadow(cache_key), }, gpu_cache, - rg_builder, + render_tasks, None, false, - RenderTaskParent::RenderTask(clip_task_id), - surfaces, - |rg_builder| { - let clip_data = ClipData::rounded_rect( - source.minimal_shadow_rect.size, - &source.shadow_radius, - ClipMode::Clip, - ); - + |render_tasks| { // Draw the rounded rect. - let mask_task_id = rg_builder.add().init(RenderTask::new_dynamic( + let mask_task_id = render_tasks.add().init(RenderTask::new_rounded_rect_mask( cache_size, - RenderTaskKind::new_rounded_rect_mask( - source.minimal_shadow_rect.origin, - clip_data, - device_pixel_scale, - fb_config, - ), + clip_data_address, + source.minimal_shadow_rect.origin, + device_pixel_scale, + fb_config, )); // Blur it RenderTask::new_blur( DeviceSize::new(blur_radius_dp, blur_radius_dp), mask_task_id, - rg_builder, + render_tasks, RenderTargetKind::Alpha, + ClearMode::Zero, None, cache_size, ) } )); } - ClipItemKind::Rectangle { .. } | + ClipItemKind::Rectangle { mode: ClipMode::Clip, .. } => { + if !clip_instance.flags.contains(ClipNodeFlags::SAME_COORD_SYSTEM) { + // This is conservative - it's only the case that we actually need + // a clear here if we end up adding this mask via add_tiled_clip_mask, + // but for simplicity we will just clear if any of these are encountered, + // since they are rare. + needs_clear = true; + } + } + ClipItemKind::Rectangle { mode: ClipMode::ClipOut, .. } | ClipItemKind::RoundedRectangle { .. } | ClipItemKind::Image { .. } => {} } } - clip_task_id - } - - // Write (up to) 8 floats of data specific to the type - // of render task that is provided to the GPU shaders - // via a vertex texture. - pub fn write_task_data( - &self, - target_rect: DeviceIntRect, - ) -> RenderTaskData { - // NOTE: The ordering and layout of these structures are - // required to match both the GPU structures declared - // in prim_shared.glsl, and also the uses in submit_batch() - // in renderer.rs. - // TODO(gw): Maybe there's a way to make this stuff a bit - // more type-safe. Although, it will always need - // to be kept in sync with the GLSL code anyway. - - let data = match self { - RenderTaskKind::Picture(ref task) => { - // Note: has to match `PICTURE_TYPE_*` in shaders - [ - task.device_pixel_scale.0, - task.content_origin.x, - task.content_origin.y, - 0.0, - ] - } - RenderTaskKind::CacheMask(ref task) => { - [ - task.device_pixel_scale.0, - task.actual_rect.origin.x, - task.actual_rect.origin.y, - 0.0, - ] - } - RenderTaskKind::ClipRegion(ref task) => { - [ - task.device_pixel_scale.0, - 0.0, - 0.0, - 0.0, - ] - } - RenderTaskKind::VerticalBlur(ref task) | - RenderTaskKind::HorizontalBlur(ref task) => { - [ - task.blur_std_deviation, - task.blur_region.width as f32, - task.blur_region.height as f32, - 0.0, - ] - } - RenderTaskKind::Image(..) | - RenderTaskKind::Cached(..) | - RenderTaskKind::Readback(..) | - RenderTaskKind::Scaling(..) | - RenderTaskKind::Border(..) | - RenderTaskKind::LineDecoration(..) | - RenderTaskKind::FastLinearGradient(..) | - RenderTaskKind::LinearGradient(..) | - RenderTaskKind::RadialGradient(..) | - RenderTaskKind::ConicGradient(..) | - RenderTaskKind::Blit(..) => { - [0.0; 4] - } - - - RenderTaskKind::SvgFilter(ref task) => { - match task.info { - SvgFilterInfo::Opacity(opacity) => [opacity, 0.0, 0.0, 0.0], - SvgFilterInfo::Offset(offset) => [offset.x, offset.y, 0.0, 0.0], - _ => [0.0; 4] - } - } - - #[cfg(test)] - RenderTaskKind::Test(..) => { - [0.0; 4] - } + // If we have a potentially tiled clip mask, clear the mask area first. Otherwise, + // the first (primary) clip mask will overwrite all the clip mask pixels with + // blending disabled to set to the initial value. + let clear_mode = if needs_clear { + ClearMode::One + } else { + ClearMode::DontCare }; - RenderTaskData { - data: [ - target_rect.origin.x as f32, - target_rect.origin.y as f32, - target_rect.size.width as f32, - target_rect.size.height as f32, - data[0], - data[1], - data[2], - data[3], - ] - } - } - - pub fn write_gpu_blocks( - &mut self, - gpu_cache: &mut GpuCache, - ) { - if let RenderTaskKind::SvgFilter(ref mut filter_task) = self { - match filter_task.info { - SvgFilterInfo::ColorMatrix(ref matrix) => { - let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new); - if let Some(mut request) = gpu_cache.request(handle) { - for i in 0..5 { - request.push([matrix[i*4], matrix[i*4+1], matrix[i*4+2], matrix[i*4+3]]); - } - } - } - SvgFilterInfo::DropShadow(color) | - SvgFilterInfo::Flood(color) => { - let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new); - if let Some(mut request) = gpu_cache.request(handle) { - request.push(color.to_array()); - } - } - SvgFilterInfo::ComponentTransfer(ref data) => { - let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new); - if let Some(request) = gpu_cache.request(handle) { - data.update(request); - } - } - SvgFilterInfo::Composite(ref operator) => { - if let CompositeOperator::Arithmetic(k_vals) = operator { - let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new); - if let Some(mut request) = gpu_cache.request(handle) { - request.push(*k_vals); - } - } - } - _ => {}, - } - } - } -} - -/// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs, -/// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive -/// in a single frame. -pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>; - -/// Since we only use it within a single primitive, the key only needs to contain the down-scaling level -/// and the blur std deviation. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum BlurTaskKey { - DownScale(u32), - Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 }, -} - -impl BlurTaskKey { - fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self { - // Quantise the std deviations and store it as integers to work around - // Eq and Hash's f32 allergy. - // The blur radius is rounded before RenderTask::new_blur so we don't need - // a lot of precision. - const QUANTIZATION_FACTOR: f32 = 1024.0; - let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32; - let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32; - BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y } - } -} - -// The majority of render tasks have 0, 1 or 2 dependencies, except for pictures that -// typically have dozens to hundreds of dependencies. SmallVec with 2 inline elements -// avoids many tiny heap allocations in pages with a lot of text shadows and other -// types of render tasks. -pub type TaskDependencies = SmallVec<[RenderTaskId;2]>; - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct RenderTask { - pub location: RenderTaskLocation, - pub children: TaskDependencies, - pub kind: RenderTaskKind, - - // TODO(gw): These fields and perhaps others can become private once the - // frame_graph / render_task source files are unified / cleaned up. - pub free_after: PassId, - pub render_on: PassId, - - /// The gpu cache handle for the render task's destination rect. - /// - /// Will be set to None if the render task is cached, in which case the texture cache - /// manages the handle. - pub uv_rect_handle: GpuCacheHandle, - pub cache_handle: Option<RenderTaskCacheEntryHandle>, - uv_rect_kind: UvRectKind, -} - -impl RenderTask { - pub fn new( - location: RenderTaskLocation, - kind: RenderTaskKind, - ) -> Self { - render_task_sanity_check(&location.size()); - - RenderTask { - location, - children: TaskDependencies::new(), - kind, - free_after: PassId::MAX, - render_on: PassId::MIN, - uv_rect_handle: GpuCacheHandle::new(), - uv_rect_kind: UvRectKind::Rect, - cache_handle: None, - } - } - - pub fn new_dynamic( - size: DeviceIntSize, - kind: RenderTaskKind, - ) -> Self { - RenderTask::new( - RenderTaskLocation::Unallocated { size }, - kind, + render_tasks.add().init( + RenderTask::with_dynamic_location( + outer_rect.size, + smallvec![], + RenderTaskKind::CacheMask(CacheMaskTask { + actual_rect: outer_rect, + clip_node_range, + root_spatial_node_index, + device_pixel_scale, + }), + clear_mode, + ) ) } - pub fn with_uv_rect_kind(mut self, uv_rect_kind: UvRectKind) -> Self { - self.uv_rect_kind = uv_rect_kind; - self - } - - pub fn new_image( + pub fn new_rounded_rect_mask( size: DeviceIntSize, - request: ImageRequest, + clip_data_address: GpuCacheAddress, + local_pos: LayoutPoint, + device_pixel_scale: DevicePixelScale, + fb_config: &FrameBuilderConfig, ) -> Self { - // Note: this is a special constructor for image render tasks that does not - // do the render task size sanity check. This is because with SWGL we purposefully - // avoid tiling large images. There is no upload with SWGL so whatever was - // successfully allocated earlier will be what shaders read, regardless of the size - // and copying into tiles would only slow things down. - // As a result we can run into very large images being added to the frame graph - // (this is covered by a few reftests on the CI). + let clear_mode = if fb_config.gpu_supports_fast_clears { + ClearMode::One + } else { + ClearMode::DontCare + }; - RenderTask { - location: RenderTaskLocation::CacheRequest { size, }, - children: TaskDependencies::new(), - kind: RenderTaskKind::Image(request), - free_after: PassId::MAX, - render_on: PassId::MIN, - uv_rect_handle: GpuCacheHandle::new(), - uv_rect_kind: UvRectKind::Rect, - cache_handle: None, - } + RenderTask::with_dynamic_location( + size, + TaskDependencies::new(), + RenderTaskKind::ClipRegion(ClipRegionTask { + clip_data_address, + local_pos, + device_pixel_scale, + }), + clear_mode, + ) } - - #[cfg(test)] - pub fn new_test( - location: RenderTaskLocation, - target: RenderTargetKind, - ) -> Self { - RenderTask { - location, - children: TaskDependencies::new(), - kind: RenderTaskKind::Test(target), - free_after: PassId::MAX, - render_on: PassId::MIN, - uv_rect_handle: GpuCacheHandle::new(), - uv_rect_kind: UvRectKind::Rect, - cache_handle: None, + // In order to do the blur down-scaling passes without introducing errors, we need the + // source of each down-scale pass to be a multuple of two. If need be, this inflates + // the source size so that each down-scale pass will sample correctly. + pub fn adjusted_blur_source_size(original_size: DeviceSize, mut std_dev: DeviceSize) -> DeviceSize { + let mut adjusted_size = original_size; + let mut scale_factor = 1.0; + while std_dev.width > MAX_BLUR_STD_DEVIATION && std_dev.height > MAX_BLUR_STD_DEVIATION { + if adjusted_size.width < MIN_DOWNSCALING_RT_SIZE as f32 || + adjusted_size.height < MIN_DOWNSCALING_RT_SIZE as f32 { + break; + } + std_dev = std_dev * 0.5; + scale_factor *= 2.0; + adjusted_size = (original_size.to_f32() / scale_factor).ceil(); } - } - pub fn new_blit( - size: DeviceIntSize, - source: RenderTaskId, - rg_builder: &mut RenderTaskGraphBuilder, - ) -> RenderTaskId { - // If this blit uses a render task as a source, - // ensure it's added as a child task. This will - // ensure it gets allocated in the correct pass - // and made available as an input when this task - // executes. - - let blit_task_id = rg_builder.add().init(RenderTask::new_dynamic( - size, - RenderTaskKind::Blit(BlitTask { source }), - )); - - rg_builder.add_dependency(blit_task_id, source); - - blit_task_id + adjusted_size * scale_factor } // Construct a render task to apply a blur to a primitive. @@ -861,16 +691,17 @@ impl RenderTask { pub fn new_blur( blur_std_deviation: DeviceSize, src_task_id: RenderTaskId, - rg_builder: &mut RenderTaskGraphBuilder, + render_tasks: &mut RenderTaskGraph, target_kind: RenderTargetKind, + clear_mode: ClearMode, mut blur_cache: Option<&mut BlurTaskCache>, blur_region: DeviceIntSize, ) -> RenderTaskId { // Adjust large std deviation value. let mut adjusted_blur_std_deviation = blur_std_deviation; let (blur_target_size, uv_rect_kind) = { - let src_task = rg_builder.get_task(src_task_id); - (src_task.location.size(), src_task.uv_rect_kind()) + let src_task = &render_tasks[src_task_id]; + (src_task.get_dynamic_size(), src_task.uv_rect_kind()) }; let mut adjusted_blur_target_size = blur_target_size; let mut downscaling_src_task_id = src_task_id; @@ -894,7 +725,7 @@ impl RenderTask { downscaling_src_task_id = cached_task.unwrap_or_else(|| { RenderTask::new_scaling( downscaling_src_task_id, - rg_builder, + render_tasks, target_kind, adjusted_blur_target_size, ) @@ -918,27 +749,31 @@ impl RenderTask { let blur_region = blur_region / (scale_factor as i32); let blur_task_id = cached_task.unwrap_or_else(|| { - let blur_task_v = rg_builder.add().init(RenderTask::new_dynamic( + let blur_task_v = render_tasks.add().init(RenderTask::with_dynamic_location( adjusted_blur_target_size, + smallvec![downscaling_src_task_id], RenderTaskKind::VerticalBlur(BlurTask { blur_std_deviation: adjusted_blur_std_deviation.height, target_kind, + uv_rect_handle: GpuCacheHandle::new(), blur_region, + uv_rect_kind, }), - ).with_uv_rect_kind(uv_rect_kind)); - rg_builder.add_dependency(blur_task_v, downscaling_src_task_id); + clear_mode, + )); - let task_id = rg_builder.add().init(RenderTask::new_dynamic( + render_tasks.add().init(RenderTask::with_dynamic_location( adjusted_blur_target_size, + smallvec![blur_task_v], RenderTaskKind::HorizontalBlur(BlurTask { blur_std_deviation: adjusted_blur_std_deviation.width, target_kind, + uv_rect_handle: GpuCacheHandle::new(), blur_region, + uv_rect_kind, }), - ).with_uv_rect_kind(uv_rect_kind)); - rg_builder.add_dependency(task_id, blur_task_v); - - task_id + clear_mode, + )) }); if let Some(ref mut cache) = blur_cache { @@ -948,15 +783,29 @@ impl RenderTask { blur_task_id } + pub fn new_border_segment( + size: DeviceIntSize, + instances: Vec<BorderInstance>, + ) -> Self { + RenderTask::with_dynamic_location( + size, + TaskDependencies::new(), + RenderTaskKind::Border(BorderTask { + instances, + }), + ClearMode::Transparent, + ) + } + pub fn new_scaling( src_task_id: RenderTaskId, - rg_builder: &mut RenderTaskGraphBuilder, + render_tasks: &mut RenderTaskGraph, target_kind: RenderTargetKind, size: DeviceIntSize, ) -> RenderTaskId { Self::new_scaling_with_padding( - src_task_id, - rg_builder, + BlitSource::RenderTask { task_id: src_task_id }, + render_tasks, target_kind, size, DeviceIntSideOffsets::zero(), @@ -964,33 +813,36 @@ impl RenderTask { } pub fn new_scaling_with_padding( - source: RenderTaskId, - rg_builder: &mut RenderTaskGraphBuilder, + source: BlitSource, + render_tasks: &mut RenderTaskGraph, target_kind: RenderTargetKind, padded_size: DeviceIntSize, padding: DeviceIntSideOffsets, ) -> RenderTaskId { - let uv_rect_kind = rg_builder.get_task(source).uv_rect_kind(); + let (uv_rect_kind, children, image) = match source { + BlitSource::RenderTask { task_id } => (render_tasks[task_id].uv_rect_kind(), smallvec![task_id], None), + BlitSource::Image { key } => (UvRectKind::Rect, smallvec![], Some(key)), + }; - let task_id = rg_builder.add().init( - RenderTask::new_dynamic( + render_tasks.add().init( + RenderTask::with_dynamic_location( padded_size, + children, RenderTaskKind::Scaling(ScalingTask { target_kind, + image, + uv_rect_kind, padding, }), - ).with_uv_rect_kind(uv_rect_kind) - ); - - rg_builder.add_dependency(task_id, source); - - task_id + ClearMode::DontCare, + ) + ) } pub fn new_svg_filter( filter_primitives: &[FilterPrimitive], filter_datas: &[SFilterData], - rg_builder: &mut RenderTaskGraphBuilder, + render_tasks: &mut RenderTaskGraph, content_size: DeviceIntSize, uv_rect_kind: UvRectKind, original_task_id: RenderTaskId, @@ -1005,7 +857,7 @@ impl RenderTask { let get_task_input = | input: &FilterPrimitiveInput, filter_primitives: &[FilterPrimitive], - rg_builder: &mut RenderTaskGraphBuilder, + render_tasks: &mut RenderTaskGraph, cur_index: usize, outputs: &[RenderTaskId], original: RenderTaskId, @@ -1019,22 +871,20 @@ impl RenderTask { match (input_color_space, color_space) { (ColorSpace::Srgb, ColorSpace::LinearRgb) => { - task_id = RenderTask::new_svg_filter_primitive( + task_id = render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![task_id], content_size, uv_rect_kind, SvgFilterInfo::SrgbToLinear, - rg_builder, - ); + )); }, (ColorSpace::LinearRgb, ColorSpace::Srgb) => { - task_id = RenderTask::new_svg_filter_primitive( + task_id = render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![task_id], content_size, uv_rect_kind, SvgFilterInfo::LinearToSrgb, - rg_builder, - ); + )); }, _ => {}, } @@ -1051,7 +901,7 @@ impl RenderTask { get_task_input( &identity.input, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, @@ -1062,7 +912,7 @@ impl RenderTask { let input_1_task_id = get_task_input( &blend.input1, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, @@ -1071,37 +921,34 @@ impl RenderTask { let input_2_task_id = get_task_input( &blend.input2, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, primitive.color_space ); - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![input_1_task_id, input_2_task_id], content_size, uv_rect_kind, SvgFilterInfo::Blend(blend.mode), - rg_builder, - ) + )) }, FilterPrimitiveKind::Flood(ref flood) => { - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![], content_size, uv_rect_kind, SvgFilterInfo::Flood(flood.color), - rg_builder, - ) + )) } FilterPrimitiveKind::Blur(ref blur) => { - let width_std_deviation = blur.width * device_pixel_scale.0; - let height_std_deviation = blur.height * device_pixel_scale.0; + let blur_std_deviation = blur.radius * device_pixel_scale.0; let input_task_id = get_task_input( &blur.input, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, @@ -1109,18 +956,18 @@ impl RenderTask { ); RenderTask::new_blur( - DeviceSize::new(width_std_deviation, height_std_deviation), + DeviceSize::new(blur_std_deviation, blur_std_deviation), // TODO: This is a hack to ensure that a blur task's input is always // in the blur's previous pass. - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::Identity, - rg_builder, - ), - rg_builder, + )), + render_tasks, RenderTargetKind::Color, + ClearMode::Transparent, None, content_size, ) @@ -1129,45 +976,43 @@ impl RenderTask { let input_task_id = get_task_input( &opacity.input, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, primitive.color_space ); - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::Opacity(opacity.opacity), - rg_builder, - ) + )) } FilterPrimitiveKind::ColorMatrix(ref color_matrix) => { let input_task_id = get_task_input( &color_matrix.input, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, primitive.color_space ); - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::ColorMatrix(Box::new(color_matrix.matrix)), - rg_builder, - ) + )) } FilterPrimitiveKind::DropShadow(ref drop_shadow) => { let input_task_id = get_task_input( &drop_shadow.input, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, @@ -1177,36 +1022,37 @@ impl RenderTask { let blur_std_deviation = drop_shadow.shadow.blur_radius * device_pixel_scale.0; let offset = drop_shadow.shadow.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale; - let offset_task_id = RenderTask::new_svg_filter_primitive( - smallvec![input_task_id], - content_size, - uv_rect_kind, - SvgFilterInfo::Offset(offset), - rg_builder, + let offset_task_id = render_tasks.add().init( + RenderTask::new_svg_filter_primitive( + smallvec![input_task_id], + content_size, + uv_rect_kind, + SvgFilterInfo::Offset(offset), + ) ); let blur_task_id = RenderTask::new_blur( DeviceSize::new(blur_std_deviation, blur_std_deviation), offset_task_id, - rg_builder, + render_tasks, RenderTargetKind::Color, + ClearMode::Transparent, None, content_size, ); - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![input_task_id, blur_task_id], content_size, uv_rect_kind, SvgFilterInfo::DropShadow(drop_shadow.shadow.color), - rg_builder, - ) + )) } FilterPrimitiveKind::ComponentTransfer(ref component_transfer) => { let input_task_id = get_task_input( &component_transfer.input, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, @@ -1218,20 +1064,19 @@ impl RenderTask { if filter_data.is_identity() { input_task_id } else { - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::ComponentTransfer(filter_data.clone()), - rg_builder, - ) + )) } } FilterPrimitiveKind::Offset(ref info) => { let input_task_id = get_task_input( &info.input, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, @@ -1239,19 +1084,18 @@ impl RenderTask { ); let offset = info.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale; - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::Offset(offset), - rg_builder, - ) + )) } FilterPrimitiveKind::Composite(info) => { let input_1_task_id = get_task_input( &info.input1, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, @@ -1260,20 +1104,19 @@ impl RenderTask { let input_2_task_id = get_task_input( &info.input2, filter_primitives, - rg_builder, + render_tasks, cur_index, &outputs, original_task_id, primitive.color_space ); - RenderTask::new_svg_filter_primitive( + render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![input_1_task_id, input_2_task_id], content_size, uv_rect_kind, SvgFilterInfo::Composite(info.operator), - rg_builder, - ) + )) } }; outputs.push(render_task_id); @@ -1284,13 +1127,12 @@ impl RenderTask { // Convert to sRGB if needed if filter_primitives.last().unwrap().color_space == ColorSpace::LinearRgb { - render_task_id = RenderTask::new_svg_filter_primitive( + render_task_id = render_tasks.add().init(RenderTask::new_svg_filter_primitive( smallvec![render_task_id], content_size, uv_rect_kind, SvgFilterInfo::LinearToSrgb, - rg_builder, - ); + )); } render_task_id @@ -1301,71 +1143,191 @@ impl RenderTask { target_size: DeviceIntSize, uv_rect_kind: UvRectKind, info: SvgFilterInfo, - rg_builder: &mut RenderTaskGraphBuilder, - ) -> RenderTaskId { - let task_id = rg_builder.add().init(RenderTask::new_dynamic( + ) -> Self { + RenderTask::with_dynamic_location( target_size, + tasks, RenderTaskKind::SvgFilter(SvgFilterTask { extra_gpu_cache_handle: None, + uv_rect_handle: GpuCacheHandle::new(), + uv_rect_kind, info, }), - ).with_uv_rect_kind(uv_rect_kind)); - - for child_id in tasks { - rg_builder.add_dependency(task_id, child_id); - } - - task_id + ClearMode::Transparent, + ) } pub fn uv_rect_kind(&self) -> UvRectKind { - self.uv_rect_kind - } + match self.kind { + RenderTaskKind::CacheMask(..) | + RenderTaskKind::Readback(..) => { + unreachable!("bug: unexpected render task"); + } - pub fn get_texture_address(&self, gpu_cache: &GpuCache) -> GpuCacheAddress { - gpu_cache.get_address(&self.uv_rect_handle) - } + RenderTaskKind::Picture(ref task) => { + task.uv_rect_kind + } - pub fn get_dynamic_size(&self) -> DeviceIntSize { - self.location.size() + RenderTaskKind::VerticalBlur(ref task) | + RenderTaskKind::HorizontalBlur(ref task) => { + task.uv_rect_kind + } + + RenderTaskKind::Scaling(ref task) => { + task.uv_rect_kind + } + + RenderTaskKind::SvgFilter(ref task) => { + task.uv_rect_kind + } + + RenderTaskKind::ClipRegion(..) | + RenderTaskKind::Border(..) | + RenderTaskKind::Gradient(..) | + RenderTaskKind::LineDecoration(..) | + RenderTaskKind::Blit(..) => { + UvRectKind::Rect + } + + #[cfg(test)] + RenderTaskKind::Test(..) => { + unreachable!("Unexpected render task"); + } + } } - pub fn get_target_texture(&self) -> CacheTextureId { - match self.location { - RenderTaskLocation::Dynamic { texture_id, .. } => { - assert_ne!(texture_id, CacheTextureId::INVALID); - texture_id + // Write (up to) 8 floats of data specific to the type + // of render task that is provided to the GPU shaders + // via a vertex texture. + pub fn write_task_data(&self) -> RenderTaskData { + // NOTE: The ordering and layout of these structures are + // required to match both the GPU structures declared + // in prim_shared.glsl, and also the uses in submit_batch() + // in renderer.rs. + // TODO(gw): Maybe there's a way to make this stuff a bit + // more type-safe. Although, it will always need + // to be kept in sync with the GLSL code anyway. + + let data = match self.kind { + RenderTaskKind::Picture(ref task) => { + // Note: has to match `PICTURE_TYPE_*` in shaders + [ + task.device_pixel_scale.0, + task.content_origin.x as f32, + task.content_origin.y as f32, + ] + } + RenderTaskKind::CacheMask(ref task) => { + [ + task.device_pixel_scale.0, + task.actual_rect.origin.x as f32, + task.actual_rect.origin.y as f32, + ] } - RenderTaskLocation::CacheRequest { .. } | - RenderTaskLocation::Unallocated { .. } | - RenderTaskLocation::Static { .. } => { + RenderTaskKind::ClipRegion(ref task) => { + [ + task.device_pixel_scale.0, + 0.0, + 0.0, + ] + } + RenderTaskKind::VerticalBlur(ref task) | + RenderTaskKind::HorizontalBlur(ref task) => { + [ + task.blur_std_deviation, + task.blur_region.width as f32, + task.blur_region.height as f32, + ] + } + RenderTaskKind::Readback(..) | + RenderTaskKind::Scaling(..) | + RenderTaskKind::Border(..) | + RenderTaskKind::LineDecoration(..) | + RenderTaskKind::Gradient(..) | + RenderTaskKind::Blit(..) => { + [0.0; 3] + } + + + RenderTaskKind::SvgFilter(ref task) => { + match task.info { + SvgFilterInfo::Opacity(opacity) => [opacity, 0.0, 0.0], + SvgFilterInfo::Offset(offset) => [offset.x, offset.y, 0.0], + _ => [0.0; 3] + } + } + + #[cfg(test)] + RenderTaskKind::Test(..) => { unreachable!(); } + }; + + let (mut target_rect, target_index) = self.get_target_rect(); + // The primitives inside a fixed-location render task + // are already placed to their corresponding positions, + // so the shader doesn't need to shift by the origin. + if let RenderTaskLocation::Fixed(_) = self.location { + target_rect.origin = DeviceIntPoint::origin(); + } + + RenderTaskData { + data: [ + target_rect.origin.x as f32, + target_rect.origin.y as f32, + target_rect.size.width as f32, + target_rect.size.height as f32, + target_index.0 as f32, + data[0], + data[1], + data[2], + ] } } - pub fn get_texture_source(&self) -> TextureSource { - match self.location { - RenderTaskLocation::Dynamic { texture_id, .. } => { - assert_ne!(texture_id, CacheTextureId::INVALID); - TextureSource::TextureCache(texture_id, Swizzle::default()) + pub fn get_texture_address(&self, gpu_cache: &GpuCache) -> GpuCacheAddress { + match self.kind { + RenderTaskKind::Picture(ref info) => { + gpu_cache.get_address(&info.uv_rect_handle) } - RenderTaskLocation::Static { surface: StaticRenderTaskSurface::ReadOnly { source }, .. } => { - source + RenderTaskKind::VerticalBlur(ref info) | + RenderTaskKind::HorizontalBlur(ref info) => { + gpu_cache.get_address(&info.uv_rect_handle) } - RenderTaskLocation::Static { surface: StaticRenderTaskSurface::TextureCache { texture, .. }, .. } => { - TextureSource::TextureCache(texture, Swizzle::default()) + RenderTaskKind::SvgFilter(ref info) => { + gpu_cache.get_address(&info.uv_rect_handle) } - RenderTaskLocation::Static { .. } | - RenderTaskLocation::CacheRequest { .. } | - RenderTaskLocation::Unallocated { .. } => { - unreachable!(); + RenderTaskKind::ClipRegion(..) | + RenderTaskKind::Readback(..) | + RenderTaskKind::Scaling(..) | + RenderTaskKind::Blit(..) | + RenderTaskKind::Border(..) | + RenderTaskKind::CacheMask(..) | + RenderTaskKind::Gradient(..) | + RenderTaskKind::LineDecoration(..) => { + panic!("texture handle not supported for this task kind"); } + #[cfg(test)] + RenderTaskKind::Test(..) => { + panic!("RenderTask tests aren't expected to exercise this code"); + } + } + } + + pub fn get_dynamic_size(&self) -> DeviceIntSize { + match self.location { + RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(), + RenderTaskLocation::Dynamic(_, size) => size, + RenderTaskLocation::TextureCache { rect, .. } => rect.size, + RenderTaskLocation::PictureCache { size, .. } => size, } } - pub fn get_target_rect(&self) -> DeviceIntRect { + pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) { match self.location { + RenderTaskLocation::Fixed(rect) => { + (rect, RenderTargetIndex(0)) + } // Previously, we only added render tasks after the entire // primitive chain was determined visible. This meant that // we could assert any render task in the list was also @@ -1380,52 +1342,223 @@ impl RenderTask { // TODO(gw): Consider some kind of tag or other method // to mark a task as unused explicitly. This // would allow us to restore this debug check. - RenderTaskLocation::Dynamic { rect, .. } => rect, - RenderTaskLocation::Static { rect, .. } => rect, - RenderTaskLocation::CacheRequest { .. } - | RenderTaskLocation::Unallocated { .. } => { - panic!("bug: get_target_rect called before allocating"); + RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => { + (DeviceIntRect::new(origin, size), target_index) + } + RenderTaskLocation::Dynamic(None, _) => { + (DeviceIntRect::zero(), RenderTargetIndex(0)) + } + RenderTaskLocation::TextureCache {layer, rect, .. } => { + (rect, RenderTargetIndex(layer as usize)) + } + RenderTaskLocation::PictureCache { ref surface, size, .. } => { + let layer = match surface { + ResolvedSurfaceTexture::TextureCache { layer, .. } => *layer, + ResolvedSurfaceTexture::Native { .. } => 0, + }; + + ( + DeviceIntRect::new( + DeviceIntPoint::zero(), + size, + ), + RenderTargetIndex(layer as usize), + ) } } } pub fn target_kind(&self) -> RenderTargetKind { - self.kind.target_kind() + match self.kind { + RenderTaskKind::LineDecoration(..) | + RenderTaskKind::Readback(..) | + RenderTaskKind::Border(..) | + RenderTaskKind::Gradient(..) | + RenderTaskKind::Picture(..) | + RenderTaskKind::Blit(..) | + RenderTaskKind::SvgFilter(..) => { + RenderTargetKind::Color + } + + RenderTaskKind::ClipRegion(..) | + RenderTaskKind::CacheMask(..) => { + RenderTargetKind::Alpha + } + + RenderTaskKind::VerticalBlur(ref task_info) | + RenderTaskKind::HorizontalBlur(ref task_info) => { + task_info.target_kind + } + + RenderTaskKind::Scaling(ref task_info) => { + task_info.target_kind + } + + #[cfg(test)] + RenderTaskKind::Test(kind) => kind, + } } pub fn write_gpu_blocks( &mut self, - target_rect: DeviceIntRect, gpu_cache: &mut GpuCache, ) { profile_scope!("write_gpu_blocks"); + let (target_rect, target_index) = self.get_target_rect(); - self.kind.write_gpu_blocks(gpu_cache); - - if self.cache_handle.is_some() { - // The uv rect handle of cached render tasks is requested and set by the - // render task cache. - return; - } + let (cache_handle, uv_rect_kind) = match self.kind { + RenderTaskKind::HorizontalBlur(ref mut info) | + RenderTaskKind::VerticalBlur(ref mut info) => { + (&mut info.uv_rect_handle, info.uv_rect_kind) + } + RenderTaskKind::Picture(ref mut info) => { + (&mut info.uv_rect_handle, info.uv_rect_kind) + } + RenderTaskKind::SvgFilter(ref mut info) => { + (&mut info.uv_rect_handle, info.uv_rect_kind) + } + RenderTaskKind::Readback(..) | + RenderTaskKind::Scaling(..) | + RenderTaskKind::Blit(..) | + RenderTaskKind::ClipRegion(..) | + RenderTaskKind::Border(..) | + RenderTaskKind::CacheMask(..) | + RenderTaskKind::Gradient(..) | + RenderTaskKind::LineDecoration(..) => { + return; + } + #[cfg(test)] + RenderTaskKind::Test(..) => { + panic!("RenderTask tests aren't expected to exercise this code"); + } + }; - if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) { + if let Some(mut request) = gpu_cache.request(cache_handle) { let p0 = target_rect.min().to_f32(); let p1 = target_rect.max().to_f32(); let image_source = ImageSource { p0, p1, - user_data: [0.0; 4], - uv_rect_kind: self.uv_rect_kind, + texture_layer: target_index.0 as f32, + user_data: [0.0; 3], + uv_rect_kind, }; image_source.write_gpu_blocks(&mut request); } + + if let RenderTaskKind::SvgFilter(ref mut filter_task) = self.kind { + match filter_task.info { + SvgFilterInfo::ColorMatrix(ref matrix) => { + let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new); + if let Some(mut request) = gpu_cache.request(handle) { + for i in 0..5 { + request.push([matrix[i*4], matrix[i*4+1], matrix[i*4+2], matrix[i*4+3]]); + } + } + } + SvgFilterInfo::DropShadow(color) | + SvgFilterInfo::Flood(color) => { + let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new); + if let Some(mut request) = gpu_cache.request(handle) { + request.push(color.to_array()); + } + } + SvgFilterInfo::ComponentTransfer(ref data) => { + let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new); + if let Some(request) = gpu_cache.request(handle) { + data.update(request); + } + } + SvgFilterInfo::Composite(ref operator) => { + if let CompositeOperator::Arithmetic(k_vals) = operator { + let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new); + if let Some(mut request) = gpu_cache.request(handle) { + request.push(*k_vals); + } + } + } + _ => {}, + } + } } - /// Called by the render task cache. - /// - /// Tells the render task that it is cached (which means its gpu cache - /// handle is managed by the texture cache). - pub fn mark_cached(&mut self, handle: RenderTaskCacheEntryHandle) { - self.cache_handle = Some(handle); + #[cfg(feature = "debugger")] + pub fn print_with<T: PrintTreePrinter>(&self, pt: &mut T, tree: &RenderTaskGraph) -> bool { + match self.kind { + RenderTaskKind::Picture(ref task) => { + pt.new_level(format!("Picture of {:?}", task.pic_index)); + } + RenderTaskKind::CacheMask(ref task) => { + pt.new_level(format!("CacheMask with {} clips", task.clip_node_range.count)); + pt.add_item(format!("rect: {:?}", task.actual_rect)); + } + RenderTaskKind::LineDecoration(..) => { + pt.new_level("LineDecoration".to_owned()); + } + RenderTaskKind::ClipRegion(..) => { + pt.new_level("ClipRegion".to_owned()); + } + RenderTaskKind::VerticalBlur(ref task) => { + pt.new_level("VerticalBlur".to_owned()); + task.print_with(pt); + } + RenderTaskKind::HorizontalBlur(ref task) => { + pt.new_level("HorizontalBlur".to_owned()); + task.print_with(pt); + } + RenderTaskKind::Readback(ref rect) => { + pt.new_level("Readback".to_owned()); + pt.add_item(format!("rect: {:?}", rect)); + } + RenderTaskKind::Scaling(ref kind) => { + pt.new_level("Scaling".to_owned()); + pt.add_item(format!("kind: {:?}", kind)); + } + RenderTaskKind::Border(..) => { + pt.new_level("Border".to_owned()); + } + RenderTaskKind::Blit(ref task) => { + pt.new_level("Blit".to_owned()); + pt.add_item(format!("source: {:?}", task.source)); + } + RenderTaskKind::Gradient(..) => { + pt.new_level("Gradient".to_owned()); + } + RenderTaskKind::SvgFilter(ref task) => { + pt.new_level("SvgFilter".to_owned()); + pt.add_item(format!("primitive: {:?}", task.info)); + } + #[cfg(test)] + RenderTaskKind::Test(..) => { + pt.new_level("Test".to_owned()); + } + } + + pt.add_item(format!("clear to: {:?}", self.clear_mode)); + pt.add_item(format!("dimensions: {:?}", self.location.size())); + + for &child_id in &self.children { + if tree[child_id].print_with(pt, tree) { + pt.add_item(format!("self: {:?}", child_id)) + } + } + + pt.end_level(); + true + } + + /// Mark this render task for keeping the results alive up until the end of the frame. + #[inline] + pub fn mark_for_saving(&mut self) { + match self.location { + RenderTaskLocation::Fixed(..) | + RenderTaskLocation::Dynamic(..) => { + self.saved_index = Some(SavedTargetIndex::PENDING); + } + RenderTaskLocation::TextureCache { .. } | + RenderTaskLocation::PictureCache { .. } => { + panic!("Unable to mark a permanently cached task for saving!"); + } + } } } diff --git a/third_party/webrender/webrender/src/render_task_cache.rs b/third_party/webrender/webrender/src/render_task_cache.rs index 370897f30b0..22ea235e33e 100644 --- a/third_party/webrender/webrender/src/render_task_cache.rs +++ b/third_party/webrender/webrender/src/render_task_cache.rs @@ -11,34 +11,15 @@ use crate::device::TextureFilter; use crate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle}; use crate::gpu_cache::GpuCache; use crate::internal_types::FastHashMap; -use crate::picture::{SurfaceIndex, SurfaceInfo}; use crate::prim_store::image::ImageCacheKey; -use crate::prim_store::gradient::{ - FastLinearGradientCacheKey, LinearGradientCacheKey, RadialGradientCacheKey, - ConicGradientCacheKey, -}; +use crate::prim_store::gradient::GradientCacheKey; use crate::prim_store::line_dec::LineDecorationCacheKey; use crate::resource_cache::CacheItem; use std::{mem, usize, f32, i32}; -use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader}; +use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction}; use crate::render_target::RenderTargetKind; -use crate::render_task::{RenderTask, StaticRenderTaskSurface, RenderTaskLocation, RenderTaskKind, CachedTask}; -use crate::render_task_graph::{RenderTaskGraphBuilder, RenderTaskId}; -use crate::frame_builder::add_child_render_task; -use euclid::Scale; - -const MAX_CACHE_TASK_SIZE: f32 = 4096.0; - -/// Describes a parent dependency for a render task. Render tasks -/// may depend on a surface (e.g. when a surface uses a cached border) -/// or an arbitrary render task (e.g. when a clip mask uses a blurred -/// box-shadow input). -pub enum RenderTaskParent { - /// Parent is a surface - Surface(SurfaceIndex), - /// Parent is a render task - RenderTask(RenderTaskId), -} +use crate::render_task::{RenderTask, RenderTaskLocation}; +use crate::render_task_graph::{RenderTaskGraph, RenderTaskId}; #[derive(Clone, Debug, Hash, PartialEq, Eq)] #[cfg_attr(feature = "capture", derive(Serialize))] @@ -48,10 +29,7 @@ pub enum RenderTaskCacheKeyKind { Image(ImageCacheKey), BorderSegment(BorderSegmentCacheKey), LineDecoration(LineDecorationCacheKey), - FastLinearGradient(FastLinearGradientCacheKey), - LinearGradient(LinearGradientCacheKey), - RadialGradient(RadialGradientCacheKey), - ConicGradient(ConicGradientCacheKey), + Gradient(GradientCacheKey), } #[derive(Clone, Debug, Hash, PartialEq, Eq)] @@ -66,16 +44,9 @@ pub struct RenderTaskCacheKey { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct RenderTaskCacheEntry { - user_data: Option<[f32; 4]>, - target_kind: RenderTargetKind, + user_data: Option<[f32; 3]>, is_opaque: bool, - frame_id: u64, pub handle: TextureCacheHandle, - /// If a render task was generated for this cache entry on _this_ frame, - /// we need to track the task id here. This allows us to hook it up as - /// a dependency of any parent tasks that make a reqiest from the render - /// task cache. - pub render_task_id: Option<RenderTaskId>, } #[derive(Debug, MallocSizeOf)] @@ -90,7 +61,6 @@ pub enum RenderTaskCacheMarker {} pub struct RenderTaskCache { map: FastHashMap<RenderTaskCacheKey, FreeListHandle<RenderTaskCacheMarker>>, cache_entries: FreeList<RenderTaskCacheEntry, RenderTaskCacheMarker>, - frame_id: u64, } pub type RenderTaskCacheEntryHandle = WeakFreeListHandle<RenderTaskCacheMarker>; @@ -100,7 +70,6 @@ impl RenderTaskCache { RenderTaskCache { map: FastHashMap::default(), cache_entries: FreeList::new(), - frame_id: 0, } } @@ -113,7 +82,6 @@ impl RenderTaskCache { &mut self, texture_cache: &mut TextureCache, ) { - self.frame_id += 1; profile_scope!("begin_frame"); // Drop any items from the cache that have been // evicted from the texture cache. @@ -129,35 +97,17 @@ impl RenderTaskCache { // from here so that this hash map doesn't // grow indefinitely! let cache_entries = &mut self.cache_entries; - let frame_id = self.frame_id; self.map.retain(|_, handle| { - let mut retain = texture_cache.is_allocated( + let retain = texture_cache.is_allocated( &cache_entries.get(handle).handle, ); - if retain { - let entry = cache_entries.get_mut(&handle); - if frame_id > entry.frame_id + 10 { - texture_cache.evict_handle(&entry.handle); - retain = false; - } - } - if !retain { let handle = mem::replace(handle, FreeListHandle::invalid()); cache_entries.free(handle); } - retain }); - - // Clear out the render task ID of any remaining cache entries that were drawn - // on the previous frame, so we don't accidentally hook up stale dependencies - // when building the frame graph. - for (_, handle) in &self.map { - let entry = self.cache_entries.get_mut(handle); - entry.render_task_id = None; - } } fn alloc_render_task( @@ -167,11 +117,17 @@ impl RenderTaskCache { texture_cache: &mut TextureCache, ) { // Find out what size to alloc in the texture cache. - let size = render_task.location.size(); - let target_kind = render_task.target_kind(); + let size = match render_task.location { + RenderTaskLocation::Fixed(..) | + RenderTaskLocation::PictureCache { .. } | + RenderTaskLocation::TextureCache { .. } => { + panic!("BUG: dynamic task was expected"); + } + RenderTaskLocation::Dynamic(_, size) => size, + }; // Select the right texture page to allocate from. - let image_format = match target_kind { + let image_format = match render_task.target_kind() { RenderTargetKind::Color => texture_cache.shared_color_expected_format(), RenderTargetKind::Alpha => texture_cache.shared_alpha_expected_format(), }; @@ -196,28 +152,24 @@ impl RenderTaskCache { descriptor, TextureFilter::Linear, None, - entry.user_data.unwrap_or([0.0; 4]), + entry.user_data.unwrap_or([0.0; 3]), DirtyRect::All, gpu_cache, None, render_task.uv_rect_kind(), Eviction::Auto, - TargetShader::Default, ); // Get the allocation details in the texture cache, and store - // this in the render task. The renderer will draw this task - // into the appropriate rect of the texture cache on this frame. - let (texture_id, uv_rect, _, _, _) = + // this in the render task. The renderer will draw this + // task into the appropriate layer and rect of the texture + // cache on this frame. + let (texture_id, texture_layer, uv_rect, _, _) = texture_cache.get_cache_location(&entry.handle); - let surface = StaticRenderTaskSurface::TextureCache { + render_task.location = RenderTaskLocation::TextureCache { texture: texture_id, - target_kind, - }; - - render_task.location = RenderTaskLocation::Static { - surface, + layer: texture_layer, rect: uv_rect.to_i32(), }; } @@ -227,18 +179,14 @@ impl RenderTaskCache { key: RenderTaskCacheKey, texture_cache: &mut TextureCache, gpu_cache: &mut GpuCache, - rg_builder: &mut RenderTaskGraphBuilder, - user_data: Option<[f32; 4]>, + render_tasks: &mut RenderTaskGraph, + user_data: Option<[f32; 3]>, is_opaque: bool, - parent: RenderTaskParent, - surfaces: &[SurfaceInfo], f: F, - ) -> Result<RenderTaskId, ()> + ) -> Result<RenderTaskCacheEntryHandle, ()> where - F: FnOnce(&mut RenderTaskGraphBuilder) -> Result<RenderTaskId, ()>, + F: FnOnce(&mut RenderTaskGraph) -> Result<RenderTaskId, ()>, { - let frame_id = self.frame_id; - let size = key.size; // Get the texture cache handle for this cache key, // or create one. let cache_entries = &mut self.cache_entries; @@ -246,79 +194,31 @@ impl RenderTaskCache { let entry = RenderTaskCacheEntry { handle: TextureCacheHandle::invalid(), user_data, - target_kind: RenderTargetKind::Color, // will be set below. is_opaque, - frame_id, - render_task_id: None, }; cache_entries.insert(entry) }); let cache_entry = cache_entries.get_mut(entry_handle); - cache_entry.frame_id = self.frame_id; // Check if this texture cache handle is valid. if texture_cache.request(&cache_entry.handle, gpu_cache) { // Invoke user closure to get render task chain // to draw this into the texture cache. - let render_task_id = f(rg_builder)?; + let render_task_id = f(render_tasks)?; + render_tasks.cacheable_render_tasks.push(render_task_id); cache_entry.user_data = user_data; cache_entry.is_opaque = is_opaque; - cache_entry.render_task_id = Some(render_task_id); - - let render_task = rg_builder.get_task_mut(render_task_id); - - render_task.mark_cached(entry_handle.weak()); - cache_entry.target_kind = render_task.kind.target_kind(); RenderTaskCache::alloc_render_task( - render_task, + &mut render_tasks[render_task_id], cache_entry, gpu_cache, texture_cache, ); } - // If this render task cache is being drawn this frame, ensure we hook up the - // render task for it as a dependency of any render task that uses this as - // an input source. - if let Some(render_task_id) = cache_entry.render_task_id { - match parent { - RenderTaskParent::Surface(surface_index) => { - // If parent is a surface, use helper fn to add this dependency, - // which correctly takes account of the render task configuration - // of the surface. - add_child_render_task( - surface_index, - render_task_id, - surfaces, - rg_builder - ); - } - RenderTaskParent::RenderTask(parent_render_task_id) => { - // For render tasks, just add it as a direct dependency on the - // task graph builder. - rg_builder.add_dependency( - parent_render_task_id, - render_task_id, - ); - } - } - - return Ok(render_task_id); - } - - let target_kind = cache_entry.target_kind; - let mut task = RenderTask::new( - RenderTaskLocation::CacheRequest { size, }, - RenderTaskKind::Cached(CachedTask { - target_kind, - }), - ); - task.mark_cached(entry_handle.weak()); - let render_task_id = rg_builder.add().init(task); - - Ok(render_task_id) + Ok(entry_handle.weak()) } pub fn get_cache_entry( @@ -359,17 +259,9 @@ impl RenderTaskCache { // Gecko tests. // Note: zero-square tasks are prohibited in WR task graph, so // we ensure each dimension to be at least the length of 1 after rounding. -pub fn to_cache_size(size: LayoutSize, device_pixel_scale: &mut Scale<f32, LayoutPixel, DevicePixel>) -> DeviceIntSize { - let mut device_size = (size * *device_pixel_scale).round(); - - if device_size.width > MAX_CACHE_TASK_SIZE || device_size.height > MAX_CACHE_TASK_SIZE { - let scale = MAX_CACHE_TASK_SIZE / f32::max(device_size.width, device_size.height); - *device_pixel_scale = *device_pixel_scale * Scale::new(scale); - device_size = (size * *device_pixel_scale).round(); - } - +pub fn to_cache_size(size: DeviceSize) -> DeviceIntSize { DeviceIntSize::new( - 1.max(device_size.width as i32), - 1.max(device_size.height as i32), + 1.max(size.width.round() as i32), + 1.max(size.height.round() as i32), ) } diff --git a/third_party/webrender/webrender/src/render_task_graph.rs b/third_party/webrender/webrender/src/render_task_graph.rs index aa089a15d3a..3058a988386 100644 --- a/third_party/webrender/webrender/src/render_task_graph.rs +++ b/third_party/webrender/webrender/src/render_task_graph.rs @@ -1,808 +1,448 @@ -// 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/. - //! This module contains the render task graph. //! //! Code associated with creating specific render tasks is in the render_task //! module. -use api::units::*; use api::ImageFormat; -use crate::gpu_cache::{GpuCache, GpuCacheAddress}; -use crate::internal_types::{TextureSource, CacheTextureId, FastHashMap, FastHashSet}; +use api::units::*; +use crate::internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex}; use crate::render_backend::FrameId; -use crate::render_task::{StaticRenderTaskSurface, RenderTaskLocation, RenderTask}; -use crate::render_target::RenderTargetKind; -use crate::render_task::{RenderTaskData, RenderTaskKind}; -use crate::resource_cache::ResourceCache; -use crate::texture_pack::GuillotineAllocator; -use crate::prim_store::DeferredResolve; -use crate::image_source::{resolve_image, resolve_cached_render_task}; -use crate::util::VecHelper; -use smallvec::SmallVec; -use std::mem; - -use crate::render_target::{RenderTargetList, ColorRenderTarget}; +use crate::render_target::{RenderTarget, RenderTargetKind, RenderTargetList, ColorRenderTarget}; use crate::render_target::{PictureCacheTarget, TextureCacheRenderTarget, AlphaRenderTarget}; -use crate::util::Allocation; -use std::{usize, f32}; +use crate::render_task::{BlitSource, RenderTask, RenderTaskKind, RenderTaskAddress, RenderTaskData}; +use crate::render_task::{RenderTaskLocation}; +use crate::util::{VecHelper, Allocation}; +use std::{cmp, usize, f32, i32, u32}; -/// According to apitrace, textures larger than 2048 break fast clear -/// optimizations on some intel drivers. We sometimes need to go larger, but -/// we try to avoid it. -const MAX_SHARED_SURFACE_SIZE: i32 = 2048; - -/// If we ever need a larger texture than the ideal, we better round it up to a -/// reasonable number in order to have a bit of leeway in case the size of this -/// this target is changing each frame. -const TEXTURE_DIMENSION_MASK: i32 = 0xFF; +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct RenderTaskGraph { + pub tasks: Vec<RenderTask>, + pub task_data: Vec<RenderTaskData>, + /// Tasks that don't have dependencies, and that may be shared between + /// picture tasks. + /// + /// We render these unconditionally before-rendering the rest of the tree. + pub cacheable_render_tasks: Vec<RenderTaskId>, + next_saved: SavedTargetIndex, + frame_id: FrameId, +} /// Allows initializing a render task directly into the render task buffer. /// /// See utils::VecHelpers. RenderTask is fairly large so avoiding the move when -/// pushing into the vector can save a lot of expensive memcpys on pages with many +/// pushing into the vector can save a lot of exensive memcpys on pages with many /// render tasks. pub struct RenderTaskAllocation<'a> { - pub alloc: Allocation<'a, RenderTask>, + alloc: Allocation<'a, RenderTask>, + #[cfg(debug_assertions)] + frame_id: FrameId, } impl<'l> RenderTaskAllocation<'l> { #[inline(always)] pub fn init(self, value: RenderTask) -> RenderTaskId { RenderTaskId { - index: self.alloc.init(value) as u16, + index: self.alloc.init(value) as u32, + #[cfg(debug_assertions)] + frame_id: self.frame_id, } } } -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -#[derive(MallocSizeOf)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct RenderTaskId { - pub index: u16, -} - -impl RenderTaskId { - pub const INVALID: RenderTaskId = RenderTaskId { - index: u16::MAX, - }; -} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] -pub struct PassId(usize); - -impl PassId { - pub const MIN: PassId = PassId(0); - pub const MAX: PassId = PassId(!0); -} - -/// An internal representation of a dynamic surface that tasks can be -/// allocated into. Maintains some extra metadata about each surface -/// during the graph build. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -struct Surface { - /// Whether this is a color or alpha render target - kind: RenderTargetKind, - /// Allocator for this surface texture - allocator: GuillotineAllocator, - /// We can only allocate into this for reuse if it's a shared surface - is_shared: bool, -} - -impl Surface { - /// Allocate a rect within a shared surfce. Returns None if the - /// format doesn't match, or allocation fails. - fn alloc_rect( - &mut self, - size: DeviceIntSize, - kind: RenderTargetKind, - is_shared: bool, - ) -> Option<DeviceIntPoint> { - if self.kind == kind && self.is_shared == is_shared { - self.allocator - .allocate(&size) - .map(|(_slice, origin)| origin) - } else { - None +impl RenderTaskGraph { + pub fn new(frame_id: FrameId, counters: &RenderTaskGraphCounters) -> Self { + // Preallocate a little more than what we needed in the previous frame so that small variations + // in the number of items don't cause us to constantly reallocate. + let extra_items = 8; + RenderTaskGraph { + tasks: Vec::with_capacity(counters.tasks_len + extra_items), + task_data: Vec::with_capacity(counters.task_data_len + extra_items), + cacheable_render_tasks: Vec::with_capacity(counters.cacheable_render_tasks_len + extra_items), + next_saved: SavedTargetIndex(0), + frame_id, } } -} - -/// A sub-pass can draw to either a dynamic (temporary render target) surface, -/// or a persistent surface (texture or picture cache). -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Debug)] -pub enum SubPassSurface { - /// A temporary (intermediate) surface. - Dynamic { - /// The renderer texture id - texture_id: CacheTextureId, - /// Color / alpha render target - target_kind: RenderTargetKind, - /// The rectangle occupied by tasks in this surface. Used as a clear - /// optimization on some GPUs. - used_rect: DeviceIntRect, - }, - Persistent { - /// Reference to the texture or picture cache surface being drawn to. - surface: StaticRenderTaskSurface, - }, -} - -/// A subpass is a specific render target, and a list of tasks to draw to it. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct SubPass { - /// The surface this subpass draws to - pub surface: SubPassSurface, - /// The tasks assigned to this subpass. - pub task_ids: Vec<RenderTaskId>, -} - -/// A pass expresses dependencies between tasks. Each pass consists of a number -/// of subpasses. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct Pass { - /// The tasks assigned to this render pass - pub task_ids: Vec<RenderTaskId>, - /// The subpasses that make up this dependency pass - pub sub_passes: Vec<SubPass>, - /// A list of intermediate surfaces that can be invalidated after - /// this pass completes. - pub textures_to_invalidate: Vec<CacheTextureId>, -} - -/// The RenderTaskGraph is the immutable representation of the render task graph. It is -/// built by the RenderTaskGraphBuilder, and is constructed once per frame. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct RenderTaskGraph { - /// List of tasks added to the graph - pub tasks: Vec<RenderTask>, - - /// The passes that were created, based on dependencies between tasks - pub passes: Vec<Pass>, - - /// Current frame id, used for debug validation - frame_id: FrameId, - /// GPU specific data for each task that is made available to shaders - pub task_data: Vec<RenderTaskData>, - - /// Total number of intermediate surfaces that will be drawn to, used for test validation. - #[cfg(test)] - surface_count: usize, - - /// Total number of real allocated textures that will be drawn to, used for test validation. - #[cfg(test)] - unique_surfaces: FastHashSet<CacheTextureId>, -} - -/// The persistent interface that is used during frame building to construct the -/// frame graph. -pub struct RenderTaskGraphBuilder { - /// List of tasks added to the builder - tasks: Vec<RenderTask>, - - /// List of task roots - roots: FastHashSet<RenderTaskId>, - - /// Input dependencies where the input is a persistent target, - /// rather than a specific render task id. Useful for expressing - /// when a task relies on a readback of a surface that is partially - /// drawn to. - target_inputs: Vec<(RenderTaskId, StaticRenderTaskSurface)>, - - /// Current frame id, used for debug validation - frame_id: FrameId, - - /// A list of texture surfaces that can be freed at the end of a pass. Retained - /// here to reduce heap allocations. - textures_to_free: FastHashSet<CacheTextureId>, - - // Keep a map of `texture_id` to metadata about surfaces that are currently - // borrowed from the render target pool. - active_surfaces: FastHashMap<CacheTextureId, Surface>, - - /// A temporary buffer used by assign_free_pass. Kept here to avoid heap reallocs - child_task_buffer: Vec<RenderTaskId>, -} - -impl RenderTaskGraphBuilder { - /// Construct a new graph builder. Typically constructed once and maintained - /// over many frames, to avoid extra heap allocations where possible. - pub fn new() -> Self { - RenderTaskGraphBuilder { - tasks: Vec::new(), - roots: FastHashSet::default(), - target_inputs: Vec::new(), - frame_id: FrameId::INVALID, - textures_to_free: FastHashSet::default(), - active_surfaces: FastHashMap::default(), - child_task_buffer: Vec::new(), + pub fn counters(&self) -> RenderTaskGraphCounters { + RenderTaskGraphCounters { + tasks_len: self.tasks.len(), + task_data_len: self.task_data.len(), + cacheable_render_tasks_len: self.cacheable_render_tasks.len(), } } - pub fn frame_id(&self) -> FrameId { - self.frame_id - } - - /// Begin a new frame - pub fn begin_frame(&mut self, frame_id: FrameId) { - self.frame_id = frame_id; - self.roots.clear(); - } - - /// Get immutable access to a task - // TODO(gw): There's only a couple of places that existing code needs to access - // a task during the building step. Perhaps we can remove this? - pub fn get_task( - &self, - task_id: RenderTaskId, - ) -> &RenderTask { - &self.tasks[task_id.index as usize] - } - - /// Get mutable access to a task - // TODO(gw): There's only a couple of places that existing code needs to access - // a task during the building step. Perhaps we can remove this? - pub fn get_task_mut( - &mut self, - task_id: RenderTaskId, - ) -> &mut RenderTask { - &mut self.tasks[task_id.index as usize] - } - - /// Add a new task to the graph. pub fn add(&mut self) -> RenderTaskAllocation { - // Assume every task is a root to start with - self.roots.insert( - RenderTaskId { index: self.tasks.len() as u16 } - ); - RenderTaskAllocation { alloc: self.tasks.alloc(), + #[cfg(debug_assertions)] + frame_id: self.frame_id, } } - /// Express a dependency, such that `task_id` depends on `input` as a texture source. + /// Express a render task dependency between a parent and child task. + /// This is used to assign tasks to render passes. pub fn add_dependency( &mut self, - task_id: RenderTaskId, - input: RenderTaskId, + parent_id: RenderTaskId, + child_id: RenderTaskId, ) { - self.tasks[task_id.index as usize].children.push(input); - - // Once a task is an input, it's no longer a root - self.roots.remove(&input); + let parent = &mut self[parent_id]; + parent.children.push(child_id); } - /// Register a persistent surface as an input dependency of a task (readback). - pub fn add_target_input( + /// Assign this frame's render tasks to render passes ordered so that passes appear + /// earlier than the ones that depend on them. + pub fn generate_passes( &mut self, - task_id: RenderTaskId, - target: StaticRenderTaskSurface, - ) { - self.target_inputs.push((task_id, target)); + main_render_task: Option<RenderTaskId>, + screen_size: DeviceIntSize, + gpu_supports_fast_clears: bool, + ) -> Vec<RenderPass> { + profile_scope!("generate_passes"); + let mut passes = Vec::new(); + + if !self.cacheable_render_tasks.is_empty() { + self.generate_passes_impl( + &self.cacheable_render_tasks[..], + screen_size, + gpu_supports_fast_clears, + false, + &mut passes, + ); + } + + if let Some(main_task) = main_render_task { + self.generate_passes_impl( + &[main_task], + screen_size, + gpu_supports_fast_clears, + true, + &mut passes, + ); + } + + + self.resolve_target_conflicts(&mut passes); + + passes } - /// End the graph building phase and produce the immutable task graph for this frame - pub fn end_frame( - &mut self, - resource_cache: &mut ResourceCache, - gpu_cache: &mut GpuCache, - deferred_resolves: &mut Vec<DeferredResolve>, - ) -> RenderTaskGraph { - // Copy the render tasks over to the immutable graph output - let task_count = self.tasks.len(); - let tasks = mem::replace( - &mut self.tasks, - Vec::with_capacity(task_count), - ); - - let mut graph = RenderTaskGraph { - tasks, - passes: Vec::new(), - task_data: Vec::with_capacity(task_count), - frame_id: self.frame_id, - #[cfg(test)] - surface_count: 0, - #[cfg(test)] - unique_surfaces: FastHashSet::default(), - }; - - // Handle late mapping of dependencies on a specific persistent target. - // NOTE: This functionality isn't used by current callers of the frame graph, but - // will be used in future (for example, to express readbacks of partially - // rendered picture tiles for mix-blend-mode etc). - if !self.target_inputs.is_empty() { - // Create a mapping from persistent surface id -> render task root (used below): - let mut roots = FastHashMap::default(); - roots.reserve(self.roots.len()); - for root_id in &self.roots { - let task = &graph.tasks[root_id.index as usize]; - match task.location { - RenderTaskLocation::Static { ref surface, .. } => { - // We should never encounter a graph where the same surface is a - // render root more than one. - assert!(!roots.contains_key(surface)); - roots.insert(surface.clone(), *root_id); - } - RenderTaskLocation::Dynamic { .. } - | RenderTaskLocation::CacheRequest { .. } - | RenderTaskLocation::Unallocated { .. } => { - // Intermediate surfaces can't be render roots, they should always - // be a dependency of a render root. - panic!("bug: invalid root"); - } - } + /// Assign the render tasks from the tree rooted at root_task to render passes and + /// append them to the `passes` vector so that the passes that we depend on end up + /// _earlier_ in the pass list. + fn generate_passes_impl( + &self, + root_tasks: &[RenderTaskId], + screen_size: DeviceIntSize, + gpu_supports_fast_clears: bool, + for_main_framebuffer: bool, + passes: &mut Vec<RenderPass>, + ) { + // We recursively visit tasks from the roots (main and cached render tasks), to figure out + // which ones affect the frame and which passes they should be assigned to. + // + // We track the maximum depth of each task (how far it is from the roots) as well as the total + // maximum depth of the graph to determine each tasks' pass index. In a nutshell, depth 0 is + // for the last render pass (for example the main framebuffer), while the highest depth + // corresponds to the first pass. + + fn assign_task_depth( + tasks: &[RenderTask], + task_id: RenderTaskId, + task_depth: i32, + task_max_depths: &mut [i32], + max_depth: &mut i32, + ) { + *max_depth = std::cmp::max(*max_depth, task_depth); + + let task_max_depth = &mut task_max_depths[task_id.index as usize]; + if task_depth > *task_max_depth { + *task_max_depth = task_depth; + } else { + // If this task has already been processed at a larger depth, + // there is no need to process it again. + return; } - assert_eq!(roots.len(), self.roots.len()); - - // Now resolve those dependencies on persistent targets and add them - // as a render task dependency. - for (task_id, target_id) in self.target_inputs.drain(..) { - match roots.get(&target_id) { - Some(root_task_id) => { - graph.tasks[task_id.index as usize].children.push(*root_task_id); - self.roots.remove(root_task_id); - } - None => { - println!("WARN: {:?} depends on root {:?} but it has no tasks!", - task_id, - target_id, - ); - } - } + + let task = &tasks[task_id.index as usize]; + for child in &task.children { + assign_task_depth( + tasks, + *child, + task_depth + 1, + task_max_depths, + max_depth, + ); } } - // Two traversals of the graph are required. The first pass determines how many passes - // are required, and assigns render tasks a pass to be drawn on. The second pass determines - // when the last time a render task is used as an input, and assigns what pass the surface - // backing that render task can be freed (the surface is then returned to the render target - // pool and may be aliased / reused during subsequent passes). - - let mut pass_count = 0; - - // Traverse each root, and assign `render_on` for each task and count number of required passes - for root_id in &self.roots { - assign_render_pass( - *root_id, - PassId(0), - &mut graph, - &mut pass_count, + // The maximum depth of each task. Values that are still equal to -1 after recursively visiting + // the nodes correspond to tasks that don't contribute to the frame. + let mut task_max_depths = vec![-1; self.tasks.len()]; + let mut max_depth = 0; + + for root_task in root_tasks { + assign_task_depth( + &self.tasks, + *root_task, + 0, + &mut task_max_depths, + &mut max_depth, ); } - // Determine which pass each task can be freed on, which depends on which is - // the last task that has this as an input. - for i in 0 .. graph.tasks.len() { - let task_id = RenderTaskId { index: i as u16 }; - assign_free_pass( - task_id, - &mut self.child_task_buffer, - &mut graph, - ); + let offset = passes.len(); + + passes.reserve(max_depth as usize + 1); + for _ in 0..max_depth { + passes.alloc().init(RenderPass::new_off_screen(screen_size, gpu_supports_fast_clears)); } - // Construct passes array for tasks to be assigned to below - for _ in 0 .. pass_count+1 { - graph.passes.push(Pass { - task_ids: Vec::new(), - sub_passes: Vec::new(), - textures_to_invalidate: Vec::new(), - }); + if for_main_framebuffer { + passes.alloc().init(RenderPass::new_main_framebuffer(screen_size, gpu_supports_fast_clears)); + } else { + passes.alloc().init(RenderPass::new_off_screen(screen_size, gpu_supports_fast_clears)); } - // Assign tasks to each pass based on their `render_on` attribute - for (index, task) in graph.tasks.iter().enumerate() { - if task.kind.is_a_rendering_operation() { - let id = RenderTaskId { index: index as u16 }; - graph.passes[task.render_on.0].task_ids.push(id); + // Assign tasks to their render passes. + for task_index in 0..self.tasks.len() { + if task_max_depths[task_index] < 0 { + // The task wasn't visited, it means it doesn't contribute to this frame. + continue; } + let pass_index = offset + (max_depth - task_max_depths[task_index]) as usize; + let task_id = RenderTaskId { + index: task_index as u32, + #[cfg(debug_assertions)] + frame_id: self.frame_id, + }; + let task = &self.tasks[task_index]; + passes[pass_index as usize].add_render_task( + task_id, + task.get_dynamic_size(), + task.target_kind(), + &task.location, + ); } + } - // At this point, tasks are assigned to each dependency pass. Now we - // can go through each pass and create sub-passes, assigning each task - // to a target and destination rect. - assert!(self.active_surfaces.is_empty()); - - for (pass_id, pass) in graph.passes.iter_mut().enumerate().rev() { - assert!(self.textures_to_free.is_empty()); - - for task_id in &pass.task_ids { - let task = &mut graph.tasks[task_id.index as usize]; - - match task.location { - RenderTaskLocation::Unallocated { size } => { - let mut location = None; - let kind = task.kind.target_kind(); - - // Allow this render task to use a shared surface target if it - // is freed straight after this pass. Tasks that must remain - // allocated for inputs on subsequent passes are always assigned - // to a standalone surface, to simplify lifetime management of - // render targets. - - let can_use_shared_surface = - task.render_on == PassId(task.free_after.0 + 1); - - if can_use_shared_surface { - // If we can use a shared surface, step through the existing shared - // surfaces for this subpass, and see if we can allocate the task - // to one of these targets. - for sub_pass in &mut pass.sub_passes { - if let SubPassSurface::Dynamic { texture_id, ref mut used_rect, .. } = sub_pass.surface { - let surface = self.active_surfaces.get_mut(&texture_id).unwrap(); - if let Some(p) = surface.alloc_rect(size, kind, true) { - location = Some((texture_id, p)); - *used_rect = used_rect.union(&DeviceIntRect::new(p, size)); - sub_pass.task_ids.push(*task_id); - break; - } - } - } - } - - if location.is_none() { - // If it wasn't possible to allocate the task to a shared surface, get a new - // render target from the resource cache pool/ - - // If this is a really large task, don't bother allocating it as a potential - // shared surface for other tasks. - - let can_use_shared_surface = can_use_shared_surface && - size.width <= MAX_SHARED_SURFACE_SIZE && - size.height <= MAX_SHARED_SURFACE_SIZE; - - let surface_size = if can_use_shared_surface { - DeviceIntSize::new( - MAX_SHARED_SURFACE_SIZE, - MAX_SHARED_SURFACE_SIZE, - ) - } else { - // Round up size here to avoid constant re-allocs during resizing - DeviceIntSize::new( - (size.width + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK, - (size.height + TEXTURE_DIMENSION_MASK) & !TEXTURE_DIMENSION_MASK, - ) - }; - - let format = match kind { - RenderTargetKind::Color => ImageFormat::RGBA8, - RenderTargetKind::Alpha => ImageFormat::R8, - }; - - // Get render target of appropriate size and format from resource cache - let texture_id = resource_cache.get_or_create_render_target_from_pool( - surface_size, - format, - ); - - // Allocate metadata we need about this surface while it's active - let mut surface = Surface { - kind, - allocator: GuillotineAllocator::new(Some(surface_size)), - is_shared: can_use_shared_surface, - }; - - // Allocation of the task must fit in this new surface! - let p = surface.alloc_rect( - size, - kind, - can_use_shared_surface, - ).expect("bug: alloc must succeed!"); - - location = Some((texture_id, p)); - - // Store the metadata about this newly active surface. We should never - // get a target surface with the same texture_id as a currently active surface. - let _prev_surface = self.active_surfaces.insert(texture_id, surface); - assert!(_prev_surface.is_none()); - - // Store some information about surface allocations if in test mode - #[cfg(test)] - { - graph.surface_count += 1; - graph.unique_surfaces.insert(texture_id); - } - - // Add the target as a new subpass for this render pass. - pass.sub_passes.push(SubPass { - surface: SubPassSurface::Dynamic { - texture_id, - target_kind: kind, - used_rect: DeviceIntRect::new(p, size), - }, - task_ids: vec![*task_id], - }); - } - - // By now, we must have allocated a surface and rect for this task, so assign it! - assert!(location.is_some()); - task.location = RenderTaskLocation::Dynamic { - texture_id: location.unwrap().0, - rect: DeviceIntRect::new(location.unwrap().1, size), - }; - } - RenderTaskLocation::Static { ref surface, .. } => { - // No need to allocate for this surface, since it's a persistent - // target. Instead, just create a new sub-pass for it. - pass.sub_passes.push(SubPass { - surface: SubPassSurface::Persistent { - surface: surface.clone(), - }, - task_ids: vec![*task_id], - }); - } - RenderTaskLocation::CacheRequest { .. } => { - // No need to allocate nor to create a sub-path for read-only locations. - } - RenderTaskLocation::Dynamic { .. } => { - // Dynamic tasks shouldn't be allocated by this point - panic!("bug: encountered an already allocated task"); - } - } - - // Return the shared surfaces from this pass - let task = &graph.tasks[task_id.index as usize]; - for child_id in &task.children { - let child_task = &graph.tasks[child_id.index as usize]; - match child_task.location { - RenderTaskLocation::Unallocated { .. } => panic!("bug: must be allocated"), - RenderTaskLocation::Dynamic { texture_id, .. } => { - // If this task can be freed after this pass, include it in the - // unique set of textures to be returned to the render target pool below. - if child_task.free_after == PassId(pass_id) { - self.textures_to_free.insert(texture_id); - } - } - RenderTaskLocation::Static { .. } => {} - RenderTaskLocation::CacheRequest { .. } => {} - } - } + /// Resolve conflicts between the generated passes and the limitiations of our target + /// allocation scheme. + /// + /// The render task graph operates with a ping-pong target allocation scheme where + /// a set of targets is written to by even passes and a different set of targets is + /// written to by odd passes. + /// Since tasks cannot read and write the same target, we can run into issues if a + /// task pass in N + 2 reads the result of a task in pass N. + /// To avoid such cases have to insert blit tasks to copy the content of the task + /// into pass N + 1 which is readable by pass N + 2. + /// + /// In addition, allocated rects of pass N are currently not tracked and can be + /// overwritten by allocations in later passes on the same target, unless the task + /// has been marked for saving, which perserves the allocated rect until the end of + /// the frame. This is a big hammer, hopefully we won't need to mark many passes + /// for saving. A better solution would be to track allocations through the entire + /// graph, there is a prototype of that in https://github.com/nical/toy-render-graph/ + fn resolve_target_conflicts(&mut self, passes: &mut [RenderPass]) { + // Keep track of blit tasks we inserted to avoid adding several blits for the same + // task. + let mut task_redirects = vec![None; self.tasks.len()]; + + let mut task_passes = vec![-1; self.tasks.len()]; + for pass_index in 0..passes.len() { + for task in &passes[pass_index].tasks { + task_passes[task.index as usize] = pass_index as i32; } + } - // Return no longer used textures to the pool, so that they can be reused / aliased - // by later passes. - for texture_id in self.textures_to_free.drain() { - resource_cache.return_render_target_to_pool(texture_id); - self.active_surfaces.remove(&texture_id).unwrap(); - pass.textures_to_invalidate.push(texture_id); + for task_index in 0..self.tasks.len() { + if task_passes[task_index] < 0 { + // The task doesn't contribute to this frame. + continue; } - } - // By now, all surfaces that were borrowed from the render target pool must - // be returned to the resource cache, or we are leaking intermediate surfaces! - assert!(self.active_surfaces.is_empty()); - - // Each task is now allocated to a surface and target rect. Write that to the - // GPU blocks and task_data. After this point, the graph is returned and is - // considered to be immutable for the rest of the frame building process. - - for task in &mut graph.tasks { - // First check whether the render task texture and uv rects are managed - // externally. This is the case for image tasks and cached tasks. In both - // cases it results in a finding the information in the texture cache. - let cache_item = if let Some(ref cache_handle) = task.cache_handle { - Some(resolve_cached_render_task( - cache_handle, - resource_cache, - )) - } else if let RenderTaskKind::Image(request) = &task.kind { - Some(resolve_image( - *request, - resource_cache, - gpu_cache, - deferred_resolves, - )) - } else { - // General case (non-cached non-image tasks). - None - }; + let pass_index = task_passes[task_index]; + + // Go through each dependency and check whether they belong + // to a pass that uses the same targets and/or are more than + // one pass behind. + for nth_child in 0..self.tasks[task_index].children.len() { + let child_task_index = self.tasks[task_index].children[nth_child].index as usize; + let child_pass_index = task_passes[child_task_index]; + + if child_pass_index == pass_index - 1 { + // This should be the most common case. + continue; + } - if let Some(cache_item) = cache_item { - // Update the render task even if the item is invalid. - // We'll handle it later and it's easier to not have to - // deal with unexpected location variants like - // RenderTaskLocation::CacheRequest when we do. - let source = cache_item.texture_id; - task.uv_rect_handle = cache_item.uv_rect_handle; - task.location = RenderTaskLocation::Static { - surface: StaticRenderTaskSurface::ReadOnly { source }, - rect: cache_item.uv_rect, + // TODO: Picture tasks don't support having their dependency tasks redirected. + // Pictures store their respective render task(s) on their SurfaceInfo. + // We cannot blit the picture task here because we would need to update the + // surface's render tasks, but we don't have access to that info here. + // Also a surface may be expecting a picture task and not a blit task, so + // even if we could update the surface's render task(s), it might cause other issues. + // For now we mark the task to be saved rather than trying to redirect to a blit task. + let task_is_picture = if let RenderTaskKind::Picture(..) = self.tasks[task_index].kind { + true + } else { + false }; - } - // Give the render task an opportunity to add any - // information to the GPU cache, if appropriate. - let target_rect = task.get_target_rect(); - task.write_gpu_blocks( - target_rect, - gpu_cache, - ); + if child_pass_index % 2 != pass_index % 2 || task_is_picture { + // The tasks and its dependency aren't on the same targets, + // but the dependency needs to be kept alive. + self.tasks[child_task_index].mark_for_saving(); + continue; + } - graph.task_data.push( - task.kind.write_task_data(target_rect) - ); - } + if let Some(blit_id) = task_redirects[child_task_index] { + // We already resolved a similar conflict with a blit task, + // reuse the same blit instead of creating a new one. + self.tasks[task_index].children[nth_child] = blit_id; - graph - } -} + // Mark for saving if the blit is more than pass appart from + // our task. + if child_pass_index < pass_index - 2 { + self.tasks[blit_id.index as usize].mark_for_saving(); + } -impl RenderTaskGraph { - /// Print the render task graph to console - #[allow(dead_code)] - pub fn print( - &self, - ) { - println!("-- RenderTaskGraph --"); - - for (i, task) in self.tasks.iter().enumerate() { - println!("Task {}: render_on={} free_after={} {:?}", - i, - task.render_on.0, - task.free_after.0, - task.kind.as_str(), - ); - } + continue; + } + + // Our dependency is an even number of passes behind, need + // to insert a blit to ensure we don't read and write from + // the same target. - for (p, pass) in self.passes.iter().enumerate() { - println!("Pass {}:", p); + let child_task_id = RenderTaskId { + index: child_task_index as u32, + #[cfg(debug_assertions)] + frame_id: self.frame_id, + }; - for (s, sub_pass) in pass.sub_passes.iter().enumerate() { - println!("\tSubPass {}: {:?}", - s, - sub_pass.surface, + let mut blit = RenderTask::new_blit( + self.tasks[child_task_index].location.size(), + BlitSource::RenderTask { task_id: child_task_id }, ); - for task_id in &sub_pass.task_ids { - println!("\t\tTask {:?}", task_id.index); + // Mark for saving if the blit is more than pass appart from + // our task. + if child_pass_index < pass_index - 2 { + blit.mark_for_saving(); } + + let blit_id = RenderTaskId { + index: self.tasks.len() as u32, + #[cfg(debug_assertions)] + frame_id: self.frame_id, + }; + + self.tasks.alloc().init(blit); + + passes[child_pass_index as usize + 1].tasks.push(blit_id); + + self.tasks[task_index].children[nth_child] = blit_id; + task_redirects[child_task_index] = Some(blit_id); } } } - pub fn resolve_location( - &self, - task_id: impl Into<Option<RenderTaskId>>, - gpu_cache: &GpuCache, - ) -> Option<(GpuCacheAddress, TextureSource)> { - self.resolve_impl(task_id.into()?, gpu_cache) + pub fn get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress { + #[cfg(all(debug_assertions, not(feature = "replay")))] + debug_assert_eq!(self.frame_id, id.frame_id); + RenderTaskAddress(id.index as u16) } - fn resolve_impl( - &self, - task_id: RenderTaskId, - gpu_cache: &GpuCache, - ) -> Option<(GpuCacheAddress, TextureSource)> { - let task = &self[task_id]; - let texture_source = task.get_texture_source(); - - if let TextureSource::Invalid = texture_source { - return None; + pub fn write_task_data(&mut self) { + profile_scope!("write_task_data"); + for task in &self.tasks { + self.task_data.push(task.write_task_data()); } - - let uv_address = task.get_texture_address(gpu_cache); - - Some((uv_address, texture_source)) } - - /// Return the surface and texture counts, used for testing - #[cfg(test)] - pub fn surface_counts(&self) -> (usize, usize) { - (self.surface_count, self.unique_surfaces.len()) + pub fn save_target(&mut self) -> SavedTargetIndex { + let id = self.next_saved; + self.next_saved.0 += 1; + id } - /// Return current frame id, used for validation #[cfg(debug_assertions)] pub fn frame_id(&self) -> FrameId { self.frame_id } } -/// Batching uses index access to read information about tasks impl std::ops::Index<RenderTaskId> for RenderTaskGraph { type Output = RenderTask; fn index(&self, id: RenderTaskId) -> &RenderTask { + #[cfg(all(debug_assertions, not(feature = "replay")))] + debug_assert_eq!(self.frame_id, id.frame_id); &self.tasks[id.index as usize] } } -/// Recursive helper to assign pass that a task should render on -fn assign_render_pass( - id: RenderTaskId, - pass: PassId, - graph: &mut RenderTaskGraph, - pass_count: &mut usize, -) { - let task = &mut graph.tasks[id.index as usize]; - - // No point in recursing into paths in the graph if this task already - // has been set to draw after this pass. - if task.render_on > pass { - return; +impl std::ops::IndexMut<RenderTaskId> for RenderTaskGraph { + fn index_mut(&mut self, id: RenderTaskId) -> &mut RenderTask { + #[cfg(all(debug_assertions, not(feature = "replay")))] + debug_assert_eq!(self.frame_id, id.frame_id); + &mut self.tasks[id.index as usize] } +} - let next_pass = if task.kind.is_a_rendering_operation() { - // Keep count of number of passes needed - *pass_count = pass.0.max(*pass_count); - PassId(pass.0 + 1) - } else { - // If the node is not a rendering operation, it doesn't create a - // render pass, so we don't increment the pass count. - // For now we expect non-rendering nodes to be leafs of the graph. - // We don't strictly depend on it but it simplifies the mental model. - debug_assert!(task.children.is_empty()); - pass - }; +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub struct RenderTaskId { + pub index: u32, - // A task should be rendered on the earliest pass in the dependency - // graph that it's required. Using max here ensures the correct value - // in the presence of multiple paths to this task from the root(s). - task.render_on = task.render_on.max(pass); - - // TODO(gw): Work around the borrowck - maybe we could structure the dependencies - // storage better, to avoid this? - let mut child_task_ids: SmallVec<[RenderTaskId; 8]> = SmallVec::new(); - child_task_ids.extend_from_slice(&task.children); - - for child_id in child_task_ids { - assign_render_pass( - child_id, - next_pass, - graph, - pass_count, - ); - } + #[cfg(debug_assertions)] + #[cfg_attr(feature = "replay", serde(default = "FrameId::first"))] + frame_id: FrameId, } -fn assign_free_pass( - id: RenderTaskId, - child_task_buffer: &mut Vec<RenderTaskId>, - graph: &mut RenderTaskGraph, -) { - let task = &graph.tasks[id.index as usize]; - let render_on = task.render_on; - debug_assert!(child_task_buffer.is_empty()); - - // TODO(gw): Work around the borrowck - maybe we could structure the dependencies - // storage better, to avoid this? - child_task_buffer.extend_from_slice(&task.children); - - for child_id in child_task_buffer.drain(..) { - let child_task = &mut graph.tasks[child_id.index as usize]; - - // Each dynamic child task can free its backing surface after the last - // task that references it as an input. Using min here ensures the - // safe time to free this surface in the presence of multiple paths - // to this task from the root(s). - match child_task.location { - RenderTaskLocation::CacheRequest { .. } => {} - RenderTaskLocation::Static { .. } => { - // never get freed anyway, so can leave untouched - // (could validate that they remain at PassId::MIN) - } - RenderTaskLocation::Unallocated { .. } => { - child_task.free_after = child_task.free_after.min(render_on); - } - RenderTaskLocation::Dynamic { .. } => { - panic!("bug: should not be allocated yet"); - } +#[derive(Debug)] +pub struct RenderTaskGraphCounters { + tasks_len: usize, + task_data_len: usize, + cacheable_render_tasks_len: usize, +} + +impl RenderTaskGraphCounters { + pub fn new() -> Self { + RenderTaskGraphCounters { + tasks_len: 0, + task_data_len: 0, + cacheable_render_tasks_len: 0, } } } +impl RenderTaskId { + pub const INVALID: RenderTaskId = RenderTaskId { + index: u32::MAX, + #[cfg(debug_assertions)] + frame_id: FrameId::INVALID, + }; +} + +/// Contains the set of `RenderTarget`s specific to the kind of pass. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum RenderPassKind { + /// The final pass to the main frame buffer, where we have a single color + /// target for display to the user. + MainFramebuffer { + main_target: ColorRenderTarget, + }, + /// An intermediate pass, where we may have multiple targets. + OffScreen { + alpha: RenderTargetList<AlphaRenderTarget>, + color: RenderTargetList<ColorRenderTarget>, + texture_cache: FastHashMap<(CacheTextureId, usize), TextureCacheRenderTarget>, + picture_cache: Vec<PictureCacheTarget>, + }, +} + /// A render pass represents a set of rendering operations that don't depend on one /// another. /// @@ -811,35 +451,90 @@ fn assign_free_pass( #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct RenderPass { - /// The subpasses that describe targets being rendered to in this pass - pub alpha: RenderTargetList<AlphaRenderTarget>, - pub color: RenderTargetList<ColorRenderTarget>, - pub texture_cache: FastHashMap<CacheTextureId, TextureCacheRenderTarget>, - pub picture_cache: Vec<PictureCacheTarget>, - pub textures_to_invalidate: Vec<CacheTextureId>, + /// The kind of pass, as well as the set of targets associated with that + /// kind of pass. + pub kind: RenderPassKind, + /// The set of tasks to be performed in this pass, as indices into the + /// `RenderTaskGraph`. + pub tasks: Vec<RenderTaskId>, + /// Screen size in device pixels - used for opaque alpha batch break threshold. + pub screen_size: DeviceIntSize, } impl RenderPass { + /// Creates a pass for the main framebuffer. There is only one of these, and + /// it is always the last pass. + pub fn new_main_framebuffer( + screen_size: DeviceIntSize, + gpu_supports_fast_clears: bool, + ) -> Self { + let main_target = ColorRenderTarget::new(screen_size, gpu_supports_fast_clears); + RenderPass { + kind: RenderPassKind::MainFramebuffer { + main_target, + }, + tasks: vec![], + screen_size, + } + } + /// Creates an intermediate off-screen pass. - pub fn new(src: &Pass) -> Self { + pub fn new_off_screen( + screen_size: DeviceIntSize, + gpu_supports_fast_clears: bool, + ) -> Self { RenderPass { - color: RenderTargetList::new( - ImageFormat::RGBA8, - ), - alpha: RenderTargetList::new( - ImageFormat::R8, - ), - texture_cache: FastHashMap::default(), - picture_cache: Vec::new(), - textures_to_invalidate: src.textures_to_invalidate.clone(), + kind: RenderPassKind::OffScreen { + color: RenderTargetList::new( + screen_size, + ImageFormat::RGBA8, + gpu_supports_fast_clears, + ), + alpha: RenderTargetList::new( + screen_size, + ImageFormat::R8, + gpu_supports_fast_clears, + ), + texture_cache: FastHashMap::default(), + picture_cache: Vec::new(), + }, + tasks: vec![], + screen_size, + } + } + + /// Adds a task to this pass. + pub fn add_render_task( + &mut self, + task_id: RenderTaskId, + size: DeviceIntSize, + target_kind: RenderTargetKind, + location: &RenderTaskLocation, + ) { + if let RenderPassKind::OffScreen { ref mut color, ref mut alpha, .. } = self.kind { + // If this will be rendered to a dynamically-allocated region on an + // off-screen render target, update the max-encountered size. We don't + // need to do this for things drawn to the texture cache, since those + // don't affect our render target allocation. + if location.is_dynamic() { + let max_size = match target_kind { + RenderTargetKind::Color => &mut color.max_dynamic_size, + RenderTargetKind::Alpha => &mut alpha.max_dynamic_size, + }; + max_size.width = cmp::max(max_size.width, size.width); + max_size.height = cmp::max(max_size.height, size.height); + } } + + self.tasks.push(task_id); } } // Dump an SVG visualization of the render graph for debugging purposes -#[cfg(feature = "capture")] +#[allow(dead_code)] pub fn dump_render_tasks_as_svg( render_tasks: &RenderTaskGraph, + passes: &[RenderPass], output: &mut dyn std::io::Write, ) -> std::io::Result<()> { use svg_fmt::*; @@ -864,10 +559,10 @@ pub fn dump_render_tasks_as_svg( size: Text, } - for pass in render_tasks.passes.iter().rev() { + for pass in passes { let mut layout = VerticalLayout::new(x, margin, node_width); - for task_id in &pass.task_ids { + for task_id in &pass.tasks { let task_index = task_id.index as usize; let task = &render_tasks.tasks[task_index]; @@ -876,7 +571,8 @@ pub fn dump_render_tasks_as_svg( let tx = rect.x + rect.w / 2.0; let ty = rect.y + 10.0; - let label = text(tx, ty, format!("{}", task.kind.as_str())); + let saved = if task.saved_index.is_some() { " (Saved)" } else { "" }; + let label = text(tx, ty, format!("{}{}", task.kind.as_str(), saved)); let size = text(tx, ty + 12.0, format!("{:?}", task.location.size())); nodes[task_index] = Some(Node { rect, label, size }); @@ -1017,266 +713,175 @@ fn dump_task_dependency_link( } } -/// Construct a picture cache render task location for testing -#[cfg(test)] -fn pc_target( - surface_id: u64, - tile_x: i32, - tile_y: i32, -) -> RenderTaskLocation { - use crate::{ - composite::{NativeSurfaceId, NativeTileId}, - picture::ResolvedSurfaceTexture, - }; - - let width = 512; - let height = 512; - - RenderTaskLocation::Static { - surface: StaticRenderTaskSurface::PictureCache { - surface: ResolvedSurfaceTexture::Native { - id: NativeTileId { - surface_id: NativeSurfaceId(surface_id), - x: tile_x, - y: tile_y, - }, - size: DeviceIntSize::new(width, height), - }, - }, - rect: DeviceIntSize::new(width, height).into(), - } -} - #[cfg(test)] -impl RenderTaskGraphBuilder { - fn test_expect( - mut self, - pass_count: usize, - total_surface_count: usize, - unique_surfaces: &[(i32, i32, ImageFormat)], - ) { - use crate::render_backend::FrameStamp; - use api::{DocumentId, IdNamespace}; - - let mut rc = ResourceCache::new_for_testing(); - let mut gc = GpuCache::new(); - - let mut frame_stamp = FrameStamp::first(DocumentId::new(IdNamespace(1), 1)); - frame_stamp.advance(); - gc.prepare_for_frames(); - gc.begin_frame(frame_stamp); - - let g = self.end_frame(&mut rc, &mut gc, &mut Vec::new()); - g.print(); - - assert_eq!(g.passes.len(), pass_count); - assert_eq!(g.surface_counts(), (total_surface_count, unique_surfaces.len())); - - rc.validate_surfaces(unique_surfaces); - } -} - -/// Construct a testing render task with given location +use euclid::{size2, rect}; #[cfg(test)] -fn task_location(location: RenderTaskLocation) -> RenderTask { - RenderTask::new_test( - location, - RenderTargetKind::Color, - ) -} +use smallvec::SmallVec; -/// Construct a dynamic render task location for testing #[cfg(test)] -fn task_dynamic(size: i32) -> RenderTask { - RenderTask::new_test( - RenderTaskLocation::Unallocated { size: DeviceIntSize::new(size, size) }, - RenderTargetKind::Color, - ) +fn dyn_location(w: i32, h: i32) -> RenderTaskLocation { + RenderTaskLocation::Dynamic(None, size2(w, h)) } #[test] -fn fg_test_1() { - // Test that a root target can be used as an input for readbacks - // This functionality isn't currently used, but will be in future. +fn diamond_task_graph() { + // A simple diamon shaped task graph. + // + // [b1] + // / \ + // [a] [main_pic] + // \ / + // [b2] - let mut gb = RenderTaskGraphBuilder::new(); + let color = RenderTargetKind::Color; - let root_target = pc_target(0, 0, 0); + let counters = RenderTaskGraphCounters::new(); + let mut tasks = RenderTaskGraph::new(FrameId::first(), &counters); - let root = gb.add().init(task_location(root_target.clone())); + let a = tasks.add().init(RenderTask::new_test(color, dyn_location(640, 640), SmallVec::new())); + let b1 = tasks.add().init(RenderTask::new_test(color, dyn_location(320, 320), smallvec![a])); + let b2 = tasks.add().init(RenderTask::new_test(color, dyn_location(320, 320), smallvec![a])); - let readback = gb.add().init(task_dynamic(100)); - gb.add_dependency(readback, root); + let main_pic = tasks.add().init(RenderTask::new_test( + color, + RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)), + smallvec![b1, b2], + )); - let mix_blend_content = gb.add().init(task_dynamic(50)); + let initial_number_of_tasks = tasks.tasks.len(); - let content = gb.add().init(task_location(root_target)); - gb.add_dependency(content, readback); - gb.add_dependency(content, mix_blend_content); + let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true); - gb.test_expect(3, 1, &[ - (2048, 2048, ImageFormat::RGBA8), - ]); -} + // We should not have added any blits. + assert_eq!(tasks.tasks.len(), initial_number_of_tasks); -#[test] -fn fg_test_2() { - // Test that texture cache tasks can be added and scheduled correctly as inputs - // to picture cache tasks. Ensure that no dynamic surfaces are allocated from the - // target pool in this case. + assert_eq!(passes.len(), 3); + assert_eq!(passes[0].tasks, vec![a]); - let mut gb = RenderTaskGraphBuilder::new(); + assert_eq!(passes[1].tasks.len(), 2); + assert!(passes[1].tasks.contains(&b1)); + assert!(passes[1].tasks.contains(&b2)); - let pc_root = gb.add().init(task_location(pc_target(0, 0, 0))); + assert_eq!(passes[2].tasks, vec![main_pic]); +} - let tc_0 = StaticRenderTaskSurface::TextureCache { - texture: CacheTextureId(0), - target_kind: RenderTargetKind::Color, - }; +#[test] +fn blur_task_graph() { + // This test simulates a complicated shadow stack effect with target allocation + // conflicts to resolve. - let tc_1 = StaticRenderTaskSurface::TextureCache { - texture: CacheTextureId(1), - target_kind: RenderTargetKind::Color, - }; + let color = RenderTargetKind::Color; - gb.add_target_input( - pc_root, - tc_0.clone(), - ); + let counters = RenderTaskGraphCounters::new(); + let mut tasks = RenderTaskGraph::new(FrameId::first(), &counters); - gb.add_target_input( - pc_root, - tc_1.clone(), - ); + let pic = tasks.add().init(RenderTask::new_test(color, dyn_location(640, 640), SmallVec::new())); + let scale1 = tasks.add().init(RenderTask::new_test(color, dyn_location(320, 320), smallvec![pic])); + let scale2 = tasks.add().init(RenderTask::new_test(color, dyn_location(160, 160), smallvec![scale1])); + let scale3 = tasks.add().init(RenderTask::new_test(color, dyn_location(80, 80), smallvec![scale2])); + let scale4 = tasks.add().init(RenderTask::new_test(color, dyn_location(40, 40), smallvec![scale3])); - gb.add().init( - task_location(RenderTaskLocation::Static { surface: tc_0.clone(), rect: DeviceIntSize::new(128, 128).into() }), - ); + let vblur1 = tasks.add().init(RenderTask::new_test(color, dyn_location(40, 40), smallvec![scale4])); + let hblur1 = tasks.add().init(RenderTask::new_test(color, dyn_location(40, 40), smallvec![vblur1])); - gb.add().init( - task_location(RenderTaskLocation::Static { surface: tc_1.clone(), rect: DeviceIntSize::new(128, 128).into() }), - ); + let vblur2 = tasks.add().init(RenderTask::new_test(color, dyn_location(40, 40), smallvec![scale4])); + let hblur2 = tasks.add().init(RenderTask::new_test(color, dyn_location(40, 40), smallvec![vblur2])); - gb.test_expect(2, 0, &[]); -} + // Insert a task that is an even number of passes away from its dependency. + // This means the source and destination are on the same target and we have to resolve + // this conflict by automatically inserting a blit task. + let vblur3 = tasks.add().init(RenderTask::new_test(color, dyn_location(80, 80), smallvec![scale3])); + let hblur3 = tasks.add().init(RenderTask::new_test(color, dyn_location(80, 80), smallvec![vblur3])); -#[test] -fn fg_test_3() { - // Test that small targets are allocated in a shared surface, and that large - // tasks are allocated in a rounded up texture size. + // Insert a task that is an odd number > 1 of passes away from its dependency. + // This should force us to mark the dependency "for saving" to keep its content valid + // until the task can access it. + let vblur4 = tasks.add().init(RenderTask::new_test(color, dyn_location(160, 160), smallvec![scale2])); + let hblur4 = tasks.add().init(RenderTask::new_test(color, dyn_location(160, 160), smallvec![vblur4])); - let mut gb = RenderTaskGraphBuilder::new(); + let main_pic = tasks.add().init(RenderTask::new_test( + color, + RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)), + smallvec![hblur1, hblur2, hblur3, hblur4], + )); - let pc_root = gb.add().init(task_location(pc_target(0, 0, 0))); + let initial_number_of_tasks = tasks.tasks.len(); - let child_pic_0 = gb.add().init(task_dynamic(128)); - let child_pic_1 = gb.add().init(task_dynamic(3000)); + let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true); - gb.add_dependency(pc_root, child_pic_0); - gb.add_dependency(pc_root, child_pic_1); + // We should have added a single blit task. + assert_eq!(tasks.tasks.len(), initial_number_of_tasks + 1); - gb.test_expect(2, 2, &[ - (2048, 2048, ImageFormat::RGBA8), - (3072, 3072, ImageFormat::RGBA8), - ]); -} + // vblur3's dependency to scale3 should be replaced by a blit. + let blit = tasks[vblur3].children[0]; + assert!(blit != scale3); -#[test] -fn fg_test_4() { - // Test that for a simple dependency chain of tasks, that render - // target surfaces are aliased and reused between passes where possible. + match tasks[blit].kind { + RenderTaskKind::Blit(..) => {} + _ => { panic!("This should be a blit task."); } + } - let mut gb = RenderTaskGraphBuilder::new(); + assert_eq!(passes.len(), 8); - let pc_root = gb.add().init(task_location(pc_target(0, 0, 0))); + assert_eq!(passes[0].tasks, vec![pic]); + assert_eq!(passes[1].tasks, vec![scale1]); + assert_eq!(passes[2].tasks, vec![scale2]); + assert_eq!(passes[3].tasks, vec![scale3]); - let child_pic_0 = gb.add().init(task_dynamic(128)); - let child_pic_1 = gb.add().init(task_dynamic(128)); - let child_pic_2 = gb.add().init(task_dynamic(128)); + assert_eq!(passes[4].tasks.len(), 2); + assert!(passes[4].tasks.contains(&scale4)); + assert!(passes[4].tasks.contains(&blit)); - gb.add_dependency(pc_root, child_pic_0); - gb.add_dependency(child_pic_0, child_pic_1); - gb.add_dependency(child_pic_1, child_pic_2); + assert_eq!(passes[5].tasks.len(), 4); + assert!(passes[5].tasks.contains(&vblur1)); + assert!(passes[5].tasks.contains(&vblur2)); + assert!(passes[5].tasks.contains(&vblur3)); + assert!(passes[5].tasks.contains(&vblur4)); - gb.test_expect(4, 3, &[ - (2048, 2048, ImageFormat::RGBA8), - (2048, 2048, ImageFormat::RGBA8), - ]); -} + assert_eq!(passes[6].tasks.len(), 4); + assert!(passes[6].tasks.contains(&hblur1)); + assert!(passes[6].tasks.contains(&hblur2)); + assert!(passes[6].tasks.contains(&hblur3)); + assert!(passes[6].tasks.contains(&hblur4)); -#[test] -fn fg_test_5() { - // Test that a task that is used as an input by direct parent and also - // distance ancestor are scheduled correctly, and allocates the correct - // number of passes, taking advantage of surface reuse / aliasing where feasible. - - let mut gb = RenderTaskGraphBuilder::new(); - - let pc_root = gb.add().init(task_location(pc_target(0, 0, 0))); - - let child_pic_0 = gb.add().init(task_dynamic(128)); - let child_pic_1 = gb.add().init(task_dynamic(64)); - let child_pic_2 = gb.add().init(task_dynamic(32)); - let child_pic_3 = gb.add().init(task_dynamic(16)); - - gb.add_dependency(pc_root, child_pic_0); - gb.add_dependency(child_pic_0, child_pic_1); - gb.add_dependency(child_pic_1, child_pic_2); - gb.add_dependency(child_pic_2, child_pic_3); - gb.add_dependency(pc_root, child_pic_3); - - gb.test_expect(5, 4, &[ - (256, 256, ImageFormat::RGBA8), - (2048, 2048, ImageFormat::RGBA8), - (2048, 2048, ImageFormat::RGBA8), - ]); + assert_eq!(passes[7].tasks, vec![main_pic]); + + // See vblur4's comment above. + assert!(tasks[scale2].saved_index.is_some()); } #[test] -fn fg_test_6() { - // Test that a task that is used as an input dependency by two parent - // tasks is correctly allocated and freed. - - let mut gb = RenderTaskGraphBuilder::new(); - - let pc_root_1 = gb.add().init(task_location(pc_target(0, 0, 0))); - let pc_root_2 = gb.add().init(task_location(pc_target(0, 1, 0))); +fn culled_tasks() { + // This test checks that tasks that do not contribute to the frame don't appear in the + // generated passes. - let child_pic = gb.add().init(task_dynamic(128)); + let color = RenderTargetKind::Color; - gb.add_dependency(pc_root_1, child_pic); - gb.add_dependency(pc_root_2, child_pic); - - gb.test_expect(2, 1, &[ - (2048, 2048, ImageFormat::RGBA8), - ]); -} - -#[test] -fn fg_test_7() { - // Test that a standalone surface is not incorrectly used to - // allocate subsequent shared task rects. + let counters = RenderTaskGraphCounters::new(); + let mut tasks = RenderTaskGraph::new(FrameId::first(), &counters); - let mut gb = RenderTaskGraphBuilder::new(); + let a1 = tasks.add().init(RenderTask::new_test(color, dyn_location(640, 640), SmallVec::new())); + let _a2 = tasks.add().init(RenderTask::new_test(color, dyn_location(320, 320), smallvec![a1])); - let pc_root = gb.add().init(task_location(pc_target(0, 0, 0))); + let b1 = tasks.add().init(RenderTask::new_test(color, dyn_location(640, 640), SmallVec::new())); + let b2 = tasks.add().init(RenderTask::new_test(color, dyn_location(320, 320), smallvec![b1])); + let _b3 = tasks.add().init(RenderTask::new_test(color, dyn_location(320, 320), smallvec![b2])); - let child0 = gb.add().init(task_dynamic(16)); - let child1 = gb.add().init(task_dynamic(16)); + let main_pic = tasks.add().init(RenderTask::new_test( + color, + RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)), + smallvec![b2], + )); - let child2 = gb.add().init(task_dynamic(16)); - let child3 = gb.add().init(task_dynamic(16)); + let initial_number_of_tasks = tasks.tasks.len(); - gb.add_dependency(pc_root, child0); - gb.add_dependency(child0, child1); - gb.add_dependency(pc_root, child1); + let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true); - gb.add_dependency(pc_root, child2); - gb.add_dependency(child2, child3); + // We should not have added any blits. + assert_eq!(tasks.tasks.len(), initial_number_of_tasks); - gb.test_expect(3, 3, &[ - (256, 256, ImageFormat::RGBA8), - (2048, 2048, ImageFormat::RGBA8), - (2048, 2048, ImageFormat::RGBA8), - ]); + assert_eq!(passes.len(), 3); + assert_eq!(passes[0].tasks, vec![b1]); + assert_eq!(passes[1].tasks, vec![b2]); + assert_eq!(passes[2].tasks, vec![main_pic]); } diff --git a/third_party/webrender/webrender/src/renderer/mod.rs b/third_party/webrender/webrender/src/renderer.rs index b5649bb6318..3552a304f76 100644 --- a/third_party/webrender/webrender/src/renderer/mod.rs +++ b/third_party/webrender/webrender/src/renderer.rs @@ -34,117 +34,96 @@ //! up the scissor, are accepting already transformed coordinates, which we can get by //! calling `DrawTarget::to_framebuffer_rect` -use api::{BlobImageHandler, ColorF, ColorU, MixBlendMode}; -use api::{DocumentId, Epoch, ExternalImageHandler}; -use api::CrashAnnotator; -#[cfg(feature = "replay")] -use api::ExternalImageId; -use api::{ExternalImageSource, ExternalImageType, FontRenderMode, ImageFormat}; -use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest}; -use api::{VoidPtrToSizeFn, PremultipliedColorF}; -use api::{RenderNotifier, ImageBufferKind, SharedFontInstanceMap}; +use api::{ApiMsg, BlobImageHandler, ColorF, ColorU, MixBlendMode}; +use api::{DocumentId, Epoch, ExternalImageHandler, ExternalImageId}; +use api::{ExternalImageSource, ExternalImageType, FontRenderMode, FrameMsg, ImageFormat}; +use api::{PipelineId, ImageRendering, Checkpoint, NotificationRequest, OutputImageHandler}; +use api::{DebugCommand, MemoryReport, VoidPtrToSizeFn, PremultipliedColorF}; +use api::{RenderApiSender, RenderNotifier, TextureTarget, SharedFontInstanceMap}; #[cfg(feature = "replay")] use api::ExternalImage; use api::units::*; -use api::channel::{unbounded_channel, Receiver}; pub use api::DebugFlags; -use core::time::Duration; - -use crate::render_api::{RenderApiSender, DebugCommand, FrameMsg, MemoryReport}; use crate::batch::{AlphaBatchContainer, BatchKind, BatchFeatures, BatchTextures, BrushBatchKind, ClipBatchList}; #[cfg(any(feature = "capture", feature = "replay"))] use crate::capture::{CaptureConfig, ExternalCaptureImage, PlainExternalImage}; -use crate::composite::{CompositeState, CompositeTileSurface, ResolvedExternalSurface, CompositorSurfaceTransform}; -use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeFeatures, CompositeSurfaceFormat, ResolvedExternalSurfaceColorData}; +use crate::composite::{CompositeState, CompositeTileSurface, CompositeTile, ResolvedExternalSurface}; +use crate::composite::{CompositorKind, Compositor, NativeTileId, CompositeSurfaceFormat, ResolvedExternalSurfaceColorData}; use crate::composite::{CompositorConfig, NativeSurfaceOperationDetails, NativeSurfaceId, NativeSurfaceOperation}; -use crate::composite::TileKind; -use crate::c_str; use crate::debug_colors; -use crate::device::{DepthFunction, Device, DrawTarget, ExternalTexture, GpuFrameId}; -use crate::device::{ProgramCache, ReadTarget, ShaderError, Texture, TextureFilter, TextureFlags, TextureSlot}; -use crate::device::{UploadMethod, UploadPBOPool, VertexUsageHint}; -use crate::device::query::{GpuSampler, GpuTimer}; -#[cfg(feature = "capture")] -use crate::device::FBOId; -use crate::debug_item::DebugItem; +use crate::debug_render::{DebugItem, DebugRenderer}; +use crate::device::{DepthFunction, Device, GpuFrameId, Program, UploadMethod, Texture, PBO}; +use crate::device::{DrawTarget, ExternalTexture, FBOId, ReadTarget, TextureSlot}; +use crate::device::{ShaderError, TextureFilter, TextureFlags, + VertexUsageHint, VAO, VBO, CustomVAO}; +use crate::device::ProgramCache; +use crate::device::query::GpuTimer; +use euclid::{rect, Transform3D, Scale, default}; use crate::frame_builder::{Frame, ChasePrimitive, FrameBuilderConfig}; +use gleam::gl; use crate::glyph_cache::GlyphCache; use crate::glyph_rasterizer::{GlyphFormat, GlyphRasterizer}; -use crate::gpu_cache::{GpuCacheUpdate, GpuCacheUpdateList}; +use crate::gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}; use crate::gpu_cache::{GpuCacheDebugChunk, GpuCacheDebugCmd}; -use crate::gpu_types::{PrimitiveInstanceData, ScalingInstance, SvgFilterInstance}; -use crate::gpu_types::{BlurInstance, ClearInstance, CompositeInstance, ZBufferId}; +use crate::gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, ScalingInstance, SvgFilterInstance, TransformData}; +use crate::gpu_types::{ClearInstance, CompositeInstance, ResolveInstanceData, ZBufferId}; use crate::internal_types::{TextureSource, ResourceCacheError}; -#[cfg(any(feature = "capture", feature = "replay"))] -use crate::internal_types::DebugOutput; -use crate::internal_types::{CacheTextureId, FastHashMap, FastHashSet, RenderedDocument, ResultMsg}; -use crate::internal_types::{TextureCacheAllocInfo, TextureCacheAllocationKind, TextureUpdateList}; -use crate::internal_types::{RenderTargetInfo, Swizzle, DeferredResolveIndex}; -use crate::picture::{self, ResolvedSurfaceTexture}; +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}; +use malloc_size_of::MallocSizeOfOps; +use crate::picture::{RecordedDirtyRegion, tile_cache_sizes, ResolvedSurfaceTexture}; use crate::prim_store::DeferredResolve; -use crate::profiler::{self, GpuProfileTag, TransactionProfile}; -use crate::profiler::{Profiler, add_event_marker, add_text_marker, thread_is_being_profiled}; +use crate::profiler::{BackendProfileCounters, FrameProfileCounters, TimeProfileCounter, + GpuProfileTag, RendererProfileCounters, RendererProfileTimers}; +use crate::profiler::{Profiler, ChangeIndicator, ProfileStyle, add_event_marker, thread_is_being_profiled}; use crate::device::query::{GpuProfiler, GpuDebugMethod}; +use rayon::{ThreadPool, ThreadPoolBuilder}; use crate::render_backend::{FrameId, RenderBackend}; use crate::render_task_graph::RenderTaskGraph; -use crate::render_task::{RenderTask, RenderTaskKind, ReadbackTask}; +use crate::render_task::{RenderTask, RenderTaskData, RenderTaskKind}; use crate::resource_cache::ResourceCache; use crate::scene_builder_thread::{SceneBuilderThread, SceneBuilderThreadChannels, LowPrioritySceneBuilderThread}; use crate::screen_capture::AsyncScreenshotGrabber; +use crate::shade::{Shaders, WrShaders}; +use smallvec::SmallVec; +use crate::texture_cache::TextureCache; use crate::render_target::{AlphaRenderTarget, ColorRenderTarget, PictureCacheTarget}; -use crate::render_target::{RenderTarget, TextureCacheRenderTarget}; -use crate::render_target::{RenderTargetKind, BlitJob}; -use crate::texture_cache::{TextureCache, TextureCacheConfig}; -use crate::tile_cache::PictureCacheDebugInfo; +use crate::render_target::{RenderTarget, TextureCacheRenderTarget, RenderTargetList}; +use crate::render_target::{RenderTargetKind, BlitJob, BlitJobSource}; +use crate::render_task_graph::RenderPassKind; use crate::util::drain_filter; -use crate::host_utils::{thread_started, thread_stopped}; -use crate::rectangle_occlusion as occlusion; -use upload::{upload_to_texture_cache, UploadTexturePool}; - -use euclid::{rect, Transform3D, Scale, default}; -use gleam::gl; -use malloc_size_of::MallocSizeOfOps; -use rayon::{ThreadPool, ThreadPoolBuilder}; +use crate::c_str; -use std::{ - cell::RefCell, - collections::VecDeque, - f32, - mem, - num::NonZeroUsize, - path::PathBuf, - rc::Rc, - sync::Arc, - sync::atomic::{AtomicBool, Ordering}, - thread, -}; -#[cfg(any(feature = "capture", feature = "replay"))] +use std; +use std::cmp; +use std::collections::VecDeque; use std::collections::hash_map::Entry; +use std::f32; +use std::marker::PhantomData; +use std::mem; +use std::os::raw::c_void; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::{channel, Sender, Receiver}; +use std::thread; +use std::cell::RefCell; use tracy_rs::register_thread_with_profiler; use time::precise_time_ns; +use std::ffi::CString; -mod debug; -mod gpu_cache; -mod shade; -mod vertex; -mod upload; - -pub use debug::DebugRenderer; -pub use shade::{Shaders, SharedShaders}; -pub use vertex::{desc, VertexArrayKind, MAX_VERTEX_TEXTURE_WIDTH}; - -/// Use this hint for all vertex data re-initialization. This allows -/// the driver to better re-use RBOs internally. -pub const ONE_TIME_USAGE_HINT: VertexUsageHint = VertexUsageHint::Stream; - -/// Is only false if no WR instances have ever been created. -static HAS_BEEN_INITIALIZED: AtomicBool = AtomicBool::new(false); - -/// Returns true if a WR instance has ever been initialized in this process. -pub fn wr_has_been_initialized() -> bool { - HAS_BEEN_INITIALIZED.load(Ordering::SeqCst) +cfg_if! { + if #[cfg(feature = "debugger")] { + use serde_json; + use crate::debug_server; + } } +const DEFAULT_BATCH_LOOKBACK_COUNT: usize = 10; +const VERTEX_TEXTURE_EXTRA_ROWS: i32 = 10; + /// The size of the array of each type of vertex data texture that /// is round-robin-ed each frame during bind_frame_data. Doing this /// helps avoid driver stalls while updating the texture in some @@ -155,7 +134,21 @@ pub fn wr_has_been_initialized() -> bool { /// combination of UBO/SSBO usage. Although this only affects some /// platforms, it's enabled on all platforms to reduce testing /// differences between platforms. -pub const VERTEX_DATA_TEXTURE_COUNT: usize = 3; +const VERTEX_DATA_TEXTURE_COUNT: usize = 3; + +/// Is only false if no WR instances have ever been created. +static HAS_BEEN_INITIALIZED: AtomicBool = AtomicBool::new(false); + +/// Returns true if a WR instance has ever been initialized in this process. +pub fn wr_has_been_initialized() -> bool { + HAS_BEEN_INITIALIZED.load(Ordering::SeqCst) +} + +pub const MAX_VERTEX_TEXTURE_WIDTH: usize = webrender_build::MAX_VERTEX_TEXTURE_WIDTH; +/// Enabling this toggle would force the GPU cache scattered texture to +/// be resized every frame, which enables GPU debuggers to see if this +/// is performed correctly. +const GPU_CACHE_RESIZE_TEST: bool = false; /// Number of GPU blocks per UV rectangle provided for an image. pub const BLOCKS_PER_UV_RECT: usize = 2; @@ -168,6 +161,14 @@ const GPU_TAG_BRUSH_LINEAR_GRADIENT: GpuProfileTag = GpuProfileTag { label: "B_LinearGradient", color: debug_colors::POWDERBLUE, }; +const GPU_TAG_BRUSH_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { + label: "B_RadialGradient", + color: debug_colors::LIGHTPINK, +}; +const GPU_TAG_BRUSH_CONIC_GRADIENT: GpuProfileTag = GpuProfileTag { + label: "B_ConicGradient", + color: debug_colors::GREEN, +}; const GPU_TAG_BRUSH_YUV_IMAGE: GpuProfileTag = GpuProfileTag { label: "B_YuvImage", color: debug_colors::DARKGREEN, @@ -200,20 +201,8 @@ const GPU_TAG_CACHE_LINE_DECORATION: GpuProfileTag = GpuProfileTag { label: "C_LineDecoration", color: debug_colors::YELLOWGREEN, }; -const GPU_TAG_CACHE_FAST_LINEAR_GRADIENT: GpuProfileTag = GpuProfileTag { - label: "C_FastLinearGradient", - color: debug_colors::BROWN, -}; -const GPU_TAG_CACHE_LINEAR_GRADIENT: GpuProfileTag = GpuProfileTag { - label: "C_LinearGradient", - color: debug_colors::BROWN, -}; -const GPU_TAG_CACHE_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag { - label: "C_RadialGradient", - color: debug_colors::BROWN, -}; -const GPU_TAG_CACHE_CONIC_GRADIENT: GpuProfileTag = GpuProfileTag { - label: "C_ConicGradient", +const GPU_TAG_CACHE_GRADIENT: GpuProfileTag = GpuProfileTag { + label: "C_Gradient", color: debug_colors::BROWN, }; const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { @@ -245,15 +234,15 @@ const GPU_TAG_SCALE: GpuProfileTag = GpuProfileTag { color: debug_colors::GHOSTWHITE, }; const GPU_SAMPLER_TAG_ALPHA: GpuProfileTag = GpuProfileTag { - label: "Alpha targets", + label: "Alpha Targets", color: debug_colors::BLACK, }; const GPU_SAMPLER_TAG_OPAQUE: GpuProfileTag = GpuProfileTag { - label: "Opaque pass", + label: "Opaque Pass", color: debug_colors::BLACK, }; const GPU_SAMPLER_TAG_TRANSPARENT: GpuProfileTag = GpuProfileTag { - label: "Transparent pass", + label: "Transparent Pass", color: debug_colors::BLACK, }; const GPU_TAG_SVG_FILTER: GpuProfileTag = GpuProfileTag { @@ -272,9 +261,30 @@ const GPU_TAG_CLEAR: GpuProfileTag = GpuProfileTag { /// The clear color used for the texture cache when the debug display is enabled. /// We use a shade of blue so that we can still identify completely blue items in /// the texture cache. -pub const TEXTURE_CACHE_DBG_CLEAR_COLOR: [f32; 4] = [0.0, 0.0, 0.8, 1.0]; +const TEXTURE_CACHE_DBG_CLEAR_COLOR: [f32; 4] = [0.0, 0.0, 0.8, 1.0]; impl BatchKind { + #[cfg(feature = "debugger")] + fn debug_name(&self) -> &'static str { + match *self { + BatchKind::SplitComposite => "SplitComposite", + BatchKind::Brush(kind) => { + match kind { + BrushBatchKind::Solid => "Brush (Solid)", + BrushBatchKind::Image(..) => "Brush (Image)", + BrushBatchKind::Blend => "Brush (Blend)", + BrushBatchKind::MixBlend { .. } => "Brush (Composite)", + BrushBatchKind::YuvImage(..) => "Brush (YuvImage)", + BrushBatchKind::ConicGradient => "Brush (ConicGradient)", + BrushBatchKind::RadialGradient => "Brush (RadialGradient)", + BrushBatchKind::LinearGradient => "Brush (LinearGradient)", + BrushBatchKind::Opacity => "Brush (Opacity)", + } + } + BatchKind::TextRun(_) => "TextRun", + } + } + fn sampler_tag(&self) -> GpuProfileTag { match *self { BatchKind::SplitComposite => GPU_TAG_PRIM_SPLIT_COMPOSITE, @@ -285,6 +295,8 @@ impl BatchKind { BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND, BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND, BrushBatchKind::YuvImage(..) => GPU_TAG_BRUSH_YUV_IMAGE, + BrushBatchKind::ConicGradient => GPU_TAG_BRUSH_CONIC_GRADIENT, + BrushBatchKind::RadialGradient => GPU_TAG_BRUSH_RADIAL_GRADIENT, BrushBatchKind::LinearGradient => GPU_TAG_BRUSH_LINEAR_GRADIENT, BrushBatchKind::Opacity => GPU_TAG_BRUSH_OPACITY, } @@ -312,21 +324,19 @@ pub enum ShaderColorMode { SubpixelWithBgColorPass1 = 4, SubpixelWithBgColorPass2 = 5, SubpixelDualSource = 6, - BitmapShadow = 7, + Bitmap = 7, ColorBitmap = 8, Image = 9, - MultiplyDualSource = 10, } impl From<GlyphFormat> for ShaderColorMode { fn from(format: GlyphFormat) -> ShaderColorMode { match format { - GlyphFormat::Alpha | - GlyphFormat::TransformedAlpha | - GlyphFormat::Bitmap => ShaderColorMode::Alpha, + GlyphFormat::Alpha | GlyphFormat::TransformedAlpha => ShaderColorMode::Alpha, GlyphFormat::Subpixel | GlyphFormat::TransformedSubpixel => { panic!("Subpixel glyph formats must be handled separately."); } + GlyphFormat::Bitmap => ShaderColorMode::Bitmap, GlyphFormat::ColorBitmap => ShaderColorMode::ColorBitmap, } } @@ -342,13 +352,14 @@ pub(crate) enum TextureSampler { Color0, Color1, Color2, + PrevPassAlpha, + PrevPassColor, GpuCache, TransformPalette, RenderTasks, Dither, PrimitiveHeadersF, PrimitiveHeadersI, - ClipMask, } impl TextureSampler { @@ -370,17 +381,551 @@ impl Into<TextureSlot> for TextureSampler { TextureSampler::Color0 => TextureSlot(0), TextureSampler::Color1 => TextureSlot(1), TextureSampler::Color2 => TextureSlot(2), - TextureSampler::GpuCache => TextureSlot(3), - TextureSampler::TransformPalette => TextureSlot(4), - TextureSampler::RenderTasks => TextureSlot(5), - TextureSampler::Dither => TextureSlot(6), - TextureSampler::PrimitiveHeadersF => TextureSlot(7), - TextureSampler::PrimitiveHeadersI => TextureSlot(8), - TextureSampler::ClipMask => TextureSlot(9), + TextureSampler::PrevPassAlpha => TextureSlot(3), + TextureSampler::PrevPassColor => TextureSlot(4), + TextureSampler::GpuCache => TextureSlot(5), + TextureSampler::TransformPalette => TextureSlot(6), + TextureSampler::RenderTasks => TextureSlot(7), + TextureSampler::Dither => TextureSlot(8), + TextureSampler::PrimitiveHeadersF => TextureSlot(9), + TextureSampler::PrimitiveHeadersI => TextureSlot(10), } } } +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct PackedVertex { + pub pos: [f32; 2], +} + +pub(crate) mod desc { + use crate::device::{VertexAttribute, VertexAttributeKind, VertexDescriptor}; + + pub const PRIM_INSTANCES: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aData", + count: 4, + kind: VertexAttributeKind::I32, + }, + ], + }; + + pub const BLUR: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aBlurRenderTaskAddress", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aBlurSourceTaskAddress", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aBlurDirection", + count: 1, + kind: VertexAttributeKind::I32, + }, + ], + }; + + pub const LINE: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aTaskRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aLocalSize", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aWavyLineThickness", + count: 1, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aStyle", + count: 1, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aAxisSelect", + count: 1, + kind: VertexAttributeKind::F32, + }, + ], + }; + + pub const GRADIENT: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aTaskRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aStops", + count: 4, + kind: VertexAttributeKind::F32, + }, + // TODO(gw): We should probably pack these as u32 colors instead + // of passing as full float vec4 here. It won't make much + // difference in real world, since these are only invoked + // rarely, when creating the cache. + VertexAttribute { + name: "aColor0", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor1", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor2", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor3", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aAxisSelect", + count: 1, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aStartStop", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + }; + + pub const BORDER: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aTaskOrigin", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor0", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor1", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aFlags", + count: 1, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aWidths", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aRadii", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aClipParams1", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aClipParams2", + count: 4, + kind: VertexAttributeKind::F32, + }, + ], + }; + + pub const SCALE: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aScaleTargetRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aScaleSourceRect", + count: 4, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aScaleSourceLayer", + count: 1, + kind: VertexAttributeKind::I32, + }, + ], + }; + + pub const CLIP: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aTransformIds", + count: 2, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aClipDataResourceAddress", + count: 4, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aClipLocalPos", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aClipTileRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aClipDeviceArea", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aClipOrigins", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aDevicePixelScale", + count: 1, + kind: VertexAttributeKind::F32, + }, + ], + }; + + pub const GPU_CACHE_UPDATE: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::U16Norm, + }, + VertexAttribute { + name: "aValue", + count: 4, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[], + }; + + pub const RESOLVE: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + ], + }; + + pub const SVG_FILTER: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aFilterRenderTaskAddress", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aFilterInput1TaskAddress", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aFilterInput2TaskAddress", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aFilterKind", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aFilterInputCount", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aFilterGenericInt", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aFilterExtraDataAddress", + count: 2, + kind: VertexAttributeKind::U16, + }, + ], + }; + + pub const VECTOR_STENCIL: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aFromPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aCtrlPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aToPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aFromNormal", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aCtrlNormal", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aToNormal", + count: 2, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aPathID", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aPad", + count: 1, + kind: VertexAttributeKind::U16, + }, + ], + }; + + pub const VECTOR_COVER: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aTargetRect", + count: 4, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aStencilOrigin", + count: 2, + kind: VertexAttributeKind::I32, + }, + VertexAttribute { + name: "aSubpixel", + count: 1, + kind: VertexAttributeKind::U16, + }, + VertexAttribute { + name: "aPad", + count: 1, + kind: VertexAttributeKind::U16, + }, + ], + }; + + pub const COMPOSITE: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aDeviceRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aDeviceClipRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aParams", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aUvRect0", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aUvRect1", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aUvRect2", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aTextureLayers", + count: 3, + kind: VertexAttributeKind::F32, + }, + ], + }; + + pub const CLEAR: VertexDescriptor = VertexDescriptor { + vertex_attributes: &[ + VertexAttribute { + name: "aPosition", + count: 2, + kind: VertexAttributeKind::F32, + }, + ], + instance_attributes: &[ + VertexAttribute { + name: "aRect", + count: 4, + kind: VertexAttributeKind::F32, + }, + VertexAttribute { + name: "aColor", + count: 4, + kind: VertexAttributeKind::F32, + }, + ], + }; +} + +#[derive(Debug, Copy, Clone)] +pub(crate) enum VertexArrayKind { + Primitive, + Blur, + Clip, + VectorStencil, + VectorCover, + Border, + Scale, + LineDecoration, + Gradient, + Resolve, + SvgFilter, + Composite, + Clear, +} + #[derive(Clone, Debug, PartialEq)] pub enum GraphicsApi { OpenGL, @@ -393,6 +938,28 @@ pub struct GraphicsApiInfo { pub version: String, } +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +pub enum ImageBufferKind { + Texture2D = 0, + TextureRect = 1, + TextureExternal = 2, + Texture2DArray = 3, +} + +//TODO: those types are the same, so let's merge them +impl From<TextureTarget> for ImageBufferKind { + fn from(target: TextureTarget) -> Self { + match target { + TextureTarget::Default => ImageBufferKind::Texture2D, + TextureTarget::Rect => ImageBufferKind::TextureRect, + TextureTarget::Array => ImageBufferKind::Texture2DArray, + TextureTarget::External => ImageBufferKind::TextureExternal, + } + } +} + #[derive(Debug)] pub struct GpuProfile { pub frame_id: GpuFrameId, @@ -400,7 +967,7 @@ pub struct GpuProfile { } impl GpuProfile { - fn new(frame_id: GpuFrameId, timers: &[GpuTimer]) -> GpuProfile { + fn new<T>(frame_id: GpuFrameId, timers: &[GpuTimer<T>]) -> GpuProfile { let mut paint_time_ns = 0; for timer in timers { paint_time_ns += timer.time_ns; @@ -447,6 +1014,13 @@ enum PartialPresentMode { }, } +/// A Texture that has been initialized by the `device` module and is ready to +/// be used. +struct ActiveTexture { + texture: Texture, + saved_index: Option<SavedTargetIndex>, +} + /// Helper struct for resolving device Textures for use during rendering passes. /// /// Manages the mapping between the at-a-distance texture handles used by the @@ -457,24 +1031,51 @@ struct TextureResolver { texture_cache_map: FastHashMap<CacheTextureId, Texture>, /// Map of external image IDs to native textures. - external_images: FastHashMap<DeferredResolveIndex, ExternalTexture>, + external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>, /// A special 1x1 dummy texture used for shaders that expect to work with /// the output of the previous pass but are actually running in the first /// pass. dummy_cache_texture: Texture, + + /// The outputs of the previous pass, if applicable. + prev_pass_color: Option<ActiveTexture>, + prev_pass_alpha: Option<ActiveTexture>, + + /// Saved render targets from previous passes. This is used when a pass + /// needs access to the result of a pass other than the immediately-preceding + /// one. In this case, the `RenderTask` will get a non-`None` `saved_index`, + /// which will cause the resulting render target to be persisted in this list + /// (at that index) until the end of the frame. + saved_targets: Vec<Texture>, + + /// Pool of idle render target textures ready for re-use. + /// + /// Naively, it would seem like we only ever need two pairs of (color, + /// alpha) render targets: one for the output of the previous pass (serving + /// as input to the current pass), and one for the output of the current + /// pass. However, there are cases where the output of one pass is used as + /// the input to multiple future passes. For example, drop-shadows draw the + /// picture in pass X, then reference it in pass X+1 to create the blurred + /// shadow, and pass the results of both X and X+1 to pass X+2 draw the + /// actual content. + /// + /// See the comments in `allocate_target_texture` for more insight on why + /// reuse is a win. + render_target_pool: Vec<Texture>, } impl TextureResolver { fn new(device: &mut Device) -> TextureResolver { let dummy_cache_texture = device .create_texture( - ImageBufferKind::Texture2D, + TextureTarget::Array, ImageFormat::RGBA8, 1, 1, TextureFilter::Linear, None, + 1, ); device.upload_texture_immediate( &dummy_cache_texture, @@ -485,6 +1086,10 @@ impl TextureResolver { texture_cache_map: FastHashMap::default(), external_images: FastHashMap::default(), dummy_cache_texture, + prev_pass_alpha: None, + prev_pass_color: None, + saved_targets: Vec::default(), + render_target_pool: Vec::new(), } } @@ -494,23 +1099,141 @@ impl TextureResolver { for (_id, texture) in self.texture_cache_map { device.delete_texture(texture); } + + for texture in self.render_target_pool { + device.delete_texture(texture); + } } fn begin_frame(&mut self) { + assert!(self.prev_pass_color.is_none()); + assert!(self.prev_pass_alpha.is_none()); + assert!(self.saved_targets.is_empty()); + } + + fn end_frame(&mut self, device: &mut Device, frame_id: GpuFrameId) { + // return the cached targets to the pool + self.end_pass(device, None, None); + // return the saved targets as well + while let Some(target) = self.saved_targets.pop() { + self.return_to_pool(device, target); + } + + // GC the render target pool, if it's currently > 32 MB in size. + // + // We use a simple scheme whereby we drop any texture that hasn't been used + // in the last 60 frames, until we are below the size threshold. This should + // generally prevent any sustained build-up of unused textures, unless we don't + // generate frames for a long period. This can happen when the window is + // minimized, and we probably want to flush all the WebRender caches in that case [1]. + // There is also a second "red line" memory threshold which prevents + // memory exhaustion if many render targets are allocated within a small + // number of frames. For now this is set at 320 MB (10x the normal memory threshold). + // + // [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1494099 + self.gc_targets( + device, + frame_id, + 32 * 1024 * 1024, + 32 * 1024 * 1024 * 10, + 60, + ); + } + + /// Transfers ownership of a render target back to the pool. + fn return_to_pool(&mut self, device: &mut Device, target: Texture) { + device.invalidate_render_target(&target); + self.render_target_pool.push(target); + } + + /// Frees any memory possible, in the event of a memory pressure signal. + fn on_memory_pressure( + &mut self, + device: &mut Device, + ) { + // Clear all textures in the render target pool + for target in self.render_target_pool.drain(..) { + device.delete_texture(target); + } + } + + /// Drops all targets from the render target pool that do not satisfy the predicate. + pub fn gc_targets( + &mut self, + device: &mut Device, + current_frame_id: GpuFrameId, + total_bytes_threshold: usize, + total_bytes_red_line_threshold: usize, + frames_threshold: usize, + ) { + // Get the total GPU memory size used by the current render target pool + let mut rt_pool_size_in_bytes: usize = self.render_target_pool + .iter() + .map(|t| t.size_in_bytes()) + .sum(); + + // If the total size of the pool is less than the threshold, don't bother + // trying to GC any targets + if rt_pool_size_in_bytes <= total_bytes_threshold { + return; + } + + // Sort the current pool by age, so that we remove oldest textures first + self.render_target_pool.sort_by_key(|t| t.last_frame_used()); + + // We can't just use retain() because `Texture` requires manual cleanup. + let mut retained_targets = SmallVec::<[Texture; 8]>::new(); + + for target in self.render_target_pool.drain(..) { + // Drop oldest textures until we are under the allowed size threshold. + // However, if it's been used in very recently, it is always kept around, + // which ensures we don't thrash texture allocations on pages that do + // require a very large render target pool and are regularly changing. + if (rt_pool_size_in_bytes > total_bytes_red_line_threshold) || + (rt_pool_size_in_bytes > total_bytes_threshold && + !target.used_recently(current_frame_id, frames_threshold)) + { + rt_pool_size_in_bytes -= target.size_in_bytes(); + device.delete_texture(target); + } else { + retained_targets.push(target); + } + } + + self.render_target_pool.extend(retained_targets); } fn end_pass( &mut self, device: &mut Device, - textures_to_invalidate: &[CacheTextureId], + a8_texture: Option<ActiveTexture>, + rgba8_texture: Option<ActiveTexture>, ) { - // For any texture that is no longer needed, immediately - // invalidate it so that tiled GPUs don't need to resolve it - // back to memory. - for texture_id in textures_to_invalidate { - let render_target = &self.texture_cache_map[texture_id]; - device.invalidate_render_target(render_target); + // If we have cache textures from previous pass, return them to the pool. + // Also assign the pool index of those cache textures to last pass's index because this is + // the result of last pass. + // Note: the order here is important, needs to match the logic in `RenderPass::build()`. + if let Some(at) = self.prev_pass_color.take() { + if let Some(index) = at.saved_index { + assert_eq!(self.saved_targets.len(), index.0); + self.saved_targets.push(at.texture); + } else { + self.return_to_pool(device, at.texture); + } } + if let Some(at) = self.prev_pass_alpha.take() { + if let Some(index) = at.saved_index { + assert_eq!(self.saved_targets.len(), index.0); + self.saved_targets.push(at.texture); + } else { + self.return_to_pool(device, at.texture); + } + } + + // We have another pass to process, make these textures available + // as inputs to the next pass. + self.prev_pass_color = rgba8_texture; + self.prev_pass_alpha = a8_texture; } // Bind a source texture to the device. @@ -524,9 +1247,27 @@ impl TextureResolver { device.bind_texture(sampler, &self.dummy_cache_texture, swizzle); swizzle } - TextureSource::External(ref index, _) => { + TextureSource::PrevPassAlpha => { + let texture = match self.prev_pass_alpha { + Some(ref at) => &at.texture, + None => &self.dummy_cache_texture, + }; + let swizzle = Swizzle::default(); + device.bind_texture(sampler, texture, swizzle); + swizzle + } + TextureSource::PrevPassColor => { + let texture = match self.prev_pass_color { + Some(ref at) => &at.texture, + None => &self.dummy_cache_texture, + }; + let swizzle = Swizzle::default(); + device.bind_texture(sampler, texture, swizzle); + swizzle + } + TextureSource::External(external_image) => { let texture = self.external_images - .get(index) + .get(&(external_image.id, external_image.channel_index)) .expect("BUG: External image should be resolved by now"); device.bind_external_texture(sampler, texture); Swizzle::default() @@ -536,6 +1277,28 @@ impl TextureResolver { device.bind_texture(sampler, texture, swizzle); swizzle } + TextureSource::RenderTaskCache(saved_index, swizzle) => { + if saved_index.0 < self.saved_targets.len() { + let texture = &self.saved_targets[saved_index.0]; + device.bind_texture(sampler, texture, swizzle) + } else { + // Check if this saved index is referring to a the prev pass + if Some(saved_index) == self.prev_pass_color.as_ref().and_then(|at| at.saved_index) { + let texture = match self.prev_pass_color { + Some(ref at) => &at.texture, + None => &self.dummy_cache_texture, + }; + device.bind_texture(sampler, texture, swizzle); + } else if Some(saved_index) == self.prev_pass_alpha.as_ref().and_then(|at| at.saved_index) { + let texture = match self.prev_pass_alpha { + Some(ref at) => &at.texture, + None => &self.dummy_cache_texture, + }; + device.bind_texture(sampler, texture, swizzle); + } + } + swizzle + } } } @@ -548,12 +1311,29 @@ impl TextureResolver { TextureSource::Dummy => { Some((&self.dummy_cache_texture, Swizzle::default())) } + TextureSource::PrevPassAlpha => Some(( + match self.prev_pass_alpha { + Some(ref at) => &at.texture, + None => &self.dummy_cache_texture, + }, + Swizzle::default(), + )), + TextureSource::PrevPassColor => Some(( + match self.prev_pass_color { + Some(ref at) => &at.texture, + None => &self.dummy_cache_texture, + }, + Swizzle::default(), + )), TextureSource::External(..) => { panic!("BUG: External textures cannot be resolved, they can only be bound."); } TextureSource::TextureCache(index, swizzle) => { Some((&self.texture_cache_map[&index], swizzle)) } + TextureSource::RenderTaskCache(saved_index, swizzle) => { + Some((&self.saved_targets[saved_index.0], swizzle)) + } } } @@ -565,9 +1345,9 @@ impl TextureResolver { default_value: TexelRect, ) -> TexelRect { match source { - TextureSource::External(ref index, _) => { + TextureSource::External(ref external_image) => { let texture = self.external_images - .get(index) + .get(&(external_image.id, external_image.channel_index)) .expect("BUG: External image should be resolved by now"); texture.get_uv_rect() } @@ -577,21 +1357,6 @@ impl TextureResolver { } } - /// Returns the size of the texture in pixels - fn get_texture_size(&self, texture: &TextureSource) -> DeviceIntSize { - match *texture { - TextureSource::Invalid => DeviceIntSize::zero(), - TextureSource::TextureCache(id, _) => { - self.texture_cache_map[&id].get_dimensions() - }, - TextureSource::External(index, _) => { - let uv_rect = self.external_images[&index].get_uv_rect(); - (uv_rect.uv1 - uv_rect.uv0).abs().to_size().to_i32() - }, - TextureSource::Dummy => DeviceIntSize::new(1, 1), - } - } - fn report_memory(&self) -> MemoryReport { let mut report = MemoryReport::default(); @@ -600,6 +1365,9 @@ impl TextureResolver { for t in self.texture_cache_map.values() { report.texture_cache_textures += t.size_in_bytes(); } + for t in self.render_target_pool.iter() { + report.render_target_textures += t.size_in_bytes(); + } report } @@ -617,39 +1385,533 @@ pub enum BlendMode { SubpixelConstantTextColor(ColorF), SubpixelWithBgColor, Advanced(MixBlendMode), - MultiplyDualSource, - Screen, - Exclusion, } -impl BlendMode { - /// Decides when a given mix-blend-mode can be implemented in terms of - /// simple blending, dual-source blending, advanced blending, or not at - /// all based on available capabilities. - pub fn from_mix_blend_mode( - mode: MixBlendMode, - advanced_blend: bool, - coherent: bool, - dual_source: bool, - ) -> Option<BlendMode> { - // If we emulate a mix-blend-mode via simple or dual-source blending, - // care must be taken to output alpha As + Ad*(1-As) regardless of what - // the RGB output is to comply with the mix-blend-mode spec. - Some(match mode { - // If we have coherent advanced blend, just use that. - _ if advanced_blend && coherent => BlendMode::Advanced(mode), - // Screen can be implemented as Cs + Cd - Cs*Cd => Cs + Cd*(1-Cs) - MixBlendMode::Screen => BlendMode::Screen, - // Exclusion can be implemented as Cs + Cd - 2*Cs*Cd => Cs*(1-Cd) + Cd*(1-Cs) - MixBlendMode::Exclusion => BlendMode::Exclusion, - // Multiply can be implemented as Cs*Cd + Cs*(1-Ad) + Cd*(1-As) => Cs*(1-Ad) + Cd*(1 - SRC1=(As-Cs)) - MixBlendMode::Multiply if dual_source => BlendMode::MultiplyDualSource, - // Otherwise, use advanced blend without coherency if available. - _ if advanced_blend => BlendMode::Advanced(mode), - // If advanced blend is not available, then we have to use brush_mix_blend. - _ => return None, +/// Tracks the state of each row in the GPU cache texture. +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]>, + /// 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]), + 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 +/// get synchronized. +enum GpuCacheBus { + /// PBO-based updates, currently operate on a row granularity. + /// Therefore, are subject to fragmentation issues. + PixelBuffer { + /// PBO used for transfers. + buffer: PBO, + /// Per-row data. + rows: Vec<CacheRow>, + }, + /// Shader-based scattering updates. Currently rendered by a set + /// of points into the GPU texture, each carrying a `GpuBlockData`. + Scatter { + /// Special program to run the scattered update. + program: Program, + /// VAO containing the source vertex buffers. + vao: CustomVAO, + /// VBO for positional data, supplied as normalized `u16`. + buf_position: VBO<[u16; 2]>, + /// VBO for gpu block data. + buf_value: VBO<GpuBlockData>, + /// Currently stored block count. + count: usize, + }, +} + +/// The device-specific representation of the cache texture in gpu_cache.rs +struct GpuCacheTexture { + texture: Option<Texture>, + bus: GpuCacheBus, +} + +impl GpuCacheTexture { + + /// Ensures that we have an appropriately-sized texture. Returns true if a + /// new texture was created. + fn ensure_texture(&mut self, device: &mut Device, height: i32) { + // If we already have a texture that works, we're done. + if self.texture.as_ref().map_or(false, |t| t.get_dimensions().height >= height) { + if GPU_CACHE_RESIZE_TEST { + // Special debug mode - resize the texture even though it's fine. + } else { + return; + } + } + + // Take the old texture, if any. + let blit_source = self.texture.take(); + + // Create the new texture. + assert!(height >= 2, "Height is too small for ANGLE"); + let new_size = DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as _, height); + // If glCopyImageSubData is supported, this texture doesn't need + // to be a render target. This prevents GL errors due to framebuffer + // incompleteness on devices that don't support RGBAF32 render targets. + // TODO(gw): We still need a proper solution for the subset of devices + // that don't support glCopyImageSubData *OR* rendering to a + // RGBAF32 render target. These devices will currently fail + // to resize the GPU cache texture. + let supports_copy_image_sub_data = device.get_capabilities().supports_copy_image_sub_data; + let rt_info = if supports_copy_image_sub_data { + None + } else { + Some(RenderTargetInfo { has_depth: false }) + }; + let mut texture = device.create_texture( + TextureTarget::Default, + ImageFormat::RGBAF32, + new_size.width, + new_size.height, + TextureFilter::Nearest, + rt_info, + 1, + ); + + // Blit the contents of the previous texture, if applicable. + if let Some(blit_source) = blit_source { + device.blit_renderable_texture(&mut texture, &blit_source); + device.delete_texture(blit_source); + } + + self.texture = Some(texture); + } + + fn new(device: &mut Device, use_scatter: bool) -> Result<Self, RendererError> { + let bus = if use_scatter { + let program = device.create_program_linked( + "gpu_cache_update", + &[], + &desc::GPU_CACHE_UPDATE, + )?; + let buf_position = device.create_vbo(); + let buf_value = device.create_vbo(); + //Note: the vertex attributes have to be supplied in the same order + // as for program creation, but each assigned to a different stream. + let vao = device.create_custom_vao(&[ + buf_position.stream_with(&desc::GPU_CACHE_UPDATE.vertex_attributes[0..1]), + buf_value .stream_with(&desc::GPU_CACHE_UPDATE.vertex_attributes[1..2]), + ]); + GpuCacheBus::Scatter { + program, + vao, + buf_position, + buf_value, + count: 0, + } + } else { + let buffer = device.create_pbo(); + GpuCacheBus::PixelBuffer { + buffer, + rows: Vec::new(), + } + }; + + Ok(GpuCacheTexture { + texture: None, + bus, }) } + + fn deinit(mut self, device: &mut Device) { + if let Some(t) = self.texture.take() { + device.delete_texture(t); + } + match self.bus { + GpuCacheBus::PixelBuffer { buffer, ..} => { + device.delete_pbo(buffer); + } + GpuCacheBus::Scatter { program, vao, buf_position, buf_value, ..} => { + device.delete_program(program); + device.delete_custom_vao(vao); + device.delete_vbo(buf_position); + device.delete_vbo(buf_value); + } + } + } + + fn get_height(&self) -> i32 { + self.texture.as_ref().map_or(0, |t| t.get_dimensions().height) + } + + fn prepare_for_updates( + &mut self, + device: &mut Device, + total_block_count: usize, + max_height: i32, + ) { + self.ensure_texture(device, max_height); + match self.bus { + GpuCacheBus::PixelBuffer { .. } => {}, + GpuCacheBus::Scatter { + ref mut buf_position, + ref mut buf_value, + ref mut count, + .. + } => { + *count = 0; + if total_block_count > buf_value.allocated_count() { + device.allocate_vbo(buf_position, total_block_count, VertexUsageHint::Stream); + device.allocate_vbo(buf_value, total_block_count, VertexUsageHint::Stream); + } + } + } + } + + fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) { + match self.bus { + GpuCacheBus::PixelBuffer { ref mut rows, .. } => { + for update in &updates.updates { + match *update { + GpuCacheUpdate::Copy { + block_index, + block_count, + address, + } => { + let row = address.v as usize; + + // Ensure that the CPU-side shadow copy of the GPU cache data has enough + // rows to apply this patch. + while rows.len() <= row { + // Add a new row. + rows.push(CacheRow::new()); + } + + // 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); + } + } + } + } + GpuCacheBus::Scatter { + ref buf_position, + ref buf_value, + ref mut count, + .. + } => { + //TODO: re-use this heap allocation + // Unused positions will be left as 0xFFFF, which translates to + // (1.0, 1.0) in the vertex output position and gets culled out + let mut position_data = vec![[!0u16; 2]; updates.blocks.len()]; + let size = self.texture.as_ref().unwrap().get_dimensions().to_usize(); + + for update in &updates.updates { + match *update { + GpuCacheUpdate::Copy { + block_index, + block_count, + address, + } => { + // Convert the absolute texel position into normalized + let y = ((2*address.v as usize + 1) << 15) / size.height; + for i in 0 .. block_count { + let x = ((2*address.u as usize + 2*i + 1) << 15) / size.width; + position_data[block_index + i] = [x as _, y as _]; + } + } + } + } + + device.fill_vbo(buf_value, &updates.blocks, *count); + device.fill_vbo(buf_position, &position_data, *count); + *count += position_data.len(); + } + } + } + + fn flush(&mut self, device: &mut Device) -> usize { + let texture = self.texture.as_ref().unwrap(); + match self.bus { + GpuCacheBus::PixelBuffer { ref buffer, ref mut rows } => { + let rows_dirty = rows + .iter() + .filter(|row| row.is_dirty()) + .count(); + if rows_dirty == 0 { + return 0 + } + + let (upload_size, _) = device.required_upload_size_and_stride( + DeviceIntSize::new(MAX_VERTEX_TEXTURE_WIDTH as i32, 1), + texture.get_format(), + ); + + let mut uploader = device.upload_texture( + texture, + buffer, + rows_dirty * upload_size, + ); + + for (row_index, row) in rows.iter_mut().enumerate() { + if !row.is_dirty() { + continue; + } + + let blocks = row.dirty_blocks(); + let rect = DeviceIntRect::new( + DeviceIntPoint::new(row.min_dirty as i32, row_index as i32), + DeviceIntSize::new(blocks.len() as i32, 1), + ); + + uploader.upload(rect, 0, None, None, blocks.as_ptr(), blocks.len()); + + row.clear_dirty(); + } + + rows_dirty + } + GpuCacheBus::Scatter { ref program, ref vao, count, .. } => { + device.disable_depth(); + device.set_blend(false); + device.bind_program(program); + device.bind_custom_vao(vao); + device.bind_draw_target( + DrawTarget::from_texture( + texture, + 0, + false, + ), + ); + device.draw_nonindexed_points(0, count as _); + 0 + } + } + } +} + +struct VertexDataTexture<T> { + texture: Option<Texture>, + format: ImageFormat, + pbo: PBO, + _marker: PhantomData<T>, +} + +impl<T> VertexDataTexture<T> { + fn new( + device: &mut Device, + format: ImageFormat, + ) -> Self { + VertexDataTexture { + texture: None, + format, + pbo: device.create_pbo(), + _marker: PhantomData, + } + } + + /// Returns a borrow of the GPU texture. Panics if it hasn't been initialized. + fn texture(&self) -> &Texture { + self.texture.as_ref().unwrap() + } + + /// Returns an estimate of the GPU memory consumed by this VertexDataTexture. + fn size_in_bytes(&self) -> usize { + self.texture.as_ref().map_or(0, |t| t.size_in_bytes()) + } + + fn update(&mut self, device: &mut Device, data: &mut Vec<T>) { + debug_assert!(mem::size_of::<T>() % 16 == 0); + let texels_per_item = mem::size_of::<T>() / 16; + let items_per_row = MAX_VERTEX_TEXTURE_WIDTH / texels_per_item; + debug_assert_ne!(items_per_row, 0); + + // Ensure we always end up with a texture when leaving this method. + let mut len = data.len(); + if len == 0 { + if self.texture.is_some() { + return; + } + data.reserve(items_per_row); + len = items_per_row; + } else { + // Extend the data array to have enough capacity to upload at least + // a multiple of the row size. This ensures memory safety when the + // array is passed to OpenGL to upload to the GPU. + let extra = len % items_per_row; + if extra != 0 { + let padding = items_per_row - extra; + data.reserve(padding); + len += padding; + } + } + + let needed_height = (len / items_per_row) as i32; + let existing_height = self.texture.as_ref().map_or(0, |t| t.get_dimensions().height); + + // Create a new texture if needed. + // + // These textures are generally very small, which is why we don't bother + // with incremental updates and just re-upload every frame. For most pages + // they're one row each, and on stress tests like css-francine they end up + // in the 6-14 range. So we size the texture tightly to what we need (usually + // 1), and shrink it if the waste would be more than `VERTEX_TEXTURE_EXTRA_ROWS` + // rows. This helps with memory overhead, especially because there are several + // instances of these textures per Renderer. + if needed_height > existing_height || needed_height + VERTEX_TEXTURE_EXTRA_ROWS < existing_height { + // Drop the existing texture, if any. + if let Some(t) = self.texture.take() { + device.delete_texture(t); + } + + let texture = device.create_texture( + TextureTarget::Default, + self.format, + MAX_VERTEX_TEXTURE_WIDTH as i32, + // Ensure height is at least two to work around + // https://bugs.chromium.org/p/angleproject/issues/detail?id=3039 + needed_height.max(2), + TextureFilter::Nearest, + None, + 1, + ); + self.texture = Some(texture); + } + + // Note: the actual width can be larger than the logical one, with a few texels + // of each row unused at the tail. This is needed because there is still hardware + // (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 = 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 as i32, needed_height), + ); + + debug_assert!(len <= data.capacity(), "CPU copy will read out of bounds"); + let (upload_size, _) = device.required_upload_size_and_stride( + rect.size, + self.texture().get_format(), + ); + 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) { + device.delete_pbo(self.pbo); + if let Some(t) = self.texture.take() { + device.delete_texture(t); + } + } +} + +struct FrameOutput { + last_access: GpuFrameId, + fbo_id: FBOId, +} + +#[derive(PartialEq)] +struct TargetSelector { + size: DeviceIntSize, + num_layers: usize, + format: ImageFormat, +} + +struct LazyInitializedDebugRenderer { + debug_renderer: Option<DebugRenderer>, + failed: bool, +} + +impl LazyInitializedDebugRenderer { + pub fn new() -> Self { + Self { + debug_renderer: None, + failed: false, + } + } + + pub fn get_mut<'a>(&'a mut self, device: &mut Device) -> Option<&'a mut DebugRenderer> { + if self.failed { + return None; + } + if self.debug_renderer.is_none() { + match DebugRenderer::new(device) { + Ok(renderer) => { self.debug_renderer = Some(renderer); } + Err(_) => { + // The shader compilation code already logs errors. + self.failed = true; + } + } + } + + self.debug_renderer.as_mut() + } + + /// Returns mut ref to `DebugRenderer` if one already exists, otherwise returns `None`. + pub fn try_get_mut<'a>(&'a mut self) -> Option<&'a mut DebugRenderer> { + self.debug_renderer.as_mut() + } + + pub fn deinit(self, device: &mut Device) { + if let Some(debug_renderer) = self.debug_renderer { + debug_renderer.deinit(device); + } + } +} + +// NB: If you add more VAOs here, be sure to deinitialize them in +// `Renderer::deinit()` below. +pub struct RendererVAOs { + prim_vao: VAO, + blur_vao: VAO, + clip_vao: VAO, + border_vao: VAO, + line_vao: VAO, + scale_vao: VAO, + gradient_vao: VAO, + resolve_vao: VAO, + svg_filter_vao: VAO, + composite_vao: VAO, + clear_vao: VAO, } /// Information about the state of the debugging / profiler overlay in native compositing mode. @@ -671,50 +1933,87 @@ impl DebugOverlayState { } } -/// Tracks buffer damage rects over a series of frames. -#[derive(Debug, Default)] -struct BufferDamageTracker { - damage_rects: [DeviceRect; 2], - current_offset: usize, +pub struct VertexDataTextures { + prim_header_f_texture: VertexDataTexture<PrimitiveHeaderF>, + prim_header_i_texture: VertexDataTexture<PrimitiveHeaderI>, + transforms_texture: VertexDataTexture<TransformData>, + render_task_texture: VertexDataTexture<RenderTaskData>, } -impl BufferDamageTracker { - /// Sets the damage rect for the current frame. Should only be called *after* - /// get_damage_rect() has been called to get the current backbuffer's damage rect. - fn push_dirty_rect(&mut self, rect: &DeviceRect) { - self.damage_rects[self.current_offset] = rect.clone(); - self.current_offset = match self.current_offset { - 0 => self.damage_rects.len() - 1, - n => n - 1, - } - } - - /// Gets the damage rect for the current backbuffer, given the backbuffer's age. - /// (The number of frames since it was previously the backbuffer.) - /// Returns an empty rect if the buffer is valid, and None if the entire buffer is invalid. - fn get_damage_rect(&self, buffer_age: usize) -> Option<DeviceRect> { - match buffer_age { - // 0 means this is a new buffer, so is completely invalid. - 0 => None, - // 1 means this backbuffer was also the previous frame's backbuffer - // (so must have been copied to the frontbuffer). It is therefore entirely valid. - 1 => Some(DeviceRect::zero()), - // We must calculate the union of the damage rects since this buffer was previously - // the backbuffer. - n if n <= self.damage_rects.len() + 1 => { - Some( - self.damage_rects.iter() - .cycle() - .skip(self.current_offset + 1) - .take(n - 1) - .fold(DeviceRect::zero(), |acc, r| acc.union(r)) - ) - } - // The backbuffer is older than the number of frames for which we track, - // so we treat it as entirely invalid. - _ => None, +impl VertexDataTextures { + fn new( + device: &mut Device, + ) -> Self { + VertexDataTextures { + prim_header_f_texture: VertexDataTexture::new(device, ImageFormat::RGBAF32), + prim_header_i_texture: VertexDataTexture::new(device, ImageFormat::RGBAI32), + transforms_texture: VertexDataTexture::new(device, ImageFormat::RGBAF32), + render_task_texture: VertexDataTexture::new(device, ImageFormat::RGBAF32), } } + + fn update( + &mut self, + device: &mut Device, + frame: &mut Frame, + ) { + self.prim_header_f_texture.update( + device, + &mut frame.prim_headers.headers_float, + ); + device.bind_texture( + TextureSampler::PrimitiveHeadersF, + &self.prim_header_f_texture.texture(), + Swizzle::default(), + ); + + self.prim_header_i_texture.update( + device, + &mut frame.prim_headers.headers_int, + ); + device.bind_texture( + TextureSampler::PrimitiveHeadersI, + &self.prim_header_i_texture.texture(), + Swizzle::default(), + ); + + self.transforms_texture.update( + device, + &mut frame.transform_palette, + ); + device.bind_texture( + TextureSampler::TransformPalette, + &self.transforms_texture.texture(), + Swizzle::default(), + ); + + self.render_task_texture.update( + device, + &mut frame.render_tasks.task_data, + ); + device.bind_texture( + TextureSampler::RenderTasks, + &self.render_task_texture.texture(), + Swizzle::default(), + ); + } + + fn size_in_bytes(&self) -> usize { + self.prim_header_f_texture.size_in_bytes() + + self.prim_header_i_texture.size_in_bytes() + + self.transforms_texture.size_in_bytes() + + self.render_task_texture.size_in_bytes() + } + + fn deinit( + self, + device: &mut Device, + ) { + self.transforms_texture.deinit(device); + self.prim_header_f_texture.deinit(device); + self.prim_header_i_texture.deinit(device); + self.render_task_texture.deinit(device); + } } /// The renderer is responsible for submitting to the GPU the work prepared by the @@ -724,6 +2023,7 @@ impl BufferDamageTracker { /// one per OS window), and all instances share the same thread. pub struct Renderer { result_rx: Receiver<ResultMsg>, + debug_server: Box<dyn DebugServer>, pub device: Device, pending_texture_updates: Vec<TextureUpdateList>, /// True if there are any TextureCacheUpdate pending. @@ -732,7 +2032,7 @@ pub struct Renderer { pending_gpu_cache_updates: Vec<GpuCacheUpdateList>, pending_gpu_cache_clear: bool, pending_shader_updates: Vec<PathBuf>, - active_documents: FastHashMap<DocumentId, RenderedDocument>, + active_documents: Vec<(DocumentId, RenderedDocument)>, shaders: Rc<RefCell<Shaders>>, @@ -742,23 +2042,26 @@ pub struct Renderer { enable_clear_scissor: bool, enable_advanced_blend_barriers: bool, clear_caches_with_quads: bool, - clear_alpha_targets_with_quads: bool, - debug: debug::LazyInitializedDebugRenderer, + debug: LazyInitializedDebugRenderer, debug_flags: DebugFlags, - profile: TransactionProfile, - frame_counter: u64, - resource_upload_time: f64, - gpu_cache_upload_time: f64, + backend_profile_counters: BackendProfileCounters, + profile_counters: RendererProfileCounters, + resource_upload_time: u64, + gpu_cache_upload_time: u64, profiler: Profiler, + new_frame_indicator: ChangeIndicator, + new_scene_indicator: ChangeIndicator, + slow_frame_indicator: ChangeIndicator, + slow_txn_indicator: ChangeIndicator, last_time: u64, - pub gpu_profiler: GpuProfiler, - vaos: vertex::RendererVAOs, + pub gpu_profile: GpuProfiler<GpuProfileTag>, + vaos: RendererVAOs, - gpu_cache_texture: gpu_cache::GpuCacheTexture, - vertex_data_textures: Vec<vertex::VertexDataTextures>, + gpu_cache_texture: GpuCacheTexture, + vertex_data_textures: Vec<VertexDataTextures>, current_vertex_data_textures: usize, /// When the GPU cache debugger is enabled, we keep track of the live blocks @@ -774,8 +2077,8 @@ pub struct Renderer { // Manages and resolves source textures IDs to real texture IDs. texture_resolver: TextureResolver, - texture_upload_pbo_pool: UploadPBOPool, - staging_texture_pool: UploadTexturePool, + // A PBO used to do asynchronous texture cache uploads. + texture_cache_upload_pbo: PBO, dither_matrix_texture: Option<Texture>, @@ -783,10 +2086,18 @@ pub struct Renderer { /// application to provide external buffers for image data. external_image_handler: Option<Box<dyn ExternalImageHandler>>, + /// Optional trait object that allows the client + /// application to provide a texture handle to + /// copy the WR output to. + output_image_handler: Option<Box<dyn OutputImageHandler>>, + /// Optional function pointers for measuring memory used by a given /// heap-allocated pointer. size_of_ops: Option<MallocSizeOfOps>, + // Currently allocated FBOs for output frames. + output_targets: FastHashMap<u32, FrameOutput>, + pub renderer_errors: Vec<RendererError>, pub(in crate) async_frame_recorder: Option<AsyncScreenshotGrabber>, @@ -839,13 +2150,10 @@ pub struct Renderer { /// State related to the debug / profiling overlays debug_overlay_state: DebugOverlayState, - /// Tracks the dirty rectangles from previous frames. Used on platforms - /// that require keeping the front buffer fully correct when doing + /// The dirty rectangle from the previous frame, used on platforms that + /// require keeping the front buffer fully correct when doing /// partial present (e.g. unix desktop with EGL_EXT_buffer_age). - buffer_damage_tracker: BufferDamageTracker, - - max_primitive_instance_count: usize, - enable_instancing: bool, + prev_dirty_rect: DeviceRect, } #[derive(Debug)] @@ -896,7 +2204,8 @@ impl Renderer { gl: Rc<dyn gl::Gl>, notifier: Box<dyn RenderNotifier>, mut options: RendererOptions, - shaders: Option<&SharedShaders>, + shaders: Option<&mut WrShaders>, + 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 @@ -914,17 +2223,19 @@ impl Renderer { HAS_BEEN_INITIALIZED.store(true, Ordering::SeqCst); - let (api_tx, api_rx) = unbounded_channel(); - let (result_tx, result_rx) = unbounded_channel(); + let (api_tx, api_rx) = channel(); + let (result_tx, result_rx) = channel(); let gl_type = gl.get_type(); + let debug_server = new_debug_server(options.start_debug_server, api_tx.clone()); + let mut device = Device::new( gl, - options.crash_annotator.clone(), options.resource_override_path.clone(), options.use_optimized_shaders, options.upload_method.clone(), options.cached_programs.take(), + options.allow_pixel_local_storage_support, options.allow_texture_storage_support, options.allow_texture_swizzling, options.dump_shader_source.take(), @@ -936,40 +2247,43 @@ impl Renderer { let swizzle_settings = device.swizzle_settings(); let use_dual_source_blending = device.get_capabilities().supports_dual_source_blending && - options.allow_dual_source_blending; + options.allow_dual_source_blending && + // If using pixel local storage, subpixel AA isn't supported (we disable it on all + // mobile devices explicitly anyway). + !device.get_capabilities().supports_pixel_local_storage; let ext_blend_equation_advanced = options.allow_advanced_blend_equation && device.get_capabilities().supports_advanced_blend_equation; let ext_blend_equation_advanced_coherent = device.supports_extension("GL_KHR_blend_equation_advanced_coherent"); - // 2048 is the minimum that the texture cache can work with. - const MIN_TEXTURE_SIZE: i32 = 2048; - let mut max_internal_texture_size = device.max_texture_size(); - if max_internal_texture_size < MIN_TEXTURE_SIZE { + // 512 is the minimum that the texture cache can work with. + const MIN_TEXTURE_SIZE: i32 = 512; + if let Some(user_limit) = options.max_texture_size { + assert!(user_limit >= MIN_TEXTURE_SIZE); + device.clamp_max_texture_size(user_limit); + } + if device.max_texture_size() < MIN_TEXTURE_SIZE { // Broken GL contexts can return a max texture size of zero (See #1260). // Better to gracefully fail now than panic as soon as a texture is allocated. error!( "Device reporting insufficient max texture size ({})", - max_internal_texture_size + device.max_texture_size() ); return Err(RendererError::MaxTextureSize); } - if let Some(internal_limit) = options.max_internal_texture_size { - assert!(internal_limit >= MIN_TEXTURE_SIZE); - max_internal_texture_size = max_internal_texture_size.min(internal_limit); - } - - let image_tiling_threshold = options.image_tiling_threshold - .min(max_internal_texture_size); + let max_texture_size = device.max_texture_size(); + let max_texture_layers = device.max_texture_layers(); device.begin_frame(); let shaders = match shaders { - Some(shaders) => Rc::clone(shaders), + Some(shaders) => Rc::clone(&shaders.shaders), None => Rc::new(RefCell::new(Shaders::new(&mut device, gl_type, &options)?)), }; + let backend_profile_counters = BackendProfileCounters::new(); + let dither_matrix_texture = if options.enable_dithering { let dither_matrix: [u8; 64] = [ 0, @@ -1039,12 +2353,13 @@ impl Renderer { ]; let texture = device.create_texture( - ImageBufferKind::Texture2D, + TextureTarget::Default, ImageFormat::R8, 8, 8, TextureFilter::Nearest, None, + 1, ); device.upload_texture_immediate(&texture, &dither_matrix); @@ -1053,20 +2368,41 @@ impl Renderer { None }; - let max_primitive_instance_count = - RendererOptions::MAX_INSTANCE_BUFFER_SIZE / mem::size_of::<PrimitiveInstanceData>(); - let vaos = vertex::RendererVAOs::new( - &mut device, - if options.enable_instancing { None } else { NonZeroUsize::new(max_primitive_instance_count) }, - ); + let x0 = 0.0; + let y0 = 0.0; + let x1 = 1.0; + let y1 = 1.0; + + let quad_indices: [u16; 6] = [0, 1, 2, 2, 1, 3]; + let quad_vertices = [ + PackedVertex { pos: [x0, y0] }, + PackedVertex { pos: [x1, y0] }, + PackedVertex { pos: [x0, y1] }, + PackedVertex { pos: [x1, y1] }, + ]; + + let prim_vao = device.create_vao(&desc::PRIM_INSTANCES); + device.bind_vao(&prim_vao); + device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static); + device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static); + + let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao); + let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao); + let border_vao = device.create_vao_with_new_instances(&desc::BORDER, &prim_vao); + let scale_vao = device.create_vao_with_new_instances(&desc::SCALE, &prim_vao); + let line_vao = device.create_vao_with_new_instances(&desc::LINE, &prim_vao); + let gradient_vao = device.create_vao_with_new_instances(&desc::GRADIENT, &prim_vao); + let resolve_vao = device.create_vao_with_new_instances(&desc::RESOLVE, &prim_vao); + let svg_filter_vao = device.create_vao_with_new_instances(&desc::SVG_FILTER, &prim_vao); + let composite_vao = device.create_vao_with_new_instances(&desc::COMPOSITE, &prim_vao); + let clear_vao = device.create_vao_with_new_instances(&desc::CLEAR, &prim_vao); + let texture_cache_upload_pbo = device.create_pbo(); - let texture_upload_pbo_pool = UploadPBOPool::new(&mut device, options.upload_pbo_default_size); - let staging_texture_pool = UploadTexturePool::new(); let texture_resolver = TextureResolver::new(&mut device); let mut vertex_data_textures = Vec::new(); for _ in 0 .. VERTEX_DATA_TEXTURE_COUNT { - vertex_data_textures.push(vertex::VertexDataTextures::new()); + vertex_data_textures.push(VertexDataTextures::new(&mut device)); } // On some (mostly older, integrated) GPUs, the normal GPU texture cache update path @@ -1077,23 +2413,17 @@ impl Renderer { // We want a better solution long-term, but for now this is a significant performance // improvement on HD4600 era GPUs, and shouldn't hurt performance in a noticeable // way on other systems running under ANGLE. - let is_software = device.get_capabilities().renderer_name.starts_with("Software"); + let is_angle = device.get_capabilities().renderer_name.contains("ANGLE"); - // On other GL platforms, like macOS or Android, creating many PBOs is very inefficient. - // This is what happens in GPU cache updates in PBO path. Instead, we switch everything - // except software GL to use the GPU scattered updates. - let supports_scatter = device.get_capabilities().supports_color_buffer_float; - let gpu_cache_texture = gpu_cache::GpuCacheTexture::new( + let gpu_cache_texture = GpuCacheTexture::new( &mut device, - supports_scatter && !is_software, + is_angle, )?; device.end_frame(); let backend_notifier = notifier.clone(); - let clear_alpha_targets_with_quads = !device.get_capabilities().supports_alpha_target_clears; - let prefer_subpixel_aa = options.force_subpixel_aa || (options.enable_subpixel_aa && use_dual_source_blending); let default_font_render_mode = match (options.enable_aa, prefer_subpixel_aa) { (true, true) => FontRenderMode::Subpixel, @@ -1102,7 +2432,7 @@ impl Renderer { }; let compositor_kind = match options.compositor_config { - CompositorConfig::Draw { max_partial_present_rects, draw_previous_partial_present_regions, .. } => { + CompositorConfig::Draw { max_partial_present_rects, draw_previous_partial_present_regions } => { CompositorKind::Draw { max_partial_present_rects, draw_previous_partial_present_regions } } CompositorConfig::Native { ref compositor, max_update_rects, .. } => { @@ -1110,7 +2440,7 @@ impl Renderer { CompositorKind::Native { max_update_rects, - capabilities, + virtual_surface_size: capabilities.virtual_surface_size, } } }; @@ -1120,20 +2450,17 @@ impl Renderer { dual_source_blending_is_enabled: true, dual_source_blending_is_supported: use_dual_source_blending, chase_primitive: options.chase_primitive, + global_enable_picture_caching: options.enable_picture_caching, testing: options.testing, gpu_supports_fast_clears: options.gpu_supports_fast_clears, gpu_supports_advanced_blend: ext_blend_equation_advanced, advanced_blend_is_coherent: ext_blend_equation_advanced_coherent, - gpu_supports_render_target_partial_update: device.get_capabilities().supports_render_target_partial_update, - external_images_require_copy: !device.get_capabilities().supports_image_external_essl3, - batch_lookback_count: RendererOptions::BATCH_LOOKBACK_COUNT, + batch_lookback_count: options.batch_lookback_count, background_color: options.clear_color, compositor_kind, tile_size_override: None, max_depth_ids: device.max_depth_ids(), - max_target_size: max_internal_texture_size, - force_invalidation: false, - is_software, + max_target_size: max_texture_size, }; info!("WR {:?}", config); @@ -1143,6 +2470,9 @@ impl Renderer { let enclosing_size_of_op = options.enclosing_size_of_op; let make_size_of_ops = move || size_of_op.map(|o| MallocSizeOfOps::new(o, enclosing_size_of_op)); + let thread_listener = Arc::new(options.thread_listener); + let thread_listener_for_rayon_start = thread_listener.clone(); + let thread_listener_for_rayon_end = thread_listener.clone(); let workers = options .workers .take() @@ -1151,34 +2481,44 @@ impl Renderer { .thread_name(|idx|{ format!("WRWorker#{}", idx) }) .start_handler(move |idx| { register_thread_with_profiler(format!("WRWorker#{}", idx)); - thread_started(&format!("WRWorker#{}", idx)); + if let Some(ref thread_listener) = *thread_listener_for_rayon_start { + thread_listener.thread_started(&format!("WRWorker#{}", idx)); + } }) - .exit_handler(move |_idx| { - thread_stopped(); + .exit_handler(move |idx| { + if let Some(ref thread_listener) = *thread_listener_for_rayon_end { + thread_listener.thread_stopped(&format!("WRWorker#{}", idx)); + } }) .build(); Arc::new(worker.unwrap()) }); let sampler = options.sampler; let namespace_alloc_by_client = options.namespace_alloc_by_client; + let max_glyph_cache_size = options.max_glyph_cache_size.unwrap_or(GlyphCache::DEFAULT_MAX_BYTES_USED); let font_instances = SharedFontInstanceMap::new(); let blob_image_handler = options.blob_image_handler.take(); + let thread_listener_for_render_backend = thread_listener.clone(); + let thread_listener_for_scene_builder = thread_listener.clone(); + let thread_listener_for_lp_scene_builder = thread_listener.clone(); let scene_builder_hooks = options.scene_builder_hooks; let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0)); let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0)); let lp_scene_thread_name = format!("WRSceneBuilderLP#{}", options.renderer_id.unwrap_or(0)); - let glyph_rasterizer = GlyphRasterizer::new(workers, device.get_capabilities().supports_r8_texture_upload)?; + let glyph_rasterizer = GlyphRasterizer::new(workers)?; - let (scene_builder_channels, scene_tx) = + let (scene_builder_channels, scene_tx, backend_scene_tx, scene_rx) = SceneBuilderThreadChannels::new(api_tx.clone()); let sb_font_instances = font_instances.clone(); thread::Builder::new().name(scene_thread_name.clone()).spawn(move || { register_thread_with_profiler(scene_thread_name.clone()); - thread_started(&scene_thread_name); + if let Some(ref thread_listener) = *thread_listener_for_scene_builder { + thread_listener.thread_started(&scene_thread_name); + } let mut scene_builder = SceneBuilderThread::new( config, @@ -1190,24 +2530,31 @@ impl Renderer { ); scene_builder.run(); - thread_stopped(); + if let Some(ref thread_listener) = *thread_listener_for_scene_builder { + thread_listener.thread_stopped(&scene_thread_name); + } })?; let low_priority_scene_tx = if options.support_low_priority_transactions { - let (low_priority_scene_tx, low_priority_scene_rx) = unbounded_channel(); + let (low_priority_scene_tx, low_priority_scene_rx) = channel(); let lp_builder = LowPrioritySceneBuilderThread { rx: low_priority_scene_rx, tx: scene_tx.clone(), + simulate_slow_ms: 0, }; thread::Builder::new().name(lp_scene_thread_name.clone()).spawn(move || { register_thread_with_profiler(lp_scene_thread_name.clone()); - thread_started(&lp_scene_thread_name); + if let Some(ref thread_listener) = *thread_listener_for_lp_scene_builder { + thread_listener.thread_started(&lp_scene_thread_name); + } let mut scene_builder = lp_builder; scene_builder.run(); - thread_stopped(); + if let Some(ref thread_listener) = *thread_listener_for_lp_scene_builder { + thread_listener.thread_stopped(&lp_scene_thread_name); + } })?; low_priority_scene_tx @@ -1219,29 +2566,32 @@ impl Renderer { .as_ref() .map(|handler| handler.create_similar()); - let texture_cache_config = options.texture_cache_config.clone(); - let mut picture_tile_size = options.picture_tile_size.unwrap_or(picture::TILE_SIZE_DEFAULT); - // Clamp the picture tile size to reasonable values. - picture_tile_size.width = picture_tile_size.width.max(128).min(4096); - picture_tile_size.height = picture_tile_size.height.max(128).min(4096); - - let rb_scene_tx = scene_tx.clone(); let rb_font_instances = font_instances.clone(); let enable_multithreading = options.enable_multithreading; + let texture_cache_eviction_threshold_bytes = options.texture_cache_eviction_threshold_bytes; + let texture_cache_max_evictions_per_frame = options.texture_cache_max_evictions_per_frame; thread::Builder::new().name(rb_thread_name.clone()).spawn(move || { register_thread_with_profiler(rb_thread_name.clone()); - thread_started(&rb_thread_name); + if let Some(ref thread_listener) = *thread_listener_for_render_backend { + thread_listener.thread_started(&rb_thread_name); + } let texture_cache = TextureCache::new( - max_internal_texture_size, - image_tiling_threshold, - picture_tile_size, + max_texture_size, + max_texture_layers, + if config.global_enable_picture_caching { + tile_cache_sizes(config.testing) + } else { + &[] + }, + start_size, color_cache_formats, swizzle_settings, - &texture_cache_config, + texture_cache_eviction_threshold_bytes, + texture_cache_max_evictions_per_frame, ); - let glyph_cache = GlyphCache::new(); + let glyph_cache = GlyphCache::new(max_glyph_cache_size); let mut resource_cache = ResourceCache::new( texture_cache, @@ -1255,7 +2605,10 @@ impl Renderer { let mut backend = RenderBackend::new( api_rx, result_tx, - rb_scene_tx, + scene_tx, + low_priority_scene_tx, + backend_scene_tx, + scene_rx, device_pixel_ratio, resource_cache, backend_notifier, @@ -1266,8 +2619,10 @@ impl Renderer { debug_flags, namespace_alloc_by_client, ); - backend.run(); - thread_stopped(); + backend.run(backend_profile_counters); + if let Some(ref thread_listener) = *thread_listener_for_render_backend { + thread_listener.thread_stopped(&rb_thread_name); + } })?; let debug_method = if !options.enable_gpu_markers { @@ -1284,14 +2639,15 @@ impl Renderer { info!("using {:?}", debug_method); - let gpu_profiler = GpuProfiler::new(Rc::clone(device.rc_gl()), debug_method); + let gpu_profile = GpuProfiler::new(Rc::clone(device.rc_gl()), debug_method); #[cfg(feature = "capture")] let read_fbo = device.create_fbo(); let mut renderer = Renderer { result_rx, + debug_server, device, - active_documents: FastHashMap::default(), + active_documents: Vec::new(), pending_texture_updates: Vec::new(), pending_texture_cache_updates: false, pending_native_surface_updates: Vec::new(), @@ -1299,36 +2655,52 @@ impl Renderer { pending_gpu_cache_clear: false, pending_shader_updates: Vec::new(), shaders, - debug: debug::LazyInitializedDebugRenderer::new(), + debug: LazyInitializedDebugRenderer::new(), debug_flags: DebugFlags::empty(), - profile: TransactionProfile::new(), - frame_counter: 0, - resource_upload_time: 0.0, - gpu_cache_upload_time: 0.0, + backend_profile_counters: BackendProfileCounters::new(), + profile_counters: RendererProfileCounters::new(), + resource_upload_time: 0, + gpu_cache_upload_time: 0, profiler: Profiler::new(), + new_frame_indicator: ChangeIndicator::new(), + new_scene_indicator: ChangeIndicator::new(), + slow_frame_indicator: ChangeIndicator::new(), + slow_txn_indicator: ChangeIndicator::new(), max_recorded_profiles: options.max_recorded_profiles, clear_color: options.clear_color, enable_clear_scissor: options.enable_clear_scissor, enable_advanced_blend_barriers: !ext_blend_equation_advanced_coherent, clear_caches_with_quads: options.clear_caches_with_quads, - clear_alpha_targets_with_quads, last_time: 0, - gpu_profiler, - vaos, + gpu_profile, + vaos: RendererVAOs { + prim_vao, + blur_vao, + clip_vao, + border_vao, + scale_vao, + gradient_vao, + resolve_vao, + line_vao, + svg_filter_vao, + composite_vao, + clear_vao, + }, vertex_data_textures, current_vertex_data_textures: 0, pipeline_info: PipelineInfo::default(), dither_matrix_texture, external_image_handler: None, + output_image_handler: None, size_of_ops: make_size_of_ops(), + output_targets: FastHashMap::default(), cpu_profiles: VecDeque::new(), gpu_profiles: VecDeque::new(), gpu_cache_texture, gpu_cache_debug_chunks: Vec::new(), gpu_cache_frame_id: FrameId::INVALID, gpu_cache_overflow: false, - texture_upload_pbo_pool, - staging_texture_pool, + texture_cache_upload_pbo, texture_resolver, renderer_errors: Vec::new(), async_frame_recorder: None, @@ -1348,22 +2720,14 @@ impl Renderer { current_compositor_kind: compositor_kind, allocated_native_surfaces: FastHashSet::default(), debug_overlay_state: DebugOverlayState::new(), - buffer_damage_tracker: BufferDamageTracker::default(), - max_primitive_instance_count, - enable_instancing: options.enable_instancing, + prev_dirty_rect: DeviceRect::zero(), }; // We initially set the flags to default and then now call set_debug_flags // to ensure any potential transition when enabling a flag is run. renderer.set_debug_flags(debug_flags); - let sender = RenderApiSender::new( - api_tx, - scene_tx, - low_priority_scene_tx, - blob_image_handler, - font_instances, - ); + let sender = RenderApiSender::new(api_tx, blob_image_handler, font_instances); Ok((renderer, sender)) } @@ -1395,12 +2759,8 @@ impl Renderer { self.device.preferred_color_formats().external } - pub fn required_texture_stride_alignment(&self, format: ImageFormat) -> usize { - self.device.required_pbo_stride().num_bytes(format).get() - } - - pub fn set_clear_color(&mut self, color: Option<ColorF>) { - self.clear_color = color; + pub fn optimal_texture_stride_alignment(&self, format: ImageFormat) -> usize { + self.device.optimal_pbo_stride().num_bytes(format).get() } pub fn flush_pipeline_info(&mut self) -> PipelineInfo { @@ -1417,7 +2777,6 @@ impl Renderer { /// Should be called before `render()`, as texture cache updates are done here. pub fn update(&mut self) { profile_scope!("update"); - // Pull any pending results and return the most recent. while let Ok(msg) = self.result_rx.try_recv() { match msg { @@ -1429,31 +2788,30 @@ impl Renderer { } ResultMsg::PublishDocument( document_id, - mut doc, + doc, resource_update_list, + profile_counters, ) => { - // Add a new document to the active set - - // If the document we are replacing must be drawn (in order to - // update the texture cache), issue a render just to - // off-screen targets, ie pass None to render_impl. We do this - // because a) we don't need to render to the main framebuffer - // so it is cheaper not to, and b) doing so without a - // subsequent present would break partial present. - if let Some(mut prev_doc) = self.active_documents.remove(&document_id) { - doc.profile.merge(&mut prev_doc.profile); - - if prev_doc.frame.must_be_drawn() { - self.render_impl( - document_id, - &mut prev_doc, - None, - 0, - ).ok(); - } + if doc.is_new_scene { + self.new_scene_indicator.changed(); } - self.active_documents.insert(document_id, doc); + // Add a new document to the active set, expressed as a `Vec` in order + // to re-order based on `DocumentLayer` during rendering. + match self.active_documents.iter().position(|&(id, _)| id == document_id) { + Some(pos) => { + // If the document we are replacing must be drawn + // (in order to update the texture cache), issue + // a render just to off-screen targets. + if self.active_documents[pos].1.frame.must_be_drawn() { + let device_size = self.device_size; + self.render_impl(device_size).ok(); + } + + self.active_documents[pos].1 = doc; + } + None => self.active_documents.push((document_id, doc)), + } // IMPORTANT: The pending texture cache updates must be applied // *after* the previous frame has been rendered above @@ -1469,6 +2827,7 @@ impl Renderer { self.pending_texture_cache_updates |= !resource_update_list.texture_updates.updates.is_empty(); self.pending_texture_updates.push(resource_update_list.texture_updates); self.pending_native_surface_updates.extend(resource_update_list.native_surface_updates); + self.backend_profile_counters = profile_counters; self.documents_seen.insert(document_id); } ResultMsg::UpdateGpuCache(mut list) => { @@ -1511,22 +2870,15 @@ impl Renderer { // if any of the existing documents have not rendered yet, and // have picture/texture cache targets, force a render so that // those targets are updated. - let active_documents = mem::replace( - &mut self.active_documents, - FastHashMap::default(), - ); - for (doc_id, mut doc) in active_documents { - if doc.frame.must_be_drawn() { - // As this render will not be presented, we must pass None to - // render_impl. This avoids interfering with partial present - // logic, as well as being more efficient. - self.render_impl( - doc_id, - &mut doc, - None, - 0, - ).ok(); - } + let must_be_drawn = self.active_documents + .iter() + .any(|(_, doc)| { + doc.frame.must_be_drawn() + }); + + if must_be_drawn { + let device_size = self.device_size; + self.render_impl(device_size).ok(); } } @@ -1544,11 +2896,19 @@ impl Renderer { // the device module asserts if we delete textures while // not in a frame. if memory_pressure { - self.texture_upload_pbo_pool.on_memory_pressure(&mut self.device); - self.staging_texture_pool.delete_textures(&mut self.device); + self.texture_resolver.on_memory_pressure( + &mut self.device, + ); } self.device.end_frame(); + // If we receive a `PublishDocument` message followed by this one + // within the same update we need to cancel the frame because we + // might have deleted the resources in use in the frame due to a + // memory pressure event. + if memory_pressure { + self.active_documents.clear(); + } } ResultMsg::AppendNotificationRequests(mut notifications) => { // We need to know specifically if there are any pending @@ -1572,6 +2932,10 @@ impl Renderer { self.pending_shader_updates.push(path); } ResultMsg::DebugOutput(output) => match output { + DebugOutput::FetchDocuments(string) | + DebugOutput::FetchClipScrollTree(string) => { + self.debug_server.send(string); + } #[cfg(feature = "capture")] DebugOutput::SaveCapture(config, deferred) => { self.save_capture(config, deferred); @@ -1589,12 +2953,234 @@ impl Renderer { } } + #[cfg(not(feature = "debugger"))] + fn get_screenshot_for_debugger(&mut self) -> String { + // Avoid unused param warning. + let _ = &self.debug_server; + String::new() + } + + #[cfg(feature = "debugger")] + fn get_screenshot_for_debugger(&mut self) -> String { + use api::{ImageDescriptor, ImageDescriptorFlags}; + + let desc = ImageDescriptor::new(1024, 768, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE); + let data = self.device.read_pixels(&desc); + let screenshot = debug_server::Screenshot::new(desc.size, data); + + serde_json::to_string(&screenshot).unwrap() + } + + #[cfg(not(feature = "debugger"))] + fn get_passes_for_debugger(&self) -> String { + // Avoid unused param warning. + let _ = &self.debug_server; + String::new() + } + + #[cfg(feature = "debugger")] + fn debug_alpha_target(target: &AlphaRenderTarget) -> debug_server::Target { + let mut debug_target = debug_server::Target::new("A8"); + + debug_target.add( + debug_server::BatchKind::Cache, + "Scalings", + target.scalings.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "Zero Clears", + target.zero_clears.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "One Clears", + target.one_clears.len(), + ); + debug_target.add( + debug_server::BatchKind::Clip, + "BoxShadows [p]", + target.clip_batcher.primary_clips.box_shadows.len(), + ); + debug_target.add( + debug_server::BatchKind::Clip, + "BoxShadows [s]", + target.clip_batcher.secondary_clips.box_shadows.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "Vertical Blur", + target.vertical_blurs.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "Horizontal Blur", + target.horizontal_blurs.len(), + ); + debug_target.add( + debug_server::BatchKind::Clip, + "Slow Rectangles [p]", + target.clip_batcher.primary_clips.slow_rectangles.len(), + ); + debug_target.add( + debug_server::BatchKind::Clip, + "Fast Rectangles [p]", + target.clip_batcher.primary_clips.fast_rectangles.len(), + ); + debug_target.add( + debug_server::BatchKind::Clip, + "Slow Rectangles [s]", + target.clip_batcher.secondary_clips.slow_rectangles.len(), + ); + debug_target.add( + debug_server::BatchKind::Clip, + "Fast Rectangles [s]", + target.clip_batcher.secondary_clips.fast_rectangles.len(), + ); + for (_, items) in target.clip_batcher.primary_clips.images.iter() { + debug_target.add(debug_server::BatchKind::Clip, "Image mask [p]", items.len()); + } + for (_, items) in target.clip_batcher.secondary_clips.images.iter() { + debug_target.add(debug_server::BatchKind::Clip, "Image mask [s]", items.len()); + } + + debug_target + } + + #[cfg(feature = "debugger")] + fn debug_color_target(target: &ColorRenderTarget) -> debug_server::Target { + let mut debug_target = debug_server::Target::new("RGBA8"); + + debug_target.add( + debug_server::BatchKind::Cache, + "Scalings", + target.scalings.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "Readbacks", + target.readbacks.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "Vertical Blur", + target.vertical_blurs.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "Horizontal Blur", + target.horizontal_blurs.len(), + ); + debug_target.add( + debug_server::BatchKind::Cache, + "SVG Filters", + target.svg_filters.iter().map(|(_, batch)| batch.len()).sum(), + ); + + for alpha_batch_container in &target.alpha_batch_containers { + for batch in alpha_batch_container.opaque_batches.iter().rev() { + debug_target.add( + debug_server::BatchKind::Opaque, + batch.key.kind.debug_name(), + batch.instances.len(), + ); + } + + for batch in &alpha_batch_container.alpha_batches { + debug_target.add( + debug_server::BatchKind::Alpha, + batch.key.kind.debug_name(), + batch.instances.len(), + ); + } + } + + debug_target + } + + #[cfg(feature = "debugger")] + fn debug_texture_cache_target(target: &TextureCacheRenderTarget) -> debug_server::Target { + let mut debug_target = debug_server::Target::new("Texture Cache"); + + debug_target.add( + debug_server::BatchKind::Cache, + "Horizontal Blur", + target.horizontal_blurs.len(), + ); + + debug_target + } + + #[cfg(feature = "debugger")] + fn get_passes_for_debugger(&self) -> String { + let mut debug_passes = debug_server::PassList::new(); + + for &(_, ref render_doc) in &self.active_documents { + for pass in &render_doc.frame.passes { + let mut debug_targets = Vec::new(); + match pass.kind { + RenderPassKind::MainFramebuffer { ref main_target, .. } => { + debug_targets.push(Self::debug_color_target(main_target)); + } + RenderPassKind::OffScreen { ref alpha, ref color, ref texture_cache, .. } => { + debug_targets.extend(alpha.targets.iter().map(Self::debug_alpha_target)); + debug_targets.extend(color.targets.iter().map(Self::debug_color_target)); + debug_targets.extend(texture_cache.iter().map(|(_, target)| Self::debug_texture_cache_target(target))); + } + } + + debug_passes.add(debug_server::Pass { targets: debug_targets }); + } + } + + serde_json::to_string(&debug_passes).unwrap() + } + + #[cfg(not(feature = "debugger"))] + fn get_render_tasks_for_debugger(&self) -> String { + String::new() + } + + #[cfg(feature = "debugger")] + fn get_render_tasks_for_debugger(&self) -> String { + let mut debug_root = debug_server::RenderTaskList::new(); + + for &(_, ref render_doc) in &self.active_documents { + let debug_node = debug_server::TreeNode::new("document render tasks"); + let mut builder = debug_server::TreeNodeBuilder::new(debug_node); + + let render_tasks = &render_doc.frame.render_tasks; + match render_tasks.tasks.first() { + Some(main_task) => main_task.print_with(&mut builder, render_tasks), + None => continue, + }; + + debug_root.add(builder.build()); + } + + serde_json::to_string(&debug_root).unwrap() + } + fn handle_debug_command(&mut self, command: DebugCommand) { match command { DebugCommand::EnableDualSourceBlending(_) | DebugCommand::SetPictureTileSize(_) => { panic!("Should be handled by render backend"); } + DebugCommand::FetchDocuments | + DebugCommand::FetchClipScrollTree => {} + DebugCommand::FetchRenderTasks => { + let json = self.get_render_tasks_for_debugger(); + self.debug_server.send(json); + } + DebugCommand::FetchPasses => { + let json = self.get_passes_for_debugger(); + self.debug_server.send(json); + } + DebugCommand::FetchScreenshot => { + let json = self.get_screenshot_for_debugger(); + self.debug_server.send(json); + } DebugCommand::SaveCapture(..) | DebugCommand::LoadCapture(..) | DebugCommand::StartCaptureSequence(..) | @@ -1603,11 +3189,22 @@ impl Renderer { } DebugCommand::ClearCaches(_) | DebugCommand::SimulateLongSceneBuild(_) + | DebugCommand::SimulateLongLowPrioritySceneBuild(_) | DebugCommand::EnableNativeCompositor(_) | DebugCommand::SetBatchingLookback(_) | DebugCommand::EnableMultithreading(_) => {} DebugCommand::InvalidateGpuCache => { - self.gpu_cache_texture.invalidate(); + match self.gpu_cache_texture.bus { + GpuCacheBus::PixelBuffer { ref mut rows, .. } => { + info!("Invalidating GPU caches"); + for row in rows { + row.add_dirty(0, MAX_VERTEX_TEXTURE_WIDTH); + } + } + GpuCacheBus::Scatter { .. } => { + warn!("Unable to invalidate scattered GPU cache"); + } + } } DebugCommand::SetFlags(flags) => { self.set_debug_flags(flags); @@ -1620,6 +3217,11 @@ impl Renderer { self.external_image_handler = Some(handler); } + /// Set a callback for handling external outputs. + pub fn set_output_image_handler(&mut self, handler: Box<dyn OutputImageHandler>) { + self.output_image_handler = Some(handler); + } + /// Retrieve (and clear) the current list of recorded frame profiles. pub fn get_frame_profiles(&mut self) -> (Vec<CpuProfile>, Vec<GpuProfile>) { let cpu_profiles = self.cpu_profiles.drain(..).collect(); @@ -1636,46 +3238,13 @@ impl Renderer { /// Renders the current frame. /// /// A Frame is supplied by calling [`generate_frame()`][webrender_api::Transaction::generate_frame]. - /// buffer_age is the age of the current backbuffer. It is only relevant if partial present - /// is active, otherwise 0 should be passed here. pub fn render( &mut self, device_size: DeviceIntSize, - buffer_age: usize, ) -> Result<RenderResults, Vec<RendererError>> { self.device_size = Some(device_size); - // TODO(gw): We want to make the active document that is - // being rendered configurable via the public - // API in future. For now, just select the last - // added document as the active one to render - // (Gecko only ever creates a single document - // per renderer right now). - let doc_id = self.active_documents.keys().last().cloned(); - - let result = match doc_id { - Some(doc_id) => { - // Remove the doc from the map to appease the borrow checker - let mut doc = self.active_documents - .remove(&doc_id) - .unwrap(); - - let result = self.render_impl( - doc_id, - &mut doc, - Some(device_size), - buffer_age, - ); - - self.active_documents.insert(doc_id, doc); - - result - } - None => { - self.last_time = precise_time_ns(); - Ok(RenderResults::default()) - } - }; + let result = self.render_impl(Some(device_size)); drain_filter( &mut self.notifications, @@ -1702,7 +3271,10 @@ impl Renderer { DebugFlags::RENDER_TARGET_DBG | DebugFlags::TEXTURE_CACHE_DBG | DebugFlags::EPOCHS | + DebugFlags::NEW_FRAME_INDICATOR | + DebugFlags::NEW_SCENE_INDICATOR | DebugFlags::GPU_CACHE_DBG | + DebugFlags::SLOW_FRAME_INDICATOR | DebugFlags::PICTURE_CACHING_DBG | DebugFlags::PRIMITIVE_DBG | DebugFlags::ZOOM_DBG @@ -1738,21 +3310,13 @@ impl Renderer { } /// Bind a draw target for the debug / profiler overlays, if required. - fn bind_debug_overlay(&mut self, device_size: DeviceIntSize) -> Option<DrawTarget> { + fn bind_debug_overlay(&mut self) { // Debug overlay setup are only required in native compositing mode if self.debug_overlay_state.is_enabled { if let CompositorKind::Native { .. } = self.current_compositor_kind { let compositor = self.compositor_config.compositor().unwrap(); let surface_size = self.debug_overlay_state.current_size.unwrap(); - // Ensure old surface is invalidated before binding - compositor.invalidate_tile( - NativeTileId::DEBUG_OVERLAY, - DeviceIntRect::new( - DeviceIntPoint::zero(), - surface_size, - ), - ); // Bind the native surface let surface_info = compositor.bind( NativeTileId::DEBUG_OVERLAY, @@ -1777,19 +3341,10 @@ impl Renderer { // When native compositing, clear the debug overlay each frame. self.device.clear_target( Some([0.0, 0.0, 0.0, 0.0]), - None, // debug renderer does not use depth + Some(1.0), None, ); - - Some(draw_target) - } else { - // If we're not using the native compositor, then the default - // frame buffer is already bound. Create a DrawTarget for it and - // return it. - Some(DrawTarget::new_default(device_size, self.device.surface_origin_is_top_left())) } - } else { - None } } @@ -1804,35 +3359,32 @@ impl Renderer { compositor.add_surface( NativeSurfaceId::DEBUG_OVERLAY, - CompositorSurfaceTransform::identity(), + DeviceIntPoint::zero(), DeviceIntRect::new( DeviceIntPoint::zero(), self.debug_overlay_state.current_size.unwrap(), ), - ImageRendering::Auto, ); } } } - // If device_size is None, don't render to the main frame buffer. This is useful to - // update texture cache render tasks but avoid doing a full frame render. If the - // render is not going to be presented, then this must be set to None, as performing a - // composite without a present will confuse partial present. + // If device_size is None, don't render + // to the main frame buffer. This is useful + // to update texture cache render tasks but + // avoid doing a full frame render. fn render_impl( &mut self, - doc_id: DocumentId, - active_doc: &mut RenderedDocument, device_size: Option<DeviceIntSize>, - buffer_age: usize, ) -> Result<RenderResults, Vec<RendererError>> { profile_scope!("render"); let mut results = RenderResults::default(); - self.profile.start_time(profiler::RENDERER_TIME); - - self.staging_texture_pool.begin_frame(); + if self.active_documents.is_empty() { + self.last_time = precise_time_ns(); + return Ok(results); + } - let compositor_kind = active_doc.frame.composite_state.compositor_kind; + let compositor_kind = self.active_documents[0].1.frame.composite_state.compositor_kind; // CompositorKind is updated if self.current_compositor_kind != compositor_kind { let enable = match (self.current_compositor_kind, compositor_kind) { @@ -1849,19 +3401,21 @@ impl Renderer { (CompositorKind::Draw { .. }, CompositorKind::Native { .. }) => { true } - (current_compositor_kind, active_doc_compositor_kind) => { - warn!("Compositor mismatch, assuming this is Wrench running. Current {:?}, active {:?}", - current_compositor_kind, active_doc_compositor_kind); - false + (_, _) => { + unreachable!(); } }; - if let Some(config) = self.compositor_config.compositor() { - config.enable_native_compositor(enable); - } + self.compositor_config + .compositor() + .unwrap() + .enable_native_compositor(enable); self.current_compositor_kind = compositor_kind; } + let mut frame_profiles = Vec::new(); + let mut profile_timers = RendererProfileTimers::new(); + // The texture resolver scope should be outside of any rendering, including // debug rendering. This ensures that when we return render targets to the // pool via glInvalidateFramebuffer, we don't do any debug rendering after @@ -1870,14 +3424,28 @@ impl Renderer { // resolve step when the debug overlay is enabled. self.texture_resolver.begin_frame(); - if let Some(device_size) = device_size { - self.update_gpu_profile(device_size); - } + let profile_samplers = { + let _gm = self.gpu_profile.start_marker("build samples"); + // Block CPU waiting for last frame's GPU profiles to arrive. + // In general this shouldn't block unless heavily GPU limited. + let (gpu_frame_id, timers, samplers) = self.gpu_profile.build_samples(); + + if self.max_recorded_profiles > 0 { + while self.gpu_profiles.len() >= self.max_recorded_profiles { + self.gpu_profiles.pop_front(); + } + self.gpu_profiles + .push_back(GpuProfile::new(gpu_frame_id, &timers)); + } + profile_timers.gpu_samples = timers; + samplers + }; - let cpu_frame_id = { - let _gm = self.gpu_profiler.start_marker("begin frame"); + + let cpu_frame_id = profile_timers.cpu_time.profile(|| { + let _gm = self.gpu_profile.start_marker("begin frame"); let frame_id = self.device.begin_frame(); - self.gpu_profiler.begin_frame(frame_id); + self.gpu_profile.begin_frame(frame_id); self.device.disable_scissor(); self.device.disable_depth(); @@ -1888,33 +3456,41 @@ impl Renderer { self.update_native_surfaces(); frame_id - }; - - if let Some(device_size) = device_size { - // Inform the client that we are starting a composition transaction if native - // compositing is enabled. This needs to be done early in the frame, so that - // we can create debug overlays after drawing the main surfaces. - if let CompositorKind::Native { .. } = self.current_compositor_kind { - let compositor = self.compositor_config.compositor().unwrap(); - compositor.begin_frame(); - } + }); - // Update the state of the debug overlay surface, ensuring that - // the compositor mode has a suitable surface to draw to, if required. - self.update_debug_overlay(device_size); + // Inform the client that we are starting a composition transaction if native + // compositing is enabled. This needs to be done early in the frame, so that + // we can create debug overlays after drawing the main surfaces. + if let CompositorKind::Native { .. } = self.current_compositor_kind { + let compositor = self.compositor_config.compositor().unwrap(); + compositor.begin_frame(); } - let frame = &mut active_doc.frame; - let profile = &mut active_doc.profile; - assert!(self.current_compositor_kind == frame.composite_state.compositor_kind); + profile_timers.cpu_time.profile(|| { + //Note: another borrowck dance + let mut active_documents = mem::replace(&mut self.active_documents, Vec::default()); + // sort by the document layer id + active_documents.sort_by_key(|&(_, ref render_doc)| render_doc.frame.layer); - if self.shared_texture_cache_cleared { - assert!(self.documents_seen.contains(&doc_id), - "Cleared texture cache without sending new document frame."); - } + #[cfg(feature = "replay")] + self.texture_resolver.external_images.extend( + self.owned_external_images.iter().map(|(key, value)| (*key, value.clone())) + ); + + let last_document_index = active_documents.len() - 1; + for (doc_index, (document_id, RenderedDocument { ref mut frame, .. })) in active_documents.iter_mut().enumerate() { + assert!(self.current_compositor_kind == frame.composite_state.compositor_kind); - match self.prepare_gpu_cache(&frame.deferred_resolves) { - Ok(..) => { + if self.shared_texture_cache_cleared { + assert!(self.documents_seen.contains(&document_id), + "Cleared texture cache without sending new document frame."); + } + + frame.profile_counters.reset_targets(); + if let Err(e) = self.prepare_gpu_cache(frame) { + self.renderer_errors.push(e); + continue; + } assert!(frame.gpu_cache_frame_id <= self.gpu_cache_frame_id, "Received frame depends on a later GPU cache epoch ({:?}) than one we received last via `UpdateGpuCache` ({:?})", frame.gpu_cache_frame_id, self.gpu_cache_frame_id); @@ -1927,55 +3503,81 @@ impl Renderer { self.draw_frame( frame, device_size, - buffer_age, + cpu_frame_id, &mut results, + doc_index == 0, ); - // TODO(nical): do this automatically by selecting counters in the wr profiler // Profile marker for the number of invalidated picture cache if thread_is_being_profiled() { - let duration = Duration::new(0,0); - if let Some(n) = self.profile.get(profiler::RENDERED_PICTURE_TILES) { - let message = (n as usize).to_string(); - add_text_marker(cstr!("NumPictureCacheInvalidated"), &message, duration); - } + let num_invalidated = self.profile_counters.rendered_picture_cache_tiles.get_accum(); + let message = format!("NumPictureCacheInvalidated: {}", num_invalidated); + add_event_marker(&(CString::new(message).unwrap())); } if device_size.is_some() { self.draw_frame_debug_items(&frame.debug_items); } + if self.debug_flags.contains(DebugFlags::PROFILER_DBG) { + frame_profiles.push(frame.profile_counters.clone()); + } - self.profile.merge(profile); - } - Err(e) => { - self.renderer_errors.push(e); + let dirty_regions = + mem::replace(&mut frame.recorded_dirty_regions, Vec::new()); + results.recorded_dirty_regions.extend(dirty_regions); + + // If we're the last document, don't call end_pass here, because we'll + // be moving on to drawing the debug overlays. See the comment above + // the end_pass call in draw_frame about debug draw overlays + // for a bit more context. + if doc_index != last_document_index { + self.texture_resolver.end_pass(&mut self.device, None, None); + } } - } - self.unlock_external_images(&frame.deferred_resolves); + self.unlock_external_images(); + self.active_documents = active_documents; - let _gm = self.gpu_profiler.start_marker("end frame"); - self.gpu_profiler.end_frame(); + let _gm = self.gpu_profile.start_marker("end frame"); + self.gpu_profile.end_frame(); + }); + + if let Some(device_size) = device_size { + // Update the state of the debug overlay surface, ensuring that + // the compositor mode has a suitable surface to draw to, if required. + self.update_debug_overlay(device_size); - let debug_overlay = device_size.and_then(|device_size| { // Bind a surface to draw the debug / profiler information to. - self.bind_debug_overlay(device_size).map(|draw_target| { - self.draw_render_target_debug(&draw_target); - self.draw_texture_cache_debug(&draw_target); - self.draw_gpu_cache_debug(device_size); - self.draw_zoom_debug(device_size); - self.draw_epoch_debug(); - draw_target - }) - }); + self.bind_debug_overlay(); - self.profile.end_time(profiler::RENDERER_TIME); - self.profile.end_time_if_started(profiler::TOTAL_FRAME_CPU_TIME); + self.draw_render_target_debug(device_size); + self.draw_texture_cache_debug(device_size); + self.draw_gpu_cache_debug(device_size); + self.draw_zoom_debug(device_size); + self.draw_epoch_debug(); + } let current_time = precise_time_ns(); if device_size.is_some() { - let time = profiler::ns_to_ms(current_time - self.last_time); - self.profile.set(profiler::FRAME_TIME, time); + let ns = current_time - self.last_time; + self.profile_counters.frame_time.set(ns); + } + + let frame_cpu_time_ns = self.backend_profile_counters.total_time.get() + + profile_timers.cpu_time.get(); + let frame_cpu_time_ms = frame_cpu_time_ns as f64 / 1000000.0; + if frame_cpu_time_ms > 16.0 { + self.slow_frame_indicator.changed(); + } + + if self.backend_profile_counters.scene_changed { + let txn_time_ns = self.backend_profile_counters.txn.total_send_time.get() + + self.backend_profile_counters.txn.display_list_build_time.get() + + self.backend_profile_counters.txn.scene_build_time.get(); + let txn_time_ms = txn_time_ns as f64 / 1000000.0; + if txn_time_ms > 100.0 { + self.slow_txn_indicator.changed(); + } } if self.max_recorded_profiles > 0 { @@ -1984,80 +3586,119 @@ impl Renderer { } let cpu_profile = CpuProfile::new( cpu_frame_id, - (self.profile.get_or(profiler::FRAME_BUILDING_TIME, 0.0) * 1000000.0) as u64, - (self.profile.get_or(profiler::RENDERER_TIME, 0.0) * 1000000.0) as u64, - self.profile.get_or(profiler::DRAW_CALLS, 0.0) as usize, + self.backend_profile_counters.total_time.get(), + profile_timers.cpu_time.get(), + self.profile_counters.draw_calls.get(), ); self.cpu_profiles.push_back(cpu_profile); } - if thread_is_being_profiled() { - let duration = Duration::new(0,0); - let message = (self.profile.get_or(profiler::DRAW_CALLS, 0.0) as usize).to_string(); - add_text_marker(cstr!("NumDrawCalls"), &message, duration); - } - - results.stats.texture_upload_mb = self.profile.get_or(profiler::TEXTURE_UPLOADS_MEM, 0.0); - self.frame_counter += 1; - results.stats.resource_upload_time = self.resource_upload_time; - self.resource_upload_time = 0.0; - results.stats.gpu_cache_upload_time = self.gpu_cache_upload_time; - self.gpu_cache_upload_time = 0.0; - - if let Some(stats) = active_doc.frame_stats.take() { - // Copy the full frame stats to RendererStats - results.stats.merge(&stats); - - self.profiler.update_frame_stats(stats); - } - - // Note: this clears the values in self.profile. - self.profiler.set_counters(&mut self.profile); - - // Note: profile counters must be set before this or they will count for next frame. - self.profiler.update(); - - if self.debug_flags.intersects(DebugFlags::PROFILER_DBG | DebugFlags::PROFILER_CAPTURE) { + if self.debug_flags.contains(DebugFlags::PROFILER_DBG) { if let Some(device_size) = device_size { //TODO: take device/pixel ratio into equation? if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) { + let style = if self.debug_flags.contains(DebugFlags::SMART_PROFILER) { + ProfileStyle::Smart + } else if self.debug_flags.contains(DebugFlags::COMPACT_PROFILER) { + ProfileStyle::Compact + } else { + ProfileStyle::Full + }; + + let screen_fraction = 1.0 / device_size.to_f32().area(); self.profiler.draw_profile( - self.frame_counter, + &frame_profiles, + &self.backend_profile_counters, + &self.profile_counters, + &mut profile_timers, + &profile_samplers, + screen_fraction, debug_renderer, - device_size, + style, ); } } } - if self.debug_flags.contains(DebugFlags::ECHO_DRIVER_MESSAGES) { - self.device.echo_driver_messages(); + let mut x = 0.0; + if self.debug_flags.contains(DebugFlags::NEW_FRAME_INDICATOR) { + if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) { + self.new_frame_indicator.changed(); + self.new_frame_indicator.draw( + x, 0.0, + ColorU::new(0, 110, 220, 255), + debug_renderer, + ); + x += ChangeIndicator::width(); + } } - if let Some(debug_renderer) = self.debug.try_get_mut() { - let small_screen = self.debug_flags.contains(DebugFlags::SMALL_SCREEN); - let scale = if small_screen { 1.6 } else { 1.0 }; - // TODO(gw): Tidy this up so that compositor config integrates better - // with the (non-compositor) surface y-flip options. - let surface_origin_is_top_left = match self.current_compositor_kind { - CompositorKind::Native { .. } => true, - CompositorKind::Draw { .. } => self.device.surface_origin_is_top_left(), - }; - // If there is a debug overlay, render it. Otherwise, just clear - // the debug renderer. - debug_renderer.render( - &mut self.device, - debug_overlay.and(device_size), - scale, - surface_origin_is_top_left, - ); + if self.debug_flags.contains(DebugFlags::NEW_SCENE_INDICATOR) { + if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) { + self.new_scene_indicator.draw( + x, 0.0, + ColorU::new(0, 220, 110, 255), + debug_renderer, + ); + x += ChangeIndicator::width(); + } } - self.staging_texture_pool.end_frame(&mut self.device); - self.texture_upload_pbo_pool.end_frame(&mut self.device); - self.device.end_frame(); + if self.debug_flags.contains(DebugFlags::SLOW_FRAME_INDICATOR) { + if let Some(debug_renderer) = self.debug.get_mut(&mut self.device) { + self.slow_txn_indicator.draw( + x, 0.0, + ColorU::new(250, 80, 80, 255), + debug_renderer, + ); + self.slow_frame_indicator.draw( + x, 10.0, + ColorU::new(220, 30, 10, 255), + debug_renderer, + ); + } + } - if debug_overlay.is_some() { + if self.debug_flags.contains(DebugFlags::ECHO_DRIVER_MESSAGES) { + self.device.echo_driver_messages(); + } + + results.stats.texture_upload_kb = self.profile_counters.texture_data_uploaded.get(); + self.backend_profile_counters.reset(); + self.profile_counters.reset(); + self.profile_counters.frame_counter.inc(); + results.stats.resource_upload_time = self.resource_upload_time; + self.resource_upload_time = 0; + results.stats.gpu_cache_upload_time = self.gpu_cache_upload_time; + self.gpu_cache_upload_time = 0; + + profile_timers.cpu_time.profile(|| { + if let Some(debug_renderer) = self.debug.try_get_mut() { + let small_screen = self.debug_flags.contains(DebugFlags::SMALL_SCREEN); + let scale = if small_screen { 1.6 } else { 1.0 }; + // TODO(gw): Tidy this up so that compositor config integrates better + // with the (non-compositor) surface y-flip options. + let surface_origin_is_top_left = match self.current_compositor_kind { + CompositorKind::Native { .. } => true, + CompositorKind::Draw { .. } => self.device.surface_origin_is_top_left(), + }; + debug_renderer.render( + &mut self.device, + device_size, + scale, + surface_origin_is_top_left, + ); + } + // See comment for texture_resolver.begin_frame() for explanation + // of why this must be done after all rendering, including debug + // overlays. The end_frame() call implicitly calls end_pass(), which + // should ensure any left over render targets get invalidated and + // returned to the pool correctly. + self.texture_resolver.end_frame(&mut self.device, cpu_frame_id); + self.device.end_frame(); + }); + + if device_size.is_some() { self.last_time = current_time; // Unbind the target for the debug overlay. No debug or profiler drawing @@ -2065,15 +3706,13 @@ impl Renderer { self.unbind_debug_overlay(); } - if device_size.is_some() { - // Inform the client that we are finished this composition transaction if native - // compositing is enabled. This must be called after any debug / profiling compositor - // surfaces have been drawn and added to the visual tree. - if let CompositorKind::Native { .. } = self.current_compositor_kind { - profile_scope!("compositor.end_frame"); - let compositor = self.compositor_config.compositor().unwrap(); - compositor.end_frame(); - } + // Inform the client that we are finished this composition transaction if native + // compositing is enabled. This must be called after any debug / profiling compositor + // surfaces have been drawn and added to the visual tree. + if let CompositorKind::Native { .. } = self.current_compositor_kind { + profile_scope!("compositor.end_frame"); + let compositor = self.compositor_config.compositor().unwrap(); + compositor.end_frame(); } self.documents_seen.clear(); @@ -2086,132 +3725,117 @@ impl Renderer { } } - fn update_gpu_profile(&mut self, device_size: DeviceIntSize) { - let _gm = self.gpu_profiler.start_marker("build samples"); - // Block CPU waiting for last frame's GPU profiles to arrive. - // In general this shouldn't block unless heavily GPU limited. - let (gpu_frame_id, timers, samplers) = self.gpu_profiler.build_samples(); + fn update_gpu_cache(&mut self) { + let _gm = self.gpu_profile.start_marker("gpu cache update"); + + // For an artificial stress test of GPU cache resizing, + // always pass an extra update list with at least one block in it. + let gpu_cache_height = self.gpu_cache_texture.get_height(); + if gpu_cache_height != 0 && GPU_CACHE_RESIZE_TEST { + self.pending_gpu_cache_updates.push(GpuCacheUpdateList { + frame_id: FrameId::INVALID, + clear: false, + height: gpu_cache_height, + blocks: vec![[1f32; 4].into()], + updates: Vec::new(), + debug_commands: Vec::new(), + }); + } - if self.max_recorded_profiles > 0 { - while self.gpu_profiles.len() >= self.max_recorded_profiles { - self.gpu_profiles.pop_front(); - } + let (updated_blocks, max_requested_height) = self + .pending_gpu_cache_updates + .iter() + .fold((0, gpu_cache_height), |(count, height), list| { + (count + list.blocks.len(), cmp::max(height, list.height)) + }); - self.gpu_profiles.push_back(GpuProfile::new(gpu_frame_id, &timers)); + if max_requested_height > self.get_max_texture_size() && !self.gpu_cache_overflow { + self.gpu_cache_overflow = true; + self.renderer_errors.push(RendererError::MaxTextureSize); } - self.profiler.set_gpu_time_queries(timers); - - if !samplers.is_empty() { - let screen_fraction = 1.0 / device_size.to_f32().area(); + // Note: if we decide to switch to scatter-style GPU cache update + // permanently, we can have this code nicer with `BufferUploader` kind + // of helper, similarly to how `TextureUploader` API is used. + self.gpu_cache_texture.prepare_for_updates( + &mut self.device, + updated_blocks, + max_requested_height, + ); - fn accumulate_sampler_value(description: &str, samplers: &[GpuSampler]) -> f32 { - let mut accum = 0.0; - for sampler in samplers { - if sampler.tag.label != description { - continue; - } + for update_list in self.pending_gpu_cache_updates.drain(..) { + assert!(update_list.height <= max_requested_height); + if update_list.frame_id > self.gpu_cache_frame_id { + self.gpu_cache_frame_id = update_list.frame_id + } + self.gpu_cache_texture + .update(&mut self.device, &update_list); + } - accum += sampler.count as f32; - } + let mut upload_time = TimeProfileCounter::new("GPU cache upload time", false, Some(0.0..2.0)); + let updated_rows = upload_time.profile(|| { + self.gpu_cache_texture.flush(&mut self.device) + }); + self.gpu_cache_upload_time += upload_time.get(); - accum - } + let counters = &mut self.backend_profile_counters.resources.gpu_cache; + counters.updated_rows.set(updated_rows); + counters.updated_blocks.set(updated_blocks); + } - let alpha_targets = accumulate_sampler_value(&"Alpha targets", &samplers) * screen_fraction; - let transparent_pass = accumulate_sampler_value(&"Transparent pass", &samplers) * screen_fraction; - let opaque_pass = accumulate_sampler_value(&"Opaque pass", &samplers) * screen_fraction; - self.profile.set(profiler::ALPHA_TARGETS_SAMPLERS, alpha_targets); - self.profile.set(profiler::TRANSPARENT_PASS_SAMPLERS, transparent_pass); - self.profile.set(profiler::OPAQUE_PASS_SAMPLERS, opaque_pass); - self.profile.set(profiler::TOTAL_SAMPLERS, alpha_targets + transparent_pass + opaque_pass); + fn prepare_gpu_cache(&mut self, frame: &Frame) -> Result<(), RendererError> { + if self.pending_gpu_cache_clear { + let use_scatter = + matches!(self.gpu_cache_texture.bus, GpuCacheBus::Scatter { .. }); + let new_cache = GpuCacheTexture::new(&mut self.device, use_scatter)?; + let old_cache = mem::replace(&mut self.gpu_cache_texture, new_cache); + old_cache.deinit(&mut self.device); + self.pending_gpu_cache_clear = false; } + + let deferred_update_list = self.update_deferred_resolves(&frame.deferred_resolves); + self.pending_gpu_cache_updates.extend(deferred_update_list); + + self.update_gpu_cache(); + + // Note: the texture might have changed during the `update`, + // so we need to bind it here. + self.device.bind_texture( + TextureSampler::GpuCache, + self.gpu_cache_texture.texture.as_ref().unwrap(), + Swizzle::default(), + ); + + Ok(()) } fn update_texture_cache(&mut self) { profile_scope!("update_texture_cache"); - let _gm = self.gpu_profiler.start_marker("texture cache update"); + let _gm = self.gpu_profile.start_marker("texture cache update"); let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]); self.pending_texture_cache_updates = false; - self.profile.start_time(profiler::TEXTURE_CACHE_UPDATE_TIME); - - let mut create_cache_texture_time = 0; - let mut delete_cache_texture_time = 0; - - for update_list in pending_texture_updates.drain(..) { - // Find any textures that will need to be deleted in this group of allocations. - let mut pending_deletes = Vec::new(); - for allocation in &update_list.allocations { - let old = self.texture_resolver.texture_cache_map.remove(&allocation.id); - match allocation.kind { - TextureCacheAllocationKind::Alloc(_) => { - assert!(old.is_none(), "Renderer and backend disagree!"); - } - TextureCacheAllocationKind::Reset(_) | - TextureCacheAllocationKind::Free => { - assert!(old.is_some(), "Renderer and backend disagree!"); - } - } - if let Some(texture) = old { - // Regenerate the cache allocation info so we can search through deletes for reuse. - let size = texture.get_dimensions(); - let info = TextureCacheAllocInfo { - width: size.width, - height: size.height, - format: texture.get_format(), - filter: texture.get_filter(), - target: texture.get_target(), - is_shared_cache: texture.flags().contains(TextureFlags::IS_SHARED_TEXTURE_CACHE), - has_depth: texture.supports_depth(), + let mut upload_time = TimeProfileCounter::new("Resource upload time", false, Some(0.0..2.0)); + upload_time.profile(|| { + for update_list in pending_texture_updates.drain(..) { + for allocation in update_list.allocations { + match allocation.kind { + TextureCacheAllocationKind::Alloc(_) => add_event_marker(c_str!("TextureCacheAlloc")), + TextureCacheAllocationKind::Realloc(_) => add_event_marker(c_str!("TextureCacheRealloc")), + TextureCacheAllocationKind::Reset(_) => add_event_marker(c_str!("TextureCacheReset")), + TextureCacheAllocationKind::Free => add_event_marker(c_str!("TextureCacheFree")), }; - pending_deletes.push((texture, info)); - } - } - // Look for any alloc or reset that has matching alloc info and save it from being deleted. - let mut reused_textures = VecDeque::with_capacity(pending_deletes.len()); - for allocation in &update_list.allocations { - match allocation.kind { - TextureCacheAllocationKind::Alloc(ref info) | - TextureCacheAllocationKind::Reset(ref info) => { - reused_textures.push_back( - pending_deletes.iter() - .position(|(_, old_info)| *old_info == *info) - .map(|index| pending_deletes.swap_remove(index).0) - ); - } - TextureCacheAllocationKind::Free => {} - } - } - // Now that we've saved as many deletions for reuse as we can, actually delete whatever is left. - if !pending_deletes.is_empty() { - let delete_texture_start = precise_time_ns(); - for (texture, _) in pending_deletes { - add_event_marker(c_str!("TextureCacheFree")); - self.device.delete_texture(texture); - } - delete_cache_texture_time += precise_time_ns() - delete_texture_start; - } - - for allocation in update_list.allocations { - match allocation.kind { - TextureCacheAllocationKind::Alloc(_) => add_event_marker(c_str!("TextureCacheAlloc")), - TextureCacheAllocationKind::Reset(_) => add_event_marker(c_str!("TextureCacheReset")), - TextureCacheAllocationKind::Free => {} - }; - match allocation.kind { - TextureCacheAllocationKind::Alloc(ref info) | - TextureCacheAllocationKind::Reset(ref info) => { - let create_cache_texture_start = precise_time_ns(); - // Create a new native texture, as requested by the texture cache. - // If we managed to reuse a deleted texture, then prefer that instead. - // - // Ensure no PBO is bound when creating the texture storage, - // or GL will attempt to read data from there. - let mut texture = reused_textures.pop_front().unwrap_or(None).unwrap_or_else(|| { - self.device.create_texture( - info.target, + let old = match allocation.kind { + TextureCacheAllocationKind::Alloc(ref info) | + TextureCacheAllocationKind::Realloc(ref info) | + TextureCacheAllocationKind::Reset(ref info) => { + // Create a new native texture, as requested by the texture cache. + // + // Ensure no PBO is bound when creating the texture storage, + // or GL will attempt to read data from there. + let mut texture = self.device.create_texture( + TextureTarget::Array, info.format, info.width, info.height, @@ -2219,230 +3843,302 @@ impl Renderer { // This needs to be a render target because some render // tasks get rendered into the texture cache. Some(RenderTargetInfo { has_depth: info.has_depth }), - ) - }); + info.layer_count, + ); - if info.is_shared_cache { - texture.flags_mut() - .insert(TextureFlags::IS_SHARED_TEXTURE_CACHE); - - // On Mali-Gxx devices we use batched texture uploads as it performs much better. - // However, due to another driver bug we must ensure the textures are fully cleared, - // otherwise we get visual artefacts when blitting to the texture cache. - if self.device.use_batched_texture_uploads() && - !self.device.get_capabilities().supports_render_target_partial_update - { - self.clear_texture(&texture, [0.0; 4]); - } + if info.is_shared_cache { + texture.flags_mut() + .insert(TextureFlags::IS_SHARED_TEXTURE_CACHE); - // Textures in the cache generally don't need to be cleared, - // but we do so if the debug display is active to make it - // easier to identify unallocated regions. - if self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG) { - self.clear_texture(&texture, TEXTURE_CACHE_DBG_CLEAR_COLOR); + // Textures in the cache generally don't need to be cleared, + // but we do so if the debug display is active to make it + // easier to identify unallocated regions. + if self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG) { + self.clear_texture(&texture, TEXTURE_CACHE_DBG_CLEAR_COLOR); + } } + + self.texture_resolver.texture_cache_map.insert(allocation.id, texture) + } + TextureCacheAllocationKind::Free => { + self.texture_resolver.texture_cache_map.remove(&allocation.id) } + }; - create_cache_texture_time += precise_time_ns() - create_cache_texture_start; + match allocation.kind { + TextureCacheAllocationKind::Alloc(_) => { + assert!(old.is_none(), "Renderer and backend disagree!"); + } + TextureCacheAllocationKind::Realloc(_) => { + self.device.blit_renderable_texture( + self.texture_resolver.texture_cache_map.get_mut(&allocation.id).unwrap(), + old.as_ref().unwrap(), + ); + } + TextureCacheAllocationKind::Reset(_) | + TextureCacheAllocationKind::Free => { + assert!(old.is_some(), "Renderer and backend disagree!"); + } + } - self.texture_resolver.texture_cache_map.insert(allocation.id, texture); + if let Some(old) = old { + self.device.delete_texture(old); } - TextureCacheAllocationKind::Free => {} - }; - } + } - upload_to_texture_cache(self, update_list.updates); - } + for (texture_id, updates) in update_list.updates { + let texture = &self.texture_resolver.texture_cache_map[&texture_id]; + let device = &mut self.device; - if create_cache_texture_time > 0 { - self.profile.set( - profiler::CREATE_CACHE_TEXTURE_TIME, - profiler::ns_to_ms(create_cache_texture_time) - ); - } - if delete_cache_texture_time > 0 { - self.profile.set( - profiler::DELETE_CACHE_TEXTURE_TIME, - profiler::ns_to_ms(delete_cache_texture_time) - ) - } + // Calculate the total size of buffer required to upload all updates. + let required_size = updates.iter().map(|update| { + // Perform any debug clears now. As this requires a mutable borrow of device, + // it must be done before all the updates which require a TextureUploader. + if let TextureUpdateSource::DebugClear = update.source { + let draw_target = DrawTarget::from_texture( + texture, + update.layer_index as usize, + false, + ); + device.bind_draw_target(draw_target); + device.clear_target( + Some(TEXTURE_CACHE_DBG_CLEAR_COLOR), + None, + Some(draw_target.to_framebuffer_rect(update.rect.to_i32())) + ); - let t = self.profile.end_time(profiler::TEXTURE_CACHE_UPDATE_TIME); - self.resource_upload_time += t; + 0 + } else { + let (upload_size, _) = device.required_upload_size_and_stride( + update.rect.size, + texture.get_format(), + ); + upload_size + } + }).sum(); - drain_filter( - &mut self.notifications, - |n| { n.when() == Checkpoint::FrameTexturesUpdated }, - |n| { n.notify(); }, - ); + if required_size == 0 { + continue; + } + + // For best performance we use a single TextureUploader for all uploads. + // Using individual TextureUploaders was causing performance issues on some drivers + // due to allocating too many PBOs. + let mut uploader = device.upload_texture( + texture, + &self.texture_cache_upload_pbo, + required_size + ); + + for update in updates { + let TextureCacheUpdate { rect, stride, offset, layer_index, format_override, source } = update; + + let bytes_uploaded = match source { + TextureUpdateSource::Bytes { data } => { + let data = &data[offset as usize ..]; + uploader.upload( + rect, + layer_index, + stride, + format_override, + data.as_ptr(), + data.len(), + ) + } + TextureUpdateSource::External { id, channel_index } => { + let handler = self.external_image_handler + .as_mut() + .expect("Found external image, but no handler set!"); + // The filter is only relevant for NativeTexture external images. + let dummy_data; + let data = match handler.lock(id, channel_index, ImageRendering::Auto).source { + ExternalImageSource::RawData(data) => { + &data[offset as usize ..] + } + ExternalImageSource::Invalid => { + // Create a local buffer to fill the pbo. + let bpp = texture.get_format().bytes_per_pixel(); + let width = stride.unwrap_or(rect.size.width * bpp); + let total_size = width * rect.size.height; + // WR haven't support RGBAF32 format in texture_cache, so + // we use u8 type here. + dummy_data = vec![0xFFu8; total_size as usize]; + &dummy_data + } + ExternalImageSource::NativeTexture(eid) => { + panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id); + } + }; + let size = uploader.upload( + rect, + layer_index, + stride, + format_override, + data.as_ptr(), + data.len() + ); + handler.unlock(id, channel_index); + size + } + TextureUpdateSource::DebugClear => { + // DebugClear updates are handled separately. + 0 + } + }; + self.profile_counters.texture_data_uploaded.add(bytes_uploaded >> 10); + } + } + + if update_list.clears_shared_cache { + self.shared_texture_cache_cleared = true; + } + } + + drain_filter( + &mut self.notifications, + |n| { n.when() == Checkpoint::FrameTexturesUpdated }, + |n| { n.notify(); }, + ); + }); + self.resource_upload_time += upload_time.get(); } - fn bind_textures(&mut self, textures: &BatchTextures) { - for i in 0 .. 3 { - self.texture_resolver.bind( - &textures.input.colors[i], + pub(crate) fn draw_instanced_batch<T>( + &mut self, + data: &[T], + vertex_array_kind: VertexArrayKind, + textures: &BatchTextures, + stats: &mut RendererStats, + ) { + let mut swizzles = [Swizzle::default(); 3]; + for i in 0 .. textures.colors.len() { + let swizzle = self.texture_resolver.bind( + &textures.colors[i], TextureSampler::color(i), &mut self.device, ); + if cfg!(debug_assertions) { + swizzles[i] = swizzle; + for j in 0 .. i { + if textures.colors[j] == textures.colors[i] && swizzles[j] != swizzle { + error!("Swizzling conflict in {:?}", textures); + } + } + } } - self.texture_resolver.bind( - &textures.clip_mask, - TextureSampler::ClipMask, - &mut self.device, - ); - // TODO: this probably isn't the best place for this. if let Some(ref texture) = self.dither_matrix_texture { self.device.bind_texture(TextureSampler::Dither, texture, Swizzle::default()); } + + self.draw_instanced_batch_with_previously_bound_textures(data, vertex_array_kind, stats) } - fn draw_instanced_batch<T: Clone>( + pub(crate) fn draw_instanced_batch_with_previously_bound_textures<T>( &mut self, data: &[T], vertex_array_kind: VertexArrayKind, - textures: &BatchTextures, stats: &mut RendererStats, ) { - self.bind_textures(textures); - // If we end up with an empty draw call here, that means we have // probably introduced unnecessary batch breaks during frame // building - so we should be catching this earlier and removing // the batch. debug_assert!(!data.is_empty()); - let vao = &self.vaos[vertex_array_kind]; + let vao = get_vao(vertex_array_kind, &self.vaos); + self.device.bind_vao(vao); - let chunk_size = if self.debug_flags.contains(DebugFlags::DISABLE_BATCHING) { - 1 - } else if vertex_array_kind == VertexArrayKind::Primitive { - self.max_primitive_instance_count - } else { - data.len() - }; + let batched = !self.debug_flags.contains(DebugFlags::DISABLE_BATCHING); - for chunk in data.chunks(chunk_size) { - if self.enable_instancing { - self.device - .update_vao_instances(vao, chunk, ONE_TIME_USAGE_HINT, None); - self.device - .draw_indexed_triangles_instanced_u16(6, chunk.len() as i32); - } else { - self.device - .update_vao_instances(vao, chunk, ONE_TIME_USAGE_HINT, NonZeroUsize::new(4)); + if batched { + self.device + .update_vao_instances(vao, data, VertexUsageHint::Stream); + self.device + .draw_indexed_triangles_instanced_u16(6, data.len() as i32); + self.profile_counters.draw_calls.inc(); + stats.total_draw_calls += 1; + } else { + for i in 0 .. data.len() { self.device - .draw_indexed_triangles(6 * chunk.len() as i32); + .update_vao_instances(vao, &data[i .. i + 1], VertexUsageHint::Stream); + self.device.draw_triangles_u16(0, 6); + self.profile_counters.draw_calls.inc(); + stats.total_draw_calls += 1; } - self.profile.inc(profiler::DRAW_CALLS); - stats.total_draw_calls += 1; } - self.profile.add(profiler::VERTICES, 6 * data.len()); + self.profile_counters.vertices.add(6 * data.len()); } fn handle_readback_composite( &mut self, draw_target: DrawTarget, uses_scissor: bool, + source: &RenderTask, backdrop: &RenderTask, readback: &RenderTask, ) { - // Extract the rectangle in the backdrop surface's device space of where - // we need to read from. - let readback_origin = match readback.kind { - RenderTaskKind::Readback(ReadbackTask { readback_origin: Some(o), .. }) => o, - RenderTaskKind::Readback(ReadbackTask { readback_origin: None, .. }) => { - // If this is a dummy readback, just early out. We know that the - // clear of the target will ensure the task rect is already zero alpha, - // so it won't affect the rendering output. - return; - } - _ => unreachable!(), - }; - if uses_scissor { self.device.disable_scissor(); } - let texture_source = TextureSource::TextureCache( - readback.get_target_texture(), - Swizzle::default(), - ); let (cache_texture, _) = self.texture_resolver - .resolve(&texture_source).expect("bug: no source texture"); + .resolve(&TextureSource::PrevPassColor) + .unwrap(); // Before submitting the composite batch, do the // framebuffer readbacks that are needed for each // composite operation in this batch. - let readback_rect = readback.get_target_rect(); - let backdrop_rect = backdrop.get_target_rect(); - let (backdrop_screen_origin, _) = match backdrop.kind { + let (readback_rect, readback_layer) = readback.get_target_rect(); + let (backdrop_rect, _) = backdrop.get_target_rect(); + let (backdrop_screen_origin, backdrop_scale) = match backdrop.kind { + RenderTaskKind::Picture(ref task_info) => (task_info.content_origin, task_info.device_pixel_scale), + _ => panic!("bug: composite on non-picture?"), + }; + let (source_screen_origin, source_scale) = match source.kind { RenderTaskKind::Picture(ref task_info) => (task_info.content_origin, task_info.device_pixel_scale), _ => panic!("bug: composite on non-picture?"), }; // Bind the FBO to blit the backdrop to. - // Called per-instance in case the FBO changes. The device will skip - // the GL call if the requested target is already bound. + // Called per-instance in case the layer (and therefore FBO) + // changes. The device will skip the GL call if the requested + // target is already bound. let cache_draw_target = DrawTarget::from_texture( cache_texture, + readback_layer.0 as usize, false, ); - // Get the rect that we ideally want, in space of the parent surface - let wanted_rect = DeviceRect::new( - readback_origin, - readback_rect.size.to_f32(), - ); + let source_in_backdrop_space = source_screen_origin.to_f32() * (backdrop_scale.0 / source_scale.0); - // Get the rect that is available on the parent surface. It may be smaller - // than desired because this is a picture cache tile covering only part of - // the wanted rect and/or because the parent surface was clipped. - let avail_rect = DeviceRect::new( - backdrop_screen_origin, - backdrop_rect.size.to_f32(), + let mut src = DeviceIntRect::new( + (source_in_backdrop_space + (backdrop_rect.origin - backdrop_screen_origin).to_f32()).to_i32(), + readback_rect.size, ); + let mut dest = readback_rect.to_i32(); + let device_to_framebuffer = Scale::new(1i32); - if let Some(int_rect) = wanted_rect.intersection(&avail_rect) { - // If there is a valid intersection, work out the correct origins and - // sizes of the copy rects, and do the blit. - let copy_size = int_rect.size.to_i32(); - - let src_origin = backdrop_rect.origin.to_f32() + - int_rect.origin.to_vector() - - backdrop_screen_origin.to_vector(); - - let src = DeviceIntRect::new( - src_origin.to_i32(), - copy_size, - ); - - let dest_origin = readback_rect.origin.to_f32() + - int_rect.origin.to_vector() - - readback_origin.to_vector(); - - let dest = DeviceIntRect::new( - dest_origin.to_i32(), - copy_size, - ); - - // Should always be drawing to picture cache tiles or off-screen surface! - debug_assert!(!draw_target.is_default()); - let device_to_framebuffer = Scale::new(1i32); - - self.device.blit_render_target( - draw_target.into(), - src * device_to_framebuffer, - cache_draw_target, - dest * device_to_framebuffer, - TextureFilter::Linear, - ); + // Need to invert the y coordinates and flip the image vertically when + // reading back from the framebuffer. + if draw_target.is_default() { + src.origin.y = draw_target.dimensions().height as i32 - src.size.height - src.origin.y; + dest.origin.y += dest.size.height; + dest.size.height = -dest.size.height; } - // Restore draw target to current pass render target, and reset + self.device.blit_render_target( + draw_target.into(), + src * device_to_framebuffer, + cache_draw_target, + dest * device_to_framebuffer, + TextureFilter::Linear, + ); + + // Restore draw target to current pass render target + layer, and reset // the read target. self.device.bind_draw_target(draw_target); self.device.reset_read_target(); @@ -2457,25 +4153,30 @@ impl Renderer { blits: &[BlitJob], render_tasks: &RenderTaskGraph, draw_target: DrawTarget, + content_origin: &DeviceIntPoint, ) { if blits.is_empty() { return; } - let _timer = self.gpu_profiler.start_timer(GPU_TAG_BLIT); + let _timer = self.gpu_profile.start_timer(GPU_TAG_BLIT); // TODO(gw): For now, we don't bother batching these by source texture. // If if ever shows up as an issue, we can easily batch them. for blit in blits { - let (source, source_rect) = { - // A blit from the child render task into this target. - // TODO(gw): Support R8 format here once we start - // creating mips for alpha masks. - let task = &render_tasks[blit.source]; - let source_rect = task.get_target_rect(); - let source_texture = task.get_texture_source(); - - (source_texture, source_rect) + let (source, layer, source_rect) = match blit.source { + BlitJobSource::Texture(texture_id, layer, source_rect) => { + // A blit from a texture into this target. + (texture_id, layer as usize, source_rect) + } + BlitJobSource::RenderTask(task_id) => { + // A blit from the child render task into this target. + // TODO(gw): Support R8 format here once we start + // creating mips for alpha masks. + let source = &render_tasks[task_id]; + let (source_rect, layer) = source.get_target_rect(); + (TextureSource::PrevPassColor, layer.0, source_rect) + } }; debug_assert_eq!(source_rect.size, blit.target_rect.size); @@ -2489,6 +4190,7 @@ impl Renderer { let read_target = DrawTarget::from_texture( texture, + layer, false, ); @@ -2496,7 +4198,7 @@ impl Renderer { read_target.into(), read_target.to_framebuffer_rect(source_rect), draw_target, - draw_target.to_framebuffer_rect(blit.target_rect), + draw_target.to_framebuffer_rect(blit.target_rect.translate(-content_origin.to_vector())), TextureFilter::Linear, ); } @@ -2512,25 +4214,22 @@ impl Renderer { return } - let _timer = self.gpu_profiler.start_timer(GPU_TAG_SCALE); - - for (source, instances) in scalings { - let buffer_kind = source.image_buffer_kind(); + let _timer = self.gpu_profile.start_timer(GPU_TAG_SCALE); - self.shaders - .borrow_mut() - .get_scale_shader(buffer_kind) - .bind( - &mut self.device, - &projection, - Some(self.texture_resolver.get_texture_size(source).to_f32()), - &mut self.renderer_errors, - ); + self.shaders + .borrow_mut() + .cs_scale + .bind( + &mut self.device, + &projection, + &mut self.renderer_errors, + ); + for (source, instances) in scalings { self.draw_instanced_batch( instances, VertexArrayKind::Scale, - &BatchTextures::composite_rgb(*source), + &BatchTextures::color(*source), stats, ); } @@ -2547,12 +4246,11 @@ impl Renderer { return; } - let _timer = self.gpu_profiler.start_timer(GPU_TAG_SVG_FILTER); + let _timer = self.gpu_profile.start_timer(GPU_TAG_SVG_FILTER); self.shaders.borrow_mut().cs_svg_filter.bind( &mut self.device, &projection, - None, &mut self.renderer_errors ); @@ -2568,29 +4266,25 @@ impl Renderer { &mut self, target: &PictureCacheTarget, draw_target: DrawTarget, + content_origin: DeviceIntPoint, projection: &default::Transform3D<f32>, render_tasks: &RenderTaskGraph, stats: &mut RendererStats, ) { profile_scope!("draw_picture_cache_target"); - self.profile.inc(profiler::RENDERED_PICTURE_TILES); - let _gm = self.gpu_profiler.start_marker("picture cache target"); + self.profile_counters.rendered_picture_cache_tiles.inc(); + let _gm = self.gpu_profile.start_marker("picture cache target"); let framebuffer_kind = FramebufferKind::Other; { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_SETUP_TARGET); + let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET); self.device.bind_draw_target(draw_target); self.device.enable_depth_write(); self.set_blend(false, framebuffer_kind); let clear_color = target.clear_color.map(|c| c.to_array()); - let scissor_rect = if self.device.get_capabilities().supports_render_target_partial_update { - target.alpha_batch_container.task_scissor_rect - } else { - None - }; - match scissor_rect { + match target.alpha_batch_container.task_scissor_rect { // If updating only a dirty rect within a picture cache target, the // clear must also be scissored to that dirty region. Some(r) if self.clear_caches_with_quads => { @@ -2610,13 +4304,12 @@ impl Renderer { self.shaders.borrow_mut().ps_clear.bind( &mut self.device, &projection, - None, &mut self.renderer_errors, ); self.draw_instanced_batch( &[instance], VertexArrayKind::Clear, - &BatchTextures::empty(), + &BatchTextures::no_texture(), stats, ); if clear_color.is_none() { @@ -2627,7 +4320,10 @@ impl Renderer { } other => { let scissor_rect = other.map(|rect| { - draw_target.build_scissor_rect(Some(rect)) + draw_target.build_scissor_rect( + Some(rect), + content_origin, + ) }); self.device.clear_target(clear_color, Some(1.0), scissor_rect); } @@ -2638,13 +4334,12 @@ impl Renderer { self.draw_alpha_batch_container( &target.alpha_batch_container, draw_target, + content_origin, framebuffer_kind, projection, render_tasks, stats, ); - - self.device.invalidate_depth_target(); } /// Draw an alpha batch container into a given draw target. This is used @@ -2653,6 +4348,7 @@ impl Renderer { &mut self, alpha_batch_container: &AlphaBatchContainer, draw_target: DrawTarget, + content_origin: DeviceIntPoint, framebuffer_kind: FramebufferKind, projection: &default::Transform3D<f32>, render_tasks: &RenderTaskGraph, @@ -2664,14 +4360,15 @@ impl Renderer { self.device.enable_scissor(); let scissor_rect = draw_target.build_scissor_rect( alpha_batch_container.task_scissor_rect, + content_origin, ); self.device.set_scissor_rect(scissor_rect) } if !alpha_batch_container.opaque_batches.is_empty() && !self.debug_flags.contains(DebugFlags::DISABLE_OPAQUE_PASS) { - let _gl = self.gpu_profiler.start_marker("opaque batches"); - let opaque_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_OPAQUE); + let _gl = self.gpu_profile.start_marker("opaque batches"); + let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); self.set_blend(false, framebuffer_kind); //Note: depth equality is needed for split planes self.device.enable_depth(DepthFunction::LessEqual); @@ -2689,13 +4386,13 @@ impl Renderer { } self.shaders.borrow_mut() - .get(&batch.key, batch.features, self.debug_flags, &self.device) + .get(&batch.key, batch.features, self.debug_flags) .bind( - &mut self.device, projection, None, + &mut self.device, projection, &mut self.renderer_errors, ); - let _timer = self.gpu_profiler.start_timer(batch.key.kind.sampler_tag()); + let _timer = self.gpu_profile.start_timer(batch.key.kind.sampler_tag()); self.draw_instanced_batch( &batch.instances, VertexArrayKind::Primitive, @@ -2705,20 +4402,37 @@ impl Renderer { } self.device.disable_depth_write(); - self.gpu_profiler.finish_sampler(opaque_sampler); + self.gpu_profile.finish_sampler(opaque_sampler); } else { self.device.disable_depth(); } if !alpha_batch_container.alpha_batches.is_empty() && !self.debug_flags.contains(DebugFlags::DISABLE_ALPHA_PASS) { - let _gl = self.gpu_profiler.start_marker("alpha batches"); - let transparent_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); + let _gl = self.gpu_profile.start_marker("alpha batches"); + let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); self.set_blend(true, framebuffer_kind); let mut prev_blend_mode = BlendMode::None; let shaders_rc = self.shaders.clone(); + // If the device supports pixel local storage, initialize the PLS buffer for + // the transparent pass. This involves reading the current framebuffer value + // and storing that in PLS. + // TODO(gw): This is quite expensive and relies on framebuffer fetch being + // available. We can probably switch the opaque pass over to use + // PLS too, and remove this pass completely. + if self.device.get_capabilities().supports_pixel_local_storage { + // TODO(gw): If using PLS, the fixed function blender is disabled. It's possible + // we could take advantage of this by skipping batching on the blend + // mode in these cases. + self.init_pixel_local_storage( + alpha_batch_container.task_rect, + projection, + stats, + ); + } + for batch in &alpha_batch_container.alpha_batches { if should_skip_batch(&batch.key.kind, self.debug_flags) { continue; @@ -2729,7 +4443,6 @@ impl Renderer { &batch.key, batch.features | BatchFeatures::ALPHA_PASS, self.debug_flags, - &self.device, ); if batch.key.blend_mode != prev_blend_mode { @@ -2767,7 +4480,6 @@ impl Renderer { shader.bind( &mut self.device, projection, - None, &mut self.renderer_errors, ); self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass0 as _); @@ -2778,37 +4490,28 @@ impl Renderer { } self.device.set_blend_mode_advanced(mode); } - BlendMode::MultiplyDualSource => { - self.device.set_blend_mode_multiply_dual_source(); - } - BlendMode::Screen => { - self.device.set_blend_mode_screen(); - } - BlendMode::Exclusion => { - self.device.set_blend_mode_exclusion(); - } } prev_blend_mode = batch.key.blend_mode; } // Handle special case readback for composites. - if let BatchKind::Brush(BrushBatchKind::MixBlend { task_id, backdrop_id }) = batch.key.kind { + if let BatchKind::Brush(BrushBatchKind::MixBlend { task_id, source_id, backdrop_id }) = batch.key.kind { // composites can't be grouped together because // they may overlap and affect each other. debug_assert_eq!(batch.instances.len(), 1); self.handle_readback_composite( draw_target, uses_scissor, + &render_tasks[source_id], &render_tasks[task_id], &render_tasks[backdrop_id], ); } - let _timer = self.gpu_profiler.start_timer(batch.key.kind.sampler_tag()); + let _timer = self.gpu_profile.start_timer(batch.key.kind.sampler_tag()); shader.bind( &mut self.device, projection, - None, &mut self.renderer_errors, ); @@ -2825,7 +4528,6 @@ impl Renderer { shader.bind( &mut self.device, projection, - None, &mut self.renderer_errors, ); self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass1 as _); @@ -2842,7 +4544,6 @@ impl Renderer { shader.bind( &mut self.device, projection, - None, &mut self.renderer_errors, ); self.device.switch_mode(ShaderColorMode::SubpixelWithBgColorPass2 as _); @@ -2856,8 +4557,19 @@ impl Renderer { } } + // If the device supports pixel local storage, resolve the PLS values. + // This pass reads the final PLS color value, and writes it to a normal + // fragment output. + if self.device.get_capabilities().supports_pixel_local_storage { + self.resolve_pixel_local_storage( + alpha_batch_container.task_rect, + projection, + stats, + ); + } + self.set_blend(false, framebuffer_kind); - self.gpu_profiler.finish_sampler(transparent_sampler); + self.gpu_profile.finish_sampler(transparent_sampler); } self.device.disable_depth(); @@ -2876,7 +4588,7 @@ impl Renderer { return; } - let opaque_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_OPAQUE); + let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); self.device.disable_depth(); self.set_blend(false, FramebufferKind::Main); @@ -2933,19 +4645,19 @@ impl Renderer { .get_composite_shader( CompositeSurfaceFormat::Yuv, surface.image_buffer_kind, - CompositeFeatures::empty(), ).bind( &mut self.device, &projection, - None, &mut self.renderer_errors ); - let textures = BatchTextures::composite_yuv( - planes[0].texture, - planes[1].texture, - planes[2].texture, - ); + let textures = BatchTextures { + colors: [ + planes[0].texture, + planes[1].texture, + planes[2].texture, + ], + }; // When the texture is an external texture, the UV rect is not known when // the external surface descriptor is created, because external textures @@ -2953,9 +4665,9 @@ impl Renderer { // the frame render. To handle this, query the texture resolver for the // UV rect if it's an external texture, otherwise use the default UV rect. let uv_rects = [ - self.texture_resolver.get_uv_rect(&textures.input.colors[0], planes[0].uv_rect), - self.texture_resolver.get_uv_rect(&textures.input.colors[1], planes[1].uv_rect), - self.texture_resolver.get_uv_rect(&textures.input.colors[2], planes[2].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[0], planes[0].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[1], planes[1].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[2], planes[2].uv_rect), ]; let instance = CompositeInstance::new_yuv( @@ -2967,36 +4679,42 @@ impl Renderer { color_space, format, rescale, + [ + planes[0].texture_layer as f32, + planes[1].texture_layer as f32, + planes[2].texture_layer as f32, + ], uv_rects, ); ( textures, instance ) }, ResolvedExternalSurfaceColorData::Rgb{ ref plane, flip_y, .. } => { + self.shaders .borrow_mut() .get_composite_shader( CompositeSurfaceFormat::Rgba, surface.image_buffer_kind, - CompositeFeatures::empty(), ).bind( &mut self.device, &projection, - None, &mut self.renderer_errors ); - let textures = BatchTextures::composite_rgb(plane.texture); - let mut uv_rect = self.texture_resolver.get_uv_rect(&textures.input.colors[0], plane.uv_rect); + let textures = BatchTextures::color(plane.texture); + let mut uv_rect = self.texture_resolver.get_uv_rect(&textures.colors[0], plane.uv_rect); if flip_y { let y = uv_rect.uv0.y; uv_rect.uv0.y = uv_rect.uv1.y; uv_rect.uv1.y = y; } + let instance = CompositeInstance::new_rgb( surface_rect.to_f32(), surface_rect.to_f32(), PremultipliedColorF::WHITE, + plane.texture_layer as f32, ZBufferId(0), uv_rect, ); @@ -3018,80 +4736,96 @@ impl Renderer { .unbind(); } - self.gpu_profiler.finish_sampler(opaque_sampler); + self.gpu_profile.finish_sampler(opaque_sampler); } /// Draw a list of tiles to the framebuffer - fn draw_tile_list<'a, I: Iterator<Item = &'a occlusion::Item>>( + fn draw_tile_list<'a, I: Iterator<Item = &'a CompositeTile>>( &mut self, tiles_iter: I, - composite_state: &CompositeState, external_surfaces: &[ResolvedExternalSurface], projection: &default::Transform3D<f32>, + partial_present_mode: Option<PartialPresentMode>, stats: &mut RendererStats, ) { - let mut current_shader_params = ( - CompositeSurfaceFormat::Rgba, - ImageBufferKind::Texture2D, - CompositeFeatures::empty(), - None, - ); - let mut current_textures = BatchTextures::empty(); - let mut instances = Vec::new(); - self.shaders .borrow_mut() .get_composite_shader( - current_shader_params.0, - current_shader_params.1, - current_shader_params.2, + CompositeSurfaceFormat::Rgba, + ImageBufferKind::Texture2DArray, ).bind( &mut self.device, projection, - None, &mut self.renderer_errors ); - for item in tiles_iter { - let tile = &composite_state.tiles[item.key]; + let mut current_shader_params = (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray); + let mut current_textures = BatchTextures::no_texture(); + let mut instances = Vec::new(); + + for tile in tiles_iter { + // Determine a clip rect to apply to this tile, depending on what + // the partial present mode is. + let partial_clip_rect = match partial_present_mode { + Some(PartialPresentMode::Single { dirty_rect }) => dirty_rect, + None => tile.rect, + }; - let clip_rect = item.rectangle.to_rect(); + let clip_rect = match partial_clip_rect.intersection(&tile.clip_rect) { + Some(rect) => rect, + None => continue, + }; + + // Simple compositor needs the valid rect in device space to match clip rect + let valid_device_rect = tile.valid_rect.translate( + tile.rect.origin.to_vector() + ); + + // Only composite the part of the tile that contains valid pixels + let clip_rect = match clip_rect.intersection(&valid_device_rect) { + Some(rect) => rect, + None => continue, + }; // Work out the draw params based on the tile surface let (instance, textures, shader_params) = match tile.surface { CompositeTileSurface::Color { color } => { - let dummy = TextureSource::Dummy; - let image_buffer_kind = dummy.image_buffer_kind(); - let instance = CompositeInstance::new( - tile.rect, - clip_rect, - color.premultiplied(), - tile.z_id, - ); - let features = instance.get_rgb_features(); ( - instance, - BatchTextures::composite_rgb(dummy), - (CompositeSurfaceFormat::Rgba, image_buffer_kind, features, None), + CompositeInstance::new( + tile.rect, + clip_rect, + color.premultiplied(), + 0.0, + tile.z_id, + ), + BatchTextures::color(TextureSource::Dummy), + (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray), ) } - CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture } } => { - let instance = CompositeInstance::new( - tile.rect, - clip_rect, - PremultipliedColorF::WHITE, - tile.z_id, - ); - let features = instance.get_rgb_features(); + CompositeTileSurface::Clear => { ( - instance, - BatchTextures::composite_rgb(texture), - ( - CompositeSurfaceFormat::Rgba, - ImageBufferKind::Texture2D, - features, - None, + CompositeInstance::new( + tile.rect, + clip_rect, + PremultipliedColorF::BLACK, + 0.0, + tile.z_id, ), + BatchTextures::color(TextureSource::Dummy), + (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray), + ) + } + CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::TextureCache { texture, layer } } => { + ( + CompositeInstance::new( + tile.rect, + clip_rect, + PremultipliedColorF::WHITE, + layer as f32, + tile.z_id, + ), + BatchTextures::color(texture), + (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray), ) } CompositeTileSurface::ExternalSurface { external_surface_index } => { @@ -3099,11 +4833,14 @@ impl Renderer { match surface.color_data { ResolvedExternalSurfaceColorData::Yuv{ ref planes, color_space, format, rescale, .. } => { - let textures = BatchTextures::composite_yuv( - planes[0].texture, - planes[1].texture, - planes[2].texture, - ); + + let textures = BatchTextures { + colors: [ + planes[0].texture, + planes[1].texture, + planes[2].texture, + ], + }; // When the texture is an external texture, the UV rect is not known when // the external surface descriptor is created, because external textures @@ -3111,9 +4848,9 @@ impl Renderer { // the frame render. To handle this, query the texture resolver for the // UV rect if it's an external texture, otherwise use the default UV rect. let uv_rects = [ - self.texture_resolver.get_uv_rect(&textures.input.colors[0], planes[0].uv_rect), - self.texture_resolver.get_uv_rect(&textures.input.colors[1], planes[1].uv_rect), - self.texture_resolver.get_uv_rect(&textures.input.colors[2], planes[2].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[0], planes[0].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[1], planes[1].uv_rect), + self.texture_resolver.get_uv_rect(&textures.colors[2], planes[2].uv_rect), ]; ( @@ -3124,15 +4861,15 @@ impl Renderer { color_space, format, rescale, + [ + planes[0].texture_layer as f32, + planes[1].texture_layer as f32, + planes[2].texture_layer as f32, + ], uv_rects, ), textures, - ( - CompositeSurfaceFormat::Yuv, - surface.image_buffer_kind, - CompositeFeatures::empty(), - None - ), + (CompositeSurfaceFormat::Yuv, surface.image_buffer_kind), ) }, ResolvedExternalSurfaceColorData::Rgb{ ref plane, flip_y, .. } => { @@ -3143,43 +4880,22 @@ impl Renderer { uv_rect.uv0.y = uv_rect.uv1.y; uv_rect.uv1.y = y; } - let instance = CompositeInstance::new_rgb( - tile.rect, - clip_rect, - PremultipliedColorF::WHITE, - tile.z_id, - uv_rect, - ); - let features = instance.get_rgb_features(); + ( - instance, - BatchTextures::composite_rgb(plane.texture), - ( - CompositeSurfaceFormat::Rgba, - surface.image_buffer_kind, - features, - Some(self.texture_resolver.get_texture_size(&plane.texture).to_f32()), + CompositeInstance::new_rgb( + tile.rect, + clip_rect, + PremultipliedColorF::WHITE, + plane.texture_layer as f32, + tile.z_id, + uv_rect, ), + BatchTextures::color(plane.texture), + (CompositeSurfaceFormat::Rgba, surface.image_buffer_kind), ) }, } } - CompositeTileSurface::Clear => { - let dummy = TextureSource::Dummy; - let image_buffer_kind = dummy.image_buffer_kind(); - let instance = CompositeInstance::new( - tile.rect, - clip_rect, - PremultipliedColorF::BLACK, - tile.z_id, - ); - let features = instance.get_rgb_features(); - ( - instance, - BatchTextures::composite_rgb(dummy), - (CompositeSurfaceFormat::Rgba, image_buffer_kind, features, None), - ) - } CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::Native { .. } } => { unreachable!("bug: found native surface in simple composite path"); } @@ -3204,11 +4920,10 @@ impl Renderer { if shader_params != current_shader_params { self.shaders .borrow_mut() - .get_composite_shader(shader_params.0, shader_params.1, shader_params.2) + .get_composite_shader(shader_params.0, shader_params.1) .bind( &mut self.device, projection, - shader_params.3, &mut self.renderer_errors ); @@ -3239,142 +4954,145 @@ impl Renderer { fn composite_simple( &mut self, composite_state: &CompositeState, + clear_framebuffer: bool, draw_target: DrawTarget, projection: &default::Transform3D<f32>, results: &mut RenderResults, - partial_present_mode: Option<PartialPresentMode>, + max_partial_present_rects: usize, + draw_previous_partial_present_regions: bool, ) { - let _gm = self.gpu_profiler.start_marker("framebuffer"); - let _timer = self.gpu_profiler.start_timer(GPU_TAG_COMPOSITE); + let _gm = self.gpu_profile.start_marker("framebuffer"); + let _timer = self.gpu_profile.start_timer(GPU_TAG_COMPOSITE); self.device.bind_draw_target(draw_target); - self.device.disable_depth_write(); - self.device.disable_depth(); - - // If using KHR_partial_update, call eglSetDamageRegion. - // This must be called exactly once per frame, and prior to any rendering to the main - // framebuffer. Additionally, on Mali-G77 we encountered rendering issues when calling - // this earlier in the frame, during offscreen render passes. So call it now, immediately - // before rendering to the main framebuffer. See bug 1685276 for details. - if let Some(partial_present) = self.compositor_config.partial_present() { - if let Some(PartialPresentMode::Single { dirty_rect }) = partial_present_mode { - partial_present.set_buffer_damage_region(&[dirty_rect.to_i32()]); - } - } - - let cap = composite_state.tiles.len(); + self.device.enable_depth(DepthFunction::LessEqual); + self.device.enable_depth_write(); - let mut occlusion = occlusion::FrontToBackBuilder::with_capacity(cap, cap); - let mut clear_tiles = Vec::new(); + // Determine the partial present mode for this frame, which is used during + // framebuffer clears and calculating the clip rect for each tile that is drawn. + let mut partial_present_mode = None; - for (idx, tile) in composite_state.tiles.iter().enumerate() { - // Clear tiles overwrite whatever is under them, so they are treated as opaque. - let is_opaque = tile.kind != TileKind::Alpha; + if max_partial_present_rects > 0 { + // We can only use partial present if we have valid dirty rects and the + // client hasn't reset partial present state since last frame. + if composite_state.dirty_rects_are_valid && !self.force_redraw { + let mut combined_dirty_rect = DeviceRect::zero(); - // Determine a clip rect to apply to this tile, depending on what - // the partial present mode is. - let partial_clip_rect = match partial_present_mode { - Some(PartialPresentMode::Single { dirty_rect }) => dirty_rect.to_box2d(), - None => tile.rect.to_box2d(), - }; + // Work out how many dirty rects WR produced, and if that's more than + // what the device supports. + for tile in composite_state.opaque_tiles.iter().chain(composite_state.alpha_tiles.iter()) { + let dirty_rect = tile.dirty_rect.translate(tile.rect.origin.to_vector()); + combined_dirty_rect = combined_dirty_rect.union(&dirty_rect); + } - // Simple compositor needs the valid rect in device space to match clip rect - let valid_device_rect = tile.valid_rect.translate( - tile.rect.origin.to_vector() - ).to_box2d(); + let combined_dirty_rect = combined_dirty_rect.round(); + let combined_dirty_rect_i32 = combined_dirty_rect.to_i32(); + // If nothing has changed, don't return any dirty rects at all (the client + // can use this as a signal to skip present completely). + if !combined_dirty_rect.is_empty() { + results.dirty_rects.push(combined_dirty_rect_i32); + } - let rect = tile.rect.to_box2d() - .intersection_unchecked(&tile.clip_rect.to_box2d()) - .intersection_unchecked(&partial_clip_rect) - .intersection_unchecked(&valid_device_rect); + // If the implementation requires manually keeping the buffer consistent, + // combine the previous frame's damage for tile clipping. + // (Not for the returned region though, that should be from this frame only) + partial_present_mode = Some(PartialPresentMode::Single { + dirty_rect: if draw_previous_partial_present_regions { + combined_dirty_rect.union(&self.prev_dirty_rect) + } else { combined_dirty_rect }, + }); - if rect.is_empty() { - continue; - } + if draw_previous_partial_present_regions { + self.prev_dirty_rect = combined_dirty_rect; + } + } else { + // If we don't have a valid partial present scenario, return a single + // dirty rect to the client that covers the entire framebuffer. + let fb_rect = DeviceIntRect::new( + DeviceIntPoint::zero(), + draw_target.dimensions(), + ); + results.dirty_rects.push(fb_rect); - if tile.kind == TileKind::Clear { - // Clear tiles are specific to how we render the window buttons on - // Windows 8. We can get away with drawing them at the end on top - // of everything else, which we do to avoid having to juggle with - // the blend state. - clear_tiles.push(occlusion::Item { rectangle: rect, key: idx }); - continue; + if draw_previous_partial_present_regions { + self.prev_dirty_rect = fb_rect.to_f32(); + } } - occlusion.add(&rect, is_opaque, idx); + self.force_redraw = false; } - // Clear the framebuffer - let clear_color = self.clear_color.map(|color| color.to_array()); + // Clear the framebuffer, if required + if clear_framebuffer { + let clear_color = self.clear_color.map(|color| color.to_array()); - match partial_present_mode { - Some(PartialPresentMode::Single { dirty_rect }) => { - // There is no need to clear if the dirty rect is occluded. Additionally, - // on Mali-G77 we have observed artefacts when calling glClear (even with - // the empty scissor rect set) after calling eglSetDamageRegion with an - // empty damage region. So avoid clearing in that case. See bug 1709548. - if !dirty_rect.is_empty() && occlusion.test(&dirty_rect.to_box2d()) { + match partial_present_mode { + Some(PartialPresentMode::Single { dirty_rect }) => { // We have a single dirty rect, so clear only that self.device.clear_target(clear_color, - None, + Some(1.0), Some(draw_target.to_framebuffer_rect(dirty_rect.to_i32()))); } - } - None => { - // Partial present is disabled, so clear the entire framebuffer - self.device.clear_target(clear_color, - None, - None); + None => { + // Partial present is disabled, so clear the entire framebuffer + self.device.clear_target(clear_color, + Some(1.0), + None); + } } } // We are only interested in tiles backed with actual cached pixels so we don't // count clear tiles here. - let num_tiles = composite_state.tiles - .iter() - .filter(|tile| tile.kind != TileKind::Clear).count(); - self.profile.set(profiler::PICTURE_TILES, num_tiles); - - if !occlusion.opaque_items().is_empty() { - let opaque_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_OPAQUE); + let num_tiles = composite_state.opaque_tiles.len() + + composite_state.alpha_tiles.len(); + self.profile_counters.total_picture_cache_tiles.set(num_tiles); + + // Draw opaque tiles first, front-to-back to get maxmum + // z-reject efficiency. + if !composite_state.opaque_tiles.is_empty() { + let opaque_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_OPAQUE); + self.device.enable_depth_write(); self.set_blend(false, FramebufferKind::Main); self.draw_tile_list( - occlusion.opaque_items().iter(), - &composite_state, + composite_state.opaque_tiles.iter().rev(), &composite_state.external_surfaces, projection, + partial_present_mode, &mut results.stats, ); - self.gpu_profiler.finish_sampler(opaque_sampler); + self.gpu_profile.finish_sampler(opaque_sampler); } - // Draw alpha tiles - if !occlusion.alpha_items().is_empty() { - let transparent_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); + if !composite_state.clear_tiles.is_empty() { + let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); + self.device.disable_depth_write(); self.set_blend(true, FramebufferKind::Main); - self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main); + self.device.set_blend_mode_premultiplied_dest_out(); self.draw_tile_list( - occlusion.alpha_items().iter().rev(), - &composite_state, + composite_state.clear_tiles.iter(), &composite_state.external_surfaces, projection, + partial_present_mode, &mut results.stats, ); - self.gpu_profiler.finish_sampler(transparent_sampler); + self.gpu_profile.finish_sampler(transparent_sampler); } - if !clear_tiles.is_empty() { - let transparent_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); + // Draw alpha tiles + if !composite_state.alpha_tiles.is_empty() { + let transparent_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_TRANSPARENT); + self.device.disable_depth_write(); self.set_blend(true, FramebufferKind::Main); - self.device.set_blend_mode_premultiplied_dest_out(); + self.set_blend_mode_premultiplied_alpha(FramebufferKind::Main); self.draw_tile_list( - clear_tiles.iter(), - &composite_state, + composite_state.alpha_tiles.iter(), &composite_state.external_surfaces, projection, + partial_present_mode, &mut results.stats, ); - self.gpu_profiler.finish_sampler(transparent_sampler); + self.gpu_profile.finish_sampler(transparent_sampler); } } @@ -3382,16 +5100,18 @@ impl Renderer { &mut self, draw_target: DrawTarget, target: &ColorRenderTarget, + content_origin: DeviceIntPoint, clear_color: Option<[f32; 4]>, clear_depth: Option<f32>, render_tasks: &RenderTaskGraph, projection: &default::Transform3D<f32>, + frame_id: GpuFrameId, stats: &mut RendererStats, ) { profile_scope!("draw_color_target"); - self.profile.inc(profiler::COLOR_PASSES); - let _gm = self.gpu_profiler.start_marker("color target"); + self.profile_counters.color_passes.inc(); + let _gm = self.gpu_profile.start_marker("color target"); // sanity check for the depth buffer if let DrawTarget::Texture { with_depth, .. } = draw_target { @@ -3405,7 +5125,7 @@ impl Renderer { }; { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_SETUP_TARGET); + let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET); self.device.bind_draw_target(draw_target); self.device.disable_depth(); self.set_blend(false, framebuffer_kind); @@ -3436,7 +5156,7 @@ impl Renderer { // target slices were minimum 2048x2048. Now that we size // them adaptively, this may be less of a win (except perhaps // on a mostly-unused last slice of a large texture array). - Some(draw_target.to_framebuffer_rect(target.used_rect)) + Some(draw_target.to_framebuffer_rect(target.used_rect())) } DrawTarget::Texture { .. } | DrawTarget::External { .. } => { None @@ -3456,9 +5176,7 @@ impl Renderer { // Handle any blits from the texture cache to this target. self.handle_blits( - &target.blits, - render_tasks, - draw_target, + &target.blits, render_tasks, draw_target, &content_origin, ); // Draw any blurs for this target. @@ -3468,22 +5186,26 @@ impl Renderer { // fast path blur shaders for common // blur radii with fixed weights. if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_BLUR); + let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR); self.set_blend(false, framebuffer_kind); self.shaders.borrow_mut().cs_blur_rgba8 - .bind(&mut self.device, projection, None, &mut self.renderer_errors); + .bind(&mut self.device, projection, &mut self.renderer_errors); if !target.vertical_blurs.is_empty() { - self.draw_blurs( + self.draw_instanced_batch( &target.vertical_blurs, + VertexArrayKind::Blur, + &BatchTextures::no_texture(), stats, ); } if !target.horizontal_blurs.is_empty() { - self.draw_blurs( + self.draw_instanced_batch( &target.horizontal_blurs, + VertexArrayKind::Blur, + &BatchTextures::no_texture(), stats, ); } @@ -3508,6 +5230,7 @@ impl Renderer { self.draw_alpha_batch_container( alpha_batch_container, draw_target, + content_origin, framebuffer_kind, projection, render_tasks, @@ -3515,27 +5238,47 @@ impl Renderer { ); } - if clear_depth.is_some() { - self.device.invalidate_depth_target(); - } - } - - fn draw_blurs( - &mut self, - blurs: &FastHashMap<TextureSource, Vec<BlurInstance>>, - stats: &mut RendererStats, - ) { - for (texture, blurs) in blurs { - let textures = BatchTextures::composite_rgb( - *texture, - ); - - self.draw_instanced_batch( - blurs, - VertexArrayKind::Blur, - &textures, - stats, - ); + // For any registered image outputs on this render target, + // get the texture from caller and blit it. + for output in &target.outputs { + let handler = self.output_image_handler + .as_mut() + .expect("Found output image, but no handler set!"); + if let Some((texture_id, output_size)) = handler.lock(output.pipeline_id) { + let fbo_id = match self.output_targets.entry(texture_id) { + Entry::Vacant(entry) => { + let fbo_id = self.device.create_fbo_for_external_texture(texture_id); + entry.insert(FrameOutput { + fbo_id, + last_access: frame_id, + }); + fbo_id + } + Entry::Occupied(mut entry) => { + let target = entry.get_mut(); + target.last_access = frame_id; + target.fbo_id + } + }; + let (src_rect, _) = render_tasks[output.task_id].get_target_rect(); + if !self.device.surface_origin_is_top_left() { + self.device.blit_render_target_invert_y( + draw_target.into(), + draw_target.to_framebuffer_rect(src_rect.translate(-content_origin.to_vector())), + DrawTarget::External { fbo: fbo_id, size: output_size }, + output_size.into(), + ); + } else { + self.device.blit_render_target( + draw_target.into(), + draw_target.to_framebuffer_rect(src_rect.translate(-content_origin.to_vector())), + DrawTarget::External { fbo: fbo_id, size: output_size }, + output_size.into(), + TextureFilter::Linear, + ); + } + handler.unlock(output.pipeline_id); + } } } @@ -3543,7 +5286,6 @@ impl Renderer { fn draw_clip_batch_list( &mut self, list: &ClipBatchList, - draw_target: &DrawTarget, projection: &default::Transform3D<f32>, stats: &mut RendererStats, ) { @@ -3553,85 +5295,72 @@ impl Renderer { // draw rounded cornered rectangles if !list.slow_rectangles.is_empty() { - let _gm2 = self.gpu_profiler.start_marker("slow clip rectangles"); + let _gm2 = self.gpu_profile.start_marker("slow clip rectangles"); self.shaders.borrow_mut().cs_clip_rectangle_slow.bind( &mut self.device, projection, - None, &mut self.renderer_errors, ); self.draw_instanced_batch( &list.slow_rectangles, - VertexArrayKind::ClipRect, - &BatchTextures::empty(), + VertexArrayKind::Clip, + &BatchTextures::no_texture(), stats, ); } if !list.fast_rectangles.is_empty() { - let _gm2 = self.gpu_profiler.start_marker("fast clip rectangles"); + let _gm2 = self.gpu_profile.start_marker("fast clip rectangles"); self.shaders.borrow_mut().cs_clip_rectangle_fast.bind( &mut self.device, projection, - None, &mut self.renderer_errors, ); self.draw_instanced_batch( &list.fast_rectangles, - VertexArrayKind::ClipRect, - &BatchTextures::empty(), + VertexArrayKind::Clip, + &BatchTextures::no_texture(), stats, ); } - // draw box-shadow clips for (mask_texture_id, items) in list.box_shadows.iter() { - let _gm2 = self.gpu_profiler.start_marker("box-shadows"); - let textures = BatchTextures::composite_rgb(*mask_texture_id); + let _gm2 = self.gpu_profile.start_marker("box-shadows"); + let textures = BatchTextures { + colors: [ + *mask_texture_id, + TextureSource::Invalid, + TextureSource::Invalid, + ], + }; self.shaders.borrow_mut().cs_clip_box_shadow - .bind(&mut self.device, projection, None, &mut self.renderer_errors); + .bind(&mut self.device, projection, &mut self.renderer_errors); self.draw_instanced_batch( items, - VertexArrayKind::ClipBoxShadow, + VertexArrayKind::Clip, &textures, stats, ); } // draw image masks - let mut using_scissor = false; - for ((mask_texture_id, clip_rect), items) in list.images.iter() { - let _gm2 = self.gpu_profiler.start_marker("clip images"); - // Some image masks may require scissoring to ensure they don't draw - // outside their task's target bounds. Axis-aligned primitives will - // be clamped inside the shader and should not require scissoring. - // TODO: We currently assume scissor state is off by default for - // alpha targets here, but in the future we may want to track the - // current scissor state so that this can be properly saved and - // restored here. - if let Some(clip_rect) = clip_rect { - if !using_scissor { - self.device.enable_scissor(); - using_scissor = true; - } - let scissor_rect = draw_target.build_scissor_rect(Some(*clip_rect)); - self.device.set_scissor_rect(scissor_rect); - } else if using_scissor { - self.device.disable_scissor(); - using_scissor = false; - } - let textures = BatchTextures::composite_rgb(*mask_texture_id); + for (mask_texture_id, items) in list.images.iter() { + let _gm2 = self.gpu_profile.start_marker("clip images"); + let textures = BatchTextures { + colors: [ + *mask_texture_id, + TextureSource::Invalid, + TextureSource::Invalid, + ], + }; self.shaders.borrow_mut().cs_clip_image - .bind(&mut self.device, projection, None, &mut self.renderer_errors); + .bind(&mut self.device, projection, &mut self.renderer_errors); self.draw_instanced_batch( items, - VertexArrayKind::ClipImage, + VertexArrayKind::Clip, &textures, stats, ); } - if using_scissor { - self.device.disable_scissor(); - } } fn draw_alpha_target( @@ -3644,88 +5373,41 @@ impl Renderer { ) { profile_scope!("draw_alpha_target"); - self.profile.inc(profiler::ALPHA_PASSES); - let _gm = self.gpu_profiler.start_marker("alpha target"); - let alpha_sampler = self.gpu_profiler.start_sampler(GPU_SAMPLER_TAG_ALPHA); + self.profile_counters.alpha_passes.inc(); + let _gm = self.gpu_profile.start_marker("alpha target"); + let alpha_sampler = self.gpu_profile.start_sampler(GPU_SAMPLER_TAG_ALPHA); { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_SETUP_TARGET); + let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_TARGET); self.device.bind_draw_target(draw_target); self.device.disable_depth(); self.device.disable_depth_write(); self.set_blend(false, FramebufferKind::Other); - let zero_color = [0.0, 0.0, 0.0, 0.0]; - let one_color = [1.0, 1.0, 1.0, 1.0]; + // TODO(gw): Applying a scissor rect and minimal clear here + // is a very large performance win on the Intel and nVidia + // GPUs that I have tested with. It's possible it may be a + // performance penalty on other GPU types - we should test this + // and consider different code paths. - // On some Mali-T devices we have observed crashes in subsequent draw calls - // immediately after clearing the alpha render target regions with glClear(). - // Using the shader to clear the regions avoids the crash. See bug 1638593. - if self.clear_alpha_targets_with_quads - && !(target.zero_clears.is_empty() && target.one_clears.is_empty()) - { - let zeroes = target.zero_clears - .iter() - .map(|task_id| { - let rect = render_tasks[*task_id].get_target_rect().to_f32(); - ClearInstance { - rect: [ - rect.origin.x, rect.origin.y, - rect.size.width, rect.size.height, - ], - color: zero_color, - } - }); - - let ones = target.one_clears - .iter() - .map(|task_id| { - let rect = render_tasks[*task_id].get_target_rect().to_f32(); - ClearInstance { - rect: [ - rect.origin.x, rect.origin.y, - rect.size.width, rect.size.height, - ], - color: one_color, - } - }); - - let instances = zeroes.chain(ones).collect::<Vec<_>>(); - self.shaders.borrow_mut().ps_clear.bind( - &mut self.device, - &projection, + let zero_color = [0.0, 0.0, 0.0, 0.0]; + for &task_id in &target.zero_clears { + let (rect, _) = render_tasks[task_id].get_target_rect(); + self.device.clear_target( + Some(zero_color), None, - &mut self.renderer_errors, + Some(draw_target.to_framebuffer_rect(rect)), ); - self.draw_instanced_batch( - &instances, - VertexArrayKind::Clear, - &BatchTextures::empty(), - stats, - ); - } else { - // TODO(gw): Applying a scissor rect and minimal clear here - // is a very large performance win on the Intel and nVidia - // GPUs that I have tested with. It's possible it may be a - // performance penalty on other GPU types - we should test this - // and consider different code paths. - for &task_id in &target.zero_clears { - let rect = render_tasks[task_id].get_target_rect(); - self.device.clear_target( - Some(zero_color), - None, - Some(draw_target.to_framebuffer_rect(rect)), - ); - } + } - for &task_id in &target.one_clears { - let rect = render_tasks[task_id].get_target_rect(); - self.device.clear_target( - Some(one_color), - None, - Some(draw_target.to_framebuffer_rect(rect)), - ); - } + let one_color = [1.0, 1.0, 1.0, 1.0]; + for &task_id in &target.one_clears { + let (rect, _) = render_tasks[task_id].get_target_rect(); + self.device.clear_target( + Some(one_color), + None, + Some(draw_target.to_framebuffer_rect(rect)), + ); } } @@ -3736,21 +5418,25 @@ impl Renderer { // fast path blur shaders for common // blur radii with fixed weights. if !target.vertical_blurs.is_empty() || !target.horizontal_blurs.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_BLUR); + let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR); self.shaders.borrow_mut().cs_blur_a8 - .bind(&mut self.device, projection, None, &mut self.renderer_errors); + .bind(&mut self.device, projection, &mut self.renderer_errors); if !target.vertical_blurs.is_empty() { - self.draw_blurs( + self.draw_instanced_batch( &target.vertical_blurs, + VertexArrayKind::Blur, + &BatchTextures::no_texture(), stats, ); } if !target.horizontal_blurs.is_empty() { - self.draw_blurs( + self.draw_instanced_batch( &target.horizontal_blurs, + VertexArrayKind::Blur, + &BatchTextures::no_texture(), stats, ); } @@ -3764,7 +5450,7 @@ impl Renderer { // Draw the clip items into the tiled alpha mask. { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_CLIP); + let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_CLIP); // TODO(gw): Consider grouping multiple clip masks per shader // invocation here to reduce memory bandwith further? @@ -3775,7 +5461,6 @@ impl Renderer { self.set_blend(false, FramebufferKind::Other); self.draw_clip_batch_list( &target.clip_batcher.primary_clips, - &draw_target, projection, stats, ); @@ -3786,49 +5471,58 @@ impl Renderer { self.set_blend_mode_multiply(FramebufferKind::Other); self.draw_clip_batch_list( &target.clip_batcher.secondary_clips, - &draw_target, projection, stats, ); } - self.gpu_profiler.finish_sampler(alpha_sampler); + self.gpu_profile.finish_sampler(alpha_sampler); } fn draw_texture_cache_target( &mut self, texture: &CacheTextureId, + layer: LayerIndex, target: &TextureCacheRenderTarget, render_tasks: &RenderTaskGraph, stats: &mut RendererStats, ) { profile_scope!("draw_texture_cache_target"); + let texture_source = TextureSource::TextureCache(*texture, Swizzle::default()); + let projection = { + let (texture, _) = self.texture_resolver + .resolve(&texture_source) + .expect("BUG: invalid target texture"); + let target_size = texture.get_dimensions(); + + Transform3D::ortho( + 0.0, + target_size.width as f32, + 0.0, + target_size.height as f32, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), + ) + }; + self.device.disable_depth(); self.device.disable_depth_write(); self.set_blend(false, FramebufferKind::Other); - let texture = &self.texture_resolver.texture_cache_map[texture]; - let target_size = texture.get_dimensions(); - - let projection = Transform3D::ortho( - 0.0, - target_size.width as f32, - 0.0, - target_size.height as f32, - self.device.ortho_near_plane(), - self.device.ortho_far_plane(), - ); - - let draw_target = DrawTarget::from_texture( - texture, - false, - ); - self.device.bind_draw_target(draw_target); - { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CLEAR); + let _timer = self.gpu_profile.start_timer(GPU_TAG_CLEAR); + + let (texture, _) = self.texture_resolver + .resolve(&texture_source) + .expect("BUG: invalid target texture"); + let draw_target = DrawTarget::from_texture( + texture, + layer, + false, + ); + self.device.bind_draw_target(draw_target); self.device.disable_depth(); self.device.disable_depth_write(); @@ -3849,13 +5543,12 @@ impl Renderer { self.shaders.borrow_mut().ps_clear.bind( &mut self.device, &projection, - None, &mut self.renderer_errors, ); self.draw_instanced_batch( &instances, VertexArrayKind::Clear, - &BatchTextures::empty(), + &BatchTextures::no_texture(), stats, ); } else { @@ -3870,9 +5563,7 @@ impl Renderer { // Handle any blits to this texture from child tasks. self.handle_blits( - &target.blits, - render_tasks, - draw_target, + &target.blits, render_tasks, draw_target, &DeviceIntPoint::zero(), ); } @@ -3880,7 +5571,7 @@ impl Renderer { if !target.border_segments_solid.is_empty() || !target.border_segments_complex.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_BORDER); + let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_BORDER); self.set_blend(true, FramebufferKind::Other); self.set_blend_mode_premultiplied_alpha(FramebufferKind::Other); @@ -3889,14 +5580,13 @@ impl Renderer { self.shaders.borrow_mut().cs_border_solid.bind( &mut self.device, &projection, - None, &mut self.renderer_errors, ); self.draw_instanced_batch( &target.border_segments_solid, VertexArrayKind::Border, - &BatchTextures::empty(), + &BatchTextures::no_texture(), stats, ); } @@ -3905,14 +5595,13 @@ impl Renderer { self.shaders.borrow_mut().cs_border_segment.bind( &mut self.device, &projection, - None, &mut self.renderer_errors, ); self.draw_instanced_batch( &target.border_segments_complex, VertexArrayKind::Border, - &BatchTextures::empty(), + &BatchTextures::no_texture(), stats, ); } @@ -3922,7 +5611,7 @@ impl Renderer { // Draw any line decorations for this target. if !target.line_decorations.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_LINE_DECORATION); + let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_LINE_DECORATION); self.set_blend(true, FramebufferKind::Other); self.set_blend_mode_premultiplied_alpha(FramebufferKind::Other); @@ -3930,130 +5619,55 @@ impl Renderer { self.shaders.borrow_mut().cs_line_decoration.bind( &mut self.device, &projection, - None, &mut self.renderer_errors, ); self.draw_instanced_batch( &target.line_decorations, VertexArrayKind::LineDecoration, - &BatchTextures::empty(), - stats, - ); - - self.set_blend(false, FramebufferKind::Other); - } - - // Draw any fast path linear gradients for this target. - if !target.fast_linear_gradients.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_FAST_LINEAR_GRADIENT); - - self.set_blend(false, FramebufferKind::Other); - - self.shaders.borrow_mut().cs_fast_linear_gradient.bind( - &mut self.device, - &projection, - None, - &mut self.renderer_errors, - ); - - self.draw_instanced_batch( - &target.fast_linear_gradients, - VertexArrayKind::FastLinearGradient, - &BatchTextures::empty(), - stats, - ); - } - - // Draw any linear gradients for this target. - if !target.linear_gradients.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_LINEAR_GRADIENT); - - self.set_blend(false, FramebufferKind::Other); - - self.shaders.borrow_mut().cs_linear_gradient.bind( - &mut self.device, - &projection, - None, - &mut self.renderer_errors, - ); - - if let Some(ref texture) = self.dither_matrix_texture { - self.device.bind_texture(TextureSampler::Dither, texture, Swizzle::default()); - } - - self.draw_instanced_batch( - &target.linear_gradients, - VertexArrayKind::LinearGradient, - &BatchTextures::empty(), + &BatchTextures::no_texture(), stats, ); - } - - // Draw any radial gradients for this target. - if !target.radial_gradients.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_RADIAL_GRADIENT); self.set_blend(false, FramebufferKind::Other); - - self.shaders.borrow_mut().cs_radial_gradient.bind( - &mut self.device, - &projection, - None, - &mut self.renderer_errors, - ); - - if let Some(ref texture) = self.dither_matrix_texture { - self.device.bind_texture(TextureSampler::Dither, texture, Swizzle::default()); - } - - self.draw_instanced_batch( - &target.radial_gradients, - VertexArrayKind::RadialGradient, - &BatchTextures::empty(), - stats, - ); } - // Draw any conic gradients for this target. - if !target.conic_gradients.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_CONIC_GRADIENT); + // Draw any gradients for this target. + if !target.gradients.is_empty() { + let _timer = self.gpu_profile.start_timer(GPU_TAG_CACHE_GRADIENT); self.set_blend(false, FramebufferKind::Other); - self.shaders.borrow_mut().cs_conic_gradient.bind( + self.shaders.borrow_mut().cs_gradient.bind( &mut self.device, &projection, - None, &mut self.renderer_errors, ); - if let Some(ref texture) = self.dither_matrix_texture { - self.device.bind_texture(TextureSampler::Dither, texture, Swizzle::default()); - } - self.draw_instanced_batch( - &target.conic_gradients, - VertexArrayKind::ConicGradient, - &BatchTextures::empty(), + &target.gradients, + VertexArrayKind::Gradient, + &BatchTextures::no_texture(), stats, ); } // Draw any blurs for this target. if !target.horizontal_blurs.is_empty() { - let _timer = self.gpu_profiler.start_timer(GPU_TAG_BLUR); + let _timer = self.gpu_profile.start_timer(GPU_TAG_BLUR); { let mut shaders = self.shaders.borrow_mut(); match target.target_kind { RenderTargetKind::Alpha => &mut shaders.cs_blur_a8, RenderTargetKind::Color => &mut shaders.cs_blur_rgba8, - }.bind(&mut self.device, &projection, None, &mut self.renderer_errors); + }.bind(&mut self.device, &projection, &mut self.renderer_errors); } - self.draw_blurs( + self.draw_instanced_batch( &target.horizontal_blurs, + VertexArrayKind::Blur, + &BatchTextures::no_texture(), stats, ); } @@ -4081,8 +5695,8 @@ impl Renderer { debug_commands: Vec::new(), }; - for (i, deferred_resolve) in deferred_resolves.iter().enumerate() { - self.gpu_profiler.place_marker("deferred resolve"); + for deferred_resolve in deferred_resolves { + self.gpu_profile.place_marker("deferred resolve"); let props = &deferred_resolve.image_properties; let ext_image = props .external_image @@ -4131,7 +5745,7 @@ impl Renderer { self.texture_resolver .external_images - .insert(DeferredResolveIndex(i as u32), texture); + .insert((ext_image.id, ext_image.channel_index), texture); list.updates.push(GpuCacheUpdate::Copy { block_index: list.blocks.len(), @@ -4145,145 +5759,112 @@ impl Renderer { Some(list) } - fn unlock_external_images( - &mut self, - deferred_resolves: &[DeferredResolve], - ) { + fn unlock_external_images(&mut self) { if !self.texture_resolver.external_images.is_empty() { let handler = self.external_image_handler .as_mut() .expect("Found external image, but no handler set!"); - for (index, _) in self.texture_resolver.external_images.drain() { - let props = &deferred_resolves[index.0 as usize].image_properties; - let ext_image = props - .external_image - .expect("BUG: Deferred resolves must be external images!"); - handler.unlock(ext_image.id, ext_image.channel_index); + for (ext_data, _) in self.texture_resolver.external_images.drain() { + handler.unlock(ext_data.0, ext_data.1); } } } - /// Update the dirty rects based on current compositing mode and config - // TODO(gw): This can be tidied up significantly once the Draw compositor - // is implemented in terms of the compositor trait. - fn calculate_dirty_rects( + /// Allocates a texture to be used as the output for a rendering pass. + /// + /// We make an effort to reuse render targe textures across passes and + /// across frames when the format and dimensions match. Because we use + /// immutable storage, we can't resize textures. + /// + /// We could consider approaches to re-use part of a larger target, if + /// available. However, we'd need to be careful about eviction. Currently, + /// render targets are freed if they haven't been used in 30 frames. If we + /// used partial targets, we'd need to track how _much_ of the target has + /// been used in the last 30 frames, since we could otherwise end up + /// keeping an enormous target alive indefinitely by constantly using it + /// in situations where a much smaller target would suffice. + fn allocate_target_texture<T: RenderTarget>( &mut self, - buffer_age: usize, - composite_state: &CompositeState, - draw_target_dimensions: DeviceIntSize, - results: &mut RenderResults, - ) -> Option<PartialPresentMode> { - let mut partial_present_mode = None; - - let (max_partial_present_rects, draw_previous_partial_present_regions) = match self.current_compositor_kind { - CompositorKind::Native { .. } => { - // Assume that we can return a single dirty rect for native - // compositor for now, and that there is no buffer-age functionality. - // These params can be exposed by the compositor capabilities struct - // as the Draw compositor is ported to use it. - (1, false) - } - CompositorKind::Draw { draw_previous_partial_present_regions, max_partial_present_rects } => { - (max_partial_present_rects, draw_previous_partial_present_regions) - } - }; - - if max_partial_present_rects > 0 { - let prev_frames_damage_rect = if let Some(..) = self.compositor_config.partial_present() { - self.buffer_damage_tracker - .get_damage_rect(buffer_age) - .or_else(|| Some(DeviceRect::from_size(draw_target_dimensions.to_f32()))) - } else { - None - }; - - let can_use_partial_present = - composite_state.dirty_rects_are_valid && - !self.force_redraw && - !(prev_frames_damage_rect.is_none() && draw_previous_partial_present_regions) && - !self.debug_overlay_state.is_enabled; - - if can_use_partial_present { - let mut combined_dirty_rect = DeviceRect::zero(); - - // Work out how many dirty rects WR produced, and if that's more than - // what the device supports. - for tile in &composite_state.tiles { - if tile.kind == TileKind::Clear { - continue; - } - let tile_dirty_rect = tile.dirty_rect.translate(tile.rect.origin.to_vector()); - let transformed_dirty_rect = if let Some(transform) = tile.transform { - transform.outer_transformed_rect(&tile_dirty_rect) - } else { - Some(tile_dirty_rect) - }; - - if let Some(dirty_rect) = transformed_dirty_rect { - combined_dirty_rect = combined_dirty_rect.union(&dirty_rect); - } - } - - let combined_dirty_rect = combined_dirty_rect.round(); - let combined_dirty_rect_i32 = combined_dirty_rect.to_i32(); - // Return this frame's dirty region. If nothing has changed, don't return any dirty - // rects at all (the client can use this as a signal to skip present completely). - if !combined_dirty_rect.is_empty() { - results.dirty_rects.push(combined_dirty_rect_i32); - } - - // Track this frame's dirty region, for calculating subsequent frames' damage. - if draw_previous_partial_present_regions { - self.buffer_damage_tracker.push_dirty_rect(&combined_dirty_rect); - } + list: &mut RenderTargetList<T>, + counters: &mut FrameProfileCounters, + ) -> Option<ActiveTexture> { + if list.targets.is_empty() { + return None + } + + // Get a bounding rect of all the layers, and round it up to a multiple + // of 256. This improves render target reuse when resizing the window, + // since we don't need to create a new render target for each slightly- + // larger frame. + let mut bounding_rect = DeviceIntRect::zero(); + for t in list.targets.iter() { + bounding_rect = t.used_rect().union(&bounding_rect); + } + debug_assert_eq!(bounding_rect.origin, DeviceIntPoint::zero()); + let dimensions = DeviceIntSize::new( + (bounding_rect.size.width + 255) & !255, + (bounding_rect.size.height + 255) & !255, + ); - // If the implementation requires manually keeping the buffer consistent, - // then we must combine this frame's dirty region with that of previous frames - // to determine the total_dirty_rect. The is used to determine what region we - // render to, and is what we send to the compositor as the buffer damage region - // (eg for KHR_partial_update). - let total_dirty_rect = if draw_previous_partial_present_regions { - combined_dirty_rect.union(&prev_frames_damage_rect.unwrap()) - } else { - combined_dirty_rect - }; + counters.targets_used.inc(); - partial_present_mode = Some(PartialPresentMode::Single { - dirty_rect: total_dirty_rect, - }); - } else { - // If we don't have a valid partial present scenario, return a single - // dirty rect to the client that covers the entire framebuffer. - let fb_rect = DeviceIntRect::new( - DeviceIntPoint::zero(), - draw_target_dimensions, - ); - results.dirty_rects.push(fb_rect); - - if draw_previous_partial_present_regions { - self.buffer_damage_tracker.push_dirty_rect(&fb_rect.to_f32()); + // Try finding a match in the existing pool. If there's no match, we'll + // create a new texture. + let selector = TargetSelector { + size: dimensions, + num_layers: list.targets.len(), + format: list.format, + }; + let index = self.texture_resolver.render_target_pool + .iter() + .position(|texture| { + selector == TargetSelector { + size: texture.get_dimensions(), + num_layers: texture.get_layer_count() as usize, + format: texture.get_format(), } - } + }); - self.force_redraw = false; - } + let rt_info = RenderTargetInfo { has_depth: list.needs_depth() }; + let texture = if let Some(idx) = index { + let mut t = self.texture_resolver.render_target_pool.swap_remove(idx); + self.device.reuse_render_target::<u8>(&mut t, rt_info); + t + } else { + counters.targets_created.inc(); + self.device.create_texture( + TextureTarget::Array, + list.format, + dimensions.width, + dimensions.height, + TextureFilter::Linear, + Some(rt_info), + list.targets.len() as _, + ) + }; - partial_present_mode + list.check_ready(&texture); + Some(ActiveTexture { + texture, + saved_index: list.saved_index.clone(), + }) } fn bind_frame_data(&mut self, frame: &mut Frame) { profile_scope!("bind_frame_data"); - let _timer = self.gpu_profiler.start_timer(GPU_TAG_SETUP_DATA); + let _timer = self.gpu_profile.start_timer(GPU_TAG_SETUP_DATA); self.vertex_data_textures[self.current_vertex_data_textures].update( &mut self.device, - &mut self.texture_upload_pbo_pool, frame, ); self.current_vertex_data_textures = (self.current_vertex_data_textures + 1) % VERTEX_DATA_TEXTURE_COUNT; + + debug_assert!(self.texture_resolver.prev_pass_alpha.is_none()); + debug_assert!(self.texture_resolver.prev_pass_color.is_none()); } fn update_native_surfaces(&mut self) { @@ -4297,17 +5878,9 @@ impl Renderer { let _inserted = self.allocated_native_surfaces.insert(id); debug_assert!(_inserted, "bug: creating existing surface"); compositor.create_surface( - id, - virtual_offset, - tile_size, - is_opaque, - ); - } - NativeSurfaceOperationDetails::CreateExternalSurface { id, is_opaque } => { - let _inserted = self.allocated_native_surfaces.insert(id); - debug_assert!(_inserted, "bug: creating existing surface"); - compositor.create_external_surface( id, + virtual_offset, + tile_size, is_opaque, ); } @@ -4322,9 +5895,6 @@ impl Renderer { NativeSurfaceOperationDetails::DestroyTile { id } => { compositor.destroy_tile(id); } - NativeSurfaceOperationDetails::AttachExternalImage { id, external_image } => { - compositor.attach_external_image(id, external_image); - } } } } @@ -4340,14 +5910,15 @@ impl Renderer { &mut self, frame: &mut Frame, device_size: Option<DeviceIntSize>, - buffer_age: usize, + frame_id: GpuFrameId, results: &mut RenderResults, + clear_framebuffer: bool, ) { profile_scope!("draw_frame"); // These markers seem to crash a lot on Android, see bug 1559834 #[cfg(not(target_os = "android"))] - let _gm = self.gpu_profiler.start_marker("draw frame"); + let _gm = self.gpu_profile.start_marker("draw frame"); if frame.passes.is_empty() { frame.has_been_rendered = true; @@ -4360,341 +5931,382 @@ impl Renderer { self.bind_frame_data(frame); - // Determine the present mode and dirty rects, if device_size - // is Some(..). If it's None, no composite will occur and only - // picture cache and texture cache targets will be updated. - // TODO(gw): Split Frame so that it's clearer when a composite - // is occurring. - let present_mode = device_size.and_then(|device_size| { - self.calculate_dirty_rects( - buffer_age, - &frame.composite_state, - device_size, - results, - ) - }); - - // If we have a native OS compositor, then make use of that interface to - // specify how to composite each of the picture cache surfaces. First, we - // need to find each tile that may be bound and updated later in the frame - // and invalidate it so that the native render compositor knows that these - // tiles can't be composited early. Next, after all such tiles have been - // invalidated, then we queue surfaces for native composition by the render - // compositor before we actually update the tiles. This allows the render - // compositor to start early composition while the tiles are updating. - if let CompositorKind::Native { .. } = self.current_compositor_kind { - let compositor = self.compositor_config.compositor().unwrap(); - // Invalidate any native surface tiles that might be updated by passes. - if !frame.has_been_rendered { - for tile in &frame.composite_state.tiles { - if tile.kind == TileKind::Clear { - continue; - } - if !tile.dirty_rect.is_empty() { - if let CompositeTileSurface::Texture { surface: ResolvedSurfaceTexture::Native { id, .. } } = - tile.surface { - let valid_rect = tile.valid_rect - .round() - .to_i32(); - compositor.invalidate_tile(id, valid_rect); - } - } - } - } - // Ensure any external surfaces that might be used during early composition - // are invalidated first so that the native compositor can properly schedule - // composition to happen only when the external surface is updated. - // See update_external_native_surfaces for more details. - for surface in &frame.composite_state.external_surfaces { - if let Some((native_surface_id, size)) = surface.update_params { - let surface_rect = size.into(); - compositor.invalidate_tile(NativeTileId { surface_id: native_surface_id, x: 0, y: 0 }, surface_rect); - } - } - // Finally queue native surfaces for early composition, if applicable. By now, - // we have already invalidated any tiles that such surfaces may depend upon, so - // the native render compositor can keep track of when to actually schedule - // composition as surfaces are updated. - if device_size.is_some() { - frame.composite_state.composite_native( - &results.dirty_rects, - &mut **compositor, - ); - } - } - for (_pass_index, pass) in frame.passes.iter_mut().enumerate() { #[cfg(not(target_os = "android"))] - let _gm = self.gpu_profiler.start_marker(&format!("pass {}", _pass_index)); - - profile_scope!("offscreen target"); - - // If this frame has already been drawn, then any texture - // cache targets have already been updated and can be - // skipped this time. - if !frame.has_been_rendered { - for (&texture_id, target) in &pass.texture_cache { - self.draw_texture_cache_target( - &texture_id, - target, - &frame.render_tasks, - &mut results.stats, - ); - } + let _gm = self.gpu_profile.start_marker(&format!("pass {}", _pass_index)); - if !pass.picture_cache.is_empty() { - self.profile.inc(profiler::COLOR_PASSES); - } + self.texture_resolver.bind( + &TextureSource::PrevPassAlpha, + TextureSampler::PrevPassAlpha, + &mut self.device, + ); + self.texture_resolver.bind( + &TextureSource::PrevPassColor, + TextureSampler::PrevPassColor, + &mut self.device, + ); - // Draw picture caching tiles for this pass. - for picture_target in &pass.picture_cache { - results.stats.color_target_count += 1; + match pass.kind { + RenderPassKind::MainFramebuffer { ref main_target, .. } => { + profile_scope!("main target"); - let draw_target = match picture_target.surface { - ResolvedSurfaceTexture::TextureCache { ref texture } => { - let (texture, _) = self.texture_resolver - .resolve(texture) - .expect("bug"); + if let Some(device_size) = device_size { + results.stats.color_target_count += 1; - DrawTarget::from_texture( - texture, - true, - ) + let offset = frame.content_origin.to_f32(); + let size = frame.device_rect.size.to_f32(); + let surface_origin_is_top_left = self.device.surface_origin_is_top_left(); + let (bottom, top) = if surface_origin_is_top_left { + (offset.y, offset.y + size.height) + } else { + (offset.y + size.height, offset.y) + }; + + let projection = Transform3D::ortho( + offset.x, + offset.x + size.width, + bottom, + top, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), + ); + + let fb_scale = Scale::<_, _, FramebufferPixel>::new(1i32); + let mut fb_rect = frame.device_rect * fb_scale; + + if !surface_origin_is_top_left { + fb_rect.origin.y = device_size.height - fb_rect.origin.y - fb_rect.size.height; } - ResolvedSurfaceTexture::Native { id, size } => { - let surface_info = match self.current_compositor_kind { + + let draw_target = DrawTarget::Default { + rect: fb_rect, + total_size: device_size * fb_scale, + surface_origin_is_top_left, + }; + + // Picture caching can be enabled / disabled dynamically from frame to + // frame. This is determined by what the frame builder selected, and is + // passed to the renderer via the composite state. + if frame.composite_state.picture_caching_is_enabled { + // If we have a native OS compositor, then make use of that interface + // to specify how to composite each of the picture cache surfaces. + match self.current_compositor_kind { CompositorKind::Native { .. } => { + self.update_external_native_surfaces( + &frame.composite_state.external_surfaces, + results, + ); let compositor = self.compositor_config.compositor().unwrap(); - compositor.bind( - id, - picture_target.dirty_rect, - picture_target.valid_rect, - ) + frame.composite_state.composite_native(&mut **compositor); } - CompositorKind::Draw { .. } => { - unreachable!(); + CompositorKind::Draw { max_partial_present_rects, draw_previous_partial_present_regions, .. } => { + self.composite_simple( + &frame.composite_state, + clear_framebuffer, + draw_target, + &projection, + results, + max_partial_present_rects, + draw_previous_partial_present_regions, + ); } - }; - - DrawTarget::NativeSurface { - offset: surface_info.origin, - external_fbo_id: surface_info.fbo_id, - dimensions: size, - } - } - }; - - let projection = Transform3D::ortho( - 0.0, - draw_target.dimensions().width as f32, - 0.0, - draw_target.dimensions().height as f32, - self.device.ortho_near_plane(), - self.device.ortho_far_plane(), - ); - - self.draw_picture_cache_target( - picture_target, - draw_target, - &projection, - &frame.render_tasks, - &mut results.stats, - ); - - // Native OS surfaces must be unbound at the end of drawing to them - if let ResolvedSurfaceTexture::Native { .. } = picture_target.surface { - match self.current_compositor_kind { - CompositorKind::Native { .. } => { - let compositor = self.compositor_config.compositor().unwrap(); - compositor.unbind(); } - CompositorKind::Draw { .. } => { - unreachable!(); + } else { + if clear_framebuffer { + let clear_color = self.clear_color.map(|color| color.to_array()); + self.device.bind_draw_target(draw_target); + self.device.enable_depth_write(); + self.device.clear_target(clear_color, + Some(1.0), + None); } + + // If picture caching is disabled, we will be drawing the entire + // framebuffer. In that case, we need to push a screen size dirty + // rect, in case partial present is enabled (an empty array of + // dirty rects when partial present is enabled is interpreted by + // Gecko as meaning nothing has changed and a swap is not required). + results.dirty_rects.push(frame.device_rect); + + self.draw_color_target( + draw_target, + main_target, + frame.content_origin, + None, + None, + &frame.render_tasks, + &projection, + frame_id, + &mut results.stats, + ); } } } - } + RenderPassKind::OffScreen { + ref mut alpha, + ref mut color, + ref mut texture_cache, + ref mut picture_cache, + } => { + profile_scope!("offscreen target"); + + let alpha_tex = self.allocate_target_texture(alpha, &mut frame.profile_counters); + let color_tex = self.allocate_target_texture(color, &mut frame.profile_counters); + + // If this frame has already been drawn, then any texture + // cache targets have already been updated and can be + // skipped this time. + if !frame.has_been_rendered { + for (&(texture_id, target_index), target) in texture_cache { + self.draw_texture_cache_target( + &texture_id, + target_index, + target, + &frame.render_tasks, + &mut results.stats, + ); + } - for target in &pass.alpha.targets { - results.stats.alpha_target_count += 1; + if !picture_cache.is_empty() { + self.profile_counters.color_passes.inc(); + } - let texture_id = target.texture_id(); + // Draw picture caching tiles for this pass. + for picture_target in picture_cache { + results.stats.color_target_count += 1; - let alpha_tex = self.texture_resolver - .texture_cache_map - .get_mut(&texture_id) - .expect("bug: texture not allocated"); + let draw_target = match picture_target.surface { + ResolvedSurfaceTexture::TextureCache { ref texture, layer } => { + let (texture, _) = self.texture_resolver + .resolve(texture) + .expect("bug"); - let draw_target = DrawTarget::from_texture( - alpha_tex, - false, - ); + DrawTarget::from_texture( + texture, + layer as usize, + true, + ) + } + ResolvedSurfaceTexture::Native { id, size } => { + let surface_info = match self.current_compositor_kind { + CompositorKind::Native { .. } => { + let compositor = self.compositor_config.compositor().unwrap(); + compositor.bind( + id, + picture_target.dirty_rect, + picture_target.valid_rect, + ) + } + CompositorKind::Draw { .. } => { + unreachable!(); + } + }; + + DrawTarget::NativeSurface { + offset: surface_info.origin, + external_fbo_id: surface_info.fbo_id, + dimensions: size, + } + } + }; - let projection = Transform3D::ortho( - 0.0, - draw_target.dimensions().width as f32, - 0.0, - draw_target.dimensions().height as f32, - self.device.ortho_near_plane(), - self.device.ortho_far_plane(), - ); + let projection = Transform3D::ortho( + 0.0, + draw_target.dimensions().width as f32, + 0.0, + draw_target.dimensions().height as f32, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), + ); - self.draw_alpha_target( - draw_target, - target, - &projection, - &frame.render_tasks, - &mut results.stats, - ); - } + self.draw_picture_cache_target( + picture_target, + draw_target, + frame.content_origin, + &projection, + &frame.render_tasks, + &mut results.stats, + ); - let color_rt_info = RenderTargetInfo { has_depth: pass.color.needs_depth() }; + // Native OS surfaces must be unbound at the end of drawing to them + if let ResolvedSurfaceTexture::Native { .. } = picture_target.surface { + match self.current_compositor_kind { + CompositorKind::Native { .. } => { + let compositor = self.compositor_config.compositor().unwrap(); + compositor.unbind(); + } + CompositorKind::Draw { .. } => { + unreachable!(); + } + } + } + } + } - for target in &pass.color.targets { - results.stats.color_target_count += 1; + for (target_index, target) in alpha.targets.iter().enumerate() { + results.stats.alpha_target_count += 1; + let draw_target = DrawTarget::from_texture( + &alpha_tex.as_ref().unwrap().texture, + target_index, + false, + ); - let texture_id = target.texture_id(); + let projection = Transform3D::ortho( + 0.0, + draw_target.dimensions().width as f32, + 0.0, + draw_target.dimensions().height as f32, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), + ); - let color_tex = self.texture_resolver - .texture_cache_map - .get_mut(&texture_id) - .expect("bug: texture not allocated"); + self.draw_alpha_target( + draw_target, + target, + &projection, + &frame.render_tasks, + &mut results.stats, + ); + } - self.device.reuse_render_target::<u8>( - color_tex, - color_rt_info, - ); + for (target_index, target) in color.targets.iter().enumerate() { + results.stats.color_target_count += 1; + let draw_target = DrawTarget::from_texture( + &color_tex.as_ref().unwrap().texture, + target_index, + target.needs_depth(), + ); - let draw_target = DrawTarget::from_texture( - color_tex, - target.needs_depth(), - ); + let projection = Transform3D::ortho( + 0.0, + draw_target.dimensions().width as f32, + 0.0, + draw_target.dimensions().height as f32, + self.device.ortho_near_plane(), + self.device.ortho_far_plane(), + ); - let projection = Transform3D::ortho( - 0.0, - draw_target.dimensions().width as f32, - 0.0, - draw_target.dimensions().height as f32, - self.device.ortho_near_plane(), - self.device.ortho_far_plane(), - ); + let clear_depth = if target.needs_depth() { + Some(1.0) + } else { + None + }; - let clear_depth = if target.needs_depth() { - Some(1.0) - } else { - None - }; + self.draw_color_target( + draw_target, + target, + frame.content_origin, + Some([0.0, 0.0, 0.0, 0.0]), + clear_depth, + &frame.render_tasks, + &projection, + frame_id, + &mut results.stats, + ); + } - self.draw_color_target( - draw_target, - target, - Some([0.0, 0.0, 0.0, 0.0]), - clear_depth, - &frame.render_tasks, - &projection, - &mut results.stats, - ); + // Only end the pass here and invalidate previous textures for + // off-screen targets. Deferring return of the inputs to the + // frame buffer until the implicit end_pass in end_frame allows + // debug draw overlays to be added without triggering a copy + // resolve stage in mobile / tiled GPUs. + self.texture_resolver.end_pass( + &mut self.device, + alpha_tex, + color_tex, + ); + } } - - // Only end the pass here and invalidate previous textures for - // off-screen targets. Deferring return of the inputs to the - // frame buffer until the implicit end_pass in end_frame allows - // debug draw overlays to be added without triggering a copy - // resolve stage in mobile / tiled GPUs. - self.texture_resolver.end_pass( - &mut self.device, - &pass.textures_to_invalidate, - ); { profile_scope!("gl.flush"); self.device.gl().flush(); } } - self.composite_frame( - frame, - device_size, - results, - present_mode, - ); + if let Some(device_size) = device_size { + self.draw_frame_debug_items(&frame.debug_items); + self.draw_render_target_debug(device_size); + self.draw_texture_cache_debug(device_size); + self.draw_gpu_cache_debug(device_size); + self.draw_zoom_debug(device_size); + } + self.draw_epoch_debug(); + + // Garbage collect any frame outputs that weren't used this frame. + let device = &mut self.device; + self.output_targets + .retain(|_, target| if target.last_access != frame_id { + device.delete_fbo(target.fbo_id); + false + } else { + true + }); frame.has_been_rendered = true; } - fn composite_frame( + /// Initialize the PLS block, by reading the current framebuffer color. + pub fn init_pixel_local_storage( &mut self, - frame: &mut Frame, - device_size: Option<DeviceIntSize>, - results: &mut RenderResults, - present_mode: Option<PartialPresentMode>, + task_rect: DeviceIntRect, + projection: &default::Transform3D<f32>, + stats: &mut RendererStats, ) { - profile_scope!("main target"); + self.device.enable_pixel_local_storage(true); - if let Some(device_size) = device_size { - results.stats.color_target_count += 1; - results.picture_cache_debug = mem::replace( - &mut frame.composite_state.picture_cache_debug, - PictureCacheDebugInfo::new(), + self.shaders + .borrow_mut() + .pls_init + .as_mut() + .unwrap() + .bind( + &mut self.device, + projection, + &mut self.renderer_errors, ); - let size = frame.device_rect.size.to_f32(); - let surface_origin_is_top_left = self.device.surface_origin_is_top_left(); - let (bottom, top) = if surface_origin_is_top_left { - (0.0, size.height) - } else { - (size.height, 0.0) - }; + let instances = [ + ResolveInstanceData::new(task_rect), + ]; - let projection = Transform3D::ortho( - 0.0, - size.width, - bottom, - top, - self.device.ortho_near_plane(), - self.device.ortho_far_plane(), - ); + self.draw_instanced_batch( + &instances, + VertexArrayKind::Resolve, + &BatchTextures::no_texture(), + stats, + ); + } - let fb_scale = Scale::<_, _, FramebufferPixel>::new(1i32); - let mut fb_rect = frame.device_rect * fb_scale; + /// Resolve the current PLS structure, writing it to a fragment color output. + pub fn resolve_pixel_local_storage( + &mut self, + task_rect: DeviceIntRect, + projection: &default::Transform3D<f32>, + stats: &mut RendererStats, + ) { + self.shaders + .borrow_mut() + .pls_resolve + .as_mut() + .unwrap() + .bind( + &mut self.device, + projection, + &mut self.renderer_errors, + ); - if !surface_origin_is_top_left { - fb_rect.origin.y = device_size.height - fb_rect.origin.y - fb_rect.size.height; - } + let instances = [ + ResolveInstanceData::new(task_rect), + ]; - let draw_target = DrawTarget::Default { - rect: fb_rect, - total_size: device_size * fb_scale, - surface_origin_is_top_left, - }; + self.draw_instanced_batch( + &instances, + VertexArrayKind::Resolve, + &BatchTextures::no_texture(), + stats, + ); - // If we have a native OS compositor, then make use of that interface - // to specify how to composite each of the picture cache surfaces. - match self.current_compositor_kind { - CompositorKind::Native { .. } => { - // We have already queued surfaces for early native composition by this point. - // All that is left is to finally update any external native surfaces that were - // invalidated so that composition can complete. - self.update_external_native_surfaces( - &frame.composite_state.external_surfaces, - results, - ); - } - CompositorKind::Draw { .. } => { - self.composite_simple( - &frame.composite_state, - draw_target, - &projection, - results, - present_mode, - ); - } - } - } else { - // Rendering a frame without presenting it will confuse the partial - // present logic, so force a full present for the next frame. - self.force_redraw(); - } + self.device.enable_pixel_local_storage(false); } pub fn debug_renderer(&mut self) -> Option<&mut DebugRenderer> { @@ -4708,29 +6320,22 @@ impl Renderer { pub fn set_debug_flags(&mut self, flags: DebugFlags) { if let Some(enabled) = flag_changed(self.debug_flags, flags, DebugFlags::GPU_TIME_QUERIES) { if enabled { - self.gpu_profiler.enable_timers(); + self.gpu_profile.enable_timers(); } else { - self.gpu_profiler.disable_timers(); + self.gpu_profile.disable_timers(); } } if let Some(enabled) = flag_changed(self.debug_flags, flags, DebugFlags::GPU_SAMPLE_QUERIES) { if enabled { - self.gpu_profiler.enable_samplers(); + self.gpu_profile.enable_samplers(); } else { - self.gpu_profiler.disable_samplers(); + self.gpu_profile.disable_samplers(); } } - self.device.set_use_batched_texture_uploads(flags.contains(DebugFlags::USE_BATCHED_TEXTURE_UPLOADS)); - self.device.set_use_draw_calls_for_texture_copy(flags.contains(DebugFlags::USE_DRAW_CALLS_FOR_TEXTURE_COPY)); - self.debug_flags = flags; } - pub fn set_profiler_ui(&mut self, ui_str: &str) { - self.profiler.set_ui(ui_str); - } - fn draw_frame_debug_items(&mut self, items: &[DebugItem]) { if items.is_empty() { return; @@ -4771,7 +6376,7 @@ impl Renderer { } } - fn draw_render_target_debug(&mut self, draw_target: &DrawTarget) { + fn draw_render_target_debug(&mut self, device_size: DeviceIntSize) { if !self.debug_flags.contains(DebugFlags::RENDER_TARGET_DBG) { return; } @@ -4781,17 +6386,14 @@ impl Renderer { None => return, }; - let textures = self.texture_resolver - .texture_cache_map - .values() - .filter(|texture| { texture.is_render_target() }) - .collect::<Vec<&Texture>>(); + let textures = + self.texture_resolver.render_target_pool.iter().collect::<Vec<&Texture>>(); Self::do_debug_blit( &mut self.device, debug_renderer, textures, - draw_target, + device_size, 0, &|_| [0.0, 1.0, 0.0, 1.0], // Use green for all RTs. ); @@ -4847,12 +6449,13 @@ impl Renderer { if self.zoom_debug_texture.is_none() { let texture = self.device.create_texture( - ImageBufferKind::Texture2D, + TextureTarget::Default, ImageFormat::BGRA8, source_rect.size.width, source_rect.size.height, TextureFilter::Nearest, Some(RenderTargetInfo { has_depth: false }), + 1, ); self.zoom_debug_texture = Some(texture); @@ -4865,6 +6468,7 @@ impl Renderer { read_target.to_framebuffer_rect(source_rect), DrawTarget::from_texture( self.zoom_debug_texture.as_ref().unwrap(), + 0, false, ), texture_rect, @@ -4875,6 +6479,7 @@ impl Renderer { self.device.blit_render_target( ReadTarget::from_texture( self.zoom_debug_texture.as_ref().unwrap(), + 0, ), texture_rect, read_target, @@ -4883,7 +6488,7 @@ impl Renderer { ); } - fn draw_texture_cache_debug(&mut self, draw_target: &DrawTarget) { + fn draw_texture_cache_debug(&mut self, device_size: DeviceIntSize) { if !self.debug_flags.contains(DebugFlags::TEXTURE_CACHE_DBG) { return; } @@ -4908,7 +6513,7 @@ impl Renderer { &mut self.device, debug_renderer, textures, - draw_target, + device_size, if self.debug_flags.contains(DebugFlags::RENDER_TARGET_DBG) { 544 } else { 0 }, &select_color, ); @@ -4918,99 +6523,99 @@ impl Renderer { device: &mut Device, debug_renderer: &mut DebugRenderer, mut textures: Vec<&Texture>, - draw_target: &DrawTarget, + device_size: DeviceIntSize, bottom: i32, select_color: &dyn Fn(&Texture) -> [f32; 4], ) { let mut spacing = 16; let mut size = 512; - let device_size = draw_target.dimensions(); let fb_width = device_size.width; let fb_height = device_size.height; - let surface_origin_is_top_left = draw_target.surface_origin_is_top_left(); - - let num_textures = textures.iter().filter(|t| t.flags().contains(TextureFlags::IS_SHARED_TEXTURE_CACHE)).count() as i32; + let num_layers: i32 = textures.iter() + .map(|texture| texture.get_layer_count()) + .sum(); - if num_textures * (size + spacing) > fb_width { - let factor = fb_width as f32 / (num_textures * (size + spacing)) as f32; + if num_layers * (size + spacing) > fb_width { + let factor = fb_width as f32 / (num_layers * (size + spacing)) as f32; size = (size as f32 * factor) as i32; spacing = (spacing as f32 * factor) as i32; } - let text_height = 14; // Visually approximated. - let text_margin = 1; - let tag_height = text_height + text_margin * 2; - let tag_y = fb_height - (bottom + spacing + tag_height); - let image_y = tag_y - size; - - // Sort the display by size (in bytes), so that left-to-right is + // Sort the display by layer size (in bytes), so that left-to-right is // largest-to-smallest. // // Note that the vec here is in increasing order, because the elements // get drawn right-to-left. - textures.sort_by_key(|t| t.size_in_bytes()); + textures.sort_by_key(|t| t.layer_size_in_bytes()); let mut i = 0; for texture in textures.iter() { - if !texture.flags().contains(TextureFlags::IS_SHARED_TEXTURE_CACHE) { - continue; - } + let y = spacing + bottom; let dimensions = texture.get_dimensions(); let src_rect = FramebufferIntRect::new( FramebufferIntPoint::zero(), FramebufferIntSize::new(dimensions.width as i32, dimensions.height as i32), ); - let x = fb_width - (spacing + size) * (i as i32 + 1); + let layer_count = texture.get_layer_count() as usize; + for layer in 0 .. layer_count { + let x = fb_width - (spacing + size) * (i as i32 + 1); - // If we have more targets than fit on one row in screen, just early exit. - if x > fb_width { - return; - } - - // Draw the info tag. - let tag_rect = rect(x, tag_y, size, tag_height); - let tag_color = select_color(texture); - device.clear_target( - Some(tag_color), - None, - Some(draw_target.to_framebuffer_rect(tag_rect)), - ); + // If we have more targets than fit on one row in screen, just early exit. + if x > fb_width { + return; + } - // Draw the dimensions onto the tag. - let dim = texture.get_dimensions(); - let text_rect = tag_rect.inflate(-text_margin, -text_margin); - debug_renderer.add_text( - text_rect.min_x() as f32, - text_rect.max_y() as f32, // Top-relative. - &format!("{}x{}", dim.width, dim.height), - ColorU::new(0, 0, 0, 255), - Some(tag_rect.to_f32()) - ); + //TODO: properly use FramebufferPixel coordinates - // Blit the contents of the texture. - let dest_rect = draw_target.to_framebuffer_rect(rect(x, image_y, size, size)); - let read_target = ReadTarget::from_texture(texture); - - if surface_origin_is_top_left { - device.blit_render_target( - read_target, - src_rect, - *draw_target, - dest_rect, - TextureFilter::Linear, + // Draw the info tag. + let text_margin = 1; + let text_height = 14; // Visually aproximated. + let tag_height = text_height + text_margin * 2; + let tag_rect = rect(x, y, size, tag_height); + let tag_color = select_color(texture); + device.clear_target( + Some(tag_color), + None, + Some(tag_rect.cast_unit()), ); - } else { - // Invert y. - device.blit_render_target_invert_y( - read_target, - src_rect, - *draw_target, - dest_rect, + + // Draw the dimensions onto the tag. + let dim = texture.get_dimensions(); + let mut text_rect = tag_rect; + text_rect.origin.y = + fb_height - text_rect.origin.y - text_rect.size.height; // Top-relative. + debug_renderer.add_text( + (x + text_margin) as f32, + (fb_height - y - text_margin) as f32, // Top-relative. + &format!("{}x{}", dim.width, dim.height), + ColorU::new(0, 0, 0, 255), + Some(text_rect.to_f32()) ); + + // Blit the contents of the layer. We need to invert Y because + // we're blitting from a texture to the main framebuffer, which + // use different conventions. + let dest_rect = rect(x, y + tag_height, size, size); + if !device.surface_origin_is_top_left() { + device.blit_render_target_invert_y( + ReadTarget::from_texture(texture, layer), + src_rect, + DrawTarget::new_default(device_size, device.surface_origin_is_top_left()), + FramebufferIntRect::from_untyped(&dest_rect), + ); + } else { + device.blit_render_target( + ReadTarget::from_texture(texture, layer), + src_rect, + DrawTarget::new_default(device_size, device.surface_origin_is_top_left()), + FramebufferIntRect::from_untyped(&dest_rect), + TextureFilter::Linear, + ); + } + i += 1; } - i += 1; } } @@ -5062,7 +6667,8 @@ impl Renderer { }; let (x_off, y_off) = (30f32, 30f32); - let height = self.gpu_cache_texture.get_height() + let height = self.gpu_cache_texture.texture + .as_ref().map_or(0, |t| t.get_dimensions().height) .min(device_size.height - (y_off as i32) * 2) as usize; debug_renderer.add_quad( x_off, @@ -5098,6 +6704,22 @@ impl Renderer { pixels } + pub fn read_gpu_cache(&mut self) -> (DeviceIntSize, Vec<u8>) { + let texture = self.gpu_cache_texture.texture.as_ref().unwrap(); + let size = device_size_as_framebuffer_size(texture.get_dimensions()); + let mut texels = vec![0; (size.width * size.height * 16) as usize]; + self.device.begin_frame(); + self.device.bind_read_target(ReadTarget::from_texture(texture, 0)); + self.device.read_pixels_into( + size.into(), + ImageFormat::RGBAF32, + &mut texels, + ); + self.device.reset_read_target(); + self.device.end_frame(); + (texture.get_dimensions(), texels) + } + // De-initialize the Renderer safely, assuming the GL is still alive and active. pub fn deinit(mut self) { //Note: this is a fake frame, only needed because texture deletion is require to happen inside a frame @@ -5124,12 +6746,25 @@ impl Renderer { for textures in self.vertex_data_textures.drain(..) { textures.deinit(&mut self.device); } - self.texture_upload_pbo_pool.deinit(&mut self.device); - self.staging_texture_pool.delete_textures(&mut self.device); + self.device.delete_pbo(self.texture_cache_upload_pbo); self.texture_resolver.deinit(&mut self.device); - self.vaos.deinit(&mut self.device); + self.device.delete_vao(self.vaos.prim_vao); + self.device.delete_vao(self.vaos.resolve_vao); + self.device.delete_vao(self.vaos.clip_vao); + self.device.delete_vao(self.vaos.gradient_vao); + self.device.delete_vao(self.vaos.blur_vao); + self.device.delete_vao(self.vaos.line_vao); + self.device.delete_vao(self.vaos.border_vao); + self.device.delete_vao(self.vaos.scale_vao); + self.device.delete_vao(self.vaos.svg_filter_vao); + self.device.delete_vao(self.vaos.composite_vao); + self.device.delete_vao(self.vaos.clear_vao); + self.debug.deinit(&mut self.device); + for (_, target) in self.output_targets { + self.device.delete_fbo(target.fbo_id); + } if let Ok(shaders) = Rc::try_unwrap(self.shaders) { shaders.into_inner().deinit(&mut self.device); } @@ -5152,8 +6787,8 @@ impl Renderer { } fn size_of<T>(&self, ptr: *const T) -> usize { - let ops = self.size_of_ops.as_ref().unwrap(); - unsafe { ops.malloc_size_of(ptr) } + let op = self.size_of_ops.as_ref().unwrap().size_of_op; + unsafe { op(ptr as *const c_void) } } /// Collects a memory report. @@ -5161,9 +6796,15 @@ impl Renderer { let mut report = MemoryReport::default(); // GPU cache CPU memory. - self.gpu_cache_texture.report_memory_to(&mut report, self.size_of_ops.as_ref().unwrap()); + if let GpuCacheBus::PixelBuffer{ref rows, ..} = self.gpu_cache_texture.bus { + for row in rows.iter() { + report.gpu_cache_cpu_mirror += self.size_of(&*row.cpu_blocks as *const _); + } + } - self.staging_texture_pool.report_memory_to(&mut report, self.size_of_ops.as_ref().unwrap()); + // GPU cache GPU memory. + report.gpu_cache_textures += + self.gpu_cache_texture.texture.as_ref().map_or(0, |t| t.size_in_bytes()); // Render task CPU memory. for (_id, doc) in &self.active_documents { @@ -5179,11 +6820,8 @@ impl Renderer { // Texture cache and render target GPU memory. report += self.texture_resolver.report_memory(); - // Texture upload PBO memory. - report += self.texture_upload_pbo_pool.report_memory(); - // Textures held internally within the device layer. - report += self.device.report_memory(self.size_of_ops.as_ref().unwrap()); + report += self.device.report_memory(); report } @@ -5234,16 +6872,24 @@ impl Renderer { } } - /// Clears the texture with a given color. + /// Clears all the layers of a texture with a given color. fn clear_texture(&mut self, texture: &Texture, color: [f32; 4]) { - self.device.bind_draw_target(DrawTarget::from_texture( - &texture, - false, - )); - self.device.clear_target(Some(color), None, None); + for i in 0..texture.get_layer_count() { + self.device.bind_draw_target(DrawTarget::from_texture( + &texture, + i as usize, + false, + )); + self.device.clear_target(Some(color), None, None); + } } } +pub trait ThreadListener { + fn thread_started(&self, thread_name: &str); + fn thread_stopped(&self, thread_name: &str); +} + /// Allows callers to hook in at certain points of the async scene build. These /// functions are all called from the scene builder thread. pub trait SceneBuilderHooks { @@ -5287,7 +6933,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, generated_frame_id: Option<u64>) -> Vec<FrameMsg>; + fn sample(&self, document_id: DocumentId, + doc: &FastHashMap<PipelineId, Epoch>) -> Vec<FrameMsg>; /// This is called exactly once, when the render backend thread is about to /// terminate. fn deregister(&self); @@ -5323,15 +6970,13 @@ pub struct RendererOptions { pub force_subpixel_aa: bool, pub clear_color: Option<ColorF>, pub enable_clear_scissor: bool, - pub max_internal_texture_size: Option<i32>, - pub image_tiling_threshold: i32, + pub max_texture_size: Option<i32>, + pub max_glyph_cache_size: Option<usize>, pub upload_method: UploadMethod, - /// The default size in bytes for PBOs used to upload texture data. - pub upload_pbo_default_size: usize, pub workers: Option<Arc<ThreadPool>>, pub enable_multithreading: bool, pub blob_image_handler: Option<Box<dyn BlobImageHandler>>, - pub crash_annotator: Option<Box<dyn CrashAnnotator>>, + pub thread_listener: Option<Box<dyn ThreadListener + Send + Sync>>, pub size_of_op: Option<VoidPtrToSizeFn>, pub enclosing_size_of_op: Option<VoidPtrToSizeFn>, pub cached_programs: Option<Rc<ProgramCache>>, @@ -5342,6 +6987,7 @@ pub struct RendererOptions { pub chase_primitive: ChasePrimitive, pub support_low_priority_transactions: bool, pub namespace_alloc_by_client: bool, + pub enable_picture_caching: bool, pub testing: bool, /// Set to true if this GPU supports hardware fast clears as a performance /// optimization. Likely requires benchmarking on various GPUs to see if @@ -5350,6 +6996,11 @@ pub struct RendererOptions { pub gpu_supports_fast_clears: bool, pub allow_dual_source_blending: bool, pub allow_advanced_blend_equation: bool, + /// If true, allow WR to use pixel local storage if the device supports it. + /// For now, this defaults to false since the code is still experimental + /// and not complete. This option will probably be removed once support is + /// complete, and WR can implicitly choose whether to make use of PLS. + pub allow_pixel_local_storage_support: bool, /// If true, allow textures to be initialized with glTexStorage. /// This affects VRAM consumption and data upload paths. pub allow_texture_storage_support: bool, @@ -5357,11 +7008,16 @@ pub struct RendererOptions { /// one expected by the driver, pretending the format is matching, and /// swizzling the components on all the shader sampling. pub allow_texture_swizzling: bool, + /// Number of batches to look back in history for adding the current + /// transparent instance into. + pub batch_lookback_count: usize, /// Use `ps_clear` shader with batched quad rendering to clear the rects /// in texture cache and picture cache tasks. /// This helps to work around some Intel drivers /// that incorrectly synchronize clears to following draws. pub clear_caches_with_quads: bool, + /// Start the debug server for this renderer. + pub start_debug_server: bool, /// Output the source of the shader with the given name. pub dump_shader_source: Option<String>, pub surface_origin_is_top_left: bool, @@ -5371,26 +7027,14 @@ pub struct RendererOptions { /// If true, panic whenever a GL error occurs. This has a significant /// performance impact, so only use when debugging specific problems! pub panic_on_gl_error: bool, - pub picture_tile_size: Option<DeviceIntSize>, - pub texture_cache_config: TextureCacheConfig, - /// If true, we'll use instanced vertex attributes. Each instace is a quad. - /// If false, we'll duplicate the instance attributes per vertex and issue - /// regular indexed draws instead. - pub enable_instancing: bool, -} - -impl RendererOptions { - /// Number of batches to look back in history for adding the current - /// transparent instance into. - const BATCH_LOOKBACK_COUNT: usize = 10; - - /// Since we are re-initializing the instance buffers on every draw call, - /// the driver has to internally manage PBOs in flight. - /// It's typically done by bucketing up to a specific limit, and then - /// just individually managing the largest buffers. - /// Having a limit here allows the drivers to more easily manage - /// the PBOs for us. - const MAX_INSTANCE_BUFFER_SIZE: usize = 0x20000; // actual threshold in macOS GL drivers + /// If the total bytes allocated in shared / standalone cache is less + /// than this, then allow the cache to grow without forcing an eviction. + pub texture_cache_eviction_threshold_bytes: usize, + /// The maximum number of items that will be evicted per frame. This limit helps avoid jank + /// on frames where we want to evict a large number of items. Instead, we'd prefer to drop + /// the items incrementally over a number of frames, even if that means the total allocated + /// size of the cache is above the desired threshold for a small number of frames. + pub texture_cache_max_evictions_per_frame: usize, } impl Default for RendererOptions { @@ -5408,16 +7052,15 @@ impl Default for RendererOptions { force_subpixel_aa: false, clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)), enable_clear_scissor: true, - max_internal_texture_size: None, - image_tiling_threshold: 4096, + max_texture_size: None, + max_glyph_cache_size: None, // This is best as `Immediate` on Angle, or `Pixelbuffer(Dynamic)` on GL, // but we are unable to make this decision here, so picking the reasonable medium. - upload_method: UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT), - upload_pbo_default_size: 512 * 512 * 4, + upload_method: UploadMethod::PixelBuffer(VertexUsageHint::Stream), workers: None, enable_multithreading: true, blob_image_handler: None, - crash_annotator: None, + thread_listener: None, size_of_op: None, enclosing_size_of_op: None, renderer_id: None, @@ -5427,53 +7070,62 @@ impl Default for RendererOptions { chase_primitive: ChasePrimitive::Nothing, support_low_priority_transactions: false, namespace_alloc_by_client: false, + enable_picture_caching: false, testing: false, gpu_supports_fast_clears: false, allow_dual_source_blending: true, allow_advanced_blend_equation: false, + allow_pixel_local_storage_support: false, allow_texture_storage_support: true, allow_texture_swizzling: true, + batch_lookback_count: DEFAULT_BATCH_LOOKBACK_COUNT, clear_caches_with_quads: true, + // For backwards compatibility we set this to true by default, so + // that if the debugger feature is enabled, the debug server will + // be started automatically. Users can explicitly disable this as + // needed. + start_debug_server: true, dump_shader_source: None, surface_origin_is_top_left: false, compositor_config: CompositorConfig::default(), enable_gpu_markers: true, panic_on_gl_error: false, - picture_tile_size: None, - texture_cache_config: TextureCacheConfig::DEFAULT, - // Disabling instancing means more vertex data to upload and potentially - // process by the vertex shaders. - enable_instancing: true, + texture_cache_eviction_threshold_bytes: 64 * 1024 * 1024, + texture_cache_max_evictions_per_frame: 32, } } } -/// The cumulative times spent in each painting phase to generate this frame. -#[derive(Debug, Default)] -pub struct FullFrameStats { - pub full_display_list: bool, - pub gecko_display_list_time: f64, - pub wr_display_list_time: f64, - pub scene_build_time: f64, - pub frame_build_time: f64, +pub trait DebugServer { + fn send(&mut self, _message: String); } -impl FullFrameStats { - pub fn merge(&self, other: &FullFrameStats) -> Self { - Self { - full_display_list: self.full_display_list || other.full_display_list, - gecko_display_list_time: self.gecko_display_list_time + other.gecko_display_list_time, - wr_display_list_time: self.wr_display_list_time + other.wr_display_list_time, - scene_build_time: self.scene_build_time + other.scene_build_time, - frame_build_time: self.frame_build_time + other.frame_build_time - } +struct NoopDebugServer; + +impl NoopDebugServer { + fn new(_: Sender<ApiMsg>) -> Self { + NoopDebugServer } +} + +impl DebugServer for NoopDebugServer { + fn send(&mut self, _: String) {} +} - pub fn total(&self) -> f64 { - self.gecko_display_list_time + self.wr_display_list_time + self.scene_build_time + self.frame_build_time +#[cfg(feature = "debugger")] +fn new_debug_server(enable: bool, api_tx: Sender<ApiMsg>) -> Box<dyn DebugServer> { + if enable { + Box::new(debug_server::DebugServerImpl::new(api_tx)) + } else { + Box::new(NoopDebugServer::new(api_tx)) } } +#[cfg(not(feature = "debugger"))] +fn new_debug_server(_enable: bool, api_tx: Sender<ApiMsg>) -> Box<dyn DebugServer> { + Box::new(NoopDebugServer::new(api_tx)) +} + /// Some basic statistics about the rendered scene, used in Gecko, as /// well as in wrench reftests to ensure that tests are batching and/or /// allocating on render targets as we expect them to. @@ -5483,26 +7135,9 @@ pub struct RendererStats { pub total_draw_calls: usize, pub alpha_target_count: usize, pub color_target_count: usize, - pub texture_upload_mb: f64, - pub resource_upload_time: f64, - pub gpu_cache_upload_time: f64, - pub gecko_display_list_time: f64, - pub wr_display_list_time: f64, - pub scene_build_time: f64, - pub frame_build_time: f64, - pub full_display_list: bool, - pub full_paint: bool, -} - -impl RendererStats { - pub fn merge(&mut self, stats: &FullFrameStats) { - self.gecko_display_list_time = stats.gecko_display_list_time; - self.wr_display_list_time = stats.wr_display_list_time; - self.scene_build_time = stats.scene_build_time; - self.frame_build_time = stats.frame_build_time; - self.full_display_list = stats.full_display_list; - self.full_paint = true; - } + pub texture_upload_kb: usize, + pub resource_upload_time: u64, + pub gpu_cache_upload_time: u64, } /// Return type from render(), which contains some repr(C) statistics as well as @@ -5512,6 +7147,11 @@ pub struct RenderResults { /// Statistics about the frame that was rendered. pub stats: RendererStats, + /// A list of dirty world rects. This is only currently + /// useful to test infrastructure. + /// TODO(gw): This needs to be refactored / removed. + pub recorded_dirty_regions: Vec<RecordedDirtyRegion>, + /// A list of the device dirty rects that were updated /// this frame. /// TODO(gw): This is an initial interface, likely to change in future. @@ -5521,10 +7161,6 @@ pub struct RenderResults { /// OS compositor support where the dirty rects apply to a /// specific picture cache slice / OS compositor surface). pub dirty_rects: Vec<DeviceIntRect>, - - /// Information about the state of picture cache tiles. This is only - /// allocated and stored if config.testing is true (such as wrench) - pub picture_cache_debug: PictureCacheDebugInfo, } #[cfg(any(feature = "capture", feature = "replay"))] @@ -5532,7 +7168,7 @@ pub struct RenderResults { #[cfg_attr(feature = "replay", derive(Deserialize))] struct PlainTexture { data: String, - size: DeviceIntSize, + size: (DeviceIntSize, i32), format: ImageFormat, filter: TextureFilter, has_depth: bool, @@ -5582,6 +7218,19 @@ impl ExternalImageHandler for DummyExternalImageHandler { fn unlock(&mut self, _key: ExternalImageId, _channel_index: u8) {} } +#[cfg(feature = "replay")] +struct VoidHandler; + +#[cfg(feature = "replay")] +impl OutputImageHandler for VoidHandler { + fn lock(&mut self, _: PipelineId) -> Option<(u32, FramebufferIntSize)> { + None + } + fn unlock(&mut self, _: PipelineId) { + unreachable!() + } +} + #[derive(Default)] pub struct PipelineInfo { pub epochs: FastHashMap<(PipelineId, DocumentId), Epoch>, @@ -5604,40 +7253,42 @@ impl Renderer { let mut file = fs::File::create(root.join(&short_path)) .expect(&format!("Unable to create {}", short_path)); - let bytes_per_texture = (rect_size.width * rect_size.height * bytes_per_pixel) as usize; - let mut data = vec![0; bytes_per_texture]; + let bytes_per_layer = (rect_size.width * rect_size.height * bytes_per_pixel) as usize; + let mut data = vec![0; bytes_per_layer]; //TODO: instead of reading from an FBO with `read_pixels*`, we could // read from textures directly with `get_tex_image*`. - let rect = device_size_as_framebuffer_size(rect_size).into(); + for layer_id in 0 .. texture.get_layer_count() { + let rect = device_size_as_framebuffer_size(rect_size).into(); - device.attach_read_texture(texture); - #[cfg(feature = "png")] - { - let mut png_data; - let (data_ref, format) = match texture.get_format() { - ImageFormat::RGBAF32 => { - png_data = vec![0; (rect_size.width * rect_size.height * 4) as usize]; - device.read_pixels_into(rect, ImageFormat::RGBA8, &mut png_data); - (&png_data, ImageFormat::RGBA8) - } - fm => (&data, fm), - }; - CaptureConfig::save_png( - root.join(format!("textures/{}-{}.png", name, 0)), - rect_size, format, - None, - data_ref, - ); + device.attach_read_texture(texture, layer_id); + #[cfg(feature = "png")] + { + let mut png_data; + let (data_ref, format) = match texture.get_format() { + ImageFormat::RGBAF32 => { + png_data = vec![0; (rect_size.width * rect_size.height * 4) as usize]; + device.read_pixels_into(rect, ImageFormat::RGBA8, &mut png_data); + (&png_data, ImageFormat::RGBA8) + } + fm => (&data, fm), + }; + CaptureConfig::save_png( + root.join(format!("textures/{}-{}.png", name, layer_id)), + rect_size, format, + None, + data_ref, + ); + } + device.read_pixels_into(rect, read_format, &mut data); + file.write_all(&data) + .unwrap(); } - device.read_pixels_into(rect, read_format, &mut data); - file.write_all(&data) - .unwrap(); PlainTexture { data: short_path, - size: rect_size, + size: (rect_size, texture.get_layer_count()), format: texture.get_format(), filter: texture.get_filter(), has_depth: texture.supports_depth(), @@ -5646,7 +7297,7 @@ impl Renderer { #[cfg(feature = "replay")] fn load_texture( - target: ImageBufferKind, + target: TextureTarget, plain: &PlainTexture, rt_info: Option<RenderTargetInfo>, root: &PathBuf, @@ -5665,10 +7316,11 @@ impl Renderer { let texture = device.create_texture( target, plain.format, - plain.size.width, - plain.size.height, + plain.size.0.width, + plain.size.0.height, plain.filter, rt_info, + plain.size.1, ); device.upload_texture_immediate(&texture, &texels); @@ -5683,14 +7335,13 @@ impl Renderer { ) { use std::fs; use std::io::Write; - use api::ExternalImageData; - use crate::render_api::CaptureBits; + use api::{CaptureBits, ExternalImageData}; let root = config.resource_root(); self.device.begin_frame(); - let _gm = self.gpu_profiler.start_marker("read GPU data"); - self.device.bind_read_target_impl(self.read_fbo, DeviceIntPoint::zero()); + let _gm = self.gpu_profile.start_marker("read GPU data"); + self.device.bind_read_target_impl(self.read_fbo); if config.bits.contains(CaptureBits::EXTERNAL_RESOURCES) && !deferred_images.is_empty() { info!("saving external images"); @@ -5729,7 +7380,8 @@ impl Renderer { ExternalImageType::Buffer => unreachable!(), }; info!("\t\tnative texture of target {:?}", target); - self.device.attach_read_texture_external(gl_id, target); + let layer_index = 0; //TODO: what about layered textures? + self.device.attach_read_texture_external(gl_id, target, layer_index); let data = self.device.read_pixels(&def.descriptor); let short_path = format!("externals/t{}.raw", tex_id); (Some(data), e.insert(short_path).clone()) @@ -5782,7 +7434,7 @@ impl Renderer { let mut plain_self = PlainRenderer { device_size: self.device_size, gpu_cache: Self::save_texture( - self.gpu_cache_texture.get_texture(), + &self.gpu_cache_texture.texture.as_ref().unwrap(), "gpu", &root, &mut self.device, ), gpu_cache_frame_id: self.gpu_cache_frame_id, @@ -5802,15 +7454,6 @@ impl Renderer { self.device.reset_read_target(); self.device.end_frame(); - - let mut stats_file = fs::File::create(config.root.join("profiler-stats.txt")) - .expect(&format!("Unable to create profiler-stats.txt")); - if self.debug_flags.intersects(DebugFlags::PROFILER_DBG | DebugFlags::PROFILER_CAPTURE) { - self.profiler.dump_stats(&mut stats_file).unwrap(); - } else { - writeln!(stats_file, "Turn on PROFILER_DBG or PROFILER_CAPTURE to get stats here!").unwrap(); - } - info!("done."); } @@ -5820,7 +7463,9 @@ impl Renderer { config: CaptureConfig, plain_externals: Vec<PlainExternalImage>, ) { - use std::{fs::File, io::Read}; + use std::fs::File; + use std::io::Read; + use std::slice; info!("loading external buffer-backed images"); assert!(self.texture_resolver.external_images.is_empty()); @@ -5867,11 +7512,13 @@ impl Renderer { let tid = match native_map.entry(plain_ext.data) { Entry::Occupied(e) => e.get().clone(), Entry::Vacant(e) => { + //TODO: provide a way to query both the layer count and the filter from external images + let (layer_count, filter) = (1, TextureFilter::Linear); let plain_tex = PlainTexture { data: e.key().clone(), - size: descriptor.size, + size: (descriptor.size, layer_count), format: descriptor.format, - filter: TextureFilter::Linear, + filter, has_depth: false, }; let t = Self::load_texture( @@ -5892,21 +7539,18 @@ impl Renderer { } } - self.device.begin_frame(); - self.gpu_cache_texture.remove_texture(&mut self.device); - if let Some(renderer) = config.deserialize_for_resource::<PlainRenderer, _>("renderer") { info!("loading cached textures"); self.device_size = renderer.device_size; + self.device.begin_frame(); for (_id, texture) in self.texture_resolver.texture_cache_map.drain() { self.device.delete_texture(texture); } for (id, texture) in renderer.textures { info!("\t{}", texture.data); - let target = ImageBufferKind::Texture2D; let t = Self::load_texture( - target, + TextureTarget::Array, &texture, Some(RenderTargetInfo { has_depth: texture.has_depth }), &root, @@ -5916,29 +7560,76 @@ impl Renderer { } info!("loading gpu cache"); + if let Some(t) = self.gpu_cache_texture.texture.take() { + self.device.delete_texture(t); + } let (t, gpu_cache_data) = Self::load_texture( - ImageBufferKind::Texture2D, + TextureTarget::Default, &renderer.gpu_cache, Some(RenderTargetInfo { has_depth: false }), &root, &mut self.device, ); - self.gpu_cache_texture.load_from_data(t, gpu_cache_data); + self.gpu_cache_texture.texture = Some(t); + match self.gpu_cache_texture.bus { + GpuCacheBus::PixelBuffer { ref mut rows, .. } => { + let dim = self.gpu_cache_texture.texture.as_ref().unwrap().get_dimensions(); + let blocks = unsafe { + slice::from_raw_parts( + gpu_cache_data.as_ptr() as *const GpuBlockData, + gpu_cache_data.len() / mem::size_of::<GpuBlockData>(), + ) + }; + // fill up the CPU cache from the contents we just loaded + rows.clear(); + rows.extend((0 .. dim.height).map(|_| CacheRow::new())); + let chunks = blocks.chunks(MAX_VERTEX_TEXTURE_WIDTH); + debug_assert_eq!(chunks.len(), rows.len()); + for (row, chunk) in rows.iter_mut().zip(chunks) { + row.cpu_blocks.copy_from_slice(chunk); + } + } + GpuCacheBus::Scatter { .. } => {} + } self.gpu_cache_frame_id = renderer.gpu_cache_frame_id; + + self.device.end_frame(); } else { info!("loading cached textures"); self.device.begin_frame(); for (_id, texture) in self.texture_resolver.texture_cache_map.drain() { self.device.delete_texture(texture); } + + info!("loading gpu cache"); + if let Some(t) = self.gpu_cache_texture.texture.take() { + self.device.delete_texture(t); + } + self.device.end_frame(); } - self.device.end_frame(); + self.output_image_handler = Some(Box::new(VoidHandler) as Box<_>); self.external_image_handler = Some(Box::new(image_handler) as Box<_>); info!("done."); } } +fn get_vao(vertex_array_kind: VertexArrayKind, vaos: &RendererVAOs) -> &VAO { + match vertex_array_kind { + VertexArrayKind::Primitive => &vaos.prim_vao, + VertexArrayKind::Clip => &vaos.clip_vao, + VertexArrayKind::Blur => &vaos.blur_vao, + VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(), + VertexArrayKind::Border => &vaos.border_vao, + VertexArrayKind::Scale => &vaos.scale_vao, + VertexArrayKind::LineDecoration => &vaos.line_vao, + VertexArrayKind::Gradient => &vaos.gradient_vao, + VertexArrayKind::Resolve => &vaos.resolve_vao, + VertexArrayKind::SvgFilter => &vaos.svg_filter_vao, + VertexArrayKind::Composite => &vaos.composite_vao, + VertexArrayKind::Clear => &vaos.clear_vao, + } +} #[derive(Clone, Copy, PartialEq)] enum FramebufferKind { Main, @@ -5950,6 +7641,8 @@ fn should_skip_batch(kind: &BatchKind, flags: DebugFlags) -> bool { BatchKind::TextRun(_) => { flags.contains(DebugFlags::DISABLE_TEXT_PRIMS) } + BatchKind::Brush(BrushBatchKind::ConicGradient) | + BatchKind::Brush(BrushBatchKind::RadialGradient) | BatchKind::Brush(BrushBatchKind::LinearGradient) => { flags.contains(DebugFlags::DISABLE_GRADIENT_PRIMS) } @@ -5962,7 +7655,6 @@ impl CompositeState { /// cache tiles to the OS compositor fn composite_native( &self, - dirty_rects: &[DeviceIntRect], compositor: &mut dyn Compositor, ) { // Add each surface to the visual tree. z-order is implicit based on @@ -5971,44 +7663,9 @@ impl CompositeState { for surface in &self.descriptor.surfaces { compositor.add_surface( surface.surface_id.expect("bug: no native surface allocated"), - surface.transform, + surface.offset.to_i32(), surface.clip_rect.to_i32(), - surface.image_rendering, ); } - compositor.start_compositing(dirty_rects, &[]); - } -} - -mod tests { - #[test] - fn test_buffer_damage_tracker() { - use super::BufferDamageTracker; - use api::units::{DevicePoint, DeviceRect, DeviceSize}; - - let mut tracker = BufferDamageTracker::default(); - assert_eq!(tracker.get_damage_rect(0), None); - assert_eq!(tracker.get_damage_rect(1), Some(DeviceRect::zero())); - assert_eq!(tracker.get_damage_rect(2), Some(DeviceRect::zero())); - assert_eq!(tracker.get_damage_rect(3), Some(DeviceRect::zero())); - assert_eq!(tracker.get_damage_rect(4), None); - - let damage1 = DeviceRect::new(DevicePoint::new(10.0, 10.0), DeviceSize::new(10.0, 10.0)); - let damage2 = DeviceRect::new(DevicePoint::new(20.0, 20.0), DeviceSize::new(10.0, 10.0)); - let combined = damage1.union(&damage2); - - tracker.push_dirty_rect(&damage1); - assert_eq!(tracker.get_damage_rect(0), None); - assert_eq!(tracker.get_damage_rect(1), Some(DeviceRect::zero())); - assert_eq!(tracker.get_damage_rect(2), Some(damage1)); - assert_eq!(tracker.get_damage_rect(3), Some(damage1)); - assert_eq!(tracker.get_damage_rect(4), None); - - tracker.push_dirty_rect(&damage2); - assert_eq!(tracker.get_damage_rect(0), None); - assert_eq!(tracker.get_damage_rect(1), Some(DeviceRect::zero())); - assert_eq!(tracker.get_damage_rect(2), Some(damage2)); - assert_eq!(tracker.get_damage_rect(3), Some(combined)); - assert_eq!(tracker.get_damage_rect(4), None); } } diff --git a/third_party/webrender/webrender/src/renderer/gpu_cache.rs b/third_party/webrender/webrender/src/renderer/gpu_cache.rs deleted file mode 100644 index e7f16e91c5c..00000000000 --- a/third_party/webrender/webrender/src/renderer/gpu_cache.rs +++ /dev/null @@ -1,525 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use std::{cmp, mem}; -use api::units::*; -use malloc_size_of::MallocSizeOfOps; -use crate::{ - device::{CustomVAO, Device, DrawTarget, Program, ReadTarget, Texture, TextureFilter, UploadPBOPool, VBO}, - gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList}, - internal_types::{RenderTargetInfo, Swizzle}, - prim_store::DeferredResolve, - profiler, - render_api::MemoryReport, - render_backend::FrameId, -}; - -/// Enabling this toggle would force the GPU cache scattered texture to -/// be resized every frame, which enables GPU debuggers to see if this -/// is performed correctly. -const GPU_CACHE_RESIZE_TEST: bool = false; - -/// Tracks the state of each row in the GPU cache texture. -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; super::MAX_VERTEX_TEXTURE_WIDTH]>, - /// 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; super::MAX_VERTEX_TEXTURE_WIDTH]), - min_dirty: super::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 = super::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 -/// get synchronized. -enum GpuCacheBus { - /// PBO-based updates, currently operate on a row granularity. - /// Therefore, are subject to fragmentation issues. - PixelBuffer { - /// Per-row data. - rows: Vec<CacheRow>, - }, - /// Shader-based scattering updates. Currently rendered by a set - /// of points into the GPU texture, each carrying a `GpuBlockData`. - Scatter { - /// Special program to run the scattered update. - program: Program, - /// VAO containing the source vertex buffers. - vao: CustomVAO, - /// VBO for positional data, supplied as normalized `u16`. - buf_position: VBO<[u16; 2]>, - /// VBO for gpu block data. - buf_value: VBO<GpuBlockData>, - /// Currently stored block count. - count: usize, - }, -} - -/// The device-specific representation of the cache texture in gpu_cache.rs -pub struct GpuCacheTexture { - texture: Option<Texture>, - bus: GpuCacheBus, -} - -impl GpuCacheTexture { - /// Ensures that we have an appropriately-sized texture. - fn ensure_texture(&mut self, device: &mut Device, height: i32) { - // If we already have a texture that works, we're done. - if self.texture.as_ref().map_or(false, |t| t.get_dimensions().height >= height) { - if GPU_CACHE_RESIZE_TEST { - // Special debug mode - resize the texture even though it's fine. - } else { - return; - } - } - - // Take the old texture, if any. - let blit_source = self.texture.take(); - - // Create the new texture. - assert!(height >= 2, "Height is too small for ANGLE"); - let new_size = DeviceIntSize::new(super::MAX_VERTEX_TEXTURE_WIDTH as _, height); - // GpuCacheBus::Scatter always requires the texture to be a render target. For - // GpuCacheBus::PixelBuffer, we only create the texture with a render target if - // RGBAF32 render targets are actually supported, and only if glCopyImageSubData - // is not. glCopyImageSubData does not require a render target to copy the texture - // data, and if neither RGBAF32 render targets nor glCopyImageSubData is supported, - // we simply re-upload the entire contents rather than copying upon resize. - let supports_copy_image_sub_data = device.get_capabilities().supports_copy_image_sub_data; - let supports_color_buffer_float = device.get_capabilities().supports_color_buffer_float; - let rt_info = if matches!(self.bus, GpuCacheBus::PixelBuffer { .. }) - && (supports_copy_image_sub_data || !supports_color_buffer_float) - { - None - } else { - Some(RenderTargetInfo { has_depth: false }) - }; - let mut texture = device.create_texture( - api::ImageBufferKind::Texture2D, - api::ImageFormat::RGBAF32, - new_size.width, - new_size.height, - TextureFilter::Nearest, - rt_info, - ); - - // Copy the contents of the previous texture, if applicable. - if let Some(blit_source) = blit_source { - if !supports_copy_image_sub_data && !supports_color_buffer_float { - // Cannot copy texture, so must re-upload everything. - match self.bus { - GpuCacheBus::PixelBuffer { ref mut rows } => { - for row in rows { - row.add_dirty(0, super::MAX_VERTEX_TEXTURE_WIDTH); - } - } - GpuCacheBus::Scatter { .. } => { - panic!("Texture must be copyable to use scatter GPU cache bus method"); - } - } - } else { - device.copy_entire_texture(&mut texture, &blit_source); - } - device.delete_texture(blit_source); - } - - self.texture = Some(texture); - } - - pub fn new(device: &mut Device, use_scatter: bool) -> Result<Self, super::RendererError> { - use super::desc::GPU_CACHE_UPDATE; - - let bus = if use_scatter { - assert!( - device.get_capabilities().supports_color_buffer_float, - "GpuCache scatter method requires EXT_color_buffer_float", - ); - let program = device.create_program_linked( - "gpu_cache_update", - &[], - &GPU_CACHE_UPDATE, - )?; - let buf_position = device.create_vbo(); - let buf_value = device.create_vbo(); - //Note: the vertex attributes have to be supplied in the same order - // as for program creation, but each assigned to a different stream. - let vao = device.create_custom_vao(&[ - buf_position.stream_with(&GPU_CACHE_UPDATE.vertex_attributes[0..1]), - buf_value .stream_with(&GPU_CACHE_UPDATE.vertex_attributes[1..2]), - ]); - GpuCacheBus::Scatter { - program, - vao, - buf_position, - buf_value, - count: 0, - } - } else { - GpuCacheBus::PixelBuffer { - rows: Vec::new(), - } - }; - - Ok(GpuCacheTexture { - texture: None, - bus, - }) - } - - pub fn deinit(mut self, device: &mut Device) { - if let Some(t) = self.texture.take() { - device.delete_texture(t); - } - if let GpuCacheBus::Scatter { program, vao, buf_position, buf_value, .. } = self.bus { - device.delete_program(program); - device.delete_custom_vao(vao); - device.delete_vbo(buf_position); - device.delete_vbo(buf_value); - } - } - - pub fn get_height(&self) -> i32 { - self.texture.as_ref().map_or(0, |t| t.get_dimensions().height) - } - - #[cfg(feature = "capture")] - pub fn get_texture(&self) -> &Texture { - self.texture.as_ref().unwrap() - } - - fn prepare_for_updates( - &mut self, - device: &mut Device, - total_block_count: usize, - max_height: i32, - ) { - self.ensure_texture(device, max_height); - match self.bus { - GpuCacheBus::PixelBuffer { .. } => {}, - GpuCacheBus::Scatter { - ref mut buf_position, - ref mut buf_value, - ref mut count, - .. - } => { - *count = 0; - if total_block_count > buf_value.allocated_count() { - device.allocate_vbo(buf_position, total_block_count, super::ONE_TIME_USAGE_HINT); - device.allocate_vbo(buf_value, total_block_count, super::ONE_TIME_USAGE_HINT); - } - } - } - } - - pub fn invalidate(&mut self) { - match self.bus { - GpuCacheBus::PixelBuffer { ref mut rows, .. } => { - info!("Invalidating GPU caches"); - for row in rows { - row.add_dirty(0, super::MAX_VERTEX_TEXTURE_WIDTH); - } - } - GpuCacheBus::Scatter { .. } => { - warn!("Unable to invalidate scattered GPU cache"); - } - } - } - - fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) { - match self.bus { - GpuCacheBus::PixelBuffer { ref mut rows, .. } => { - for update in &updates.updates { - match *update { - GpuCacheUpdate::Copy { - block_index, - block_count, - address, - } => { - let row = address.v as usize; - - // Ensure that the CPU-side shadow copy of the GPU cache data has enough - // rows to apply this patch. - while rows.len() <= row { - // Add a new row. - rows.push(CacheRow::new()); - } - - // 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); - } - } - } - } - GpuCacheBus::Scatter { - ref buf_position, - ref buf_value, - ref mut count, - .. - } => { - //TODO: re-use this heap allocation - // Unused positions will be left as 0xFFFF, which translates to - // (1.0, 1.0) in the vertex output position and gets culled out - let mut position_data = vec![[!0u16; 2]; updates.blocks.len()]; - let size = self.texture.as_ref().unwrap().get_dimensions().to_usize(); - - for update in &updates.updates { - match *update { - GpuCacheUpdate::Copy { - block_index, - block_count, - address, - } => { - // Convert the absolute texel position into normalized - let y = ((2*address.v as usize + 1) << 15) / size.height; - for i in 0 .. block_count { - let x = ((2*address.u as usize + 2*i + 1) << 15) / size.width; - position_data[block_index + i] = [x as _, y as _]; - } - } - } - } - - device.fill_vbo(buf_value, &updates.blocks, *count); - device.fill_vbo(buf_position, &position_data, *count); - *count += position_data.len(); - } - } - } - - fn flush(&mut self, device: &mut Device, pbo_pool: &mut UploadPBOPool) -> usize { - let texture = self.texture.as_ref().unwrap(); - match self.bus { - GpuCacheBus::PixelBuffer { ref mut rows } => { - let rows_dirty = rows - .iter() - .filter(|row| row.is_dirty()) - .count(); - if rows_dirty == 0 { - return 0 - } - - let mut uploader = device.upload_texture(pbo_pool); - - for (row_index, row) in rows.iter_mut().enumerate() { - if !row.is_dirty() { - continue; - } - - let blocks = row.dirty_blocks(); - let rect = DeviceIntRect::new( - DeviceIntPoint::new(row.min_dirty as i32, row_index as i32), - DeviceIntSize::new(blocks.len() as i32, 1), - ); - - uploader.upload(device, texture, rect, None, None, blocks.as_ptr(), blocks.len()); - - row.clear_dirty(); - } - - uploader.flush(device); - - rows_dirty - } - GpuCacheBus::Scatter { ref program, ref vao, count, .. } => { - device.disable_depth(); - device.set_blend(false); - device.bind_program(program); - device.bind_custom_vao(vao); - device.bind_draw_target( - DrawTarget::from_texture( - texture, - false, - ), - ); - device.draw_nonindexed_points(0, count as _); - 0 - } - } - } - - #[cfg(feature = "replay")] - pub fn remove_texture(&mut self, device: &mut Device) { - if let Some(t) = self.texture.take() { - device.delete_texture(t); - } - } - - #[cfg(feature = "replay")] - pub fn load_from_data(&mut self, texture: Texture, data: Vec<u8>) { - assert!(self.texture.is_none()); - match self.bus { - GpuCacheBus::PixelBuffer { ref mut rows, .. } => { - let dim = texture.get_dimensions(); - let blocks = unsafe { - std::slice::from_raw_parts( - data.as_ptr() as *const GpuBlockData, - data.len() / mem::size_of::<GpuBlockData>(), - ) - }; - // fill up the CPU cache from the contents we just loaded - rows.clear(); - rows.extend((0 .. dim.height).map(|_| CacheRow::new())); - let chunks = blocks.chunks(super::MAX_VERTEX_TEXTURE_WIDTH); - debug_assert_eq!(chunks.len(), rows.len()); - for (row, chunk) in rows.iter_mut().zip(chunks) { - row.cpu_blocks.copy_from_slice(chunk); - } - } - GpuCacheBus::Scatter { .. } => {} - } - self.texture = Some(texture); - } - - pub fn report_memory_to(&self, report: &mut MemoryReport, size_op_funs: &MallocSizeOfOps) { - if let GpuCacheBus::PixelBuffer{ref rows, ..} = self.bus { - for row in rows.iter() { - report.gpu_cache_cpu_mirror += unsafe { (size_op_funs.size_of_op)(row.cpu_blocks.as_ptr() as *const _) }; - } - } - - // GPU cache GPU memory. - report.gpu_cache_textures += - self.texture.as_ref().map_or(0, |t| t.size_in_bytes()); - } -} - -impl super::Renderer { - pub fn update_gpu_cache(&mut self) { - let _gm = self.gpu_profiler.start_marker("gpu cache update"); - - // For an artificial stress test of GPU cache resizing, - // always pass an extra update list with at least one block in it. - let gpu_cache_height = self.gpu_cache_texture.get_height(); - if gpu_cache_height != 0 && GPU_CACHE_RESIZE_TEST { - self.pending_gpu_cache_updates.push(GpuCacheUpdateList { - frame_id: FrameId::INVALID, - clear: false, - height: gpu_cache_height, - blocks: vec![[1f32; 4].into()], - updates: Vec::new(), - debug_commands: Vec::new(), - }); - } - - let (updated_blocks, max_requested_height) = self - .pending_gpu_cache_updates - .iter() - .fold((0, gpu_cache_height), |(count, height), list| { - (count + list.blocks.len(), cmp::max(height, list.height)) - }); - - if max_requested_height > self.get_max_texture_size() && !self.gpu_cache_overflow { - self.gpu_cache_overflow = true; - self.renderer_errors.push(super::RendererError::MaxTextureSize); - } - - // Note: if we decide to switch to scatter-style GPU cache update - // permanently, we can have this code nicer with `BufferUploader` kind - // of helper, similarly to how `TextureUploader` API is used. - self.gpu_cache_texture.prepare_for_updates( - &mut self.device, - updated_blocks, - max_requested_height, - ); - - for update_list in self.pending_gpu_cache_updates.drain(..) { - assert!(update_list.height <= max_requested_height); - if update_list.frame_id > self.gpu_cache_frame_id { - self.gpu_cache_frame_id = update_list.frame_id - } - self.gpu_cache_texture - .update(&mut self.device, &update_list); - } - - self.profile.start_time(profiler::GPU_CACHE_UPLOAD_TIME); - let updated_rows = self.gpu_cache_texture.flush( - &mut self.device, - &mut self.texture_upload_pbo_pool - ); - self.gpu_cache_upload_time += self.profile.end_time(profiler::GPU_CACHE_UPLOAD_TIME); - - self.profile.set(profiler::GPU_CACHE_ROWS_UPDATED, updated_rows); - self.profile.set(profiler::GPU_CACHE_BLOCKS_UPDATED, updated_blocks); - } - - pub fn prepare_gpu_cache( - &mut self, - deferred_resolves: &[DeferredResolve], - ) -> Result<(), super::RendererError> { - if self.pending_gpu_cache_clear { - let use_scatter = - matches!(self.gpu_cache_texture.bus, GpuCacheBus::Scatter { .. }); - let new_cache = GpuCacheTexture::new(&mut self.device, use_scatter)?; - let old_cache = mem::replace(&mut self.gpu_cache_texture, new_cache); - old_cache.deinit(&mut self.device); - self.pending_gpu_cache_clear = false; - } - - let deferred_update_list = self.update_deferred_resolves(deferred_resolves); - self.pending_gpu_cache_updates.extend(deferred_update_list); - - self.update_gpu_cache(); - - // Note: the texture might have changed during the `update`, - // so we need to bind it here. - self.device.bind_texture( - super::TextureSampler::GpuCache, - self.gpu_cache_texture.texture.as_ref().unwrap(), - Swizzle::default(), - ); - - Ok(()) - } - - pub fn read_gpu_cache(&mut self) -> (DeviceIntSize, Vec<u8>) { - let texture = self.gpu_cache_texture.texture.as_ref().unwrap(); - let size = device_size_as_framebuffer_size(texture.get_dimensions()); - let mut texels = vec![0; (size.width * size.height * 16) as usize]; - self.device.begin_frame(); - self.device.bind_read_target(ReadTarget::from_texture(texture)); - self.device.read_pixels_into( - size.into(), - api::ImageFormat::RGBAF32, - &mut texels, - ); - self.device.reset_read_target(); - self.device.end_frame(); - (texture.get_dimensions(), texels) - } -} diff --git a/third_party/webrender/webrender/src/renderer/upload.rs b/third_party/webrender/webrender/src/renderer/upload.rs deleted file mode 100644 index 0dac5eaefd1..00000000000 --- a/third_party/webrender/webrender/src/renderer/upload.rs +++ /dev/null @@ -1,792 +0,0 @@ -/* 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/. */ - -//! This module contains the convoluted logic that goes into uploading content into -//! the texture cache's textures. -//! -//! We need to support various combinations of code paths depending on the quirks of -//! each hardware/driver configuration: -//! - direct upload, -//! - staged upload via a pixel buffer object, -//! - staged upload via a direct upload to a staging texture where PBO's aren't supported, -//! - copy from the staging to destination textures, either via blits or batched draw calls. -//! -//! Conceptually a lot of this logic should probably be in the device module, but some code -//! here relies on submitting draw calls via the renderer. - - -use std::mem; -use std::collections::VecDeque; -use euclid::Transform3D; -use time::precise_time_ns; -use malloc_size_of::MallocSizeOfOps; -use api::units::*; -use api::{ExternalImageSource, PremultipliedColorF, ImageBufferKind, ImageRendering, ImageFormat}; -use crate::renderer::{ - Renderer, VertexArrayKind, RendererStats, TextureSampler, TEXTURE_CACHE_DBG_CLEAR_COLOR -}; -use crate::internal_types::{ - FastHashMap, TextureUpdateSource, Swizzle, TextureCacheUpdate, - CacheTextureId, RenderTargetInfo, -}; -use crate::device::{ - Device, UploadMethod, Texture, DrawTarget, UploadStagingBuffer, TextureFlags, TextureUploader, - TextureFilter, -}; -use crate::gpu_types::{ZBufferId, CompositeInstance}; -use crate::batch::BatchTextures; -use crate::texture_pack::{GuillotineAllocator, FreeRectSlice}; -use crate::composite::{CompositeFeatures, CompositeSurfaceFormat}; -use crate::profiler; -use crate::render_api::MemoryReport; - -pub const BATCH_UPLOAD_TEXTURE_SIZE: DeviceIntSize = DeviceIntSize::new(512, 512); - -/// Upload a number of items to texture cache textures. -/// -/// This is the main entry point of the texture cache upload code. -/// See also the module documentation for more information. -pub fn upload_to_texture_cache( - renderer: &mut Renderer, - update_list: FastHashMap<CacheTextureId, Vec<TextureCacheUpdate>>, -) { - - let mut stats = UploadStats { - num_draw_calls: 0, - upload_time: 0, - cpu_buffer_alloc_time: 0, - texture_alloc_time: 0, - cpu_copy_time: 0, - gpu_copy_commands_time: 0, - bytes_uploaded: 0, - }; - - let upload_total_start = precise_time_ns(); - - let mut batch_upload_textures = Vec::new(); - - // A list of copies that must be performed from the temporary textures to the texture cache. - let mut batch_upload_copies = Vec::new(); - - // For each texture format, this stores a list of staging buffers - // and a texture allocator for packing the buffers. - let mut batch_upload_buffers = FastHashMap::default(); - - // For best performance we use a single TextureUploader for all uploads. - // This allows us to fill PBOs more efficiently and therefore allocate fewer PBOs. - let mut uploader = renderer.device.upload_texture( - &mut renderer.texture_upload_pbo_pool, - ); - - let num_updates = update_list.len(); - - for (texture_id, updates) in update_list { - let texture = &renderer.texture_resolver.texture_cache_map[&texture_id]; - for update in updates { - let TextureCacheUpdate { rect, stride, offset, format_override, source } = update; - - let dummy_data; - let data = match source { - TextureUpdateSource::Bytes { ref data } => { - &data[offset as usize ..] - } - TextureUpdateSource::External { id, channel_index } => { - let handler = renderer.external_image_handler - .as_mut() - .expect("Found external image, but no handler set!"); - // The filter is only relevant for NativeTexture external images. - match handler.lock(id, channel_index, ImageRendering::Auto).source { - ExternalImageSource::RawData(data) => { - &data[offset as usize ..] - } - ExternalImageSource::Invalid => { - // Create a local buffer to fill the pbo. - let bpp = texture.get_format().bytes_per_pixel(); - let width = stride.unwrap_or(rect.size.width * bpp); - let total_size = width * rect.size.height; - // WR haven't support RGBAF32 format in texture_cache, so - // we use u8 type here. - dummy_data = vec![0xFFu8; total_size as usize]; - &dummy_data - } - ExternalImageSource::NativeTexture(eid) => { - panic!("Unexpected external texture {:?} for the texture cache update of {:?}", eid, id); - } - } - } - TextureUpdateSource::DebugClear => { - let draw_target = DrawTarget::from_texture( - texture, - false, - ); - renderer.device.bind_draw_target(draw_target); - renderer.device.clear_target( - Some(TEXTURE_CACHE_DBG_CLEAR_COLOR), - None, - Some(draw_target.to_framebuffer_rect(update.rect.to_i32())) - ); - - continue; - } - }; - - let use_batch_upload = renderer.device.use_batched_texture_uploads() && - texture.flags().contains(TextureFlags::IS_SHARED_TEXTURE_CACHE) && - rect.size.width <= BATCH_UPLOAD_TEXTURE_SIZE.width && - rect.size.height <= BATCH_UPLOAD_TEXTURE_SIZE.height; - - if use_batch_upload { - copy_into_staging_buffer( - &mut renderer.device, - &mut uploader, - &mut renderer.staging_texture_pool, - rect, - stride, - data, - texture_id, - texture, - &mut batch_upload_buffers, - &mut batch_upload_textures, - &mut batch_upload_copies, - &mut stats, - ); - } else { - let upload_start_time = precise_time_ns(); - - stats.bytes_uploaded += uploader.upload( - &mut renderer.device, - texture, - rect, - stride, - format_override, - data.as_ptr(), - data.len() - ); - - stats.upload_time += precise_time_ns() - upload_start_time; - } - - if let TextureUpdateSource::External { id, channel_index } = source { - let handler = renderer.external_image_handler - .as_mut() - .expect("Found external image, but no handler set!"); - handler.unlock(id, channel_index); - } - } - } - - let upload_start_time = precise_time_ns(); - // Upload batched texture updates to their temporary textures. - for batch_buffer in batch_upload_buffers.into_iter().map(|(_, (_, buffers))| buffers).flatten() { - let texture = &batch_upload_textures[batch_buffer.texture_index]; - match batch_buffer.staging_buffer { - StagingBufferKind::Pbo(pbo) => { - stats.bytes_uploaded += uploader.upload_staged( - &mut renderer.device, - texture, - DeviceIntRect::from_size(texture.get_dimensions()), - None, - pbo, - ); - } - StagingBufferKind::CpuBuffer { bytes, .. } => { - let bpp = texture.get_format().bytes_per_pixel(); - stats.bytes_uploaded += uploader.upload( - &mut renderer.device, - texture, - batch_buffer.upload_rect, - Some(BATCH_UPLOAD_TEXTURE_SIZE.width * bpp), - None, - bytes.as_ptr(), - bytes.len() - ); - renderer.staging_texture_pool.return_temporary_buffer(bytes); - } - } - } - stats.upload_time += precise_time_ns() - upload_start_time; - - - // Flush all uploads, batched or otherwise. - let flush_start_time = precise_time_ns(); - uploader.flush(&mut renderer.device); - stats.upload_time += precise_time_ns() - flush_start_time; - - if !batch_upload_copies.is_empty() { - // Copy updates that were batch uploaded to their correct destination in the texture cache. - // Sort them by destination and source to minimize framebuffer binding changes. - batch_upload_copies.sort_unstable_by_key(|b| (b.dest_texture_id.0, b.src_texture_index)); - - let gpu_copy_start = precise_time_ns(); - - if renderer.device.use_draw_calls_for_texture_copy() { - // Some drivers are very have a very high CPU overhead when submitting hundreds of small blit - // commands (low end intel drivers on Windows for example can take take 100+ ms submitting a - // few hundred blits). In this case we do the copy with batched draw calls. - copy_from_staging_to_cache_using_draw_calls( - renderer, - &mut stats, - &batch_upload_textures, - batch_upload_copies, - ); - } else { - copy_from_staging_to_cache( - renderer, - &batch_upload_textures, - batch_upload_copies, - ); - } - - stats.gpu_copy_commands_time += precise_time_ns() - gpu_copy_start; - } - - for texture in batch_upload_textures.drain(..) { - renderer.staging_texture_pool.return_texture(texture); - } - - // Update the profile counters. We use add instead of set because - // this function can be called several times per frame. - // We don't update the counters when their value is zero, so that - // the profiler can treat them as events and we can get notified - // when they happen. - - let upload_total = precise_time_ns() - upload_total_start; - renderer.profile.add( - profiler::TOTAL_UPLOAD_TIME, - profiler::ns_to_ms(upload_total) - ); - - if num_updates > 0 { - renderer.profile.add(profiler::TEXTURE_UPLOADS, num_updates); - } - - if stats.bytes_uploaded > 0 { - renderer.profile.add( - profiler::TEXTURE_UPLOADS_MEM, - profiler::bytes_to_mb(stats.bytes_uploaded) - ); - } - - if stats.cpu_copy_time > 0 { - renderer.profile.add( - profiler::UPLOAD_CPU_COPY_TIME, - profiler::ns_to_ms(stats.cpu_copy_time) - ); - } - if stats.upload_time > 0 { - renderer.profile.add( - profiler::UPLOAD_TIME, - profiler::ns_to_ms(stats.upload_time) - ); - } - if stats.texture_alloc_time > 0 { - renderer.profile.add( - profiler::STAGING_TEXTURE_ALLOCATION_TIME, - profiler::ns_to_ms(stats.texture_alloc_time) - ); - } - if stats.cpu_buffer_alloc_time > 0 { - renderer.profile.add( - profiler::CPU_TEXTURE_ALLOCATION_TIME, - profiler::ns_to_ms(stats.cpu_buffer_alloc_time) - ); - } - if stats.num_draw_calls > 0{ - renderer.profile.add( - profiler::UPLOAD_NUM_COPY_BATCHES, - stats.num_draw_calls - ); - } - - if stats.gpu_copy_commands_time > 0 { - renderer.profile.add( - profiler::UPLOAD_GPU_COPY_TIME, - profiler::ns_to_ms(stats.gpu_copy_commands_time) - ); - } -} - -/// Copy an item into a batched upload staging buffer. -fn copy_into_staging_buffer<'a>( - device: &mut Device, - uploader: &mut TextureUploader< 'a>, - staging_texture_pool: &mut UploadTexturePool, - update_rect: DeviceIntRect, - update_stride: Option<i32>, - data: &[u8], - dest_texture_id: CacheTextureId, - texture: &Texture, - batch_upload_buffers: &mut FastHashMap<ImageFormat, (GuillotineAllocator, Vec<BatchUploadBuffer<'a>>)>, - batch_upload_textures: &mut Vec<Texture>, - batch_upload_copies: &mut Vec<BatchUploadCopy>, - stats: &mut UploadStats -) { - let (allocator, buffers) = batch_upload_buffers.entry(texture.get_format()) - .or_insert_with(|| (GuillotineAllocator::new(None), Vec::new())); - - // Allocate a region within the staging buffer for this update. If there is - // no room in an existing buffer then allocate another texture and buffer. - let (slice, origin) = match allocator.allocate(&update_rect.size) { - Some((slice, origin)) => (slice, origin), - None => { - let new_slice = FreeRectSlice(buffers.len() as u32); - allocator.extend(new_slice, BATCH_UPLOAD_TEXTURE_SIZE, update_rect.size); - - let texture_alloc_time_start = precise_time_ns(); - let staging_texture = staging_texture_pool.get_texture(device, texture.get_format()); - stats.texture_alloc_time = precise_time_ns() - texture_alloc_time_start; - - let texture_index = batch_upload_textures.len(); - batch_upload_textures.push(staging_texture); - - let cpu_buffer_alloc_start_time = precise_time_ns(); - let staging_buffer = match device.upload_method() { - UploadMethod::Immediate => StagingBufferKind::CpuBuffer { - bytes: staging_texture_pool.get_temporary_buffer(), - }, - UploadMethod::PixelBuffer(_) => { - let pbo = uploader.stage( - device, - texture.get_format(), - BATCH_UPLOAD_TEXTURE_SIZE, - ).unwrap(); - - StagingBufferKind::Pbo(pbo) - } - }; - stats.cpu_buffer_alloc_time += precise_time_ns() - cpu_buffer_alloc_start_time; - - buffers.push(BatchUploadBuffer { - staging_buffer, - texture_index, - upload_rect: DeviceIntRect::zero() - }); - - (new_slice, DeviceIntPoint::zero()) - } - }; - let buffer = &mut buffers[slice.0 as usize]; - let allocated_rect = DeviceIntRect::new(origin, update_rect.size); - buffer.upload_rect = buffer.upload_rect.union(&allocated_rect); - - batch_upload_copies.push(BatchUploadCopy { - src_texture_index: buffer.texture_index, - src_offset: allocated_rect.origin, - dest_texture_id, - dest_offset: update_rect.origin, - size: update_rect.size, - }); - - unsafe { - let memcpy_start_time = precise_time_ns(); - let bpp = texture.get_format().bytes_per_pixel() as usize; - let width_bytes = update_rect.size.width as usize * bpp; - let src_stride = update_stride.map_or(width_bytes, |stride| { - assert!(stride >= 0); - stride as usize - }); - let src_size = (update_rect.size.height as usize - 1) * src_stride + width_bytes; - assert!(src_size <= data.len()); - - let src: &[mem::MaybeUninit<u8>] = std::slice::from_raw_parts(data.as_ptr() as *const _, src_size); - let (dst_stride, dst) = match &mut buffer.staging_buffer { - StagingBufferKind::Pbo(buffer) => ( - buffer.get_stride(), - buffer.get_mapping(), - ), - StagingBufferKind::CpuBuffer { bytes } => ( - BATCH_UPLOAD_TEXTURE_SIZE.width as usize * bpp, - &mut bytes[..], - ) - }; - - // copy the data line-by-line in to the buffer so that we do not overwrite - // any other region of the buffer. - for y in 0..allocated_rect.size.height as usize { - let src_start = y * src_stride; - let src_end = src_start + width_bytes; - let dst_start = (allocated_rect.origin.y as usize + y as usize) * dst_stride + - allocated_rect.origin.x as usize * bpp; - let dst_end = dst_start + width_bytes; - - dst[dst_start..dst_end].copy_from_slice(&src[src_start..src_end]) - } - - stats.cpu_copy_time += precise_time_ns() - memcpy_start_time; - } -} - - -/// Copy from the staging PBOs or textures to texture cache textures using blit commands. -/// -/// Using blits instead of draw calls is supposedly more efficient but some drivers have -/// a very high per-command overhead so in some configurations we end up using -/// copy_from_staging_to_cache_using_draw_calls instead. -fn copy_from_staging_to_cache( - renderer: &mut Renderer, - batch_upload_textures: &[Texture], - batch_upload_copies: Vec<BatchUploadCopy>, -) { - for copy in batch_upload_copies { - let dest_texture = &renderer.texture_resolver.texture_cache_map[©.dest_texture_id]; - - renderer.device.copy_texture_sub_region( - &batch_upload_textures[copy.src_texture_index], - copy.src_offset.x as _, - copy.src_offset.y as _, - dest_texture, - copy.dest_offset.x as _, - copy.dest_offset.y as _, - copy.size.width as _, - copy.size.height as _, - ); - } -} - -/// Generate and submit composite shader batches to copy from -/// the staging textures to the destination cache textures. -/// -/// If this shows up in GPU time ptofiles we could replace it with -/// a simpler shader (composite.glsl is already quite simple). -fn copy_from_staging_to_cache_using_draw_calls( - renderer: &mut Renderer, - stats: &mut UploadStats, - batch_upload_textures: &[Texture], - batch_upload_copies: Vec<BatchUploadCopy>, -) { - let mut dummy_stats = RendererStats { - total_draw_calls: 0, - alpha_target_count: 0, - color_target_count: 0, - texture_upload_mb: 0.0, - resource_upload_time: 0.0, - gpu_cache_upload_time: 0.0, - gecko_display_list_time: 0.0, - wr_display_list_time: 0.0, - scene_build_time: 0.0, - frame_build_time: 0.0, - full_display_list: false, - full_paint: false, - }; - - let mut copy_instances = Vec::new(); - let mut prev_src = None; - let mut prev_dst = None; - - for copy in batch_upload_copies { - - let src_changed = prev_src != Some(copy.src_texture_index); - let dst_changed = prev_dst != Some(copy.dest_texture_id); - - if (src_changed || dst_changed) && !copy_instances.is_empty() { - - renderer.draw_instanced_batch( - ©_instances, - VertexArrayKind::Composite, - // We bind the staging texture manually because it isn't known - // to the texture resolver. - &BatchTextures::empty(), - &mut dummy_stats, - ); - - stats.num_draw_calls += 1; - copy_instances.clear(); - } - - if dst_changed { - let dest_texture = &renderer.texture_resolver.texture_cache_map[©.dest_texture_id]; - let target_size = dest_texture.get_dimensions(); - - let draw_target = DrawTarget::from_texture( - dest_texture, - false, - ); - renderer.device.bind_draw_target(draw_target); - - let projection = Transform3D::ortho( - 0.0, - target_size.width as f32, - 0.0, - target_size.height as f32, - renderer.device.ortho_near_plane(), - renderer.device.ortho_far_plane(), - ); - - renderer.shaders - .borrow_mut() - .get_composite_shader( - CompositeSurfaceFormat::Rgba, - ImageBufferKind::Texture2D, - CompositeFeatures::empty(), - ).bind( - &mut renderer.device, - &projection, - None, - &mut renderer.renderer_errors - ); - - prev_dst = Some(copy.dest_texture_id); - } - - if src_changed { - renderer.device.bind_texture( - TextureSampler::Color0, - &batch_upload_textures[copy.src_texture_index], - Swizzle::default(), - ); - - prev_src = Some(copy.src_texture_index) - } - - let dest_rect = DeviceRect { - origin: copy.dest_offset.to_f32(), - size: copy.size.to_f32(), - }; - - let src_rect = TexelRect::new( - copy.src_offset.x as f32, - copy.src_offset.y as f32, - (copy.src_offset.x + copy.size.width) as f32, - (copy.src_offset.y + copy.size.height) as f32, - ); - - copy_instances.push(CompositeInstance::new_rgb( - dest_rect, - dest_rect, - PremultipliedColorF::WHITE, - ZBufferId(0), - src_rect, - )); - } - - if !copy_instances.is_empty() { - renderer.draw_instanced_batch( - ©_instances, - VertexArrayKind::Composite, - // We bind the staging texture manually because it isn't known - // to the texture resolver. - &BatchTextures::empty(), - &mut dummy_stats, - ); - - stats.num_draw_calls += 1; - } -} - -/// A very basic pool to avoid reallocating staging textures as well as staging -/// CPU side buffers. -pub struct UploadTexturePool { - /// The textures in the pool associated with a last used frame index. - /// - /// The outer array corresponds to each of teh three supported texture formats. - textures: [VecDeque<(Texture, u64)>; 3], - // Frame at which to deallocate some textures if there are too many in the pool, - // for each format. - delay_texture_deallocation: [u64; 3], - current_frame: u64, - - /// Temporary buffers that are used when using staging uploads + glTexImage2D. - /// - /// Temporary buffers aren't used asynchronously so they can be reused every frame. - /// To keep things simple we always allocate enough memory for formats with four bytes - /// per pixel (more than we need for alpha-only textures but it works just as well). - temporary_buffers: Vec<Vec<mem::MaybeUninit<u8>>>, - used_temporary_buffers: usize, - delay_buffer_deallocation: u64, -} - -impl UploadTexturePool { - pub fn new() -> Self { - UploadTexturePool { - textures: [VecDeque::new(), VecDeque::new(), VecDeque::new()], - delay_texture_deallocation: [0; 3], - current_frame: 0, - temporary_buffers: Vec::new(), - used_temporary_buffers: 0, - delay_buffer_deallocation: 0, - } - } - - fn format_index(&self, format: ImageFormat) -> usize { - match format { - ImageFormat::RGBA8 => 0, - ImageFormat::BGRA8 => 1, - ImageFormat::R8 => 2, - _ => { panic!("unexpected format"); } - } - } - - pub fn begin_frame(&mut self) { - self.current_frame += 1; - } - - /// Create or reuse a staging texture. - /// - /// See also return_texture. - pub fn get_texture(&mut self, device: &mut Device, format: ImageFormat) -> Texture { - - // First try to reuse a texture from the pool. - // "available" here means hasn't been used for 2 frames to avoid stalls. - // No need to scan the vector. Newer textures are always pushed at the back - // of the vector so we know the first element is the least recently used. - let format_idx = self.format_index(format); - let can_reuse = self.textures[format_idx].get(0) - .map(|tex| self.current_frame - tex.1 > 2) - .unwrap_or(false); - - if can_reuse { - return self.textures[format_idx].pop_front().unwrap().0; - } - - // If we couldn't find an available texture, create a new one. - - device.create_texture( - ImageBufferKind::Texture2D, - format, - BATCH_UPLOAD_TEXTURE_SIZE.width, - BATCH_UPLOAD_TEXTURE_SIZE.height, - TextureFilter::Nearest, - // Currently we need render target support as we always use glBlitFramebuffer - // to copy the texture data. Instead, we should use glCopyImageSubData on some - // platforms, and avoid creating the FBOs in that case. - Some(RenderTargetInfo { has_depth: false }), - ) - } - - /// Hand the staging texture back to the pool after being done with uploads. - /// - /// The texture must have been obtained from this pool via get_texture. - pub fn return_texture(&mut self, texture: Texture) { - let format_idx = self.format_index(texture.get_format()); - self.textures[format_idx].push_back((texture, self.current_frame)); - } - - /// Create or reuse a temporary CPU buffer. - /// - /// These buffers are used in the batched upload path when PBOs are not supported. - /// Content is first written to the temporary buffer and uploaded via a single - /// glTexSubImage2D call. - pub fn get_temporary_buffer(&mut self) -> Vec<mem::MaybeUninit<u8>> { - self.used_temporary_buffers += 1; - self.temporary_buffers.pop().unwrap_or_else(|| { - vec![mem::MaybeUninit::new(0); BATCH_UPLOAD_TEXTURE_SIZE.area() as usize * 4] - }) - } - - /// Return memory that was obtained from this pool via get_temporary_buffer. - pub fn return_temporary_buffer(&mut self, buffer: Vec<mem::MaybeUninit<u8>>) { - assert_eq!(buffer.len(), BATCH_UPLOAD_TEXTURE_SIZE.area() as usize * 4); - self.temporary_buffers.push(buffer); - } - - /// Deallocate this pool's CPU and GPU memory. - pub fn delete_textures(&mut self, device: &mut Device) { - for format in &mut self.textures { - while let Some(texture) = format.pop_back() { - device.delete_texture(texture.0) - } - } - self.temporary_buffers.clear(); - } - - /// Deallocate some textures if there are too many for a long time. - pub fn end_frame(&mut self, device: &mut Device) { - for format_idx in 0..self.textures.len() { - // Count the number of reusable staging textures. - // if it stays high for a large number of frames, truncate it back to 8-ish - // over multiple frames. - - let mut num_reusable_textures = 0; - for texture in &self.textures[format_idx] { - if self.current_frame - texture.1 > 2 { - num_reusable_textures += 1; - } - } - - if num_reusable_textures < 8 { - // Don't deallocate textures for another 120 frames. - self.delay_texture_deallocation[format_idx] = self.current_frame + 120; - } - - // Deallocate up to 4 staging textures every frame. - let to_remove = if self.current_frame > self.delay_texture_deallocation[format_idx] { - num_reusable_textures.min(4) - } else { - 0 - }; - - for _ in 0..to_remove { - let texture = self.textures[format_idx].pop_front().unwrap().0; - device.delete_texture(texture); - } - } - - // Similar logic for temporary CPU buffers. - let unused_buffers = self.temporary_buffers.len() - self.used_temporary_buffers; - if unused_buffers < 8 { - self.delay_buffer_deallocation = self.current_frame + 120; - } - let to_remove = if self.current_frame > self.delay_buffer_deallocation { - unused_buffers.min(4) - } else { - 0 - }; - for _ in 0..to_remove { - // Unlike textures it doesn't matter whether we pop from the front or back - // of the vector. - self.temporary_buffers.pop(); - } - self.used_temporary_buffers = 0; - } - - pub fn report_memory_to(&self, report: &mut MemoryReport, size_op_funs: &MallocSizeOfOps) { - for buf in &self.temporary_buffers { - report.upload_staging_memory += unsafe { (size_op_funs.size_of_op)(buf.as_ptr() as *const _) }; - } - - for format in &self.textures { - for texture in format { - report.upload_staging_textures += texture.0.size_in_bytes(); - } - } - } -} - -struct UploadStats { - num_draw_calls: u32, - upload_time: u64, - cpu_buffer_alloc_time: u64, - texture_alloc_time: u64, - cpu_copy_time: u64, - gpu_copy_commands_time: u64, - bytes_uploaded: usize, -} - -#[derive(Debug)] -enum StagingBufferKind<'a> { - Pbo(UploadStagingBuffer<'a>), - CpuBuffer { bytes: Vec<mem::MaybeUninit<u8>> } -} -#[derive(Debug)] -struct BatchUploadBuffer<'a> { - staging_buffer: StagingBufferKind<'a>, - texture_index: usize, - // A rectangle containing all items going into this staging texture, so - // that we can avoid uploading the entire area if we are using glTexSubImage2d. - upload_rect: DeviceIntRect, -} - -// On some devices performing many small texture uploads is slow, so instead we batch -// updates in to a small number of uploads to temporary textures, then copy from those -// textures to the correct place in the texture cache. -// A list of temporary textures that batches of updates are uploaded to. -#[derive(Debug)] -struct BatchUploadCopy { - // Index within batch_upload_textures - src_texture_index: usize, - src_offset: DeviceIntPoint, - dest_texture_id: CacheTextureId, - dest_offset: DeviceIntPoint, - size: DeviceIntSize, -} diff --git a/third_party/webrender/webrender/src/renderer/vertex.rs b/third_party/webrender/webrender/src/renderer/vertex.rs deleted file mode 100644 index de5a08fa3c5..00000000000 --- a/third_party/webrender/webrender/src/renderer/vertex.rs +++ /dev/null @@ -1,1095 +0,0 @@ -/* 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/. */ - -//! Rendering logic related to the vertex shaders and their states, uncluding -//! - Vertex Array Objects -//! - vertex layout descriptors -//! - textures bound at vertex stage - -use std::{marker::PhantomData, mem, num::NonZeroUsize, ops}; -use api::units::*; -use crate::{ - device::{ - Device, Texture, TextureFilter, TextureUploader, UploadPBOPool, VertexUsageHint, VAO, - }, - frame_builder::Frame, - gpu_types::{PrimitiveHeaderI, PrimitiveHeaderF, TransformData}, - internal_types::Swizzle, - render_task::RenderTaskData, -}; - -pub const VERTEX_TEXTURE_EXTRA_ROWS: i32 = 10; - -pub const MAX_VERTEX_TEXTURE_WIDTH: usize = webrender_build::MAX_VERTEX_TEXTURE_WIDTH; - -pub mod desc { - use crate::device::{VertexAttribute, VertexAttributeKind, VertexDescriptor}; - - pub const PRIM_INSTANCES: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[VertexAttribute { - name: "aData", - count: 4, - kind: VertexAttributeKind::I32, - }], - }; - - pub const BLUR: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aBlurRenderTaskAddress", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aBlurSourceTaskAddress", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aBlurDirection", - count: 1, - kind: VertexAttributeKind::I32, - }, - ], - }; - - pub const LINE: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aTaskRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aLocalSize", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aWavyLineThickness", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aStyle", - count: 1, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aAxisSelect", - count: 1, - kind: VertexAttributeKind::F32, - }, - ], - }; - - pub const FAST_LINEAR_GRADIENT: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aTaskRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aColor0", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aColor1", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aAxisSelect", - count: 1, - kind: VertexAttributeKind::F32, - }, - ], - }; - - pub const LINEAR_GRADIENT: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aTaskRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aStartPoint", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aEndPoint", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aScale", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aExtendMode", - count: 1, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aGradientStopsAddress", - count: 1, - kind: VertexAttributeKind::I32, - }, - ], - }; - - pub const RADIAL_GRADIENT: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aTaskRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aCenter", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aScale", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aStartRadius", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aEndRadius", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aXYRatio", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aExtendMode", - count: 1, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aGradientStopsAddress", - count: 1, - kind: VertexAttributeKind::I32, - }, - ], - }; - - pub const CONIC_GRADIENT: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aTaskRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aCenter", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aScale", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aStartOffset", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aEndOffset", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aAngle", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aExtendMode", - count: 1, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aGradientStopsAddress", - count: 1, - kind: VertexAttributeKind::I32, - }, - ], - }; - - pub const BORDER: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aTaskOrigin", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aColor0", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aColor1", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aFlags", - count: 1, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aWidths", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aRadii", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipParams1", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipParams2", - count: 4, - kind: VertexAttributeKind::F32, - }, - ], - }; - - pub const SCALE: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aScaleTargetRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aScaleSourceRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - ], - }; - - pub const CLIP_RECT: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - // common clip attributes - VertexAttribute { - name: "aClipDeviceArea", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipOrigins", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aDevicePixelScale", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aTransformIds", - count: 2, - kind: VertexAttributeKind::I32, - }, - // specific clip attributes - VertexAttribute { - name: "aClipLocalPos", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipLocalRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipMode", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipRect_TL", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipRadii_TL", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipRect_TR", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipRadii_TR", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipRect_BL", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipRadii_BL", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipRect_BR", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipRadii_BR", - count: 4, - kind: VertexAttributeKind::F32, - }, - ], - }; - - pub const CLIP_BOX_SHADOW: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - // common clip attributes - VertexAttribute { - name: "aClipDeviceArea", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipOrigins", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aDevicePixelScale", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aTransformIds", - count: 2, - kind: VertexAttributeKind::I32, - }, - // specific clip attributes - VertexAttribute { - name: "aClipDataResourceAddress", - count: 2, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aClipSrcRectSize", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipMode", - count: 1, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aStretchMode", - count: 2, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aClipDestRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - ], - }; - - pub const CLIP_IMAGE: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - // common clip attributes - VertexAttribute { - name: "aClipDeviceArea", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipOrigins", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aDevicePixelScale", - count: 1, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aTransformIds", - count: 2, - kind: VertexAttributeKind::I32, - }, - // specific clip attributes - VertexAttribute { - name: "aClipTileRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aClipDataResourceAddress", - count: 2, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aClipLocalRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - ], - }; - - pub const GPU_CACHE_UPDATE: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[ - VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U16Norm, - }, - VertexAttribute { - name: "aValue", - count: 4, - kind: VertexAttributeKind::F32, - }, - ], - instance_attributes: &[], - }; - - pub const RESOLVE: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[VertexAttribute { - name: "aRect", - count: 4, - kind: VertexAttributeKind::F32, - }], - }; - - pub const SVG_FILTER: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aFilterRenderTaskAddress", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aFilterInput1TaskAddress", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aFilterInput2TaskAddress", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aFilterKind", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aFilterInputCount", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aFilterGenericInt", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aFilterExtraDataAddress", - count: 2, - kind: VertexAttributeKind::U16, - }, - ], - }; - - pub const VECTOR_STENCIL: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aFromPosition", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aCtrlPosition", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aToPosition", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aFromNormal", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aCtrlNormal", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aToNormal", - count: 2, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aPathID", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aPad", - count: 1, - kind: VertexAttributeKind::U16, - }, - ], - }; - - pub const VECTOR_COVER: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aTargetRect", - count: 4, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aStencilOrigin", - count: 2, - kind: VertexAttributeKind::I32, - }, - VertexAttribute { - name: "aSubpixel", - count: 1, - kind: VertexAttributeKind::U16, - }, - VertexAttribute { - name: "aPad", - count: 1, - kind: VertexAttributeKind::U16, - }, - ], - }; - - pub const COMPOSITE: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aDeviceRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aDeviceClipRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aColor", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aParams", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aUvRect0", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aUvRect1", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aUvRect2", - count: 4, - kind: VertexAttributeKind::F32, - }, - ], - }; - - pub const CLEAR: VertexDescriptor = VertexDescriptor { - vertex_attributes: &[VertexAttribute { - name: "aPosition", - count: 2, - kind: VertexAttributeKind::U8Norm, - }], - instance_attributes: &[ - VertexAttribute { - name: "aRect", - count: 4, - kind: VertexAttributeKind::F32, - }, - VertexAttribute { - name: "aColor", - count: 4, - kind: VertexAttributeKind::F32, - }, - ], - }; -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum VertexArrayKind { - Primitive, - Blur, - ClipImage, - ClipRect, - ClipBoxShadow, - VectorStencil, - VectorCover, - Border, - Scale, - LineDecoration, - FastLinearGradient, - LinearGradient, - RadialGradient, - ConicGradient, - Resolve, - SvgFilter, - Composite, - Clear, -} - -pub struct VertexDataTexture<T> { - texture: Option<Texture>, - format: api::ImageFormat, - _marker: PhantomData<T>, -} - -impl<T> VertexDataTexture<T> { - pub fn new(format: api::ImageFormat) -> Self { - Self { - texture: None, - format, - _marker: PhantomData, - } - } - - /// Returns a borrow of the GPU texture. Panics if it hasn't been initialized. - pub fn texture(&self) -> &Texture { - self.texture.as_ref().unwrap() - } - - /// Returns an estimate of the GPU memory consumed by this VertexDataTexture. - pub fn size_in_bytes(&self) -> usize { - self.texture.as_ref().map_or(0, |t| t.size_in_bytes()) - } - - pub fn update<'a>( - &'a mut self, - device: &mut Device, - texture_uploader: &mut TextureUploader<'a>, - data: &mut Vec<T>, - ) { - debug_assert!(mem::size_of::<T>() % 16 == 0); - let texels_per_item = mem::size_of::<T>() / 16; - let items_per_row = MAX_VERTEX_TEXTURE_WIDTH / texels_per_item; - debug_assert_ne!(items_per_row, 0); - - // Ensure we always end up with a texture when leaving this method. - let mut len = data.len(); - if len == 0 { - if self.texture.is_some() { - return; - } - data.reserve(items_per_row); - len = items_per_row; - } else { - // Extend the data array to have enough capacity to upload at least - // a multiple of the row size. This ensures memory safety when the - // array is passed to OpenGL to upload to the GPU. - let extra = len % items_per_row; - if extra != 0 { - let padding = items_per_row - extra; - data.reserve(padding); - len += padding; - } - } - - let needed_height = (len / items_per_row) as i32; - let existing_height = self - .texture - .as_ref() - .map_or(0, |t| t.get_dimensions().height); - - // Create a new texture if needed. - // - // These textures are generally very small, which is why we don't bother - // with incremental updates and just re-upload every frame. For most pages - // they're one row each, and on stress tests like css-francine they end up - // in the 6-14 range. So we size the texture tightly to what we need (usually - // 1), and shrink it if the waste would be more than `VERTEX_TEXTURE_EXTRA_ROWS` - // rows. This helps with memory overhead, especially because there are several - // instances of these textures per Renderer. - if needed_height > existing_height - || needed_height + VERTEX_TEXTURE_EXTRA_ROWS < existing_height - { - // Drop the existing texture, if any. - if let Some(t) = self.texture.take() { - device.delete_texture(t); - } - - let texture = device.create_texture( - api::ImageBufferKind::Texture2D, - self.format, - MAX_VERTEX_TEXTURE_WIDTH as i32, - // Ensure height is at least two to work around - // https://bugs.chromium.org/p/angleproject/issues/detail?id=3039 - needed_height.max(2), - TextureFilter::Nearest, - None, - ); - self.texture = Some(texture); - } - - // Note: the actual width can be larger than the logical one, with a few texels - // of each row unused at the tail. This is needed because there is still hardware - // (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 = 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 as i32, needed_height), - ); - - debug_assert!(len <= data.capacity(), "CPU copy will read out of bounds"); - texture_uploader.upload( - device, - self.texture(), - rect, - None, - None, - data.as_ptr(), - len, - ); - } - - pub fn deinit(mut self, device: &mut Device) { - if let Some(t) = self.texture.take() { - device.delete_texture(t); - } - } -} - -pub struct VertexDataTextures { - prim_header_f_texture: VertexDataTexture<PrimitiveHeaderF>, - prim_header_i_texture: VertexDataTexture<PrimitiveHeaderI>, - transforms_texture: VertexDataTexture<TransformData>, - render_task_texture: VertexDataTexture<RenderTaskData>, -} - -impl VertexDataTextures { - pub fn new() -> Self { - VertexDataTextures { - prim_header_f_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32), - prim_header_i_texture: VertexDataTexture::new(api::ImageFormat::RGBAI32), - transforms_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32), - render_task_texture: VertexDataTexture::new(api::ImageFormat::RGBAF32), - } - } - - pub fn update(&mut self, device: &mut Device, pbo_pool: &mut UploadPBOPool, frame: &mut Frame) { - let mut texture_uploader = device.upload_texture(pbo_pool); - self.prim_header_f_texture.update( - device, - &mut texture_uploader, - &mut frame.prim_headers.headers_float, - ); - self.prim_header_i_texture.update( - device, - &mut texture_uploader, - &mut frame.prim_headers.headers_int, - ); - self.transforms_texture - .update(device, &mut texture_uploader, &mut frame.transform_palette); - self.render_task_texture.update( - device, - &mut texture_uploader, - &mut frame.render_tasks.task_data, - ); - - // Flush and drop the texture uploader now, so that - // we can borrow the textures to bind them. - texture_uploader.flush(device); - - device.bind_texture( - super::TextureSampler::PrimitiveHeadersF, - &self.prim_header_f_texture.texture(), - Swizzle::default(), - ); - device.bind_texture( - super::TextureSampler::PrimitiveHeadersI, - &self.prim_header_i_texture.texture(), - Swizzle::default(), - ); - device.bind_texture( - super::TextureSampler::TransformPalette, - &self.transforms_texture.texture(), - Swizzle::default(), - ); - device.bind_texture( - super::TextureSampler::RenderTasks, - &self.render_task_texture.texture(), - Swizzle::default(), - ); - } - - pub fn size_in_bytes(&self) -> usize { - self.prim_header_f_texture.size_in_bytes() - + self.prim_header_i_texture.size_in_bytes() - + self.transforms_texture.size_in_bytes() - + self.render_task_texture.size_in_bytes() - } - - pub fn deinit(self, device: &mut Device) { - self.transforms_texture.deinit(device); - self.prim_header_f_texture.deinit(device); - self.prim_header_i_texture.deinit(device); - self.render_task_texture.deinit(device); - } -} - -pub struct RendererVAOs { - prim_vao: VAO, - blur_vao: VAO, - clip_rect_vao: VAO, - clip_box_shadow_vao: VAO, - clip_image_vao: VAO, - border_vao: VAO, - line_vao: VAO, - scale_vao: VAO, - fast_linear_gradient_vao: VAO, - linear_gradient_vao: VAO, - radial_gradient_vao: VAO, - conic_gradient_vao: VAO, - resolve_vao: VAO, - svg_filter_vao: VAO, - composite_vao: VAO, - clear_vao: VAO, -} - -impl RendererVAOs { - pub fn new(device: &mut Device, indexed_quads: Option<NonZeroUsize>) -> Self { - const QUAD_INDICES: [u16; 6] = [0, 1, 2, 2, 1, 3]; - const QUAD_VERTICES: [[u8; 2]; 4] = [[0, 0], [0xFF, 0], [0, 0xFF], [0xFF, 0xFF]]; - - let instance_divisor = if indexed_quads.is_some() { 0 } else { 1 }; - let prim_vao = device.create_vao(&desc::PRIM_INSTANCES, instance_divisor); - - device.bind_vao(&prim_vao); - match indexed_quads { - Some(count) => { - assert!(count.get() < u16::MAX as usize); - let quad_indices = (0 .. count.get() as u16) - .flat_map(|instance| QUAD_INDICES.iter().map(move |&index| instance * 4 + index)) - .collect::<Vec<_>>(); - device.update_vao_indices(&prim_vao, &quad_indices, VertexUsageHint::Static); - let quad_vertices = (0 .. count.get() as u16) - .flat_map(|_| QUAD_VERTICES.iter().cloned()) - .collect::<Vec<_>>(); - device.update_vao_main_vertices(&prim_vao, &quad_vertices, VertexUsageHint::Static); - } - None => { - device.update_vao_indices(&prim_vao, &QUAD_INDICES, VertexUsageHint::Static); - device.update_vao_main_vertices(&prim_vao, &QUAD_VERTICES, VertexUsageHint::Static); - } - } - - RendererVAOs { - blur_vao: device.create_vao_with_new_instances(&desc::BLUR, &prim_vao), - clip_rect_vao: device.create_vao_with_new_instances(&desc::CLIP_RECT, &prim_vao), - clip_box_shadow_vao: device - .create_vao_with_new_instances(&desc::CLIP_BOX_SHADOW, &prim_vao), - clip_image_vao: device.create_vao_with_new_instances(&desc::CLIP_IMAGE, &prim_vao), - border_vao: device.create_vao_with_new_instances(&desc::BORDER, &prim_vao), - scale_vao: device.create_vao_with_new_instances(&desc::SCALE, &prim_vao), - line_vao: device.create_vao_with_new_instances(&desc::LINE, &prim_vao), - fast_linear_gradient_vao: device.create_vao_with_new_instances(&desc::FAST_LINEAR_GRADIENT, &prim_vao), - linear_gradient_vao: device.create_vao_with_new_instances(&desc::LINEAR_GRADIENT, &prim_vao), - radial_gradient_vao: device.create_vao_with_new_instances(&desc::RADIAL_GRADIENT, &prim_vao), - conic_gradient_vao: device.create_vao_with_new_instances(&desc::CONIC_GRADIENT, &prim_vao), - resolve_vao: device.create_vao_with_new_instances(&desc::RESOLVE, &prim_vao), - svg_filter_vao: device.create_vao_with_new_instances(&desc::SVG_FILTER, &prim_vao), - composite_vao: device.create_vao_with_new_instances(&desc::COMPOSITE, &prim_vao), - clear_vao: device.create_vao_with_new_instances(&desc::CLEAR, &prim_vao), - prim_vao, - } - } - - pub fn deinit(self, device: &mut Device) { - device.delete_vao(self.prim_vao); - device.delete_vao(self.resolve_vao); - device.delete_vao(self.clip_rect_vao); - device.delete_vao(self.clip_box_shadow_vao); - device.delete_vao(self.clip_image_vao); - device.delete_vao(self.fast_linear_gradient_vao); - device.delete_vao(self.linear_gradient_vao); - device.delete_vao(self.radial_gradient_vao); - device.delete_vao(self.conic_gradient_vao); - device.delete_vao(self.blur_vao); - device.delete_vao(self.line_vao); - device.delete_vao(self.border_vao); - device.delete_vao(self.scale_vao); - device.delete_vao(self.svg_filter_vao); - device.delete_vao(self.composite_vao); - device.delete_vao(self.clear_vao); - } -} - -impl ops::Index<VertexArrayKind> for RendererVAOs { - type Output = VAO; - fn index(&self, kind: VertexArrayKind) -> &VAO { - match kind { - VertexArrayKind::Primitive => &self.prim_vao, - VertexArrayKind::ClipImage => &self.clip_image_vao, - VertexArrayKind::ClipRect => &self.clip_rect_vao, - VertexArrayKind::ClipBoxShadow => &self.clip_box_shadow_vao, - VertexArrayKind::Blur => &self.blur_vao, - VertexArrayKind::VectorStencil | VertexArrayKind::VectorCover => unreachable!(), - VertexArrayKind::Border => &self.border_vao, - VertexArrayKind::Scale => &self.scale_vao, - VertexArrayKind::LineDecoration => &self.line_vao, - VertexArrayKind::FastLinearGradient => &self.fast_linear_gradient_vao, - VertexArrayKind::LinearGradient => &self.linear_gradient_vao, - VertexArrayKind::RadialGradient => &self.radial_gradient_vao, - VertexArrayKind::ConicGradient => &self.conic_gradient_vao, - VertexArrayKind::Resolve => &self.resolve_vao, - VertexArrayKind::SvgFilter => &self.svg_filter_vao, - VertexArrayKind::Composite => &self.composite_vao, - VertexArrayKind::Clear => &self.clear_vao, - } - } -} diff --git a/third_party/webrender/webrender/src/resource_cache.rs b/third_party/webrender/webrender/src/resource_cache.rs index 0c5d106bb1c..a87d179b08d 100644 --- a/third_party/webrender/webrender/src/resource_cache.rs +++ b/third_party/webrender/webrender/src/resource_cache.rs @@ -2,16 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{BlobImageResources, BlobImageRequest, RasterizedBlobImage, ImageFormat}; -use api::{DebugFlags, FontInstanceKey, FontKey, FontTemplate, GlyphIndex}; -use api::{ExternalImageData, ExternalImageType, ExternalImageId, BlobImageResult, FontInstanceData}; +use api::{AddFont, BlobImageResources, ResourceUpdate}; +use api::{BlobImageRequest, RasterizedBlobImage}; +use api::{ClearCache, DebugFlags, FontInstanceKey, FontKey, FontTemplate, GlyphIndex}; +use api::{ExternalImageData, ExternalImageType, BlobImageResult, FontInstanceData}; use api::{DirtyRect, GlyphDimensions, IdNamespace, DEFAULT_TILE_SIZE}; use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering, TileSize}; -use api::{BlobImageKey, VoidPtrToSizeFn}; +use api::{BlobImageKey, MemoryReport, VoidPtrToSizeFn}; use api::{SharedFontInstanceMap, BaseFontInstance}; +use api::image_tiling::{compute_tile_size, compute_tile_range}; use api::units::*; -use crate::{render_api::{ClearCache, AddFont, ResourceUpdate, MemoryReport}, util::WeakTable}; -use crate::image_tiling::{compute_tile_size, compute_tile_range}; #[cfg(feature = "capture")] use crate::capture::ExternalCaptureImage; #[cfg(feature = "replay")] @@ -25,12 +25,11 @@ use crate::glyph_cache::GlyphCacheEntry; use crate::glyph_rasterizer::{GLYPH_FLASHING, FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer}; use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle}; use crate::gpu_types::UvRectKind; -use crate::internal_types::{CacheTextureId, FastHashMap, FastHashSet, TextureSource, ResourceUpdateList}; -use crate::picture::SurfaceInfo; -use crate::profiler::{self, TransactionProfile, bytes_to_mb}; +use crate::internal_types::{FastHashMap, FastHashSet, TextureSource, ResourceUpdateList}; +use crate::profiler::{ResourceProfileCounters, TextureCacheProfileCounters}; use crate::render_backend::{FrameId, FrameStamp}; -use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder}; -use crate::render_task_cache::{RenderTaskCache, RenderTaskCacheKey, RenderTaskParent}; +use crate::render_task_graph::{RenderTaskGraph, RenderTaskId}; +use crate::render_task_cache::{RenderTaskCache, RenderTaskCacheKey}; use crate::render_task_cache::{RenderTaskCacheEntry, RenderTaskCacheEntryHandle}; use euclid::point2; use smallvec::SmallVec; @@ -48,7 +47,7 @@ use std::path::PathBuf; use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::u32; -use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader}; +use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction}; // Counter for generating unique native surface ids static NEXT_NATIVE_SURFACE_ID: AtomicUsize = AtomicUsize::new(0); @@ -58,9 +57,6 @@ static NEXT_NATIVE_SURFACE_ID: AtomicUsize = AtomicUsize::new(0); pub struct GlyphFetchResult { pub index_in_text_run: i32, pub uv_rect_address: GpuCacheAddress, - pub offset: DevicePoint, - pub size: DeviceIntSize, - pub scale: f32, } // These coordinates are always in texels. @@ -79,7 +75,7 @@ pub struct CacheItem { pub texture_id: TextureSource, pub uv_rect_handle: GpuCacheHandle, pub uv_rect: DeviceIntRect, - pub user_data: [f32; 4], + pub texture_layer: i32, } impl CacheItem { @@ -88,13 +84,9 @@ impl CacheItem { texture_id: TextureSource::Invalid, uv_rect_handle: GpuCacheHandle::new(), uv_rect: DeviceIntRect::zero(), - user_data: [0.0; 4], + texture_layer: 0, } } - - pub fn is_valid(&self) -> bool { - self.texture_id != TextureSource::Invalid - } } /// Represents the backing store of an image in the cache. @@ -228,7 +220,9 @@ struct CachedImageInfo { impl CachedImageInfo { fn mark_unused(&mut self, texture_cache: &mut TextureCache) { - texture_cache.evict_handle(&self.texture_cache_handle); + if self.manual_eviction { + texture_cache.evict_manual_handle(&self.texture_cache_handle); + } self.manual_eviction = false; } } @@ -400,10 +394,6 @@ struct Resources { font_templates: FastHashMap<FontKey, FontTemplate>, font_instances: SharedFontInstanceMap, image_templates: ImageTemplates, - // We keep a set of Weak references to the fonts so that we're able to include them in memory - // reports even if only the OS is holding on to the Vec<u8>. PtrWeakHashSet will periodically - // drop any references that have gone dead. - weak_fonts: WeakTable } impl BlobImageResources for Resources { @@ -423,29 +413,6 @@ pub type GlyphDimensionsCache = FastHashMap<(FontInstanceKey, GlyphIndex), Optio #[derive(Clone, Copy, Debug, PartialEq)] pub struct BlobImageRasterizerEpoch(usize); -/// Internal information about allocated render targets in the pool -struct RenderTarget { - size: DeviceIntSize, - format: ImageFormat, - texture_id: CacheTextureId, - /// If true, this is currently leant out, and not available to other passes - is_active: bool, - last_frame_used: FrameId, -} - -impl RenderTarget { - fn size_in_bytes(&self) -> usize { - let bpp = self.format.bytes_per_pixel() as usize; - (self.size.width * self.size.height) as usize * bpp - } - - /// Returns true if this texture was used within `threshold` frames of - /// the current frame. - pub fn used_recently(&self, current_frame_id: FrameId, threshold: usize) -> bool { - self.last_frame_used + threshold >= current_frame_id - } -} - /// High-level container for resources managed by the `RenderBackend`. /// /// This includes a variety of things, including images, fonts, and glyphs, @@ -485,12 +452,6 @@ pub struct ResourceCache { /// A list of queued compositor surface updates to apply next frame. pending_native_surface_updates: Vec<NativeSurfaceOperation>, - - image_templates_memory: usize, - font_templates_memory: usize, - - /// A pool of render targets for use by the render task graph - render_target_pool: Vec<RenderTarget>, } impl ResourceCache { @@ -508,7 +469,6 @@ impl ResourceCache { font_instances, font_templates: FastHashMap::default(), image_templates: ImageTemplates::default(), - weak_fonts: WeakTable::new(), }, cached_glyph_dimensions: FastHashMap::default(), texture_cache, @@ -522,44 +482,13 @@ impl ResourceCache { pending_native_surface_updates: Vec::new(), #[cfg(feature = "capture")] capture_dirty: true, - image_templates_memory: 0, - font_templates_memory: 0, - render_target_pool: Vec::new(), } } - /// Construct a resource cache for use in unit tests. - #[cfg(test)] - pub fn new_for_testing() -> Self { - use rayon::ThreadPoolBuilder; - - let texture_cache = TextureCache::new_for_testing( - 4096, - ImageFormat::RGBA8, - ); - let workers = Arc::new(ThreadPoolBuilder::new().build().unwrap()); - let glyph_rasterizer = GlyphRasterizer::new(workers, true).unwrap(); - let cached_glyphs = GlyphCache::new(); - let font_instances = SharedFontInstanceMap::new(); - - ResourceCache::new( - texture_cache, - glyph_rasterizer, - cached_glyphs, - font_instances, - ) - } - pub fn max_texture_size(&self) -> i32 { self.texture_cache.max_texture_size() } - /// Maximum texture size before we consider it preferrable to break the texture - /// into tiles. - pub fn tiling_threshold(&self) -> i32 { - self.texture_cache.tiling_threshold() - } - pub fn enable_multithreading(&mut self, enable: bool) { self.glyph_rasterizer.enable_multithreading(enable); } @@ -585,25 +514,21 @@ impl ResourceCache { &mut self, key: RenderTaskCacheKey, gpu_cache: &mut GpuCache, - rg_builder: &mut RenderTaskGraphBuilder, - user_data: Option<[f32; 4]>, + render_tasks: &mut RenderTaskGraph, + user_data: Option<[f32; 3]>, is_opaque: bool, - parent: RenderTaskParent, - surfaces: &[SurfaceInfo], f: F, - ) -> RenderTaskId + ) -> RenderTaskCacheEntryHandle where - F: FnOnce(&mut RenderTaskGraphBuilder) -> RenderTaskId, + F: FnOnce(&mut RenderTaskGraph) -> RenderTaskId, { self.cached_render_tasks.request_render_task( key, &mut self.texture_cache, gpu_cache, - rg_builder, + render_tasks, user_data, is_opaque, - parent, - surfaces, |render_graph| Ok(f(render_graph)) ).expect("Failed to request a render task from the resource cache!") } @@ -611,7 +536,7 @@ impl ResourceCache { pub fn post_scene_building_update( &mut self, updates: Vec<ResourceUpdate>, - profile: &mut TransactionProfile, + profile_counters: &mut ResourceProfileCounters, ) { // TODO, there is potential for optimization here, by processing updates in // bulk rather than one by one (for example by sorting allocations by size or @@ -626,8 +551,7 @@ impl ResourceCache { match update { ResourceUpdate::AddImage(img) => { if let ImageData::Raw(ref bytes) = img.data { - self.image_templates_memory += bytes.len(); - profile.set(profiler::IMAGE_TEMPLATES_MEM, bytes_to_mb(self.image_templates_memory)); + profile_counters.image_templates.inc(bytes.len()); } self.add_image_template( img.key, @@ -636,7 +560,6 @@ impl ResourceCache { &img.descriptor.size.into(), img.tiling, ); - profile.set(profiler::IMAGE_TEMPLATES, self.resources.image_templates.images.len()); } ResourceUpdate::UpdateImage(img) => { self.update_image_template(img.key, img.descriptor, img.data.into(), &img.dirty_rect); @@ -664,16 +587,12 @@ impl ResourceCache { } ResourceUpdate::DeleteImage(img) => { self.delete_image_template(img); - profile.set(profiler::IMAGE_TEMPLATES, self.resources.image_templates.images.len()); - profile.set(profiler::IMAGE_TEMPLATES_MEM, bytes_to_mb(self.image_templates_memory)); } ResourceUpdate::DeleteBlobImage(img) => { self.delete_image_template(img.as_image()); } ResourceUpdate::DeleteFont(font) => { self.delete_font_template(font); - profile.set(profiler::FONT_TEMPLATES, self.resources.font_templates.len()); - profile.set(profiler::FONT_TEMPLATES_MEM, bytes_to_mb(self.font_templates_memory)); } ResourceUpdate::DeleteFontInstance(font) => { self.delete_font_instance(font); @@ -685,15 +604,13 @@ impl ResourceCache { ResourceUpdate::AddFont(font) => { match font { AddFont::Raw(id, bytes, index) => { - self.font_templates_memory += bytes.len(); - profile.set(profiler::FONT_TEMPLATES_MEM, bytes_to_mb(self.font_templates_memory)); + profile_counters.font_templates.inc(bytes.len()); self.add_font_template(id, FontTemplate::Raw(bytes, index)); } AddFont::Native(id, native_font_handle) => { self.add_font_template(id, FontTemplate::Native(native_font_handle)); } } - profile.set(profiler::FONT_TEMPLATES, self.resources.font_templates.len()); } ResourceUpdate::AddFontInstance(..) => { // Already added in ApiResources. @@ -705,7 +622,7 @@ impl ResourceCache { pub fn add_rasterized_blob_images( &mut self, images: Vec<(BlobImageRequest, BlobImageResult)>, - profile: &mut TransactionProfile, + texture_cache_profile: &mut TextureCacheProfileCounters, ) { for (request, result) in images { let data = match result { @@ -716,7 +633,7 @@ impl ResourceCache { } }; - profile.add(profiler::RASTERIZED_BLOBS_PX, data.rasterized_rect.area()); + texture_cache_profile.rasterized_blob_pixels.inc(data.rasterized_rect.area() as usize); // First make sure we have an entry for this key (using a placeholder // if need be). @@ -744,18 +661,13 @@ impl ResourceCache { pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) { // Push the new font to the font renderer, and also store // it locally for glyph metric requests. - if let FontTemplate::Raw(ref font, _) = template { - self.resources.weak_fonts.insert(Arc::downgrade(font)); - } self.glyph_rasterizer.add_font(font_key, template.clone()); self.resources.font_templates.insert(font_key, template); } pub fn delete_font_template(&mut self, font_key: FontKey) { self.glyph_rasterizer.delete_font(font_key); - if let Some(FontTemplate::Raw(data, _)) = self.resources.font_templates.remove(&font_key) { - self.font_templates_memory -= data.len(); - } + self.resources.font_templates.remove(&font_key); self.cached_glyphs .clear_fonts(|font| font.font_key == font_key); } @@ -780,7 +692,7 @@ impl ResourceCache { visible_rect: &DeviceIntRect, mut tiling: Option<TileSize>, ) { - if tiling.is_none() && Self::should_tile(self.tiling_threshold(), &descriptor, &data) { + if tiling.is_none() && Self::should_tile(self.max_texture_size(), &descriptor, &data) { // We aren't going to be able to upload a texture this big, so tile it, even // if tiling was not requested. tiling = Some(DEFAULT_TILE_SIZE); @@ -804,14 +716,14 @@ impl ResourceCache { data: CachedImageData, dirty_rect: &ImageDirtyRect, ) { - let tiling_threshold = self.tiling_threshold(); + let max_texture_size = self.max_texture_size(); let image = match self.resources.image_templates.get_mut(image_key) { Some(res) => res, None => panic!("Attempt to update non-existent image"), }; let mut tiling = image.tiling; - if tiling.is_none() && Self::should_tile(tiling_threshold, &descriptor, &data) { + if tiling.is_none() && Self::should_tile(max_texture_size, &descriptor, &data) { tiling = Some(DEFAULT_TILE_SIZE); } @@ -875,10 +787,6 @@ impl ResourceCache { match value { Some(image) => if image.data.is_blob() { - if let CachedImageData::Raw(data) = image.data { - self.image_templates_memory -= data.len(); - } - let blob_key = BlobImageKey(image_key); self.deleted_blob_keys.back_mut().unwrap().push(blob_key); self.rasterized_blob_images.remove(&blob_key); @@ -898,14 +806,11 @@ impl ResourceCache { .map_or(ImageGeneration::INVALID, |template| template.generation) } - /// Requests an image to ensure that it will be in the texture cache this frame. - /// - /// returns the size in device pixel of the image or tile. pub fn request_image( &mut self, request: ImageRequest, gpu_cache: &mut GpuCache, - ) -> DeviceIntSize { + ) { debug_assert_eq!(self.state, State::AddResources); let template = match self.resources.image_templates.get(request.key) { @@ -913,18 +818,13 @@ impl ResourceCache { None => { warn!("ERROR: Trying to render deleted / non-existent key"); debug!("key={:?}", request.key); - return DeviceIntSize::zero(); + return } }; - let size = match request.tile { - Some(tile) => compute_tile_size(&template.visible_rect, template.tiling.unwrap(), tile), - None => template.descriptor.size, - }; - // Images that don't use the texture cache can early out. if !template.data.uses_texture_cache() { - return size; + return; } let side_size = @@ -935,7 +835,7 @@ impl ResourceCache { warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!", template.descriptor.size.width, template.descriptor.size.height, template.tiling.unwrap_or(0)); self.cached_images.insert(request.key, ImageResult::Err(ImageCacheError::OverLimitSize)); - return DeviceIntSize::zero(); + return; } let storage = match self.cached_images.entry(request.key) { @@ -1000,11 +900,11 @@ impl ResourceCache { let needs_upload = self.texture_cache.request(&entry.texture_cache_handle, gpu_cache); if !needs_upload && entry.dirty_rect.is_empty() { - return size; + return } if !self.pending_image_requests.insert(request) { - return size; + return } if template.data.is_blob() { @@ -1016,8 +916,6 @@ impl ResourceCache { assert!(!missing); } - - size } fn discard_tiles_outside_visible_area( @@ -1072,6 +970,7 @@ impl ResourceCache { mut font: FontInstance, glyph_keys: &[GlyphKey], gpu_cache: &mut GpuCache, + render_task_tree: &mut RenderTaskGraph, ) { debug_assert_eq!(self.state, State::AddResources); @@ -1082,6 +981,8 @@ impl ResourceCache { glyph_keys, &mut self.texture_cache, gpu_cache, + &mut self.cached_render_tasks, + render_task_tree, ); } @@ -1130,9 +1031,6 @@ impl ResourceCache { fetch_buffer.push(GlyphFetchResult { index_in_text_run: loop_index as i32, uv_rect_address: gpu_cache.get_address(&cache_item.uv_rect_handle), - offset: DevicePoint::new(cache_item.user_data[0], cache_item.user_data[1]), - size: cache_item.uv_rect.size, - scale: cache_item.user_data[2], }); } @@ -1213,14 +1111,15 @@ impl ResourceCache { }) } - pub fn begin_frame(&mut self, stamp: FrameStamp, profile: &mut TransactionProfile) { + pub fn begin_frame(&mut self, stamp: FrameStamp) { profile_scope!("begin_frame"); debug_assert_eq!(self.state, State::Idle); self.state = State::AddResources; - self.texture_cache.begin_frame(stamp, profile); + self.texture_cache.begin_frame(stamp); self.cached_glyphs.begin_frame( stamp, &mut self.texture_cache, + &self.cached_render_tasks, &mut self.glyph_rasterizer, ); self.cached_render_tasks.begin_frame(&mut self.texture_cache); @@ -1234,7 +1133,8 @@ impl ResourceCache { pub fn block_until_all_resources_added( &mut self, gpu_cache: &mut GpuCache, - profile: &mut TransactionProfile, + render_tasks: &mut RenderTaskGraph, + texture_cache_profile: &mut TextureCacheProfileCounters, ) { profile_scope!("block_until_all_resources_added"); @@ -1245,7 +1145,9 @@ impl ResourceCache { &mut self.cached_glyphs, &mut self.texture_cache, gpu_cache, - profile, + &mut self.cached_render_tasks, + render_tasks, + texture_cache_profile, ); // Apply any updates of new / updated images (incl. blobs) to the texture cache. @@ -1354,13 +1256,12 @@ impl ResourceCache { descriptor, filter, Some(image_data), - [0.0; 4], + [0.0; 3], dirty_rect, gpu_cache, None, UvRectKind::Rect, eviction, - TargetShader::Default, ); } } @@ -1390,24 +1291,6 @@ impl ResourceCache { id } - pub fn create_compositor_external_surface( - &mut self, - is_opaque: bool, - ) -> NativeSurfaceId { - let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed) as u64); - - self.pending_native_surface_updates.push( - NativeSurfaceOperation { - details: NativeSurfaceOperationDetails::CreateExternalSurface { - id, - is_opaque, - }, - } - ); - - id - } - /// Queue up destruction of an existing native OS surface. This is used when /// a picture cache surface is dropped or resized. pub fn destroy_compositor_surface( @@ -1451,46 +1334,11 @@ impl ResourceCache { ); } - pub fn attach_compositor_external_image( - &mut self, - id: NativeSurfaceId, - external_image: ExternalImageId, - ) { - self.pending_native_surface_updates.push( - NativeSurfaceOperation { - details: NativeSurfaceOperationDetails::AttachExternalImage { - id, - external_image, - }, - } - ); - } - - - pub fn end_frame(&mut self, profile: &mut TransactionProfile) { + pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) { debug_assert_eq!(self.state, State::QueryResources); profile_scope!("end_frame"); self.state = State::Idle; - - // GC the render target pool, if it's currently > 64 MB in size. - // - // We use a simple scheme whereby we drop any texture that hasn't been used - // in the last 60 frames, until we are below the size threshold. This should - // generally prevent any sustained build-up of unused textures, unless we don't - // generate frames for a long period. This can happen when the window is - // minimized, and we probably want to flush all the WebRender caches in that case [1]. - // There is also a second "red line" memory threshold which prevents - // memory exhaustion if many render targets are allocated within a small - // number of frames. For now this is set at 320 MB (10x the normal memory threshold). - // - // [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1494099 - self.gc_render_targets( - 64 * 1024 * 1024, - 32 * 1024 * 1024 * 10, - 60, - ); - - self.texture_cache.end_frame(profile); + self.texture_cache.end_frame(texture_cache_profile); } pub fn set_debug_flags(&mut self, flags: DebugFlags) { @@ -1516,9 +1364,6 @@ impl ResourceCache { if what.contains(ClearCache::TEXTURE_CACHE) { self.texture_cache.clear_all(); } - if what.contains(ClearCache::RENDER_TARGETS) { - self.clear_render_target_pool(); - } } pub fn clear_namespace(&mut self, namespace: IdNamespace) { @@ -1547,19 +1392,11 @@ impl ResourceCache { pub fn report_memory(&self, op: VoidPtrToSizeFn) -> MemoryReport { let mut report = MemoryReport::default(); - let mut seen_fonts = std::collections::HashSet::new(); // Measure fonts. We only need the templates here, because the instances // don't have big buffers. for (_, font) in self.resources.font_templates.iter() { if let FontTemplate::Raw(ref raw, _) = font { report.fonts += unsafe { op(raw.as_ptr() as *const c_void) }; - seen_fonts.insert(raw.as_ptr()); - } - } - - for font in self.resources.weak_fonts.iter() { - if !seen_fonts.contains(&font.as_ptr()) { - report.weak_fonts += unsafe { op(font.as_ptr() as *const c_void) }; } } @@ -1610,129 +1447,6 @@ impl ResourceCache { assert!(!self.rasterized_blob_images.keys().any(&blob_f)); } } - - /// Get a render target from the pool, or allocate a new one if none are - /// currently available that match the requested parameters. - pub fn get_or_create_render_target_from_pool( - &mut self, - size: DeviceIntSize, - format: ImageFormat, - ) -> CacheTextureId { - for target in &mut self.render_target_pool { - if target.size == size && - target.format == format && - !target.is_active { - // Found a target that's not currently in use which matches. Update - // the last_frame_used for GC purposes. - target.is_active = true; - target.last_frame_used = self.current_frame_id; - return target.texture_id; - } - } - - // Need to create a new render target and add it to the pool - - let texture_id = self.texture_cache.alloc_render_target( - size, - format, - ); - - self.render_target_pool.push(RenderTarget { - size, - format, - texture_id, - is_active: true, - last_frame_used: self.current_frame_id, - }); - - texture_id - } - - /// Return a render target to the pool. - pub fn return_render_target_to_pool( - &mut self, - id: CacheTextureId, - ) { - let target = self.render_target_pool - .iter_mut() - .find(|t| t.texture_id == id) - .expect("bug: invalid render target id"); - - assert!(target.is_active); - target.is_active = false; - } - - /// Clear all current render targets (e.g. on memory pressure) - fn clear_render_target_pool( - &mut self, - ) { - for target in self.render_target_pool.drain(..) { - debug_assert!(!target.is_active); - self.texture_cache.free_render_target(target.texture_id); - } - } - - /// Garbage collect and remove old render targets from the pool that haven't - /// been used for some time. - fn gc_render_targets( - &mut self, - total_bytes_threshold: usize, - total_bytes_red_line_threshold: usize, - frames_threshold: usize, - ) { - // Get the total GPU memory size used by the current render target pool - let mut rt_pool_size_in_bytes: usize = self.render_target_pool - .iter() - .map(|t| t.size_in_bytes()) - .sum(); - - // If the total size of the pool is less than the threshold, don't bother - // trying to GC any targets - if rt_pool_size_in_bytes <= total_bytes_threshold { - return; - } - - // Sort the current pool by age, so that we remove oldest textures first - self.render_target_pool.sort_by_key(|t| t.last_frame_used); - - // We can't just use retain() because `RenderTarget` requires manual cleanup. - let mut retained_targets = SmallVec::<[RenderTarget; 8]>::new(); - - for target in self.render_target_pool.drain(..) { - assert!(!target.is_active); - - // Drop oldest textures until we are under the allowed size threshold. - // However, if it's been used in very recently, it is always kept around, - // which ensures we don't thrash texture allocations on pages that do - // require a very large render target pool and are regularly changing. - let above_red_line = rt_pool_size_in_bytes > total_bytes_red_line_threshold; - let above_threshold = rt_pool_size_in_bytes > total_bytes_threshold; - let used_recently = target.used_recently(self.current_frame_id, frames_threshold); - let used_this_frame = target.last_frame_used == self.current_frame_id; - - if !used_this_frame && (above_red_line || (above_threshold && !used_recently)) { - rt_pool_size_in_bytes -= target.size_in_bytes(); - self.texture_cache.free_render_target(target.texture_id); - } else { - retained_targets.push(target); - } - } - - self.render_target_pool.extend(retained_targets); - } - - #[cfg(test)] - pub fn validate_surfaces( - &self, - expected_surfaces: &[(i32, i32, ImageFormat)], - ) { - assert_eq!(expected_surfaces.len(), self.render_target_pool.len()); - - for (expected, surface) in expected_surfaces.iter().zip(self.render_target_pool.iter()) { - assert_eq!(DeviceIntSize::new(expected.0, expected.1), surface.size); - assert_eq!(expected.2, surface.format); - } - } } impl Drop for ResourceCache { @@ -1993,7 +1707,6 @@ impl ResourceCache { config: &CaptureConfig, ) -> Vec<PlainExternalImage> { use std::{fs, path::Path}; - use crate::texture_cache::TextureCacheConfig; info!("loading resource cache"); //TODO: instead of filling the local path to Arc<data> map as we process @@ -2017,11 +1730,13 @@ impl ResourceCache { self.current_frame_id = FrameId::INVALID; self.texture_cache = TextureCache::new( self.texture_cache.max_texture_size(), - self.texture_cache.tiling_threshold(), - self.texture_cache.default_picture_tile_size(), + self.texture_cache.max_texture_layers(), + &self.texture_cache.picture_tile_sizes(), + DeviceIntSize::zero(), self.texture_cache.color_formats(), self.texture_cache.swizzle_settings(), - &TextureCacheConfig::DEFAULT, + self.texture_cache.eviction_threshold_bytes(), + self.texture_cache.max_evictions_per_frame(), ); } } diff --git a/third_party/webrender/webrender/src/scene.rs b/third_party/webrender/webrender/src/scene.rs index edacd9bd2d9..3caf68ab603 100644 --- a/third_party/webrender/webrender/src/scene.rs +++ b/third_party/webrender/webrender/src/scene.rs @@ -4,17 +4,16 @@ use api::{BuiltDisplayList, DisplayListWithCache, ColorF, DynamicProperties, Epoch, FontRenderMode}; use api::{PipelineId, PropertyBinding, PropertyBindingId, PropertyValue, MixBlendMode, StackingContext}; +use api::MemoryReport; use api::units::*; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -use crate::render_api::MemoryReport; use crate::composite::CompositorKind; -use crate::clip::{ClipStore, ClipStoreStats}; -use crate::spatial_tree::SpatialTree; +use crate::clip::{ClipStore, ClipDataStore}; +use crate::spatial_tree::{SpatialTree, SpatialNodeIndex}; use crate::frame_builder::{ChasePrimitive, FrameBuilderConfig}; use crate::hit_test::{HitTester, HitTestingScene, HitTestingSceneStats}; -use crate::internal_types::FastHashMap; +use crate::internal_types::{FastHashMap, FastHashSet}; use crate::prim_store::{PrimitiveStore, PrimitiveStoreStats, PictureIndex}; -use crate::tile_cache::TileCacheConfig; use std::sync::Arc; /// Stores a map of the animated property bindings for the current display list. These @@ -72,7 +71,6 @@ impl SceneProperties { if *pending_properties != self.current_properties { self.transform_properties.clear(); self.float_properties.clear(); - self.color_properties.clear(); for property in &pending_properties.transforms { self.transform_properties @@ -162,6 +160,7 @@ impl SceneProperties { pub struct ScenePipeline { pub pipeline_id: PipelineId, pub viewport_size: LayoutSize, + pub content_size: LayoutSize, pub background_color: Option<ColorF>, pub display_list: DisplayListWithCache, } @@ -196,6 +195,7 @@ impl Scene { display_list: BuiltDisplayList, background_color: Option<ColorF>, viewport_size: LayoutSize, + content_size: LayoutSize, ) { // Adds a cache to the given display list. If this pipeline already had // a display list before, that display list is updated and used instead. @@ -210,6 +210,7 @@ impl Scene { let new_pipeline = ScenePipeline { pipeline_id, viewport_size, + content_size, background_color, display_list, }; @@ -269,13 +270,14 @@ pub struct BuiltScene { pub pipeline_epochs: FastHashMap<PipelineId, Epoch>, pub output_rect: DeviceIntRect, pub background_color: Option<ColorF>, + pub root_pic_index: PictureIndex, pub prim_store: PrimitiveStore, pub clip_store: ClipStore, pub config: FrameBuilderConfig, pub spatial_tree: SpatialTree, pub hit_testing_scene: Arc<HitTestingScene>, - pub tile_cache_config: TileCacheConfig, - pub tile_cache_pictures: Vec<PictureIndex>, + pub content_slice_count: usize, + pub picture_cache_spatial_nodes: FastHashSet<SpatialNodeIndex>, } impl BuiltScene { @@ -285,31 +287,29 @@ impl BuiltScene { pipeline_epochs: FastHashMap::default(), output_rect: DeviceIntRect::zero(), background_color: None, + root_pic_index: PictureIndex(0), prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()), - clip_store: ClipStore::new(&ClipStoreStats::empty()), + clip_store: ClipStore::new(), spatial_tree: SpatialTree::new(), hit_testing_scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())), - tile_cache_config: TileCacheConfig::new(0), - tile_cache_pictures: Vec::new(), + content_slice_count: 0, + picture_cache_spatial_nodes: FastHashSet::default(), config: FrameBuilderConfig { default_font_render_mode: FontRenderMode::Mono, dual_source_blending_is_enabled: true, dual_source_blending_is_supported: false, chase_primitive: ChasePrimitive::Nothing, + global_enable_picture_caching: false, testing: false, gpu_supports_fast_clears: false, gpu_supports_advanced_blend: false, advanced_blend_is_coherent: false, - gpu_supports_render_target_partial_update: true, - external_images_require_copy: false, batch_lookback_count: 0, background_color: None, compositor_kind: CompositorKind::default(), tile_size_override: None, max_depth_ids: 0, max_target_size: 0, - force_invalidation: false, - is_software: false, }, } } @@ -319,14 +319,18 @@ impl BuiltScene { SceneStats { prim_store_stats: self.prim_store.get_stats(), hit_test_stats: self.hit_testing_scene.get_stats(), - clip_store_stats: self.clip_store.get_stats(), } } - pub fn create_hit_tester(&mut self) -> HitTester { + pub fn create_hit_tester( + &mut self, + clip_data_store: &ClipDataStore, + ) -> HitTester { HitTester::new( Arc::clone(&self.hit_testing_scene), &self.spatial_tree, + &self.clip_store, + clip_data_store, ) } } @@ -338,7 +342,6 @@ impl BuiltScene { pub struct SceneStats { pub prim_store_stats: PrimitiveStoreStats, pub hit_test_stats: HitTestingSceneStats, - pub clip_store_stats: ClipStoreStats, } impl SceneStats { @@ -346,7 +349,6 @@ impl SceneStats { SceneStats { prim_store_stats: PrimitiveStoreStats::empty(), hit_test_stats: HitTestingSceneStats::empty(), - clip_store_stats: ClipStoreStats::empty(), } } } diff --git a/third_party/webrender/webrender/src/scene_builder_thread.rs b/third_party/webrender/webrender/src/scene_builder_thread.rs index c10bba793bc..dd599f45f84 100644 --- a/third_party/webrender/webrender/src/scene_builder_thread.rs +++ b/third_party/webrender/webrender/src/scene_builder_thread.rs @@ -2,20 +2,16 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{AsyncBlobImageRasterizer, BlobImageResult}; -use api::{DocumentId, PipelineId, ExternalEvent, BlobImageRequest}; -use api::{NotificationRequest, Checkpoint, IdNamespace, QualitySettings}; -use api::{PrimitiveKeyKind, SharedFontInstanceMap}; -use api::{GlyphDimensionRequest, GlyphIndexRequest}; -use api::channel::{unbounded_channel, single_msg_channel, Receiver, Sender}; +use api::{AsyncBlobImageRasterizer, BlobImageRequest, BlobImageResult}; +use api::{DocumentId, PipelineId, ApiMsg, FrameMsg, SceneMsg, ResourceUpdate, ExternalEvent}; +use api::{NotificationRequest, Checkpoint, IdNamespace, QualitySettings, TransactionMsg}; +use api::{ClipIntern, FilterDataIntern, MemoryReport, PrimitiveKeyKind, SharedFontInstanceMap}; +use api::{DocumentLayer, GlyphDimensionRequest, GlyphIndexRequest}; use api::units::*; -use crate::render_api::{ApiMsg, FrameMsg, SceneMsg, ResourceUpdate, TransactionMsg, MemoryReport}; #[cfg(feature = "capture")] use crate::capture::CaptureConfig; use crate::frame_builder::FrameBuilderConfig; use crate::scene_building::SceneBuilder; -use crate::clip::{ClipIntern, PolygonIntern}; -use crate::filterdata::FilterDataIntern; use crate::intern::{Internable, Interner, UpdateList}; use crate::internal_types::{FastHashMap, FastHashSet}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; @@ -26,16 +22,35 @@ use crate::prim_store::image::{Image, YuvImage}; use crate::prim_store::line_dec::LineDecoration; use crate::prim_store::picture::Picture; use crate::prim_store::text_run::TextRun; -use crate::profiler::{self, TransactionProfile}; use crate::render_backend::SceneView; -use crate::renderer::{FullFrameStats, PipelineInfo, SceneBuilderHooks}; +use crate::renderer::{PipelineInfo, SceneBuilderHooks}; use crate::scene::{Scene, BuiltScene, SceneStats}; use std::iter; +use std::sync::mpsc::{channel, Receiver, Sender}; +use std::mem::replace; use time::precise_time_ns; use crate::util::drain_filter; use std::thread; use std::time::Duration; +#[cfg(feature = "debugger")] +use crate::debug_server; +#[cfg(feature = "debugger")] +use api::{BuiltDisplayListIter, DisplayItem}; + +/// Various timing information that will be turned into +/// TransactionProfileCounters later down the pipeline. +#[derive(Clone, Debug)] +pub struct TransactionTimings { + pub builder_start_time_ns: u64, + pub builder_end_time_ns: u64, + pub send_time_ns: u64, + pub scene_build_start_time_ns: u64, + pub scene_build_end_time_ns: u64, + pub blob_rasterization_end_time_ns: u64, + pub display_list_len: usize, +} + fn rasterize_blobs(txn: &mut TransactionMsg, is_low_priority: bool) { profile_scope!("rasterize_blobs"); @@ -63,11 +78,12 @@ pub struct BuiltTransaction { pub removed_pipelines: Vec<(PipelineId, DocumentId)>, pub notifications: Vec<NotificationRequest>, pub interner_updates: Option<InternerUpdates>, + pub scene_build_start_time: u64, + pub scene_build_end_time: u64, pub render_frame: bool, pub invalidate_rendered_frame: bool, pub discard_frame_state_for_pipelines: Vec<PipelineId>, - pub profile: TransactionProfile, - pub frame_stats: FullFrameStats, + pub timings: Option<TransactionTimings>, } #[cfg(feature = "replay")] @@ -81,20 +97,27 @@ pub struct LoadScene { pub interners: Interners, } -/// Message to the scene builder thread. +// Message to the scene builder thread. pub enum SceneBuilderRequest { Transactions(Vec<Box<TransactionMsg>>), - AddDocument(DocumentId, DeviceIntSize), + ExternalEvent(ExternalEvent), + AddDocument(DocumentId, DeviceIntSize, DocumentLayer), DeleteDocument(DocumentId), GetGlyphDimensions(GlyphDimensionRequest), GetGlyphIndices(GlyphIndexRequest), - ClearNamespace(IdNamespace), - SimulateLongSceneBuild(u32), - ExternalEvent(ExternalEvent), WakeUp, - StopRenderBackend, - ShutDown(Option<Sender<()>>), + Stop, Flush(Sender<()>), + ClearNamespace(IdNamespace), + SimulateLongSceneBuild(u32), + SimulateLongLowPrioritySceneBuild(u32), + /// Enqueue this to inform the scene builder to pick one message from + /// backend_rx. + BackendMessage, +} + +/// Message from render backend to scene builder. +pub enum BackendSceneBuilderRequest { SetFrameBuilderConfig(FrameBuilderConfig), ReportMemory(Box<MemoryReport>, Sender<Box<MemoryReport>>), #[cfg(feature = "capture")] @@ -105,30 +128,21 @@ pub enum SceneBuilderRequest { StartCaptureSequence(CaptureConfig), #[cfg(feature = "capture")] StopCaptureSequence, + DocumentsForDebugger } // Message from scene builder to render backend. pub enum SceneBuilderResult { Transactions(Vec<Box<BuiltTransaction>>, Option<Sender<SceneSwapResult>>), + #[cfg(feature = "capture")] + CapturedTransactions(Vec<Box<BuiltTransaction>>, CaptureConfig, Option<Sender<SceneSwapResult>>), ExternalEvent(ExternalEvent), FlushComplete(Sender<()>), - DeleteDocument(DocumentId), ClearNamespace(IdNamespace), GetGlyphDimensions(GlyphDimensionRequest), GetGlyphIndices(GlyphIndexRequest), - StopRenderBackend, - ShutDown(Option<Sender<()>>), - - #[cfg(feature = "capture")] - /// The same as `Transactions`, but also supplies a `CaptureConfig` that the - /// render backend should use for sequence capture, until the next - /// `CapturedTransactions` or `StopCaptureSequence` result. - CapturedTransactions(Vec<Box<BuiltTransaction>>, CaptureConfig, Option<Sender<SceneSwapResult>>), - - #[cfg(feature = "capture")] - /// The scene builder has stopped sequence capture, so the render backend - /// should do the same. - StopCaptureSequence, + Stopped, + DocumentsForDebugger(String) } // Message from render backend to scene builder to indicate the @@ -193,7 +207,7 @@ macro_rules! declare_interners { } } -crate::enumerate_interners!(declare_interners); +enumerate_interners!(declare_interners); // A document in the scene builder contains the current scene, // as well as a persistent clip interner. This allows clips @@ -204,16 +218,21 @@ struct Document { interners: Interners, stats: SceneStats, view: SceneView, + /// A set of pipelines that the caller has requested be + /// made available as output textures. + output_pipelines: FastHashSet<PipelineId>, } impl Document { - fn new(device_rect: DeviceIntRect, device_pixel_ratio: f32) -> Self { + fn new(device_rect: DeviceIntRect, layer: DocumentLayer, device_pixel_ratio: f32) -> Self { Document { scene: Scene::new(), interners: Interners::default(), stats: SceneStats::empty(), + output_pipelines: FastHashSet::default(), view: SceneView { device_rect, + layer, device_pixel_ratio, page_zoom_factor: 1.0, quality_settings: QualitySettings::default(), @@ -225,7 +244,9 @@ impl Document { pub struct SceneBuilderThread { documents: FastHashMap<DocumentId, Document>, rx: Receiver<SceneBuilderRequest>, - tx: Sender<ApiMsg>, + backend_rx: Receiver<BackendSceneBuilderRequest>, + tx: Sender<SceneBuilderResult>, + api_tx: Sender<ApiMsg>, config: FrameBuilderConfig, default_device_pixel_ratio: f32, font_instances: SharedFontInstanceMap, @@ -239,20 +260,28 @@ pub struct SceneBuilderThread { pub struct SceneBuilderThreadChannels { rx: Receiver<SceneBuilderRequest>, - tx: Sender<ApiMsg>, + backend_rx: Receiver<BackendSceneBuilderRequest>, + tx: Sender<SceneBuilderResult>, + api_tx: Sender<ApiMsg>, } impl SceneBuilderThreadChannels { pub fn new( - tx: Sender<ApiMsg> - ) -> (Self, Sender<SceneBuilderRequest>) { - let (in_tx, in_rx) = unbounded_channel(); + api_tx: Sender<ApiMsg> + ) -> (Self, Sender<SceneBuilderRequest>, Sender<BackendSceneBuilderRequest>, Receiver<SceneBuilderResult>) { + let (in_tx, in_rx) = channel(); + let (out_tx, out_rx) = channel(); + let (backend_tx, backend_rx) = channel(); ( Self { rx: in_rx, - tx, + backend_rx, + tx: out_tx, + api_tx, }, in_tx, + backend_tx, + out_rx, ) } } @@ -266,12 +295,14 @@ impl SceneBuilderThread { hooks: Option<Box<dyn SceneBuilderHooks + Send>>, channels: SceneBuilderThreadChannels, ) -> Self { - let SceneBuilderThreadChannels { rx, tx } = channels; + let SceneBuilderThreadChannels { rx, backend_rx, tx, api_tx } = channels; Self { documents: Default::default(), rx, + backend_rx, tx, + api_tx, config, default_device_pixel_ratio, font_instances, @@ -289,7 +320,8 @@ impl SceneBuilderThread { /// We first put something in the result queue and then send a wake-up /// message to the api queue that the render backend is blocking on. pub fn send(&self, msg: SceneBuilderResult) { - self.tx.send(ApiMsg::SceneBuilderResult(msg)).unwrap(); + self.tx.send(msg).unwrap(); + let _ = self.api_tx.send(ApiMsg::WakeUp); } /// The scene builder thread's event loop. @@ -306,9 +338,9 @@ impl SceneBuilderThread { Ok(SceneBuilderRequest::Flush(tx)) => { self.send(SceneBuilderResult::FlushComplete(tx)); } - Ok(SceneBuilderRequest::Transactions(txns)) => { - let built_txns : Vec<Box<BuiltTransaction>> = txns.into_iter() - .map(|txn| self.process_transaction(*txn)) + Ok(SceneBuilderRequest::Transactions(mut txns)) => { + let built_txns : Vec<Box<BuiltTransaction>> = txns.iter_mut() + .map(|txn| self.process_transaction(txn)) .collect(); #[cfg(feature = "capture")] match built_txns.iter().any(|txn| txn.built_scene.is_some()) { @@ -317,16 +349,16 @@ impl SceneBuilderThread { } self.forward_built_transactions(built_txns); } - Ok(SceneBuilderRequest::AddDocument(document_id, initial_size)) => { + Ok(SceneBuilderRequest::AddDocument(document_id, initial_size, layer)) => { let old = self.documents.insert(document_id, Document::new( initial_size.into(), + layer, self.default_device_pixel_ratio, )); debug_assert!(old.is_none()); } Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { self.documents.remove(&document_id); - self.send(SceneBuilderResult::DeleteDocument(document_id)); } Ok(SceneBuilderRequest::ClearNamespace(id)) => { self.documents.retain(|doc_id, _doc| doc_id.namespace_id != id); @@ -336,46 +368,54 @@ impl SceneBuilderThread { self.send(SceneBuilderResult::ExternalEvent(evt)); } Ok(SceneBuilderRequest::GetGlyphDimensions(request)) => { - self.send(SceneBuilderResult::GetGlyphDimensions(request)); + self.send(SceneBuilderResult::GetGlyphDimensions(request)) } Ok(SceneBuilderRequest::GetGlyphIndices(request)) => { - self.send(SceneBuilderResult::GetGlyphIndices(request)); + self.send(SceneBuilderResult::GetGlyphIndices(request)) } - Ok(SceneBuilderRequest::StopRenderBackend) => { - self.send(SceneBuilderResult::StopRenderBackend); - } - Ok(SceneBuilderRequest::ShutDown(sync)) => { - self.send(SceneBuilderResult::ShutDown(sync)); + Ok(SceneBuilderRequest::Stop) => { + self.tx.send(SceneBuilderResult::Stopped).unwrap(); + // We don't need to send a WakeUp to api_tx because we only + // get the Stop when the RenderBackend loop is exiting. break; } Ok(SceneBuilderRequest::SimulateLongSceneBuild(time_ms)) => { self.simulate_slow_ms = time_ms } - Ok(SceneBuilderRequest::ReportMemory(mut report, tx)) => { - (*report) += self.report_memory(); - tx.send(report).unwrap(); - } - Ok(SceneBuilderRequest::SetFrameBuilderConfig(cfg)) => { - self.config = cfg; - } - #[cfg(feature = "replay")] - Ok(SceneBuilderRequest::LoadScenes(msg)) => { - self.load_scenes(msg); - } - #[cfg(feature = "capture")] - Ok(SceneBuilderRequest::SaveScene(config)) => { - self.save_scene(config); - } - #[cfg(feature = "capture")] - Ok(SceneBuilderRequest::StartCaptureSequence(config)) => { - self.start_capture_sequence(config); - } - #[cfg(feature = "capture")] - Ok(SceneBuilderRequest::StopCaptureSequence) => { - // FIXME(aosmond): clear config for frames and resource cache without scene - // rebuild? - self.capture_config = None; - self.send(SceneBuilderResult::StopCaptureSequence); + Ok(SceneBuilderRequest::SimulateLongLowPrioritySceneBuild(_)) => {} + Ok(SceneBuilderRequest::BackendMessage) => { + let msg = self.backend_rx.try_recv().unwrap(); + match msg { + BackendSceneBuilderRequest::ReportMemory(mut report, tx) => { + (*report) += self.report_memory(); + tx.send(report).unwrap(); + } + BackendSceneBuilderRequest::SetFrameBuilderConfig(cfg) => { + self.config = cfg; + } + #[cfg(feature = "replay")] + BackendSceneBuilderRequest::LoadScenes(msg) => { + self.load_scenes(msg); + } + #[cfg(feature = "capture")] + BackendSceneBuilderRequest::SaveScene(config) => { + self.save_scene(config); + } + #[cfg(feature = "capture")] + BackendSceneBuilderRequest::StartCaptureSequence(config) => { + self.start_capture_sequence(config); + } + #[cfg(feature = "capture")] + BackendSceneBuilderRequest::StopCaptureSequence => { + // FIXME(aosmond): clear config for frames and resource cache without scene + // rebuild? + self.capture_config = None; + } + BackendSceneBuilderRequest::DocumentsForDebugger => { + let json = self.get_docs_for_debugger(); + self.send(SceneBuilderResult::DocumentsForDebugger(json)); + } + } } Err(_) => { break; @@ -400,8 +440,7 @@ impl SceneBuilderThread { let interners_name = format!("interners-{}-{}", id.namespace_id.0, id.id); config.serialize_for_scene(&doc.interners, interners_name); - use crate::render_api::CaptureBits; - if config.bits.contains(CaptureBits::SCENE) { + if config.bits.contains(api::CaptureBits::SCENE) { let file_name = format!("scene-{}-{}", id.namespace_id.0, id.id); config.serialize_for_scene(&doc.scene, file_name); } @@ -413,14 +452,19 @@ impl SceneBuilderThread { for mut item in scenes { self.config = item.config; + let scene_build_start_time = precise_time_ns(); + let mut built_scene = None; let mut interner_updates = None; + let output_pipelines = FastHashSet::default(); + if item.scene.has_root_pipeline() { built_scene = Some(SceneBuilder::build( &item.scene, item.font_instances, &item.view, + &output_pipelines, &self.config, &mut item.interners, &SceneStats::empty(), @@ -438,6 +482,7 @@ impl SceneBuilderThread { interners: item.interners, stats: SceneStats::empty(), view: item.view.clone(), + output_pipelines, }, ); @@ -454,9 +499,10 @@ impl SceneBuilderThread { removed_pipelines: Vec::new(), discard_frame_state_for_pipelines: Vec::new(), notifications: Vec::new(), + scene_build_start_time, + scene_build_end_time: precise_time_ns(), interner_updates, - profile: TransactionProfile::new(), - frame_stats: FullFrameStats::default(), + timings: None, })]; self.forward_built_transactions(txns); @@ -473,8 +519,7 @@ impl SceneBuilderThread { let interners_name = format!("interners-{}-{}", id.namespace_id.0, id.id); config.serialize_for_scene(&doc.interners, interners_name); - use crate::render_api::CaptureBits; - if config.bits.contains(CaptureBits::SCENE) { + if config.bits.contains(api::CaptureBits::SCENE) { let file_name = format!("scene-{}-{}", id.namespace_id.0, id.id); config.serialize_for_scene(&doc.scene, file_name); } @@ -491,25 +536,88 @@ impl SceneBuilderThread { self.save_capture_sequence(); } + #[cfg(feature = "debugger")] + fn traverse_items<'a>( + &self, + traversal: &mut BuiltDisplayListIter<'a>, + node: &mut debug_server::TreeNode, + ) { + loop { + let subtraversal = { + let item = match traversal.next() { + Some(item) => item, + None => break, + }; + + match *item.item() { + display_item @ DisplayItem::PushStackingContext(..) => { + let mut subtraversal = item.sub_iter(); + let mut child_node = + debug_server::TreeNode::new(&display_item.debug_name().to_string()); + self.traverse_items(&mut subtraversal, &mut child_node); + node.add_child(child_node); + Some(subtraversal) + } + DisplayItem::PopStackingContext => { + return; + } + display_item => { + node.add_item(&display_item.debug_name().to_string()); + None + } + } + }; + + // If flatten_item created a sub-traversal, we need `traversal` to have the + // same state as the completed subtraversal, so we reinitialize it here. + if let Some(subtraversal) = subtraversal { + *traversal = subtraversal; + } + } + } + + #[cfg(not(feature = "debugger"))] + fn get_docs_for_debugger(&self) -> String { + String::new() + } + + #[cfg(feature = "debugger")] + fn get_docs_for_debugger(&self) -> String { + let mut docs = debug_server::DocumentList::new(); + + for (_, doc) in &self.documents { + let mut debug_doc = debug_server::TreeNode::new("document"); + + for (_, pipeline) in &doc.scene.pipelines { + let mut debug_dl = debug_server::TreeNode::new("display-list"); + self.traverse_items(&mut pipeline.display_list.iter(), &mut debug_dl); + debug_doc.add_child(debug_dl); + } + + docs.add(debug_doc); + } + + serde_json::to_string(&docs).unwrap() + } + /// Do the bulk of the work of the scene builder thread. - fn process_transaction(&mut self, mut txn: TransactionMsg) -> Box<BuiltTransaction> { + fn process_transaction(&mut self, txn: &mut TransactionMsg) -> Box<BuiltTransaction> { profile_scope!("process_transaction"); if let Some(ref hooks) = self.hooks { hooks.pre_scene_build(); } + let scene_build_start_time = precise_time_ns(); + let doc = self.documents.get_mut(&txn.document_id).unwrap(); let scene = &mut doc.scene; - let mut profile = txn.profile.take(); + let mut timings = None; - let scene_build_start = precise_time_ns(); let mut discard_frame_state_for_pipelines = Vec::new(); let mut removed_pipelines = Vec::new(); let mut rebuild_scene = false; - let mut frame_stats = FullFrameStats::default(); - for message in txn.scene_ops.drain(..) { match message { SceneMsg::UpdateEpoch(pipeline_id, epoch) => { @@ -530,21 +638,14 @@ impl SceneBuilderThread { pipeline_id, background, viewport_size, + content_size, display_list, preserve_frame_state, } => { + let display_list_len = display_list.data().len(); + let (builder_start_time_ns, builder_end_time_ns, send_time_ns) = - display_list.times(); - let content_send_time = profiler::ns_to_ms(precise_time_ns() - send_time_ns); - let dl_build_time = profiler::ns_to_ms(builder_end_time_ns - builder_start_time_ns); - profile.set(profiler::CONTENT_SEND_TIME, content_send_time); - profile.set(profiler::DISPLAY_LIST_BUILD_TIME, dl_build_time); - profile.set(profiler::DISPLAY_LIST_MEM, profiler::bytes_to_mb(display_list.data().len())); - - let (gecko_display_list_time, full_display_list) = display_list.gecko_display_list_stats(); - frame_stats.full_display_list = full_display_list; - frame_stats.gecko_display_list_time = gecko_display_list_time; - frame_stats.wr_display_list_time += dl_build_time; + display_list.times(); if self.removed_pipelines.contains(&pipeline_id) { continue; @@ -561,8 +662,19 @@ impl SceneBuilderThread { display_list, background, viewport_size, + content_size, ); + timings = Some(TransactionTimings { + builder_start_time_ns, + builder_end_time_ns, + send_time_ns, + scene_build_start_time_ns: 0, + scene_build_end_time_ns: 0, + blob_rasterization_end_time_ns: 0, + display_list_len, + }); + if !preserve_frame_state { discard_frame_state_for_pipelines.push(pipeline_id); } @@ -578,6 +690,13 @@ impl SceneBuilderThread { self.removed_pipelines.insert(pipeline_id); removed_pipelines.push((pipeline_id, txn.document_id)); } + SceneMsg::EnableFrameOutput(pipeline_id, enable) => { + if enable { + doc.output_pipelines.insert(pipeline_id); + } else { + doc.output_pipelines.remove(&pipeline_id); + } + } } } @@ -591,6 +710,7 @@ impl SceneBuilderThread { &scene, self.font_instances.clone(), &doc.view, + &doc.output_pipelines, &self.config, &mut doc.interners, &doc.stats, @@ -607,19 +727,15 @@ impl SceneBuilderThread { built_scene = Some(built); } - let scene_build_time_ms = - profiler::ns_to_ms(precise_time_ns() - scene_build_start); - profile.set(profiler::SCENE_BUILD_TIME, scene_build_time_ms); - - frame_stats.scene_build_time += scene_build_time_ms; + let scene_build_end_time = precise_time_ns(); - if !txn.blob_requests.is_empty() { - profile.start_time(profiler::BLOB_RASTERIZATION_TIME); + let is_low_priority = false; + rasterize_blobs(txn, is_low_priority); - let is_low_priority = false; - rasterize_blobs(&mut txn, is_low_priority); - - profile.end_time(profiler::BLOB_RASTERIZATION_TIME); + if let Some(timings) = timings.as_mut() { + timings.blob_rasterization_end_time_ns = precise_time_ns(); + timings.scene_build_start_time_ns = scene_build_start_time; + timings.scene_build_end_time_ns = scene_build_end_time; } drain_filter( @@ -634,20 +750,21 @@ impl SceneBuilderThread { Box::new(BuiltTransaction { document_id: txn.document_id, - render_frame: txn.generate_frame.as_bool(), + render_frame: txn.generate_frame, invalidate_rendered_frame: txn.invalidate_rendered_frame, built_scene, view: doc.view, - rasterized_blobs: txn.rasterized_blobs, - resource_updates: txn.resource_updates, - blob_rasterizer: txn.blob_rasterizer, - frame_ops: txn.frame_ops, + rasterized_blobs: replace(&mut txn.rasterized_blobs, Vec::new()), + resource_updates: replace(&mut txn.resource_updates, Vec::new()), + blob_rasterizer: replace(&mut txn.blob_rasterizer, None), + frame_ops: replace(&mut txn.frame_ops, Vec::new()), removed_pipelines, discard_frame_state_for_pipelines, - notifications: txn.notifications, + notifications: replace(&mut txn.notifications, Vec::new()), interner_updates, - profile, - frame_stats, + scene_build_start_time, + scene_build_end_time, + timings, }) } @@ -670,9 +787,9 @@ impl SceneBuilderThread { .flatten().collect(), }; - let (tx, rx) = single_msg_channel(); + let (tx, rx) = channel(); let txn = txns.iter().find(|txn| txn.built_scene.is_some()).unwrap(); - hooks.pre_scene_swap((txn.profile.get(profiler::SCENE_BUILD_TIME).unwrap() * 1000000.0) as u64); + hooks.pre_scene_swap(txn.scene_build_end_time - txn.scene_build_start_time); (Some(info), Some(tx), Some(rx)) } else { @@ -695,12 +812,14 @@ impl SceneBuilderThread { #[cfg(feature = "capture")] match self.capture_config { - Some(ref config) => self.send(SceneBuilderResult::CapturedTransactions(txns, config.clone(), result_tx)), - None => self.send(SceneBuilderResult::Transactions(txns, result_tx)), - }; + Some(ref config) => self.tx.send(SceneBuilderResult::CapturedTransactions(txns, config.clone(), result_tx)).unwrap(), + None => self.tx.send(SceneBuilderResult::Transactions(txns, result_tx)).unwrap(), + } #[cfg(not(feature = "capture"))] - self.send(SceneBuilderResult::Transactions(txns, result_tx)); + self.tx.send(SceneBuilderResult::Transactions(txns, result_tx)).unwrap(); + + let _ = self.api_tx.send(ApiMsg::WakeUp); if let Some(pipeline_info) = pipeline_info { // Block until the swap is done, then invoke the hook. @@ -742,6 +861,7 @@ impl SceneBuilderThread { pub struct LowPrioritySceneBuilderThread { pub rx: Receiver<SceneBuilderRequest>, pub tx: Sender<SceneBuilderRequest>, + pub simulate_slow_ms: u32, } impl LowPrioritySceneBuilderThread { @@ -754,10 +874,19 @@ impl LowPrioritySceneBuilderThread { .collect(); self.tx.send(SceneBuilderRequest::Transactions(txns)).unwrap(); } - Ok(SceneBuilderRequest::ShutDown(sync)) => { - self.tx.send(SceneBuilderRequest::ShutDown(sync)).unwrap(); + Ok(SceneBuilderRequest::AddDocument(id, size, layer)) => { + self.tx.send(SceneBuilderRequest::AddDocument(id, size, layer)).unwrap(); + } + Ok(SceneBuilderRequest::DeleteDocument(document_id)) => { + self.tx.send(SceneBuilderRequest::DeleteDocument(document_id)).unwrap(); + } + Ok(SceneBuilderRequest::Stop) => { + self.tx.send(SceneBuilderRequest::Stop).unwrap(); break; } + Ok(SceneBuilderRequest::SimulateLongLowPrioritySceneBuild(time_ms)) => { + self.simulate_slow_ms = time_ms; + } Ok(other) => { self.tx.send(other).unwrap(); } @@ -773,6 +902,10 @@ impl LowPrioritySceneBuilderThread { rasterize_blobs(&mut txn, is_low_priority); txn.blob_requests = Vec::new(); + if self.simulate_slow_ms > 0 { + thread::sleep(Duration::from_millis(self.simulate_slow_ms as u64)); + } + txn } } diff --git a/third_party/webrender/webrender/src/scene_building.rs b/third_party/webrender/webrender/src/scene_building.rs index 44b75e6d847..bfc466640bc 100644 --- a/third_party/webrender/webrender/src/scene_building.rs +++ b/third_party/webrender/webrender/src/scene_building.rs @@ -2,86 +2,46 @@ * 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/. */ -//! # Scene building -//! -//! Scene building is the phase during which display lists, a representation built for -//! serialization, are turned into a scene, webrender's internal representation that is -//! suited for rendering frames. -//! -//! This phase is happening asynchronously on the scene builder thread. -//! -//! # General algorithm -//! -//! The important aspects of scene building are: -//! - Building up primitive lists (much of the cost of scene building goes here). -//! - Creating pictures for content that needs to be rendered into a surface, be it so that -//! filters can be applied or for caching purposes. -//! - Maintaining a temporary stack of stacking contexts to keep track of some of the -//! drawing states. -//! - Stitching multiple display lists which reference each other (without cycles) into -//! a single scene (see build_reference_frame). -//! - Interning, which detects when some of the retained state stays the same between display -//! lists. -//! -//! The scene builder linearly traverses the serialized display list which is naturally -//! ordered back-to-front, accumulating primitives in the top-most stacking context's -//! primitive list. -//! At the end of each stacking context (see pop_stacking_context), its primitive list is -//! either handed over to a picture if one is created, or it is concatenated into the parent -//! stacking context's primitive list. -//! -//! The flow of the algorithm is mostly linear except when handling: -//! - shadow stacks (see push_shadow and pop_all_shadows), -//! - backdrop filters (see add_backdrop_filter) -//! - use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter, PrimitiveFlags}; use api::{ClipId, ColorF, CommonItemProperties, ComplexClipRegion, ComponentTransferFuncType, RasterSpace}; use api::{DisplayItem, DisplayItemRef, ExtendMode, ExternalScrollId, FilterData, SharedFontInstanceMap}; use api::{FilterOp, FilterPrimitive, FontInstanceKey, FontSize, GlyphInstance, GlyphOptions, GradientStop}; use api::{IframeDisplayItem, ImageKey, ImageRendering, ItemRange, ColorDepth, QualitySettings}; use api::{LineOrientation, LineStyle, NinePatchBorderSource, PipelineId, MixBlendMode, StackingContextFlags}; -use api::{PropertyBinding, ReferenceFrameKind, ScrollFrameDisplayItem, ScrollSensitivity}; -use api::{Shadow, SpaceAndClipInfo, SpatialId, StickyFrameDisplayItem, ImageMask, ItemTag}; +use api::{PropertyBinding, ReferenceFrame, ReferenceFrameKind, ScrollFrameDisplayItem, ScrollSensitivity}; +use api::{Shadow, SpaceAndClipInfo, SpatialId, StackingContext, StickyFrameDisplayItem, ImageMask}; use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, ColorRange, YuvData, TempFilterData}; -use api::{ReferenceTransformBinding, Rotation, FillRule}; +use api::image_tiling::simplify_repeated_primitive; use api::units::*; -use crate::image_tiling::simplify_repeated_primitive; use crate::clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemKeyKind}; -use crate::clip::{ClipInternData, ClipNodeKind, ClipInstance, SceneClipInstance}; -use crate::clip::{PolygonDataHandle}; -use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialTree, SpatialNodeIndex, StaticCoordinateSystemId}; +use crate::clip::{ClipInternData, ClipNodeKind, ClipInstance}; +use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialTree, SpatialNodeIndex}; use crate::frame_builder::{ChasePrimitive, FrameBuilderConfig}; use crate::glyph_rasterizer::FontInstance; -use crate::hit_test::HitTestingScene; +use crate::hit_test::{HitTestingItem, HitTestingScene}; use crate::intern::Interner; -use crate::internal_types::{FastHashMap, LayoutPrimitiveInfo, Filter}; +use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo, Filter}; use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureOptions}; -use crate::picture::{BlitReason, OrderedPictureChild, PrimitiveList}; -use crate::prim_store::{PrimitiveInstance, register_prim_chase_id}; +use crate::picture::{BlitReason, OrderedPictureChild, PrimitiveList, TileCacheInstance, ClusterFlags}; +use crate::prim_store::PrimitiveInstance; use crate::prim_store::{PrimitiveInstanceKind, NinePatchDescriptor, PrimitiveStore}; use crate::prim_store::{InternablePrimitive, SegmentInstanceIndex, PictureIndex}; -use crate::prim_store::PolygonKey; +use crate::prim_store::{register_prim_chase_id, get_line_decoration_size}; +use crate::prim_store::{SpaceSnapper}; use crate::prim_store::backdrop::Backdrop; use crate::prim_store::borders::{ImageBorder, NormalBorderPrim}; -use crate::prim_store::gradient::{ - GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient, - ConicGradientParams, optimize_radial_gradient, apply_gradient_local_clip, - optimize_linear_gradient, -}; +use crate::prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient, ConicGradientParams}; use crate::prim_store::image::{Image, YuvImage}; -use crate::prim_store::line_dec::{LineDecoration, LineDecorationCacheKey, get_line_decoration_size}; +use crate::prim_store::line_dec::{LineDecoration, LineDecorationCacheKey}; use crate::prim_store::picture::{Picture, PictureCompositeKey, PictureKey}; use crate::prim_store::text_run::TextRun; use crate::render_backend::SceneView; use crate::resource_cache::ImageRequest; -use crate::scene::{Scene, ScenePipeline, BuiltScene, SceneStats, StackingContextHelpers}; +use crate::scene::{Scene, BuiltScene, SceneStats, StackingContextHelpers}; use crate::scene_builder_thread::Interners; -use crate::space::SpaceSnapper; use crate::spatial_node::{StickyFrameInfo, ScrollFrameKind}; -use crate::tile_cache::TileCacheBuilder; use euclid::approxeq::ApproxEq; -use std::{f32, mem, usize}; +use std::{f32, mem, usize, ops}; use std::collections::vec_deque::VecDeque; use std::sync::Arc; use crate::util::{MaxRect, VecHelper}; @@ -242,204 +202,31 @@ impl CompositeOps { self.filter_primitives.is_empty() && self.mix_blend_mode.is_none() } - - /// Returns true if this CompositeOps contains any filters that affect - /// the content (false if no filters, or filters are all no-ops). - fn has_valid_filters(&self) -> bool { - // For each filter, create a new image with that composite mode. - let mut current_filter_data_index = 0; - for filter in &self.filters { - match filter { - Filter::ComponentTransfer => { - let filter_data = - &self.filter_datas[current_filter_data_index]; - let filter_data = filter_data.sanitize(); - current_filter_data_index = current_filter_data_index + 1; - if filter_data.is_identity() { - continue - } else { - return true; - } - } - _ => { - if filter.is_noop() { - continue; - } else { - return true; - } - } - } - } - - if !self.filter_primitives.is_empty() { - return true; - } - - false - } -} - -/// Represents the current input for a picture chain builder (either a -/// prim list from the stacking context, or a wrapped picture instance). -enum PictureSource { - PrimitiveList { - prim_list: PrimitiveList, - }, - WrappedPicture { - instance: PrimitiveInstance, - }, -} - -/// Helper struct to build picture chains during scene building from -/// a flattened stacking context struct. -struct PictureChainBuilder { - /// The current input source for the next picture - current: PictureSource, - - /// Positioning node for this picture chain - spatial_node_index: SpatialNodeIndex, - /// Prim flags for any pictures in this chain - flags: PrimitiveFlags, -} - -impl PictureChainBuilder { - /// Create a new picture chain builder, from a primitive list - fn from_prim_list( - prim_list: PrimitiveList, - flags: PrimitiveFlags, - spatial_node_index: SpatialNodeIndex, - ) -> Self { - PictureChainBuilder { - current: PictureSource::PrimitiveList { - prim_list, - }, - spatial_node_index, - flags, - } - } - - /// Create a new picture chain builder, from a picture wrapper instance - fn from_instance( - instance: PrimitiveInstance, - flags: PrimitiveFlags, - spatial_node_index: SpatialNodeIndex, - ) -> Self { - PictureChainBuilder { - current: PictureSource::WrappedPicture { - instance, - }, - flags, - spatial_node_index, - } - } - - /// Wrap the existing content with a new picture with the given parameters - #[must_use] - fn add_picture( - self, - composite_mode: PictureCompositeMode, - context_3d: Picture3DContext<OrderedPictureChild>, - options: PictureOptions, - interners: &mut Interners, - prim_store: &mut PrimitiveStore, - ) -> PictureChainBuilder { - let prim_list = match self.current { - PictureSource::PrimitiveList { prim_list } => { - prim_list - } - PictureSource::WrappedPicture { instance } => { - let mut prim_list = PrimitiveList::empty(); - - prim_list.add_prim( - instance, - LayoutRect::zero(), - self.spatial_node_index, - self.flags, - ); - - prim_list - } - }; - - let pic_index = PictureIndex(prim_store.pictures - .alloc() - .init(PicturePrimitive::new_image( - Some(composite_mode.clone()), - context_3d, - true, - self.flags, - prim_list, - self.spatial_node_index, - options, - )) - ); - - let instance = create_prim_instance( - pic_index, - Some(composite_mode).into(), - ClipChainId::NONE, - interners, - ); - - PictureChainBuilder { - current: PictureSource::WrappedPicture { - instance, - }, - spatial_node_index: self.spatial_node_index, - flags: self.flags, - } - } - - /// Finish building this picture chain. Set the clip chain on the outermost picture - fn finalize( - self, - clip_chain_id: ClipChainId, - interners: &mut Interners, - prim_store: &mut PrimitiveStore, - ) -> PrimitiveInstance { - match self.current { - PictureSource::WrappedPicture { mut instance } => { - instance.clip_set.clip_chain_id = clip_chain_id; - instance - } - PictureSource::PrimitiveList { prim_list } => { - // If no picture was created for this stacking context, create a - // pass-through wrapper now. This is only needed in 1-2 edge cases - // now, and will be removed as a follow up. - let pic_index = PictureIndex(prim_store.pictures - .alloc() - .init(PicturePrimitive::new_image( - None, - Picture3DContext::Out, - true, - self.flags, - prim_list, - self.spatial_node_index, - PictureOptions::default(), - )) - ); - - create_prim_instance( - pic_index, - None.into(), - clip_chain_id, - interners, - ) - } - } - } } bitflags! { /// Slice flags pub struct SliceFlags : u8 { - /// Slice created by a prim that has PrimitiveFlags::IS_SCROLLBAR_CONTAINER + /// Slice created by a cluster that has ClusterFlags::SCROLLBAR_CONTAINER const IS_SCROLLBAR = 1; - /// Represents a mix-blend container (can't split out compositor surfaces in this slice) - const IS_BLEND_CONTAINER = 2; } } +/// Information about a set of primitive clusters that will form a picture cache slice. +struct Slice { + /// The spatial node root of the picture cache. If this is None, the slice + /// will not be cached and instead drawn directly to the parent surface. This + /// is a temporary measure until we enable caching all slices. + cache_scroll_root: Option<SpatialNodeIndex>, + /// List of primitive clusters that make up this slice + prim_list: PrimitiveList, + /// A list of clips that are shared by all primitives in the slice. These can be + /// filtered out and applied when the tile cache is composited rather than per-item. + shared_clips: Option<Vec<ClipInstance>>, + /// Various flags describing properties of this slice + pub flags: SliceFlags, +} + /// A structure that converts a serialized display list into a form that WebRender /// can use to later build a frame. This structure produces a BuiltScene. Public /// members are typically those that are destructured into the BuiltScene. @@ -450,6 +237,10 @@ pub struct SceneBuilder<'a> { /// The map of all font instances. font_instances: SharedFontInstanceMap, + /// A set of pipelines that the caller has requested be made available as + /// output textures. + output_pipelines: &'a FastHashSet<PipelineId>, + /// The data structure that converts between ClipId/SpatialId and the various /// index types that the SpatialTree uses. id_to_index_mapper: NodeIdToIndexMapper, @@ -457,12 +248,6 @@ pub struct SceneBuilder<'a> { /// A stack of stacking context properties. sc_stack: Vec<FlattenedStackingContext>, - /// Stack of spatial node indices forming containing block for 3d contexts - containing_block_stack: Vec<SpatialNodeIndex>, - - /// Stack of requested raster spaces for stacking contexts - raster_space_stack: Vec<RasterSpace>, - /// Maintains state for any currently active shadows pending_shadow_items: VecDeque<ShadowItem>, @@ -485,31 +270,36 @@ pub struct SceneBuilder<'a> { /// Reference to the set of data that is interned across display lists. interners: &'a mut Interners, + /// The root picture index for this builder. This is the picture + /// to start the culling phase from. + pub root_pic_index: PictureIndex, + /// Helper struct to map stacking context coords <-> reference frame coords. rf_mapper: ReferenceFrameMapper, /// Helper struct to map spatial nodes to external scroll offsets. external_scroll_mapper: ScrollOffsetMapper, + /// If true, picture caching setup has already been completed. + picture_caching_initialized: bool, + /// The current recursion depth of iframes encountered. Used to restrict picture /// caching slices to only the top-level content frame. - iframe_size: Vec<LayoutSize>, + iframe_depth: usize, - /// Clip-chain for root iframes applied to any tile caches created within this iframe - root_iframe_clip: Option<ClipChainId>, + /// The number of picture cache slices that were created for content. + content_slice_count: usize, + + /// A set of any spatial nodes that are attached to either a picture cache + /// root, or a clip node on the picture cache primitive. These are used + /// to detect cases where picture caching must be disabled. This is mostly + /// a temporary workaround for some existing wrench tests. I don't think + /// Gecko ever produces picture cache slices with complex transforms, so + /// in future we should prevent this in the public API and remove this hack. + picture_cache_spatial_nodes: FastHashSet<SpatialNodeIndex>, /// The current quality / performance settings for this scene. quality_settings: QualitySettings, - - /// Maintains state about the list of tile caches being built for this scene. - tile_cache_builder: TileCacheBuilder, - - /// A helper struct to snap local rects in device space. During frame - /// building we may establish new raster roots, however typically that is in - /// cases where we won't be applying snapping (e.g. has perspective), or in - /// edge cases (e.g. SVG filter) where we can accept slightly incorrect - /// behaviour in favour of getting the common case right. - snap_to_device: SpaceSnapper, } impl<'a> SceneBuilder<'a> { @@ -517,6 +307,7 @@ impl<'a> SceneBuilder<'a> { scene: &Scene, font_instances: SharedFontInstanceMap, view: &SceneView, + output_pipelines: &FastHashSet<PipelineId>, frame_builder_config: &FrameBuilderConfig, interners: &mut Interners, stats: &SceneStats, @@ -531,47 +322,82 @@ impl<'a> SceneBuilder<'a> { .background_color .and_then(|color| if color.a > 0.0 { Some(color) } else { None }); - let device_pixel_scale = view.accumulated_scale_factor_for_snapping(); - let spatial_tree = SpatialTree::new(); - - let snap_to_device = SpaceSnapper::new( - ROOT_SPATIAL_NODE_INDEX, - device_pixel_scale, - ); - let mut builder = SceneBuilder { scene, - spatial_tree, + spatial_tree: SpatialTree::new(), font_instances, config: *frame_builder_config, + output_pipelines, id_to_index_mapper: NodeIdToIndexMapper::default(), hit_testing_scene: HitTestingScene::new(&stats.hit_test_stats), pending_shadow_items: VecDeque::new(), sc_stack: Vec::new(), - containing_block_stack: Vec::new(), - raster_space_stack: vec![RasterSpace::Screen], prim_store: PrimitiveStore::new(&stats.prim_store_stats), - clip_store: ClipStore::new(&stats.clip_store_stats), + clip_store: ClipStore::new(), interners, + root_pic_index: PictureIndex(0), rf_mapper: ReferenceFrameMapper::new(), external_scroll_mapper: ScrollOffsetMapper::new(), - iframe_size: Vec::new(), - root_iframe_clip: None, + picture_caching_initialized: false, + iframe_depth: 0, + content_slice_count: 0, + picture_cache_spatial_nodes: FastHashSet::default(), quality_settings: view.quality_settings, - tile_cache_builder: TileCacheBuilder::new(), - snap_to_device, }; - builder.build_all(&root_pipeline); + let device_pixel_scale = view.accumulated_scale_factor_for_snapping(); - // Construct the picture cache primitive instance(s) from the tile cache builder - let (tile_cache_config, tile_cache_pictures) = builder.tile_cache_builder.build( - &builder.config, - &mut builder.clip_store, - &mut builder.prim_store, - builder.interners, + builder.clip_store.register_clip_template( + ClipId::root(root_pipeline_id), + ClipId::root(root_pipeline_id), + &[], ); + builder.clip_store.push_clip_root( + Some(ClipId::root(root_pipeline_id)), + false, + ); + + builder.push_root( + root_pipeline_id, + &root_pipeline.viewport_size, + &root_pipeline.content_size, + device_pixel_scale, + ); + + // In order to ensure we have a single root stacking context for the + // entire display list, we push one here. Gecko _almost_ wraps its + // entire display list within a single stacking context, but sometimes + // appends a few extra items in AddWindowOverlayWebRenderCommands. We + // could fix it there, but it's easier and more robust for WebRender + // to just ensure there's a context on the stack whenever we append + // primitives (since otherwise we'd panic). + // + // Note that we don't do this for iframes, even if they're pipeline + // roots, because they should be entirely contained within a stacking + // context, and we probably wouldn't crash if they weren't. + builder.push_stacking_context( + root_pipeline.pipeline_id, + CompositeOps::default(), + TransformStyle::Flat, + /* prim_flags = */ PrimitiveFlags::IS_BACKFACE_VISIBLE, + ROOT_SPATIAL_NODE_INDEX, + None, + RasterSpace::Screen, + StackingContextFlags::IS_BACKDROP_ROOT, + device_pixel_scale, + ); + + builder.build_items( + &mut root_pipeline.display_list.iter(), + root_pipeline.pipeline_id, + ); + + builder.pop_stacking_context(); + builder.clip_store.pop_clip_root(); + + debug_assert!(builder.sc_stack.is_empty()); + BuiltScene { has_root_pipeline: scene.has_root_pipeline(), pipeline_epochs: scene.pipeline_epochs.clone(), @@ -581,9 +407,10 @@ impl<'a> SceneBuilder<'a> { spatial_tree: builder.spatial_tree, prim_store: builder.prim_store, clip_store: builder.clip_store, + root_pic_index: builder.root_pic_index, config: builder.config, - tile_cache_config, - tile_cache_pictures, + content_slice_count: builder.content_slice_count, + picture_cache_spatial_nodes: builder.picture_cache_spatial_nodes, } } @@ -609,250 +436,219 @@ impl<'a> SceneBuilder<'a> { rf_offset + scroll_offset } - fn build_all(&mut self, root_pipeline: &ScenePipeline) { - enum ContextKind<'a> { - Root, - StackingContext { - sc_info: StackingContextInfo, - }, - ReferenceFrame, - Iframe { - parent_traversal: BuiltDisplayListIter<'a>, - } - } - struct BuildContext<'a> { - pipeline_id: PipelineId, - kind: ContextKind<'a>, + /// Figure out the shape of the display list, and wrap various primitive clusters + /// into tile cache primitive instances. + fn setup_picture_caching( + &mut self, + main_prim_list: &mut PrimitiveList, + ) { + if !self.config.global_enable_picture_caching { + return; } - let root_clip_id = ClipId::root(root_pipeline.pipeline_id); - self.clip_store.register_clip_template(root_clip_id, root_clip_id, &[]); - self.clip_store.push_clip_root(Some(root_clip_id), false); - self.push_root( - root_pipeline.pipeline_id, - &root_pipeline.viewport_size, - ); - - let mut stack = vec![BuildContext { - pipeline_id: root_pipeline.pipeline_id, - kind: ContextKind::Root, - }]; - let mut traversal = root_pipeline.display_list.iter(); + // Ensure that setup_picture_caching has executed + debug_assert!(self.picture_caching_initialized); - 'outer: while let Some(bc) = stack.pop() { - loop { - let item = match traversal.next() { - Some(item) => item, - None => break, - }; + // Unconditionally insert a marker to create a picture cache slice on the + // first cluster. This handles implicit picture caches, and also the common + // case, by allowing the root / background primitives to be cached in a slice. + if let Some(cluster) = main_prim_list.clusters.first_mut() { + cluster.flags.insert(ClusterFlags::CREATE_PICTURE_CACHE_PRE); + } - match item.item() { - DisplayItem::PushStackingContext(ref info) => { - profile_scope!("build_stacking_context"); - let spatial_node_index = self.get_space(info.spatial_id); - let mut subtraversal = item.sub_iter(); - // Avoid doing unnecessary work for empty stacking contexts. - if subtraversal.current_stacking_context_empty() { - subtraversal.skip_current_stacking_context(); - traversal = subtraversal; - continue; - } + // List of slices that have been found + let mut slices: Vec<Slice> = Vec::new(); + // Tracker for whether a new slice should be created + let mut create_slice = true; + // The clips found the last time we traversed a set of clip chains. Stored and cleared + // here to avoid constant allocations. + let mut prim_clips = Vec::new(); + // If true, the cache is out of date and needs to be rebuilt. + let mut update_shared_clips = true; + // The last prim clip chain we build prim_clips for. + let mut last_prim_clip_chain_id = ClipChainId::NONE; + + // Walk the supplied top level of clusters, slicing into slices as appropriate + for cluster in main_prim_list.clusters.drain(..) { + // Check if this cluster requires a new slice + create_slice |= cluster.flags.intersects( + ClusterFlags::CREATE_PICTURE_CACHE_PRE | ClusterFlags::IS_CLEAR_PRIMITIVE + ); - let composition_operations = CompositeOps::new( - filter_ops_for_compositing(item.filters()), - filter_datas_for_compositing(item.filter_datas()), - filter_primitives_for_compositing(item.filter_primitives()), - info.stacking_context.mix_blend_mode_for_compositing(), - ); + if create_slice { + let slice_flags = if cluster.flags.contains(ClusterFlags::SCROLLBAR_CONTAINER) { + SliceFlags::IS_SCROLLBAR + } else { + SliceFlags::empty() + }; + let slice = Slice { + cache_scroll_root: cluster.cache_scroll_root, + prim_list: PrimitiveList::empty(), + shared_clips: None, + flags: slice_flags + }; - let sc_info = self.push_stacking_context( - composition_operations, - info.stacking_context.transform_style, - info.prim_flags, - spatial_node_index, - info.stacking_context.clip_id, - info.stacking_context.raster_space, - info.stacking_context.flags, - bc.pipeline_id, - ); + // Open up clip chains on the stack on the new slice + slices.push(slice); + create_slice = false; + } - self.rf_mapper.push_offset(info.origin.to_vector()); - let new_context = BuildContext { - pipeline_id: bc.pipeline_id, - kind: ContextKind::StackingContext { - sc_info, - }, - }; - stack.push(bc); - stack.push(new_context); + // Step through each prim instance, in order to collect shared clips for the slice. + for instance in &cluster.prim_instances { + // If the primitive clip chain is different, then we need to rebuild prim_clips. + update_shared_clips |= last_prim_clip_chain_id != instance.clip_chain_id; + last_prim_clip_chain_id = instance.clip_chain_id; + + if update_shared_clips { + prim_clips.clear(); + // Update the list of clips that apply to this primitive instance + add_clips( + instance.clip_chain_id, + &mut prim_clips, + &self.clip_store, + &self.interners, + ); + } - subtraversal.merge_debug_stats_from(&mut traversal); - traversal = subtraversal; - continue 'outer; + // If there are no shared clips set for this slice, the shared clips are just + // the current clips set. Otherwise, the shared clips are those that are + // in both the current shared list and the clips list for this primitive. + match slices.last_mut().unwrap().shared_clips { + Some(ref mut shared_clips) => { + if update_shared_clips { + shared_clips.retain(|h1: &ClipInstance| { + let uid = h1.handle.uid(); + prim_clips.iter().any(|h2| { + uid == h2.handle.uid() && + h1.spatial_node_index == h2.spatial_node_index + }) + }); + } } - DisplayItem::PushReferenceFrame(ref info) => { - profile_scope!("build_reference_frame"); - let parent_space = self.get_space(info.parent_spatial_id); - let mut subtraversal = item.sub_iter(); - let current_offset = self.current_offset(parent_space); - - let transform = match info.reference_frame.transform { - ReferenceTransformBinding::Static { binding } => binding, - ReferenceTransformBinding::Computed { scale_from, vertical_flip, rotation } => { - let content_size = &self.iframe_size.last().unwrap(); - - let mut transform = if let Some(scale_from) = scale_from { - // If we have a 90/270 degree rotation, then scale_from - // and content_size are in different coordinate spaces and - // we need to swap width/height for them to be correct. - match rotation { - Rotation::Degree0 | - Rotation::Degree180 => { - LayoutTransform::scale( - content_size.width / scale_from.width, - content_size.height / scale_from.height, - 1.0 - ) - }, - Rotation::Degree90 | - Rotation::Degree270 => { - LayoutTransform::scale( - content_size.height / scale_from.width, - content_size.width / scale_from.height, - 1.0 - ) - - } - } - } else { - LayoutTransform::identity() - }; - - if vertical_flip { - let content_size = &self.iframe_size.last().unwrap(); - transform = transform - .then_translate(LayoutVector3D::new(0.0, content_size.height, 0.0)) - .pre_scale(1.0, -1.0, 1.0); - } - - let rotate = rotation.to_matrix(**content_size); - let transform = transform.then(&rotate); + ref mut shared_clips @ None => { + *shared_clips = Some(prim_clips.clone()); + } + } - PropertyBinding::Value(transform) - }, - }; + update_shared_clips = false; + } - self.push_reference_frame( - info.reference_frame.id, - Some(parent_space), - bc.pipeline_id, - info.reference_frame.transform_style, - transform, - info.reference_frame.kind, - current_offset + info.origin.to_vector(), - ); + // If this cluster creates a slice after, then note that for next cluster + create_slice |= cluster.flags.intersects( + ClusterFlags::CREATE_PICTURE_CACHE_POST | ClusterFlags::IS_CLEAR_PRIMITIVE + ); - self.rf_mapper.push_scope(); - let new_context = BuildContext { - pipeline_id: bc.pipeline_id, - kind: ContextKind::ReferenceFrame, - }; - stack.push(bc); - stack.push(new_context); + // Finally, add this cluster to the current slice + slices.last_mut().unwrap().prim_list.add_cluster(cluster); + } - subtraversal.merge_debug_stats_from(&mut traversal); - traversal = subtraversal; - continue 'outer; - } - DisplayItem::PopReferenceFrame | - DisplayItem::PopStackingContext => break, - DisplayItem::Iframe(ref info) => { - profile_scope!("iframe"); - - let space = self.get_space(info.space_and_clip.spatial_id); - let (size, subtraversal) = match self.push_iframe(info, space) { - Some(pair) => pair, - None => continue, - }; + // Step through the slices, creating picture cache wrapper instances. + for (slice_index, slice) in slices.drain(..).enumerate() { + let background_color = if slice_index == 0 { + self.config.background_color + } else { + None + }; - // Get a clip-chain id for the root clip for this pipeline. We will - // add that as an unconditional clip to any tile cache created within - // this iframe. This ensures these clips are handled by the tile cache - // compositing code, which is more efficient and accurate than applying - // these clips individually to each primitive. - let clip_id = ClipId::root(info.pipeline_id); - let clip_chain_id = self.get_clip_chain(clip_id); - - // If this is a root iframe, force a new tile cache both before and after - // adding primitives for this iframe. - if self.iframe_size.is_empty() { - self.add_tile_cache_barrier_if_needed(SliceFlags::empty()); - assert!(self.root_iframe_clip.is_none()); - self.root_iframe_clip = Some(clip_chain_id); - } + // If the cluster specifies a scroll root, use it. Otherwise, + // just cache assuming no scrolling takes place. Even if that's + // not true, we still get caching benefits for any changes that + // occur while not scrolling (such as animation, video etc); + let scroll_root = slice.cache_scroll_root.unwrap_or(ROOT_SPATIAL_NODE_INDEX); + + let instance = create_tile_cache( + slice_index, + slice.flags, + scroll_root, + slice.prim_list, + background_color, + slice.shared_clips.unwrap_or_else(Vec::new), + &mut self.interners, + &mut self.prim_store, + &mut self.clip_store, + &mut self.picture_cache_spatial_nodes, + &self.config, + ); - self.rf_mapper.push_scope(); - self.iframe_size.push(size); + main_prim_list.add_prim( + instance, + LayoutRect::zero(), + scroll_root, + PrimitiveFlags::IS_BACKFACE_VISIBLE, + ); + } + } - let new_context = BuildContext { - pipeline_id: info.pipeline_id, - kind: ContextKind::Iframe { - parent_traversal: mem::replace(&mut traversal, subtraversal), - }, - }; - stack.push(bc); - stack.push(new_context); - continue 'outer; - } - _ => { - self.build_item(item, bc.pipeline_id); - } - }; - } + fn build_items( + &mut self, + traversal: &mut BuiltDisplayListIter<'a>, + pipeline_id: PipelineId, + ) { + loop { + let item = match traversal.next() { + Some(item) => item, + None => break, + }; - match bc.kind { - ContextKind::Root => {} - ContextKind::StackingContext { sc_info } => { - self.rf_mapper.pop_offset(); - self.pop_stacking_context(sc_info); + let subtraversal = match item.item() { + DisplayItem::PushStackingContext(ref info) => { + let space = self.get_space(info.spatial_id); + let mut subtraversal = item.sub_iter(); + self.build_stacking_context( + &mut subtraversal, + pipeline_id, + &info.stacking_context, + space, + info.origin, + item.filters(), + &item.filter_datas(), + item.filter_primitives(), + info.prim_flags, + ); + Some(subtraversal) } - ContextKind::ReferenceFrame => { - self.rf_mapper.pop_scope(); + DisplayItem::PushReferenceFrame(ref info) => { + let parent_space = self.get_space(info.parent_spatial_id); + let mut subtraversal = item.sub_iter(); + self.build_reference_frame( + &mut subtraversal, + pipeline_id, + parent_space, + info.origin, + &info.reference_frame, + ); + Some(subtraversal) } - ContextKind::Iframe { parent_traversal } => { - self.iframe_size.pop(); - self.rf_mapper.pop_scope(); - - self.clip_store.pop_clip_root(); - if self.iframe_size.is_empty() { - assert!(self.root_iframe_clip.is_some()); - self.root_iframe_clip = None; - self.add_tile_cache_barrier_if_needed(SliceFlags::empty()); - } + DisplayItem::PopReferenceFrame | + DisplayItem::PopStackingContext => return, + _ => None, + }; - traversal = parent_traversal; - } + // If build_item created a sub-traversal, we need `traversal` to have the + // same state as the completed subtraversal, so we reinitialize it here. + if let Some(mut subtraversal) = subtraversal { + subtraversal.merge_debug_stats_from(traversal); + *traversal = subtraversal; + } else { + self.build_item(item, pipeline_id); } + } - // TODO: factor this out to be part of capture - if cfg!(feature = "display_list_stats") { - let stats = traversal.debug_stats(); - let total_bytes: usize = stats.iter().map(|(_, stats)| stats.num_bytes).sum(); - println!("item, total count, total bytes, % of DL bytes, bytes per item"); - for (label, stats) in stats { - println!("{}, {}, {}kb, {}%, {}", - label, - stats.total_count, - stats.num_bytes / 1000, - ((stats.num_bytes as f32 / total_bytes.max(1) as f32) * 100.0) as usize, - stats.num_bytes / stats.total_count.max(1)); - } - println!(); + // TODO: factor this out to be part of capture + if cfg!(feature = "display_list_stats") { + let stats = traversal.debug_stats(); + let total_bytes: usize = stats.iter().map(|(_, stats)| stats.num_bytes).sum(); + println!("item, total count, total bytes, % of DL bytes, bytes per item"); + for (label, stats) in stats { + println!("{}, {}, {}kb, {}%, {}", + label, + stats.total_count, + stats.num_bytes / 1000, + ((stats.num_bytes as f32 / total_bytes.max(1) as f32) * 100.0) as usize, + stats.num_bytes / stats.total_count.max(1)); } + println!(); } - - self.clip_store.pop_clip_root(); - debug_assert!(self.sc_stack.is_empty()); } fn build_sticky_frame( @@ -885,20 +681,18 @@ impl<'a> SceneBuilder<'a> { pipeline_id: PipelineId, ) { let current_offset = self.current_offset(parent_node_index); - let clip_rect = info.clip_rect.translate(current_offset); - + let clip_region = ClipRegion::create_for_clip_node_with_local_clip( + &info.clip_rect, + ¤t_offset, + ); // Just use clip rectangle as the frame rect for this scroll frame. // This is useful when calculating scroll extents for the // SpatialNode::scroll(..) API as well as for properly setting sticky // positioning offsets. - let frame_rect = clip_rect; + let frame_rect = clip_region.main; let content_size = info.content_rect.size; - self.add_rect_clip_node( - info.clip_id, - &info.parent_space_and_clip, - &clip_rect, - ); + self.add_clip_node(info.clip_id, &info.parent_space_and_clip, clip_region); self.add_scroll_frame( info.scroll_frame_id, @@ -913,27 +707,107 @@ impl<'a> SceneBuilder<'a> { ); } - fn push_iframe( + fn build_reference_frame( + &mut self, + traversal: &mut BuiltDisplayListIter<'a>, + pipeline_id: PipelineId, + parent_spatial_node: SpatialNodeIndex, + origin: LayoutPoint, + reference_frame: &ReferenceFrame, + ) { + profile_scope!("build_reference_frame"); + let current_offset = self.current_offset(parent_spatial_node); + self.push_reference_frame( + reference_frame.id, + Some(parent_spatial_node), + pipeline_id, + reference_frame.transform_style, + reference_frame.transform, + reference_frame.kind, + current_offset + origin.to_vector(), + ); + + self.rf_mapper.push_scope(); + self.build_items( + traversal, + pipeline_id, + ); + self.rf_mapper.pop_scope(); + } + + + fn build_stacking_context( + &mut self, + traversal: &mut BuiltDisplayListIter<'a>, + pipeline_id: PipelineId, + stacking_context: &StackingContext, + spatial_node_index: SpatialNodeIndex, + origin: LayoutPoint, + filters: ItemRange<FilterOp>, + filter_datas: &[TempFilterData], + filter_primitives: ItemRange<FilterPrimitive>, + prim_flags: PrimitiveFlags, + ) { + profile_scope!("build_stacking_context"); + // Avoid doing unnecessary work for empty stacking contexts. + if traversal.current_stacking_context_empty() { + traversal.skip_current_stacking_context(); + return; + } + + let composition_operations = { + CompositeOps::new( + filter_ops_for_compositing(filters), + filter_datas_for_compositing(filter_datas), + filter_primitives_for_compositing(filter_primitives), + stacking_context.mix_blend_mode_for_compositing(), + ) + }; + + self.push_stacking_context( + pipeline_id, + composition_operations, + stacking_context.transform_style, + prim_flags, + spatial_node_index, + stacking_context.clip_id, + stacking_context.raster_space, + stacking_context.flags, + self.sc_stack.last().unwrap().snap_to_device.device_pixel_scale, + ); + + self.rf_mapper.push_offset(origin.to_vector()); + self.build_items( + traversal, + pipeline_id, + ); + self.rf_mapper.pop_offset(); + + self.pop_stacking_context(); + } + + fn build_iframe( &mut self, info: &IframeDisplayItem, spatial_node_index: SpatialNodeIndex, - ) -> Option<(LayoutSize, BuiltDisplayListIter<'a>)> { + ) { let iframe_pipeline_id = info.pipeline_id; let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) { Some(pipeline) => pipeline, None => { debug_assert!(info.ignore_missing_pipeline); - return None + return }, }; let current_offset = self.current_offset(spatial_node_index); - let clip_rect = info.clip_rect.translate(current_offset); - - self.add_rect_clip_node( + self.add_clip_node( ClipId::root(iframe_pipeline_id), &info.space_and_clip, - &clip_rect, + ClipRegion::create_for_clip_node_with_local_clip( + &info.clip_rect, + ¤t_offset, + ), ); self.clip_store.push_clip_root( @@ -941,42 +815,52 @@ impl<'a> SceneBuilder<'a> { true, ); - let bounds = self.snap_rect( - &info.bounds.translate(current_offset), + let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device; + snap_to_device.set_target_spatial_node( spatial_node_index, + &self.spatial_tree, ); + let bounds = snap_to_device.snap_rect( + &info.bounds.translate(current_offset), + ); + + let content_size = snap_to_device.snap_size(&pipeline.content_size); + let spatial_node_index = self.push_reference_frame( SpatialId::root_reference_frame(iframe_pipeline_id), Some(spatial_node_index), iframe_pipeline_id, TransformStyle::Flat, PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false - }, + ReferenceFrameKind::Transform, bounds.origin.to_vector(), ); let iframe_rect = LayoutRect::new(LayoutPoint::zero(), bounds.size); - let is_root_pipeline = self.iframe_size.is_empty(); - self.add_scroll_frame( SpatialId::root_scroll_node(iframe_pipeline_id), spatial_node_index, - ExternalScrollId(0, iframe_pipeline_id), + Some(ExternalScrollId(0, iframe_pipeline_id)), iframe_pipeline_id, &iframe_rect, - &bounds.size, + &content_size, ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::PipelineRoot { - is_root_pipeline, - }, + ScrollFrameKind::PipelineRoot, LayoutVector2D::zero(), ); - Some((bounds.size, pipeline.display_list.iter())) + self.rf_mapper.push_scope(); + self.iframe_depth += 1; + + self.build_items( + &mut pipeline.display_list.iter(), + pipeline.pipeline_id, + ); + self.iframe_depth -= 1; + self.rf_mapper.pop_scope(); + + self.clip_store.pop_clip_root(); } fn get_space( @@ -1003,28 +887,29 @@ impl<'a> SceneBuilder<'a> { let current_offset = self.current_offset(spatial_node_index); - let unsnapped_clip_rect = common.clip_rect.translate(current_offset); - let clip_rect = self.snap_rect( - &unsnapped_clip_rect, + let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device; + snap_to_device.set_target_spatial_node( spatial_node_index, + &self.spatial_tree ); + let unsnapped_clip_rect = common.clip_rect.translate(current_offset); + let clip_rect = snap_to_device.snap_rect(&unsnapped_clip_rect); + let unsnapped_rect = bounds.map(|bounds| { bounds.translate(current_offset) }); // If no bounds rect is given, default to clip rect. let rect = unsnapped_rect.map_or(clip_rect, |bounds| { - self.snap_rect( - &bounds, - spatial_node_index, - ) + snap_to_device.snap_rect(&bounds) }); let layout = LayoutPrimitiveInfo { rect, clip_rect, flags: common.flags, + hit_info: common.hit_info, }; (layout, unsnapped_rect.unwrap_or(unsnapped_clip_rect), spatial_node_index, clip_chain_id) @@ -1046,11 +931,12 @@ impl<'a> SceneBuilder<'a> { rect: &LayoutRect, target_spatial_node: SpatialNodeIndex, ) -> LayoutRect { - self.snap_to_device.set_target_spatial_node( + let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device; + snap_to_device.set_target_spatial_node( target_spatial_node, &self.spatial_tree ); - self.snap_to_device.snap_rect(rect) + snap_to_device.snap_rect(rect) } fn build_item<'b>( @@ -1060,8 +946,6 @@ impl<'a> SceneBuilder<'a> { ) { match *item.item() { DisplayItem::Image(ref info) => { - profile_scope!("image"); - let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, @@ -1080,8 +964,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::RepeatingImage(ref info) => { - profile_scope!("repeating_image"); - let (layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, @@ -1106,8 +988,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::YuvImage(ref info) => { - profile_scope!("yuv_image"); - let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, @@ -1125,8 +1005,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::Text(ref info) => { - profile_scope!("text"); - // TODO(aosmond): Snapping text primitives does not make much sense, given the // primitive bounds and clip are supposed to be conservative, not definitive. // E.g. they should be able to grow and not impact the output. However there @@ -1149,46 +1027,32 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::Rectangle(ref info) => { - profile_scope!("rect"); - let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, ); - self.add_primitive( + self.add_solid_rectangle( spatial_node_index, clip_chain_id, &layout, - Vec::new(), - PrimitiveKeyKind::Rectangle { - color: info.color.into(), - }, + info.color, ); } DisplayItem::HitTest(ref info) => { - profile_scope!("hit_test"); - - // TODO(gw): We could skip building the clip-chain here completely, as it's not used by - // hit-test items. - let (layout, _, spatial_node_index, _) = self.process_common_properties( + let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties( &info.common, None, ); - // Don't add transparent rectangles to the draw list, - // but do consider them for hit testing. This allows - // specifying invisible hit testing areas. - self.add_primitive_to_hit_testing_list( - &layout, + self.add_solid_rectangle( spatial_node_index, - info.common.clip_id, - info.tag, + clip_chain_id, + &layout, + PropertyBinding::Value(ColorF::TRANSPARENT), ); } DisplayItem::ClearRectangle(ref info) => { - profile_scope!("clear"); - let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, @@ -1201,8 +1065,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::Line(ref info) => { - profile_scope!("line"); - let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.area, @@ -1219,167 +1081,71 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::Gradient(ref info) => { - profile_scope!("gradient"); - - if !info.gradient.is_valid() { - return; - } - - let (mut layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( + let (layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, ); - let mut tile_size = process_repeat_size( + let tile_size = process_repeat_size( &layout.rect, &unsnapped_rect, info.tile_size, ); - let mut stops = read_gradient_stops(item.gradient_stops()); - let mut start = info.gradient.start_point; - let mut end = info.gradient.end_point; - let flags = layout.flags; - - let optimized = optimize_linear_gradient( - &mut layout.rect, - &mut tile_size, - info.tile_spacing, - &layout.clip_rect, - &mut start, - &mut end, + if let Some(prim_key_kind) = self.create_linear_gradient_prim( + &layout, + info.gradient.start_point, + info.gradient.end_point, + item.gradient_stops(), info.gradient.extend_mode, - &mut stops, - &mut |rect, start, end, stops| { - let layout = LayoutPrimitiveInfo { rect: *rect, clip_rect: *rect, flags }; - if let Some(prim_key_kind) = self.create_linear_gradient_prim( - &layout, - start, - end, - stops.to_vec(), - ExtendMode::Clamp, - rect.size, - LayoutSize::zero(), - None, - ) { - self.add_nonshadowable_primitive( - spatial_node_index, - clip_chain_id, - &layout, - Vec::new(), - prim_key_kind, - ); - } - } - ); - - if !optimized && !tile_size.ceil().is_empty() { - if let Some(prim_key_kind) = self.create_linear_gradient_prim( + tile_size, + info.tile_spacing, + None, + ) { + self.add_nonshadowable_primitive( + spatial_node_index, + clip_chain_id, &layout, - start, - end, - stops, - info.gradient.extend_mode, - tile_size, - info.tile_spacing, - None, - ) { - self.add_nonshadowable_primitive( - spatial_node_index, - clip_chain_id, - &layout, - Vec::new(), - prim_key_kind, - ); - } + Vec::new(), + prim_key_kind, + ); } } DisplayItem::RadialGradient(ref info) => { - profile_scope!("radial"); - - if !info.gradient.is_valid() { - return; - } - - let (mut layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( + let (layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, ); - let mut center = info.gradient.center; - - let stops = read_gradient_stops(item.gradient_stops()); - - let mut tile_size = process_repeat_size( + let tile_size = process_repeat_size( &layout.rect, &unsnapped_rect, info.tile_size, ); - let mut prim_rect = layout.rect; - let mut tile_spacing = info.tile_spacing; - optimize_radial_gradient( - &mut prim_rect, - &mut tile_size, - &mut center, - &mut tile_spacing, - &layout.clip_rect, - info.gradient.radius, - info.gradient.end_offset, + let prim_key_kind = self.create_radial_gradient_prim( + &layout, + info.gradient.center, + info.gradient.start_offset * info.gradient.radius.width, + info.gradient.end_offset * info.gradient.radius.width, + info.gradient.radius.width / info.gradient.radius.height, + item.gradient_stops(), info.gradient.extend_mode, - &stops, - &mut |solid_rect, color| { - self.add_nonshadowable_primitive( - spatial_node_index, - clip_chain_id, - &LayoutPrimitiveInfo { - rect: *solid_rect, - .. layout - }, - Vec::new(), - PrimitiveKeyKind::Rectangle { color: PropertyBinding::Value(color) }, - ); - } + tile_size, + info.tile_spacing, + None, ); - // TODO: create_radial_gradient_prim already calls - // this, but it leaves the info variable that is - // passed to add_nonshadowable_primitive unmodified - // which can cause issues. - simplify_repeated_primitive(&tile_size, &mut tile_spacing, &mut prim_rect); - - if !tile_size.ceil().is_empty() { - layout.rect = prim_rect; - let prim_key_kind = self.create_radial_gradient_prim( - &layout, - center, - info.gradient.start_offset * info.gradient.radius.width, - info.gradient.end_offset * info.gradient.radius.width, - info.gradient.radius.width / info.gradient.radius.height, - stops, - info.gradient.extend_mode, - tile_size, - tile_spacing, - None, - ); - - self.add_nonshadowable_primitive( - spatial_node_index, - clip_chain_id, - &layout, - Vec::new(), - prim_key_kind, - ); - } + self.add_nonshadowable_primitive( + spatial_node_index, + clip_chain_id, + &layout, + Vec::new(), + prim_key_kind, + ); } DisplayItem::ConicGradient(ref info) => { - profile_scope!("conic"); - - if !info.gradient.is_valid() { - return; - } - - let (mut layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( + let (layout, unsnapped_rect, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, ); @@ -1390,40 +1156,28 @@ impl<'a> SceneBuilder<'a> { info.tile_size, ); - let offset = apply_gradient_local_clip( - &mut layout.rect, - &tile_size, - &info.tile_spacing, - &layout.clip_rect, + let prim_key_kind = self.create_conic_gradient_prim( + &layout, + info.gradient.center, + info.gradient.angle, + info.gradient.start_offset, + info.gradient.end_offset, + item.gradient_stops(), + info.gradient.extend_mode, + tile_size, + info.tile_spacing, + None, ); - let center = info.gradient.center + offset; - if !tile_size.ceil().is_empty() { - let prim_key_kind = self.create_conic_gradient_prim( - &layout, - center, - info.gradient.angle, - info.gradient.start_offset, - info.gradient.end_offset, - item.gradient_stops(), - info.gradient.extend_mode, - tile_size, - info.tile_spacing, - None, - ); - - self.add_nonshadowable_primitive( - spatial_node_index, - clip_chain_id, - &layout, - Vec::new(), - prim_key_kind, - ); - } + self.add_nonshadowable_primitive( + spatial_node_index, + clip_chain_id, + &layout, + Vec::new(), + prim_key_kind, + ); } DisplayItem::BoxShadow(ref info) => { - profile_scope!("box_shadow"); - let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.box_bounds, @@ -1442,8 +1196,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::Border(ref info) => { - profile_scope!("border"); - let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties_with_bounds( &info.common, &info.bounds, @@ -1457,9 +1209,14 @@ impl<'a> SceneBuilder<'a> { item.gradient_stops(), ); } + DisplayItem::Iframe(ref info) => { + let space = self.get_space(info.space_and_clip.spatial_id); + self.build_iframe( + info, + space, + ); + } DisplayItem::ImageMaskClip(ref info) => { - profile_scope!("image_clip"); - let parent_space = self.get_space(info.parent_space_and_clip.spatial_id); let current_offset = self.current_offset(parent_space); @@ -1472,13 +1229,9 @@ impl<'a> SceneBuilder<'a> { info.id, &info.parent_space_and_clip, &image_mask, - info.fill_rule, - item.points(), ); } DisplayItem::RoundedRectClip(ref info) => { - profile_scope!("rounded_clip"); - let parent_space = self.get_space(info.parent_space_and_clip.spatial_id); let current_offset = self.current_offset(parent_space); @@ -1490,8 +1243,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::RectClip(ref info) => { - profile_scope!("rect_clip"); - let parent_space = self.get_space(info.parent_space_and_clip.spatial_id); let current_offset = self.current_offset(parent_space); let clip_rect = info.clip_rect.translate(current_offset); @@ -1503,8 +1254,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::Clip(ref info) => { - profile_scope!("clip"); - let parent_space = self.get_space(info.parent_space_and_clip.spatial_id); let current_offset = self.current_offset(parent_space); let clip_region = ClipRegion::create_for_clip_node( @@ -1515,26 +1264,21 @@ impl<'a> SceneBuilder<'a> { self.add_clip_node(info.id, &info.parent_space_and_clip, clip_region); } DisplayItem::ClipChain(ref info) => { - profile_scope!("clip_chain"); - let parent = info.parent.map_or(ClipId::root(pipeline_id), |id| ClipId::ClipChain(id)); - let mut clips: SmallVec<[SceneClipInstance; 4]> = SmallVec::new(); + let mut instances: SmallVec<[ClipInstance; 4]> = SmallVec::new(); for clip_item in item.clip_chain_items() { let template = self.clip_store.get_template(clip_item); - let instances = &self.clip_store.instances[template.clips.start as usize .. template.clips.end as usize]; - clips.extend_from_slice(instances); + instances.extend_from_slice(&template.instances); } self.clip_store.register_clip_template( ClipId::ClipChain(info.id), parent, - &clips, + &instances, ); }, DisplayItem::ScrollFrame(ref info) => { - profile_scope!("scrollframe"); - let parent_space = self.get_space(info.parent_space_and_clip.spatial_id); self.build_scroll_frame( info, @@ -1543,8 +1287,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::StickyFrame(ref info) => { - profile_scope!("stickyframe"); - let parent_space = self.get_space(info.parent_spatial_id); self.build_sticky_frame( info, @@ -1552,8 +1294,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::BackdropFilter(ref info) => { - profile_scope!("backdrop"); - let (layout, _, spatial_node_index, clip_chain_id) = self.process_common_properties( &info.common, None, @@ -1577,16 +1317,14 @@ impl<'a> SceneBuilder<'a> { DisplayItem::SetGradientStops | DisplayItem::SetFilterOps | DisplayItem::SetFilterData | - DisplayItem::SetFilterPrimitives | - DisplayItem::SetPoints => {} + DisplayItem::SetFilterPrimitives => {} // Special items that are handled in the parent method DisplayItem::PushStackingContext(..) | DisplayItem::PushReferenceFrame(..) | DisplayItem::PopReferenceFrame | - DisplayItem::PopStackingContext | - DisplayItem::Iframe(_) => { - unreachable!("Handled in `build_all`") + DisplayItem::PopStackingContext => { + unreachable!("Should have returned in parent method.") } DisplayItem::ReuseItems(key) | @@ -1595,8 +1333,6 @@ impl<'a> SceneBuilder<'a> { } DisplayItem::PushShadow(info) => { - profile_scope!("push_shadow"); - let spatial_node_index = self.get_space(info.space_and_clip.spatial_id); let clip_chain_id = self.get_clip_chain( info.space_and_clip.clip_id, @@ -1610,8 +1346,6 @@ impl<'a> SceneBuilder<'a> { ); } DisplayItem::PopAllShadows => { - profile_scope!("pop_all_shadows"); - self.pop_all_shadows(); } } @@ -1696,17 +1430,41 @@ impl<'a> SceneBuilder<'a> { &mut self, info: &LayoutPrimitiveInfo, spatial_node_index: SpatialNodeIndex, - clip_id: ClipId, - tag: ItemTag, + clip_chain_id: ClipChainId, ) { - self.hit_testing_scene.add_item( + let tag = match info.hit_info { + Some(tag) => tag, + None => return, + }; + + // We want to get a range of clip chain roots that apply to this + // hit testing primitive. + + // Get the start index for the clip chain root range for this primitive. + let start = self.hit_testing_scene.next_clip_chain_index(); + + // Add the clip chain root for the primitive itself. + self.hit_testing_scene.add_clip_chain(clip_chain_id); + + // Append any clip chain roots from enclosing stacking contexts. + for sc in &self.sc_stack { + self.hit_testing_scene.add_clip_chain(sc.clip_chain_id); + } + + // Construct a clip chain roots range to be stored with the item. + let clip_chain_range = ops::Range { + start, + end: self.hit_testing_scene.next_clip_chain_index(), + }; + + // Create and store the hit testing primitive itself. + let new_item = HitTestingItem::new( tag, info, spatial_node_index, - clip_id, - &self.clip_store, - self.interners, + clip_chain_range, ); + self.hit_testing_scene.add_item(new_item); } /// Add an already created primitive to the draw lists. @@ -1722,33 +1480,13 @@ impl<'a> SceneBuilder<'a> { println!("\tadded to stacking context at {}", self.sc_stack.len()); } - // If we have a valid stacking context, the primitive gets added to that. - // Otherwise, it gets added to a top-level picture cache slice. - - match self.sc_stack.last_mut() { - Some(stacking_context) => { - stacking_context.prim_list.add_prim( - prim_instance, - prim_rect, - spatial_node_index, - flags, - ); - } - None => { - self.tile_cache_builder.add_prim( - prim_instance, - prim_rect, - spatial_node_index, - flags, - &self.spatial_tree, - &self.clip_store, - self.interners, - &self.config, - &self.quality_settings, - self.root_iframe_clip, - ); - } - } + let stacking_context = self.sc_stack.last_mut().unwrap(); + stacking_context.prim_list.add_prim( + prim_instance, + prim_rect, + spatial_node_index, + flags, + ); } /// Convenience interface that creates a primitive entry and adds it @@ -1838,6 +1576,11 @@ impl<'a> SceneBuilder<'a> { &info.rect, &prim_instance, ); + self.add_primitive_to_hit_testing_list( + info, + spatial_node_index, + clip_chain_id, + ); self.add_primitive_to_draw_list( prim_instance, info.rect, @@ -1846,23 +1589,9 @@ impl<'a> SceneBuilder<'a> { ); } - /// If no stacking contexts are present (i.e. we are adding prims to a tile - /// cache), set a barrier to force creation of a slice before the next prim - fn add_tile_cache_barrier_if_needed( - &mut self, - slice_flags: SliceFlags, - ) { - if self.sc_stack.is_empty() { - // Shadows can only exist within a stacking context - assert!(self.pending_shadow_items.is_empty()); - - self.tile_cache_builder.add_tile_cache_barrier(slice_flags); - } - } - - /// Push a new stacking context. Returns context that must be passed to pop_stacking_context(). - fn push_stacking_context( + pub fn push_stacking_context( &mut self, + pipeline_id: PipelineId, composite_ops: CompositeOps, transform_style: TransformStyle, prim_flags: PrimitiveFlags, @@ -1870,12 +1599,22 @@ impl<'a> SceneBuilder<'a> { clip_id: Option<ClipId>, requested_raster_space: RasterSpace, flags: StackingContextFlags, - pipeline_id: PipelineId, - ) -> StackingContextInfo { - profile_scope!("push_stacking_context"); + device_pixel_scale: DevicePixelScale, + ) { + // Check if this stacking context is the root of a pipeline, and the caller + // has requested it as an output frame. + let is_pipeline_root = + self.sc_stack.last().map_or(true, |sc| sc.pipeline_id != pipeline_id); + let frame_output_pipeline_id = if is_pipeline_root && self.output_pipelines.contains(&pipeline_id) { + Some(pipeline_id) + } else { + None + }; - // Push current requested raster space on stack for prims to access - self.raster_space_stack.push(requested_raster_space); + let clip_chain_id = match clip_id { + Some(clip_id) => self.clip_store.get_or_build_clip_chain_id(clip_id), + None => ClipChainId::NONE, + }; // Get the transform-style of the parent stacking context, // which determines if we *might* need to draw this on @@ -1923,20 +1662,21 @@ impl<'a> SceneBuilder<'a> { (parent_is_3d || transform_style == TransformStyle::Preserve3D); let context_3d = if participating_in_3d_context { - // Get the spatial node index of the containing block, which + // Find the spatial node index of the containing block, which // defines the context of backface-visibility. - let ancestor_index = self.containing_block_stack - .last() - .cloned() - .unwrap_or(ROOT_SPATIAL_NODE_INDEX); - + let ancestor_context = self.sc_stack + .iter() + .rfind(|sc| !sc.is_3d()); Picture3DContext::In { root_data: if parent_is_3d { None } else { Some(Vec::new()) }, - ancestor_index, + ancestor_index: match ancestor_context { + Some(sc) => sc.spatial_node_index, + None => ROOT_SPATIAL_NODE_INDEX, + }, } } else { Picture3DContext::Out @@ -1947,171 +1687,165 @@ impl<'a> SceneBuilder<'a> { // prepare step to skip the intermediate surface if the // clip node doesn't affect the stacking context rect. let mut blit_reason = BlitReason::empty(); + let mut current_clip_chain_id = clip_chain_id; if flags.contains(StackingContextFlags::IS_BLEND_CONTAINER) { blit_reason |= BlitReason::ISOLATE; } - // If this stacking context has any complex clips, we need to draw it - // to an off-screen surface. - if let Some(clip_id) = clip_id { - if self.clip_store.has_complex_clips(clip_id) { - blit_reason |= BlitReason::CLIP; - } - } + // Walk each clip in this chain, to see whether any of the clips + // require that we draw this to an intermediate surface. + while current_clip_chain_id != ClipChainId::NONE { + let clip_chain_node = &self + .clip_store + .clip_chain_nodes[current_clip_chain_id.0 as usize]; - let is_redundant = FlattenedStackingContext::is_redundant( - flags, - &context_3d, - &composite_ops, - blit_reason, - self.sc_stack.last(), - prim_flags, - ); + let clip_node_data = &self.interners.clip[clip_chain_node.handle]; - // If stacking context is a scrollbar, force a new slice for the primitives - // within. The stacking context will be redundant and removed by above check. - let set_tile_cache_barrier = prim_flags.contains(PrimitiveFlags::IS_SCROLLBAR_CONTAINER); + if let ClipNodeKind::Complex = clip_node_data.clip_node_kind { + blit_reason = BlitReason::CLIP; + break; + } - if set_tile_cache_barrier { - self.add_tile_cache_barrier_if_needed(SliceFlags::IS_SCROLLBAR); + current_clip_chain_id = clip_chain_node.parent_clip_chain_id; } - let mut sc_info = StackingContextInfo { - pop_hit_testing_clip: false, - pop_stacking_context: false, - pop_containing_block: false, - set_tile_cache_barrier, - }; - - // If this is not 3d, then it establishes an ancestor root for child 3d contexts. - if !participating_in_3d_context { - sc_info.pop_containing_block = true; - self.containing_block_stack.push(spatial_node_index); - } + let snap_to_device = self.sc_stack.last().map_or( + SpaceSnapper::new( + ROOT_SPATIAL_NODE_INDEX, + device_pixel_scale, + ), + |sc| sc.snap_to_device.clone(), + ); - // If this stacking context is redundant, we don't care about getting a clip-chain for it. - // However, if we _do_ have a clip, we must build it here before the `push_clip_root` - // calls below, to ensure we get the clips for drawing this stacking context itself. - let clip_chain_id = if is_redundant { - ClipChainId::NONE - } else { - // Get a clip-chain for this stacking context - even if the stacking context - // itself has no clips, it's possible that there are clips to collect from - // the previous clip-chain builder. - let clip_id = clip_id.unwrap_or(ClipId::root(pipeline_id)); - self.clip_store.get_or_build_clip_chain_id(clip_id) + let is_redundant = match self.sc_stack.last() { + Some(parent) => { + FlattenedStackingContext::is_redundant( + &context_3d, + &composite_ops, + prim_flags, + blit_reason, + requested_raster_space, + parent, + ) + } + None => { + false + } }; - // If this has a valid clip, register with the hit-testing scene if let Some(clip_id) = clip_id { - self.hit_testing_scene.push_clip(clip_id); - sc_info.pop_hit_testing_clip = true; - } - - // If this stacking context is redundant (prims will be pushed into - // the parent during pop) but it has a valid clip, then we need to - // add that clip to the current clip chain builder, so it's correctly - // applied to any primitives within this redundant stacking context. - // For the normal case, we start a new clip root, knowing that the - // clip on this stacking context will be pushed onto the stack during - // frame building. - if is_redundant { - self.clip_store.push_clip_root(clip_id, true); - } else { - self.clip_store.push_clip_root(None, false); - } - - // If not redundant, create a stacking context to hold primitive clusters - if !is_redundant { - sc_info.pop_stacking_context = true; - - // Push the SC onto the stack, so we know how to handle things in - // pop_stacking_context. - self.sc_stack.push(FlattenedStackingContext { - prim_list: PrimitiveList::empty(), - prim_flags, - spatial_node_index, - clip_chain_id, - composite_ops, - blit_reason, - transform_style, - context_3d, - is_redundant, - is_backdrop_root: flags.contains(StackingContextFlags::IS_BACKDROP_ROOT), - flags, - }); + // If this stacking context is redundant (prims will be pushed into + // the parent during pop) but it has a valid clip, then we need to + // add that clip to the current clip chain builder, so it's correctly + // applied to any primitives within this redundant stacking context. + // For the normal case, we start a new clip root, knowing that the + // clip on this stacking context will be pushed onto the stack during + // frame building. + if is_redundant { + self.clip_store.push_clip_root(Some(clip_id), true); + } else { + self.clip_store.push_clip_root(None, false); + } } - sc_info + // Push the SC onto the stack, so we know how to handle things in + // pop_stacking_context. + self.sc_stack.push(FlattenedStackingContext { + prim_list: PrimitiveList::empty(), + pipeline_id, + prim_flags, + requested_raster_space, + spatial_node_index, + clip_id, + clip_chain_id, + frame_output_pipeline_id, + composite_ops, + blit_reason, + transform_style, + context_3d, + is_redundant, + is_backdrop_root: flags.contains(StackingContextFlags::IS_BACKDROP_ROOT), + snap_to_device, + }); } - fn pop_stacking_context( - &mut self, - info: StackingContextInfo, - ) { - profile_scope!("pop_stacking_context"); - - // Pop off current raster space (pushed unconditionally in push_stacking_context) - self.raster_space_stack.pop().unwrap(); - - // Pop off clip builder root (pushed unconditionally in push_stacking_context) - self.clip_store.pop_clip_root(); - - // If the stacking context formed a containing block, pop off the stack - if info.pop_containing_block { - self.containing_block_stack.pop().unwrap(); - } - - if info.set_tile_cache_barrier { - self.add_tile_cache_barrier_if_needed(SliceFlags::empty()); - } + pub fn pop_stacking_context(&mut self) { + let mut stacking_context = self.sc_stack.pop().unwrap(); - // If the stacking context established a clip root, pop off the stack - if info.pop_hit_testing_clip { - self.hit_testing_scene.pop_clip(); + if stacking_context.clip_id.is_some() { + self.clip_store.pop_clip_root(); } - // If the stacking context was otherwise redundant, early exit - if !info.pop_stacking_context { - return; - } + // If we encounter a stacking context that is effectively a no-op, then instead + // of creating a picture, just append the primitive list to the parent stacking + // context as a short cut. This serves two purposes: + // (a) It's an optimization to reduce picture count and allocations, as display lists + // often contain a lot of these stacking contexts that don't require pictures or + // off-screen surfaces. + // (b) It's useful for the initial version of picture caching in gecko, by enabling + // is to just look for interesting scroll roots on the root stacking context, + // without having to consider cuts at stacking context boundaries. + let parent_is_empty = match self.sc_stack.last_mut() { + Some(parent_sc) => { + if stacking_context.is_redundant { + if !stacking_context.prim_list.is_empty() { + // If popping a redundant stacking context that is from a different pipeline, + // we want to insert flags where the picture cache slices should be created + // for this iframe. For now, we want to match existing behavior, that is: + // - Only cache content that is within the main scroll root, and: + // - Skip caching fixed position content before / after the scroll root. + // This means that we don't add scrollbars, which cause lots of extra + // invalidations. There is ongoing work to add tags to primitives that + // are scrollbars. Once this lands, we can simplify this logic considerably + // (and add a separate picture cache slice / OS layer for scroll bars). + if parent_sc.pipeline_id != stacking_context.pipeline_id && self.iframe_depth == 1 { + self.content_slice_count = stacking_context.init_picture_caching( + &self.spatial_tree, + &self.clip_store, + &self.quality_settings, + ); - let stacking_context = self.sc_stack.pop().unwrap(); - - // If the stacking context is a blend container, and if we're at the top level - // of the stacking context tree, we can make this blend container into a tile - // cache. This means that we get caching and correct scrolling invalidation for - // root level blend containers. For these cases, the readbacks of the backdrop - // are handled by doing partial reads of the picture cache tiles during rendering. - if stacking_context.flags.contains(StackingContextFlags::IS_BLEND_CONTAINER) && - self.sc_stack.is_empty() && - self.tile_cache_builder.can_add_container_tile_cache() && - self.spatial_tree.get_static_coordinate_system_id(stacking_context.spatial_node_index) == StaticCoordinateSystemId::ROOT - { - self.tile_cache_builder.add_tile_cache( - stacking_context.prim_list, - stacking_context.clip_chain_id, - &self.spatial_tree, - &self.clip_store, - self.interners, - &self.config, - self.root_iframe_clip, - SliceFlags::IS_BLEND_CONTAINER, - ); + // Mark that a user supplied tile cache was specified. + self.picture_caching_initialized = true; + } - return; - } + // If the parent context primitives list is empty, it's faster + // to assign the storage of the popped context instead of paying + // the copying cost for extend. + if parent_sc.prim_list.is_empty() { + parent_sc.prim_list = stacking_context.prim_list; + } else { + parent_sc.prim_list.extend(stacking_context.prim_list); + } + } - let parent_is_empty = match self.sc_stack.last() { - Some(parent_sc) => { - assert!(!stacking_context.is_redundant); + return; + } parent_sc.prim_list.is_empty() }, None => true, }; - let mut source = match stacking_context.context_3d { + if self.sc_stack.is_empty() { + // If we didn't encounter a content iframe, then set up picture caching slice markers + // on the root stacking context. This can happen in Gecko when the parent process + // provides the content display list (e.g. about:support, about:config etc). + if !self.picture_caching_initialized { + self.content_slice_count = stacking_context.init_picture_caching( + &self.spatial_tree, + &self.clip_store, + &self.quality_settings, + ); + self.picture_caching_initialized = true; + } + + self.setup_picture_caching( + &mut stacking_context.prim_list, + ); + } + + let (leaf_context_3d, leaf_composite_mode, leaf_output_pipeline_id) = match stacking_context.context_3d { // TODO(gw): For now, as soon as this picture is in // a 3D context, we draw it to an intermediate // surface and apply plane splitting. However, @@ -2119,92 +1853,63 @@ impl<'a> SceneBuilder<'a> { // During culling, we can check if there is actually // perspective present, and skip the plane splitting // completely when that is not the case. - Picture3DContext::In { ancestor_index, .. } => { - let composite_mode = Some( - PictureCompositeMode::Blit(BlitReason::PRESERVE3D | stacking_context.blit_reason) - ); - - // Add picture for this actual stacking context contents to render into. - let pic_index = PictureIndex(self.prim_store.pictures - .alloc() - .init(PicturePrimitive::new_image( - composite_mode.clone(), - Picture3DContext::In { root_data: None, ancestor_index }, - true, - stacking_context.prim_flags, - stacking_context.prim_list, - stacking_context.spatial_node_index, - PictureOptions::default(), - )) - ); - - let instance = create_prim_instance( - pic_index, - composite_mode.into(), - ClipChainId::NONE, - &mut self.interners, - ); - - PictureChainBuilder::from_instance( - instance, - stacking_context.prim_flags, - stacking_context.spatial_node_index, - ) - } - Picture3DContext::Out => { + Picture3DContext::In { ancestor_index, .. } => ( + Picture3DContext::In { root_data: None, ancestor_index }, + Some(PictureCompositeMode::Blit(BlitReason::PRESERVE3D | stacking_context.blit_reason)), + None, + ), + Picture3DContext::Out => ( + Picture3DContext::Out, if stacking_context.blit_reason.is_empty() { - PictureChainBuilder::from_prim_list( - stacking_context.prim_list, - stacking_context.prim_flags, - stacking_context.spatial_node_index, - ) + // By default, this picture will be collapsed into + // the owning target. + None } else { - let composite_mode = Some( - PictureCompositeMode::Blit(stacking_context.blit_reason) - ); + // Add a dummy composite filter if the SC has to be isolated. + Some(PictureCompositeMode::Blit(stacking_context.blit_reason)) + }, + stacking_context.frame_output_pipeline_id + ), + }; - // Add picture for this actual stacking context contents to render into. - let pic_index = PictureIndex(self.prim_store.pictures - .alloc() - .init(PicturePrimitive::new_image( - composite_mode.clone(), - Picture3DContext::Out, - true, - stacking_context.prim_flags, - stacking_context.prim_list, - stacking_context.spatial_node_index, - PictureOptions::default(), - )) - ); + // Add picture for this actual stacking context contents to render into. + let leaf_pic_index = PictureIndex(self.prim_store.pictures + .alloc() + .init(PicturePrimitive::new_image( + leaf_composite_mode.clone(), + leaf_context_3d, + leaf_output_pipeline_id, + true, + stacking_context.prim_flags, + stacking_context.requested_raster_space, + stacking_context.prim_list, + stacking_context.spatial_node_index, + None, + PictureOptions::default(), + )) + ); - let instance = create_prim_instance( - pic_index, - composite_mode.into(), - ClipChainId::NONE, - &mut self.interners, - ); + // Create a chain of pictures based on presence of filters, + // mix-blend-mode and/or 3d rendering context containers. - PictureChainBuilder::from_instance( - instance, - stacking_context.prim_flags, - stacking_context.spatial_node_index, - ) - } - } - }; + let mut current_pic_index = leaf_pic_index; + let mut cur_instance = create_prim_instance( + leaf_pic_index, + leaf_composite_mode.into(), + ClipChainId::NONE, + &mut self.interners, + ); + + if cur_instance.is_chased() { + println!("\tis a leaf primitive for a stacking context"); + } // If establishing a 3d context, the `cur_instance` represents // a picture with all the *trailing* immediate children elements. // We append this to the preserve-3D picture set and make a container picture of them. if let Picture3DContext::In { root_data: Some(mut prims), ancestor_index } = stacking_context.context_3d { - let instance = source.finalize( - ClipChainId::NONE, - &mut self.interners, - &mut self.prim_store, - ); - prims.push(ExtendedPrimitiveInstance { - instance, + instance: cur_instance, spatial_node_index: stacking_context.spatial_node_index, flags: stacking_context.prim_flags, }); @@ -2220,7 +1925,7 @@ impl<'a> SceneBuilder<'a> { } // This is the acttual picture representing our 3D hierarchy root. - let pic_index = PictureIndex(self.prim_store.pictures + current_pic_index = PictureIndex(self.prim_store.pictures .alloc() .init(PicturePrimitive::new_image( None, @@ -2228,38 +1933,42 @@ impl<'a> SceneBuilder<'a> { root_data: Some(Vec::new()), ancestor_index, }, + stacking_context.frame_output_pipeline_id, true, stacking_context.prim_flags, + stacking_context.requested_raster_space, prim_list, stacking_context.spatial_node_index, + None, PictureOptions::default(), )) ); - let instance = create_prim_instance( - pic_index, + cur_instance = create_prim_instance( + current_pic_index, PictureCompositeKey::Identity, ClipChainId::NONE, &mut self.interners, ); - - source = PictureChainBuilder::from_instance( - instance, - stacking_context.prim_flags, - stacking_context.spatial_node_index, - ); } - let has_filters = stacking_context.composite_ops.has_valid_filters(); - - source = self.wrap_prim_with_filters( - source, + let (filtered_pic_index, filtered_instance) = self.wrap_prim_with_filters( + cur_instance, + current_pic_index, stacking_context.composite_ops.filters, stacking_context.composite_ops.filter_primitives, stacking_context.composite_ops.filter_datas, + stacking_context.prim_flags, + stacking_context.requested_raster_space, + stacking_context.spatial_node_index, true, ); + let has_filters = current_pic_index != filtered_pic_index; + + current_pic_index = filtered_pic_index; + cur_instance = filtered_instance; + // Same for mix-blend-mode, except we can skip if this primitive is the first in the parent // stacking context. // From https://drafts.fxtf.org/compositing-1/#generalformula, the formula for blending is: @@ -2273,20 +1982,44 @@ impl<'a> SceneBuilder<'a> { // backdrop alpha will be 0, and then the blend equation collapses to just // Cs = Cs, and the blend mode isn't taken into account at all. if let (Some(mix_blend_mode), false) = (stacking_context.composite_ops.mix_blend_mode, parent_is_empty) { - let parent_is_isolated = match self.sc_stack.last() { - Some(parent_sc) => parent_sc.blit_reason.contains(BlitReason::ISOLATE), - None => false, - }; - if parent_is_isolated { - let composite_mode = PictureCompositeMode::MixBlend(mix_blend_mode); + if self.sc_stack.last().unwrap().blit_reason.contains(BlitReason::ISOLATE) { + let composite_mode = Some(PictureCompositeMode::MixBlend(mix_blend_mode)); - source = source.add_picture( - composite_mode, - Picture3DContext::Out, - PictureOptions::default(), + let mut prim_list = PrimitiveList::empty(); + prim_list.add_prim( + cur_instance.clone(), + LayoutRect::zero(), + stacking_context.spatial_node_index, + stacking_context.prim_flags, + ); + + let blend_pic_index = PictureIndex(self.prim_store.pictures + .alloc() + .init(PicturePrimitive::new_image( + composite_mode.clone(), + Picture3DContext::Out, + None, + true, + stacking_context.prim_flags, + stacking_context.requested_raster_space, + prim_list, + stacking_context.spatial_node_index, + None, + PictureOptions::default(), + )) + ); + + current_pic_index = blend_pic_index; + cur_instance = create_prim_instance( + blend_pic_index, + composite_mode.into(), + ClipChainId::NONE, &mut self.interners, - &mut self.prim_store, ); + + if cur_instance.is_chased() { + println!("\tis a mix-blend picture for a stacking context with {:?}", mix_blend_mode); + } } else { // If we have a mix-blend-mode, the stacking context needs to be isolated // to blend correctly as per the CSS spec. @@ -2297,11 +2030,7 @@ impl<'a> SceneBuilder<'a> { // Set the stacking context clip on the outermost picture in the chain, // unless we already set it on the leaf picture. - let cur_instance = source.finalize( - stacking_context.clip_chain_id, - &mut self.interners, - &mut self.prim_store, - ); + cur_instance.clip_chain_id = stacking_context.clip_chain_id; // The primitive instance for the remainder of flat children of this SC // if it's a part of 3D hierarchy but not the root of it. @@ -2322,13 +2051,7 @@ impl<'a> SceneBuilder<'a> { } // This must be the root stacking context None => { - self.add_primitive_to_draw_list( - cur_instance, - LayoutRect::zero(), - stacking_context.spatial_node_index, - stacking_context.prim_flags, - ); - + self.root_pic_index = current_pic_index; None } }; @@ -2376,6 +2099,8 @@ impl<'a> SceneBuilder<'a> { &mut self, pipeline_id: PipelineId, viewport_size: &LayoutSize, + content_size: &LayoutSize, + device_pixel_scale: DevicePixelScale, ) { if let ChasePrimitive::Id(id) = self.config.chase_primitive { println!("Chasing {:?} by index", id); @@ -2388,29 +2113,33 @@ impl<'a> SceneBuilder<'a> { pipeline_id, TransformStyle::Flat, PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, + ReferenceFrameKind::Transform, LayoutVector2D::zero(), ); - let viewport_rect = self.snap_rect( - &LayoutRect::new(LayoutPoint::zero(), *viewport_size), + // We can't use this with the stacking context because it does not exist + // yet. Just create a dedicated snapper for the root. + let snap_to_device = SpaceSnapper::new_with_target( spatial_node_index, + ROOT_SPATIAL_NODE_INDEX, + device_pixel_scale, + &self.spatial_tree, + ); + + let content_size = snap_to_device.snap_size(content_size); + let viewport_rect = snap_to_device.snap_rect( + &LayoutRect::new(LayoutPoint::zero(), *viewport_size), ); self.add_scroll_frame( SpatialId::root_scroll_node(pipeline_id), spatial_node_index, - ExternalScrollId(0, pipeline_id), + Some(ExternalScrollId(0, pipeline_id)), pipeline_id, &viewport_rect, - &viewport_rect.size, + &content_size, ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::PipelineRoot { - is_root_pipeline: true, - }, + ScrollFrameKind::PipelineRoot, LayoutVector2D::zero(), ); } @@ -2420,31 +2149,18 @@ impl<'a> SceneBuilder<'a> { new_node_id: ClipId, space_and_clip: &SpaceAndClipInfo, image_mask: &ImageMask, - fill_rule: FillRule, - points_range: ItemRange<LayoutPoint>, ) { let spatial_node_index = self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id); - let snapped_mask_rect = self.snap_rect( - &image_mask.rect, + let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device; + snap_to_device.set_target_spatial_node( spatial_node_index, + &self.spatial_tree, ); - let points: Vec<LayoutPoint> = points_range.iter().collect(); - - // If any points are provided, then intern a polygon with the points and fill rule. - let mut polygon_handle: Option<PolygonDataHandle> = None; - if points.len() > 0 { - let item = PolygonKey::new(&points, fill_rule); - - let handle = self - .interners - .polygon - .intern(&item, || item); - polygon_handle = Some(handle); - } + let snapped_mask_rect = snap_to_device.snap_rect(&image_mask.rect); let item = ClipItemKey { - kind: ClipItemKeyKind::image_mask(image_mask, snapped_mask_rect, polygon_handle), + kind: ClipItemKeyKind::image_mask(image_mask, snapped_mask_rect), }; let handle = self @@ -2456,10 +2172,7 @@ impl<'a> SceneBuilder<'a> { } }); - let instance = SceneClipInstance { - key: item, - clip: ClipInstance::new(handle, spatial_node_index), - }; + let instance = ClipInstance::new(handle, spatial_node_index); self.clip_store.register_clip_template( new_node_id, @@ -2477,11 +2190,14 @@ impl<'a> SceneBuilder<'a> { ) { let spatial_node_index = self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id); - let snapped_clip_rect = self.snap_rect( - clip_rect, + let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device; + snap_to_device.set_target_spatial_node( spatial_node_index, + &self.spatial_tree, ); + let snapped_clip_rect = snap_to_device.snap_rect(clip_rect); + let item = ClipItemKey { kind: ClipItemKeyKind::rectangle(snapped_clip_rect, ClipMode::Clip), }; @@ -2494,10 +2210,7 @@ impl<'a> SceneBuilder<'a> { } }); - let instance = SceneClipInstance { - key: item, - clip: ClipInstance::new(handle, spatial_node_index), - }; + let instance = ClipInstance::new(handle, spatial_node_index); self.clip_store.register_clip_template( new_node_id, @@ -2515,10 +2228,13 @@ impl<'a> SceneBuilder<'a> { ) { let spatial_node_index = self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id); - let snapped_region_rect = self.snap_rect( - &clip.rect.translate(current_offset), + let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device; + snap_to_device.set_target_spatial_node( spatial_node_index, + &self.spatial_tree, ); + + let snapped_region_rect = snap_to_device.snap_rect(&clip.rect.translate(current_offset)); let item = ClipItemKey { kind: ClipItemKeyKind::rounded_rect( snapped_region_rect, @@ -2536,10 +2252,7 @@ impl<'a> SceneBuilder<'a> { } }); - let instance = SceneClipInstance { - key: item, - clip: ClipInstance::new(handle, spatial_node_index), - }; + let instance = ClipInstance::new(handle, spatial_node_index); self.clip_store.register_clip_template( new_node_id, @@ -2560,11 +2273,14 @@ impl<'a> SceneBuilder<'a> { // Map the ClipId for the positioning node to a spatial node index. let spatial_node_index = self.id_to_index_mapper.get_spatial_node_index(space_and_clip.spatial_id); - let snapped_clip_rect = self.snap_rect( - &clip_region.main, + let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device; + snap_to_device.set_target_spatial_node( spatial_node_index, + &self.spatial_tree, ); - let mut instances: SmallVec<[SceneClipInstance; 4]> = SmallVec::new(); + + let snapped_clip_rect = snap_to_device.snap_rect(&clip_region.main); + let mut instances: SmallVec<[ClipInstance; 4]> = SmallVec::new(); // Intern each clip item in this clip node, and add the interned // handle to a clip chain node, parented to form a chain. @@ -2583,15 +2299,10 @@ impl<'a> SceneBuilder<'a> { clip_node_kind: ClipNodeKind::Rectangle, } }); - instances.push( - SceneClipInstance { - key: item, - clip: ClipInstance::new(handle, spatial_node_index), - }, - ); + instances.push(ClipInstance::new(handle, spatial_node_index)); for region in clip_region.complex_clips { - let snapped_region_rect = self.snap_rect(®ion.rect, spatial_node_index); + let snapped_region_rect = snap_to_device.snap_rect(®ion.rect); let item = ClipItemKey { kind: ClipItemKeyKind::rounded_rect( snapped_region_rect, @@ -2609,12 +2320,7 @@ impl<'a> SceneBuilder<'a> { } }); - instances.push( - SceneClipInstance { - key: item, - clip: ClipInstance::new(handle, spatial_node_index), - }, - ); + instances.push(ClipInstance::new(handle, spatial_node_index)); } self.clip_store.register_clip_template( @@ -2628,7 +2334,7 @@ impl<'a> SceneBuilder<'a> { &mut self, new_node_id: SpatialId, parent_node_index: SpatialNodeIndex, - external_id: ExternalScrollId, + external_id: Option<ExternalScrollId>, pipeline_id: PipelineId, frame_rect: &LayoutRect, content_size: &LayoutSize, @@ -2695,68 +2401,62 @@ impl<'a> SceneBuilder<'a> { // Gaussian blur with a standard deviation equal to half the blur radius." let std_deviation = pending_shadow.shadow.blur_radius * 0.5; + // If the shadow has no blur, any elements will get directly rendered + // into the parent picture surface, instead of allocating and drawing + // into an intermediate surface. In this case, we will need to apply + // the local clip rect to primitives. + let is_passthrough = pending_shadow.shadow.blur_radius == 0.0; + + // shadows always rasterize in local space. + // TODO(gw): expose API for clients to specify a raster scale + let raster_space = if is_passthrough { + self.sc_stack.last().unwrap().requested_raster_space + } else { + RasterSpace::Local(1.0) + }; + // Add any primitives that come after this shadow in the item // list to this shadow. let mut prim_list = PrimitiveList::empty(); - let blur_filter = Filter::Blur(std_deviation, std_deviation); - let blur_is_noop = blur_filter.is_noop(); for item in &items { - let (instance, info, spatial_node_index) = match item { + match item { ShadowItem::Image(ref pending_image) => { - self.create_shadow_prim( + self.add_shadow_prim( &pending_shadow, pending_image, - blur_is_noop, + &mut prim_list, ) } ShadowItem::LineDecoration(ref pending_line_dec) => { - self.create_shadow_prim( + self.add_shadow_prim( &pending_shadow, pending_line_dec, - blur_is_noop, + &mut prim_list, ) } ShadowItem::NormalBorder(ref pending_border) => { - self.create_shadow_prim( + self.add_shadow_prim( &pending_shadow, pending_border, - blur_is_noop, + &mut prim_list, ) } ShadowItem::Primitive(ref pending_primitive) => { - self.create_shadow_prim( + self.add_shadow_prim( &pending_shadow, pending_primitive, - blur_is_noop, + &mut prim_list, ) } ShadowItem::TextRun(ref pending_text_run) => { - self.create_shadow_prim( + self.add_shadow_prim( &pending_shadow, pending_text_run, - blur_is_noop, + &mut prim_list, ) } - _ => { - continue; - } - }; - - if blur_is_noop { - self.add_primitive_to_draw_list( - instance, - info.rect, - spatial_node_index, - info.flags, - ); - } else { - prim_list.add_prim( - instance, - info.rect, - spatial_node_index, - info.flags, - ); + _ => {} } } @@ -2767,10 +2467,9 @@ impl<'a> SceneBuilder<'a> { // blur radius is 0, the code in Picture::prepare_for_render will // detect this and mark the picture to be drawn directly into the // parent picture, which avoids an intermediate surface and blur. - let blur_filter = Filter::Blur(std_deviation, std_deviation); - assert!(!blur_filter.is_noop()); - let composite_mode = Some(PictureCompositeMode::Filter(blur_filter)); - let composite_mode_key = composite_mode.clone().into(); + let blur_filter = Filter::Blur(std_deviation); + let composite_mode = PictureCompositeMode::Filter(blur_filter); + let composite_mode_key = Some(composite_mode.clone()).into(); // Pass through configuration information about whether WR should // do the bounding rect inflation for text shadows. @@ -2782,12 +2481,15 @@ impl<'a> SceneBuilder<'a> { let shadow_pic_index = PictureIndex(self.prim_store.pictures .alloc() .init(PicturePrimitive::new_image( - composite_mode, + Some(composite_mode), Picture3DContext::Out, - false, + None, + is_passthrough, PrimitiveFlags::IS_BACKFACE_VISIBLE, + raster_space, prim_list, pending_shadow.spatial_node_index, + None, options, )) ); @@ -2852,29 +2554,33 @@ impl<'a> SceneBuilder<'a> { self.pending_shadow_items = items; } - fn create_shadow_prim<P>( + fn add_shadow_prim<P>( &mut self, pending_shadow: &PendingShadow, pending_primitive: &PendingPrimitive<P>, - blur_is_noop: bool, - ) -> (PrimitiveInstance, LayoutPrimitiveInfo, SpatialNodeIndex) + prim_list: &mut PrimitiveList, + ) where P: InternablePrimitive + CreateShadow, Interners: AsMut<Interner<P>>, { + let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device; + snap_to_device.set_target_spatial_node( + pending_primitive.spatial_node_index, + &self.spatial_tree, + ); + // Offset the local rect and clip rect by the shadow offset. The pending // primitive has already been snapped, but we will need to snap the // shadow after translation. We don't need to worry about the size // changing because the shadow has the same raster space as the // primitive, and thus we know the size is already rounded. let mut info = pending_primitive.info.clone(); - info.rect = self.snap_rect( + info.rect = snap_to_device.snap_rect( &info.rect.translate(pending_shadow.shadow.offset), - pending_primitive.spatial_node_index, ); - info.clip_rect = self.snap_rect( + info.clip_rect = snap_to_device.snap_rect( &info.clip_rect.translate(pending_shadow.shadow.offset), - pending_primitive.spatial_node_index, ); // Construct and add a primitive for the given shadow. @@ -2882,14 +2588,16 @@ impl<'a> SceneBuilder<'a> { &info, pending_primitive.spatial_node_index, pending_primitive.clip_chain_id, - pending_primitive.prim.create_shadow( - &pending_shadow.shadow, - blur_is_noop, - self.raster_space_stack.last().cloned().unwrap(), - ), + pending_primitive.prim.create_shadow(&pending_shadow.shadow), ); - (shadow_prim_instance, info, pending_primitive.spatial_node_index) + // Add the new primitive to the shadow picture. + prim_list.add_prim( + shadow_prim_instance, + info.rect, + pending_primitive.spatial_node_index, + info.flags, + ); } fn add_shadow_prim_to_draw_list<P>( @@ -2931,25 +2639,54 @@ impl<'a> SceneBuilder<'a> { ) { } - pub fn add_clear_rectangle( + pub fn add_solid_rectangle( &mut self, spatial_node_index: SpatialNodeIndex, clip_chain_id: ClipChainId, info: &LayoutPrimitiveInfo, + color: PropertyBinding<ColorF>, ) { - // Clear prims must be in their own picture cache slice to - // be composited correctly. - self.add_tile_cache_barrier_if_needed(SliceFlags::empty()); + match color { + PropertyBinding::Value(value) => { + if value.a == 0.0 { + // Don't add transparent rectangles to the draw list, + // but do consider them for hit testing. This allows + // specifying invisible hit testing areas. + self.add_primitive_to_hit_testing_list( + info, + spatial_node_index, + clip_chain_id, + ); + return; + } + }, + PropertyBinding::Binding(..) => {}, + } self.add_primitive( spatial_node_index, clip_chain_id, info, Vec::new(), - PrimitiveKeyKind::Clear, + PrimitiveKeyKind::Rectangle { + color: color.into(), + }, ); + } - self.add_tile_cache_barrier_if_needed(SliceFlags::empty()); + pub fn add_clear_rectangle( + &mut self, + spatial_node_index: SpatialNodeIndex, + clip_chain_id: ClipChainId, + info: &LayoutPrimitiveInfo, + ) { + self.add_primitive( + spatial_node_index, + clip_chain_id, + info, + Vec::new(), + PrimitiveKeyKind::Clear, + ); } pub fn add_line( @@ -3066,7 +2803,7 @@ impl<'a> SceneBuilder<'a> { &info, gradient.start_point, gradient.end_point, - read_gradient_stops(gradient_stops), + gradient_stops, gradient.extend_mode, LayoutSize::new(border.height as f32, border.width as f32), LayoutSize::zero(), @@ -3091,7 +2828,7 @@ impl<'a> SceneBuilder<'a> { gradient.start_offset * gradient.radius.width, gradient.end_offset * gradient.radius.width, gradient.radius.width / gradient.radius.height, - read_gradient_stops(gradient_stops), + gradient_stops, gradient.extend_mode, LayoutSize::new(border.height as f32, border.width as f32), LayoutSize::zero(), @@ -3147,7 +2884,7 @@ impl<'a> SceneBuilder<'a> { info: &LayoutPrimitiveInfo, start_point: LayoutPoint, end_point: LayoutPoint, - stops: Vec<GradientStopKey>, + stops: ItemRange<GradientStop>, extend_mode: ExtendMode, stretch_size: LayoutSize, mut tile_spacing: LayoutSize, @@ -3156,17 +2893,19 @@ impl<'a> SceneBuilder<'a> { let mut prim_rect = info.rect; simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect); - let mut is_entirely_transparent = true; - for stop in &stops { - if stop.color.a > 0 { - is_entirely_transparent = false; - break; + let mut max_alpha: f32 = 0.0; + + let stops = stops.iter().map(|stop| { + max_alpha = max_alpha.max(stop.color.a); + GradientStopKey { + offset: stop.offset, + color: stop.color.into(), } - } + }).collect(); // If all the stops have no alpha, then this // gradient can't contribute to the scene. - if is_entirely_transparent { + if max_alpha <= 0.0 { return None; } @@ -3188,13 +2927,6 @@ impl<'a> SceneBuilder<'a> { (start_point, end_point) }; - let is_tiled = prim_rect.size.width > stretch_size.width - || prim_rect.size.height > stretch_size.height; - // SWGL has a fast-path that can render gradients faster than it can sample from the - // texture cache so we disable caching in this configuration. Cached gradients are - // faster on hardware. - let cached = !self.config.is_software || is_tiled; - Some(LinearGradient { extend_mode, start_point: sp.into(), @@ -3204,7 +2936,6 @@ impl<'a> SceneBuilder<'a> { stops, reverse_stops, nine_patch, - cached, }) } @@ -3215,7 +2946,7 @@ impl<'a> SceneBuilder<'a> { start_radius: f32, end_radius: f32, ratio_xy: f32, - stops: Vec<GradientStopKey>, + stops: ItemRange<GradientStop>, extend_mode: ExtendMode, stretch_size: LayoutSize, mut tile_spacing: LayoutSize, @@ -3230,6 +2961,13 @@ impl<'a> SceneBuilder<'a> { ratio_xy, }; + let stops = stops.iter().map(|stop| { + GradientStopKey { + offset: stop.offset, + color: stop.color.into(), + } + }).collect(); + RadialGradient { extend_mode, center: center.into(), @@ -3337,18 +3075,10 @@ impl<'a> SceneBuilder<'a> { }) .collect(); - // Query the current requested raster space (stack handled by push/pop - // stacking context). - let requested_raster_space = self.raster_space_stack - .last() - .cloned() - .unwrap(); - TextRun { glyphs: Arc::new(glyphs), font, shadow: false, - requested_raster_space, } }; @@ -3463,6 +3193,7 @@ impl<'a> SceneBuilder<'a> { }; let backdrop_spatial_node_index = self.prim_store.pictures[backdrop_pic_index.0].spatial_node_index; + let requested_raster_space = self.sc_stack.last().expect("no active stacking context").requested_raster_space; let mut instance = self.create_primitive( info, @@ -3501,10 +3232,13 @@ impl<'a> SceneBuilder<'a> { .init(PicturePrimitive::new_image( composite_mode.clone(), Picture3DContext::Out, + None, true, prim_flags, + requested_raster_space, prim_list, backdrop_spatial_node_index, + None, PictureOptions { inflate_if_required: false, }, @@ -3519,17 +3253,15 @@ impl<'a> SceneBuilder<'a> { ); } - let mut source = PictureChainBuilder::from_instance( + let (mut filtered_pic_index, mut filtered_instance) = self.wrap_prim_with_filters( instance, - info.flags, - backdrop_spatial_node_index, - ); - - source = self.wrap_prim_with_filters( - source, + backdrop_pic_index, filters, filter_primitives, filter_datas, + info.flags, + requested_raster_space, + backdrop_spatial_node_index, false, ); @@ -3544,20 +3276,23 @@ impl<'a> SceneBuilder<'a> { let filter_primitives = stacking_context.composite_ops.filter_primitives.clone(); let filter_datas = stacking_context.composite_ops.filter_datas.clone(); - source = self.wrap_prim_with_filters( - source, + let (pic_index, instance) = self.wrap_prim_with_filters( + filtered_instance, + filtered_pic_index, filters, filter_primitives, filter_datas, + info.flags, + requested_raster_space, + backdrop_spatial_node_index, false, ); + + filtered_instance = instance; + filtered_pic_index = pic_index; } - let filtered_instance = source.finalize( - clip_chain_id, - &mut self.interners, - &mut self.prim_store, - ); + filtered_instance.clip_chain_id = clip_chain_id; self.sc_stack .iter_mut() @@ -3573,9 +3308,6 @@ impl<'a> SceneBuilder<'a> { ); } - /// Create pictures for each stacking context rendered into their parents, down to the nearest - /// backdrop root until we have a picture that represents the contents of all primitives added - /// since the backdrop root pub fn cut_backdrop_picture(&mut self) -> Option<PictureIndex> { let mut flattened_items = None; let mut backdrop_root = None; @@ -3619,15 +3351,18 @@ impl<'a> SceneBuilder<'a> { Some(pic_index) } - #[must_use] fn wrap_prim_with_filters( &mut self, - mut source: PictureChainBuilder, + mut cur_instance: PrimitiveInstance, + mut current_pic_index: PictureIndex, mut filter_ops: Vec<Filter>, mut filter_primitives: Vec<FilterPrimitive>, filter_datas: Vec<FilterData>, + flags: PrimitiveFlags, + requested_raster_space: RasterSpace, + spatial_node_index: SpatialNodeIndex, inflate_if_required: bool, - ) -> PictureChainBuilder { + ) -> (PictureIndex, PrimitiveInstance) { // TODO(cbrewster): Currently CSS and SVG filters live side by side in WebRender, but unexpected results will // happen if they are used simulataneously. Gecko only provides either filter ops or filter primitives. // At some point, these two should be combined and CSS filters should be expressed in terms of SVG filters. @@ -3637,7 +3372,7 @@ impl<'a> SceneBuilder<'a> { // For each filter, create a new image with that composite mode. let mut current_filter_data_index = 0; for filter in &mut filter_ops { - let composite_mode = match filter { + let composite_mode = Some(match *filter { Filter::ComponentTransfer => { let filter_data = &filter_datas[current_filter_data_index]; @@ -3666,22 +3401,50 @@ impl<'a> SceneBuilder<'a> { PictureCompositeMode::ComponentTransferFilter(handle) } } - _ => { - if filter.is_noop() { - continue; - } else { - PictureCompositeMode::Filter(filter.clone()) - } - } - }; + _ => PictureCompositeMode::Filter(filter.clone()), + }); - source = source.add_picture( - composite_mode, - Picture3DContext::Out, - PictureOptions { inflate_if_required }, + let mut prim_list = PrimitiveList::empty(); + prim_list.add_prim( + cur_instance.clone(), + LayoutRect::zero(), + spatial_node_index, + flags, + ); + + let filter_pic_index = PictureIndex(self.prim_store.pictures + .alloc() + .init(PicturePrimitive::new_image( + composite_mode.clone(), + Picture3DContext::Out, + None, + true, + flags, + requested_raster_space, + prim_list, + spatial_node_index, + None, + PictureOptions { + inflate_if_required, + }, + )) + ); + + current_pic_index = filter_pic_index; + cur_instance = create_prim_instance( + current_pic_index, + composite_mode.into(), + ClipChainId::NONE, &mut self.interners, - &mut self.prim_store, ); + + if cur_instance.is_chased() { + println!("\tis a composite picture for a stacking context with {:?}", filter); + } + + // Run the optimize pass on this picture, to see if we can + // collapse opacity and avoid drawing to an off-screen surface. + self.prim_store.optimize_picture_if_possible(current_pic_index); } if !filter_primitives.is_empty() { @@ -3711,27 +3474,55 @@ impl<'a> SceneBuilder<'a> { filter_datas, ); - source = source.add_picture( - composite_mode, - Picture3DContext::Out, - PictureOptions { inflate_if_required }, + let mut prim_list = PrimitiveList::empty(); + prim_list.add_prim( + cur_instance.clone(), + LayoutRect::zero(), + spatial_node_index, + flags, + ); + + let filter_pic_index = PictureIndex(self.prim_store.pictures + .alloc() + .init(PicturePrimitive::new_image( + Some(composite_mode.clone()), + Picture3DContext::Out, + None, + true, + flags, + requested_raster_space, + prim_list, + spatial_node_index, + None, + PictureOptions { + inflate_if_required, + }, + )) + ); + + current_pic_index = filter_pic_index; + cur_instance = create_prim_instance( + current_pic_index, + Some(composite_mode).into(), + ClipChainId::NONE, &mut self.interners, - &mut self.prim_store, ); - } - source + if cur_instance.is_chased() { + println!("\tis a composite picture for a stacking context with an SVG filter"); + } + + // Run the optimize pass on this picture, to see if we can + // collapse opacity and avoid drawing to an off-screen surface. + self.prim_store.optimize_picture_if_possible(current_pic_index); + } + (current_pic_index, cur_instance) } } pub trait CreateShadow { - fn create_shadow( - &self, - shadow: &Shadow, - blur_is_noop: bool, - current_raster_space: RasterSpace, - ) -> Self; + fn create_shadow(&self, shadow: &Shadow) -> Self; } pub trait IsVisible { @@ -3747,19 +3538,6 @@ struct ExtendedPrimitiveInstance { flags: PrimitiveFlags, } -/// Internal tracking information about the currently pushed stacking context. -/// Used to track what operations need to happen when a stacking context is popped. -struct StackingContextInfo { - /// If true, pop an entry from the hit-testing scene. - pop_hit_testing_clip: bool, - /// If true, pop and entry from the containing block stack. - pop_containing_block: bool, - /// If true, pop an entry from the flattened stacking context stack. - pop_stacking_context: bool, - /// If true, set a tile cache barrier when popping the stacking context. - set_tile_cache_barrier: bool, -} - /// Properties of a stacking context that are maintained /// during creation of the scene. These structures are /// not persisted after the initial scene build. @@ -3770,11 +3548,20 @@ struct FlattenedStackingContext { /// Primitive instance flags for compositing this stacking context prim_flags: PrimitiveFlags, + /// Whether or not the caller wants this drawn in + /// screen space (quality) or local space (performance) + requested_raster_space: RasterSpace, + /// The positioning node for this stacking context spatial_node_index: SpatialNodeIndex, /// The clip chain for this stacking context clip_chain_id: ClipChainId, + clip_id: Option<ClipId>, + + /// If set, this should be provided to caller + /// as an output texture. + frame_output_pipeline_id: Option<PipelineId>, /// The list of filters / mix-blend-mode for this /// stacking context. @@ -3784,6 +3571,9 @@ struct FlattenedStackingContext { /// be an offscreen surface. blit_reason: BlitReason, + /// Pipeline this stacking context belongs to. + pipeline_id: PipelineId, + /// CSS transform-style property. transform_style: TransformStyle, @@ -3796,8 +3586,12 @@ struct FlattenedStackingContext { /// True if this stacking context is redundant (i.e. doesn't require a surface) is_redundant: bool, - /// Flags identifying the type of container (among other things) this stacking context is - flags: StackingContextFlags, + /// A helper struct to snap local rects in device space. During frame + /// building we may establish new raster roots, however typically that is in + /// cases where we won't be applying snapping (e.g. has perspective), or in + /// edge cases (e.g. SVG filter) where we can accept slightly incorrect + /// behaviour in favour of getting the common case right. + snap_to_device: SpaceSnapper, } impl FlattenedStackingContext { @@ -3806,38 +3600,171 @@ impl FlattenedStackingContext { self.transform_style == TransformStyle::Preserve3D && self.composite_ops.is_empty() } + /// Set up appropriate cluster flags for picture caching on this stacking context. + fn init_picture_caching( + &mut self, + spatial_tree: &SpatialTree, + clip_store: &ClipStore, + quality_settings: &QualitySettings, + ) -> usize { + struct SliceInfo { + cluster_index: usize, + scroll_root: SpatialNodeIndex, + cluster_flags: ClusterFlags, + } + + let mut content_slice_count = 0; + let mut slices: Vec<SliceInfo> = Vec::new(); + + // Step through each cluster, and work out where the slice boundaries should be. + for (cluster_index, cluster) in self.prim_list.clusters.iter().enumerate() { + let scroll_root = spatial_tree.find_scroll_root( + cluster.spatial_node_index, + ); + + // We want to create a slice in the following conditions: + // (1) This cluster is a scrollbar + // (2) Certain conditions when the scroll root changes (see below) + // (3) No slice exists yet + let mut cluster_flags = ClusterFlags::empty(); + + if cluster.flags.contains(ClusterFlags::SCROLLBAR_CONTAINER) { + // Scrollbar containers need to ensure that a new slice is + // created both before and after the scrollbar, so that no + // other prims with the same scroll root sneak into this slice. + cluster_flags.insert( + ClusterFlags::CREATE_PICTURE_CACHE_PRE | + ClusterFlags::CREATE_PICTURE_CACHE_POST + ); + } + + let create_new_slice_for_scroll_root = + slices.last().map(|slice| { + match (slice.scroll_root, scroll_root) { + (ROOT_SPATIAL_NODE_INDEX, ROOT_SPATIAL_NODE_INDEX) => { + // Both current slice and this cluster are fixed position, no need to cut + false + } + (ROOT_SPATIAL_NODE_INDEX, _) => { + // A real scroll root is being established, so create a cache slice + true + } + (_, ROOT_SPATIAL_NODE_INDEX) => { + // If quality settings force subpixel AA over performance, skip creating + // a slice for the fixed position element(s) here. + if quality_settings.force_subpixel_aa_where_possible { + return false; + } + + // A fixed position slice is encountered within a scroll root. Only create + // a slice in this case if all the clips referenced by this cluster are also + // fixed position. There's no real point in creating slices for these cases, + // since we'll have to rasterize them as the scrolling clip moves anyway. It + // also allows us to retain subpixel AA in these cases. For these types of + // slices, the intra-slice dirty rect handling typically works quite well + // (a common case is parallax scrolling effects). + for prim_instance in &cluster.prim_instances { + let mut current_clip_chain_id = prim_instance.clip_chain_id; + + while current_clip_chain_id != ClipChainId::NONE { + let clip_chain_node = &clip_store + .clip_chain_nodes[current_clip_chain_id.0 as usize]; + let spatial_root = spatial_tree.find_scroll_root(clip_chain_node.spatial_node_index); + if spatial_root != ROOT_SPATIAL_NODE_INDEX { + return false; + } + current_clip_chain_id = clip_chain_node.parent_clip_chain_id; + } + } + + true + } + (curr_scroll_root, scroll_root) => { + // Two scrolling roots - only need a new slice if they differ + curr_scroll_root != scroll_root + } + } + }).unwrap_or(true); + + if create_new_slice_for_scroll_root { + cluster_flags.insert(ClusterFlags::CREATE_PICTURE_CACHE_PRE); + } + + // Create a new slice if required + if !cluster_flags.is_empty() { + slices.push(SliceInfo { + cluster_index, + scroll_root, + cluster_flags, + }); + } + } + + // If the page would create too many slices (an arbitrary definition where + // it's assumed the GPU memory + compositing overhead would be too high) + // then just create a single picture cache for the entire content. This at + // least means that we can cache small content changes efficiently when + // scrolling isn't occurring. Scrolling regions will be handled reasonably + // efficiently by the dirty rect tracking (since it's likely that if the + // page has so many slices there isn't a single major scroll region). + const MAX_CONTENT_SLICES: usize = 8; + + if slices.len() > MAX_CONTENT_SLICES { + if let Some(cluster) = self.prim_list.clusters.first_mut() { + content_slice_count = 1; + cluster.flags.insert(ClusterFlags::CREATE_PICTURE_CACHE_PRE); + cluster.cache_scroll_root = None; + } + } else { + // Walk the list of slices, setting appropriate flags on the clusters which are + // later used during setup_picture_caching. + for slice in slices.drain(..) { + content_slice_count += 1; + let cluster = &mut self.prim_list.clusters[slice.cluster_index]; + // Mark that this cluster creates a picture cache slice + cluster.flags.insert(slice.cluster_flags); + cluster.cache_scroll_root = Some(slice.scroll_root); + } + } + + // Always end the cache at the end of the stacking context, so that we don't + // cache anything from primitives outside this pipeline in the same slice. + if let Some(cluster) = self.prim_list.clusters.last_mut() { + cluster.flags.insert(ClusterFlags::CREATE_PICTURE_CACHE_POST); + } + + content_slice_count + } + /// Return true if the stacking context isn't needed. pub fn is_redundant( - sc_flags: StackingContextFlags, context_3d: &Picture3DContext<ExtendedPrimitiveInstance>, composite_ops: &CompositeOps, - blit_reason: BlitReason, - parent: Option<&FlattenedStackingContext>, prim_flags: PrimitiveFlags, + blit_reason: BlitReason, + requested_raster_space: RasterSpace, + parent: &FlattenedStackingContext, ) -> bool { - // If this is a backdrop or blend container, it's needed - if sc_flags.intersects(StackingContextFlags::IS_BACKDROP_ROOT | StackingContextFlags::IS_BLEND_CONTAINER) { + // Any 3d context is required + if let Picture3DContext::In { .. } = context_3d { return false; } - // Any 3d context is required - if let Picture3DContext::In { .. } = context_3d { + // If there are filters / mix-blend-mode + if !composite_ops.filters.is_empty() { return false; } - // If any filters are present that affect the output - if composite_ops.has_valid_filters() { + // If there are svg filters + if !composite_ops.filter_primitives.is_empty() { return false; } // We can skip mix-blend modes if they are the first primitive in a stacking context, // see pop_stacking_context for a full explanation. - if composite_ops.mix_blend_mode.is_some() { - if let Some(parent) = parent { - if !parent.prim_list.is_empty() { - return false; - } - } + if composite_ops.mix_blend_mode.is_some() && + !parent.prim_list.is_empty() { + return false; } // If backface visibility is explicitly set. @@ -3845,11 +3772,21 @@ impl FlattenedStackingContext { return false; } + // If rasterization space is different + if requested_raster_space != parent.requested_raster_space { + return false; + } + // If need to isolate in surface due to clipping / mix-blend-mode if !blit_reason.is_empty() { return false; } + // If this stacking context is a scrollbar, retain it so it can form a picture cache slice + if prim_flags.contains(PrimitiveFlags::IS_SCROLLBAR_CONTAINER) { + return false; + } + // It is redundant! true } @@ -3871,10 +3808,13 @@ impl FlattenedStackingContext { .init(PicturePrimitive::new_image( composite_mode.clone(), flat_items_context_3d, + None, true, self.prim_flags, + self.requested_raster_space, mem::replace(&mut self.prim_list, PrimitiveList::empty()), self.spatial_node_index, + None, PictureOptions::default(), )) ); @@ -4043,11 +3983,108 @@ fn process_repeat_size( ) } -fn read_gradient_stops(stops: ItemRange<GradientStop>) -> Vec<GradientStopKey> { - stops.iter().map(|stop| { - GradientStopKey { - offset: stop.offset, - color: stop.color.into(), +/// Given a PrimitiveList and scroll root, construct a tile cache primitive instance +/// that wraps the primitive list. +fn create_tile_cache( + slice: usize, + slice_flags: SliceFlags, + scroll_root: SpatialNodeIndex, + prim_list: PrimitiveList, + background_color: Option<ColorF>, + shared_clips: Vec<ClipInstance>, + interners: &mut Interners, + prim_store: &mut PrimitiveStore, + clip_store: &mut ClipStore, + picture_cache_spatial_nodes: &mut FastHashSet<SpatialNodeIndex>, + frame_builder_config: &FrameBuilderConfig, +) -> PrimitiveInstance { + // Add this spatial node to the list to check for complex transforms + // at the start of a frame build. + picture_cache_spatial_nodes.insert(scroll_root); + + // Now, create a picture with tile caching enabled that will hold all + // of the primitives selected as belonging to the main scroll root. + let pic_key = PictureKey::new( + Picture { + composite_mode_key: PictureCompositeKey::Identity, + }, + ); + + let pic_data_handle = interners + .picture + .intern(&pic_key, || ()); + + // Build a clip-chain for the tile cache, that contains any of the shared clips + // we will apply when drawing the tiles. In all cases provided by Gecko, these + // are rectangle clips with a scale/offset transform only, and get handled as + // a simple local clip rect in the vertex shader. However, this should in theory + // also work with any complex clips, such as rounded rects and image masks, by + // producing a clip mask that is applied to the picture cache tiles. + let mut parent_clip_chain_id = ClipChainId::NONE; + for clip_instance in &shared_clips { + // Add this spatial node to the list to check for complex transforms + // at the start of a frame build. + picture_cache_spatial_nodes.insert(clip_instance.spatial_node_index); + + parent_clip_chain_id = clip_store.add_clip_chain_node( + clip_instance.handle, + clip_instance.spatial_node_index, + parent_clip_chain_id, + ); + } + + let tile_cache = Box::new(TileCacheInstance::new( + slice, + slice_flags, + scroll_root, + background_color, + shared_clips, + parent_clip_chain_id, + frame_builder_config, + )); + + let pic_index = prim_store.pictures.alloc().init(PicturePrimitive::new_image( + Some(PictureCompositeMode::TileCache { }), + Picture3DContext::Out, + None, + true, + PrimitiveFlags::IS_BACKFACE_VISIBLE, + RasterSpace::Screen, + prim_list, + scroll_root, + Some(tile_cache), + PictureOptions::default(), + )); + + PrimitiveInstance::new( + LayoutRect::max_rect(), + PrimitiveInstanceKind::Picture { + data_handle: pic_data_handle, + pic_index: PictureIndex(pic_index), + segment_instance_index: SegmentInstanceIndex::INVALID, + }, + parent_clip_chain_id, + ) +} + +// Helper fn to collect clip handles from a given clip chain. +fn add_clips( + clip_chain_id: ClipChainId, + prim_clips: &mut Vec<ClipInstance>, + clip_store: &ClipStore, + interners: &Interners, +) { + let mut current_clip_chain_id = clip_chain_id; + + while current_clip_chain_id != ClipChainId::NONE { + let clip_chain_node = &clip_store + .clip_chain_nodes[current_clip_chain_id.0 as usize]; + + let clip_node_data = &interners.clip[clip_chain_node.handle]; + if let ClipNodeKind::Rectangle = clip_node_data.clip_node_kind { + prim_clips.push(ClipInstance::new(clip_chain_node.handle, clip_chain_node.spatial_node_index)); } - }).collect() + + current_clip_chain_id = clip_chain_node.parent_clip_chain_id; + } } diff --git a/third_party/webrender/webrender/src/screen_capture.rs b/third_party/webrender/webrender/src/screen_capture.rs index 9da2db2397e..56fdf458674 100644 --- a/third_party/webrender/webrender/src/screen_capture.rs +++ b/third_party/webrender/webrender/src/screen_capture.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; -use api::{ImageFormat, ImageBufferKind}; +use api::{ImageFormat, TextureTarget}; use api::units::*; use gleam::gl::GlType; @@ -115,9 +115,6 @@ impl AsyncScreenshotGrabber { ) -> (AsyncScreenshotHandle, DeviceIntSize) { let screenshot_size = match self.mode { AsyncScreenshotGrabberMode::ProfilerScreenshots => { - assert_ne!(window_rect.size.width, 0); - assert_ne!(window_rect.size.height, 0); - let scale = (buffer_size.width as f32 / window_rect.size.width as f32) .min(buffer_size.height as f32 / window_rect.size.height as f32); @@ -144,7 +141,7 @@ impl AsyncScreenshotGrabber { let read_size = match self.mode { AsyncScreenshotGrabberMode::ProfilerScreenshots => { let stride = (screenshot_size.width * image_format.bytes_per_pixel()) as usize; - let rounded = round_up_to_multiple(stride, device.required_pbo_stride().num_bytes(image_format)); + let rounded = round_up_to_multiple(stride, device.optimal_pbo_stride().num_bytes(image_format)); let optimal_width = rounded as i32 / image_format.bytes_per_pixel(); DeviceIntSize::new( @@ -185,7 +182,7 @@ impl AsyncScreenshotGrabber { 0, ); - ReadTarget::from_texture(&self.scaling_textures[0]) + ReadTarget::from_texture(&self.scaling_textures[0], 0) } AsyncScreenshotGrabberMode::CompositionRecorder => ReadTarget::Default, @@ -251,12 +248,13 @@ impl AsyncScreenshotGrabber { // texture is the wrong size, then create a new one. if level == self.scaling_textures.len() || self.scaling_textures[level].get_dimensions() != texture_size { let texture = device.create_texture( - ImageBufferKind::Texture2D, + TextureTarget::Default, image_format, texture_size.width, texture_size.height, TextureFilter::Linear, Some(RenderTargetInfo { has_depth: false }), + 1, ); if level == self.scaling_textures.len() { self.scaling_textures.push(texture); @@ -280,14 +278,14 @@ impl AsyncScreenshotGrabber { ); ( - ReadTarget::from_texture(&self.scaling_textures[level + 1]), + ReadTarget::from_texture(&self.scaling_textures[level + 1], 0), DeviceIntRect::new(DeviceIntPoint::new(0, 0), dest_size * 2), ) } else { (read_target, read_target_rect) }; - let draw_target = DrawTarget::from_texture(&self.scaling_textures[level], false); + let draw_target = DrawTarget::from_texture(&self.scaling_textures[level], 0 as _, false); let draw_target_rect = draw_target .to_framebuffer_rect(DeviceIntRect::new(DeviceIntPoint::new(0, 0), dest_size)); diff --git a/third_party/webrender/webrender/src/segment.rs b/third_party/webrender/webrender/src/segment.rs index 84d19cb4fc1..77bca528215 100644 --- a/third_party/webrender/webrender/src/segment.rs +++ b/third_party/webrender/webrender/src/segment.rs @@ -49,35 +49,13 @@ //! [clip.rs]: ../clip/index.html //! -use api::{BorderRadius, ClipMode}; +use api::{BorderRadius, ClipMode, EdgeAaSegmentMask}; use api::units::*; use std::{cmp, usize}; -use crate::util::{extract_inner_rect_safe}; +use crate::util::{extract_inner_rect_safe, RectHelpers}; use smallvec::SmallVec; bitflags! { - /// Each bit of the edge AA mask is: - /// 0, when the edge of the primitive needs to be considered for AA - /// 1, when the edge of the segment needs to be considered for AA - /// - /// *Note*: the bit values have to match the shader logic in - /// `write_transform_vertex()` function. - #[cfg_attr(feature = "capture", derive(Serialize))] - #[cfg_attr(feature = "replay", derive(Deserialize))] - #[derive(MallocSizeOf)] - pub struct EdgeAaSegmentMask: u8 { - /// - const LEFT = 0x1; - /// - const TOP = 0x2; - /// - const RIGHT = 0x4; - /// - const BOTTOM = 0x8; - } -} - -bitflags! { pub struct ItemFlags: u8 { const X_ACTIVE = 0x1; const Y_ACTIVE = 0x2; @@ -302,7 +280,7 @@ impl SegmentBuilder { ) { self.has_interesting_clips = true; - if inner_rect.is_empty() { + if !inner_rect.is_well_formed_and_nonempty() { self.items.push(Item::new( outer_rect, None, @@ -353,8 +331,6 @@ impl SegmentBuilder { ), ]; - self.items.reserve(segments.len() + 1); - for segment in segments { self.items.push(Item::new( *segment, @@ -402,8 +378,6 @@ impl SegmentBuilder { let p2 = inner.bottom_right(); let p3 = rect.bottom_right(); - self.items.reserve(9); - let corner_segments = &[ LayoutRect::new( LayoutPoint::new(p0.x, p0.y), @@ -705,9 +679,9 @@ fn emit_segment_if_needed( #[cfg(test)] mod test { - use api::{BorderRadius, ClipMode}; + use api::{BorderRadius, ClipMode, EdgeAaSegmentMask}; use api::units::{LayoutPoint, LayoutRect, LayoutSize}; - use super::{Segment, SegmentBuilder, EdgeAaSegmentMask}; + use super::{Segment, SegmentBuilder}; use std::cmp; fn rect(x0: f32, y0: f32, x1: f32, y1: f32) -> LayoutRect { diff --git a/third_party/webrender/webrender/src/renderer/shade.rs b/third_party/webrender/webrender/src/shade.rs index 1f7d5cb444f..cce58259015 100644 --- a/third_party/webrender/webrender/src/renderer/shade.rs +++ b/third_party/webrender/webrender/src/shade.rs @@ -2,15 +2,14 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{ImageBufferKind, units::DeviceSize}; use crate::batch::{BatchKey, BatchKind, BrushBatchKind, BatchFeatures}; -use crate::composite::{CompositeFeatures, CompositeSurfaceFormat}; +use crate::composite::CompositeSurfaceFormat; use crate::device::{Device, Program, ShaderError}; use euclid::default::Transform3D; use crate::glyph_rasterizer::GlyphFormat; use crate::renderer::{ desc, - BlendMode, DebugFlags, RendererError, RendererOptions, + BlendMode, DebugFlags, ImageBufferKind, RendererError, RendererOptions, TextureSampler, VertexArrayKind, ShaderPrecacheFlags, }; @@ -22,39 +21,33 @@ use std::rc::Rc; use webrender_build::shader::{ShaderFeatures, ShaderFeatureFlags, get_shader_features}; -/// Which extension version to use for texture external support. -#[derive(Clone, Copy, Debug, PartialEq)] -enum TextureExternalVersion { - // GL_OES_EGL_image_external_essl3 (Compatible with ESSL 3.0 and - // later shaders, but not supported on all GLES 3 devices.) - ESSL3, - // GL_OES_EGL_image_external (Compatible with ESSL 1.0 shaders) - ESSL1, -} - -fn get_feature_string(kind: ImageBufferKind, texture_external_version: TextureExternalVersion) -> &'static str { - match (kind, texture_external_version) { - (ImageBufferKind::Texture2D, _) => "TEXTURE_2D", - (ImageBufferKind::TextureRect, _) => "TEXTURE_RECT", - (ImageBufferKind::TextureExternal, TextureExternalVersion::ESSL3) => "TEXTURE_EXTERNAL", - (ImageBufferKind::TextureExternal, TextureExternalVersion::ESSL1) => "TEXTURE_EXTERNAL_ESSL1", +impl ImageBufferKind { + pub(crate) fn get_feature_string(&self) -> &'static str { + match *self { + ImageBufferKind::Texture2D => "TEXTURE_2D", + ImageBufferKind::Texture2DArray => "", + ImageBufferKind::TextureRect => "TEXTURE_RECT", + ImageBufferKind::TextureExternal => "TEXTURE_EXTERNAL", + } } -} -fn has_platform_support(kind: ImageBufferKind, gl_type: &GlType) -> bool { - match (kind, gl_type) { - (ImageBufferKind::Texture2D, _) => true, - (ImageBufferKind::TextureRect, &GlType::Gles) => false, - (ImageBufferKind::TextureRect, &GlType::Gl) => true, - (ImageBufferKind::TextureExternal, &GlType::Gles) => true, - (ImageBufferKind::TextureExternal, &GlType::Gl) => false, + fn has_platform_support(&self, gl_type: &GlType) -> bool { + match (*self, gl_type) { + (ImageBufferKind::Texture2D, _) => true, + (ImageBufferKind::Texture2DArray, _) => true, + (ImageBufferKind::TextureRect, &GlType::Gles) => false, + (ImageBufferKind::TextureRect, &GlType::Gl) => true, + (ImageBufferKind::TextureExternal, &GlType::Gles) => true, + (ImageBufferKind::TextureExternal, &GlType::Gl) => false, + } } } -pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 3] = [ +pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [ ImageBufferKind::Texture2D, ImageBufferKind::TextureRect, ImageBufferKind::TextureExternal, + ImageBufferKind::Texture2DArray, ]; const ADVANCED_BLEND_FEATURE: &str = "ADVANCED_BLEND"; @@ -63,18 +56,18 @@ const DEBUG_OVERDRAW_FEATURE: &str = "DEBUG_OVERDRAW"; const DITHERING_FEATURE: &str = "DITHERING"; const DUAL_SOURCE_FEATURE: &str = "DUAL_SOURCE_BLENDING"; const FAST_PATH_FEATURE: &str = "FAST_PATH"; +const PIXEL_LOCAL_STORAGE_FEATURE: &str = "PIXEL_LOCAL_STORAGE"; pub(crate) enum ShaderKind { Primitive, Cache(VertexArrayKind), - ClipCache(VertexArrayKind), + ClipCache, Brush, Text, #[allow(dead_code)] VectorStencil, #[allow(dead_code)] VectorCover, - #[allow(dead_code)] Resolve, Composite, Clear, @@ -92,13 +85,20 @@ impl LazilyCompiledShader { pub(crate) fn new( kind: ShaderKind, name: &'static str, - unsorted_features: &[&'static str], + features: &[&'static str], device: &mut Device, precache_flags: ShaderPrecacheFlags, shader_list: &ShaderFeatures, ) -> Result<Self, ShaderError> { - let mut features = unsorted_features.to_vec(); - features.sort(); + let mut shader = LazilyCompiledShader { + program: None, + name, + kind, + //Note: this isn't really the default state, but there is no chance + // an actual projection passed here would accidentally match. + cached_projection: Transform3D::identity(), + features: features.to_vec(), + }; // Ensure this shader config is in the available shader list so that we get // alerted if the list gets out-of-date when shaders or features are added. @@ -110,16 +110,6 @@ impl LazilyCompiledShader { config, ); - let mut shader = LazilyCompiledShader { - program: None, - name, - kind, - //Note: this isn't really the default state, but there is no chance - // an actual projection passed here would accidentally match. - cached_projection: Transform3D::identity(), - features, - }; - if precache_flags.intersects(ShaderPrecacheFlags::ASYNC_COMPILE | ShaderPrecacheFlags::FULL_COMPILE) { let t0 = precise_time_ns(); shader.get_internal(device, precache_flags)?; @@ -127,7 +117,7 @@ impl LazilyCompiledShader { debug!("[C: {:.1} ms ] Precache {} {:?}", (t1 - t0) as f64 / 1000000.0, name, - unsorted_features + features ); } @@ -138,7 +128,6 @@ impl LazilyCompiledShader { &mut self, device: &mut Device, projection: &Transform3D<f32>, - texture_size: Option<DeviceSize>, renderer_errors: &mut Vec<RendererError>, ) { let update_projection = self.cached_projection != *projection; @@ -150,9 +139,6 @@ impl LazilyCompiledShader { } }; device.bind_program(program); - if let Some(texture_size) = texture_size { - device.set_shader_texture_size(program, texture_size); - } if update_projection { device.set_uniforms(program, projection); // thanks NLL for this (`program` technically borrows `self`) @@ -202,7 +188,7 @@ impl LazilyCompiledShader { &self.features, ) } - ShaderKind::ClipCache(..) => { + ShaderKind::ClipCache => { create_clip_shader( self.name, device, @@ -223,7 +209,7 @@ impl LazilyCompiledShader { ShaderKind::Cache(format) => format, ShaderKind::VectorStencil => VertexArrayKind::VectorStencil, ShaderKind::VectorCover => VertexArrayKind::VectorCover, - ShaderKind::ClipCache(format) => format, + ShaderKind::ClipCache => VertexArrayKind::Clip, ShaderKind::Resolve => VertexArrayKind::Resolve, ShaderKind::Composite => VertexArrayKind::Composite, ShaderKind::Clear => VertexArrayKind::Clear, @@ -232,14 +218,9 @@ impl LazilyCompiledShader { let vertex_descriptor = match vertex_format { VertexArrayKind::Primitive => &desc::PRIM_INSTANCES, VertexArrayKind::LineDecoration => &desc::LINE, - VertexArrayKind::FastLinearGradient => &desc::FAST_LINEAR_GRADIENT, - VertexArrayKind::LinearGradient => &desc::LINEAR_GRADIENT, - VertexArrayKind::RadialGradient => &desc::RADIAL_GRADIENT, - VertexArrayKind::ConicGradient => &desc::CONIC_GRADIENT, + VertexArrayKind::Gradient => &desc::GRADIENT, VertexArrayKind::Blur => &desc::BLUR, - VertexArrayKind::ClipImage => &desc::CLIP_IMAGE, - VertexArrayKind::ClipRect => &desc::CLIP_RECT, - VertexArrayKind::ClipBoxShadow => &desc::CLIP_BOX_SHADOW, + VertexArrayKind::Clip => &desc::CLIP, VertexArrayKind::VectorStencil => &desc::VECTOR_STENCIL, VertexArrayKind::VectorCover => &desc::VECTOR_COVER, VertexArrayKind::Border => &desc::BORDER, @@ -253,7 +234,7 @@ impl LazilyCompiledShader { device.link_program(program, vertex_descriptor)?; device.bind_program(program); match self.kind { - ShaderKind::ClipCache(..) => { + ShaderKind::ClipCache => { device.bind_shader_samplers( &program, &[ @@ -274,12 +255,13 @@ impl LazilyCompiledShader { ("sColor1", TextureSampler::Color1), ("sColor2", TextureSampler::Color2), ("sDither", TextureSampler::Dither), + ("sPrevPassAlpha", TextureSampler::PrevPassAlpha), + ("sPrevPassColor", TextureSampler::PrevPassColor), ("sTransformPalette", TextureSampler::TransformPalette), ("sRenderTasks", TextureSampler::RenderTasks), ("sGpuCache", TextureSampler::GpuCache), ("sPrimitiveHeadersF", TextureSampler::PrimitiveHeadersF), ("sPrimitiveHeadersI", TextureSampler::PrimitiveHeadersI), - ("sClipMask", TextureSampler::ClipMask), ], ); } @@ -324,19 +306,22 @@ impl BrushShader { shader_list: &ShaderFeatures, use_advanced_blend: bool, use_dual_source: bool, + use_pixel_local_storage: bool, ) -> Result<Self, ShaderError> { - let opaque_features = features.to_vec(); let opaque = LazilyCompiledShader::new( ShaderKind::Brush, name, - &opaque_features, + features, device, precache_flags, &shader_list, )?; - let mut alpha_features = opaque_features.to_vec(); + let mut alpha_features = features.to_vec(); alpha_features.push(ALPHA_FEATURE); + if use_pixel_local_storage { + alpha_features.push(PIXEL_LOCAL_STORAGE_FEATURE); + } let alpha = LazilyCompiledShader::new( ShaderKind::Brush, @@ -404,7 +389,7 @@ impl BrushShader { }) } - fn get(&mut self, blend_mode: BlendMode, features: BatchFeatures, debug_flags: DebugFlags) + fn get(&mut self, blend_mode: BlendMode, debug_flags: DebugFlags) -> &mut LazilyCompiledShader { match blend_mode { _ if debug_flags.contains(DebugFlags::SHOW_OVERDRAW) => &mut self.debug_overdraw, @@ -413,22 +398,13 @@ impl BrushShader { BlendMode::PremultipliedAlpha | BlendMode::PremultipliedDestOut | BlendMode::SubpixelConstantTextColor(..) | - BlendMode::SubpixelWithBgColor | - BlendMode::Screen | - BlendMode::Exclusion => { - if features.contains(BatchFeatures::ALPHA_PASS) { - &mut self.alpha - } else { - &mut self.opaque - } - } + BlendMode::SubpixelWithBgColor => &mut self.alpha, BlendMode::Advanced(_) => { self.advanced_blend .as_mut() .expect("bug: no advanced blend shader loaded") } - BlendMode::SubpixelDualSource | - BlendMode::MultiplyDualSource => { + BlendMode::SubpixelDualSource => { self.dual_source .as_mut() .expect("bug: no dual source shader loaded") @@ -465,7 +441,6 @@ impl TextShader { ) -> Result<Self, ShaderError> { let mut simple_features = features.to_vec(); simple_features.push("ALPHA_PASS"); - simple_features.push("TEXTURE_2D"); let simple = LazilyCompiledShader::new( ShaderKind::Text, @@ -479,7 +454,6 @@ impl TextShader { let mut glyph_transform_features = features.to_vec(); glyph_transform_features.push("GLYPH_TRANSFORM"); glyph_transform_features.push("ALPHA_PASS"); - glyph_transform_features.push("TEXTURE_2D"); let glyph_transform = LazilyCompiledShader::new( ShaderKind::Text, @@ -492,7 +466,6 @@ impl TextShader { let mut debug_overdraw_features = features.to_vec(); debug_overdraw_features.push("DEBUG_OVERDRAW"); - debug_overdraw_features.push("TEXTURE_2D"); let debug_overdraw = LazilyCompiledShader::new( ShaderKind::Text, @@ -559,12 +532,9 @@ pub struct Shaders { pub cs_blur_rgba8: LazilyCompiledShader, pub cs_border_segment: LazilyCompiledShader, pub cs_border_solid: LazilyCompiledShader, - pub cs_scale: Vec<Option<LazilyCompiledShader>>, + pub cs_scale: LazilyCompiledShader, pub cs_line_decoration: LazilyCompiledShader, - pub cs_fast_linear_gradient: LazilyCompiledShader, - pub cs_linear_gradient: LazilyCompiledShader, - pub cs_radial_gradient: LazilyCompiledShader, - pub cs_conic_gradient: LazilyCompiledShader, + pub cs_gradient: LazilyCompiledShader, pub cs_svg_filter: LazilyCompiledShader, // Brush shaders @@ -574,9 +544,10 @@ pub struct Shaders { brush_blend: BrushShader, brush_mix_blend: BrushShader, brush_yuv_image: Vec<Option<BrushShader>>, + brush_conic_gradient: BrushShader, + brush_radial_gradient: BrushShader, brush_linear_gradient: BrushShader, brush_opacity: BrushShader, - brush_opacity_aa: BrushShader, /// These are "cache clip shaders". These shaders are used to /// draw clip instances into the cached clip mask. The results @@ -596,6 +567,12 @@ pub struct Shaders { pub ps_text_run: TextShader, pub ps_text_run_dual_source: Option<TextShader>, + // Helper shaders for pixel local storage render paths. + // pls_init: Initialize pixel local storage, based on current framebuffer value. + // pls_resolve: Convert pixel local storage, writing out to fragment value. + pub pls_init: Option<LazilyCompiledShader>, + pub pls_resolve: Option<LazilyCompiledShader>, + ps_split_composite: LazilyCompiledShader, pub ps_clear: LazilyCompiledShader, @@ -610,9 +587,6 @@ pub struct Shaders { // shaders with WR_FEATURE flags on or off based on the type of image // buffer we're sourcing from (see IMAGE_BUFFER_KINDS). pub composite_rgba: Vec<Option<LazilyCompiledShader>>, - // A faster set of rgba composite shaders that do not support UV clamping - // or color modulation. - pub composite_rgba_fast_path: Vec<Option<LazilyCompiledShader>>, // The same set of composite shaders but with WR_FEATURE_YUV added. pub composite_yuv: Vec<Option<LazilyCompiledShader>>, } @@ -623,28 +597,25 @@ impl Shaders { gl_type: GlType, options: &RendererOptions, ) -> Result<Self, ShaderError> { + let use_pixel_local_storage = device + .get_capabilities() + .supports_pixel_local_storage; + // If using PLS, we disable all subpixel AA implicitly. Subpixel AA is always + // disabled on mobile devices anyway, due to uncertainty over the subpixel + // layout configuration. let use_dual_source_blending = device.get_capabilities().supports_dual_source_blending && - options.allow_dual_source_blending; + options.allow_dual_source_blending && + !use_pixel_local_storage; let use_advanced_blend_equation = device.get_capabilities().supports_advanced_blend_equation && options.allow_advanced_blend_equation; - let texture_external_version = if device.get_capabilities().supports_image_external_essl3 { - TextureExternalVersion::ESSL3 - } else { - TextureExternalVersion::ESSL1 - }; let mut shader_flags = match gl_type { GlType::Gl => ShaderFeatureFlags::GL, - GlType::Gles => { - let texture_external_flag = match texture_external_version { - TextureExternalVersion::ESSL3 => ShaderFeatureFlags::TEXTURE_EXTERNAL, - TextureExternalVersion::ESSL1 => ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1, - }; - ShaderFeatureFlags::GLES | texture_external_flag - } + GlType::Gles => ShaderFeatureFlags::GLES | ShaderFeatureFlags::TEXTURE_EXTERNAL, }; + shader_flags.set(ShaderFeatureFlags::PIXEL_LOCAL_STORAGE, use_pixel_local_storage); shader_flags.set(ShaderFeatureFlags::ADVANCED_BLEND_EQUATION, use_advanced_blend_equation); shader_flags.set(ShaderFeatureFlags::DUAL_SOURCE_BLENDING, use_dual_source_blending); shader_flags.set(ShaderFeatureFlags::DITHERING, options.enable_dithering); @@ -658,6 +629,7 @@ impl Shaders { &shader_list, false /* advanced blend */, false /* dual source */, + use_pixel_local_storage, )?; let brush_blend = BrushShader::new( @@ -668,6 +640,7 @@ impl Shaders { &shader_list, false /* advanced blend */, false /* dual source */, + use_pixel_local_storage, )?; let brush_mix_blend = BrushShader::new( @@ -678,10 +651,11 @@ impl Shaders { &shader_list, false /* advanced blend */, false /* dual source */, + use_pixel_local_storage, )?; - let brush_linear_gradient = BrushShader::new( - "brush_linear_gradient", + let brush_conic_gradient = BrushShader::new( + "brush_conic_gradient", device, if options.enable_dithering { &[DITHERING_FEATURE] @@ -692,16 +666,37 @@ impl Shaders { &shader_list, false /* advanced blend */, false /* dual source */, + use_pixel_local_storage, )?; - let brush_opacity_aa = BrushShader::new( - "brush_opacity", + let brush_radial_gradient = BrushShader::new( + "brush_radial_gradient", + device, + if options.enable_dithering { + &[DITHERING_FEATURE] + } else { + &[] + }, + options.precache_flags, + &shader_list, + false /* advanced blend */, + false /* dual source */, + use_pixel_local_storage, + )?; + + let brush_linear_gradient = BrushShader::new( + "brush_linear_gradient", device, - &["ANTIALIASING"], + if options.enable_dithering { + &[DITHERING_FEATURE] + } else { + &[] + }, options.precache_flags, &shader_list, false /* advanced blend */, false /* dual source */, + use_pixel_local_storage, )?; let brush_opacity = BrushShader::new( @@ -712,6 +707,7 @@ impl Shaders { &shader_list, false /* advanced blend */, false /* dual source */, + use_pixel_local_storage, )?; let cs_blur_a8 = LazilyCompiledShader::new( @@ -742,7 +738,7 @@ impl Shaders { )?; let cs_clip_rectangle_slow = LazilyCompiledShader::new( - ShaderKind::ClipCache(VertexArrayKind::ClipRect), + ShaderKind::ClipCache, "cs_clip_rectangle", &[], device, @@ -751,7 +747,7 @@ impl Shaders { )?; let cs_clip_rectangle_fast = LazilyCompiledShader::new( - ShaderKind::ClipCache(VertexArrayKind::ClipRect), + ShaderKind::ClipCache, "cs_clip_rectangle", &[FAST_PATH_FEATURE], device, @@ -760,73 +756,77 @@ impl Shaders { )?; let cs_clip_box_shadow = LazilyCompiledShader::new( - ShaderKind::ClipCache(VertexArrayKind::ClipBoxShadow), + ShaderKind::ClipCache, "cs_clip_box_shadow", - &["TEXTURE_2D"], + &[], device, options.precache_flags, &shader_list, )?; let cs_clip_image = LazilyCompiledShader::new( - ShaderKind::ClipCache(VertexArrayKind::ClipImage), + ShaderKind::ClipCache, "cs_clip_image", - &["TEXTURE_2D"], + &[], device, options.precache_flags, &shader_list, )?; - let mut cs_scale = Vec::new(); - let scale_shader_num = IMAGE_BUFFER_KINDS.len(); - // PrimitiveShader is not clonable. Use push() to initialize the vec. - for _ in 0 .. scale_shader_num { - cs_scale.push(None); - } - for image_buffer_kind in &IMAGE_BUFFER_KINDS { - if has_platform_support(*image_buffer_kind, &gl_type) { - let feature_string = get_feature_string( - *image_buffer_kind, - texture_external_version, - ); - - let mut features = Vec::new(); - if feature_string != "" { - features.push(feature_string); - } + let pls_init = if use_pixel_local_storage { + Some(LazilyCompiledShader::new( + ShaderKind::Resolve, + "pls_init", + &[PIXEL_LOCAL_STORAGE_FEATURE], + device, + options.precache_flags, + &shader_list, + )?) + } else { + None + }; - let shader = LazilyCompiledShader::new( - ShaderKind::Cache(VertexArrayKind::Scale), - "cs_scale", - &features, - device, - options.precache_flags, - &shader_list, - )?; + let pls_resolve = if use_pixel_local_storage { + Some(LazilyCompiledShader::new( + ShaderKind::Resolve, + "pls_resolve", + &[PIXEL_LOCAL_STORAGE_FEATURE], + device, + options.precache_flags, + &shader_list, + )?) + } else { + None + }; - let index = Self::get_compositing_shader_index( - *image_buffer_kind, - ); - cs_scale[index] = Some(shader); - } - } + let cs_scale = LazilyCompiledShader::new( + ShaderKind::Cache(VertexArrayKind::Scale), + "cs_scale", + &[], + device, + options.precache_flags, + &shader_list, + )?; // TODO(gw): The split composite + text shader are special cases - the only // shaders used during normal scene rendering that aren't a brush // shader. Perhaps we can unify these in future? + let mut extra_features = Vec::new(); + if use_pixel_local_storage { + extra_features.push(PIXEL_LOCAL_STORAGE_FEATURE); + } let ps_text_run = TextShader::new("ps_text_run", device, - &[], + &extra_features, options.precache_flags, &shader_list, )?; let ps_text_run_dual_source = if use_dual_source_blending { - let dual_source_features = vec![DUAL_SOURCE_FEATURE]; Some(TextShader::new("ps_text_run", device, - &dual_source_features, + &[DUAL_SOURCE_FEATURE], options.precache_flags, &shader_list, )?) @@ -837,7 +837,7 @@ impl Shaders { let ps_split_composite = LazilyCompiledShader::new( ShaderKind::Primitive, "ps_split_composite", - &[], + &extra_features, device, options.precache_flags, &shader_list, @@ -846,7 +846,7 @@ impl Shaders { let ps_clear = LazilyCompiledShader::new( ShaderKind::Clear, "ps_clear", - &[], + &extra_features, device, options.precache_flags, &shader_list, @@ -862,18 +862,11 @@ impl Shaders { brush_fast_image.push(None); } for buffer_kind in 0 .. IMAGE_BUFFER_KINDS.len() { - if !has_platform_support(IMAGE_BUFFER_KINDS[buffer_kind], &gl_type) - // Brush shaders are not ESSL1 compatible - || (IMAGE_BUFFER_KINDS[buffer_kind] == ImageBufferKind::TextureExternal - && texture_external_version == TextureExternalVersion::ESSL1) - { + if !IMAGE_BUFFER_KINDS[buffer_kind].has_platform_support(&gl_type) { continue; } - let feature_string = get_feature_string( - IMAGE_BUFFER_KINDS[buffer_kind], - texture_external_version, - ); + let feature_string = IMAGE_BUFFER_KINDS[buffer_kind].get_feature_string(); if feature_string != "" { image_features.push(feature_string); } @@ -886,6 +879,7 @@ impl Shaders { &shader_list, use_advanced_blend_equation, use_dual_source_blending, + use_pixel_local_storage, )?); image_features.push("REPETITION"); @@ -899,6 +893,7 @@ impl Shaders { &shader_list, use_advanced_blend_equation, use_dual_source_blending, + use_pixel_local_storage, )?); image_features.clear(); @@ -907,76 +902,50 @@ impl Shaders { // All yuv_image configuration. let mut yuv_features = Vec::new(); let mut rgba_features = Vec::new(); - let mut fast_path_features = Vec::new(); let yuv_shader_num = IMAGE_BUFFER_KINDS.len(); let mut brush_yuv_image = Vec::new(); let mut composite_yuv = Vec::new(); let mut composite_rgba = Vec::new(); - let mut composite_rgba_fast_path = Vec::new(); // PrimitiveShader is not clonable. Use push() to initialize the vec. for _ in 0 .. yuv_shader_num { brush_yuv_image.push(None); composite_yuv.push(None); composite_rgba.push(None); - composite_rgba_fast_path.push(None); } for image_buffer_kind in &IMAGE_BUFFER_KINDS { - if has_platform_support(*image_buffer_kind, &gl_type) { + if image_buffer_kind.has_platform_support(&gl_type) { yuv_features.push("YUV"); - fast_path_features.push("FAST_PATH"); - let index = Self::get_compositing_shader_index( - *image_buffer_kind, - ); - - let feature_string = get_feature_string( - *image_buffer_kind, - texture_external_version, - ); + let feature_string = image_buffer_kind.get_feature_string(); if feature_string != "" { yuv_features.push(feature_string); rgba_features.push(feature_string); - fast_path_features.push(feature_string); } - // YUV shaders are not compatible with ESSL1 - if *image_buffer_kind != ImageBufferKind::TextureExternal || - texture_external_version == TextureExternalVersion::ESSL3 { - let brush_shader = BrushShader::new( - "brush_yuv_image", - device, - &yuv_features, - options.precache_flags, - &shader_list, - false /* advanced blend */, - false /* dual source */, - )?; - brush_yuv_image[index] = Some(brush_shader); - - let composite_yuv_shader = LazilyCompiledShader::new( - ShaderKind::Composite, - "composite", - &yuv_features, - device, - options.precache_flags, - &shader_list, - )?; - composite_yuv[index] = Some(composite_yuv_shader); - } + let brush_shader = BrushShader::new( + "brush_yuv_image", + device, + &yuv_features, + options.precache_flags, + &shader_list, + false /* advanced blend */, + false /* dual source */, + use_pixel_local_storage, + )?; - let composite_rgba_shader = LazilyCompiledShader::new( + let composite_yuv_shader = LazilyCompiledShader::new( ShaderKind::Composite, "composite", - &rgba_features, + &yuv_features, device, options.precache_flags, &shader_list, )?; - let composite_rgba_fast_path_shader = LazilyCompiledShader::new( + let composite_rgba_shader = LazilyCompiledShader::new( ShaderKind::Composite, "composite", - &fast_path_features, + &rgba_features, device, options.precache_flags, &shader_list, @@ -985,12 +954,12 @@ impl Shaders { let index = Self::get_compositing_shader_index( *image_buffer_kind, ); + brush_yuv_image[index] = Some(brush_shader); + composite_yuv[index] = Some(composite_yuv_shader); composite_rgba[index] = Some(composite_rgba_shader); - composite_rgba_fast_path[index] = Some(composite_rgba_fast_path_shader); yuv_features.clear(); - rgba_features.clear(); - fast_path_features.clear(); + rgba_features.clear() } } @@ -1003,36 +972,9 @@ impl Shaders { &shader_list, )?; - let cs_fast_linear_gradient = LazilyCompiledShader::new( - ShaderKind::Cache(VertexArrayKind::FastLinearGradient), - "cs_fast_linear_gradient", - &[], - device, - options.precache_flags, - &shader_list, - )?; - - let cs_linear_gradient = LazilyCompiledShader::new( - ShaderKind::Cache(VertexArrayKind::LinearGradient), - "cs_linear_gradient", - &[], - device, - options.precache_flags, - &shader_list, - )?; - - let cs_radial_gradient = LazilyCompiledShader::new( - ShaderKind::Cache(VertexArrayKind::RadialGradient), - "cs_radial_gradient", - &[], - device, - options.precache_flags, - &shader_list, - )?; - - let cs_conic_gradient = LazilyCompiledShader::new( - ShaderKind::Cache(VertexArrayKind::ConicGradient), - "cs_conic_gradient", + let cs_gradient = LazilyCompiledShader::new( + ShaderKind::Cache(VertexArrayKind::Gradient), + "cs_gradient", &[], device, options.precache_flags, @@ -1062,10 +1004,7 @@ impl Shaders { cs_blur_rgba8, cs_border_segment, cs_line_decoration, - cs_fast_linear_gradient, - cs_linear_gradient, - cs_radial_gradient, - cs_conic_gradient, + cs_gradient, cs_border_solid, cs_scale, cs_svg_filter, @@ -1075,19 +1014,21 @@ impl Shaders { brush_blend, brush_mix_blend, brush_yuv_image, + brush_conic_gradient, + brush_radial_gradient, brush_linear_gradient, brush_opacity, - brush_opacity_aa, cs_clip_rectangle_slow, cs_clip_rectangle_fast, cs_clip_box_shadow, cs_clip_image, + pls_init, + pls_resolve, ps_text_run, ps_text_run_dual_source, ps_split_composite, ps_clear, composite_rgba, - composite_rgba_fast_path, composite_yuv, }) } @@ -1100,23 +1041,13 @@ impl Shaders { &mut self, format: CompositeSurfaceFormat, buffer_kind: ImageBufferKind, - features: CompositeFeatures, ) -> &mut LazilyCompiledShader { match format { CompositeSurfaceFormat::Rgba => { - if features.contains(CompositeFeatures::NO_UV_CLAMP) - && features.contains(CompositeFeatures::NO_COLOR_MODULATION) - { - let shader_index = Self::get_compositing_shader_index(buffer_kind); - self.composite_rgba_fast_path[shader_index] - .as_mut() - .expect("bug: unsupported rgba fast path shader requested") - } else { - let shader_index = Self::get_compositing_shader_index(buffer_kind); - self.composite_rgba[shader_index] - .as_mut() - .expect("bug: unsupported rgba shader requested") - } + let shader_index = Self::get_compositing_shader_index(buffer_kind); + self.composite_rgba[shader_index] + .as_mut() + .expect("bug: unsupported rgba shader requested") } CompositeSurfaceFormat::Yuv => { let shader_index = Self::get_compositing_shader_index(buffer_kind); @@ -1127,34 +1058,12 @@ impl Shaders { } } - pub fn get_scale_shader( - &mut self, - buffer_kind: ImageBufferKind, - ) -> &mut LazilyCompiledShader { - let shader_index = Self::get_compositing_shader_index(buffer_kind); - self.cs_scale[shader_index] - .as_mut() - .expect("bug: unsupported scale shader requested") - } - - pub fn get(& - mut self, - key: &BatchKey, - mut features: BatchFeatures, - debug_flags: DebugFlags, - device: &Device, - ) -> &mut LazilyCompiledShader { + pub fn get(&mut self, key: &BatchKey, features: BatchFeatures, debug_flags: DebugFlags) -> &mut LazilyCompiledShader { match key.kind { BatchKind::SplitComposite => { &mut self.ps_split_composite } BatchKind::Brush(brush_kind) => { - // SWGL uses a native anti-aliasing implementation that bypasses the shader. - // Don't consider it in that case when deciding whether or not to use - // an alpha-pass shader. - if device.get_capabilities().uses_native_antialiasing { - features.remove(BatchFeatures::ANTIALIASING); - } let brush_shader = match brush_kind { BrushBatchKind::Solid => { &mut self.brush_solid @@ -1178,26 +1087,14 @@ impl Shaders { BrushBatchKind::MixBlend { .. } => { &mut self.brush_mix_blend } + BrushBatchKind::ConicGradient => { + &mut self.brush_conic_gradient + } + BrushBatchKind::RadialGradient => { + &mut self.brush_radial_gradient + } BrushBatchKind::LinearGradient => { - // SWGL uses a native clip mask implementation that bypasses the shader. - // Don't consider it in that case when deciding whether or not to use - // an alpha-pass shader. - if device.get_capabilities().uses_native_clip_mask { - features.remove(BatchFeatures::CLIP_MASK); - } - // Gradient brushes can optimistically use the opaque shader even - // with a blend mode if they don't require any features. - if !features.intersects( - BatchFeatures::ANTIALIASING - | BatchFeatures::REPETITION - | BatchFeatures::CLIP_MASK, - ) { - features.remove(BatchFeatures::ALPHA_PASS); - } - match brush_kind { - BrushBatchKind::LinearGradient => &mut self.brush_linear_gradient, - _ => panic!(), - } + &mut self.brush_linear_gradient } BrushBatchKind::YuvImage(image_buffer_kind, ..) => { let shader_index = @@ -1207,14 +1104,10 @@ impl Shaders { .expect("Unsupported YUV shader kind") } BrushBatchKind::Opacity => { - if features.contains(BatchFeatures::ANTIALIASING) { - &mut self.brush_opacity_aa - } else { - &mut self.brush_opacity - } + &mut self.brush_opacity } }; - brush_shader.get(key.blend_mode, features, debug_flags) + brush_shader.get(key.blend_mode, debug_flags) } BatchKind::TextRun(glyph_format) => { let text_shader = match key.blend_mode { @@ -1227,24 +1120,27 @@ impl Shaders { } pub fn deinit(self, device: &mut Device) { - for shader in self.cs_scale { - if let Some(shader) = shader { - shader.deinit(device); - } - } + self.cs_scale.deinit(device); self.cs_blur_a8.deinit(device); self.cs_blur_rgba8.deinit(device); self.cs_svg_filter.deinit(device); self.brush_solid.deinit(device); self.brush_blend.deinit(device); self.brush_mix_blend.deinit(device); + self.brush_conic_gradient.deinit(device); + self.brush_radial_gradient.deinit(device); self.brush_linear_gradient.deinit(device); self.brush_opacity.deinit(device); - self.brush_opacity_aa.deinit(device); self.cs_clip_rectangle_slow.deinit(device); self.cs_clip_rectangle_fast.deinit(device); self.cs_clip_box_shadow.deinit(device); self.cs_clip_image.deinit(device); + if let Some(shader) = self.pls_init { + shader.deinit(device); + } + if let Some(shader) = self.pls_resolve { + shader.deinit(device); + } self.ps_text_run.deinit(device); if let Some(shader) = self.ps_text_run_dual_source { shader.deinit(device); @@ -1265,10 +1161,7 @@ impl Shaders { } } self.cs_border_solid.deinit(device); - self.cs_fast_linear_gradient.deinit(device); - self.cs_linear_gradient.deinit(device); - self.cs_radial_gradient.deinit(device); - self.cs_conic_gradient.deinit(device); + self.cs_gradient.deinit(device); self.cs_line_decoration.deinit(device); self.cs_border_segment.deinit(device); self.ps_split_composite.deinit(device); @@ -1279,11 +1172,6 @@ impl Shaders { shader.deinit(device); } } - for shader in self.composite_rgba_fast_path { - if let Some(shader) = shader { - shader.deinit(device); - } - } for shader in self.composite_yuv { if let Some(shader) = shader { shader.deinit(device); @@ -1292,4 +1180,10 @@ impl Shaders { } } -pub type SharedShaders = Rc<RefCell<Shaders>>; +// A wrapper around a strong reference to a Shaders +// object. We have this so that external (ffi) +// consumers can own a reference to a shared Shaders +// instance without understanding rust's refcounting. +pub struct WrShaders { + pub shaders: Rc<RefCell<Shaders>>, +} diff --git a/third_party/webrender/webrender/src/space.rs b/third_party/webrender/webrender/src/space.rs deleted file mode 100644 index 15646b478d4..00000000000 --- a/third_party/webrender/webrender/src/space.rs +++ /dev/null @@ -1,254 +0,0 @@ -/* 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/. */ - - -//! Utilities to deal with coordinate spaces. - -use std::fmt; - -use euclid::{Transform3D, Rect, Point2D, Vector2D}; - -use api::units::*; -use crate::spatial_tree::{SpatialTree, CoordinateSpaceMapping, SpatialNodeIndex, VisibleFace}; -use crate::util::project_rect; -use crate::util::{MatrixHelpers, ScaleOffset, RectHelpers, PointHelpers}; - - -#[derive(Debug, Clone)] -pub struct SpaceMapper<F, T> { - kind: CoordinateSpaceMapping<F, T>, - pub ref_spatial_node_index: SpatialNodeIndex, - pub current_target_spatial_node_index: SpatialNodeIndex, - pub bounds: Rect<f32, T>, - visible_face: VisibleFace, -} - -impl<F, T> SpaceMapper<F, T> where F: fmt::Debug { - pub fn new( - ref_spatial_node_index: SpatialNodeIndex, - bounds: Rect<f32, T>, - ) -> Self { - SpaceMapper { - kind: CoordinateSpaceMapping::Local, - ref_spatial_node_index, - current_target_spatial_node_index: ref_spatial_node_index, - bounds, - visible_face: VisibleFace::Front, - } - } - - pub fn new_with_target( - ref_spatial_node_index: SpatialNodeIndex, - target_node_index: SpatialNodeIndex, - bounds: Rect<f32, T>, - spatial_tree: &SpatialTree, - ) -> Self { - let mut mapper = Self::new(ref_spatial_node_index, bounds); - mapper.set_target_spatial_node(target_node_index, spatial_tree); - mapper - } - - pub fn set_target_spatial_node( - &mut self, - target_node_index: SpatialNodeIndex, - spatial_tree: &SpatialTree, - ) { - if target_node_index == self.current_target_spatial_node_index { - return - } - - let ref_spatial_node = &spatial_tree.spatial_nodes[self.ref_spatial_node_index.0 as usize]; - let target_spatial_node = &spatial_tree.spatial_nodes[target_node_index.0 as usize]; - self.visible_face = VisibleFace::Front; - - self.kind = if self.ref_spatial_node_index == target_node_index { - CoordinateSpaceMapping::Local - } else if ref_spatial_node.coordinate_system_id == target_spatial_node.coordinate_system_id { - let scale_offset = ref_spatial_node.content_transform - .inverse() - .accumulate(&target_spatial_node.content_transform); - CoordinateSpaceMapping::ScaleOffset(scale_offset) - } else { - let transform = spatial_tree - .get_relative_transform_with_face( - target_node_index, - self.ref_spatial_node_index, - Some(&mut self.visible_face), - ) - .into_transform() - .with_source::<F>() - .with_destination::<T>(); - CoordinateSpaceMapping::Transform(transform) - }; - - self.current_target_spatial_node_index = target_node_index; - } - - pub fn get_transform(&self) -> Transform3D<f32, F, T> { - match self.kind { - CoordinateSpaceMapping::Local => { - Transform3D::identity() - } - CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { - scale_offset.to_transform() - } - CoordinateSpaceMapping::Transform(transform) => { - transform - } - } - } - - pub fn unmap(&self, rect: &Rect<f32, T>) -> Option<Rect<f32, F>> { - match self.kind { - CoordinateSpaceMapping::Local => { - Some(rect.cast_unit()) - } - CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { - Some(scale_offset.unmap_rect(rect)) - } - CoordinateSpaceMapping::Transform(ref transform) => { - transform.inverse_rect_footprint(rect) - } - } - } - - pub fn map(&self, rect: &Rect<f32, F>) -> Option<Rect<f32, T>> { - match self.kind { - CoordinateSpaceMapping::Local => { - Some(rect.cast_unit()) - } - CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { - Some(scale_offset.map_rect(rect)) - } - CoordinateSpaceMapping::Transform(ref transform) => { - match project_rect(transform, rect, &self.bounds) { - Some(bounds) => { - Some(bounds) - } - None => { - warn!("parent relative transform can't transform the primitive rect for {:?}", rect); - None - } - } - } - } - } - - // Attempt to return a rect that is contained in the mapped rect. - pub fn map_inner_bounds(&self, rect: &Rect<f32, F>) -> Option<Rect<f32, T>> { - match self.kind { - CoordinateSpaceMapping::Local => { - Some(rect.cast_unit()) - } - CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { - Some(scale_offset.map_rect(rect)) - } - CoordinateSpaceMapping::Transform(..) => { - // We could figure out a rect that is contained in the transformed rect but - // for now we do the simple thing here and bail out. - return None; - } - } - } - - pub fn map_vector(&self, v: Vector2D<f32, F>) -> Vector2D<f32, T> { - match self.kind { - CoordinateSpaceMapping::Local => { - v.cast_unit() - } - CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => { - scale_offset.map_vector(&v) - } - CoordinateSpaceMapping::Transform(ref transform) => { - transform.transform_vector2d(v) - } - } - } -} - - -#[derive(Clone, Debug)] -pub struct SpaceSnapper { - pub ref_spatial_node_index: SpatialNodeIndex, - current_target_spatial_node_index: SpatialNodeIndex, - snapping_transform: Option<ScaleOffset>, - pub device_pixel_scale: DevicePixelScale, -} - -impl SpaceSnapper { - pub fn new( - ref_spatial_node_index: SpatialNodeIndex, - device_pixel_scale: DevicePixelScale, - ) -> Self { - SpaceSnapper { - ref_spatial_node_index, - current_target_spatial_node_index: SpatialNodeIndex::INVALID, - snapping_transform: None, - device_pixel_scale, - } - } - - pub fn new_with_target( - ref_spatial_node_index: SpatialNodeIndex, - target_node_index: SpatialNodeIndex, - device_pixel_scale: DevicePixelScale, - spatial_tree: &SpatialTree, - ) -> Self { - let mut snapper = SpaceSnapper { - ref_spatial_node_index, - current_target_spatial_node_index: SpatialNodeIndex::INVALID, - snapping_transform: None, - device_pixel_scale, - }; - - snapper.set_target_spatial_node(target_node_index, spatial_tree); - snapper - } - - pub fn set_target_spatial_node( - &mut self, - target_node_index: SpatialNodeIndex, - spatial_tree: &SpatialTree, - ) { - if target_node_index == self.current_target_spatial_node_index { - return - } - - let ref_spatial_node = &spatial_tree.spatial_nodes[self.ref_spatial_node_index.0 as usize]; - let target_spatial_node = &spatial_tree.spatial_nodes[target_node_index.0 as usize]; - - self.current_target_spatial_node_index = target_node_index; - self.snapping_transform = match (ref_spatial_node.snapping_transform, target_spatial_node.snapping_transform) { - (Some(ref ref_scale_offset), Some(ref target_scale_offset)) => { - Some(ref_scale_offset - .inverse() - .accumulate(target_scale_offset) - .scale(self.device_pixel_scale.0)) - } - _ => None, - }; - } - - pub fn snap_rect<F>(&self, rect: &Rect<f32, F>) -> Rect<f32, F> where F: fmt::Debug { - debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID); - match self.snapping_transform { - Some(ref scale_offset) => { - let snapped_device_rect : DeviceRect = scale_offset.map_rect(rect).snap(); - scale_offset.unmap_rect(&snapped_device_rect) - } - None => *rect, - } - } - - pub fn snap_point<F>(&self, point: &Point2D<f32, F>) -> Point2D<f32, F> where F: fmt::Debug { - debug_assert!(self.current_target_spatial_node_index != SpatialNodeIndex::INVALID); - match self.snapping_transform { - Some(ref scale_offset) => { - let snapped_device_vector : DevicePoint = scale_offset.map_point(point).snap(); - scale_offset.unmap_point(&snapped_device_vector) - } - None => *point, - } - } -} diff --git a/third_party/webrender/webrender/src/spatial_node.rs b/third_party/webrender/webrender/src/spatial_node.rs index b2dd77f148b..2283b0fe1a9 100644 --- a/third_party/webrender/webrender/src/spatial_node.rs +++ b/third_party/webrender/webrender/src/spatial_node.rs @@ -6,12 +6,12 @@ use api::{ExternalScrollId, PipelineId, PropertyBinding, PropertyBindingId, ReferenceFrameKind, ScrollClamping, ScrollLocation}; use api::{TransformStyle, ScrollSensitivity, StickyOffsetBounds}; use api::units::*; -use crate::spatial_tree::{CoordinateSystem, SpatialNodeIndex, TransformUpdateState}; -use crate::spatial_tree::{CoordinateSystemId, StaticCoordinateSystemId}; +use crate::spatial_tree::{CoordinateSystem, CoordinateSystemId, SpatialNodeIndex, TransformUpdateState}; use euclid::{Point2D, Vector2D, SideOffsets2D}; use crate::scene::SceneProperties; use crate::util::{LayoutFastTransform, MatrixHelpers, ScaleOffset, TransformedRectKind, PointHelpers}; +#[derive(Clone, Debug)] pub enum SpatialNodeType { /// A special kind of node that adjusts its position based on the position /// of its parent node and a given set of sticky positioning offset bounds. @@ -28,6 +28,7 @@ pub enum SpatialNodeType { } /// Contains information common among all types of SpatialTree nodes. +#[derive(Clone, Debug)] pub struct SpatialNode { /// The scale/offset of the viewport for this spatial node, relative to the /// coordinate system. Includes any accumulated scrolling offsets from nodes @@ -44,10 +45,6 @@ pub struct SpatialNode { /// The axis-aligned coordinate system id of this node. pub coordinate_system_id: CoordinateSystemId, - /// Coordinate system statically assigned during scene building (doesn't change regardless of - /// the current property binding value during frame building). - pub static_coordinate_system_id: StaticCoordinateSystemId, - /// The current transform kind of this node. pub transform_kind: TransformedRectKind, @@ -92,7 +89,7 @@ fn compute_offset_from( break; }, SpatialNodeType::ScrollFrame(ref info) => { - if info.external_id == external_id { + if info.external_id == Some(external_id) { break; } @@ -135,14 +132,12 @@ impl SpatialNode { pipeline_id: PipelineId, parent_index: Option<SpatialNodeIndex>, node_type: SpatialNodeType, - static_coordinate_system_id: StaticCoordinateSystemId, ) -> Self { SpatialNode { viewport_transform: ScaleOffset::identity(), content_transform: ScaleOffset::identity(), snapping_transform: None, coordinate_system_id: CoordinateSystemId(0), - static_coordinate_system_id, transform_kind: TransformedRectKind::AxisAligned, parent: parent_index, children: Vec::new(), @@ -157,13 +152,12 @@ impl SpatialNode { pub fn new_scroll_frame( pipeline_id: PipelineId, parent_index: SpatialNodeIndex, - external_id: ExternalScrollId, + external_id: Option<ExternalScrollId>, frame_rect: &LayoutRect, content_size: &LayoutSize, scroll_sensitivity: ScrollSensitivity, frame_kind: ScrollFrameKind, external_scroll_offset: LayoutVector2D, - static_coordinate_system_id: StaticCoordinateSystemId, ) -> Self { let node_type = SpatialNodeType::ScrollFrame(ScrollFrameInfo::new( *frame_rect, @@ -178,12 +172,7 @@ impl SpatialNode { ) ); - Self::new( - pipeline_id, - Some(parent_index), - node_type, - static_coordinate_system_id, - ) + Self::new(pipeline_id, Some(parent_index), node_type) } pub fn new_reference_frame( @@ -193,7 +182,6 @@ impl SpatialNode { kind: ReferenceFrameKind, origin_in_parent_reference_frame: LayoutVector2D, pipeline_id: PipelineId, - static_coordinate_system_id: StaticCoordinateSystemId, ) -> Self { let info = ReferenceFrameInfo { transform_style, @@ -202,26 +190,15 @@ impl SpatialNode { origin_in_parent_reference_frame, invertible: true, }; - Self::new( - pipeline_id, - parent_index, - SpatialNodeType::ReferenceFrame(info), - static_coordinate_system_id, - ) + Self::new(pipeline_id, parent_index, SpatialNodeType::ReferenceFrame(info)) } pub fn new_sticky_frame( parent_index: SpatialNodeIndex, sticky_frame_info: StickyFrameInfo, pipeline_id: PipelineId, - static_coordinate_system_id: StaticCoordinateSystemId, ) -> Self { - Self::new( - pipeline_id, - Some(parent_index), - SpatialNodeType::StickyFrame(sticky_frame_info), - static_coordinate_system_id, - ) + Self::new(pipeline_id, Some(parent_index), SpatialNodeType::StickyFrame(sticky_frame_info)) } pub fn add_child(&mut self, child: SpatialNodeIndex) { @@ -342,14 +319,9 @@ impl SpatialNode { if info.invertible { // Resolve the transform against any property bindings. - let source_transform = { - let source_transform = scene_properties.resolve_layout_transform(&info.source_transform); - if let ReferenceFrameKind::Transform { is_2d_scale_translation: true, .. } = info.kind { - assert!(source_transform.is_2d_scale_translation(), "Reference frame was marked as only having 2d scale or translation"); - } - - LayoutFastTransform::from(source_transform) - }; + let source_transform = LayoutFastTransform::from( + scene_properties.resolve_layout_transform(&info.source_transform) + ); // Do a change-basis operation on the perspective matrix using // the scroll offset. @@ -368,7 +340,7 @@ impl SpatialNode { .then_translate(-scroll_offset) } ReferenceFrameKind::Perspective { scrolling_relative_to: None } | - ReferenceFrameKind::Transform { .. } => source_transform, + ReferenceFrameKind::Transform | ReferenceFrameKind::Zoom => source_transform, }; let resolved_transform = @@ -398,9 +370,10 @@ impl SpatialNode { Some(ref scale_offset) => { // We generally do not want to snap animated transforms as it causes jitter. // However, we do want to snap the visual viewport offset when scrolling. - // This may still cause jitter when zooming, unfortunately. + // Therefore only snap the transform for Zoom reference frames. This may still + // cause jitter when zooming, unfortunately. let mut maybe_snapped = scale_offset.clone(); - if let ReferenceFrameKind::Transform { should_snap: true, .. } = info.kind { + if info.kind == ReferenceFrameKind::Zoom { maybe_snapped.offset = snap_offset( scale_offset.offset, state.coordinate_system_relative_scale_offset.scale, @@ -435,7 +408,7 @@ impl SpatialNode { transform, world_transform, should_flatten: match (info.transform_style, info.kind) { - (TransformStyle::Flat, ReferenceFrameKind::Transform { .. }) => true, + (TransformStyle::Flat, ReferenceFrameKind::Transform) => true, (_, _) => false, }, parent: Some(state.current_coordinate_system_id), @@ -700,7 +673,7 @@ impl SpatialNode { pub fn matches_external_id(&self, external_id: ExternalScrollId) -> bool { match self.node_type { - SpatialNodeType::ScrollFrame(info) if info.external_id == external_id => true, + SpatialNodeType::ScrollFrame(info) if info.external_id == Some(external_id) => true, _ => false, } } @@ -777,9 +750,7 @@ impl SpatialNode { /// or an explicitly defined scroll frame from the display list. #[derive(Copy, Clone, Debug)] pub enum ScrollFrameKind { - PipelineRoot { - is_root_pipeline: bool, - }, + PipelineRoot, Explicit, } @@ -797,7 +768,7 @@ pub struct ScrollFrameInfo { /// An external id to identify this scroll frame to API clients. This /// allows setting scroll positions via the API without relying on ClipsIds /// which may change between frames. - pub external_id: ExternalScrollId, + pub external_id: Option<ExternalScrollId>, /// Stores whether this is a scroll frame added implicitly by WR when adding /// a pipeline (either the root or an iframe). We need to exclude these @@ -812,14 +783,7 @@ pub struct ScrollFrameInfo { /// pre-scrolled in their local coordinates. pub external_scroll_offset: LayoutVector2D, - /// The negated scroll offset of this scroll node. including the - /// pre-scrolled amount. If, for example, a scroll node was pre-scrolled - /// to y=10 (10 pixels down from the initial unscrolled position), then - /// `external_scroll_offset` would be (0,10), and this `offset` field would - /// be (0,-10). If WebRender is then asked to change the scroll position by - /// an additional 10 pixels (without changing the pre-scroll amount in the - /// display list), `external_scroll_offset` would remain at (0,10) and - /// `offset` would change to (0,-20). + /// The current offset of this scroll node. pub offset: LayoutVector2D, } @@ -829,7 +793,7 @@ impl ScrollFrameInfo { viewport_rect: LayoutRect, scroll_sensitivity: ScrollSensitivity, scrollable_size: LayoutSize, - external_id: ExternalScrollId, + external_id: Option<ExternalScrollId>, frame_kind: ScrollFrameKind, external_scroll_offset: LayoutVector2D, ) -> ScrollFrameInfo { @@ -855,9 +819,14 @@ impl ScrollFrameInfo { self, old_scroll_info: &ScrollFrameInfo ) -> ScrollFrameInfo { + let offset = + old_scroll_info.offset + + self.external_scroll_offset - + old_scroll_info.external_scroll_offset; + ScrollFrameInfo { viewport_rect: self.viewport_rect, - offset: old_scroll_info.offset, + offset, scroll_sensitivity: self.scroll_sensitivity, scrollable_size: self.scrollable_size, external_id: self.external_id, @@ -939,17 +908,14 @@ fn test_cst_perspective_relative_scroll() { None, TransformStyle::Flat, PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, + ReferenceFrameKind::Transform, LayoutVector2D::zero(), pipeline_id, ); let scroll_frame_1 = cst.add_scroll_frame( root, - ext_scroll_id, + Some(ext_scroll_id), pipeline_id, &LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(100.0, 100.0)), &LayoutSize::new(100.0, 500.0), @@ -960,7 +926,7 @@ fn test_cst_perspective_relative_scroll() { let scroll_frame_2 = cst.add_scroll_frame( scroll_frame_1, - ExternalScrollId(2, pipeline_id), + None, pipeline_id, &LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(100.0, 100.0)), &LayoutSize::new(100.0, 500.0), diff --git a/third_party/webrender/webrender/src/spatial_tree.rs b/third_party/webrender/webrender/src/spatial_tree.rs index bfbea525a6e..d1eaca9d8fc 100644 --- a/third_party/webrender/webrender/src/spatial_tree.rs +++ b/third_party/webrender/webrender/src/spatial_tree.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use api::{ExternalScrollId, PropertyBinding, ReferenceFrameKind, TransformStyle}; -use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollSensitivity}; +use api::{PipelineId, ScrollClamping, ScrollNodeState, ScrollLocation, ScrollSensitivity}; use api::units::*; use euclid::Transform3D; use crate::gpu_types::TransformPalette; @@ -25,13 +25,6 @@ pub type ScrollStates = FastHashMap<ExternalScrollId, ScrollFrameInfo>; #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct CoordinateSystemId(pub u32); -#[derive(Debug, Copy, Clone, PartialEq)] -pub struct StaticCoordinateSystemId(pub u32); - -impl StaticCoordinateSystemId { - pub const ROOT: StaticCoordinateSystemId = StaticCoordinateSystemId(0); -} - /// A node in the hierarchy of coordinate system /// transforms. #[derive(Debug)] @@ -66,14 +59,6 @@ impl SpatialNodeIndex { pub const ROOT_SPATIAL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(0); const TOPMOST_SCROLL_NODE_INDEX: SpatialNodeIndex = SpatialNodeIndex(1); -// In some cases, the conversion from CSS pixels to device pixels can result in small -// rounding errors when calculating the scrollable distance of a scroll frame. Apply -// a small epsilon so that we don't detect these frames as "real" scroll frames. -const MIN_SCROLLABLE_AMOUNT: f32 = 0.01; - -// The minimum size for a scroll frame for it to be considered for a scroll root. -const MIN_SCROLL_ROOT_SIZE: f32 = 128.0; - impl SpatialNodeIndex { pub fn new(index: usize) -> Self { debug_assert!(index < ::std::u32::MAX as usize); @@ -127,9 +112,6 @@ pub struct SpatialTree { /// Temporary stack of nodes to update when traversing the tree. nodes_to_update: Vec<(SpatialNodeIndex, TransformUpdateState)>, - - /// Next id to assign when creating a new static coordinate system - next_static_coord_system_id: u32, } #[derive(Clone)] @@ -184,6 +166,16 @@ impl<Src, Dst> CoordinateSpaceMapping<Src, Dst> { } } + pub fn visible_face(&self) -> VisibleFace { + match *self { + CoordinateSpaceMapping::Transform(ref transform) if transform.is_backface_visible() => VisibleFace::Back, + CoordinateSpaceMapping::Local | + CoordinateSpaceMapping::Transform(_) | + CoordinateSpaceMapping::ScaleOffset(_) => VisibleFace::Front, + + } + } + pub fn is_perspective(&self) -> bool { match *self { CoordinateSpaceMapping::Local | @@ -203,7 +195,7 @@ impl<Src, Dst> CoordinateSpaceMapping<Src, Dst> { pub fn scale_factors(&self) -> (f32, f32) { match *self { CoordinateSpaceMapping::Local => (1.0, 1.0), - CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => (scale_offset.scale.x.abs(), scale_offset.scale.y.abs()), + CoordinateSpaceMapping::ScaleOffset(ref scale_offset) => (scale_offset.scale.x, scale_offset.scale.y), CoordinateSpaceMapping::Transform(ref transform) => scale_factors(transform), } } @@ -234,7 +226,6 @@ impl SpatialTree { pending_scroll_offsets: FastHashMap::default(), pipelines_to_discard: FastHashSet::default(), nodes_to_update: Vec::new(), - next_static_coord_system_id: 0, } } @@ -274,19 +265,6 @@ impl SpatialTree { child_index: SpatialNodeIndex, parent_index: SpatialNodeIndex, ) -> CoordinateSpaceMapping<LayoutPixel, LayoutPixel> { - self.get_relative_transform_with_face(child_index, parent_index, None) - } - - /// Calculate the relative transform from `child_index` to `parent_index`. - /// This method will panic if the nodes are not connected! - /// Also, switch the visible face to `Back` if at any stage where the - /// combined transform is flattened, we see the back face. - pub fn get_relative_transform_with_face( - &self, - child_index: SpatialNodeIndex, - parent_index: SpatialNodeIndex, - mut visible_face: Option<&mut VisibleFace>, - ) -> CoordinateSpaceMapping<LayoutPixel, LayoutPixel> { if child_index == parent_index { return CoordinateSpaceMapping::Local; } @@ -318,12 +296,6 @@ impl SpatialTree { .then(&child_transform) .with_source::<LayoutPixel>() .with_destination::<LayoutPixel>(); - - if let Some(face) = visible_face { - if result.is_backface_visible() { - *face = VisibleFace::Back; - } - } return CoordinateSpaceMapping::Transform(result); } @@ -338,11 +310,6 @@ impl SpatialTree { let coord_system = &self.coord_systems[coordinate_system_id.0 as usize]; if coord_system.should_flatten { - if let Some(ref mut face) = visible_face { - if transform.is_backface_visible() { - **face = VisibleFace::Back; - } - } transform.flatten_z_output(); } @@ -355,30 +322,10 @@ impl SpatialTree { .inverse() .to_transform(), ); - if let Some(face) = visible_face { - if transform.is_backface_visible() { - *face = VisibleFace::Back; - } - } CoordinateSpaceMapping::Transform(transform) } - pub fn is_relative_transform_complex( - &self, - child_index: SpatialNodeIndex, - parent_index: SpatialNodeIndex, - ) -> bool { - if child_index == parent_index { - return false; - } - - let child = &self.spatial_nodes[child_index.0 as usize]; - let parent = &self.spatial_nodes[parent_index.0 as usize]; - - child.coordinate_system_id != parent.coordinate_system_id - } - fn get_world_transform_impl( &self, index: SpatialNodeIndex, @@ -443,10 +390,12 @@ impl SpatialTree { let mut result = vec![]; for node in &self.spatial_nodes { if let SpatialNodeType::ScrollFrame(info) = node.node_type { - result.push(ScrollNodeState { - id: info.external_id, - scroll_offset: info.offset - info.external_scroll_offset, - }) + if let Some(id) = info.external_id { + result.push(ScrollNodeState { + id, + scroll_offset: info.offset - info.external_scroll_offset, + }) + } } } result @@ -460,8 +409,8 @@ impl SpatialTree { } match old_node.node_type { - SpatialNodeType::ScrollFrame(info) => { - scroll_states.insert(info.external_id, info); + SpatialNodeType::ScrollFrame(info) if info.external_id.is_some() => { + scroll_states.insert(info.external_id.unwrap(), info); } _ => {} } @@ -488,6 +437,34 @@ impl SpatialTree { false } + fn find_nearest_scrolling_ancestor( + &self, + index: Option<SpatialNodeIndex> + ) -> SpatialNodeIndex { + let index = match index { + Some(index) => index, + None => return self.topmost_scroll_node_index(), + }; + + let node = &self.spatial_nodes[index.0 as usize]; + match node.node_type { + SpatialNodeType::ScrollFrame(state) if state.sensitive_to_input_events() => index, + _ => self.find_nearest_scrolling_ancestor(node.parent) + } + } + + pub fn scroll_nearest_scrolling_ancestor( + &mut self, + scroll_location: ScrollLocation, + node_index: Option<SpatialNodeIndex>, + ) -> bool { + if self.spatial_nodes.is_empty() { + return false; + } + let node_index = self.find_nearest_scrolling_ancestor(node_index); + self.spatial_nodes[node_index.0 as usize].scroll(scroll_location) + } + pub fn update_tree( &mut self, pan: WorldPoint, @@ -551,7 +528,7 @@ impl SpatialTree { pub fn finalize_and_apply_pending_scroll_offsets(&mut self, old_states: ScrollStates) { for node in &mut self.spatial_nodes { let external_id = match node.node_type { - SpatialNodeType::ScrollFrame(ScrollFrameInfo { external_id, ..}) => external_id, + SpatialNodeType::ScrollFrame(ScrollFrameInfo { external_id: Some(id), ..} ) => id, _ => continue, }; @@ -565,15 +542,10 @@ impl SpatialTree { } } - /// Get the static coordinate system for a given spatial node index - pub fn get_static_coordinate_system_id(&self, node_index: SpatialNodeIndex) -> StaticCoordinateSystemId { - self.spatial_nodes[node_index.0 as usize].static_coordinate_system_id - } - pub fn add_scroll_frame( &mut self, parent_index: SpatialNodeIndex, - external_id: ExternalScrollId, + external_id: Option<ExternalScrollId>, pipeline_id: PipelineId, frame_rect: &LayoutRect, content_size: &LayoutSize, @@ -581,9 +553,6 @@ impl SpatialTree { frame_kind: ScrollFrameKind, external_scroll_offset: LayoutVector2D, ) -> SpatialNodeIndex { - // Scroll frames are only 2d translations - they can't introduce a new static coord system - let static_coordinate_system_id = self.get_static_coordinate_system_id(parent_index); - let node = SpatialNode::new_scroll_frame( pipeline_id, parent_index, @@ -593,7 +562,6 @@ impl SpatialTree { scroll_sensitivity, frame_kind, external_scroll_offset, - static_coordinate_system_id, ); self.add_spatial_node(node) } @@ -607,45 +575,6 @@ impl SpatialTree { origin_in_parent_reference_frame: LayoutVector2D, pipeline_id: PipelineId, ) -> SpatialNodeIndex { - - // Determine if this reference frame creates a new static coordinate system - let new_static_coord_system = match parent_index { - Some(..) => { - match kind { - ReferenceFrameKind::Transform { is_2d_scale_translation: true, .. } => { - // Client has guaranteed this transform will only be axis-aligned - false - } - ReferenceFrameKind::Transform { is_2d_scale_translation: false, .. } | ReferenceFrameKind::Perspective { .. } => { - // Even if client hasn't promised it's an axis-aligned transform, we can still - // check this so long as the transform isn't animated (and thus could change to - // anything by APZ during frame building) - match source_transform { - PropertyBinding::Value(m) => { - !m.is_2d_scale_translation() - } - PropertyBinding::Binding(..) => { - // Animated, so assume it may introduce a complex transform - true - } - } - } - } - } - None => { - // The root reference frame always creates a new static coord system - true - } - }; - - let static_coordinate_system_id = if new_static_coord_system { - let id = StaticCoordinateSystemId(self.next_static_coord_system_id); - self.next_static_coord_system_id += 1; - id - } else { - self.get_static_coordinate_system_id(parent_index.unwrap()) - }; - let node = SpatialNode::new_reference_frame( parent_index, transform_style, @@ -653,7 +582,6 @@ impl SpatialTree { kind, origin_in_parent_reference_frame, pipeline_id, - static_coordinate_system_id, ); self.add_spatial_node(node) } @@ -664,14 +592,10 @@ impl SpatialTree { sticky_frame_info: StickyFrameInfo, pipeline_id: PipelineId, ) -> SpatialNodeIndex { - // Sticky frames are only 2d translations - they can't introduce a new static coord system - let static_coordinate_system_id = self.get_static_coordinate_system_id(parent_index); - let node = SpatialNode::new_sticky_frame( parent_index, sticky_frame_info, pipeline_id, - static_coordinate_system_id, ); self.add_spatial_node(node) } @@ -696,31 +620,6 @@ impl SpatialTree { self.pipelines_to_discard.insert(pipeline_id); } - /// Check if a given spatial node is an ancestor of another spatial node. - pub fn is_ancestor( - &self, - maybe_parent: SpatialNodeIndex, - maybe_child: SpatialNodeIndex, - ) -> bool { - // Early out if same node - if maybe_parent == maybe_child { - return false; - } - - let mut current_node = maybe_child; - - while current_node != ROOT_SPATIAL_NODE_INDEX { - let node = &self.spatial_nodes[current_node.0 as usize]; - current_node = node.parent.expect("bug: no parent"); - - if current_node == maybe_parent { - return true; - } - } - - false - } - /// Find the spatial node that is the scroll root for a given spatial node. /// A scroll root is the first spatial node when found travelling up the /// spatial node tree that is an explicit scroll frame. @@ -728,8 +627,7 @@ impl SpatialTree { &self, spatial_node_index: SpatialNodeIndex, ) -> SpatialNodeIndex { - let mut real_scroll_root = ROOT_SPATIAL_NODE_INDEX; - let mut outermost_scroll_root = ROOT_SPATIAL_NODE_INDEX; + let mut scroll_root = ROOT_SPATIAL_NODE_INDEX; let mut node_index = spatial_node_index; while node_index != ROOT_SPATIAL_NODE_INDEX { @@ -737,38 +635,31 @@ impl SpatialTree { match node.node_type { SpatialNodeType::ReferenceFrame(ref info) => { match info.kind { - ReferenceFrameKind::Transform { is_2d_scale_translation: true, .. } => { - // We can handle scroll nodes that pass through a 2d scale/translation node + ReferenceFrameKind::Zoom => { + // We can handle scroll nodes that pass through a zoom node } - ReferenceFrameKind::Transform { is_2d_scale_translation: false, .. } | + ReferenceFrameKind::Transform | ReferenceFrameKind::Perspective { .. } => { // When a reference frame is encountered, forget any scroll roots // we have encountered, as they may end up with a non-axis-aligned transform. - real_scroll_root = ROOT_SPATIAL_NODE_INDEX; - outermost_scroll_root = ROOT_SPATIAL_NODE_INDEX; + scroll_root = ROOT_SPATIAL_NODE_INDEX; } } } SpatialNodeType::StickyFrame(..) => {} SpatialNodeType::ScrollFrame(ref info) => { match info.frame_kind { - ScrollFrameKind::PipelineRoot { is_root_pipeline } => { + ScrollFrameKind::PipelineRoot => { // Once we encounter a pipeline root, there is no need to look further - if is_root_pipeline { - break; - } + break; } ScrollFrameKind::Explicit => { - // Store the closest scroll root we find to the root, for use - // later on, even if it's not actually scrollable. - outermost_scroll_root = node_index; - // If the scroll root has no scrollable area, we don't want to // consider it. This helps pages that have a nested scroll root // within a redundant scroll root to avoid selecting the wrong // reference spatial node for a picture cache. - if info.scrollable_size.width > MIN_SCROLLABLE_AMOUNT || - info.scrollable_size.height > MIN_SCROLLABLE_AMOUNT { + if info.scrollable_size.width > 0.0 || + info.scrollable_size.height > 0.0 { // Since we are skipping redundant scroll roots, we may end up // selecting inner scroll roots that are very small. There is // no performance benefit to creating a slice for these roots, @@ -776,11 +667,11 @@ impl SpatialTree { // local-space, but makes for a reasonable estimate. The value // is arbitrary, but is generally small enough to ignore things // like scroll roots around text input elements. - if info.viewport_rect.size.width > MIN_SCROLL_ROOT_SIZE && - info.viewport_rect.size.height > MIN_SCROLL_ROOT_SIZE { + if info.viewport_rect.size.width > 128.0 && + info.viewport_rect.size.height > 128.0 { // If we've found a root that is scrollable, and a reasonable // size, select that as the current root for this node - real_scroll_root = node_index; + scroll_root = node_index; } } } @@ -790,16 +681,7 @@ impl SpatialTree { node_index = node.parent.expect("unable to find parent node"); } - // If we didn't find any real (scrollable) frames, then return the outermost - // redundant scroll frame. This is important so that we can correctly find - // the clips defined on the content which should be handled when drawing the - // picture cache tiles (by definition these clips are ancestors of the - // scroll root selected for the picture cache). - if real_scroll_root == ROOT_SPATIAL_NODE_INDEX { - outermost_scroll_root - } else { - real_scroll_root - } + scroll_root } fn print_node<T: PrintTreePrinter>( @@ -835,7 +717,6 @@ impl SpatialTree { pt.add_item(format!("viewport_transform: {:?}", node.viewport_transform)); pt.add_item(format!("snapping_transform: {:?}", node.snapping_transform)); pt.add_item(format!("coordinate_system_id: {:?}", node.coordinate_system_id)); - pt.add_item(format!("static_coordinate_system_id: {:?}", node.static_coordinate_system_id)); for child_index in &node.children { self.print_node(*child_index, pt); @@ -847,11 +728,12 @@ impl SpatialTree { /// Get the visible face of the transfrom from the specified node to its parent. pub fn get_local_visible_face(&self, node_index: SpatialNodeIndex) -> VisibleFace { let node = &self.spatial_nodes[node_index.0 as usize]; - let mut face = VisibleFace::Front; - if let Some(parent_index) = node.parent { - self.get_relative_transform_with_face(node_index, parent_index, Some(&mut face)); - } - face + let parent_index = match node.parent { + Some(index) => index, + None => return VisibleFace::Front + }; + self.get_relative_transform(node_index, parent_index) + .visible_face() } #[allow(dead_code)] @@ -888,10 +770,7 @@ fn add_reference_frame( parent, TransformStyle::Preserve3D, PropertyBinding::Value(transform), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, + ReferenceFrameKind::Transform, origin_in_parent_reference_frame, PipelineId::dummy(), ) @@ -1084,329 +963,3 @@ fn test_cst_translation_rotate() { test_pt(100.0, 0.0, &cst, child1, root, 0.0, -100.0); } - -#[test] -fn test_is_ancestor1() { - let mut st = SpatialTree::new(); - - let root = add_reference_frame( - &mut st, - None, - LayoutTransform::identity(), - LayoutVector2D::zero(), - ); - - let child1_0 = add_reference_frame( - &mut st, - Some(root), - LayoutTransform::identity(), - LayoutVector2D::zero(), - ); - - let child1_1 = add_reference_frame( - &mut st, - Some(child1_0), - LayoutTransform::identity(), - LayoutVector2D::zero(), - ); - - let child2 = add_reference_frame( - &mut st, - Some(root), - LayoutTransform::identity(), - LayoutVector2D::zero(), - ); - - st.update_tree( - WorldPoint::zero(), - DevicePixelScale::new(1.0), - &SceneProperties::new(), - ); - - assert!(!st.is_ancestor(root, root)); - assert!(!st.is_ancestor(child1_0, child1_0)); - assert!(!st.is_ancestor(child1_1, child1_1)); - assert!(!st.is_ancestor(child2, child2)); - - assert!(st.is_ancestor(root, child1_0)); - assert!(st.is_ancestor(root, child1_1)); - assert!(st.is_ancestor(child1_0, child1_1)); - - assert!(!st.is_ancestor(child1_0, root)); - assert!(!st.is_ancestor(child1_1, root)); - assert!(!st.is_ancestor(child1_1, child1_0)); - - assert!(st.is_ancestor(root, child2)); - assert!(!st.is_ancestor(child2, root)); - - assert!(!st.is_ancestor(child1_0, child2)); - assert!(!st.is_ancestor(child1_1, child2)); - assert!(!st.is_ancestor(child2, child1_0)); - assert!(!st.is_ancestor(child2, child1_1)); -} - -/// Tests that we select the correct scroll root in the simple case. -#[test] -fn test_find_scroll_root_simple() { - let mut st = SpatialTree::new(); - - let root = st.add_reference_frame( - None, - TransformStyle::Flat, - PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, - LayoutVector2D::new(0.0, 0.0), - PipelineId::dummy(), - ); - - let scroll = st.add_scroll_frame( - root, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(800.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - assert_eq!(st.find_scroll_root(scroll), scroll); -} - -/// Tests that we select the root scroll frame rather than the subframe if both are scrollable. -#[test] -fn test_find_scroll_root_sub_scroll_frame() { - let mut st = SpatialTree::new(); - - let root = st.add_reference_frame( - None, - TransformStyle::Flat, - PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, - LayoutVector2D::new(0.0, 0.0), - PipelineId::dummy(), - ); - - let root_scroll = st.add_scroll_frame( - root, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(800.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - let sub_scroll = st.add_scroll_frame( - root_scroll, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(800.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - assert_eq!(st.find_scroll_root(sub_scroll), root_scroll); -} - -/// Tests that we select the sub scroll frame when the root scroll frame is not scrollable. -#[test] -fn test_find_scroll_root_not_scrollable() { - let mut st = SpatialTree::new(); - - let root = st.add_reference_frame( - None, - TransformStyle::Flat, - PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, - LayoutVector2D::new(0.0, 0.0), - PipelineId::dummy(), - ); - - let root_scroll = st.add_scroll_frame( - root, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(400.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - let sub_scroll = st.add_scroll_frame( - root_scroll, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(800.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - assert_eq!(st.find_scroll_root(sub_scroll), sub_scroll); -} - -/// Tests that we select the sub scroll frame when the root scroll frame is too small. -#[test] -fn test_find_scroll_root_too_small() { - let mut st = SpatialTree::new(); - - let root = st.add_reference_frame( - None, - TransformStyle::Flat, - PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, - LayoutVector2D::new(0.0, 0.0), - PipelineId::dummy(), - ); - - let root_scroll = st.add_scroll_frame( - root, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(MIN_SCROLL_ROOT_SIZE, MIN_SCROLL_ROOT_SIZE)), - &LayoutSize::new(1000.0, 1000.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - let sub_scroll = st.add_scroll_frame( - root_scroll, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(800.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - assert_eq!(st.find_scroll_root(sub_scroll), sub_scroll); -} - -/// Tests that we select the root scroll node, even if it is not scrollable, -/// when encountering a non-axis-aligned transform. -#[test] -fn test_find_scroll_root_perspective() { - let mut st = SpatialTree::new(); - - let root = st.add_reference_frame( - None, - TransformStyle::Flat, - PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, - LayoutVector2D::new(0.0, 0.0), - PipelineId::dummy(), - ); - - let root_scroll = st.add_scroll_frame( - root, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(400.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - let perspective = st.add_reference_frame( - Some(root_scroll), - TransformStyle::Flat, - PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Perspective { - scrolling_relative_to: None, - }, - LayoutVector2D::new(0.0, 0.0), - PipelineId::dummy(), - ); - - let sub_scroll = st.add_scroll_frame( - perspective, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(800.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - assert_eq!(st.find_scroll_root(sub_scroll), root_scroll); -} - -/// Tests that encountering a 2D scale or translation transform does not prevent -/// us from selecting the sub scroll frame if the root scroll frame is unscrollable. -#[test] -fn test_find_scroll_root_2d_scale() { - let mut st = SpatialTree::new(); - - let root = st.add_reference_frame( - None, - TransformStyle::Flat, - PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, - LayoutVector2D::new(0.0, 0.0), - PipelineId::dummy(), - ); - - let root_scroll = st.add_scroll_frame( - root, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(400.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - let scale = st.add_reference_frame( - Some(root_scroll), - TransformStyle::Flat, - PropertyBinding::Value(LayoutTransform::identity()), - ReferenceFrameKind::Transform { - is_2d_scale_translation: true, - should_snap: false, - }, - LayoutVector2D::new(0.0, 0.0), - PipelineId::dummy(), - ); - - let sub_scroll = st.add_scroll_frame( - scale, - ExternalScrollId(1, PipelineId::dummy()), - PipelineId::dummy(), - &LayoutRect::from_size(LayoutSize::new(400.0, 400.0)), - &LayoutSize::new(800.0, 400.0), - ScrollSensitivity::ScriptAndInputEvents, - ScrollFrameKind::Explicit, - LayoutVector2D::new(0.0, 0.0), - ); - - assert_eq!(st.find_scroll_root(sub_scroll), sub_scroll); -} diff --git a/third_party/webrender/webrender/src/prim_store/storage.rs b/third_party/webrender/webrender/src/storage.rs index a928192cd99..a928192cd99 100644 --- a/third_party/webrender/webrender/src/prim_store/storage.rs +++ b/third_party/webrender/webrender/src/storage.rs diff --git a/third_party/webrender/webrender/src/texture_pack/guillotine.rs b/third_party/webrender/webrender/src/texture_allocator.rs index c71cf974e50..1f7902b2191 100644 --- a/third_party/webrender/webrender/src/texture_pack/guillotine.rs +++ b/third_party/webrender/webrender/src/texture_allocator.rs @@ -41,46 +41,37 @@ pub struct FreeRectSlice(pub u32); #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -struct FreeRect { +pub struct FreeRect { slice: FreeRectSlice, rect: DeviceIntRect, } -/// A texture allocator using the guillotine algorithm. -/// -/// See sections 2.2 and 2.2.5 in "A Thousand Ways to Pack the Bin - A Practical Approach to Two- +/// A texture allocator using the guillotine algorithm with the rectangle merge improvement. See +/// sections 2.2 and 2.2.5 in "A Thousand Ways to Pack the Bin - A Practical Approach to Two- /// Dimensional Rectangle Bin Packing": /// /// http://clb.demon.fi/files/RectangleBinPack.pdf /// -/// This approach was chosen because of its simplicity and good performance. +/// This approach was chosen because of its simplicity, good performance, and easy support for +/// dynamic texture deallocation. /// /// Note: the allocations are spread across multiple textures, and also are binned /// orthogonally in order to speed up the search. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct GuillotineAllocator { +pub struct ArrayAllocationTracker { bins: [Vec<FreeRect>; NUM_BINS], } -impl GuillotineAllocator { - pub fn new(initial_size: Option<DeviceIntSize>) -> Self { - let mut allocator = GuillotineAllocator { +impl ArrayAllocationTracker { + pub fn new() -> Self { + ArrayAllocationTracker { bins: [ Vec::new(), Vec::new(), Vec::new(), ], - }; - - if let Some(initial_size) = initial_size { - allocator.push( - FreeRectSlice(0), - initial_size.into(), - ); } - - allocator } fn push(&mut self, slice: FreeRectSlice, rect: DeviceIntRect) { @@ -222,7 +213,7 @@ fn random_fill(count: usize, texture_size: i32) -> f32 { DeviceIntSize::new(texture_size, texture_size), ); let mut rng = thread_rng(); - let mut allocator = GuillotineAllocator::new(None); + let mut allocator = ArrayAllocationTracker::new(); // check for empty allocation assert_eq!( diff --git a/third_party/webrender/webrender/src/texture_cache.rs b/third_party/webrender/webrender/src/texture_cache.rs index f2ca3213dbb..9b244656805 100644 --- a/third_party/webrender/webrender/src/texture_cache.rs +++ b/third_party/webrender/webrender/src/texture_cache.rs @@ -2,54 +2,42 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use api::{DirtyRect, ExternalImageType, ImageFormat, ImageBufferKind}; +use api::{DirtyRect, ExternalImageType, ImageFormat}; use api::{DebugFlags, ImageDescriptor}; use api::units::*; #[cfg(test)] use api::{DocumentId, IdNamespace}; use crate::device::{TextureFilter, TextureFormatPair}; -use crate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle}; +use crate::freelist::{FreeListHandle, WeakFreeListHandle}; use crate::gpu_cache::{GpuCache, GpuCacheHandle}; use crate::gpu_types::{ImageSource, UvRectKind}; use crate::internal_types::{ - CacheTextureId, Swizzle, SwizzleSettings, + CacheTextureId, LayerIndex, Swizzle, SwizzleSettings, TextureUpdateList, TextureUpdateSource, TextureSource, TextureCacheAllocInfo, TextureCacheUpdate, }; use crate::lru_cache::LRUCache; -use crate::profiler::{self, TransactionProfile}; -use crate::render_backend::{FrameStamp, FrameId}; +use crate::profiler::{ResourceProfileCounter, TextureCacheProfileCounters}; +use crate::render_backend::FrameStamp; use crate::resource_cache::{CacheItem, CachedImageData}; -use crate::texture_pack::{ - AllocatorList, - AllocId, - AtlasAllocatorList, - ShelfAllocator, - ShelfAllocatorOptions, - SlabAllocator, SlabAllocatorParameters, -}; use smallvec::SmallVec; use std::cell::Cell; -use std::{cmp, mem}; +use std::cmp; +use std::mem; use std::rc::Rc; -use euclid::size2; -use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; - -/// Information about which shader will use the entry. -/// -/// For batching purposes, it's beneficial to group some items in their -/// own textures if we know that they are used by a specific shader. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum TargetShader { - Default, - Text, -} -/// The size of each region in shared cache texture arrays. +/// The size of each region/layer in shared cache texture arrays. pub const TEXTURE_REGION_DIMENSIONS: i32 = 512; +const PICTURE_TEXTURE_ADD_SLICES: usize = 4; + +/// The chosen image format for picture tiles. +const PICTURE_TILE_FORMAT: ImageFormat = ImageFormat::RGBA8; + +/// The number of pixels in a region. Derived from the above. +const TEXTURE_REGION_PIXELS: usize = + (TEXTURE_REGION_DIMENSIONS as usize) * (TEXTURE_REGION_DIMENSIONS as usize); + /// Items in the texture cache can either be standalone textures, /// or a sub-rect inside the shared cache. #[derive(Debug)] @@ -61,25 +49,25 @@ enum EntryDetails { size_in_bytes: usize, }, Picture { - /// Size of the tile (used for debug clears only) - size: DeviceIntSize, + // Index in the picture_textures array + texture_index: usize, + // Slice in the texture array + layer_index: usize, }, Cache { /// Origin within the texture layer where this item exists. origin: DeviceIntPoint, - /// ID of the allocation specific to its allocator. - alloc_id: AllocId, - /// The allocated size in bytes for this entry. - allocated_size_in_bytes: usize, + /// The layer index of the texture array. + layer_index: usize, }, } impl EntryDetails { - fn describe(&self) -> DeviceIntPoint { + fn describe(&self) -> (LayerIndex, DeviceIntPoint) { match *self { - EntryDetails::Standalone { .. } => DeviceIntPoint::zero(), - EntryDetails::Picture { .. } => DeviceIntPoint::zero(), - EntryDetails::Cache { origin, .. } => origin, + EntryDetails::Standalone { .. } => (0, DeviceIntPoint::zero()), + EntryDetails::Picture { layer_index, .. } => (layer_index, DeviceIntPoint::zero()), + EntryDetails::Cache { origin, layer_index, .. } => (layer_index, origin), } } } @@ -87,17 +75,7 @@ impl EntryDetails { #[derive(Debug, PartialEq)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum PictureCacheEntryMarker {} - -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum AutoCacheEntryMarker {} - -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum ManualCacheEntryMarker {} +pub enum CacheEntryMarker {} // Stores information related to a single entry in the texture // cache. This is stored for each item whether it's in the shared @@ -106,14 +84,12 @@ pub enum ManualCacheEntryMarker {} #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] struct CacheEntry { - /// Size of the requested item, in device pixels. Does not include any - /// padding for alignment that the allocator may have added to this entry's - /// allocation. + /// Size the requested item, in device pixels. size: DeviceIntSize, /// Details specific to standalone or shared items. details: EntryDetails, /// Arbitrary user data associated with this item. - user_data: [f32; 4], + user_data: [f32; 3], /// The last frame this item was requested for rendering. // TODO(gw): This stamp is only used for picture cache tiles, and some checks // in the glyph cache eviction code. We could probably remove it @@ -131,15 +107,8 @@ struct CacheEntry { eviction_notice: Option<EvictionNotice>, /// The type of UV rect this entry specifies. uv_rect_kind: UvRectKind, - - shader: TargetShader, } -malloc_size_of::malloc_size_of_is_0!( - CacheEntry, - AutoCacheEntryMarker, ManualCacheEntryMarker, PictureCacheEntryMarker -); - impl CacheEntry { // Create a new entry for a standalone texture. fn new_standalone( @@ -163,7 +132,6 @@ impl CacheEntry { uv_rect_handle: GpuCacheHandle::new(), eviction_notice: None, uv_rect_kind: params.uv_rect_kind, - shader: TargetShader::Default, } } @@ -173,10 +141,11 @@ impl CacheEntry { // to fetch from. fn update_gpu_cache(&mut self, gpu_cache: &mut GpuCache) { if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) { - let origin = self.details.describe(); + let (layer_index, origin) = self.details.describe(); let image_source = ImageSource { p0: origin.to_f32(), p1: (origin + self.size).to_f32(), + texture_layer: layer_index as f32, user_data: self.user_data, uv_rect_kind: self.uv_rect_kind, }; @@ -206,29 +175,7 @@ impl CacheEntry { /// previously inserted and then evicted, lookup of the handle will fail, and /// the cache handle needs to re-upload this item to the texture cache (see /// request() below). - -#[derive(MallocSizeOf,Clone,PartialEq,Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum TextureCacheHandle { - /// A fresh handle. - Empty, - - /// A handle for a picture cache entry, evicted on every frame if not used. - Picture(WeakFreeListHandle<PictureCacheEntryMarker>), - - /// A handle for an entry with automatic eviction. - Auto(WeakFreeListHandle<AutoCacheEntryMarker>), - - /// A handle for an entry with manual eviction. - Manual(WeakFreeListHandle<ManualCacheEntryMarker>) -} - -impl TextureCacheHandle { - pub fn invalid() -> Self { - TextureCacheHandle::Empty - } -} +pub type TextureCacheHandle = WeakFreeListHandle<CacheEntryMarker>; /// Describes the eviction policy for a given entry in the texture cache. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -271,285 +218,136 @@ impl EvictionNotice { } } -/// The different budget types for the texture cache. Each type has its own -/// memory budget. Once the budget is exceeded, entries with automatic eviction -/// are evicted. Entries with manual eviction share the same budget but are not -/// evicted once the budget is exceeded. -/// Keeping separate budgets ensures that we don't evict entries from unrelated -/// textures if one texture gets full. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(u8)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -enum BudgetType { - SharedColor8Linear, - SharedColor8Nearest, - SharedColor8Glyphs, - SharedAlpha8, - SharedAlpha8Glyphs, - SharedAlpha16, - Standalone, -} - -impl BudgetType { - pub const COUNT: usize = 7; - - pub const VALUES: [BudgetType; BudgetType::COUNT] = [ - BudgetType::SharedColor8Linear, - BudgetType::SharedColor8Nearest, - BudgetType::SharedColor8Glyphs, - BudgetType::SharedAlpha8, - BudgetType::SharedAlpha8Glyphs, - BudgetType::SharedAlpha16, - BudgetType::Standalone, - ]; - - pub const PRESSURE_COUNTERS: [usize; BudgetType::COUNT] = [ - profiler::TEXTURE_CACHE_COLOR8_LINEAR_PRESSURE, - profiler::TEXTURE_CACHE_COLOR8_NEAREST_PRESSURE, - profiler::TEXTURE_CACHE_COLOR8_GLYPHS_PRESSURE, - profiler::TEXTURE_CACHE_ALPHA8_PRESSURE, - profiler::TEXTURE_CACHE_ALPHA8_GLYPHS_PRESSURE, - profiler::TEXTURE_CACHE_ALPHA16_PRESSURE, - profiler::TEXTURE_CACHE_STANDALONE_PRESSURE, - ]; - - pub fn iter() -> impl Iterator<Item = BudgetType> { - BudgetType::VALUES.iter().cloned() - } -} - /// A set of lazily allocated, fixed size, texture arrays for each format the /// texture cache supports. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] struct SharedTextures { - color8_nearest: AllocatorList<ShelfAllocator, TextureParameters>, - alpha8_linear: AllocatorList<ShelfAllocator, TextureParameters>, - alpha8_glyphs: AllocatorList<ShelfAllocator, TextureParameters>, - alpha16_linear: AllocatorList<SlabAllocator, TextureParameters>, - color8_linear: AllocatorList<ShelfAllocator, TextureParameters>, - color8_glyphs: AllocatorList<ShelfAllocator, TextureParameters>, - bytes_per_texture_of_type: [i32 ; BudgetType::COUNT], + array_color8_nearest: TextureArray, + array_alpha8_linear: TextureArray, + array_alpha16_linear: TextureArray, + array_color8_linear: TextureArray, } impl SharedTextures { /// Mints a new set of shared textures. - fn new(color_formats: TextureFormatPair<ImageFormat>, config: &TextureCacheConfig) -> Self { - let mut bytes_per_texture_of_type = [0 ; BudgetType::COUNT]; - - // Used primarily for cached shadow masks. There can be lots of - // these on some pages like francine, but most pages don't use it - // much. - // Most content tends to fit into two 512x512 textures. We are - // conservatively using 1024x1024 to fit everything in a single - // texture and avoid breaking batches, but it's worth checking - // whether it would actually lead to a lot of batch breaks in - // practice. - let alpha8_linear = AllocatorList::new( - config.alpha8_texture_size, - ShelfAllocatorOptions { - num_columns: 1, - alignment: size2(8, 8), - .. ShelfAllocatorOptions::default() - }, - TextureParameters { - formats: TextureFormatPair::from(ImageFormat::R8), - filter: TextureFilter::Linear, - }, - ); - bytes_per_texture_of_type[BudgetType::SharedAlpha8 as usize] = - config.alpha8_texture_size * config.alpha8_texture_size; - - // The cache for alpha glyphs (separate to help with batching). - let alpha8_glyphs = AllocatorList::new( - config.alpha8_glyph_texture_size, - ShelfAllocatorOptions { - num_columns: if config.alpha8_glyph_texture_size >= 1024 { 2 } else { 1 }, - alignment: size2(4, 8), - .. ShelfAllocatorOptions::default() - }, - TextureParameters { - formats: TextureFormatPair::from(ImageFormat::R8), - filter: TextureFilter::Linear, - }, - ); - bytes_per_texture_of_type[BudgetType::SharedAlpha8Glyphs as usize] = - config.alpha8_glyph_texture_size * config.alpha8_glyph_texture_size; - - // Used for experimental hdr yuv texture support, but not used in - // production Firefox. - let alpha16_linear = AllocatorList::new( - config.alpha16_texture_size, - SlabAllocatorParameters { - region_size: TEXTURE_REGION_DIMENSIONS, - }, - TextureParameters { - formats: TextureFormatPair::from(ImageFormat::R16), - filter: TextureFilter::Linear, - }, - ); - bytes_per_texture_of_type[BudgetType::SharedAlpha16 as usize] = - ImageFormat::R16.bytes_per_pixel() * - config.alpha16_texture_size * config.alpha16_texture_size; - - // The primary cache for images, etc. - let color8_linear = AllocatorList::new( - config.color8_linear_texture_size, - ShelfAllocatorOptions { - num_columns: if config.color8_linear_texture_size >= 1024 { 2 } else { 1 }, - alignment: size2(16, 16), - .. ShelfAllocatorOptions::default() - }, - TextureParameters { - formats: color_formats.clone(), - filter: TextureFilter::Linear, - }, - ); - bytes_per_texture_of_type[BudgetType::SharedColor8Linear as usize] = - color_formats.internal.bytes_per_pixel() * - config.color8_linear_texture_size * config.color8_linear_texture_size; - - // The cache for subpixel-AA and bitmap glyphs (separate to help with batching). - let color8_glyphs = AllocatorList::new( - config.color8_glyph_texture_size, - ShelfAllocatorOptions { - num_columns: if config.color8_glyph_texture_size >= 1024 { 2 } else { 1 }, - alignment: size2(4, 8), - .. ShelfAllocatorOptions::default() - }, - TextureParameters { - formats: color_formats.clone(), - filter: TextureFilter::Linear, - }, - ); - bytes_per_texture_of_type[BudgetType::SharedColor8Glyphs as usize] = - color_formats.internal.bytes_per_pixel() * - config.color8_glyph_texture_size * config.color8_glyph_texture_size; - - // Used for image-rendering: crisp. This is mostly favicons, which - // are small. Some other images use it too, but those tend to be - // larger than 512x512 and thus don't use the shared cache anyway. - let color8_nearest = AllocatorList::new( - config.color8_nearest_texture_size, - ShelfAllocatorOptions::default(), - TextureParameters { - formats: color_formats.clone(), - filter: TextureFilter::Nearest, - } - ); - bytes_per_texture_of_type[BudgetType::SharedColor8Nearest as usize] = - color_formats.internal.bytes_per_pixel() * - config.color8_nearest_texture_size * config.color8_nearest_texture_size; - + fn new(color_formats: TextureFormatPair<ImageFormat>) -> Self { Self { - alpha8_linear, - alpha8_glyphs, - alpha16_linear, - color8_linear, - color8_glyphs, - color8_nearest, - bytes_per_texture_of_type, + // Used primarily for cached shadow masks. There can be lots of + // these on some pages like francine, but most pages don't use it + // much. + array_alpha8_linear: TextureArray::new( + TextureFormatPair::from(ImageFormat::R8), + TextureFilter::Linear, + 4, + ), + // Used for experimental hdr yuv texture support, but not used in + // production Firefox. + array_alpha16_linear: TextureArray::new( + TextureFormatPair::from(ImageFormat::R16), + TextureFilter::Linear, + 1, + ), + // The primary cache for images, glyphs, etc. + array_color8_linear: TextureArray::new( + color_formats.clone(), + TextureFilter::Linear, + 16, + ), + // Used for image-rendering: crisp. This is mostly favicons, which + // are small. Some other images use it too, but those tend to be + // larger than 512x512 and thus don't use the shared cache anyway. + array_color8_nearest: TextureArray::new( + color_formats, + TextureFilter::Nearest, + 1, + ), } } /// Clears each texture in the set, with the given set of pending updates. fn clear(&mut self, updates: &mut TextureUpdateList) { - let texture_dealloc_cb = &mut |texture_id| { - updates.push_free(texture_id); - }; - - self.alpha8_linear.clear(texture_dealloc_cb); - self.alpha8_glyphs.clear(texture_dealloc_cb); - self.alpha16_linear.clear(texture_dealloc_cb); - self.color8_linear.clear(texture_dealloc_cb); - self.color8_nearest.clear(texture_dealloc_cb); - self.color8_glyphs.clear(texture_dealloc_cb); + self.array_alpha8_linear.clear(updates); + self.array_alpha16_linear.clear(updates); + self.array_color8_linear.clear(updates); + self.array_color8_nearest.clear(updates); } /// Returns a mutable borrow for the shared texture array matching the parameters. fn select( - &mut self, external_format: ImageFormat, filter: TextureFilter, shader: TargetShader, - ) -> (&mut dyn AtlasAllocatorList<TextureParameters>, BudgetType) { + &mut self, external_format: ImageFormat, filter: TextureFilter + ) -> &mut TextureArray { match external_format { ImageFormat::R8 => { assert_eq!(filter, TextureFilter::Linear); - match shader { - TargetShader::Text => { - (&mut self.alpha8_glyphs, BudgetType::SharedAlpha8Glyphs) - }, - _ => (&mut self.alpha8_linear, BudgetType::SharedAlpha8), - } + &mut self.array_alpha8_linear } ImageFormat::R16 => { assert_eq!(filter, TextureFilter::Linear); - (&mut self.alpha16_linear, BudgetType::SharedAlpha16) + &mut self.array_alpha16_linear } ImageFormat::RGBA8 | ImageFormat::BGRA8 => { - match (filter, shader) { - (TextureFilter::Linear, TargetShader::Text) => { - (&mut self.color8_glyphs, BudgetType::SharedColor8Glyphs) - }, - (TextureFilter::Linear, _) => { - (&mut self.color8_linear, BudgetType::SharedColor8Linear) - }, - (TextureFilter::Nearest, _) => { - (&mut self.color8_nearest, BudgetType::SharedColor8Nearest) - }, - _ => panic!("Unexpected filter {:?}", filter), + match filter { + TextureFilter::Linear => &mut self.array_color8_linear, + TextureFilter::Nearest => &mut self.array_color8_nearest, + _ => panic!("Unexpexcted filter {:?}", filter), } } _ => panic!("Unexpected format {:?}", external_format), } } - - /// How many bytes a single texture of the given type takes up, for the - /// configured texture sizes. - fn bytes_per_shared_texture(&self, budget_type: BudgetType) -> usize { - self.bytes_per_texture_of_type[budget_type as usize] as usize - } -} - -/// The textures used to hold picture cache tiles. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -struct PictureTexture { - texture_id: CacheTextureId, - size: DeviceIntSize, - format: ImageFormat, - is_allocated: bool, - last_frame_used: FrameId, -} - -impl PictureTexture { - fn size_in_bytes(&self) -> usize { - let bpp = self.format.bytes_per_pixel() as usize; - (self.size.width * self.size.height) as usize * bpp - } } -/// The textures used to hold picture cache tiles. +/// The texture arrays used to hold picture cache tiles. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] struct PictureTextures { - /// Current list of textures in the pool - textures: Vec<PictureTexture>, - /// Default tile size for content tiles - default_tile_size: DeviceIntSize, - /// Number of currently allocated textures in the pool - allocated_texture_count: usize, + textures: Vec<WholeTextureArray>, } impl PictureTextures { fn new( - default_tile_size: DeviceIntSize, + initial_window_size: DeviceIntSize, + picture_tile_sizes: &[DeviceIntSize], + next_texture_id: &mut CacheTextureId, + pending_updates: &mut TextureUpdateList, ) -> Self { - PictureTextures { - textures: Vec::new(), - default_tile_size, - allocated_texture_count: 0, + let mut textures = Vec::new(); + for tile_size in picture_tile_sizes { + // TODO(gw): The way initial size is used here may allocate a lot of memory once + // we are using multiple slice sizes. Do some measurements once we + // have multiple slices here and adjust the calculations as required. + let num_x = (initial_window_size.width + tile_size.width - 1) / tile_size.width; + let num_y = (initial_window_size.height + tile_size.height - 1) / tile_size.height; + let mut slice_count = (num_x * num_y).max(1).min(16) as usize; + if slice_count < 4 { + // On some platforms we get bogus (1x1) initial window size. The first real frame will then + // reallocate many more picture cache slices. Don't bother preallocating in that case. + slice_count = 0; + } + + if slice_count == 0 { + continue; + } + + let texture = WholeTextureArray { + size: *tile_size, + filter: TextureFilter::Nearest, + format: PICTURE_TILE_FORMAT, + texture_id: *next_texture_id, + slices: vec![WholeTextureSlice { uv_rect_handle: None }; slice_count], + has_depth: true, + }; + + next_texture_id.0 += 1; + + pending_updates.push_alloc(texture.texture_id, texture.to_info()); + + textures.push(texture); } + + PictureTextures { textures } } fn get_or_allocate_tile( @@ -559,143 +357,75 @@ impl PictureTextures { next_texture_id: &mut CacheTextureId, pending_updates: &mut TextureUpdateList, ) -> CacheEntry { - let mut texture_id = None; - self.allocated_texture_count += 1; + let texture_index = self.textures + .iter() + .position(|texture| { texture.size == tile_size }) + .unwrap_or(self.textures.len()); - for texture in &mut self.textures { - if texture.size == tile_size && !texture.is_allocated { - // Found a target that's not currently in use which matches. Update - // the last_frame_used for GC purposes. - texture.is_allocated = true; - texture.last_frame_used = FrameId::INVALID; - texture_id = Some(texture.texture_id); - break; - } - } - - // Need to create a new render target and add it to the pool - - let texture_id = texture_id.unwrap_or_else(|| { - let texture_id = *next_texture_id; - next_texture_id.0 += 1; - - // Push a command to allocate device storage of the right size / format. - let info = TextureCacheAllocInfo { - target: ImageBufferKind::Texture2D, - width: tile_size.width, - height: tile_size.height, - format: ImageFormat::RGBA8, + if texture_index == self.textures.len() { + self.textures.push(WholeTextureArray { + size: tile_size, filter: TextureFilter::Nearest, - is_shared_cache: false, + format: PICTURE_TILE_FORMAT, + texture_id: *next_texture_id, + slices: Vec::new(), has_depth: true, - }; - - pending_updates.push_alloc(texture_id, info); - - self.textures.push(PictureTexture { - texture_id, - is_allocated: true, - format: ImageFormat::RGBA8, - size: tile_size, - last_frame_used: FrameId::INVALID, }); - - texture_id - }); - - CacheEntry { - size: tile_size, - user_data: [0.0; 4], - last_access: now, - details: EntryDetails::Picture { - size: tile_size, - }, - uv_rect_handle: GpuCacheHandle::new(), - input_format: ImageFormat::RGBA8, - filter: TextureFilter::Nearest, - swizzle: Swizzle::default(), - texture_id, - eviction_notice: None, - uv_rect_kind: UvRectKind::Rect, - shader: TargetShader::Default, + next_texture_id.0 += 1; } - } - fn free_tile( - &mut self, - id: CacheTextureId, - current_frame_id: FrameId, - ) { - self.allocated_texture_count -= 1; + let texture = &mut self.textures[texture_index]; - let texture = self.textures - .iter_mut() - .find(|t| t.texture_id == id) - .expect("bug: invalid texture id"); + let layer_index = match texture.find_free() { + Some(index) => index, + None => { + let was_empty = texture.slices.is_empty(); + let index = texture.grow(PICTURE_TEXTURE_ADD_SLICES); + let info = texture.to_info(); + if was_empty { + pending_updates.push_alloc(texture.texture_id, info); + } else { + pending_updates.push_realloc(texture.texture_id, info); + } - assert!(texture.is_allocated); - texture.is_allocated = false; + index + }, + }; + + texture.occupy(texture_index, layer_index, now) + } - assert_eq!(texture.last_frame_used, FrameId::INVALID); - texture.last_frame_used = current_frame_id; + fn get(&mut self, index: usize) -> &mut WholeTextureArray { + &mut self.textures[index] } fn clear(&mut self, pending_updates: &mut TextureUpdateList) { - for texture in self.textures.drain(..) { - pending_updates.push_free(texture.texture_id); + for texture in &mut self.textures { + if texture.slices.is_empty() { + continue; + } + + if let Some(texture_id) = texture.reset(PICTURE_TEXTURE_ADD_SLICES) { + pending_updates.push_reset(texture_id, texture.to_info()); + } } } - fn update_profile(&self, profile: &mut TransactionProfile) { - // For now, this profile counter just accumulates the tiles and bytes - // from all picture cache textures. - let mut picture_tiles = 0; + fn update_profile(&self, profile: &mut ResourceProfileCounter) { + // For now, this profile counter just accumulates the slices and bytes + // from all picture cache texture arrays. + let mut picture_slices = 0; let mut picture_bytes = 0; for texture in &self.textures { - picture_tiles += 1; + picture_slices += texture.slices.len(); picture_bytes += texture.size_in_bytes(); } - profile.set(profiler::PICTURE_TILES, picture_tiles); - profile.set(profiler::PICTURE_TILES_MEM, profiler::bytes_to_mb(picture_bytes)); + profile.set(picture_slices, picture_bytes); } - /// Simple garbage collect of picture cache tiles - fn gc( - &mut self, - pending_updates: &mut TextureUpdateList, - ) { - // Allow the picture cache pool to keep 25% of the current allocated tile count - // as free textures to be reused. This ensures the allowed tile count is appropriate - // based on current window size. - let free_texture_count = self.textures.len() - self.allocated_texture_count; - let allowed_retained_count = (self.allocated_texture_count as f32 * 0.25).ceil() as usize; - let do_gc = free_texture_count > allowed_retained_count; - - if do_gc { - // Sort the current pool by age, so that we remove oldest textures first - self.textures.sort_unstable_by_key(|t| cmp::Reverse(t.last_frame_used)); - - // We can't just use retain() because `PictureTexture` requires manual cleanup. - let mut allocated_targets = SmallVec::<[PictureTexture; 32]>::new(); - let mut retained_targets = SmallVec::<[PictureTexture; 32]>::new(); - - for target in self.textures.drain(..) { - if target.is_allocated { - // Allocated targets can't be collected - allocated_targets.push(target); - } else if retained_targets.len() < allowed_retained_count { - // Retain the most recently used targets up to the allowed count - retained_targets.push(target); - } else { - // The rest of the targets get freed - assert_ne!(target.last_frame_used, FrameId::INVALID); - pending_updates.push_free(target.texture_id); - } - } - - self.textures.extend(retained_targets); - self.textures.extend(allocated_targets); - } + #[cfg(feature = "replay")] + fn tile_sizes(&self) -> Vec<DeviceIntSize> { + self.textures.iter().map(|pt| pt.size).collect() } } @@ -703,33 +433,8 @@ impl PictureTextures { struct CacheAllocParams { descriptor: ImageDescriptor, filter: TextureFilter, - user_data: [f32; 4], + user_data: [f32; 3], uv_rect_kind: UvRectKind, - shader: TargetShader, -} - -/// Startup parameters for the texture cache. -/// -/// Texture sizes must be at least 512. -#[derive(Clone)] -pub struct TextureCacheConfig { - pub color8_linear_texture_size: i32, - pub color8_nearest_texture_size: i32, - pub color8_glyph_texture_size: i32, - pub alpha8_texture_size: i32, - pub alpha8_glyph_texture_size: i32, - pub alpha16_texture_size: i32, -} - -impl TextureCacheConfig { - pub const DEFAULT: Self = TextureCacheConfig { - color8_linear_texture_size: 2048, - color8_nearest_texture_size: 512, - color8_glyph_texture_size: 2048, - alpha8_texture_size: 1024, - alpha8_glyph_texture_size: 2048, - alpha16_texture_size: 512, - }; } /// General-purpose manager for images in GPU memory. This includes images, @@ -761,9 +466,8 @@ pub struct TextureCache { /// Maximum texture size supported by hardware. max_texture_size: i32, - /// Maximum texture size before it is considered preferable to break the - /// texture into tiles. - tiling_threshold: i32, + /// Maximum number of texture layers supported by hardware. + max_texture_layers: usize, /// Settings on using texture unit swizzling. swizzle: Option<SwizzleSettings>, @@ -782,43 +486,79 @@ pub struct TextureCache { /// The current `FrameStamp`. Used for cache eviction policies. now: FrameStamp, - /// Cache of texture cache handles with automatic lifetime management, evicted - /// in a least-recently-used order. - lru_cache: LRUCache<CacheEntry, AutoCacheEntryMarker>, + /// List of picture cache entries. These are maintained separately from regular + /// texture cache entries. + picture_cache_handles: Vec<FreeListHandle<CacheEntryMarker>>, - /// Cache of picture cache entries. - picture_cache_entries: FreeList<CacheEntry, PictureCacheEntryMarker>, + /// Cache of texture cache handles with automatic lifetime management, evicted + /// in a least-recently-used order (except those entries with manual eviction enabled). + lru_cache: LRUCache<CacheEntry, CacheEntryMarker>, - /// Strong handles for the picture_cache_entries FreeList. - picture_cache_handles: Vec<FreeListHandle<PictureCacheEntryMarker>>, + /// A list of texture cache handles that have been set to explicitly have manual + /// eviction policy enabled. The handles reference cache entries in the lru_cache + /// above, but have opted in to manual lifetime management. + manual_handles: Vec<FreeListHandle<CacheEntryMarker>>, - /// Cache of texture cache entries with manual liftime management. - manual_entries: FreeList<CacheEntry, ManualCacheEntryMarker>, + /// Estimated memory usage of allocated entries in all of the shared textures. This + /// is used to decide when to evict old items from the cache. + shared_bytes_allocated: usize, - /// Strong handles for the manual_entries FreeList. - manual_handles: Vec<FreeListHandle<ManualCacheEntryMarker>>, + /// Number of bytes allocated in standalone textures. Used as an input to deciding + /// when to run texture cache eviction. + standalone_bytes_allocated: usize, - /// Memory usage of allocated entries in all of the shared or standalone - /// textures. Includes both manually and automatically evicted entries. - bytes_allocated: [usize ; BudgetType::COUNT], -} + /// If the total bytes allocated in shared / standalone cache is less + /// than this, then allow the cache to grow without forcing an eviction. + // TODO(gw): In future, it's probably reasonable to make this higher again, perhaps 64-128 MB. + eviction_threshold_bytes: usize, -impl TextureCache { /// The maximum number of items that will be evicted per frame. This limit helps avoid jank /// on frames where we want to evict a large number of items. Instead, we'd prefer to drop /// the items incrementally over a number of frames, even if that means the total allocated /// size of the cache is above the desired threshold for a small number of frames. - const MAX_EVICTIONS_PER_FRAME: usize = 32; + max_evictions_per_frame: usize, +} +impl TextureCache { pub fn new( max_texture_size: i32, - tiling_threshold: i32, - default_picture_tile_size: DeviceIntSize, + mut max_texture_layers: usize, + picture_tile_sizes: &[DeviceIntSize], + initial_size: DeviceIntSize, color_formats: TextureFormatPair<ImageFormat>, swizzle: Option<SwizzleSettings>, - config: &TextureCacheConfig, + eviction_threshold_bytes: usize, + max_evictions_per_frame: usize, ) -> Self { - let pending_updates = TextureUpdateList::new(); + // On MBP integrated Intel GPUs, texture arrays appear to be + // implemented as a single texture of stacked layers, and that + // texture appears to be subject to the texture size limit. As such, + // allocating more than 32 512x512 regions results in a dimension + // longer than 16k (the max texture size), causing incorrect behavior. + // + // So we clamp the number of layers on mac. This results in maximum + // texture array size of 32MB, which isn't ideal but isn't terrible + // either. OpenGL on mac is not long for this earth, so this may be + // good enough until we have WebRender on gfx-rs (on Metal). + // + // On all platforms, we also clamp the number of textures per layer to 16 + // to avoid the cost of resizing large texture arrays (at the expense + // of batching efficiency). + // + // Note that we could also define this more generally in terms of + // |max_texture_size / TEXTURE_REGION_DIMENSION|, except: + // * max_texture_size is actually clamped beyond the device limit + // by Gecko to 8192, so we'd need to thread the raw device value + // here, and: + // * The bug we're working around is likely specific to a single + // driver family, and those drivers are also likely to share + // the same max texture size of 16k. If we do encounter a driver + // with the same bug but a lower max texture size, we might need + // to rethink our strategy anyway, since a limit below 32MB might + // start to introduce performance issues. + max_texture_layers = max_texture_layers.min(16); + + let mut pending_updates = TextureUpdateList::new(); // Shared texture cache controls swizzling on a per-entry basis, assuming that // the texture as a whole doesn't need to be swizzled (but only some entries do). @@ -827,26 +567,30 @@ impl TextureCache { swizzle.map_or(true, |s| s.bgra8_sampling_swizzle == Swizzle::default()) ); - let next_texture_id = CacheTextureId(1); + let mut next_texture_id = CacheTextureId(1); TextureCache { - shared_textures: SharedTextures::new(color_formats, config), + shared_textures: SharedTextures::new(color_formats), picture_textures: PictureTextures::new( - default_picture_tile_size, + initial_size, + picture_tile_sizes, + &mut next_texture_id, + &mut pending_updates, ), max_texture_size, - tiling_threshold, + max_texture_layers, swizzle, debug_flags: DebugFlags::empty(), next_id: next_texture_id, pending_updates, now: FrameStamp::INVALID, - lru_cache: LRUCache::new(BudgetType::COUNT), - picture_cache_entries: FreeList::new(), + lru_cache: LRUCache::new(), + shared_bytes_allocated: 0, + standalone_bytes_allocated: 0, picture_cache_handles: Vec::new(), - manual_entries: FreeList::new(), manual_handles: Vec::new(), - bytes_allocated: [0 ; BudgetType::COUNT], + eviction_threshold_bytes, + max_evictions_per_frame, } } @@ -856,19 +600,22 @@ impl TextureCache { #[cfg(test)] pub fn new_for_testing( max_texture_size: i32, + max_texture_layers: usize, image_format: ImageFormat, ) -> Self { let mut cache = Self::new( max_texture_size, - max_texture_size, - crate::picture::TILE_SIZE_DEFAULT, + max_texture_layers, + &[], + DeviceIntSize::zero(), TextureFormatPair::from(image_format), None, - &TextureCacheConfig::DEFAULT, + 64 * 1024 * 1024, + 32, ); let mut now = FrameStamp::first(DocumentId::new(IdNamespace(1), 1)); now.advance(); - cache.begin_frame(now, &mut TransactionProfile::new()); + cache.begin_frame(now); cache } @@ -885,8 +632,7 @@ impl TextureCache { Vec::new(), ); for handle in manual_handles { - let entry = self.manual_entries.free(handle); - self.evict_impl(entry); + self.evict_impl(handle); } // Evict all picture cache handles @@ -895,16 +641,13 @@ impl TextureCache { Vec::new(), ); for handle in picture_handles { - let entry = self.picture_cache_entries.free(handle); - self.evict_impl(entry); + self.evict_impl(handle); } // Evict all auto (LRU) cache handles - for budget_type in BudgetType::iter() { - while let Some(entry) = self.lru_cache.pop_oldest(budget_type as u8) { - entry.evict(); - self.free(&entry); - } + while let Some(entry) = self.lru_cache.pop_oldest() { + entry.evict(); + self.free(&entry); } // Free the picture and shared textures @@ -914,71 +657,40 @@ impl TextureCache { } /// Called at the beginning of each frame. - pub fn begin_frame(&mut self, stamp: FrameStamp, profile: &mut TransactionProfile) { + pub fn begin_frame(&mut self, stamp: FrameStamp) { debug_assert!(!self.now.is_valid()); profile_scope!("begin_frame"); self.now = stamp; // Texture cache eviction is done at the start of the frame. This ensures that // we won't evict items that have been requested on this frame. - // It also frees up space in the cache for items allocated later in the frame - // potentially reducing texture allocations and fragmentation. - self.evict_items_from_cache_if_required(profile); - self.expire_old_picture_cache_tiles(); + self.evict_items_from_cache_if_required(); } - pub fn end_frame(&mut self, profile: &mut TransactionProfile) { + pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) { debug_assert!(self.now.is_valid()); - self.picture_textures.gc( - &mut self.pending_updates, - ); - - let updates = &mut self.pending_updates; // To avoid referring to self in the closure. - let callback = &mut|texture_id| { updates.push_free(texture_id); }; + self.expire_old_picture_cache_tiles(); // Release of empty shared textures is done at the end of the frame. That way, if the // eviction at the start of the frame frees up a texture, that is then subsequently // used during the frame, we avoid doing a free/alloc for it. - self.shared_textures.alpha8_linear.release_empty_textures(callback); - self.shared_textures.alpha8_glyphs.release_empty_textures(callback); - self.shared_textures.alpha16_linear.release_empty_textures(callback); - self.shared_textures.color8_linear.release_empty_textures(callback); - self.shared_textures.color8_nearest.release_empty_textures(callback); - self.shared_textures.color8_glyphs.release_empty_textures(callback); - - for budget in BudgetType::iter() { - let threshold = self.get_eviction_threshold(budget); - let pressure = self.bytes_allocated[budget as usize] as f32 / threshold as f32; - profile.set(BudgetType::PRESSURE_COUNTERS[budget as usize], pressure); - } - - profile.set(profiler::TEXTURE_CACHE_A8_PIXELS, self.shared_textures.alpha8_linear.allocated_space()); - profile.set(profiler::TEXTURE_CACHE_A8_TEXTURES, self.shared_textures.alpha8_linear.allocated_textures()); - profile.set(profiler::TEXTURE_CACHE_A8_GLYPHS_PIXELS, self.shared_textures.alpha8_glyphs.allocated_space()); - profile.set(profiler::TEXTURE_CACHE_A8_GLYPHS_TEXTURES, self.shared_textures.alpha8_glyphs.allocated_textures()); - profile.set(profiler::TEXTURE_CACHE_A16_PIXELS, self.shared_textures.alpha16_linear.allocated_space()); - profile.set(profiler::TEXTURE_CACHE_A16_TEXTURES, self.shared_textures.alpha16_linear.allocated_textures()); - profile.set(profiler::TEXTURE_CACHE_RGBA8_LINEAR_PIXELS, self.shared_textures.color8_linear.allocated_space()); - profile.set(profiler::TEXTURE_CACHE_RGBA8_LINEAR_TEXTURES, self.shared_textures.color8_linear.allocated_textures()); - profile.set(profiler::TEXTURE_CACHE_RGBA8_NEAREST_PIXELS, self.shared_textures.color8_nearest.allocated_space()); - profile.set(profiler::TEXTURE_CACHE_RGBA8_NEAREST_TEXTURES, self.shared_textures.color8_nearest.allocated_textures()); - profile.set(profiler::TEXTURE_CACHE_RGBA8_GLYPHS_PIXELS, self.shared_textures.color8_glyphs.allocated_space()); - profile.set(profiler::TEXTURE_CACHE_RGBA8_GLYPHS_TEXTURES, self.shared_textures.color8_glyphs.allocated_textures()); - - self.picture_textures.update_profile(profile); - - let shared_bytes = [ - BudgetType::SharedColor8Linear, - BudgetType::SharedColor8Nearest, - BudgetType::SharedColor8Glyphs, - BudgetType::SharedAlpha8, - BudgetType::SharedAlpha8Glyphs, - BudgetType::SharedAlpha16, - ].iter().map(|b| self.bytes_allocated[*b as usize]).sum(); - let standalone_bytes = self.bytes_allocated[BudgetType::Standalone as usize]; - - profile.set(profiler::TEXTURE_CACHE_SHARED_MEM, profiler::bytes_to_mb(shared_bytes)); - profile.set(profiler::TEXTURE_CACHE_STANDALONE_MEM, profiler::bytes_to_mb(standalone_bytes)); + self.shared_textures.array_alpha8_linear.release_empty_textures(&mut self.pending_updates); + self.shared_textures.array_alpha16_linear.release_empty_textures(&mut self.pending_updates); + self.shared_textures.array_color8_linear.release_empty_textures(&mut self.pending_updates); + self.shared_textures.array_color8_nearest.release_empty_textures(&mut self.pending_updates); + + self.shared_textures.array_alpha8_linear + .update_profile(&mut texture_cache_profile.pages_alpha8_linear); + self.shared_textures.array_alpha16_linear + .update_profile(&mut texture_cache_profile.pages_alpha16_linear); + self.shared_textures.array_color8_linear + .update_profile(&mut texture_cache_profile.pages_color8_linear); + self.shared_textures.array_color8_nearest + .update_profile(&mut texture_cache_profile.pages_color8_nearest); + self.picture_textures + .update_profile(&mut texture_cache_profile.pages_picture); + texture_cache_profile.shared_bytes.set(self.shared_bytes_allocated); + texture_cache_profile.standalone_bytes.set(self.standalone_bytes_allocated); self.now = FrameStamp::INVALID; } @@ -992,45 +704,15 @@ impl TextureCache { // texture cache (either never uploaded, or has been // evicted on a previous frame). pub fn request(&mut self, handle: &TextureCacheHandle, gpu_cache: &mut GpuCache) -> bool { - let now = self.now; - let entry = match handle { - TextureCacheHandle::Empty => None, - TextureCacheHandle::Picture(handle) => { - self.picture_cache_entries.get_opt_mut(handle) - }, - TextureCacheHandle::Auto(handle) => { - // Call touch rather than get_opt_mut so that the LRU index - // knows that the entry has been used. - self.lru_cache.touch(handle) - }, - TextureCacheHandle::Manual(handle) => { - self.manual_entries.get_opt_mut(handle) - }, - }; - entry.map_or(true, |entry| { + match self.lru_cache.touch(handle) { // If an image is requested that is already in the cache, // refresh the GPU cache data associated with this item. - entry.last_access = now; - entry.update_gpu_cache(gpu_cache); - false - }) - } - - fn get_entry_opt(&self, handle: &TextureCacheHandle) -> Option<&CacheEntry> { - match handle { - TextureCacheHandle::Empty => None, - TextureCacheHandle::Picture(handle) => self.picture_cache_entries.get_opt(handle), - TextureCacheHandle::Auto(handle) => self.lru_cache.get_opt(handle), - TextureCacheHandle::Manual(handle) => self.manual_entries.get_opt(handle), - } - } - - fn get_entry_opt_mut(&mut self, handle: &TextureCacheHandle) -> Option<&mut CacheEntry> { - match handle { - TextureCacheHandle::Empty => None, - TextureCacheHandle::Picture(handle) => self.picture_cache_entries.get_opt_mut(handle), - TextureCacheHandle::Auto(handle) => self.lru_cache.get_opt_mut(handle), - TextureCacheHandle::Manual(handle) => self.manual_entries.get_opt_mut(handle), + Some(entry) => { + entry.last_access = self.now; + entry.update_gpu_cache(gpu_cache); + false + } + None => true, } } @@ -1038,20 +720,26 @@ impl TextureCache { // texture cache (either never uploaded, or has been // evicted on a previous frame). pub fn needs_upload(&self, handle: &TextureCacheHandle) -> bool { - !self.is_allocated(handle) + self.lru_cache.get_opt(handle).is_none() } pub fn max_texture_size(&self) -> i32 { self.max_texture_size } - pub fn tiling_threshold(&self) -> i32 { - self.tiling_threshold + #[cfg(feature = "replay")] + pub fn max_texture_layers(&self) -> usize { + self.max_texture_layers + } + + #[cfg(feature = "replay")] + pub fn picture_tile_sizes(&self) -> Vec<DeviceIntSize> { + self.picture_textures.tile_sizes() } #[cfg(feature = "replay")] pub fn color_formats(&self) -> TextureFormatPair<ImageFormat> { - self.shared_textures.color8_linear.texture_parameters().formats.clone() + self.shared_textures.array_color8_linear.formats.clone() } #[cfg(feature = "replay")] @@ -1059,6 +747,16 @@ impl TextureCache { self.swizzle } + #[cfg(feature = "replay")] + pub fn eviction_threshold_bytes(&self) -> usize { + self.eviction_threshold_bytes + } + + #[cfg(feature = "replay")] + pub fn max_evictions_per_frame(&self) -> usize { + self.max_evictions_per_frame + } + pub fn pending_updates(&mut self) -> TextureUpdateList { mem::replace(&mut self.pending_updates, TextureUpdateList::new()) } @@ -1070,22 +768,22 @@ impl TextureCache { descriptor: ImageDescriptor, filter: TextureFilter, data: Option<CachedImageData>, - user_data: [f32; 4], + user_data: [f32; 3], mut dirty_rect: ImageDirtyRect, gpu_cache: &mut GpuCache, eviction_notice: Option<&EvictionNotice>, uv_rect_kind: UvRectKind, eviction: Eviction, - shader: TargetShader, ) { debug_assert!(self.now.is_valid()); + // Determine if we need to allocate texture cache memory // for this item. We need to reallocate if any of the following // is true: // - Never been in the cache // - Has been in the cache but was evicted. // - Exists in the cache but dimensions / format have changed. - let realloc = match self.get_entry_opt(handle) { + let realloc = match self.lru_cache.get_opt(handle) { Some(entry) => { entry.size != descriptor.size || (entry.input_format != descriptor.format && entry.alternative_input_format() != descriptor.format) @@ -1097,15 +795,22 @@ impl TextureCache { }; if realloc { - let params = CacheAllocParams { descriptor, filter, user_data, uv_rect_kind, shader }; - self.allocate(¶ms, handle, eviction); + let params = CacheAllocParams { descriptor, filter, user_data, uv_rect_kind }; + self.allocate(¶ms, handle); // If we reallocated, we need to upload the whole item again. dirty_rect = DirtyRect::All; } - let entry = self.get_entry_opt_mut(handle) - .expect("BUG: There must be an entry at this handle now"); + // Update eviction policy (this is a no-op if it hasn't changed) + if eviction == Eviction::Manual { + if let Some(manual_handle) = self.lru_cache.set_manual_eviction(handle) { + self.manual_handles.push(manual_handle); + } + } + + let entry = self.lru_cache.get_opt_mut(handle) + .expect("BUG: handle must be valid now"); // Install the new eviction notice for this update, if applicable. entry.eviction_notice = eviction_notice.cloned(); @@ -1127,32 +832,31 @@ impl TextureCache { // If the swizzling is supported, we always upload in the internal // texture format (thus avoiding the conversion by the driver). // Otherwise, pass the external format to the driver. - let origin = entry.details.describe(); - let texture_id = entry.texture_id; - let size = entry.size; let use_upload_format = self.swizzle.is_none(); + let (layer_index, origin) = entry.details.describe(); let op = TextureCacheUpdate::new_update( data, &descriptor, origin, - size, + entry.size, + layer_index as i32, use_upload_format, &dirty_rect, ); - self.pending_updates.push_update(texture_id, op); + self.pending_updates.push_update(entry.texture_id, op); } } // Check if a given texture handle has a valid allocation // in the texture cache. pub fn is_allocated(&self, handle: &TextureCacheHandle) -> bool { - self.get_entry_opt(handle).is_some() + self.lru_cache.get_opt(handle).is_some() } // Check if a given texture handle was last used as recently // as the specified number of previous frames. pub fn is_recently_used(&self, handle: &TextureCacheHandle, margin: usize) -> bool { - self.get_entry_opt(handle).map_or(false, |entry| { + self.lru_cache.get_opt(handle).map_or(false, |entry| { entry.last_access.frame_id() + margin >= self.now.frame_id() }) } @@ -1160,7 +864,7 @@ impl TextureCache { // Return the allocated size of the texture handle's associated data, // or otherwise indicate the handle is invalid. pub fn get_allocated_size(&self, handle: &TextureCacheHandle) -> Option<usize> { - self.get_entry_opt(handle).map(|entry| { + self.lru_cache.get_opt(handle).map(|entry| { (entry.input_format.bytes_per_pixel() * entry.size.area()) as usize }) } @@ -1171,15 +875,12 @@ impl TextureCache { // This function will assert in debug modes if the caller // tries to get a handle that was not requested this frame. pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem { - let (texture_id, uv_rect, swizzle, uv_rect_handle, user_data) = self.get_cache_location(handle); + let (texture_id, layer_index, uv_rect, swizzle, uv_rect_handle) = self.get_cache_location(handle); CacheItem { uv_rect_handle, - texture_id: TextureSource::TextureCache( - texture_id, - swizzle, - ), + texture_id: TextureSource::TextureCache(texture_id, swizzle), uv_rect, - user_data, + texture_layer: layer_index as i32, } } @@ -1191,72 +892,43 @@ impl TextureCache { pub fn get_cache_location( &self, handle: &TextureCacheHandle, - ) -> (CacheTextureId, DeviceIntRect, Swizzle, GpuCacheHandle, [f32; 4]) { - let entry = self - .get_entry_opt(handle) + ) -> (CacheTextureId, LayerIndex, DeviceIntRect, Swizzle, GpuCacheHandle) { + let entry = self.lru_cache + .get_opt(handle) .expect("BUG: was dropped from cache or not updated!"); debug_assert_eq!(entry.last_access, self.now); - let origin = entry.details.describe(); - ( - entry.texture_id, - DeviceIntRect::new(origin, entry.size), - entry.swizzle, - entry.uv_rect_handle, - entry.user_data, - ) + let (layer_index, origin) = entry.details.describe(); + (entry.texture_id, + layer_index as usize, + DeviceIntRect::new(origin, entry.size), + entry.swizzle, + entry.uv_rect_handle) } /// Internal helper function to evict a strong texture cache handle fn evict_impl( &mut self, - entry: CacheEntry, + handle: FreeListHandle<CacheEntryMarker>, ) { + let entry = self.lru_cache.remove_manual_handle(handle); entry.evict(); self.free(&entry); } /// Evict a texture cache handle that was previously set to be in manual /// eviction mode. - pub fn evict_handle(&mut self, handle: &TextureCacheHandle) { - match handle { - TextureCacheHandle::Manual(handle) => { - // Find the strong handle that matches this weak handle. If this - // ever shows up in profiles, we can make it a hash (but the number - // of manual eviction handles is typically small). - // Alternatively, we could make a more forgiving FreeList variant - // which does not differentiate between strong and weak handles. - let index = self.manual_handles.iter().position(|strong_handle| { - strong_handle.matches(handle) - }); - if let Some(index) = index { - let handle = self.manual_handles.swap_remove(index); - let entry = self.manual_entries.free(handle); - self.evict_impl(entry); - } - } - TextureCacheHandle::Auto(handle) => { - if let Some(entry) = self.lru_cache.remove(handle) { - self.evict_impl(entry); - } - } - _ => {} - } - } - - pub fn dump_color8_linear_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> { - self.shared_textures.color8_linear.dump_as_svg(output) - } - - pub fn dump_color8_glyphs_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> { - self.shared_textures.color8_glyphs.dump_as_svg(output) - } - - pub fn dump_alpha8_glyphs_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> { - self.shared_textures.alpha8_glyphs.dump_as_svg(output) - } + pub fn evict_manual_handle(&mut self, handle: &TextureCacheHandle) { + // Find the strong handle that matches this weak handle. If this + // ever shows up in profiles, we can make it a hash (but the number + // of manual eviction handles is typically small). + let index = self.manual_handles.iter().position(|strong_handle| { + strong_handle.matches(handle) + }); - pub fn dump_alpha8_linear_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> { - self.shared_textures.alpha8_linear.dump_as_svg(output) + if let Some(index) = index { + let handle = self.manual_handles.swap_remove(index); + self.evict_impl(handle); + } } /// Expire picture cache tiles that haven't been referenced in the last frame. @@ -1265,152 +937,70 @@ impl TextureCache { fn expire_old_picture_cache_tiles(&mut self) { for i in (0 .. self.picture_cache_handles.len()).rev() { let evict = { - let entry = self.picture_cache_entries.get( - &self.picture_cache_handles[i] - ); - - // This function is called at the beginning of the frame, - // so we don't yet know which picture cache tiles will be - // requested this frame. Therefore only evict picture cache - // tiles which weren't requested in the *previous* frame. - entry.last_access.frame_id() < self.now.frame_id() - 1 + let entry = self.lru_cache.get(&self.picture_cache_handles[i]); + + // Texture cache entries can be evicted at the start of + // a frame, or at any time during the frame when a cache + // allocation is occurring. This means that entries tagged + // with eager eviction may get evicted before they have a + // chance to be requested on the current frame. Instead, + // advance the frame id of the entry by one before + // comparison. This ensures that an eager entry will + // not be evicted until it is not used for at least + // one complete frame. + let mut entry_frame_id = entry.last_access.frame_id(); + entry_frame_id.advance(); + + entry_frame_id < self.now.frame_id() }; if evict { let handle = self.picture_cache_handles.swap_remove(i); - let entry = self.picture_cache_entries.free(handle); - self.evict_impl(entry); + self.evict_impl(handle); } } } - /// Get the eviction threshold, in bytes, for the given budget type. - fn get_eviction_threshold(&self, budget_type: BudgetType) -> usize { - if budget_type == BudgetType::Standalone { - // For standalone textures, the only reason to evict textures is - // to save GPU memory. Batching / draw call concerns do not apply - // to standalone textures, because unused textures don't cause - // extra draw calls. - return 16 * 1024 * 1024; - } - - // For shared textures, evicting an entry only frees up GPU memory if it - // causes one of the shared textures to become empty. - // The bigger concern for shared textures is batching: The entries that - // are needed in the current frame should be distributed across as few - // shared textures as possible, to minimize the number of draw calls. - // Ideally we only want one or two textures per type. - let expected_texture_count = match budget_type { - BudgetType::SharedColor8Nearest | BudgetType::SharedAlpha16 => { - // These types are only rarely used, we don't want more than - // one of each. - 1 - }, - - _ => { - // For the other types, having two textures is acceptable. - 2 - }, - }; - - // The threshold that we pick here will be compared to the number of - // bytes that are *occupied by entries*. And we're trying to target a - // certain number of textures. - // Unfortunately, it's hard to predict the number of needed textures - // purely based on number of occupied bytes: Due to the different - // rectangular shape of each entry, and due to decisions made by the - // allocator, sometimes we may need a new texture even if there are - // still large gaps in the existing textures. - // Let's assume that we have an average allocator wastage of 50%. - let average_used_bytes_per_texture_when_full = - self.shared_textures.bytes_per_shared_texture(budget_type) / 2; - - // Compute the threshold. - // Because of varying allocator wastage, we may still need to use more - // than the expected number of textures; that's fine. We'll also go over - // the expected texture count whenever a large number of entries are - // needed to draw a complex frame (since we don't evict entries which - // are needed for the current frame), or if eviction hasn't had a chance - // to catch up after a large allocation burst. - expected_texture_count * average_used_bytes_per_texture_when_full - } - /// Evict old items from the shared and standalone caches, if we're over a /// threshold memory usage value - fn evict_items_from_cache_if_required(&mut self, profile: &mut TransactionProfile) { - let previous_frame_id = self.now.frame_id() - 1; + fn evict_items_from_cache_if_required(&mut self) { let mut eviction_count = 0; - let mut youngest_evicted = FrameId::first(); - - for budget in BudgetType::iter() { - let threshold = self.get_eviction_threshold(budget); - while self.should_continue_evicting( - self.bytes_allocated[budget as usize], - threshold, - eviction_count, - ) { - if let Some(entry) = self.lru_cache.peek_oldest(budget as u8) { - // Only evict this item if it wasn't used in the previous frame. The reason being that if it - // was used the previous frame then it will likely be used in this frame too, and we don't - // want to be continually evicting and reuploading the item every frame. - if entry.last_access.frame_id() >= previous_frame_id { - // Since the LRU cache is ordered by frame access, we can break out of the loop here because - // we know that all remaining items were also used in the previous frame (or more recently). - break; - } - if entry.last_access.frame_id() > youngest_evicted { - youngest_evicted = entry.last_access.frame_id(); - } - let entry = self.lru_cache.pop_oldest(budget as u8).unwrap(); + + // Keep evicting while memory is above the threshold, and we haven't + // reached a maximum number of evictions this frame. + while self.current_memory_estimate() > self.eviction_threshold_bytes && eviction_count < self.max_evictions_per_frame { + match self.lru_cache.pop_oldest() { + Some(entry) => { entry.evict(); self.free(&entry); eviction_count += 1; - } else { - // The LRU cache is empty, all remaining items use manual - // eviction. In this case, there's nothing we can do until - // the calling code manually evicts items to reduce the - // allocated cache size. + } + None => { + // It's possible that we could fail to pop an item from the LRU list to evict, if every + // item in the cache is set to manual eviction mode. In this case, just break out of the + // loop as there's nothing we can do until the calling code manually evicts items to + // reduce the allocated cache size. break; } } } - - if eviction_count > 0 { - profile.set(profiler::TEXTURE_CACHE_EVICTION_COUNT, eviction_count); - profile.set( - profiler::TEXTURE_CACHE_YOUNGEST_EVICTION, - self.now.frame_id().as_usize() - youngest_evicted.as_usize() - ); - } } - /// Returns true if texture cache eviction loop should continue - fn should_continue_evicting( - &self, - bytes_allocated: usize, - threshold: usize, - eviction_count: usize, - ) -> bool { - // If current memory usage is below selected threshold, we can stop evicting items - if bytes_allocated < threshold { - return false; - } - - // If current memory usage is significantly more than the threshold, keep evicting this frame - if bytes_allocated > 4 * threshold { - return true; - } - - // Otherwise, only allow evicting up to a certain number of items per frame. This allows evictions - // to be spread over a number of frames, to avoid frame spikes. - eviction_count < Self::MAX_EVICTIONS_PER_FRAME + /// Return the total used bytes in standalone and shared textures. This is + /// used to determine how many textures need to be evicted to keep texture + /// cache memory usage under a reasonable limit. Note that this does not + /// include memory allocated to picture cache tiles, which are considered + /// separately for the purposes of texture cache eviction. + fn current_memory_estimate(&self) -> usize { + self.standalone_bytes_allocated + self.shared_bytes_allocated } // Free a cache entry from the standalone list or shared cache. fn free(&mut self, entry: &CacheEntry) { match entry.details { - EntryDetails::Picture { size } => { - self.picture_textures.free_tile(entry.texture_id, self.now.frame_id()); + EntryDetails::Picture { texture_index, layer_index } => { + let picture_texture = self.picture_textures.get(texture_index); + picture_texture.slices[layer_index].uv_rect_handle = None; if self.debug_flags.contains( DebugFlags::TEXTURE_CACHE_DBG | DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED) @@ -1418,27 +1008,28 @@ impl TextureCache { self.pending_updates.push_debug_clear( entry.texture_id, DeviceIntPoint::zero(), - size.width, - size.height, + picture_texture.size.width, + picture_texture.size.height, + layer_index, ); } } EntryDetails::Standalone { size_in_bytes, .. } => { - self.bytes_allocated[BudgetType::Standalone as usize] -= size_in_bytes; + self.standalone_bytes_allocated -= size_in_bytes; // This is a standalone texture allocation. Free it directly. self.pending_updates.push_free(entry.texture_id); } - EntryDetails::Cache { origin, alloc_id, allocated_size_in_bytes } => { - let (allocator_list, budget_type) = self.shared_textures.select( - entry.input_format, - entry.filter, - entry.shader, - ); + EntryDetails::Cache { origin, layer_index, .. } => { + // Free the block in the given region. + let texture_array = self.shared_textures.select(entry.input_format, entry.filter); + let unit = texture_array.units + .iter_mut() + .find(|unit| unit.texture_id == entry.texture_id) + .expect("Unable to find the associated texture array unit"); + let region = &mut unit.regions[layer_index]; - allocator_list.deallocate(entry.texture_id, alloc_id); - - self.bytes_allocated[budget_type as usize] -= allocated_size_in_bytes; + self.shared_bytes_allocated -= region.slab_size.size_in_bytes(texture_array.formats.internal); if self.debug_flags.contains( DebugFlags::TEXTURE_CACHE_DBG | @@ -1447,10 +1038,12 @@ impl TextureCache { self.pending_updates.push_debug_clear( entry.texture_id, origin, - entry.size.width, - entry.size.height, + region.slab_size.width, + region.slab_size.height, + layer_index, ); } + region.free(origin, &mut unit.empty_regions); } } } @@ -1459,42 +1052,13 @@ impl TextureCache { fn allocate_from_shared_cache( &mut self, params: &CacheAllocParams, - ) -> (CacheEntry, BudgetType) { - let (allocator_list, budget_type) = self.shared_textures.select( + ) -> CacheEntry { + // Mutably borrow the correct texture. + let texture_array = self.shared_textures.select( params.descriptor.format, params.filter, - params.shader, ); - - // To avoid referring to self in the closure. - let next_id = &mut self.next_id; - let pending_updates = &mut self.pending_updates; - - let (texture_id, alloc_id, allocated_rect) = allocator_list.allocate( - params.descriptor.size, - &mut |size, parameters| { - let texture_id = *next_id; - next_id.0 += 1; - pending_updates.push_alloc( - texture_id, - TextureCacheAllocInfo { - target: ImageBufferKind::Texture2D, - width: size.width, - height: size.height, - format: parameters.formats.internal, - filter: parameters.filter, - is_shared_cache: true, - has_depth: false, - }, - ); - - texture_id - }, - ); - - let formats = &allocator_list.texture_parameters().formats; - - let swizzle = if formats.external == params.descriptor.format { + let swizzle = if texture_array.formats.external == params.descriptor.format { Swizzle::default() } else { match self.swizzle { @@ -1503,28 +1067,59 @@ impl TextureCache { } }; - let bpp = formats.internal.bytes_per_pixel(); - let allocated_size_in_bytes = (allocated_rect.size.area() * bpp) as usize; - self.bytes_allocated[budget_type as usize] += allocated_size_in_bytes; + let max_texture_layers = self.max_texture_layers; + let slab_size = SlabSize::new(params.descriptor.size); - (CacheEntry { - size: params.descriptor.size, - user_data: params.user_data, - last_access: self.now, - details: EntryDetails::Cache { - origin: allocated_rect.origin, - alloc_id, - allocated_size_in_bytes, - }, - uv_rect_handle: GpuCacheHandle::new(), - input_format: params.descriptor.format, - filter: params.filter, - swizzle, - texture_id, - eviction_notice: None, - uv_rect_kind: params.uv_rect_kind, - shader: params.shader - }, budget_type) + let mut info = TextureCacheAllocInfo { + width: TEXTURE_REGION_DIMENSIONS, + height: TEXTURE_REGION_DIMENSIONS, + format: texture_array.formats.internal, + filter: texture_array.filter, + layer_count: 1, + is_shared_cache: true, + has_depth: false, + }; + + let unit_index = if let Some(index) = texture_array.units + .iter() + .position(|unit| unit.can_alloc(slab_size)) + { + index + } else if let Some(index) = texture_array.units + .iter() + .position(|unit| unit.regions.len() < max_texture_layers) + { + let unit = &mut texture_array.units[index]; + + unit.push_regions(texture_array.layers_per_allocation); + + info.layer_count = unit.regions.len() as i32; + self.pending_updates.push_realloc(unit.texture_id, info); + + index + } else { + let index = texture_array.units.len(); + texture_array.units.push(TextureArrayUnit { + texture_id: self.next_id, + regions: Vec::new(), + empty_regions: 0, + }); + + let unit = &mut texture_array.units[index]; + + unit.push_regions(texture_array.layers_per_allocation); + + info.layer_count = unit.regions.len() as i32; + self.pending_updates.push_alloc(self.next_id, info); + self.next_id.0 += 1; + index + }; + + self.shared_bytes_allocated += slab_size.size_in_bytes(texture_array.formats.internal); + + // Do the allocation. This can fail and return None + // if there are no free slots or regions available. + texture_array.alloc(params, unit_index, self.now, swizzle) } // Returns true if the given image descriptor *may* be @@ -1558,60 +1153,27 @@ impl TextureCache { allowed_in_shared_cache } - /// Allocate a render target via the pending updates sent to the renderer - pub fn alloc_render_target( - &mut self, - size: DeviceIntSize, - format: ImageFormat, - ) -> CacheTextureId { - let texture_id = self.next_id; - self.next_id.0 += 1; - - // Push a command to allocate device storage of the right size / format. - let info = TextureCacheAllocInfo { - target: ImageBufferKind::Texture2D, - width: size.width, - height: size.height, - format, - filter: TextureFilter::Linear, - is_shared_cache: false, - has_depth: false, - }; - - self.pending_updates.push_alloc(texture_id, info); - - texture_id - } - - /// Free an existing render target - pub fn free_render_target( - &mut self, - id: CacheTextureId, - ) { - self.pending_updates.push_free(id); - } - /// Allocates a new standalone cache entry. fn allocate_standalone_entry( &mut self, params: &CacheAllocParams, - ) -> (CacheEntry, BudgetType) { + ) -> CacheEntry { let texture_id = self.next_id; self.next_id.0 += 1; // Push a command to allocate device storage of the right size / format. let info = TextureCacheAllocInfo { - target: ImageBufferKind::Texture2D, width: params.descriptor.size.width, height: params.descriptor.size.height, format: params.descriptor.format, filter: params.filter, + layer_count: 1, is_shared_cache: false, has_depth: false, }; let size_in_bytes = (info.width * info.height * info.format.bytes_per_pixel()) as usize; - self.bytes_allocated[BudgetType::Standalone as usize] += size_in_bytes; + self.standalone_bytes_allocated += size_in_bytes; self.pending_updates.push_alloc(texture_id, info); @@ -1622,13 +1184,13 @@ impl TextureCache { None }; - (CacheEntry::new_standalone( + CacheEntry::new_standalone( texture_id, self.now, params, swizzle.unwrap_or_default(), size_in_bytes, - ), BudgetType::Standalone) + ) } /// Allocates a cache entry appropriate for the given parameters. @@ -1639,7 +1201,7 @@ impl TextureCache { fn allocate_cache_entry( &mut self, params: &CacheAllocParams, - ) -> (CacheEntry, BudgetType) { + ) -> CacheEntry { assert!(!params.descriptor.size.is_empty()); // If this image doesn't qualify to go in the shared (batching) cache, @@ -1653,14 +1215,9 @@ impl TextureCache { /// Allocates a cache entry for the given parameters, and updates the /// provided handle to point to the new entry. - fn allocate( - &mut self, - params: &CacheAllocParams, - handle: &mut TextureCacheHandle, - eviction: Eviction, - ) { + fn allocate(&mut self, params: &CacheAllocParams, handle: &mut TextureCacheHandle) { debug_assert!(self.now.is_valid()); - let (new_cache_entry, budget_type) = self.allocate_cache_entry(params); + let new_cache_entry = self.allocate_cache_entry(params); // If the handle points to a valid cache entry, we want to replace the // cache entry with our newly updated location. We also need to ensure @@ -1669,36 +1226,7 @@ impl TextureCache { // // If the handle is invalid, we need to insert the data, and append the // result to the corresponding vector. - let old_entry = match (&mut *handle, eviction) { - (TextureCacheHandle::Auto(handle), Eviction::Auto) => { - self.lru_cache.replace_or_insert(handle, budget_type as u8, new_cache_entry) - }, - (TextureCacheHandle::Manual(handle), Eviction::Manual) => { - let entry = self.manual_entries.get_opt_mut(handle) - .expect("Don't call this after evicting"); - Some(mem::replace(entry, new_cache_entry)) - }, - (TextureCacheHandle::Manual(_), Eviction::Auto) | - (TextureCacheHandle::Auto(_), Eviction::Manual) => { - panic!("Can't change eviction policy after initial allocation"); - }, - (TextureCacheHandle::Empty, Eviction::Auto) => { - let new_handle = self.lru_cache.push_new(budget_type as u8, new_cache_entry); - *handle = TextureCacheHandle::Auto(new_handle); - None - }, - (TextureCacheHandle::Empty, Eviction::Manual) => { - let manual_handle = self.manual_entries.insert(new_cache_entry); - let new_handle = manual_handle.weak(); - self.manual_handles.push(manual_handle); - *handle = TextureCacheHandle::Manual(new_handle); - None - }, - (TextureCacheHandle::Picture(_), _) => { - panic!("Picture cache entries are managed separately and shouldn't appear in this function"); - }, - }; - if let Some(old_entry) = old_entry { + if let Some(old_entry) = self.lru_cache.replace_or_insert(handle, new_cache_entry) { old_entry.evict(); self.free(&old_entry); } @@ -1714,18 +1242,7 @@ impl TextureCache { debug_assert!(self.now.is_valid()); debug_assert!(tile_size.width > 0 && tile_size.height > 0); - let need_alloc = match handle { - TextureCacheHandle::Empty => true, - TextureCacheHandle::Picture(handle) => { - // Check if the entry has been evicted. - self.picture_cache_entries.get_opt(handle).is_none() - }, - TextureCacheHandle::Auto(_) | TextureCacheHandle::Manual(_) => { - panic!("Unexpected handle type in update_picture_cache"); - } - }; - - if need_alloc { + if self.lru_cache.get_opt(handle).is_none() { let cache_entry = self.picture_textures.get_or_allocate_tile( tile_size, self.now, @@ -1733,56 +1250,448 @@ impl TextureCache { &mut self.pending_updates, ); - // Add the cache entry to the picture_cache_entries FreeList. - let strong_handle = self.picture_cache_entries.insert(cache_entry); - let new_handle = strong_handle.weak(); + // Add the cache entry to the LRU cache, then mark it for manual eviction + // so that the lifetime is controlled by the texture cache. - self.picture_cache_handles.push(strong_handle); + *handle = self.lru_cache.push_new(cache_entry); - *handle = TextureCacheHandle::Picture(new_handle); + let strong_handle = self.lru_cache + .set_manual_eviction(handle) + .expect("bug: handle must be valid here"); + self.picture_cache_handles.push(strong_handle); } - if let TextureCacheHandle::Picture(handle) = handle { - // Upload the resource rect and texture array layer. - self.picture_cache_entries - .get_opt_mut(handle) - .expect("BUG: handle must be valid now") - .update_gpu_cache(gpu_cache); - } else { - panic!("The handle should be valid picture cache handle now") - } + // Upload the resource rect and texture array layer. + self.lru_cache + .get_opt_mut(handle) + .expect("BUG: handle must be valid now") + .update_gpu_cache(gpu_cache); } pub fn shared_alpha_expected_format(&self) -> ImageFormat { - self.shared_textures.alpha8_linear.texture_parameters().formats.external + self.shared_textures.array_alpha8_linear.formats.external } pub fn shared_color_expected_format(&self) -> ImageFormat { - self.shared_textures.color8_linear.texture_parameters().formats.external + self.shared_textures.array_color8_linear.formats.external + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Copy, Clone, PartialEq)] +struct SlabSize { + width: i32, + height: i32, +} + +impl SlabSize { + fn new(size: DeviceIntSize) -> Self { + let x_size = quantize_dimension(size.width); + let y_size = quantize_dimension(size.height); + + assert!(x_size > 0 && x_size <= TEXTURE_REGION_DIMENSIONS); + assert!(y_size > 0 && y_size <= TEXTURE_REGION_DIMENSIONS); + + let (width, height) = match (x_size, y_size) { + // Special cased rectangular slab pages. + (512, 0..=64) => (512, 64), + (512, 128) => (512, 128), + (512, 256) => (512, 256), + (0..=64, 512) => (64, 512), + (128, 512) => (128, 512), + (256, 512) => (256, 512), + + // If none of those fit, use a square slab size. + (x_size, y_size) => { + let square_size = cmp::max(x_size, y_size); + (square_size, square_size) + } + }; + + SlabSize { + width, + height, + } } + fn size_in_bytes(&self, format: ImageFormat) -> usize { + let bpp = format.bytes_per_pixel(); + (self.width * self.height * bpp) as usize + } - pub fn default_picture_tile_size(&self) -> DeviceIntSize { - self.picture_textures.default_tile_size + fn invalid() -> SlabSize { + SlabSize { + width: 0, + height: 0, + } } +} - #[cfg(test)] - pub fn total_allocated_bytes_for_testing(&self) -> usize { - BudgetType::iter().map(|b| self.bytes_allocated[b as usize]).sum() +// The x/y location within a texture region of an allocation. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +struct TextureLocation(u8, u8); + +impl TextureLocation { + fn new(x: i32, y: i32) -> Self { + debug_assert!(x >= 0 && y >= 0 && x < 0x100 && y < 0x100); + TextureLocation(x as u8, y as u8) + } +} + +/// A region corresponds to a layer in a shared cache texture. +/// +/// All allocations within a region are of the same size. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +struct TextureRegion { + layer_index: usize, + slab_size: SlabSize, + free_slots: Vec<TextureLocation>, + total_slot_count: usize, +} + +impl TextureRegion { + fn new(layer_index: usize) -> Self { + TextureRegion { + layer_index, + slab_size: SlabSize::invalid(), + free_slots: Vec::new(), + total_slot_count: 0, + } + } + + // Initialize a region to be an allocator for a specific slab size. + fn init(&mut self, slab_size: SlabSize, empty_regions: &mut usize) { + debug_assert!(self.slab_size == SlabSize::invalid()); + debug_assert!(self.free_slots.is_empty()); + + self.slab_size = slab_size; + let slots_per_x_axis = TEXTURE_REGION_DIMENSIONS / self.slab_size.width; + let slots_per_y_axis = TEXTURE_REGION_DIMENSIONS / self.slab_size.height; + + // Add each block to a freelist. + for y in 0 .. slots_per_y_axis { + for x in 0 .. slots_per_x_axis { + self.free_slots.push(TextureLocation::new(x, y)); + } + } + + self.total_slot_count = self.free_slots.len(); + *empty_regions -= 1; + } + + // Deinit a region, allowing it to become a region with + // a different allocator size. + fn deinit(&mut self, empty_regions: &mut usize) { + self.slab_size = SlabSize::invalid(); + self.free_slots.clear(); + self.total_slot_count = 0; + *empty_regions += 1; + } + + fn is_empty(&self) -> bool { + self.slab_size == SlabSize::invalid() + } + + // Attempt to allocate a fixed size block from this region. + fn alloc(&mut self) -> Option<DeviceIntPoint> { + debug_assert!(self.slab_size != SlabSize::invalid()); + + self.free_slots.pop().map(|location| { + DeviceIntPoint::new( + self.slab_size.width * location.0 as i32, + self.slab_size.height * location.1 as i32, + ) + }) + } + + // Free a block in this region. + fn free(&mut self, point: DeviceIntPoint, empty_regions: &mut usize) { + let x = point.x / self.slab_size.width; + let y = point.y / self.slab_size.height; + self.free_slots.push(TextureLocation::new(x, y)); + + // If this region is completely unused, deinit it + // so that it can become a different slab size + // as required. + if self.free_slots.len() == self.total_slot_count { + self.deinit(empty_regions); + } + } +} + +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +struct TextureArrayUnit { + texture_id: CacheTextureId, + regions: Vec<TextureRegion>, + empty_regions: usize, +} + +impl TextureArrayUnit { + /// Adds a new empty region to the array. + fn push_regions(&mut self, count: i32) { + assert!(self.empty_regions <= self.regions.len()); + for _ in 0..count { + let index = self.regions.len(); + self.regions.push(TextureRegion::new(index)); + self.empty_regions += 1; + } + } + + /// Returns true if we can allocate the given entry. + fn can_alloc(&self, slab_size: SlabSize) -> bool { + self.empty_regions != 0 || self.regions.iter().any(|region| { + region.slab_size == slab_size && !region.free_slots.is_empty() + }) + } + + fn is_empty(&self) -> bool { + self.empty_regions == self.regions.len() + } +} + +/// A texture array contains a number of textures, each with a number of +/// layers, where each layer contains a region that can act as a slab allocator. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +struct TextureArray { + filter: TextureFilter, + formats: TextureFormatPair<ImageFormat>, + units: SmallVec<[TextureArrayUnit; 1]>, + layers_per_allocation: i32, +} + +impl TextureArray { + fn new( + formats: TextureFormatPair<ImageFormat>, + filter: TextureFilter, + layers_per_allocation: i32, + ) -> Self { + TextureArray { + formats, + filter, + units: SmallVec::new(), + layers_per_allocation, + } + } + + /// Returns the number of GPU bytes consumed by this texture array. + fn size_in_bytes(&self) -> usize { + let bpp = self.formats.internal.bytes_per_pixel() as usize; + let num_regions: usize = self.units.iter().map(|u| u.regions.len()).sum(); + num_regions * TEXTURE_REGION_PIXELS * bpp + } + + fn clear(&mut self, updates: &mut TextureUpdateList) { + for unit in self.units.drain(..) { + updates.push_free(unit.texture_id); + } + } + + fn release_empty_textures(&mut self, updates: &mut TextureUpdateList) { + self.units.retain(|unit| { + if unit.is_empty() { + updates.push_free(unit.texture_id); + + false + } else { + true + } + }); + } + + fn update_profile(&self, counter: &mut ResourceProfileCounter) { + let num_regions: usize = self.units.iter().map(|u| u.regions.len()).sum(); + counter.set(num_regions, self.size_in_bytes()); } - pub fn report_memory(&self, ops: &mut MallocSizeOfOps) -> usize { - self.lru_cache.size_of(ops) + /// Allocate space in this texture array. + fn alloc( + &mut self, + params: &CacheAllocParams, + unit_index: usize, + now: FrameStamp, + swizzle: Swizzle, + ) -> CacheEntry { + // Quantize the size of the allocation to select a region to + // allocate from. + let slab_size = SlabSize::new(params.descriptor.size); + let unit = &mut self.units[unit_index]; + + // TODO(gw): For simplicity, the initial implementation just + // has a single vec<> of regions. We could easily + // make this more efficient by storing a list of + // regions for each slab size specifically... + + // Keep track of the location of an empty region, + // in case we need to select a new empty region + // after the loop. + let mut empty_region_index = None; + let mut entry_details = None; + + // Run through the existing regions of this size, and see if + // we can find a free block in any of them. + for (i, region) in unit.regions.iter_mut().enumerate() { + if region.is_empty() { + empty_region_index = Some(i); + } else if region.slab_size == slab_size { + if let Some(location) = region.alloc() { + entry_details = Some(EntryDetails::Cache { + layer_index: region.layer_index, + origin: location, + }); + break; + } + } + } + + // Find a region of the right size and try to allocate from it. + let details = match entry_details { + Some(details) => details, + None => { + let region = &mut unit.regions[empty_region_index.unwrap()]; + region.init(slab_size, &mut unit.empty_regions); + EntryDetails::Cache { + layer_index: region.layer_index, + origin: region.alloc().unwrap(), + } + } + }; + + CacheEntry { + size: params.descriptor.size, + user_data: params.user_data, + last_access: now, + details, + uv_rect_handle: GpuCacheHandle::new(), + input_format: params.descriptor.format, + filter: self.filter, + swizzle, + texture_id: unit.texture_id, + eviction_notice: None, + uv_rect_kind: params.uv_rect_kind, + } } } + +/// A tracking structure for each slice in `WholeTextureArray`. +#[cfg_attr(feature = "capture", derive(Serialize))] +#[cfg_attr(feature = "replay", derive(Deserialize))] +#[derive(Clone, Copy, Debug)] +struct WholeTextureSlice { + uv_rect_handle: Option<GpuCacheHandle>, +} + +/// A texture array that allocates whole slices and doesn't do any region tracking. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct TextureParameters { - pub formats: TextureFormatPair<ImageFormat>, - pub filter: TextureFilter, +struct WholeTextureArray { + size: DeviceIntSize, + filter: TextureFilter, + format: ImageFormat, + texture_id: CacheTextureId, + slices: Vec<WholeTextureSlice>, + has_depth: bool, +} + +impl WholeTextureArray { + fn to_info(&self) -> TextureCacheAllocInfo { + TextureCacheAllocInfo { + width: self.size.width, + height: self.size.height, + format: self.format, + filter: self.filter, + layer_count: self.slices.len() as i32, + is_shared_cache: true, //TODO: reconsider + has_depth: self.has_depth, + } + } + + /// Returns the number of GPU bytes consumed by this texture array. + fn size_in_bytes(&self) -> usize { + let bpp = self.format.bytes_per_pixel() as usize; + self.slices.len() * (self.size.width * self.size.height) as usize * bpp + } + + /// Find an free slice. + fn find_free(&self) -> Option<LayerIndex> { + self.slices.iter().position(|slice| slice.uv_rect_handle.is_none()) + } + + /// Grow the array by the specified number of slices + fn grow(&mut self, count: usize) -> LayerIndex { + let index = self.slices.len(); + for _ in 0 .. count { + self.slices.push(WholeTextureSlice { + uv_rect_handle: None, + }); + } + index + } + + fn cache_entry_impl( + &self, + texture_index: usize, + layer_index: usize, + now: FrameStamp, + uv_rect_handle: GpuCacheHandle, + texture_id: CacheTextureId, + ) -> CacheEntry { + CacheEntry { + size: self.size, + user_data: [0.0; 3], + last_access: now, + details: EntryDetails::Picture { + texture_index, + layer_index, + }, + uv_rect_handle, + input_format: self.format, + filter: self.filter, + swizzle: Swizzle::default(), + texture_id, + eviction_notice: None, + uv_rect_kind: UvRectKind::Rect, + } + } + + /// Occupy a specified slice by a cache entry. + fn occupy( + &mut self, + texture_index: usize, + layer_index: usize, + now: FrameStamp, + ) -> CacheEntry { + let uv_rect_handle = GpuCacheHandle::new(); + assert!(self.slices[layer_index].uv_rect_handle.is_none()); + self.slices[layer_index].uv_rect_handle = Some(uv_rect_handle); + self.cache_entry_impl( + texture_index, + layer_index, + now, + uv_rect_handle, + self.texture_id, + ) + } + + /// Reset the texture array to the specified number of slices, if it's larger. + fn reset( + &mut self, num_slices: usize + ) -> Option<CacheTextureId> { + if self.slices.len() <= num_slices { + None + } else { + self.slices.truncate(num_slices); + Some(self.texture_id) + } + } } + impl TextureCacheUpdate { // Constructs a TextureCacheUpdate operation to be passed to the // rendering thread in order to do an upload to the right @@ -1792,6 +1701,7 @@ impl TextureCacheUpdate { descriptor: &ImageDescriptor, origin: DeviceIntPoint, size: DeviceIntSize, + layer_index: i32, use_upload_format: bool, dirty_rect: &ImageDirtyRect, ) -> TextureCacheUpdate { @@ -1841,6 +1751,7 @@ impl TextureCacheUpdate { stride: Some(stride), offset, format_override, + layer_index, } } DirtyRect::All => { @@ -1850,80 +1761,22 @@ impl TextureCacheUpdate { stride: descriptor.stride, offset: descriptor.offset, format_override, + layer_index, } } } } } -#[cfg(test)] -mod test_texture_cache { - #[test] - fn check_allocation_size_balance() { - // Allocate some glyphs, observe the total allocation size, and free - // the glyphs again. Check that the total allocation size is back at the - // original value. - - use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader}; - use crate::gpu_cache::GpuCache; - use crate::device::TextureFilter; - use crate::gpu_types::UvRectKind; - use api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, DirtyRect}; - use api::units::*; - use euclid::size2; - let mut gpu_cache = GpuCache::new_for_testing(); - let mut texture_cache = TextureCache::new_for_testing(2048, ImageFormat::BGRA8); - - let sizes: &[DeviceIntSize] = &[ - size2(23, 27), - size2(15, 22), - size2(11, 5), - size2(20, 25), - size2(38, 41), - size2(11, 19), - size2(13, 21), - size2(37, 40), - size2(13, 15), - size2(14, 16), - size2(10, 9), - size2(25, 28), - ]; - - let bytes_at_start = texture_cache.total_allocated_bytes_for_testing(); - - let handles: Vec<TextureCacheHandle> = sizes.iter().map(|size| { - let mut texture_cache_handle = TextureCacheHandle::invalid(); - texture_cache.request(&texture_cache_handle, &mut gpu_cache); - texture_cache.update( - &mut texture_cache_handle, - ImageDescriptor { - size: *size, - stride: None, - format: ImageFormat::BGRA8, - flags: ImageDescriptorFlags::empty(), - offset: 0, - }, - TextureFilter::Linear, - None, - [0.0; 4], - DirtyRect::All, - &mut gpu_cache, - None, - UvRectKind::Rect, - Eviction::Manual, - TargetShader::Text, - ); - texture_cache_handle - }).collect(); - - let bytes_after_allocating = texture_cache.total_allocated_bytes_for_testing(); - assert!(bytes_after_allocating > bytes_at_start); - - for handle in handles { - texture_cache.evict_handle(&handle); - } - - let bytes_at_end = texture_cache.total_allocated_bytes_for_testing(); - assert_eq!(bytes_at_end, bytes_at_start); +fn quantize_dimension(size: i32) -> i32 { + match size { + 0 => unreachable!(), + 1..=16 => 16, + 17..=32 => 32, + 33..=64 => 64, + 65..=128 => 128, + 129..=256 => 256, + 257..=512 => 512, + _ => panic!("Invalid dimensions for cache!"), } } diff --git a/third_party/webrender/webrender/src/texture_pack/mod.rs b/third_party/webrender/webrender/src/texture_pack/mod.rs deleted file mode 100644 index 21707a2e96f..00000000000 --- a/third_party/webrender/webrender/src/texture_pack/mod.rs +++ /dev/null @@ -1,329 +0,0 @@ -/* 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/. */ - -mod guillotine; -mod slab; - -pub use guillotine::*; -pub use slab::*; - -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use api::units::*; -use crate::internal_types::CacheTextureId; -use euclid::{point2, size2, default::Box2D}; -use smallvec::SmallVec; - -pub use etagere::AllocatorOptions as ShelfAllocatorOptions; -pub use etagere::BucketedAtlasAllocator as BucketedShelfAllocator; -pub use etagere::AtlasAllocator as ShelfAllocator; - -/// ID of an allocation within a given allocator. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct AllocId(pub u32); - -pub trait AtlasAllocator { - /// Specific parameters of the allocator. - type Parameters; - /// Constructor - fn new(size: i32, parameters: &Self::Parameters) -> Self; - /// Allocate a rectangle. - fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)>; - /// Deallocate a rectangle and return its size. - fn deallocate(&mut self, id: AllocId); - /// Return true if there is no live allocations. - fn is_empty(&self) -> bool; - /// Allocated area in pixels. - fn allocated_space(&self) -> i32; - /// Write a debug visualization of the atlas fitting in the provided rectangle. - /// - /// This is inserted in a larger dump so it shouldn't contain the xml start/end tags. - fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()>; -} - -pub trait AtlasAllocatorList<TextureParameters> { - /// Allocate a rectangle. - /// - /// If allocation fails, call the provided callback, add a new allocator to the list and try again. - fn allocate( - &mut self, - size: DeviceIntSize, - texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId, - ) -> (CacheTextureId, AllocId, DeviceIntRect); - - /// Deallocate a rectangle and return its size. - fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId); - - fn texture_parameters(&self) -> &TextureParameters; -} - -/// A number of 2D textures (single layer), each with a number of -/// regions that can act as a slab allocator. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -struct TextureUnit<Allocator> { - allocator: Allocator, - texture_id: CacheTextureId, -} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct AllocatorList<Allocator: AtlasAllocator, TextureParameters> { - units: SmallVec<[TextureUnit<Allocator>; 1]>, - size: i32, - atlas_parameters: Allocator::Parameters, - texture_parameters: TextureParameters, -} - -impl<Allocator: AtlasAllocator, TextureParameters> AllocatorList<Allocator, TextureParameters> { - pub fn new( - size: i32, - atlas_parameters: Allocator::Parameters, - texture_parameters: TextureParameters, - ) -> Self { - AllocatorList { - units: SmallVec::new(), - size, - atlas_parameters, - texture_parameters, - } - } - - pub fn allocate( - &mut self, - requested_size: DeviceIntSize, - texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId, - ) -> (CacheTextureId, AllocId, DeviceIntRect) { - // Try to allocate from one of the existing textures. - for unit in &mut self.units { - if let Some((alloc_id, rect)) = unit.allocator.allocate(requested_size) { - return (unit.texture_id, alloc_id, rect); - } - } - - // Need to create a new texture to hold the allocation. - let texture_id = texture_alloc_cb(size2(self.size, self.size), &self.texture_parameters); - let unit_index = self.units.len(); - - self.units.push(TextureUnit { - allocator: Allocator::new(self.size, &self.atlas_parameters), - texture_id, - }); - - let (alloc_id, rect) = self.units[unit_index] - .allocator - .allocate(requested_size) - .unwrap(); - - (texture_id, alloc_id, rect) - } - - pub fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) { - let unit = self.units - .iter_mut() - .find(|unit| unit.texture_id == texture_id) - .expect("Unable to find the associated texture array unit"); - - unit.allocator.deallocate(alloc_id); - } - - pub fn release_empty_textures<'l>(&mut self, texture_dealloc_cb: &'l mut dyn FnMut(CacheTextureId)) { - self.units.retain(|unit| { - if unit.allocator.is_empty() { - texture_dealloc_cb(unit.texture_id); - - false - } else{ - true - } - }); - } - - pub fn clear(&mut self, texture_dealloc_cb: &mut dyn FnMut(CacheTextureId)) { - for unit in self.units.drain(..) { - texture_dealloc_cb(unit.texture_id); - } - } - - #[allow(dead_code)] - pub fn dump_as_svg(&self, output: &mut dyn std::io::Write) -> std::io::Result<()> { - use svg_fmt::*; - - let num_arrays = self.units.len() as f32; - - let text_spacing = 15.0; - let unit_spacing = 30.0; - let texture_size = self.size as f32 / 2.0; - - let svg_w = unit_spacing * 2.0 + texture_size; - let svg_h = unit_spacing + num_arrays * (texture_size + text_spacing + unit_spacing); - - writeln!(output, "{}", BeginSvg { w: svg_w, h: svg_h })?; - - // Background. - writeln!(output, - " {}", - rectangle(0.0, 0.0, svg_w, svg_h) - .inflate(1.0, 1.0) - .fill(rgb(50, 50, 50)) - )?; - - let mut y = unit_spacing; - for unit in &self.units { - writeln!(output, " {}", text(unit_spacing, y, format!("{:?}", unit.texture_id)).color(rgb(230, 230, 230)))?; - - let rect = Box2D { - min: point2(unit_spacing, y), - max: point2(unit_spacing + texture_size, y + texture_size), - }; - - unit.allocator.dump_into_svg(&rect, output)?; - - y += unit_spacing + texture_size + text_spacing; - } - - writeln!(output, "{}", EndSvg) - } - - pub fn allocated_space(&self) -> i32 { - let mut accum = 0; - for unit in &self.units { - accum += unit.allocator.allocated_space(); - } - - accum - } - - pub fn allocated_textures(&self) -> usize { - self.units.len() - } -} - -impl<Allocator: AtlasAllocator, TextureParameters> AtlasAllocatorList<TextureParameters> -for AllocatorList<Allocator, TextureParameters> { - fn allocate( - &mut self, - requested_size: DeviceIntSize, - texture_alloc_cb: &mut dyn FnMut(DeviceIntSize, &TextureParameters) -> CacheTextureId, - ) -> (CacheTextureId, AllocId, DeviceIntRect) { - self.allocate(requested_size, texture_alloc_cb) - } - - fn deallocate(&mut self, texture_id: CacheTextureId, alloc_id: AllocId) { - self.deallocate(texture_id, alloc_id); - } - - fn texture_parameters(&self) -> &TextureParameters { - &self.texture_parameters - } -} - -impl AtlasAllocator for BucketedShelfAllocator { - type Parameters = ShelfAllocatorOptions; - - fn new(size: i32, options: &Self::Parameters) -> Self { - BucketedShelfAllocator::with_options(size2(size, size), options) - } - - fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> { - self.allocate(size.to_untyped()).map(|alloc| { - (AllocId(alloc.id.serialize()), alloc.rectangle.to_rect().cast_unit()) - }) - } - - fn deallocate(&mut self, id: AllocId) { - self.deallocate(etagere::AllocId::deserialize(id.0)); - } - - fn is_empty(&self) -> bool { - self.is_empty() - } - - fn allocated_space(&self) -> i32 { - self.allocated_space() - } - - fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> { - self.dump_into_svg(Some(&rect.to_i32().cast_unit()), output) - } -} - -impl AtlasAllocator for ShelfAllocator { - type Parameters = ShelfAllocatorOptions; - - fn new(size: i32, options: &Self::Parameters) -> Self { - ShelfAllocator::with_options(size2(size, size), options) - } - - fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> { - self.allocate(size.to_untyped()).map(|alloc| { - (AllocId(alloc.id.serialize()), alloc.rectangle.to_rect().cast_unit()) - }) - } - - fn deallocate(&mut self, id: AllocId) { - self.deallocate(etagere::AllocId::deserialize(id.0)); - } - - fn is_empty(&self) -> bool { - self.is_empty() - } - - fn allocated_space(&self) -> i32 { - self.allocated_space() - } - - fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> { - self.dump_into_svg(Some(&rect.to_i32().cast_unit()), output) - } -} - -#[test] -fn bug_1680769() { - let mut allocators: AllocatorList<ShelfAllocator, ()> = AllocatorList::new( - 1024, - ShelfAllocatorOptions::default(), - (), - ); - - let mut allocations = Vec::new(); - let mut next_id = CacheTextureId(0); - let alloc_cb = &mut |_: DeviceIntSize, _: &()| { - let texture_id = next_id; - next_id.0 += 1; - - texture_id - }; - - // Make some allocations, forcing the the creation of multiple textures. - for _ in 0..50 { - allocations.push(allocators.allocate(size2(256, 256), alloc_cb)); - } - - // Deallocate everything. - // It should empty all atlases and we still have textures allocated because - // we haven't called release_empty_textures yet. - for alloc in allocations.drain(..) { - allocators.deallocate(alloc.0, alloc.1); - } - - // Allocate something else. - // Bug 1680769 was causing this allocation to be duplicated and leaked in - // all textures. - allocations.push(allocators.allocate(size2(8, 8), alloc_cb)); - - // Deallocate all known allocations. - for alloc in allocations.drain(..) { - allocators.deallocate(alloc.0, alloc.1); - } - - // If we have leaked items, this won't manage to remove all textures. - allocators.release_empty_textures(&mut |_| {}); - - assert_eq!(allocators.allocated_textures(), 0); -} diff --git a/third_party/webrender/webrender/src/texture_pack/slab.rs b/third_party/webrender/webrender/src/texture_pack/slab.rs deleted file mode 100644 index 6e383833978..00000000000 --- a/third_party/webrender/webrender/src/texture_pack/slab.rs +++ /dev/null @@ -1,356 +0,0 @@ -/* 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/. */ - -#![deny(unconditional_recursion)] - -use super::{AtlasAllocator, AllocId}; -use api::units::{DeviceIntPoint, DeviceIntRect, DeviceIntSize}; -use euclid::{point2, size2, default::Box2D}; -use std::cmp; - -fn pack_alloc_id(region_index: usize, location: TextureLocation) -> AllocId { - AllocId( - region_index as u32 & 0xFFFF - | (location.0 as u32) << 16 - | (location.1 as u32) << 24 - ) -} - -fn unpack_alloc_id(id: AllocId) -> (usize, TextureLocation) { - ( - (id.0 & 0xFFFF) as usize, - TextureLocation( - ((id.0 >> 16) & 0xFF) as u8, - ((id.0 >> 24) & 0xFF) as u8, - ), - ) -} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -#[derive(Copy, Clone, PartialEq)] -struct SlabSize { - width: i32, - height: i32, -} - -impl SlabSize { - fn invalid() -> SlabSize { - SlabSize { - width: 0, - height: 0, - } - } - - fn get(size: DeviceIntSize) -> SlabSize { - fn quantize_dimension(size: i32) -> i32 { - match size { - 0 => unreachable!(), - 1..=16 => 16, - 17..=32 => 32, - 33..=64 => 64, - 65..=128 => 128, - 129..=256 => 256, - 257..=512 => 512, - _ => panic!("Invalid dimensions for cache!"), - } - } - - - let x_size = quantize_dimension(size.width); - let y_size = quantize_dimension(size.height); - - let (width, height) = match (x_size, y_size) { - // Special cased rectangular slab pages. - (512, 0..=64) => (512, 64), - (512, 128) => (512, 128), - (512, 256) => (512, 256), - (0..=64, 512) => (64, 512), - (128, 512) => (128, 512), - (256, 512) => (256, 512), - - // If none of those fit, use a square slab size. - (x_size, y_size) => { - let square_size = cmp::max(x_size, y_size); - (square_size, square_size) - } - }; - - SlabSize { - width, - height, - } - } -} - -// The x/y location within a texture region of an allocation. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -struct TextureLocation(pub u8, pub u8); - -impl TextureLocation { - fn new(x: i32, y: i32) -> Self { - debug_assert!(x >= 0 && y >= 0 && x < 0x100 && y < 0x100); - TextureLocation(x as u8, y as u8) - } -} - -/// A region is a rectangular part of a texture cache texture, split into fixed-size slabs. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -struct TextureRegion { - index: usize, - slab_size: SlabSize, - offset: DeviceIntPoint, - free_slots: Vec<TextureLocation>, - total_slot_count: usize, -} - -impl TextureRegion { - fn new(index: usize, offset: DeviceIntPoint) -> Self { - TextureRegion { - index, - slab_size: SlabSize::invalid(), - offset, - free_slots: Vec::new(), - total_slot_count: 0, - } - } - - // Initialize a region to be an allocator for a specific slab size. - fn init(&mut self, slab_size: SlabSize, region_size: i32, empty_regions: &mut usize) { - debug_assert!(self.slab_size == SlabSize::invalid()); - debug_assert!(self.free_slots.is_empty()); - - self.slab_size = slab_size; - let slots_per_x_axis = region_size / self.slab_size.width; - let slots_per_y_axis = region_size / self.slab_size.height; - - // Add each block to a freelist. - for y in 0 .. slots_per_y_axis { - for x in 0 .. slots_per_x_axis { - self.free_slots.push(TextureLocation::new(x, y)); - } - } - - self.total_slot_count = self.free_slots.len(); - *empty_regions -= 1; - } - - // Deinit a region, allowing it to become a region with - // a different allocator size. - fn deinit(&mut self, empty_regions: &mut usize) { - self.slab_size = SlabSize::invalid(); - self.free_slots.clear(); - self.total_slot_count = 0; - *empty_regions += 1; - } - - fn is_empty(&self) -> bool { - self.slab_size == SlabSize::invalid() - } - - // Attempt to allocate a fixed size block from this region. - fn alloc(&mut self) -> Option<(DeviceIntPoint, TextureLocation)> { - debug_assert!(self.slab_size != SlabSize::invalid()); - - self.free_slots.pop().map(|location| {( - point2( - self.offset.x + self.slab_size.width * location.0 as i32, - self.offset.y + self.slab_size.height * location.1 as i32, - ), - location, - )}) - } - - // Free a block in this region. - fn free(&mut self, location: TextureLocation, empty_regions: &mut usize) { - self.free_slots.push(location); - - // If this region is completely unused, deinit it - // so that it can become a different slab size - // as required. - if self.free_slots.len() == self.total_slot_count { - self.deinit(empty_regions); - } - } -} - -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct SlabAllocatorParameters { - pub region_size: i32, -} - -/// A 2D texture divided into regions. -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct SlabAllocator { - regions: Vec<TextureRegion>, - size: i32, - region_size: i32, - empty_regions: usize, - allocated_space: i32, -} - -impl SlabAllocator { - pub fn new(size: i32, options: &SlabAllocatorParameters) -> Self { - let regions_per_row = size / options.region_size; - let num_regions = (regions_per_row * regions_per_row) as usize; - - let mut regions = Vec::with_capacity(num_regions); - - for index in 0..num_regions { - let offset = point2( - (index as i32 % regions_per_row) * options.region_size, - (index as i32 / regions_per_row) * options.region_size, - ); - - regions.push(TextureRegion::new(index, offset)); - } - - SlabAllocator { - regions, - size, - region_size: options.region_size, - empty_regions: num_regions, - allocated_space: 0, - } - } - - pub fn is_empty(&self) -> bool { - self.empty_regions == self.regions.len() - } - - pub fn allocated_space(&self) -> i32 { - self.allocated_space - } - - // Returns the region index and allocated rect. - pub fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> { - let slab_size = SlabSize::get(size); - - // Keep track of the location of an empty region, - // in case we need to select a new empty region - // after the loop. - let mut empty_region_index = None; - - let allocated_size = size2(slab_size.width, slab_size.height); - - // Run through the existing regions of this size, and see if - // we can find a free block in any of them. - for (i, region) in self.regions.iter_mut().enumerate() { - if region.is_empty() { - empty_region_index = Some(i); - } else if region.slab_size == slab_size { - if let Some((origin, location)) = region.alloc() { - return Some(( - pack_alloc_id(region.index, location), - DeviceIntRect { - origin, - size: allocated_size, - } - )); - } - } - } - - if let Some(empty_region_index) = empty_region_index { - let region = &mut self.regions[empty_region_index]; - region.init(slab_size, self.region_size, &mut self.empty_regions); - let (origin, location) = region.alloc().unwrap(); - - return Some(( - pack_alloc_id(region.index, location), - DeviceIntRect { - origin, - size: allocated_size, - }, - )) - } - - None - } - - pub fn deallocate(&mut self, id: AllocId) { - let (region_index, location) = unpack_alloc_id(id); - - let region = &mut self.regions[region_index]; - region.free(location, &mut self.empty_regions); - - self.allocated_space -= region.slab_size.width * region.slab_size.height; - } - - pub fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> { - use svg_fmt::*; - - let region_spacing = 5.0; - let text_spacing = 15.0; - let regions_per_row = (self.size / self.region_size) as usize; - let wh = rect.size().width.min(rect.size().height); - let region_wh = (wh - region_spacing) / regions_per_row as f32 - region_spacing; - - let x0 = rect.min.x; - let y0 = rect.min.y; - - for (idx, region) in self.regions.iter().enumerate() { - let slab_size = region.slab_size; - let x = x0 + (idx % regions_per_row) as f32 * (region_wh + region_spacing); - - let y = y0 + text_spacing + (idx / regions_per_row) as f32 * (region_wh + region_spacing); - - let texture_background = if region.is_empty() { rgb(30, 30, 30) } else { rgb(40, 40, 130) }; - writeln!(output, " {}", rectangle(x, y, region_wh, region_wh).inflate(1.0, 1.0).fill(rgb(10, 10, 10)))?; - writeln!(output, " {}", rectangle(x, y, region_wh, region_wh).fill(texture_background))?; - - let sw = (slab_size.width as f32 / self.region_size as f32) * region_wh; - let sh = (slab_size.height as f32 / self.region_size as f32) * region_wh; - - for slot in ®ion.free_slots { - let sx = x + slot.0 as f32 * sw; - let sy = y + slot.1 as f32 * sh; - - // Allocation slot. - writeln!(output, " {}", rectangle(sx, sy, sw, sh).inflate(-0.5, -0.5).fill(rgb(30, 30, 30)))?; - } - - if slab_size.width != 0 { - let region_text = format!("{}x{}", slab_size.width, slab_size.height); - let tx = x + 1.0; - let ty = y + region_wh - 1.0; - writeln!(output, " {}", text(tx, ty, region_text).color(rgb(230, 230, 230)))?; - } - } - - Ok(()) - } -} - -impl AtlasAllocator for SlabAllocator { - type Parameters = SlabAllocatorParameters; - - fn new(size: i32, options: &Self::Parameters) -> Self { - SlabAllocator::new(size, options) - } - - fn allocate(&mut self, size: DeviceIntSize) -> Option<(AllocId, DeviceIntRect)> { - self.allocate(size) - } - - fn deallocate(&mut self, id: AllocId) { - self.deallocate(id); - } - - fn is_empty(&self) -> bool { - self.is_empty() - } - - fn allocated_space(&self) -> i32 { - self.allocated_space() - } - - fn dump_into_svg(&self, rect: &Box2D<f32>, output: &mut dyn std::io::Write) -> std::io::Result<()> { - self.dump_into_svg(rect, output) - } -} diff --git a/third_party/webrender/webrender/src/tile_cache.rs b/third_party/webrender/webrender/src/tile_cache.rs deleted file mode 100644 index a7ec0f25cb5..00000000000 --- a/third_party/webrender/webrender/src/tile_cache.rs +++ /dev/null @@ -1,743 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use api::{ColorF, PrimitiveFlags, QualitySettings}; -use api::units::*; -use crate::clip::{ClipChainId, ClipNodeKind, ClipStore, ClipInstance}; -use crate::frame_builder::FrameBuilderConfig; -use crate::internal_types::{FastHashMap, FastHashSet}; -use crate::picture::{PrimitiveList, PictureCompositeMode, PictureOptions, PicturePrimitive, SliceId}; -use crate::picture::{Picture3DContext, TileCacheParams, TileOffset}; -use crate::prim_store::{PrimitiveInstance, PrimitiveStore, PictureIndex}; -use crate::scene_building::SliceFlags; -use crate::scene_builder_thread::Interners; -use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, SpatialTree}; -use crate::util::VecHelper; - -/* - Types and functionality related to picture caching. In future, we'll - move more and more of the existing functionality out of picture.rs - and into here. - */ - -// If the page would create too many slices (an arbitrary definition where -// it's assumed the GPU memory + compositing overhead would be too high) -// then create a single picture cache for the remaining content. This at -// least means that we can cache small content changes efficiently when -// scrolling isn't occurring. Scrolling regions will be handled reasonably -// efficiently by the dirty rect tracking (since it's likely that if the -// page has so many slices there isn't a single major scroll region). -const MAX_CACHE_SLICES: usize = 12; - -/// Created during scene building, describes how to create a tile cache for a given slice. -pub struct PendingTileCache { - /// List of primitives that are part of this slice - pub prim_list: PrimitiveList, - /// Parameters that define the tile cache (such as background color, shared clips, reference spatial node) - pub params: TileCacheParams, - /// An additional clip chain that get applied to the shared clips unconditionally for this tile cache - pub iframe_clip: Option<ClipChainId>, -} - -/// Used during scene building to construct the list of pending tile caches. -pub struct TileCacheBuilder { - /// When Some(..), a new tile cache will be created for the next primitive. - force_new_tile_cache: Option<SliceFlags>, - /// List of tile caches that have been created so far (last in the list is currently active). - pending_tile_caches: Vec<PendingTileCache>, - - /// Cache the previous scroll root search for a spatial node, since they are often the same. - prev_scroll_root_cache: (SpatialNodeIndex, SpatialNodeIndex), - /// A buffer for collecting clips for a clip-chain. Retained here to avoid memory allocations in add_prim. - prim_clips_buffer: Vec<ClipInstance>, - /// Cache the last clip-chain that was added to the shared clips as it's often the same between prims. - last_checked_clip_chain: ClipChainId, -} - -/// The output of a tile cache builder, containing all details needed to construct the -/// tile cache(s) for the next scene, and retain tiles from the previous frame when sent -/// send to the frame builder. -pub struct TileCacheConfig { - /// Mapping of slice id to the parameters needed to construct this tile cache. - pub tile_caches: FastHashMap<SliceId, TileCacheParams>, - /// A set of any spatial nodes that are attached to either a picture cache - /// root, or a clip node on the picture cache primitive. These are used - /// to detect cases where picture caching must be disabled. This is mostly - /// a temporary workaround for some existing wrench tests. I don't think - /// Gecko ever produces picture cache slices with complex transforms, so - /// in future we should prevent this in the public API and remove this hack. - pub picture_cache_spatial_nodes: FastHashSet<SpatialNodeIndex>, - /// Number of picture cache slices that were created (for profiler) - pub picture_cache_slice_count: usize, -} - -impl TileCacheConfig { - pub fn new(picture_cache_slice_count: usize) -> Self { - TileCacheConfig { - tile_caches: FastHashMap::default(), - picture_cache_spatial_nodes: FastHashSet::default(), - picture_cache_slice_count, - } - } -} - -impl TileCacheBuilder { - /// Construct a new tile cache builder. - pub fn new() -> Self { - TileCacheBuilder { - force_new_tile_cache: None, - pending_tile_caches: Vec::new(), - prev_scroll_root_cache: (ROOT_SPATIAL_NODE_INDEX, ROOT_SPATIAL_NODE_INDEX), - prim_clips_buffer: Vec::new(), - last_checked_clip_chain: ClipChainId::INVALID, - } - } - - /// Set a barrier that forces a new tile cache next time a prim is added. - pub fn add_tile_cache_barrier( - &mut self, - slice_flags: SliceFlags, - ) { - self.force_new_tile_cache = Some(slice_flags); - } - - /// Returns true if it's OK to add a container tile cache (will return false - /// if too many slices have been created). - pub fn can_add_container_tile_cache(&self) -> bool { - // See the logic and comments around MAX_CACHE_SLICES in add_prim - // to explain why < MAX_CACHE_SLICES-1 is used. - self.pending_tile_caches.len() < MAX_CACHE_SLICES-1 - } - - /// Create a new tile cache for an existing prim_list - pub fn add_tile_cache( - &mut self, - prim_list: PrimitiveList, - clip_chain_id: ClipChainId, - spatial_tree: &SpatialTree, - clip_store: &ClipStore, - interners: &Interners, - config: &FrameBuilderConfig, - iframe_clip: Option<ClipChainId>, - slice_flags: SliceFlags, - ) { - assert!(self.can_add_container_tile_cache()); - - if prim_list.is_empty() { - return; - } - - // Iterate the clusters and determine which is the most commonly occurring - // scroll root. This is a reasonable heuristic to decide which spatial node - // should be considered the scroll root of this tile cache, in order to - // minimize the invalidations that occur due to scrolling. It's often the - // case that a blend container will have only a single scroll root. - let mut scroll_root_occurrences = FastHashMap::default(); - - for cluster in &prim_list.clusters { - let scroll_root = self.find_scroll_root( - cluster.spatial_node_index, - spatial_tree, - ); - - *scroll_root_occurrences.entry(scroll_root).or_insert(0) += 1; - } - - // We can't just select the most commonly occurring scroll root in this - // primitive list. If that is a nested scroll root, there may be - // primitives in the list that are outside that scroll root, which - // can cause panics when calculating relative transforms. To ensure - // this doesn't happen, only retain scroll root candidates that are - // also ancestors of every other scroll root candidate. - let scroll_roots: Vec<SpatialNodeIndex> = scroll_root_occurrences - .keys() - .cloned() - .collect(); - - scroll_root_occurrences.retain(|parent_spatial_node_index, _| { - scroll_roots.iter().all(|child_spatial_node_index| { - parent_spatial_node_index == child_spatial_node_index || - spatial_tree.is_ancestor( - *parent_spatial_node_index, - *child_spatial_node_index, - ) - }) - }); - - // Select the scroll root by finding the most commonly occurring one - let scroll_root = scroll_root_occurrences - .iter() - .max_by_key(|entry | entry.1) - .map(|(spatial_node_index, _)| *spatial_node_index) - .unwrap_or(ROOT_SPATIAL_NODE_INDEX); - - let mut first = true; - let prim_clips_buffer = &mut self.prim_clips_buffer; - let mut shared_clips = Vec::new(); - - // Work out which clips are shared by all prim instances and can thus be applied - // at the tile cache level. In future, we aim to remove this limitation by knowing - // during initial scene build which are the relevant compositor clips, but for now - // this is unlikely to be a significant cost. - for cluster in &prim_list.clusters { - for prim_instance in &prim_list.prim_instances[cluster.prim_range()] { - if first { - add_clips( - scroll_root, - prim_instance.clip_set.clip_chain_id, - &mut shared_clips, - clip_store, - interners, - spatial_tree, - ); - - self.last_checked_clip_chain = prim_instance.clip_set.clip_chain_id; - first = false; - } else { - if self.last_checked_clip_chain != prim_instance.clip_set.clip_chain_id { - prim_clips_buffer.clear(); - - add_clips( - scroll_root, - prim_instance.clip_set.clip_chain_id, - prim_clips_buffer, - clip_store, - interners, - spatial_tree, - ); - - shared_clips.retain(|h1: &ClipInstance| { - let uid = h1.handle.uid(); - prim_clips_buffer.iter().any(|h2| { - uid == h2.handle.uid() && - h1.spatial_node_index == h2.spatial_node_index - }) - }); - - self.last_checked_clip_chain = prim_instance.clip_set.clip_chain_id; - } - } - } - } - - // If a blend-container has any clips on the stacking context we are removing, - // we need to ensure those clips are added to the shared clips applied to the - // tile cache we are creating. - let mut current_clip_chain_id = clip_chain_id; - while current_clip_chain_id != ClipChainId::NONE { - let clip_chain_node = &clip_store - .clip_chain_nodes[current_clip_chain_id.0 as usize]; - - let clip_node_data = &interners.clip[clip_chain_node.handle]; - if let ClipNodeKind::Rectangle = clip_node_data.clip_node_kind { - shared_clips.push(ClipInstance::new(clip_chain_node.handle, clip_chain_node.spatial_node_index)); - } - - current_clip_chain_id = clip_chain_node.parent_clip_chain_id; - } - - // Construct the new tile cache and add to the list to be built - let slice = self.pending_tile_caches.len(); - - let params = TileCacheParams { - slice, - slice_flags, - spatial_node_index: scroll_root, - background_color: None, - shared_clips, - shared_clip_chain: ClipChainId::NONE, - virtual_surface_size: config.compositor_kind.get_virtual_surface_size(), - compositor_surface_count: prim_list.compositor_surface_count, - }; - - self.pending_tile_caches.push(PendingTileCache { - prim_list, - params, - iframe_clip, - }); - - // Add a tile cache barrier so that the next prim definitely gets added to a - // new tile cache, even if it's otherwise compatible with the blend container. - self.force_new_tile_cache = Some(SliceFlags::empty()); - } - - /// Add a primitive, either to the current tile cache, or a new one, depending on various conditions. - pub fn add_prim( - &mut self, - prim_instance: PrimitiveInstance, - prim_rect: LayoutRect, - spatial_node_index: SpatialNodeIndex, - prim_flags: PrimitiveFlags, - spatial_tree: &SpatialTree, - clip_store: &ClipStore, - interners: &Interners, - config: &FrameBuilderConfig, - quality_settings: &QualitySettings, - iframe_clip: Option<ClipChainId>, - ) { - // Check if we want to create a new slice based on the current / next scroll root - let scroll_root = self.find_scroll_root(spatial_node_index, spatial_tree); - - // Also create a new slice if there was a barrier previously set - let mut want_new_tile_cache = - self.force_new_tile_cache.is_some() || - self.pending_tile_caches.is_empty(); - - let current_scroll_root = self.pending_tile_caches - .last() - .map(|p| p.params.spatial_node_index); - - if let Some(current_scroll_root) = current_scroll_root { - want_new_tile_cache |= match (current_scroll_root, scroll_root) { - (ROOT_SPATIAL_NODE_INDEX, ROOT_SPATIAL_NODE_INDEX) => { - // Both current slice and this cluster are fixed position, no need to cut - false - } - (ROOT_SPATIAL_NODE_INDEX, _) => { - // A real scroll root is being established, so create a cache slice - true - } - (_, ROOT_SPATIAL_NODE_INDEX) => { - // If quality settings force subpixel AA over performance, skip creating - // a slice for the fixed position element(s) here. - if quality_settings.force_subpixel_aa_where_possible { - false - } else { - // A fixed position slice is encountered within a scroll root. Only create - // a slice in this case if all the clips referenced by this cluster are also - // fixed position. There's no real point in creating slices for these cases, - // since we'll have to rasterize them as the scrolling clip moves anyway. It - // also allows us to retain subpixel AA in these cases. For these types of - // slices, the intra-slice dirty rect handling typically works quite well - // (a common case is parallax scrolling effects). - let mut create_slice = true; - let mut current_clip_chain_id = prim_instance.clip_set.clip_chain_id; - - while current_clip_chain_id != ClipChainId::NONE { - let clip_chain_node = &clip_store.clip_chain_nodes[current_clip_chain_id.0 as usize]; - let spatial_root = self.find_scroll_root(clip_chain_node.spatial_node_index, spatial_tree); - if spatial_root != ROOT_SPATIAL_NODE_INDEX { - create_slice = false; - break; - } - current_clip_chain_id = clip_chain_node.parent_clip_chain_id; - } - - create_slice - } - } - (curr_scroll_root, scroll_root) => { - // Two scrolling roots - only need a new slice if they differ - curr_scroll_root != scroll_root - } - }; - - // Update the list of clips that apply to this primitive instance, to track which are the - // shared clips for this tile cache that can be applied during compositing. - if self.last_checked_clip_chain != prim_instance.clip_set.clip_chain_id { - let prim_clips_buffer = &mut self.prim_clips_buffer; - prim_clips_buffer.clear(); - add_clips( - current_scroll_root, - prim_instance.clip_set.clip_chain_id, - prim_clips_buffer, - clip_store, - interners, - spatial_tree, - ); - - let current_shared_clips = &self.pending_tile_caches - .last() - .unwrap() - .params - .shared_clips; - - // If the shared clips are not compatible, create a new slice. - // TODO(gw): Does Gecko ever supply duplicate or out-of-order - // shared clips? It doesn't seem to, but if it does, - // we will need to be more clever here to check if - // the shared clips are compatible. - want_new_tile_cache |= current_shared_clips != prim_clips_buffer; - - self.last_checked_clip_chain = prim_instance.clip_set.clip_chain_id; - } - } - - if want_new_tile_cache { - let slice = self.pending_tile_caches.len(); - - // If we have exceeded the maximum number of slices, skip creating a new - // one and the primitive will be added to the last slice. - if slice < MAX_CACHE_SLICES { - // When we reach the last valid slice that can be created, it is created as - // a fixed slice without shared clips, ensuring that we can safely add any - // subsequent primitives to it. This doesn't seem to occur on any real - // world content (only contrived test cases), where this acts as a fail safe - // to ensure we don't allocate too much GPU memory for surface caches. - // However, if we _do_ ever see this occur on real world content, we could - // probably consider increasing the max cache slices a bit more than the - // current limit. - let (params, iframe_clip) = if slice == MAX_CACHE_SLICES-1 { - let params = TileCacheParams { - slice, - slice_flags: SliceFlags::empty(), - spatial_node_index: ROOT_SPATIAL_NODE_INDEX, - background_color: None, - shared_clips: Vec::new(), - shared_clip_chain: ClipChainId::NONE, - virtual_surface_size: config.compositor_kind.get_virtual_surface_size(), - compositor_surface_count: 0, - }; - - (params, None) - } else { - let slice_flags = self.force_new_tile_cache.unwrap_or(SliceFlags::empty()); - - let background_color = if slice == 0 { - config.background_color - } else { - None - }; - - let mut shared_clips = Vec::new(); - add_clips( - scroll_root, - prim_instance.clip_set.clip_chain_id, - &mut shared_clips, - clip_store, - interners, - spatial_tree, - ); - - self.last_checked_clip_chain = prim_instance.clip_set.clip_chain_id; - - let params = TileCacheParams { - slice, - slice_flags, - spatial_node_index: scroll_root, - background_color, - shared_clips, - shared_clip_chain: ClipChainId::NONE, - virtual_surface_size: config.compositor_kind.get_virtual_surface_size(), - compositor_surface_count: 0, - }; - - (params, iframe_clip) - }; - - self.pending_tile_caches.push(PendingTileCache { - prim_list: PrimitiveList::empty(), - params, - iframe_clip, - }); - - self.force_new_tile_cache = None; - } - } - - self.pending_tile_caches - .last_mut() - .unwrap() - .prim_list - .add_prim( - prim_instance, - prim_rect, - spatial_node_index, - prim_flags, - ); - } - - /// Consume this object and build the list of tile cache primitives - pub fn build( - self, - config: &FrameBuilderConfig, - clip_store: &mut ClipStore, - prim_store: &mut PrimitiveStore, - interners: &Interners, - ) -> (TileCacheConfig, Vec<PictureIndex>) { - let mut result = TileCacheConfig::new(self.pending_tile_caches.len()); - let mut tile_cache_pictures = Vec::new(); - - for mut pending_tile_cache in self.pending_tile_caches { - // Accumulate any clip instances from the iframe_clip into the shared clips - // that will be applied by this tile cache during compositing. - if let Some(clip_chain_id) = pending_tile_cache.iframe_clip { - add_all_rect_clips( - clip_chain_id, - &mut pending_tile_cache.params.shared_clips, - clip_store, - interners, - ); - } - - let pic_index = create_tile_cache( - pending_tile_cache.params.slice, - pending_tile_cache.params.slice_flags, - pending_tile_cache.params.spatial_node_index, - pending_tile_cache.prim_list, - pending_tile_cache.params.background_color, - pending_tile_cache.params.shared_clips, - prim_store, - clip_store, - &mut result.picture_cache_spatial_nodes, - config, - &mut result.tile_caches, - ); - - tile_cache_pictures.push(pic_index); - } - - (result, tile_cache_pictures) - } - - /// Find the scroll root for a given spatial node - fn find_scroll_root( - &mut self, - spatial_node_index: SpatialNodeIndex, - spatial_tree: &SpatialTree, - ) -> SpatialNodeIndex { - if self.prev_scroll_root_cache.0 == spatial_node_index { - return self.prev_scroll_root_cache.1; - } - - let scroll_root = spatial_tree.find_scroll_root(spatial_node_index); - self.prev_scroll_root_cache = (spatial_node_index, scroll_root); - - scroll_root - } -} - -// Helper fn to collect clip handles from a given clip chain. -fn add_clips( - scroll_root: SpatialNodeIndex, - clip_chain_id: ClipChainId, - prim_clips: &mut Vec<ClipInstance>, - clip_store: &ClipStore, - interners: &Interners, - spatial_tree: &SpatialTree, -) { - let mut current_clip_chain_id = clip_chain_id; - - while current_clip_chain_id != ClipChainId::NONE { - let clip_chain_node = &clip_store - .clip_chain_nodes[current_clip_chain_id.0 as usize]; - - let clip_node_data = &interners.clip[clip_chain_node.handle]; - if let ClipNodeKind::Rectangle = clip_node_data.clip_node_kind { - if spatial_tree.is_ancestor( - clip_chain_node.spatial_node_index, - scroll_root, - ) { - prim_clips.push(ClipInstance::new(clip_chain_node.handle, clip_chain_node.spatial_node_index)); - } - } - - current_clip_chain_id = clip_chain_node.parent_clip_chain_id; - } -} - -// Walk a clip-chain, and accumulate all clip instances into supplied `prim_clips` array. -fn add_all_rect_clips( - clip_chain_id: ClipChainId, - prim_clips: &mut Vec<ClipInstance>, - clip_store: &ClipStore, - interners: &Interners, -) { - let mut current_clip_chain_id = clip_chain_id; - - while current_clip_chain_id != ClipChainId::NONE { - let clip_chain_node = &clip_store - .clip_chain_nodes[current_clip_chain_id.0 as usize]; - - let clip_node_data = &interners.clip[clip_chain_node.handle]; - if let ClipNodeKind::Rectangle = clip_node_data.clip_node_kind { - prim_clips.push(ClipInstance::new(clip_chain_node.handle, clip_chain_node.spatial_node_index)); - } - - current_clip_chain_id = clip_chain_node.parent_clip_chain_id; - } -} - -/// Given a PrimitiveList and scroll root, construct a tile cache primitive instance -/// that wraps the primitive list. -fn create_tile_cache( - slice: usize, - slice_flags: SliceFlags, - scroll_root: SpatialNodeIndex, - prim_list: PrimitiveList, - background_color: Option<ColorF>, - shared_clips: Vec<ClipInstance>, - prim_store: &mut PrimitiveStore, - clip_store: &mut ClipStore, - picture_cache_spatial_nodes: &mut FastHashSet<SpatialNodeIndex>, - frame_builder_config: &FrameBuilderConfig, - tile_caches: &mut FastHashMap<SliceId, TileCacheParams>, -) -> PictureIndex { - // Add this spatial node to the list to check for complex transforms - // at the start of a frame build. - picture_cache_spatial_nodes.insert(scroll_root); - - // Build a clip-chain for the tile cache, that contains any of the shared clips - // we will apply when drawing the tiles. In all cases provided by Gecko, these - // are rectangle clips with a scale/offset transform only, and get handled as - // a simple local clip rect in the vertex shader. However, this should in theory - // also work with any complex clips, such as rounded rects and image masks, by - // producing a clip mask that is applied to the picture cache tiles. - - let mut parent_clip_chain_id = ClipChainId::NONE; - for clip_instance in &shared_clips { - // Add this spatial node to the list to check for complex transforms - // at the start of a frame build. - picture_cache_spatial_nodes.insert(clip_instance.spatial_node_index); - - parent_clip_chain_id = clip_store.add_clip_chain_node( - clip_instance.handle, - clip_instance.spatial_node_index, - parent_clip_chain_id, - ); - } - - let slice_id = SliceId::new(slice); - - // Store some information about the picture cache slice. This is used when we swap the - // new scene into the frame builder to either reuse existing slices, or create new ones. - tile_caches.insert(slice_id, TileCacheParams { - slice, - slice_flags, - spatial_node_index: scroll_root, - background_color, - shared_clips, - shared_clip_chain: parent_clip_chain_id, - virtual_surface_size: frame_builder_config.compositor_kind.get_virtual_surface_size(), - compositor_surface_count: prim_list.compositor_surface_count, - }); - - let pic_index = prim_store.pictures.alloc().init(PicturePrimitive::new_image( - Some(PictureCompositeMode::TileCache { slice_id }), - Picture3DContext::Out, - true, - PrimitiveFlags::IS_BACKFACE_VISIBLE, - prim_list, - scroll_root, - PictureOptions::default(), - )); - - PictureIndex(pic_index) -} - -/// Debug information about a set of picture cache slices, exposed via RenderResults -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct PictureCacheDebugInfo { - pub slices: FastHashMap<usize, SliceDebugInfo>, -} - -impl PictureCacheDebugInfo { - pub fn new() -> Self { - PictureCacheDebugInfo { - slices: FastHashMap::default(), - } - } - - /// Convenience method to retrieve a given slice. Deliberately panics - /// if the slice isn't present. - pub fn slice(&self, slice: usize) -> &SliceDebugInfo { - &self.slices[&slice] - } -} - -impl Default for PictureCacheDebugInfo { - fn default() -> PictureCacheDebugInfo { - PictureCacheDebugInfo::new() - } -} - -/// Debug information about a set of picture cache tiles, exposed via RenderResults -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct SliceDebugInfo { - pub tiles: FastHashMap<TileOffset, TileDebugInfo>, -} - -impl SliceDebugInfo { - pub fn new() -> Self { - SliceDebugInfo { - tiles: FastHashMap::default(), - } - } - - /// Convenience method to retrieve a given tile. Deliberately panics - /// if the tile isn't present. - pub fn tile(&self, x: i32, y: i32) -> &TileDebugInfo { - &self.tiles[&TileOffset::new(x, y)] - } -} - -/// Debug information about a tile that was dirty and was rasterized -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct DirtyTileDebugInfo { - pub local_valid_rect: PictureRect, - pub local_dirty_rect: PictureRect, -} - -/// Debug information about the state of a tile -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "capture", derive(Serialize))] -#[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum TileDebugInfo { - /// Tile was occluded by a tile in front of it - Occluded, - /// Tile was culled (not visible in current display port) - Culled, - /// Tile was valid (no rasterization was done) and visible - Valid, - /// Tile was dirty, and was updated - Dirty(DirtyTileDebugInfo), -} - -impl TileDebugInfo { - pub fn is_occluded(&self) -> bool { - match self { - TileDebugInfo::Occluded => true, - TileDebugInfo::Culled | - TileDebugInfo::Valid | - TileDebugInfo::Dirty(..) => false, - } - } - - pub fn is_valid(&self) -> bool { - match self { - TileDebugInfo::Valid => true, - TileDebugInfo::Culled | - TileDebugInfo::Occluded | - TileDebugInfo::Dirty(..) => false, - } - } - - pub fn is_culled(&self) -> bool { - match self { - TileDebugInfo::Culled => true, - TileDebugInfo::Valid | - TileDebugInfo::Occluded | - TileDebugInfo::Dirty(..) => false, - } - } - - pub fn as_dirty(&self) -> &DirtyTileDebugInfo { - match self { - TileDebugInfo::Occluded | - TileDebugInfo::Culled | - TileDebugInfo::Valid => { - panic!("not a dirty tile!"); - } - TileDebugInfo::Dirty(ref info) => { - info - } - } - } -} diff --git a/third_party/webrender/webrender/src/util.rs b/third_party/webrender/webrender/src/util.rs index 0d940d38934..c6a10386432 100644 --- a/third_party/webrender/webrender/src/util.rs +++ b/third_party/webrender/webrender/src/util.rs @@ -4,7 +4,7 @@ use api::BorderRadius; use api::units::*; -use euclid::{Point2D, Rect, Size2D, Vector2D, point2, size2}; +use euclid::{Point2D, Rect, Size2D, Vector2D}; use euclid::{default, Transform2D, Transform3D, Scale}; use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps}; use plane_split::{Clipper, Polygon}; @@ -66,9 +66,6 @@ pub trait VecHelper<T> { /// Equivalent to `mem::replace(&mut vec, Vec::new())` fn take(&mut self) -> Self; - /// Call clear and return self (useful for chaining with calls that move the vector). - fn cleared(self) -> Self; - /// Functionally equivalent to `mem::replace(&mut vec, Vec::new())` but tries /// to keep the allocation in the caller if it is empty or replace it with a /// pre-allocated vector. @@ -102,12 +99,6 @@ impl<T> VecHelper<T> for Vec<T> { replace(self, Vec::new()) } - fn cleared(mut self) -> Self { - self.clear(); - - self - } - fn take_and_preallocate(&mut self) -> Self { let len = self.len(); if len == 0 { @@ -150,12 +141,14 @@ impl ScaleOffset { // To check that we have a pure scale / translation: // Every field must match an identity matrix, except: // - Any value present in tx,ty - // - Any value present in sx,sy + // - Any non-neg value present in sx,sy (avoid negative for reflection/rotation) - if m.m12.abs() > NEARLY_ZERO || + if m.m11 < 0.0 || + m.m12.abs() > NEARLY_ZERO || m.m13.abs() > NEARLY_ZERO || m.m14.abs() > NEARLY_ZERO || m.m21.abs() > NEARLY_ZERO || + m.m22 < 0.0 || m.m23.abs() > NEARLY_ZERO || m.m24.abs() > NEARLY_ZERO || m.m31.abs() > NEARLY_ZERO || @@ -228,80 +221,28 @@ impl ScaleOffset { } pub fn map_rect<F, T>(&self, rect: &Rect<f32, F>) -> Rect<f32, T> { - // TODO(gw): The logic below can return an unexpected result if the supplied - // rect is invalid (has size < 0). Since Gecko currently supplied - // invalid rects in some cases, adding a max(0) here ensures that - // mapping an invalid rect retains the property that rect.is_empty() - // will return true (the mapped rect output will have size 0 instead - // of a negative size). In future we could catch / assert / fix - // these invalid rects earlier, and assert here instead. - - let w = rect.size.width.max(0.0); - let h = rect.size.height.max(0.0); - - let mut x0 = rect.origin.x * self.scale.x + self.offset.x; - let mut y0 = rect.origin.y * self.scale.y + self.offset.y; - - let mut sx = w * self.scale.x; - let mut sy = h * self.scale.y; - - // Handle negative scale. Previously, branchless float math was used to find the - // min / max vertices and size. However, that sequence of operations was producind - // additional floating point accuracy on android emulator builds, causing one test - // to fail an assert. Instead, we retain the same math as previously, and adjust - // the origin / size if required. - - if self.scale.x < 0.0 { - x0 += sx; - sx = -sx; - } - if self.scale.y < 0.0 { - y0 += sy; - sy = -sy; - } - Rect::new( - Point2D::new(x0, y0), - Size2D::new(sx, sy), + Point2D::new( + rect.origin.x * self.scale.x + self.offset.x, + rect.origin.y * self.scale.y + self.offset.y, + ), + Size2D::new( + rect.size.width * self.scale.x, + rect.size.height * self.scale.y, + ) ) } pub fn unmap_rect<F, T>(&self, rect: &Rect<f32, F>) -> Rect<f32, T> { - // TODO(gw): The logic below can return an unexpected result if the supplied - // rect is invalid (has size < 0). Since Gecko currently supplied - // invalid rects in some cases, adding a max(0) here ensures that - // mapping an invalid rect retains the property that rect.is_empty() - // will return true (the mapped rect output will have size 0 instead - // of a negative size). In future we could catch / assert / fix - // these invalid rects earlier, and assert here instead. - - let w = rect.size.width.max(0.0); - let h = rect.size.height.max(0.0); - - let mut x0 = (rect.origin.x - self.offset.x) / self.scale.x; - let mut y0 = (rect.origin.y - self.offset.y) / self.scale.y; - - let mut sx = w / self.scale.x; - let mut sy = h / self.scale.y; - - // Handle negative scale. Previously, branchless float math was used to find the - // min / max vertices and size. However, that sequence of operations was producind - // additional floating point accuracy on android emulator builds, causing one test - // to fail an assert. Instead, we retain the same math as previously, and adjust - // the origin / size if required. - - if self.scale.x < 0.0 { - x0 += sx; - sx = -sx; - } - if self.scale.y < 0.0 { - y0 += sy; - sy = -sy; - } - Rect::new( - Point2D::new(x0, y0), - Size2D::new(sx, sy), + Point2D::new( + (rect.origin.x - self.offset.x) / self.scale.x, + (rect.origin.y - self.offset.y) / self.scale.y, + ), + Size2D::new( + rect.size.width / self.scale.x, + rect.size.height / self.scale.y, + ) ) } @@ -383,8 +324,6 @@ pub trait MatrixHelpers<Src, Dst> { /// Turn Z transformation into identity. This is useful when crossing "flat" /// transform styled stacking contexts upon traversing the coordinate systems. fn flatten_z_output(&mut self); - - fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst>; } impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> { @@ -435,21 +374,14 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> { self.m21 * self.m21 + self.m22 * self.m22 > limit2 } - /// Find out a point in `Src` that would be projected into the `target`. fn inverse_project(&self, target: &Point2D<f32, Dst>) -> Option<Point2D<f32, Src>> { - // form the linear equation for the hyperplane intersection - let m = Transform2D::<f32, Src, Dst>::new( + let m: Transform2D<f32, Src, Dst>; + m = Transform2D::new( self.m11 - target.x * self.m14, self.m12 - target.y * self.m14, self.m21 - target.x * self.m24, self.m22 - target.y * self.m24, self.m41 - target.x * self.m44, self.m42 - target.y * self.m44, ); - let inv = m.inverse()?; - // we found the point, now check if it maps to the positive hemisphere - if inv.m31 * self.m14 + inv.m32 * self.m24 + self.m44 > 0.0 { - Some(Point2D::new(inv.m31, inv.m32)) - } else { - None - } + m.inverse().map(|inv| Point2D::new(inv.m31, inv.m32)) } fn inverse_rect_footprint(&self, rect: &Rect<f32, Dst>) -> Option<Rect<f32, Src>> { @@ -499,7 +431,7 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> { * a b 0 1 */ fn is_2d_scale_translation(&self) -> bool { - (self.m33 - 1.0).abs() < NEARLY_ZERO && + (self.m33 - 1.0).abs() < NEARLY_ZERO && (self.m44 - 1.0).abs() < NEARLY_ZERO && self.m12.abs() < NEARLY_ZERO && self.m13.abs() < NEARLY_ZERO && self.m14.abs() < NEARLY_ZERO && self.m21.abs() < NEARLY_ZERO && self.m23.abs() < NEARLY_ZERO && self.m24.abs() < NEARLY_ZERO && @@ -527,16 +459,6 @@ impl<Src, Dst> MatrixHelpers<Src, Dst> for Transform3D<f32, Src, Dst> { self.m23 = 0.0; self.m33 = 1.0; self.m43 = 0.0; - //Note: we used to zero out m3? as well, see "reftests/flatten-all-flat.yaml" test - } - - fn cast_unit<NewSrc, NewDst>(&self) -> Transform3D<f32, NewSrc, NewDst> { - Transform3D::new( - self.m11, self.m12, self.m13, self.m14, - self.m21, self.m22, self.m23, self.m24, - self.m31, self.m32, self.m33, self.m34, - self.m41, self.m42, self.m43, self.m44, - ) } } @@ -561,6 +483,7 @@ where Self: Sized, { fn from_floats(x0: f32, y0: f32, x1: f32, y1: f32) -> Self; + fn is_well_formed_and_nonempty(&self) -> bool; fn snap(&self) -> Self; } @@ -572,6 +495,10 @@ impl<U> RectHelpers<U> for Rect<f32, U> { ) } + fn is_well_formed_and_nonempty(&self) -> bool { + self.size.width > 0.0 && self.size.height > 0.0 + } + fn snap(&self) -> Self { let origin = Point2D::new( (self.origin.x + 0.5).floor(), @@ -664,12 +591,9 @@ use euclid::vec3; #[cfg(test)] pub mod test { use super::*; - use euclid::default::{Point2D, Rect, Size2D, Transform3D}; - use euclid::{Angle, approxeq::ApproxEq}; + use euclid::default::{Point2D, Transform3D}; + use euclid::Angle; use std::f32::consts::PI; - use crate::clip::{is_left_of_line, polygon_contains_point}; - use crate::prim_store::PolygonKey; - use api::FillRule; #[test] fn inverse_project() { @@ -682,50 +606,6 @@ pub mod test { assert_eq!(m1.inverse_project(&p0), Some(Point2D::new(2.0, 2.0))); } - #[test] - fn inverse_project_footprint() { - let m = Transform3D::new( - 0.477499992, 0.135000005, -1.0, 0.000624999986, - -0.642787635, 0.766044438, 0.0, 0.0, - 0.766044438, 0.642787635, 0.0, 0.0, - 1137.10986, 113.71286, 402.0, 0.748749971, - ); - let r = Rect::new(Point2D::zero(), Size2D::new(804.0, 804.0)); - { - let points = &[ - r.origin, - r.top_right(), - r.bottom_left(), - r.bottom_right(), - ]; - let mi = m.inverse().unwrap(); - // In this section, we do the forward and backward transformation - // to confirm that its bijective. - // We also do the inverse projection path, and confirm it functions the same way. - println!("Points:"); - for p in points { - let pp = m.transform_point2d_homogeneous(*p); - let p3 = pp.to_point3d().unwrap(); - let pi = mi.transform_point3d_homogeneous(p3); - let px = pi.to_point2d().unwrap(); - let py = m.inverse_project(&pp.to_point2d().unwrap()).unwrap(); - println!("\t{:?} -> {:?} -> {:?} -> ({:?} -> {:?}, {:?})", p, pp, p3, pi, px, py); - assert!(px.approx_eq_eps(p, &Point2D::new(0.001, 0.001))); - assert!(py.approx_eq_eps(p, &Point2D::new(0.001, 0.001))); - } - } - // project - let rp = project_rect(&m, &r, &Rect::new(Point2D::zero(), Size2D::new(1000.0, 1000.0))).unwrap(); - println!("Projected {:?}", rp); - // one of the points ends up in the negative hemisphere - assert_eq!(m.inverse_project(&rp.origin), None); - // inverse - if let Some(ri) = m.inverse_rect_footprint(&rp) { - // inverse footprint should be larger, since it doesn't know the original Z - assert!(ri.contains_rect(&r), "Inverse {:?}", ri); - } - } - fn validate_convert(xref: &LayoutTransform) { let so = ScaleOffset::from_transform(xref).unwrap(); let xf = so.to_transform(); @@ -733,35 +613,6 @@ pub mod test { } #[test] - fn negative_scale_map_unmap() { - let xref = LayoutTransform::scale(1.0, -1.0, 1.0) - .pre_translate(LayoutVector3D::new(124.0, 38.0, 0.0)); - let so = ScaleOffset::from_transform(&xref).unwrap(); - let local_rect = LayoutRect::new( - LayoutPoint::new(50.0, -100.0), - LayoutSize::new(200.0, 400.0), - ); - - let mapped_rect: LayoutRect = so.map_rect(&local_rect); - let xf_rect = project_rect( - &xref, - &local_rect, - &LayoutRect::max_rect(), - ).unwrap(); - - assert!(mapped_rect.origin.x.approx_eq(&xf_rect.origin.x)); - assert!(mapped_rect.origin.y.approx_eq(&xf_rect.origin.y)); - assert!(mapped_rect.size.width.approx_eq(&xf_rect.size.width)); - assert!(mapped_rect.size.height.approx_eq(&xf_rect.size.height)); - - let unmapped_rect: LayoutRect = so.unmap_rect(&mapped_rect); - assert!(unmapped_rect.origin.x.approx_eq(&local_rect.origin.x)); - assert!(unmapped_rect.origin.y.approx_eq(&local_rect.origin.y)); - assert!(unmapped_rect.size.width.approx_eq(&local_rect.size.width)); - assert!(unmapped_rect.size.height.approx_eq(&local_rect.size.height)); - } - - #[test] fn scale_offset_convert() { let xref = LayoutTransform::translation(130.0, 200.0, 0.0); validate_convert(&xref); @@ -841,84 +692,6 @@ pub mod test { assert_eq!(origin, Point2D::new(1.0, 0.5)); assert_eq!(m.transform_point2d(origin), Some(Point2D::zero())); } - - #[test] - fn polygon_clip_is_left_of_point() { - // Define points of a line through (1, -3) and (-2, 6) to test against. - // If the triplet consisting of these two points and the test point - // form a counter-clockwise triangle, then the test point is on the - // left. The easiest way to visualize this is with an "ascending" - // line from low-Y to high-Y. - let p0_x = 1.0; - let p0_y = -3.0; - let p1_x = -2.0; - let p1_y = 6.0; - - // Test some points to the left of the line. - assert!(is_left_of_line(-9.0, 0.0, p0_x, p0_y, p1_x, p1_y) > 0.0); - assert!(is_left_of_line(-1.0, 1.0, p0_x, p0_y, p1_x, p1_y) > 0.0); - assert!(is_left_of_line(1.0, -4.0, p0_x, p0_y, p1_x, p1_y) > 0.0); - - // Test some points on the line. - assert!(is_left_of_line(-3.0, 9.0, p0_x, p0_y, p1_x, p1_y) == 0.0); - assert!(is_left_of_line(0.0, 0.0, p0_x, p0_y, p1_x, p1_y) == 0.0); - assert!(is_left_of_line(100.0, -300.0, p0_x, p0_y, p1_x, p1_y) == 0.0); - - // Test some points to the right of the line. - assert!(is_left_of_line(0.0, 1.0, p0_x, p0_y, p1_x, p1_y) < 0.0); - assert!(is_left_of_line(-4.0, 13.0, p0_x, p0_y, p1_x, p1_y) < 0.0); - assert!(is_left_of_line(5.0, -12.0, p0_x, p0_y, p1_x, p1_y) < 0.0); - } - - #[test] - fn polygon_clip_contains_point() { - // We define the points of a self-overlapping polygon, which we will - // use to create polygons with different windings and fill rules. - let p0 = LayoutPoint::new(4.0, 4.0); - let p1 = LayoutPoint::new(6.0, 4.0); - let p2 = LayoutPoint::new(4.0, 7.0); - let p3 = LayoutPoint::new(2.0, 1.0); - let p4 = LayoutPoint::new(8.0, 1.0); - let p5 = LayoutPoint::new(6.0, 7.0); - - let poly_clockwise_nonzero = PolygonKey::new( - &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Nonzero - ); - let poly_clockwise_evenodd = PolygonKey::new( - &[p5, p4, p3, p2, p1, p0].to_vec(), FillRule::Evenodd - ); - let poly_counter_clockwise_nonzero = PolygonKey::new( - &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Nonzero - ); - let poly_counter_clockwise_evenodd = PolygonKey::new( - &[p0, p1, p2, p3, p4, p5].to_vec(), FillRule::Evenodd - ); - - // We define a rect that provides a bounding clip area of - // the polygon. - let rect = LayoutRect::new(LayoutPoint::new(0.0, 0.0), - LayoutSize::new(10.0, 10.0)); - - // And we'll test three points of interest. - let p_inside_once = LayoutPoint::new(5.0, 3.0); - let p_inside_twice = LayoutPoint::new(5.0, 5.0); - let p_outside = LayoutPoint::new(9.0, 9.0); - - // We should get the same results for both clockwise and - // counter-clockwise polygons. - // For nonzero polygons, the inside twice point is considered inside. - for poly_nonzero in vec![poly_clockwise_nonzero, poly_counter_clockwise_nonzero].iter() { - assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_nonzero), true); - assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_nonzero), true); - assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_nonzero), false); - } - // For evenodd polygons, the inside twice point is considered outside. - for poly_evenodd in vec![poly_clockwise_evenodd, poly_counter_clockwise_evenodd].iter() { - assert_eq!(polygon_contains_point(&p_inside_once, &rect, &poly_evenodd), true); - assert_eq!(polygon_contains_point(&p_inside_twice, &rect, &poly_evenodd), false); - assert_eq!(polygon_contains_point(&p_outside, &rect, &poly_evenodd), false); - } - } } pub trait MaxRect { @@ -1310,56 +1083,6 @@ impl Recycler { } } -/// Record the size of a data structure to preallocate a similar size -/// at the next frame and avoid growing it too many time. -#[derive(Copy, Clone, Debug)] -pub struct Preallocator { - size: usize, -} - -impl Preallocator { - pub fn new(initial_size: usize) -> Self { - Preallocator { - size: initial_size, - } - } - - /// Record the size of a vector to preallocate it the next frame. - pub fn record_vec<T>(&mut self, vec: &Vec<T>) { - let len = vec.len(); - if len > self.size { - self.size = len; - } else { - self.size = (self.size + len) / 2; - } - } - - /// The size that we'll preallocate the vector with. - pub fn preallocation_size(&self) -> usize { - // Round up to multiple of 16 to avoid small tiny - // variations causing reallocations. - (self.size + 15) & !15 - } - - /// Preallocate vector storage. - /// - /// The preallocated amount depends on the length recorded in the last - /// record_vec call. - pub fn preallocate_vec<T>(&self, vec: &mut Vec<T>) { - let len = vec.len(); - let cap = self.preallocation_size(); - if len < cap { - vec.reserve(cap - len); - } - } -} - -impl Default for Preallocator { - fn default() -> Self { - Self::new(0) - } -} - /// Arc wrapper to support measurement via MallocSizeOf. /// /// Memory reporting for Arcs is tricky because of the risk of double-counting. @@ -1496,201 +1219,3 @@ macro_rules! c_str { } } } - -// Find a rectangle that is contained by the sum of r1 and r2. -pub fn conservative_union_rect<U>(r1: &Rect<f32, U>, r2: &Rect<f32, U>) -> Rect<f32, U> { - // +---+---+ +--+-+--+ - // | | | | | | | - // | | | | | | | - // +---+---+ +--+-+--+ - if r1.origin.y == r2.origin.y && r1.size.height == r2.size.height { - if r2.min_x() <= r1.max_x() && r2.max_x() >= r1.min_x() { - let origin_x = f32::min(r1.origin.x, r2.origin.x); - let width = f32::max(r1.max_x(), r2.max_x()) - origin_x; - - return Rect { - origin: point2(origin_x, r1.origin.y), - size: size2(width, r1.size.height), - } - } - } - - // +----+ +----+ - // | | | | - // | | +----+ - // +----+ | | - // | | +----+ - // | | | | - // +----+ +----+ - if r1.origin.x == r2.origin.x && r1.size.width == r2.size.width { - if r2.min_y() <= r1.max_y() && r2.max_y() >= r1.min_y() { - let origin_y = f32::min(r1.origin.y, r2.origin.y); - let height = f32::max(r1.max_y(), r2.max_y()) - origin_y; - - return Rect { - origin: point2(r1.origin.x, origin_y), - size: size2(r1.size.width, height), - } - } - } - - if r1.area() >= r2.area() { *r1 } else {*r2 } -} - -#[test] -fn test_conservative_union_rect() { - // Adjacent, x axis - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(4.0, 2.0), size: size2(5.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 2.0), size: size2(8.0, 4.0) }); - - let r = conservative_union_rect( - &LayoutRect { origin: point2(4.0, 2.0), size: size2(5.0, 4.0) }, - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 2.0), size: size2(8.0, 4.0) }); - - // Averlapping adjacent, x axis - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(3.0, 2.0), size: size2(5.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 2.0), size: size2(7.0, 4.0) }); - - let r = conservative_union_rect( - &LayoutRect { origin: point2(5.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(1.0, 2.0), size: size2(5.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 2.0), size: size2(7.0, 4.0) }); - - // Adjacent but not touching, x axis - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(6.0, 2.0), size: size2(5.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(6.0, 2.0), size: size2(5.0, 4.0) }); - - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(-6.0, 2.0), size: size2(1.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }); - - - // Adjacent, y axis - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(1.0, 6.0), size: size2(3.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 8.0) }); - - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 5.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(1.0, 1.0), size: size2(3.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 1.0), size: size2(3.0, 8.0) }); - - // Averlapping adjacent, y axis - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(1.0, 3.0), size: size2(3.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 5.0) }); - - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 4.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 6.0) }); - - // Adjacent but not touching, y axis - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(1.0, 10.0), size: size2(3.0, 5.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 10.0), size: size2(3.0, 5.0) }); - - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 5.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(1.0, 0.0), size: size2(3.0, 3.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(1.0, 5.0), size: size2(3.0, 4.0) }); - - - // Contained - let r = conservative_union_rect( - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - &LayoutRect { origin: point2(0.0, 1.0), size: size2(10.0, 11.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(0.0, 1.0), size: size2(10.0, 11.0) }); - - let r = conservative_union_rect( - &LayoutRect { origin: point2(0.0, 1.0), size: size2(10.0, 11.0) }, - &LayoutRect { origin: point2(1.0, 2.0), size: size2(3.0, 4.0) }, - ); - assert_eq!(r, LayoutRect { origin: point2(0.0, 1.0), size: size2(10.0, 11.0) }); -} - -/// This is inspired by the `weak-table` crate. -/// It holds a Vec of weak pointers that are garbage collected as the Vec -pub struct WeakTable { - inner: Vec<std::sync::Weak<Vec<u8>>> -} - -impl WeakTable { - pub fn new() -> WeakTable { - WeakTable { inner: Vec::new() } - } - pub fn insert(&mut self, x: std::sync::Weak<Vec<u8>>) { - if self.inner.len() == self.inner.capacity() { - self.remove_expired(); - - // We want to make sure that we change capacity() - // even if remove_expired() removes some entries - // so that we don't repeatedly hit remove_expired() - if self.inner.len() * 3 < self.inner.capacity() { - // We use a different multiple for shrinking then - // expanding so that we we don't accidentally - // oscilate. - self.inner.shrink_to_fit(); - } else { - // Otherwise double our size - self.inner.reserve(self.inner.len()) - } - } - self.inner.push(x); - } - - fn remove_expired(&mut self) { - self.inner.retain(|x| x.strong_count() > 0) - } - - pub fn iter(&self) -> impl Iterator<Item = Arc<Vec<u8>>> + '_ { - self.inner.iter().filter_map(|x| x.upgrade()) - } -} - -#[test] -fn weak_table() { - let mut tbl = WeakTable::new(); - let mut things = Vec::new(); - let target_count = 50; - for _ in 0..target_count { - things.push(Arc::new(vec![4])); - } - for i in &things { - tbl.insert(Arc::downgrade(i)) - } - assert_eq!(tbl.inner.len(), target_count); - drop(things); - assert_eq!(tbl.iter().count(), 0); - - // make sure that we shrink the table if it gets too big - // by adding a bunch of dead items - for _ in 0..target_count*2 { - tbl.insert(Arc::downgrade(&Arc::new(vec![5]))) - } - assert!(tbl.inner.capacity() <= 4); -} diff --git a/third_party/webrender/webrender/src/visibility.rs b/third_party/webrender/webrender/src/visibility.rs deleted file mode 100644 index ffd3ef8b618..00000000000 --- a/third_party/webrender/webrender/src/visibility.rs +++ /dev/null @@ -1,713 +0,0 @@ -/* 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/. */ - -//! # Visibility pass -//! -//! TODO: document what this pass does! -//! - -use api::{ColorF, DebugFlags}; -use api::units::*; -use euclid::Scale; -use std::{usize, mem}; -use crate::batch::BatchFilter; -use crate::clip::{ClipStore, ClipChainStack}; -use crate::composite::CompositeState; -use crate::spatial_tree::{ROOT_SPATIAL_NODE_INDEX, SpatialTree, SpatialNodeIndex}; -use crate::clip::{ClipInstance, ClipChainInstance}; -use crate::debug_colors; -use crate::frame_builder::FrameBuilderConfig; -use crate::gpu_cache::GpuCache; -use crate::internal_types::FastHashMap; -use crate::picture::{PictureCompositeMode, ClusterFlags, SurfaceInfo, TileCacheInstance}; -use crate::picture::{PrimitiveList, SurfaceIndex, RasterConfig, SliceId}; -use crate::prim_store::{ClipTaskIndex, PictureIndex, PrimitiveInstanceKind}; -use crate::prim_store::{PrimitiveStore, PrimitiveInstance}; -use crate::render_backend::{DataStores, ScratchBuffer}; -use crate::resource_cache::ResourceCache; -use crate::scene::SceneProperties; -use crate::space::SpaceMapper; -use crate::internal_types::Filter; -use crate::util::{MaxRect}; - -pub struct FrameVisibilityContext<'a> { - pub spatial_tree: &'a SpatialTree, - pub global_screen_world_rect: WorldRect, - pub global_device_pixel_scale: DevicePixelScale, - pub surfaces: &'a [SurfaceInfo], - pub debug_flags: DebugFlags, - pub scene_properties: &'a SceneProperties, - pub config: FrameBuilderConfig, -} - -pub struct FrameVisibilityState<'a> { - pub clip_store: &'a mut ClipStore, - pub resource_cache: &'a mut ResourceCache, - pub gpu_cache: &'a mut GpuCache, - pub scratch: &'a mut ScratchBuffer, - pub tile_cache: Option<Box<TileCacheInstance>>, - pub data_stores: &'a mut DataStores, - pub clip_chain_stack: ClipChainStack, - pub composite_state: &'a mut CompositeState, - /// A stack of currently active off-screen surfaces during the - /// visibility frame traversal. - pub surface_stack: Vec<SurfaceIndex>, -} - -impl<'a> FrameVisibilityState<'a> { - pub fn push_surface( - &mut self, - surface_index: SurfaceIndex, - shared_clips: &[ClipInstance], - spatial_tree: &SpatialTree, - ) { - self.surface_stack.push(surface_index); - self.clip_chain_stack.push_surface(shared_clips, spatial_tree); - } - - pub fn pop_surface(&mut self) { - self.surface_stack.pop().unwrap(); - self.clip_chain_stack.pop_surface(); - } -} - -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: u8 { - /// 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; - } -} - -/// Contains the current state of the primitive's visibility. -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -pub enum VisibilityState { - /// Uninitialized - this should never be encountered after prim reset - Unset, - /// Culled for being off-screen, or not possible to render (e.g. missing image resource) - Culled, - /// A picture that doesn't have a surface - primitives are composed into the - /// parent picture with a surface. - PassThrough, - /// During picture cache dependency update, was found to be intersecting with one - /// or more visible tiles. The rect in picture cache space is stored here to allow - /// the detailed calculations below. - Coarse { - /// Information about which tile batchers this prim should be added to - filter: BatchFilter, - - /// A set of flags that define how this primitive should be handled - /// during batching of visible primitives. - vis_flags: PrimitiveVisibilityFlags, - }, - /// Once coarse visibility is resolved, this will be set if the primitive - /// intersected any dirty rects, otherwise prim will be culled. - Detailed { - /// Information about which tile batchers this prim should be added to - filter: BatchFilter, - - /// A set of flags that define how this primitive should be handled - /// during batching of visible primitives. - vis_flags: PrimitiveVisibilityFlags, - }, -} - -/// Information stored for a visible primitive about the visible -/// rect and associated clip information. -#[derive(Debug)] -#[cfg_attr(feature = "capture", derive(Serialize))] -pub struct PrimitiveVisibility { - /// The clip chain instance that was built for this primitive. - pub clip_chain: ClipChainInstance, - - /// Current visibility state of the primitive. - // TODO(gw): Move more of the fields from this struct into - // the state enum. - pub state: VisibilityState, - - /// An index into the clip task instances array in the primitive - /// store. If this is ClipTaskIndex::INVALID, then the primitive - /// has no clip mask. Otherwise, it may store the offset of the - /// global clip mask task for this primitive, or the first of - /// a list of clip task ids (one per segment). - pub clip_task_index: ClipTaskIndex, - - /// The current combined local clip for this primitive, from - /// the primitive local clip above and the current clip chain. - pub combined_local_clip_rect: LayoutRect, -} - -impl PrimitiveVisibility { - pub fn new() -> Self { - PrimitiveVisibility { - state: VisibilityState::Unset, - clip_chain: ClipChainInstance::empty(), - clip_task_index: ClipTaskIndex::INVALID, - combined_local_clip_rect: LayoutRect::zero(), - } - } - - pub fn reset(&mut self) { - self.state = VisibilityState::Culled; - self.clip_task_index = ClipTaskIndex::INVALID; - } -} - -/// Update visibility pass - update each primitive visibility struct, and -/// build the clip chain instance if appropriate. -pub fn update_primitive_visibility( - store: &mut PrimitiveStore, - pic_index: PictureIndex, - parent_surface_index: SurfaceIndex, - world_culling_rect: &WorldRect, - frame_context: &FrameVisibilityContext, - frame_state: &mut FrameVisibilityState, - tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>, - is_root_tile_cache: bool, -) -> Option<PictureRect> { - profile_scope!("update_visibility"); - let (mut prim_list, surface_index, apply_local_clip_rect, world_culling_rect, is_composite) = { - let pic = &mut store.pictures[pic_index.0]; - let mut world_culling_rect = *world_culling_rect; - - let prim_list = mem::replace(&mut pic.prim_list, PrimitiveList::empty()); - let (surface_index, is_composite) = match pic.raster_config { - Some(ref raster_config) => (raster_config.surface_index, true), - None => (parent_surface_index, false) - }; - - match pic.raster_config { - Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => { - let mut tile_cache = tile_caches - .remove(&slice_id) - .expect("bug: non-existent tile cache"); - - // If we have a tile cache for this picture, see if any of the - // relative transforms have changed, which means we need to - // re-map the dependencies of any child primitives. - world_culling_rect = tile_cache.pre_update( - layout_rect_as_picture_rect(&pic.estimated_local_rect), - surface_index, - frame_context, - frame_state, - ); - - // Push a new surface, supplying the list of clips that should be - // ignored, since they are handled by clipping when drawing this surface. - frame_state.push_surface( - surface_index, - &tile_cache.shared_clips, - frame_context.spatial_tree, - ); - frame_state.tile_cache = Some(tile_cache); - } - _ => { - if is_composite { - frame_state.push_surface( - surface_index, - &[], - frame_context.spatial_tree, - ); - } - } - } - - (prim_list, surface_index, pic.apply_local_clip_rect, world_culling_rect, is_composite) - }; - - let surface = &frame_context.surfaces[surface_index.0 as usize]; - - let mut map_local_to_surface = surface - .map_local_to_surface - .clone(); - - let map_surface_to_world = SpaceMapper::new_with_target( - ROOT_SPATIAL_NODE_INDEX, - surface.surface_spatial_node_index, - frame_context.global_screen_world_rect, - frame_context.spatial_tree, - ); - - let mut surface_rect = PictureRect::zero(); - - for cluster in &mut prim_list.clusters { - profile_scope!("cluster"); - // Get the cluster and see if is visible - if !cluster.flags.contains(ClusterFlags::IS_VISIBLE) { - // Each prim instance must have reset called each frame, to clear - // indices into various scratch buffers. If this doesn't occur, - // the primitive may incorrectly be considered visible, which can - // cause unexpected conditions to occur later during the frame. - // Primitive instances are normally reset in the main loop below, - // but we must also reset them in the rare case that the cluster - // visibility has changed (due to an invalid transform and/or - // backface visibility changing for this cluster). - // TODO(gw): This is difficult to test for in CI - as a follow up, - // we should add a debug flag that validates the prim - // instance is always reset every frame to catch similar - // issues in future. - for prim_instance in &mut prim_list.prim_instances[cluster.prim_range()] { - prim_instance.reset(); - } - continue; - } - - map_local_to_surface.set_target_spatial_node( - cluster.spatial_node_index, - frame_context.spatial_tree, - ); - - for prim_instance in &mut prim_list.prim_instances[cluster.prim_range()] { - prim_instance.reset(); - - if prim_instance.is_chased() { - #[cfg(debug_assertions)] // needed for ".id" part - println!("\tpreparing {:?} in {:?}", prim_instance.id, pic_index); - println!("\t{:?}", prim_instance.kind); - } - - let (is_passthrough, prim_local_rect, prim_shadowed_rect) = match prim_instance.kind { - PrimitiveInstanceKind::Picture { pic_index, .. } => { - let (is_visible, is_passthrough) = { - let pic = &store.pictures[pic_index.0]; - (pic.is_visible(), pic.raster_config.is_none()) - }; - - if !is_visible { - continue; - } - - if is_passthrough { - frame_state.clip_chain_stack.push_clip( - prim_instance.clip_set.clip_chain_id, - frame_state.clip_store, - ); - } - - let pic_surface_rect = update_primitive_visibility( - store, - pic_index, - surface_index, - &world_culling_rect, - frame_context, - frame_state, - tile_caches, - false, - ); - - if is_passthrough { - frame_state.clip_chain_stack.pop_clip(); - } - - let pic = &store.pictures[pic_index.0]; - - if prim_instance.is_chased() && pic.estimated_local_rect != pic.precise_local_rect { - println!("\testimate {:?} adjusted to {:?}", pic.estimated_local_rect, pic.precise_local_rect); - } - - let mut shadow_rect = pic.precise_local_rect; - match pic.raster_config { - Some(ref rc) => match rc.composite_mode { - // If we have a drop shadow filter, we also need to include the shadow in - // our shadowed local rect for the purpose of calculating the size of the - // picture. - PictureCompositeMode::Filter(Filter::DropShadows(ref shadows)) => { - for shadow in shadows { - shadow_rect = shadow_rect.union(&pic.precise_local_rect.translate(shadow.offset)); - } - } - _ => {} - } - None => { - // If the primitive does not have its own raster config, we need to - // propogate the surface rect calculation to the parent. - if let Some(ref rect) = pic_surface_rect { - surface_rect = surface_rect.union(rect); - } - } - } - - (is_passthrough, pic.precise_local_rect, shadow_rect) - } - _ => { - let prim_data = &frame_state.data_stores.as_common_data(&prim_instance); - - (false, prim_data.prim_rect, prim_data.prim_rect) - } - }; - - if is_passthrough { - // Pass through pictures are always considered visible in all dirty tiles. - prim_instance.vis.state = VisibilityState::PassThrough; - } else { - if prim_local_rect.size.width <= 0.0 || prim_local_rect.size.height <= 0.0 { - if prim_instance.is_chased() { - println!("\tculled for zero local rectangle"); - } - continue; - } - - // Inflate the local rect for this primitive by the inflation factor of - // the picture context and include the shadow offset. This ensures that - // even if the primitive itstore is not visible, any effects from the - // blur radius or shadow will be correctly taken into account. - let inflation_factor = surface.inflation_factor; - let local_rect = prim_shadowed_rect - .inflate(inflation_factor, inflation_factor) - .intersection(&prim_instance.clip_set.local_clip_rect); - let local_rect = match local_rect { - Some(local_rect) => local_rect, - None => { - if prim_instance.is_chased() { - println!("\tculled for being out of the local clip rectangle: {:?}", - prim_instance.clip_set.local_clip_rect); - } - continue; - } - }; - - // Include the clip chain for this primitive in the current stack. - frame_state.clip_chain_stack.push_clip( - prim_instance.clip_set.clip_chain_id, - frame_state.clip_store, - ); - - frame_state.clip_store.set_active_clips( - prim_instance.clip_set.local_clip_rect, - cluster.spatial_node_index, - map_local_to_surface.ref_spatial_node_index, - frame_state.clip_chain_stack.current_clips_array(), - &frame_context.spatial_tree, - &frame_state.data_stores.clip, - ); - - let clip_chain = frame_state - .clip_store - .build_clip_chain_instance( - local_rect, - &map_local_to_surface, - &map_surface_to_world, - &frame_context.spatial_tree, - frame_state.gpu_cache, - frame_state.resource_cache, - surface.device_pixel_scale, - &world_culling_rect, - &mut frame_state.data_stores.clip, - true, - prim_instance.is_chased(), - ); - - // Ensure the primitive clip is popped - frame_state.clip_chain_stack.pop_clip(); - - prim_instance.vis.clip_chain = match clip_chain { - Some(clip_chain) => clip_chain, - None => { - if prim_instance.is_chased() { - println!("\tunable to build the clip chain, skipping"); - } - continue; - } - }; - - if prim_instance.is_chased() { - println!("\teffective clip chain from {:?} {}", - prim_instance.vis.clip_chain.clips_range, - if apply_local_clip_rect { "(applied)" } else { "" }, - ); - println!("\tpicture rect {:?} @{:?}", - prim_instance.vis.clip_chain.pic_clip_rect, - prim_instance.vis.clip_chain.pic_spatial_node_index, - ); - } - - prim_instance.vis.combined_local_clip_rect = if apply_local_clip_rect { - prim_instance.vis.clip_chain.local_clip_rect - } else { - prim_instance.clip_set.local_clip_rect - }; - - if prim_instance.vis.combined_local_clip_rect.size.is_empty() { - if prim_instance.is_chased() { - println!("\tculled for zero local clip rectangle"); - } - continue; - } - - // Include the visible area for primitive, including any shadows, in - // the area affected by the surface. - match prim_instance.vis.combined_local_clip_rect.intersection(&local_rect) { - Some(visible_rect) => { - if let Some(rect) = map_local_to_surface.map(&visible_rect) { - surface_rect = surface_rect.union(&rect); - } - } - None => { - if prim_instance.is_chased() { - println!("\tculled for zero visible rectangle"); - } - continue; - } - } - - frame_state.tile_cache - .as_mut() - .unwrap() - .update_prim_dependencies( - prim_instance, - cluster.spatial_node_index, - prim_local_rect, - frame_context, - frame_state.data_stores, - frame_state.clip_store, - &store.pictures, - frame_state.resource_cache, - &store.color_bindings, - &frame_state.surface_stack, - &mut frame_state.composite_state, - &mut frame_state.gpu_cache, - is_root_tile_cache, - ); - - // Skip post visibility prim update if this primitive was culled above. - match prim_instance.vis.state { - VisibilityState::Unset => panic!("bug: invalid state"), - VisibilityState::Culled => continue, - VisibilityState::Coarse { .. } | VisibilityState::Detailed { .. } | VisibilityState::PassThrough => {} - } - - // When the debug display is enabled, paint a colored rectangle around each - // primitive. - if frame_context.debug_flags.contains(::api::DebugFlags::PRIMITIVE_DBG) { - let debug_color = match prim_instance.kind { - PrimitiveInstanceKind::Picture { .. } => ColorF::TRANSPARENT, - PrimitiveInstanceKind::TextRun { .. } => debug_colors::RED, - PrimitiveInstanceKind::LineDecoration { .. } => debug_colors::PURPLE, - PrimitiveInstanceKind::NormalBorder { .. } | - PrimitiveInstanceKind::ImageBorder { .. } => debug_colors::ORANGE, - PrimitiveInstanceKind::Rectangle { .. } => ColorF { r: 0.8, g: 0.8, b: 0.8, a: 0.5 }, - PrimitiveInstanceKind::YuvImage { .. } => debug_colors::BLUE, - PrimitiveInstanceKind::Image { .. } => debug_colors::BLUE, - PrimitiveInstanceKind::LinearGradient { .. } => debug_colors::PINK, - PrimitiveInstanceKind::CachedLinearGradient { .. } => debug_colors::PINK, - PrimitiveInstanceKind::RadialGradient { .. } => debug_colors::PINK, - PrimitiveInstanceKind::ConicGradient { .. } => debug_colors::PINK, - PrimitiveInstanceKind::Clear { .. } => debug_colors::CYAN, - PrimitiveInstanceKind::Backdrop { .. } => debug_colors::MEDIUMAQUAMARINE, - }; - if debug_color.a != 0.0 { - if let Some(rect) = calculate_prim_clipped_world_rect( - &prim_instance.vis.clip_chain.pic_clip_rect, - &world_culling_rect, - &map_surface_to_world, - ) { - let debug_rect = rect * frame_context.global_device_pixel_scale; - frame_state.scratch.primitive.push_debug_rect(debug_rect, debug_color, debug_color.scale_alpha(0.5)); - } - } - } else if frame_context.debug_flags.contains(::api::DebugFlags::OBSCURE_IMAGES) { - let is_image = matches!( - prim_instance.kind, - PrimitiveInstanceKind::Image { .. } | PrimitiveInstanceKind::YuvImage { .. } - ); - if is_image { - // We allow "small" images, since they're generally UI elements. - if let Some(rect) = calculate_prim_clipped_world_rect( - &prim_instance.vis.clip_chain.pic_clip_rect, - &world_culling_rect, - &map_surface_to_world, - ) { - let rect = rect * frame_context.global_device_pixel_scale; - if rect.size.width > 70.0 && rect.size.height > 70.0 { - frame_state.scratch.primitive.push_debug_rect(rect, debug_colors::PURPLE, debug_colors::PURPLE); - } - } - } - } - - if prim_instance.is_chased() { - println!("\tvisible with {:?}", prim_instance.vis.combined_local_clip_rect); - } - - // TODO(gw): This should probably be an instance method on PrimitiveInstance? - update_prim_post_visibility( - store, - prim_instance, - world_culling_rect, - &map_surface_to_world, - ); - } - } - } - - // Similar to above, pop either the clip chain or root entry off the current clip stack. - if is_composite { - frame_state.pop_surface(); - } - - let pic = &mut store.pictures[pic_index.0]; - pic.prim_list = prim_list; - - // If the local rect changed (due to transforms in child primitives) then - // invalidate the GPU cache location to re-upload the new local rect - // and stretch size. Drop shadow filters also depend on the local rect - // size for the extra GPU cache data handle. - // TODO(gw): In future, if we support specifying a flag which gets the - // stretch size from the segment rect in the shaders, we can - // remove this invalidation here completely. - if let Some(ref rc) = pic.raster_config { - // Inflate the local bounding rect if required by the filter effect. - if pic.options.inflate_if_required { - surface_rect = rc.composite_mode.inflate_picture_rect(surface_rect, surface.scale_factors); - } - - // Layout space for the picture is picture space from the - // perspective of its child primitives. - pic.precise_local_rect = surface_rect * Scale::new(1.0); - - // If the precise rect changed since last frame, we need to invalidate - // any segments and gpu cache handles for drop-shadows. - // TODO(gw): Requiring storage of the `prev_precise_local_rect` here - // is a total hack. It's required because `prev_precise_local_rect` - // gets written to twice (during initial vis pass and also during - // prepare pass). The proper longer term fix for this is to make - // use of the conservative picture rect for segmenting (which should - // be done during scene building). - if pic.precise_local_rect != pic.prev_precise_local_rect { - match rc.composite_mode { - PictureCompositeMode::Filter(Filter::DropShadows(..)) => { - for handle in &pic.extra_gpu_data_handles { - frame_state.gpu_cache.invalidate(handle); - } - } - _ => {} - } - // Invalidate any segments built for this picture, since the local - // rect has changed. - pic.segments_are_valid = false; - pic.prev_precise_local_rect = pic.precise_local_rect; - } - - if let PictureCompositeMode::TileCache { .. } = rc.composite_mode { - let mut tile_cache = frame_state.tile_cache.take().unwrap(); - - // Build the dirty region(s) for this tile cache. - tile_cache.post_update( - frame_context, - frame_state, - ); - - tile_caches.insert(SliceId::new(tile_cache.slice), tile_cache); - } - - None - } else { - let parent_surface = &frame_context.surfaces[parent_surface_index.0 as usize]; - let map_surface_to_parent_surface = SpaceMapper::new_with_target( - parent_surface.surface_spatial_node_index, - surface.surface_spatial_node_index, - PictureRect::max_rect(), - frame_context.spatial_tree, - ); - map_surface_to_parent_surface.map(&surface_rect) - } -} - - -fn update_prim_post_visibility( - store: &mut PrimitiveStore, - prim_instance: &mut PrimitiveInstance, - world_culling_rect: WorldRect, - map_surface_to_world: &SpaceMapper<PicturePixel, WorldPixel>, -) { - profile_scope!("update_prim_post_visibility"); - match prim_instance.kind { - PrimitiveInstanceKind::Picture { pic_index, .. } => { - let pic = &mut store.pictures[pic_index.0]; - // If this picture has a surface, determine the clipped bounding rect for it to - // minimize the size of the render target that is required. - if let Some(ref mut raster_config) = pic.raster_config { - raster_config.clipped_bounding_rect = map_surface_to_world - .map(&prim_instance.vis.clip_chain.pic_clip_rect) - .and_then(|rect| { - rect.intersection(&world_culling_rect) - }) - .unwrap_or(WorldRect::zero()); - } - } - PrimitiveInstanceKind::TextRun { .. } => { - // Text runs can't request resources early here, as we don't - // know until TileCache::post_update() whether we are drawing - // on an opaque surface. - // TODO(gw): We might be able to detect simple cases of this earlier, - // during the picture traversal. But it's probably not worth it? - } - _ => {} - } -} - -pub fn compute_conservative_visible_rect( - clip_chain: &ClipChainInstance, - world_culling_rect: WorldRect, - prim_spatial_node_index: SpatialNodeIndex, - spatial_tree: &SpatialTree, -) -> LayoutRect { - // Mapping from picture space -> world space - let map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel> = SpaceMapper::new_with_target( - ROOT_SPATIAL_NODE_INDEX, - clip_chain.pic_spatial_node_index, - world_culling_rect, - spatial_tree, - ); - - // Mapping from local space -> picture space - let map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel> = SpaceMapper::new_with_target( - clip_chain.pic_spatial_node_index, - prim_spatial_node_index, - PictureRect::max_rect(), - spatial_tree, - ); - - // Unmap the world culling rect from world -> picture space. If this mapping fails due - // to matrix weirdness, best we can do is use the clip chain's local clip rect. - let pic_culling_rect = match map_pic_to_world.unmap(&world_culling_rect) { - Some(rect) => rect, - None => return clip_chain.local_clip_rect, - }; - - // Intersect the unmapped world culling rect with the primitive's clip chain rect that - // is in picture space (the clip-chain already takes into account the bounds of the - // primitive local_rect and local_clip_rect). If there is no intersection here, the - // primitive is not visible at all. - let pic_culling_rect = match pic_culling_rect.intersection(&clip_chain.pic_clip_rect) { - Some(rect) => rect, - None => return LayoutRect::zero(), - }; - - // Unmap the picture culling rect from picture -> local space. If this mapping fails due - // to matrix weirdness, best we can do is use the clip chain's local clip rect. - match map_local_to_pic.unmap(&pic_culling_rect) { - Some(rect) => rect, - None => clip_chain.local_clip_rect, - } -} - -fn calculate_prim_clipped_world_rect( - pic_clip_rect: &PictureRect, - world_culling_rect: &WorldRect, - map_surface_to_world: &SpaceMapper<PicturePixel, WorldPixel>, -) -> Option<WorldRect> { - map_surface_to_world - .map(pic_clip_rect) - .and_then(|world_rect| { - world_rect.intersection(world_culling_rect) - }) -} diff --git a/third_party/webrender/webrender/tests/angle_shader_validation.rs b/third_party/webrender/webrender/tests/angle_shader_validation.rs index d6fe618de09..dda275dfdac 100644 --- a/third_party/webrender/webrender/tests/angle_shader_validation.rs +++ b/third_party/webrender/webrender/tests/angle_shader_validation.rs @@ -2,9 +2,9 @@ * 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 mozangle; -extern crate webrender; -extern crate webrender_build; +use mozangle; +use webrender; +use webrender_build; use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator}; use webrender_build::shader::{ShaderFeatureFlags, ShaderVersion, build_shader_strings, get_shader_features}; @@ -35,9 +35,8 @@ fn validate_shaders() { &|f| webrender::get_unoptimized_shader_source(f, None) ); - let full_shader_name = format!("{} {}", shader, config); - validate(&vs_validator, &full_shader_name, vs); - validate(&fs_validator, &full_shader_name, fs); + validate(&vs_validator, shader, vs); + validate(&fs_validator, shader, fs); } } } @@ -50,18 +49,6 @@ fn validate(validator: &ShaderValidator, name: &str, source: String) { // Run Angle validator match validator.compile_and_translate(&[&source]) { Ok(_) => { - // Ensure that the shader uses at most 16 varying vectors. This counts the number of - // vectors assuming that the driver does not perform additional packing. The spec states - // that the driver should pack varyings, however, on some Adreno 3xx devices we have - // observed that this is not the case. See bug 1695912. - let varying_vectors = validator.get_num_unpacked_varying_vectors(); - let max_varying_vectors = 16; - assert!( - varying_vectors <= max_varying_vectors, - "Shader {} uses {} varying vectors. Max allowed {}", - name, varying_vectors, max_varying_vectors - ); - println!("Shader translated succesfully: {}", name); } Err(_) => { diff --git a/third_party/webrender/webrender/tests/bug_124.html b/third_party/webrender/webrender/tests/bug_124.html new file mode 100644 index 00000000000..9f15bbd01a0 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_124.html @@ -0,0 +1,5 @@ +<body style="margin:0"> + <div style="background: blue; width: 100%; height: 100%; position: absolute; overflow: hidden;"> + <div style="background: red; height: 40px; position: absolute; width: 40px; box-shadow: 10px 10px 10px black;"></div> + </div> +</body> diff --git a/third_party/webrender/webrender/tests/bug_134.html b/third_party/webrender/webrender/tests/bug_134.html new file mode 100644 index 00000000000..2d1c177a517 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_134.html @@ -0,0 +1,29 @@ +<style> + + div { + background: #F00; + position: absolute; + width: 100%; + height: 100%; + perspective: 0px; + left: 0; + } + + iframe { + display: block; + position: absolute; + width: 100%; + height: 100%; + transform: translate3d(0px, 0px, 0px); + + /* Not visible: */ + background: #00F; + border: 4px solid #0F0; + + } + +</style> + +<div> + <iframe src="data:,XXX"></iframe> +</div> diff --git a/third_party/webrender/webrender/tests/bug_137.html b/third_party/webrender/webrender/tests/bug_137.html new file mode 100644 index 00000000000..bc76802de74 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_137.html @@ -0,0 +1,25 @@ +<style> + body { + margin: 0; + background: red; + width: 100%; + height: 100%; + } + + div { + background-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="); + background-repeat: no-repeat; + background-size: 200px 200px; + position: absolute; + height: 30px; + width: 30px; + border: 2px solid black; + bottom: 0; + right: 0; + } + + + +</style> + +<div></div> diff --git a/third_party/webrender/webrender/tests/bug_143.html b/third_party/webrender/webrender/tests/bug_143.html new file mode 100644 index 00000000000..7ae5c0c0339 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_143.html @@ -0,0 +1,11 @@ +<style> + div { + background-size: cover; + border-radius: 10px; + width: 100px; + height: 100px; + //border:0.5px solid red; + background-image: url(http://www.google.com/favicon.ico); + } +</style> +<div></div> diff --git a/third_party/webrender/webrender/tests/bug_159.html b/third_party/webrender/webrender/tests/bug_159.html new file mode 100644 index 00000000000..325b99dc922 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_159.html @@ -0,0 +1,2 @@ +<h1>foobar</h1> +<div style="height: 100px; width: 100px; box-shadow: 0px 0px 50px rgba(255,0,0,0.2);"></div> diff --git a/third_party/webrender/webrender/tests/bug_166.html b/third_party/webrender/webrender/tests/bug_166.html new file mode 100644 index 00000000000..c1c52b1eaa5 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_166.html @@ -0,0 +1,10 @@ +<style> + body { + padding: 20px; + background: rgb(65, 149, 250); + //font-size: 200px; + } +</style> + +<div style="opacity: 0.999; color: white">FOOBAR</div> +<div style="opacity: 1.0; color: white">FOOBAR</div> diff --git a/third_party/webrender/webrender/tests/bug_176.html b/third_party/webrender/webrender/tests/bug_176.html new file mode 100644 index 00000000000..3650dad8205 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_176.html @@ -0,0 +1 @@ +<div style="opacity: 0.8">FOOBAR</div> diff --git a/third_party/webrender/webrender/tests/bug_177.html b/third_party/webrender/webrender/tests/bug_177.html new file mode 100644 index 00000000000..188f0b3d93a --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_177.html @@ -0,0 +1,23 @@ +<style> + + .green { + background: green; + position: absolute; + width: 100%; + height: 100px; + transform: translateX(-200px); + } + + .red { + background: red; + position: absolute; + width: 100px; + height: 100px; + right: -50px; + } + +</style> + +<div class="green"> + <div class="red"></div> +</div> diff --git a/third_party/webrender/webrender/tests/bug_178.html b/third_party/webrender/webrender/tests/bug_178.html new file mode 100644 index 00000000000..e8d341f231b --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_178.html @@ -0,0 +1,24 @@ +<style> + + .green { + background: green; + width: 100px; + height: 100px; + position: absolute; + transform: translateX(4px); + } + + .red { + position: absolute; + left: 25px; + width: 50px; + height: 26px; + background: red; + background-image: linear-gradient(to bottom, red, yellow); +} + +</style> + +<div class="green"> + <div class="red"></div> +</div> diff --git a/third_party/webrender/webrender/tests/bug_203a.html b/third_party/webrender/webrender/tests/bug_203a.html new file mode 100644 index 00000000000..dc46d06772c --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_203a.html @@ -0,0 +1,13 @@ +<style> + body { + margin: 0; + } + iframe { + position: absolute; + top: 20px; + left: 20px; + width: 200px; + height: 200px; + } +</style> +<iframe src="bug_203b.html"></iframe> diff --git a/third_party/webrender/webrender/tests/bug_203b.html b/third_party/webrender/webrender/tests/bug_203b.html new file mode 100644 index 00000000000..72f99618898 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_203b.html @@ -0,0 +1,14 @@ +<style> + div { + position: fixed; + width: 100px; + height: 100px; + background: red; + left: 0px; + top: 0px; + } +</style> +<body> + <div></div> + <h1>content</h1> +</body> diff --git a/third_party/webrender/webrender/tests/bug_servo_10136.html b/third_party/webrender/webrender/tests/bug_servo_10136.html new file mode 100644 index 00000000000..bca2e3dc29f --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_servo_10136.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<style> +body { + background: black; +} +div { + color: white; + text-shadow: 5px 5px 5px white; +} +</style> +<div>whoa</div> diff --git a/third_party/webrender/webrender/tests/bug_servo_10164.html b/third_party/webrender/webrender/tests/bug_servo_10164.html new file mode 100644 index 00000000000..8377818cdde --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_servo_10164.html @@ -0,0 +1,19 @@ +<style> + .red { + width: 200px; + height: 200px; + overflow: hidden; + transform: scale(0.5); + background: red; + } + .green { + height: 100px; + width: 200px; + transform: translateX(-100px); + background-color: green; + } +</style> + +<div class="red"> + <div class="green"></div> +</div> diff --git a/third_party/webrender/webrender/tests/bug_servo_10307.html b/third_party/webrender/webrender/tests/bug_servo_10307.html new file mode 100644 index 00000000000..e1775939f11 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_servo_10307.html @@ -0,0 +1,12 @@ +<style> + div { + width: 200px; + height: 200px; + margin: 50px; + box-shadow: 0 0 0 20px black; + transform: translate(20px, 20px); + background: red; + } +</style> +There should be a red square with a black border around it. +<div></div> diff --git a/third_party/webrender/webrender/tests/bug_servo_11358.html b/third_party/webrender/webrender/tests/bug_servo_11358.html new file mode 100644 index 00000000000..2e99b6ff860 --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_servo_11358.html @@ -0,0 +1,12 @@ +<style> + body { + background: green; + } + iframe { + width: 100vw; + height: 100vh; + border: 10px solid red; + transform: scale(.5); + } +</style> +<iframe src="data:text/html,<body style='overflow: hidden; overflow-y: auto;'><div style='background: blue; height: 2000px'>"></iframe> diff --git a/third_party/webrender/webrender/tests/bug_servo_9983a.html b/third_party/webrender/webrender/tests/bug_servo_9983a.html new file mode 100644 index 00000000000..92830ed9b1f --- /dev/null +++ b/third_party/webrender/webrender/tests/bug_servo_9983a.html @@ -0,0 +1,17 @@ +<style> + div { + position: relative; + width: 100px; + height: 100px; + background: red; + overflow: hidden; + } + iframe { + position: absolute; + height: 200px; + width: 100px; + border: none; + } +</style> +Test passes if there is a single 100x100px orange square. +<div><iframe src="data:text/html,<style>body{background:orange}</style>"></iframe></div> diff --git a/third_party/webrender/webrender/tests/color_pattern_1.png b/third_party/webrender/webrender/tests/color_pattern_1.png Binary files differnew file mode 100644 index 00000000000..61b47eb2685 --- /dev/null +++ b/third_party/webrender/webrender/tests/color_pattern_1.png diff --git a/third_party/webrender/webrender/tests/color_pattern_2.png b/third_party/webrender/webrender/tests/color_pattern_2.png Binary files differnew file mode 100644 index 00000000000..e8605692b4b --- /dev/null +++ b/third_party/webrender/webrender/tests/color_pattern_2.png diff --git a/third_party/webrender/webrender/tests/fixed-position.html b/third_party/webrender/webrender/tests/fixed-position.html new file mode 100644 index 00000000000..1b79da2018c --- /dev/null +++ b/third_party/webrender/webrender/tests/fixed-position.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<html> + <head> + <title>test - fixed position</title> + <style type="text/css"> + #container { + background-color: red; + } + + .block { + width: 100%; + height: 100px; + background-color: green; + margin-bottom: 10px; + } + + .fixed { + position: fixed; + top: 50px; + left: 50px; + width: 100px; + height: 100px; + background-color: yellow; + } + </style> + </head> + <body> + <div id="container"> + <div class="fixed"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + <div class="block"></div> + </div> + </body> +</html> diff --git a/third_party/webrender/webrender/tests/mix-blend-mode-2.html b/third_party/webrender/webrender/tests/mix-blend-mode-2.html new file mode 100644 index 00000000000..8c7b028b420 --- /dev/null +++ b/third_party/webrender/webrender/tests/mix-blend-mode-2.html @@ -0,0 +1,85 @@ +<!DOCTYPE html> +<html> + <head> + <style> + html, body { + margin: 0; + } + img { + display: block; + } + #container { + margin: 0; + position: absolute; + width: 100%; + height: 100%; + } + .rect { + position: absolute; + width: 25%; + height: 25%; + //background-image: url("color_pattern_1.png"); + } + .overlay { + z-index: 1; + } + .inner { + position: absolute; + width: 100%; + height: 100%; + background-image: url("color_pattern_2.png"); + } + </style> + </head> + <body> + <div id="container"> + <img src="color_pattern_1.png" class="rect" style="left: 0%; top: 0%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 0%; top: 0%;" style="mix-blend-mode: normal;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 25%; top: 0%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 25%; top: 0%; mix-blend-mode: multiply;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 50%; top: 0%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 50%; top: 0%; mix-blend-mode: screen;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 75%; top: 0%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 75%; top: 0%; mix-blend-mode: overlay;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 0%; top: 25%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 0%; top: 25%; mix-blend-mode: darken;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 25%; top: 25%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 25%; top: 25%; mix-blend-mode: lighten;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 50%; top: 25%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 50%; top: 25%; mix-blend-mode: color-dodge;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 75%; top: 25%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 75%; top: 25%; mix-blend-mode: color-burn;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 0%; top: 50%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 0%; top: 50%; mix-blend-mode: hard-light;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 25%; top: 50%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 25%; top: 50%; mix-blend-mode: soft-light;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 50%; top: 50%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 50%; top: 50%; mix-blend-mode: difference;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 75%; top: 50%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 75%; top: 50%; mix-blend-mode: exclusion;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 0%; top: 75%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 0%; top: 75%; mix-blend-mode: hue;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 25%; top: 75%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 25%; top: 75%; mix-blend-mode: saturation;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 50%; top: 75%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 50%; top: 75%; mix-blend-mode: color;"/> + + <img src="color_pattern_1.png" class="rect" style="left: 75%; top: 75%;"/> + <img src="color_pattern_2.png" class="rect" style="left: 75%; top: 75%; mix-blend-mode: luminosity;"/> + </div> + </body> +</html> diff --git a/third_party/webrender/webrender/tests/mix-blend-mode.html b/third_party/webrender/webrender/tests/mix-blend-mode.html new file mode 100644 index 00000000000..565feb74fef --- /dev/null +++ b/third_party/webrender/webrender/tests/mix-blend-mode.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> + <head> + <style> + #container { + position: absolute; + left: 100px; + top: 100px; + width: 467px; + height: 462px; + background-color: rgb(128, 128, 128); + } + .rect { + position: absolute; + mix-blend-mode: difference; + } + #pink { + left: 80px; + top: 60px; + width: 250px; + height: 250px; + background-color: rgb(128, 128, 0); + } + #blue { + left: 140px; + top: 120px; + width: 250px; + height: 250px; + background-color: rgb(0, 128, 128); + } + #turquoise { + left: 180px; + top: 180px; + width: 250px; + height: 250px; + background-color: rgb(128, 0, 128); + } + </style> + </head> + <body> + <div id="container"> + <div id="pink" class="rect"></div> + <div id="blue" class="rect"></div> + <div id="turquoise" class="rect"></div> + </div> + </body> +</html> diff --git a/third_party/webrender/webrender/tests/nav-1.html b/third_party/webrender/webrender/tests/nav-1.html new file mode 100644 index 00000000000..7c3e21bb804 --- /dev/null +++ b/third_party/webrender/webrender/tests/nav-1.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> + <head> + <title>nav1</title> + <style type="text/css"> + html { + background: rgb(100, 0, 0); + color: white; + } + </style> + </head> + <body> + <a href="nav-2.html">Goto nav2</a> + </body> +</html> diff --git a/third_party/webrender/webrender/tests/nav-2.html b/third_party/webrender/webrender/tests/nav-2.html new file mode 100644 index 00000000000..f05399f329b --- /dev/null +++ b/third_party/webrender/webrender/tests/nav-2.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<html> + <head> + <title>nav2</title> + <style type="text/css"> + html { + background: rgb(0, 100, 0); + color: white; + } + </style> + </head> + <body> + <a href="nav-1.html">Goto nav1</a> + </body> +</html> diff --git a/third_party/webrender/webrender_api/Cargo.toml b/third_party/webrender/webrender_api/Cargo.toml index 2d045bb7548..1a48083a10b 100644 --- a/third_party/webrender/webrender_api/Cargo.toml +++ b/third_party/webrender/webrender_api/Cargo.toml @@ -26,7 +26,6 @@ serde_bytes = "0.11" time = "0.1" malloc_size_of = { version = "0.0.1", path = "../wr_malloc_size_of", package = "wr_malloc_size_of" } peek-poke = { version = "0.2", path = "../peek-poke", features = ["extras"] } -crossbeam-channel = "0.5" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9" diff --git a/third_party/webrender/webrender/src/render_api.rs b/third_party/webrender/webrender_api/src/api.rs index b2c8a64e888..94962ad7128 100644 --- a/third_party/webrender/webrender/src/render_api.rs +++ b/third_party/webrender/webrender_api/src/api.rs @@ -4,42 +4,58 @@ #![deny(missing_docs)] +use peek_poke::PeekPoke; use std::cell::Cell; use std::fmt; use std::marker::PhantomData; +use std::os::raw::c_void; use std::path::PathBuf; use std::sync::Arc; use std::u32; -use api::HitTestFlags; +use std::sync::mpsc::{Sender, Receiver, channel}; use time::precise_time_ns; -//use crate::api::peek_poke::PeekPoke; -use crate::api::channel::{Sender, single_msg_channel, unbounded_channel}; -use crate::api::{ColorF, BuiltDisplayList, IdNamespace, ExternalScrollId}; -use crate::api::{SharedFontInstanceMap, FontKey, FontInstanceKey, NativeFontHandle, ZoomFactor}; -use crate::api::{BlobImageData, BlobImageKey, ImageData, ImageDescriptor, ImageKey, Epoch, QualitySettings}; -use crate::api::{BlobImageParams, BlobImageRequest, BlobImageResult, AsyncBlobImageRasterizer, BlobImageHandler}; -use crate::api::{DocumentId, PipelineId, PropertyBindingId, PropertyBindingKey, ExternalEvent}; -use crate::api::{HitTestResult, HitTesterRequest, ApiHitTester, PropertyValue, DynamicProperties}; -use crate::api::{ScrollClamping, TileSize, NotificationRequest, DebugFlags, ScrollNodeState}; -use crate::api::{GlyphDimensionRequest, GlyphIndexRequest, GlyphIndex, GlyphDimensions}; -use crate::api::{FontInstanceOptions, FontInstancePlatformOptions, FontVariation}; -use crate::api::DEFAULT_TILE_SIZE; -use crate::api::units::*; -use crate::api_resources::ApiResources; -use crate::scene_builder_thread::{SceneBuilderRequest, SceneBuilderResult}; -use crate::intern::InterningMemoryReport; -use crate::profiler::{self, TransactionProfile}; +// local imports +use crate::{display_item as di, font}; +use crate::color::{ColorU, ColorF}; +use crate::display_list::BuiltDisplayList; +use crate::font::SharedFontInstanceMap; +use crate::image::{BlobImageData, BlobImageKey, ImageData, ImageDescriptor, ImageKey}; +use crate::image::{BlobImageParams, BlobImageRequest, BlobImageResult, AsyncBlobImageRasterizer, BlobImageHandler}; +use crate::image::DEFAULT_TILE_SIZE; +use crate::resources::ApiResources; +use crate::units::*; + +/// Width and height in device pixels of image tiles. +pub type TileSize = u16; + +/// Documents are rendered in the ascending order of their associated layer values. +pub type DocumentLayer = i8; + +/// Various settings that the caller can select based on desired tradeoffs +/// between rendering quality and performance / power usage. +#[derive(Copy, Clone, Deserialize, Serialize)] +pub struct QualitySettings { + /// If true, disable creating separate picture cache slices when the + /// scroll root changes. This gives maximum opportunity to find an + /// opaque background, which enables subpixel AA. However, it is + /// usually significantly more expensive to render when scrolling. + pub force_subpixel_aa_where_possible: bool, +} -#[repr(C)] -#[derive(Clone, Copy, Debug)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] -struct ResourceId(pub u32); +impl Default for QualitySettings { + fn default() -> Self { + QualitySettings { + // Prefer performance over maximum subpixel AA quality, since WR + // already enables subpixel AA in more situations than other browsers. + force_subpixel_aa_where_possible: false, + } + } +} /// Update of a persistent resource in WebRender. /// /// ResourceUpdate changes keep theirs effect across display list changes. -#[derive(Clone)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] +#[derive(Clone, Deserialize, Serialize)] pub enum ResourceUpdate { /// See `AddImage`. AddImage(AddImage), @@ -66,7 +82,7 @@ pub enum ResourceUpdate { /// It is invalid to continue referring to the font key in any display list /// in the transaction that contains the `DeleteImage` message and subsequent /// transactions. - DeleteFont(FontKey), + DeleteFont(font::FontKey), /// See `AddFontInstance`. AddFontInstance(AddFontInstance), /// Deletes an already existing font instance resource. @@ -74,7 +90,7 @@ pub enum ResourceUpdate { /// It is invalid to continue referring to the font instance in any display /// list in the transaction that contains the `DeleteImage` message and /// subsequent transactions. - DeleteFontInstance(FontInstanceKey), + DeleteFontInstance(font::FontInstanceKey), } impl fmt::Debug for ResourceUpdate { @@ -107,38 +123,6 @@ impl fmt::Debug for ResourceUpdate { } } -/// Whether to generate a frame, and if so, an id that allows tracking this -/// transaction through the various frame stages. -#[derive(Clone, Debug)] -pub enum GenerateFrame { - /// Generate a frame if something changed. - Yes { - /// An id that allows tracking the frame transaction through the various - /// frame stages. Specified by the caller of generate_frame(). - id: u64, - }, - /// Don't generate a frame even if something has changed. - No, -} - -impl GenerateFrame { - /// - pub fn as_bool(&self) -> bool { - match self { - GenerateFrame::Yes { .. } => true, - GenerateFrame::No => false, - } - } - - /// Return the frame ID, if a frame is generated. - pub fn id(&self) -> Option<u64> { - match self { - GenerateFrame::Yes { id } => Some(*id), - GenerateFrame::No => None, - } - } -} - /// A Transaction is a group of commands to apply atomically to a document. /// /// This mechanism ensures that: @@ -156,19 +140,11 @@ pub struct Transaction { /// Persistent resource updates to apply as part of this transaction. pub resource_updates: Vec<ResourceUpdate>, - /// True if the transaction needs the scene building thread's attention. - /// False for things that can skip the scene builder, like APZ changes and - /// async images. - /// - /// Before this `Transaction` is converted to a `TransactionMsg`, we look - /// over its contents and set this if we're doing anything the scene builder - /// needs to know about, so this is only a default. + /// If true the transaction is piped through the scene building thread, if false + /// it will be applied directly on the render backend. use_scene_builder_thread: bool, - /// Whether to generate a frame, and if so, an id that allows tracking this - /// transaction through the various frame stages. Specified by the caller of - /// generate_frame(). - generate_frame: GenerateFrame, + generate_frame: bool, /// Set to true in order to force re-rendering even if WebRender can't internally /// detect that something has changed. @@ -186,7 +162,7 @@ impl Transaction { resource_updates: Vec::new(), notifications: Vec::new(), use_scene_builder_thread: true, - generate_frame: GenerateFrame::No, + generate_frame: false, invalidate_rendered_frame: false, low_priority: false, } @@ -211,7 +187,7 @@ impl Transaction { /// Returns true if the transaction has no effect. pub fn is_empty(&self) -> bool { - !self.generate_frame.as_bool() && + !self.generate_frame && !self.invalidate_rendered_frame && self.scene_ops.is_empty() && self.frame_ops.is_empty() && @@ -237,9 +213,8 @@ impl Transaction { /// # Examples /// /// ``` - /// # use webrender::api::{PipelineId}; - /// # use webrender::api::units::{DeviceIntSize}; - /// # use webrender::render_api::{RenderApiSender, Transaction}; + /// # use webrender_api::{PipelineId, RenderApiSender, Transaction}; + /// # use webrender_api::units::{DeviceIntSize}; /// # fn example() { /// let pipeline_id = PipelineId(0, 0); /// let mut txn = Transaction::new(); @@ -269,6 +244,7 @@ impl Transaction { /// * `background`: The background color of this pipeline. /// * `viewport_size`: The size of the viewport for this frame. /// * `pipeline_id`: The ID of the pipeline that is supplying this display list. + /// * `content_size`: The total screen space size of this display list's display items. /// * `display_list`: The root Display list used in this frame. /// * `preserve_frame_state`: If a previous frame exists which matches this pipeline /// id, this setting determines if frame state (such as scrolling @@ -278,7 +254,7 @@ impl Transaction { epoch: Epoch, background: Option<ColorF>, viewport_size: LayoutSize, - (pipeline_id, mut display_list): (PipelineId, BuiltDisplayList), + (pipeline_id, content_size, mut display_list): (PipelineId, LayoutSize, BuiltDisplayList), preserve_frame_state: bool, ) { display_list.set_send_time_ns(precise_time_ns()); @@ -289,6 +265,7 @@ impl Transaction { pipeline_id, background, viewport_size, + content_size, preserve_frame_state, } ); @@ -320,8 +297,6 @@ impl Transaction { device_rect: DeviceIntRect, device_pixel_ratio: f32, ) { - assert!(device_pixel_ratio > 0.0); - window_size_sanity_check(device_rect.size); self.scene_ops.push( SceneMsg::SetDocumentView { device_rect, @@ -330,21 +305,25 @@ impl Transaction { ); } - /// Scrolls the node identified by the given external scroll id to the - /// given scroll position, relative to the pre-scrolled offset for the - /// scrolling layer. That is, providing an origin of (0,0) will reset - /// any WR-side scrolling and just render the display items at the - /// pre-scrolled offsets as provided in the display list. Larger `origin` - /// values will cause the layer to be scrolled further towards the end of - /// the scroll range. - /// If the ScrollClamping argument is set to clamp, the scroll position - /// is clamped to what WebRender understands to be the bounds of the - /// scroll range, based on the sizes of the scrollable content and the - /// scroll port. + /// Enable copying of the output of this pipeline id to + /// an external texture for callers to consume. + pub fn enable_frame_output(&mut self, pipeline_id: PipelineId, enable: bool) { + self.scene_ops.push(SceneMsg::EnableFrameOutput(pipeline_id, enable)); + } + + /// Scrolls the scrolling layer under the `cursor` + /// + /// WebRender looks for the layer closest to the user + /// which has `ScrollPolicy::Scrollable` set. + pub fn scroll(&mut self, scroll_location: ScrollLocation, cursor: WorldPoint) { + self.frame_ops.push(FrameMsg::Scroll(scroll_location, cursor)); + } + + /// pub fn scroll_node_with_id( &mut self, origin: LayoutPoint, - id: ExternalScrollId, + id: di::ExternalScrollId, clamp: ScrollClamping, ) { self.frame_ops.push(FrameMsg::ScrollNodeWithId(origin, id, clamp)); @@ -382,8 +361,8 @@ impl Transaction { /// as to when happened. /// /// [notifier]: trait.RenderNotifier.html#tymethod.new_frame_ready - pub fn generate_frame(&mut self, id: u64) { - self.generate_frame = GenerateFrame::Yes{ id }; + pub fn generate_frame(&mut self) { + self.generate_frame = true; } /// Invalidate rendered frame. It ensure that frame will be rendered during @@ -429,7 +408,6 @@ impl Transaction { blob_rasterizer: None, blob_requests: Vec::new(), rasterized_blobs: Vec::new(), - profile: TransactionProfile::new(), }) } @@ -517,35 +495,35 @@ impl Transaction { /// See `ResourceUpdate::SetBlobImageVisibleArea`. pub fn set_blob_image_visible_area(&mut self, key: BlobImageKey, area: DeviceIntRect) { - self.resource_updates.push(ResourceUpdate::SetBlobImageVisibleArea(key, area)); + self.resource_updates.push(ResourceUpdate::SetBlobImageVisibleArea(key, area)) } /// See `ResourceUpdate::AddFont`. - pub fn add_raw_font(&mut self, key: FontKey, bytes: Vec<u8>, index: u32) { + pub fn add_raw_font(&mut self, key: font::FontKey, bytes: Vec<u8>, index: u32) { self.resource_updates .push(ResourceUpdate::AddFont(AddFont::Raw(key, Arc::new(bytes), index))); } /// See `ResourceUpdate::AddFont`. - pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) { + pub fn add_native_font(&mut self, key: font::FontKey, native_handle: font::NativeFontHandle) { self.resource_updates .push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle))); } /// See `ResourceUpdate::DeleteFont`. - pub fn delete_font(&mut self, key: FontKey) { + pub fn delete_font(&mut self, key: font::FontKey) { self.resource_updates.push(ResourceUpdate::DeleteFont(key)); } /// See `ResourceUpdate::AddFontInstance`. pub fn add_font_instance( &mut self, - key: FontInstanceKey, - font_key: FontKey, + key: font::FontInstanceKey, + font_key: font::FontKey, glyph_size: f32, - options: Option<FontInstanceOptions>, - platform_options: Option<FontInstancePlatformOptions>, - variations: Vec<FontVariation>, + options: Option<font::FontInstanceOptions>, + platform_options: Option<font::FontInstancePlatformOptions>, + variations: Vec<font::FontVariation>, ) { self.resource_updates .push(ResourceUpdate::AddFontInstance(AddFontInstance { @@ -559,7 +537,7 @@ impl Transaction { } /// See `ResourceUpdate::DeleteFontInstance`. - pub fn delete_font_instance(&mut self, key: FontInstanceKey) { + pub fn delete_font_instance(&mut self, key: font::FontInstanceKey) { self.resource_updates.push(ResourceUpdate::DeleteFontInstance(key)); } @@ -596,7 +574,7 @@ pub struct TransactionMsg { /// Updates to resources that persist across display lists. pub resource_updates: Vec<ResourceUpdate>, /// Whether to trigger frame building and rendering if something has changed. - pub generate_frame: GenerateFrame, + pub generate_frame: bool, /// Whether to force frame building and rendering even if no changes are internally /// observed. pub invalidate_rendered_frame: bool, @@ -613,13 +591,11 @@ pub struct TransactionMsg { pub blob_requests: Vec<BlobImageParams>, /// pub rasterized_blobs: Vec<(BlobImageRequest, BlobImageResult)>, - /// Collect various data along the rendering pipeline to display it in the embedded profiler. - pub profile: TransactionProfile, } impl fmt::Debug for TransactionMsg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - writeln!(f, "threaded={}, genframe={:?}, invalidate={}, low_priority={}", + writeln!(f, "threaded={}, genframe={}, invalidate={}, low_priority={}", self.use_scene_builder_thread, self.generate_frame, self.invalidate_rendered_frame, @@ -643,7 +619,7 @@ impl fmt::Debug for TransactionMsg { impl TransactionMsg { /// Returns true if this transaction has no effect. pub fn is_empty(&self) -> bool { - !self.generate_frame.as_bool() && + !self.generate_frame && !self.invalidate_rendered_frame && self.scene_ops.is_empty() && self.frame_ops.is_empty() && @@ -655,8 +631,7 @@ impl TransactionMsg { /// Creates an image resource with provided parameters. /// /// Must be matched with a `DeleteImage` at some point to prevent memory leaks. -#[derive(Clone)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] +#[derive(Clone, Deserialize, Serialize)] pub struct AddImage { /// A key to identify the image resource. pub key: ImageKey, @@ -673,8 +648,7 @@ pub struct AddImage { } /// Updates an already existing image resource. -#[derive(Clone)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] +#[derive(Clone, Deserialize, Serialize)] pub struct UpdateImage { /// The key identfying the image resource to update. pub key: ImageKey, @@ -692,8 +666,7 @@ pub struct UpdateImage { /// Creates a blob-image resource with provided parameters. /// /// Must be matched with a `DeleteImage` at some point to prevent memory leaks. -#[derive(Clone)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] +#[derive(Clone, Deserialize, Serialize)] pub struct AddBlobImage { /// A key to identify the blob-image resource. pub key: BlobImageKey, @@ -719,8 +692,7 @@ pub struct AddBlobImage { } /// Updates an already existing blob-image resource. -#[derive(Clone)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] +#[derive(Clone, Deserialize, Serialize)] pub struct UpdateBlobImage { /// The key identfying the blob-image resource to update. pub key: BlobImageKey, @@ -739,34 +711,69 @@ pub struct UpdateBlobImage { /// /// Must be matched with a corresponding `ResourceUpdate::DeleteFont` at some point to prevent /// memory leaks. -#[derive(Clone)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] +#[derive(Clone, Deserialize, Serialize)] pub enum AddFont { /// - Raw(FontKey, Arc<Vec<u8>>, u32), + Raw(font::FontKey, Arc<Vec<u8>>, u32), + /// + Native(font::FontKey, font::NativeFontHandle), +} + +/// Describe an item that matched a hit-test query. +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub struct HitTestItem { + /// The pipeline that the display item that was hit belongs to. + pub pipeline: PipelineId, + + /// The tag of the hit display item. + pub tag: di::ItemTag, + + /// The hit point in the coordinate space of the "viewport" of the display item. The + /// viewport is the scroll node formed by the root reference frame of the display item's + /// pipeline. + pub point_in_viewport: LayoutPoint, + + /// The coordinates of the original hit test point relative to the origin of this item. + /// This is useful for calculating things like text offsets in the client. + pub point_relative_to_item: LayoutPoint, +} + +/// Returned by `RenderApi::hit_test`. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct HitTestResult { + /// List of items that are match the hit-test query. + pub items: Vec<HitTestItem>, +} + +bitflags! { + #[derive(Deserialize, MallocSizeOf, Serialize)] /// - Native(FontKey, NativeFontHandle), + pub struct HitTestFlags: u8 { + /// + const FIND_ALL = 0b00000001; + /// + const POINT_RELATIVE_TO_PIPELINE_VIEWPORT = 0b00000010; + } } /// Creates a font instance resource. /// /// Must be matched with a corresponding `DeleteFontInstance` at some point /// to prevent memory leaks. -#[derive(Clone)] -#[cfg_attr(any(feature = "serde"), derive(Deserialize, Serialize))] +#[derive(Clone, Deserialize, Serialize)] pub struct AddFontInstance { /// A key to identify the font instance. - pub key: FontInstanceKey, + pub key: font::FontInstanceKey, /// The font resource's key. - pub font_key: FontKey, + pub font_key: font::FontKey, /// Glyph size in app units. pub glyph_size: f32, /// - pub options: Option<FontInstanceOptions>, + pub options: Option<font::FontInstanceOptions>, /// - pub platform_options: Option<FontInstancePlatformOptions>, + pub platform_options: Option<font::FontInstancePlatformOptions>, /// - pub variations: Vec<FontVariation>, + pub variations: Vec<font::FontVariation>, } /// Frame messages affect building the scene. @@ -780,6 +787,8 @@ pub enum SceneMsg { /// RemovePipeline(PipelineId), /// + EnableFrameOutput(PipelineId, bool), + /// SetDisplayList { /// display_list: BuiltDisplayList, @@ -792,6 +801,8 @@ pub enum SceneMsg { /// viewport_size: LayoutSize, /// + content_size: LayoutSize, + /// preserve_frame_state: bool, }, /// @@ -819,7 +830,9 @@ pub enum FrameMsg { /// SetPan(DeviceIntPoint), /// - ScrollNodeWithId(LayoutPoint, ExternalScrollId, ScrollClamping), + Scroll(ScrollLocation, WorldPoint), + /// + ScrollNodeWithId(LayoutPoint, di::ExternalScrollId, ScrollClamping), /// GetScrollNodeState(Sender<Vec<ScrollNodeState>>), /// @@ -839,6 +852,7 @@ impl fmt::Debug for SceneMsg { SceneMsg::SetDisplayList { .. } => "SceneMsg::SetDisplayList", SceneMsg::SetPageZoom(..) => "SceneMsg::SetPageZoom", SceneMsg::RemovePipeline(..) => "SceneMsg::RemovePipeline", + SceneMsg::EnableFrameOutput(..) => "SceneMsg::EnableFrameOutput", SceneMsg::SetDocumentView { .. } => "SceneMsg::SetDocumentView", SceneMsg::SetRootPipeline(..) => "SceneMsg::SetRootPipeline", SceneMsg::SetQualitySettings { .. } => "SceneMsg::SetQualitySettings", @@ -853,6 +867,7 @@ impl fmt::Debug for FrameMsg { FrameMsg::HitTest(..) => "FrameMsg::HitTest", FrameMsg::RequestHitTester(..) => "FrameMsg::RequestHitTester", FrameMsg::SetPan(..) => "FrameMsg::SetPan", + FrameMsg::Scroll(..) => "FrameMsg::Scroll", FrameMsg::ScrollNodeWithId(..) => "FrameMsg::ScrollNodeWithId", FrameMsg::GetScrollNodeState(..) => "FrameMsg::GetScrollNodeState", FrameMsg::UpdateDynamicProperties(..) => "FrameMsg::UpdateDynamicProperties", @@ -884,15 +899,13 @@ bitflags!{ /// const IMAGES = 0b1; /// - const GLYPHS = 0b10; + const GLYPHS = 0b01; /// - const GLYPH_DIMENSIONS = 0b100; + const GLYPH_DIMENSIONS = 0b001; /// - const RENDER_TASKS = 0b1000; + const RENDER_TASKS = 0b0001; /// - const TEXTURE_CACHE = 0b10000; - /// Clear render target pool - const RENDER_TARGETS = 0b100000; + const TEXTURE_CACHE = 0b00001; } } @@ -913,6 +926,19 @@ pub enum DebugCommand { SetFlags(DebugFlags), /// Configure if dual-source blending is used, if available. EnableDualSourceBlending(bool), + /// Fetch current documents and display lists. + FetchDocuments, + /// Fetch current passes and batches. + FetchPasses, + // TODO: This should be called FetchClipScrollTree. However, that requires making + // changes to webrender's web debugger ui, touching a 4Mb minified file that + // is too big to submit through the conventional means. + /// Fetch the spatial tree. + FetchClipScrollTree, + /// Fetch render tasks. + FetchRenderTasks, + /// Fetch screenshot. + FetchScreenshot, /// Save a capture of all the documents state. SaveCapture(PathBuf, CaptureBits), /// Load a capture of all the documents state. @@ -934,53 +960,312 @@ pub enum DebugCommand { /// Causes the scene builder to pause for a given amount of milliseconds each time it /// processes a transaction. SimulateLongSceneBuild(u32), + /// Causes the low priority scene builder to pause for a given amount of milliseconds + /// each time it processes a transaction. + SimulateLongLowPrioritySceneBuild(u32), /// Set an override tile size to use for picture caches SetPictureTileSize(Option<DeviceIntSize>), } /// Message sent by the `RenderApi` to the render backend thread. pub enum ApiMsg { + /// Gets the glyph dimensions + GetGlyphDimensions(font::GlyphDimensionRequest), + /// Gets the glyph indices from a string + GetGlyphIndices(font::GlyphIndexRequest), /// Adds a new document namespace. CloneApi(Sender<IdNamespace>), /// Adds a new document namespace. CloneApiByClient(IdNamespace), /// Adds a new document with given initial size. - AddDocument(DocumentId, DeviceIntSize), + AddDocument(DocumentId, DeviceIntSize, DocumentLayer), /// A message targeted at a particular document. UpdateDocuments(Vec<Box<TransactionMsg>>), + /// Deletes an existing document. + DeleteDocument(DocumentId), + /// An opaque handle that must be passed to the render notifier. It is used by Gecko + /// to forward gecko-specific messages to the render thread preserving the ordering + /// within the other messages. + ExternalEvent(ExternalEvent), + /// Removes all resources associated with a namespace. + ClearNamespace(IdNamespace), /// Flush from the caches anything that isn't necessary, to free some memory. MemoryPressure, /// Collects a memory report. ReportMemory(Sender<Box<MemoryReport>>), /// Change debugging options. DebugCommand(DebugCommand), - /// Message from the scene builder thread. - SceneBuilderResult(SceneBuilderResult), + /// Wakes the render backend's event loop up. Needed when an event is communicated + /// through another channel. + WakeUp, + /// See `RenderApi::wake_scene_builder`. + WakeSceneBuilder, + /// Block until a round-trip to the scene builder thread has completed. This + /// ensures that any transactions (including ones deferred to the scene + /// builder thread) have been processed. + FlushSceneBuilder(Sender<()>), + /// Shut the WebRender instance down. + ShutDown(Option<Sender<()>>), } impl fmt::Debug for ApiMsg { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match *self { + ApiMsg::GetGlyphDimensions(..) => "ApiMsg::GetGlyphDimensions", + ApiMsg::GetGlyphIndices(..) => "ApiMsg::GetGlyphIndices", ApiMsg::CloneApi(..) => "ApiMsg::CloneApi", ApiMsg::CloneApiByClient(..) => "ApiMsg::CloneApiByClient", ApiMsg::AddDocument(..) => "ApiMsg::AddDocument", ApiMsg::UpdateDocuments(..) => "ApiMsg::UpdateDocuments", + ApiMsg::DeleteDocument(..) => "ApiMsg::DeleteDocument", + ApiMsg::ExternalEvent(..) => "ApiMsg::ExternalEvent", + ApiMsg::ClearNamespace(..) => "ApiMsg::ClearNamespace", ApiMsg::MemoryPressure => "ApiMsg::MemoryPressure", ApiMsg::ReportMemory(..) => "ApiMsg::ReportMemory", ApiMsg::DebugCommand(..) => "ApiMsg::DebugCommand", - ApiMsg::SceneBuilderResult(..) => "ApiMsg::SceneBuilderResult", + ApiMsg::ShutDown(..) => "ApiMsg::ShutDown", + ApiMsg::WakeUp => "ApiMsg::WakeUp", + ApiMsg::WakeSceneBuilder => "ApiMsg::WakeSceneBuilder", + ApiMsg::FlushSceneBuilder(..) => "ApiMsg::FlushSceneBuilder", }) } } +/// An epoch identifies the state of a pipeline in time. +/// +/// This is mostly used as a synchronization mechanism to observe how/when particular pipeline +/// updates propagate through WebRender and are applied at various stages. +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct Epoch(pub u32); + +impl Epoch { + /// Magic invalid epoch value. + pub fn invalid() -> Epoch { + Epoch(u32::MAX) + } +} + +/// ID namespaces uniquely identify different users of WebRender's API. +/// +/// For example in Gecko each content process uses a separate id namespace. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Eq, MallocSizeOf, PartialEq, Hash, Ord, PartialOrd, PeekPoke)] +#[derive(Deserialize, Serialize)] +pub struct IdNamespace(pub u32); + +/// A key uniquely identifying a WebRender document. +/// +/// Instances can manage one or several documents (using the same render backend thread). +/// Each document will internally correspond to a single scene, and scenes are made of +/// one or several pipelines. +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub struct DocumentId { + /// + pub namespace_id: IdNamespace, + /// + pub id: u32, +} + +impl DocumentId { + /// + pub fn new(namespace_id: IdNamespace, id: u32) -> Self { + DocumentId { + namespace_id, + id, + } + } + + /// + pub const INVALID: DocumentId = DocumentId { namespace_id: IdNamespace(0), id: 0 }; +} + +/// This type carries no valuable semantics for WR. However, it reflects the fact that +/// clients (Servo) may generate pipelines by different semi-independent sources. +/// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`. +/// Having this extra Id field enables them to generate `PipelineId` without collision. +pub type PipelineSourceId = u32; + +/// From the point of view of WR, `PipelineId` is completely opaque and generic as long as +/// it's clonable, serializable, comparable, and hashable. +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub struct PipelineId(pub PipelineSourceId, pub u32); + +impl Default for PipelineId { + fn default() -> Self { + PipelineId::dummy() + } +} + +impl PipelineId { + /// + pub fn dummy() -> Self { + PipelineId(0, 0) + } +} + +/// +#[derive(Copy, Clone, Debug, MallocSizeOf, Serialize, Deserialize)] +pub enum ClipIntern {} + +/// +#[derive(Copy, Clone, Debug, MallocSizeOf, Serialize, Deserialize)] +pub enum FilterDataIntern {} + +/// Information specific to a primitive type that +/// uniquely identifies a primitive template by key. +#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash, Serialize, Deserialize)] +pub enum PrimitiveKeyKind { + /// Clear an existing rect, used for special effects on some platforms. + Clear, + /// + Rectangle { + /// + color: PropertyBinding<ColorU>, + }, +} + +/// Meta-macro to enumerate the various interner identifiers and types. +/// +/// IMPORTANT: Keep this synchronized with the list in mozilla-central located at +/// gfx/webrender_bindings/webrender_ffi.h +/// +/// Note that this could be a lot less verbose if concat_idents! were stable. :-( +#[macro_export] +macro_rules! enumerate_interners { + ($macro_name: ident) => { + $macro_name! { + clip: ClipIntern, + prim: PrimitiveKeyKind, + normal_border: NormalBorderPrim, + image_border: ImageBorder, + image: Image, + yuv_image: YuvImage, + line_decoration: LineDecoration, + linear_grad: LinearGradient, + radial_grad: RadialGradient, + conic_grad: ConicGradient, + picture: Picture, + text_run: TextRun, + filter_data: FilterDataIntern, + backdrop: Backdrop, + } + } +} + +macro_rules! declare_interning_memory_report { + ( $( $name:ident: $ty:ident, )+ ) => { + /// + #[repr(C)] + #[derive(AddAssign, Clone, Debug, Default)] + pub struct InternerSubReport { + $( + /// + pub $name: usize, + )+ + } + } +} + +enumerate_interners!(declare_interning_memory_report); + +/// Memory report for interning-related data structures. +/// cbindgen:derive-eq=false +#[repr(C)] +#[derive(Clone, Debug, Default)] +pub struct InterningMemoryReport { + /// + pub interners: InternerSubReport, + /// + pub data_stores: InternerSubReport, +} + +impl ::std::ops::AddAssign for InterningMemoryReport { + fn add_assign(&mut self, other: InterningMemoryReport) { + self.interners += other.interners; + self.data_stores += other.data_stores; + } +} + +/// Collection of heap sizes, in bytes. +/// cbindgen:derive-eq=false +#[repr(C)] +#[allow(missing_docs)] +#[derive(AddAssign, Clone, Debug, Default)] +pub struct MemoryReport { + // + // CPU Memory. + // + pub clip_stores: usize, + pub gpu_cache_metadata: usize, + pub gpu_cache_cpu_mirror: usize, + pub render_tasks: usize, + pub hit_testers: usize, + pub fonts: usize, + pub images: usize, + pub rasterized_blobs: usize, + pub shader_cache: usize, + pub interning: InterningMemoryReport, + pub display_list: usize, + + // + // GPU memory. + // + pub gpu_cache_textures: usize, + pub vertex_data_textures: usize, + pub render_target_textures: usize, + pub texture_cache_textures: usize, + pub depth_target_textures: usize, + pub swap_chain: usize, +} + +/// A C function that takes a pointer to a heap allocation and returns its size. +/// +/// This is borrowed from the malloc_size_of crate, upon which we want to avoid +/// a dependency from WebRender. +pub type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Serialize)] +struct ResourceId(pub u32); + +/// An opaque pointer-sized value. +#[repr(C)] +#[derive(Clone)] +pub struct ExternalEvent { + raw: usize, +} + +unsafe impl Send for ExternalEvent {} + +impl ExternalEvent { + /// Creates the event from an opaque pointer-sized value. + pub fn from_raw(raw: usize) -> Self { + ExternalEvent { raw } + } + /// Consumes self to make it obvious that the event should be forwarded only once. + pub fn unwrap(self) -> usize { + self.raw + } +} + +/// Describe whether or not scrolling should be clamped by the content bounds. +#[derive(Clone, Deserialize, Serialize)] +pub enum ScrollClamping { + /// + ToContentBounds, + /// + NoClamping, +} + /// Allows the API to communicate with WebRender. /// /// This object is created along with the `Renderer` and it's main use from a /// user perspective is to create one or several `RenderApi` objects. pub struct RenderApiSender { api_sender: Sender<ApiMsg>, - scene_sender: Sender<SceneBuilderRequest>, - low_priority_scene_sender: Sender<SceneBuilderRequest>, blob_image_handler: Option<Box<dyn BlobImageHandler>>, shared_font_instances: SharedFontInstanceMap, } @@ -989,15 +1274,11 @@ impl RenderApiSender { /// Used internally by the `Renderer`. pub fn new( api_sender: Sender<ApiMsg>, - scene_sender: Sender<SceneBuilderRequest>, - low_priority_scene_sender: Sender<SceneBuilderRequest>, blob_image_handler: Option<Box<dyn BlobImageHandler>>, shared_font_instances: SharedFontInstanceMap, ) -> Self { RenderApiSender { api_sender, - scene_sender, - low_priority_scene_sender, blob_image_handler, shared_font_instances, } @@ -1005,14 +1286,23 @@ impl RenderApiSender { /// Creates a new resource API object with a dedicated namespace. pub fn create_api(&self) -> RenderApi { - let (sync_tx, sync_rx) = single_msg_channel(); + let (sync_tx, sync_rx) = channel(); let msg = ApiMsg::CloneApi(sync_tx); self.api_sender.send(msg).expect("Failed to send CloneApi message"); - let namespace_id = sync_rx.recv().expect("Failed to receive CloneApi reply"); + let namespace_id = match sync_rx.recv() { + Ok(id) => id, + Err(e) => { + // This is used to discover the underlying cause of https://github.com/servo/servo/issues/13480. + let webrender_is_alive = self.api_sender.send(ApiMsg::WakeUp); + if webrender_is_alive.is_err() { + panic!("WebRender was shut down before processing CloneApi: {}", e); + } else { + panic!("CloneApi message response was dropped while WebRender was still alive: {}", e); + } + } + }; RenderApi { api_sender: self.api_sender.clone(), - scene_sender: self.scene_sender.clone(), - low_priority_scene_sender: self.low_priority_scene_sender.clone(), namespace_id, next_id: Cell::new(ResourceId(0)), resources: ApiResources::new( @@ -1032,8 +1322,6 @@ impl RenderApiSender { self.api_sender.send(msg).expect("Failed to send CloneApiByClient message"); RenderApi { api_sender: self.api_sender.clone(), - scene_sender: self.scene_sender.clone(), - low_priority_scene_sender: self.low_priority_scene_sender.clone(), namespace_id, next_id: Cell::new(ResourceId(0)), resources: ApiResources::new( @@ -1044,11 +1332,91 @@ impl RenderApiSender { } } +bitflags! { + /// Flags to enable/disable various builtin debugging tools. + #[repr(C)] + #[derive(Default, Deserialize, MallocSizeOf, Serialize)] + pub struct DebugFlags: u32 { + /// Display the frame profiler on screen. + const PROFILER_DBG = 1 << 0; + /// Display intermediate render targets on screen. + const RENDER_TARGET_DBG = 1 << 1; + /// Display all texture cache pages on screen. + const TEXTURE_CACHE_DBG = 1 << 2; + /// Display GPU timing results. + const GPU_TIME_QUERIES = 1 << 3; + /// Query the number of pixels that pass the depth test divided and show it + /// in the profiler as a percentage of the number of pixels in the screen + /// (window width times height). + const GPU_SAMPLE_QUERIES = 1 << 4; + /// Render each quad with their own draw call. + /// + /// Terrible for performance but can help with understanding the drawing + /// order when inspecting renderdoc or apitrace recordings. + const DISABLE_BATCHING = 1 << 5; + /// Display the pipeline epochs. + const EPOCHS = 1 << 6; + /// Reduce the amount of information displayed by the profiler so that + /// it occupies less screen real-estate. + const COMPACT_PROFILER = 1 << 7; + /// Print driver messages to stdout. + const ECHO_DRIVER_MESSAGES = 1 << 8; + /// Show an indicator that moves every time a frame is rendered. + const NEW_FRAME_INDICATOR = 1 << 9; + /// Show an indicator that moves every time a scene is built. + const NEW_SCENE_INDICATOR = 1 << 10; + /// Show an overlay displaying overdraw amount. + const SHOW_OVERDRAW = 1 << 11; + /// Display the contents of GPU cache. + const GPU_CACHE_DBG = 1 << 12; + /// Show a red bar that moves each time a slow frame is detected. + const SLOW_FRAME_INDICATOR = 1 << 13; + /// Clear evicted parts of the texture cache for debugging purposes. + const TEXTURE_CACHE_DBG_CLEAR_EVICTED = 1 << 14; + /// Show picture caching debug overlay + const PICTURE_CACHING_DBG = 1 << 15; + /// Highlight all primitives with colors based on kind. + const PRIMITIVE_DBG = 1 << 16; + /// Draw a zoom widget showing part of the framebuffer zoomed in. + const ZOOM_DBG = 1 << 17; + /// Scale the debug renderer down for a smaller screen. This will disrupt + /// any mapping between debug display items and page content, so shouldn't + /// be used with overlays like the picture caching or primitive display. + const SMALL_SCREEN = 1 << 18; + /// Disable various bits of the WebRender pipeline, to help narrow + /// down where slowness might be coming from. + const DISABLE_OPAQUE_PASS = 1 << 19; + /// + const DISABLE_ALPHA_PASS = 1 << 20; + /// + const DISABLE_CLIP_MASKS = 1 << 21; + /// + const DISABLE_TEXT_PRIMS = 1 << 22; + /// + const DISABLE_GRADIENT_PRIMS = 1 << 23; + /// + const OBSCURE_IMAGES = 1 << 24; + /// Taint the transparent area of the glyphs with a random opacity to easily + /// see when glyphs are re-rasterized. + const GLYPH_FLASHING = 1 << 25; + /// The profiler only displays information that is out of the ordinary. + const SMART_PROFILER = 1 << 26; + /// Dynamically control whether picture caching is enabled. + const DISABLE_PICTURE_CACHING = 1 << 27; + /// If set, dump picture cache invalidation debug to console. + const INVALIDATION_DBG = 1 << 28; + /// Log tile cache to memory for later saving as part of wr-capture + const TILE_CACHE_LOGGING_DBG = 1 << 29; + /// For debugging, force-disable automatic scaling of establishes_raster_root + /// pictures that are too large (ie go back to old behavior that prevents those + /// large pictures from establishing a raster root). + const DISABLE_RASTER_ROOT_SCALING = 1 << 30; + } +} + /// The main entry point to interact with WebRender. pub struct RenderApi { api_sender: Sender<ApiMsg>, - scene_sender: Sender<SceneBuilderRequest>, - low_priority_scene_sender: Sender<SceneBuilderRequest>, namespace_id: IdNamespace, next_id: Cell<ResourceId>, resources: ApiResources, @@ -1064,8 +1432,6 @@ impl RenderApi { pub fn create_sender(&self) -> RenderApiSender { RenderApiSender::new( self.api_sender.clone(), - self.scene_sender.clone(), - self.low_priority_scene_sender.clone(), self.resources.blob_image_handler.as_ref().map(|handler| handler.create_similar()), self.resources.get_shared_font_instances(), ) @@ -1076,51 +1442,40 @@ impl RenderApi { /// Instances can manage one or several documents (using the same render backend thread). /// Each document will internally correspond to a single scene, and scenes are made of /// one or several pipelines. - pub fn add_document(&self, initial_size: DeviceIntSize) -> DocumentId { + pub fn add_document(&self, initial_size: DeviceIntSize, layer: DocumentLayer) -> DocumentId { let new_id = self.next_unique_id(); - self.add_document_with_id(initial_size, new_id) + self.add_document_with_id(initial_size, layer, new_id) } /// See `add_document` pub fn add_document_with_id(&self, initial_size: DeviceIntSize, + layer: DocumentLayer, id: u32) -> DocumentId { - window_size_sanity_check(initial_size); - let document_id = DocumentId::new(self.namespace_id, id); - // We send this message to both the render backend and the scene builder instead of having - // the scene builder thread forward it to the render backend as we do elswhere. This is because - // some transactions can skip the scene builder thread and we want to avoid them arriving before - // the render backend knows about the existence of the corresponding document id. - // It may not be necessary, though. - self.api_sender.send( - ApiMsg::AddDocument(document_id, initial_size) - ).unwrap(); - self.scene_sender.send( - SceneBuilderRequest::AddDocument(document_id, initial_size) - ).unwrap(); + let msg = ApiMsg::AddDocument(document_id, initial_size, layer); + self.api_sender.send(msg).unwrap(); document_id } /// Delete a document. pub fn delete_document(&self, document_id: DocumentId) { - self.low_priority_scene_sender.send( - SceneBuilderRequest::DeleteDocument(document_id) - ).unwrap(); + let msg = ApiMsg::DeleteDocument(document_id); + self.api_sender.send(msg).unwrap(); } /// Generate a new font key - pub fn generate_font_key(&self) -> FontKey { + pub fn generate_font_key(&self) -> font::FontKey { let new_id = self.next_unique_id(); - FontKey::new(self.namespace_id, new_id) + font::FontKey::new(self.namespace_id, new_id) } /// Generate a new font instance key - pub fn generate_font_instance_key(&self) -> FontInstanceKey { + pub fn generate_font_instance_key(&self) -> font::FontInstanceKey { let new_id = self.next_unique_id(); - FontInstanceKey::new(self.namespace_id, new_id) + font::FontInstanceKey::new(self.namespace_id, new_id) } /// Gets the dimensions for the supplied glyph keys @@ -1130,29 +1485,29 @@ impl RenderApi { /// This means that glyph dimensions e.g. for spaces (' ') will mostly be None. pub fn get_glyph_dimensions( &self, - key: FontInstanceKey, - glyph_indices: Vec<GlyphIndex>, - ) -> Vec<Option<GlyphDimensions>> { - let (sender, rx) = single_msg_channel(); - let msg = SceneBuilderRequest::GetGlyphDimensions(GlyphDimensionRequest { + key: font::FontInstanceKey, + glyph_indices: Vec<font::GlyphIndex>, + ) -> Vec<Option<font::GlyphDimensions>> { + let (sender, rx) = channel(); + let msg = ApiMsg::GetGlyphDimensions(font::GlyphDimensionRequest { key, glyph_indices, sender }); - self.low_priority_scene_sender.send(msg).unwrap(); + self.api_sender.send(msg).unwrap(); rx.recv().unwrap() } /// Gets the glyph indices for the supplied string. These /// can be used to construct GlyphKeys. - pub fn get_glyph_indices(&self, key: FontKey, text: &str) -> Vec<Option<u32>> { - let (sender, rx) = single_msg_channel(); - let msg = SceneBuilderRequest::GetGlyphIndices(GlyphIndexRequest { + pub fn get_glyph_indices(&self, key: font::FontKey, text: &str) -> Vec<Option<u32>> { + let (sender, rx) = channel(); + let msg = ApiMsg::GetGlyphIndices(font::GlyphIndexRequest { key, text: text.to_string(), sender, }); - self.low_priority_scene_sender.send(msg).unwrap(); + self.api_sender.send(msg).unwrap(); rx.recv().unwrap() } @@ -1171,8 +1526,8 @@ impl RenderApi { /// `Renderer`'s thread, mostly replaced by `NotificationHandler`. You should /// probably use the latter instead. pub fn send_external_event(&self, evt: ExternalEvent) { - let msg = SceneBuilderRequest::ExternalEvent(evt); - self.low_priority_scene_sender.send(msg).unwrap(); + let msg = ApiMsg::ExternalEvent(evt); + self.api_sender.send(msg).unwrap(); } /// Notify WebRender that now is a good time to flush caches and release @@ -1182,8 +1537,8 @@ impl RenderApi { } /// Synchronously requests memory report. - pub fn report_memory(&self, _ops: malloc_size_of::MallocSizeOfOps) -> MemoryReport { - let (tx, rx) = single_msg_channel(); + pub fn report_memory(&self) -> MemoryReport { + let (tx, rx) = channel(); self.api_sender.send(ApiMsg::ReportMemory(tx)).unwrap(); *rx.recv().unwrap() } @@ -1194,19 +1549,14 @@ impl RenderApi { self.api_sender.send(ApiMsg::DebugCommand(cmd)).unwrap(); } - /// Stop RenderBackend's task until shut down - pub fn stop_render_backend(&self) { - self.low_priority_scene_sender.send(SceneBuilderRequest::StopRenderBackend).unwrap(); - } - /// Shut the WebRender instance down. pub fn shut_down(&self, synchronously: bool) { if synchronously { - let (tx, rx) = single_msg_channel(); - self.low_priority_scene_sender.send(SceneBuilderRequest::ShutDown(Some(tx))).unwrap(); + let (tx, rx) = channel(); + self.api_sender.send(ApiMsg::ShutDown(Some(tx))).unwrap(); rx.recv().unwrap(); } else { - self.low_priority_scene_sender.send(SceneBuilderRequest::ShutDown(None)).unwrap(); + self.api_sender.send(ApiMsg::ShutDown(None)).unwrap(); } } @@ -1244,17 +1594,44 @@ impl RenderApi { frame_ops: vec![msg], resource_updates: Vec::new(), notifications: Vec::new(), - generate_frame: GenerateFrame::No, + generate_frame: false, invalidate_rendered_frame: false, use_scene_builder_thread: false, low_priority: false, blob_rasterizer: None, blob_requests: Vec::new(), rasterized_blobs: Vec::new(), - profile: TransactionProfile::new(), }) } + /// Creates a transaction message from a single scene message. + fn scene_message(&self, msg: SceneMsg, document_id: DocumentId) -> Box<TransactionMsg> { + Box::new(TransactionMsg { + document_id, + scene_ops: vec![msg], + frame_ops: Vec::new(), + resource_updates: Vec::new(), + notifications: Vec::new(), + generate_frame: false, + invalidate_rendered_frame: false, + use_scene_builder_thread: false, + low_priority: false, + blob_rasterizer: None, + blob_requests: Vec::new(), + rasterized_blobs: Vec::new(), + }) + } + + /// A helper method to send document messages. + fn send_scene_msg(&self, document_id: DocumentId, msg: SceneMsg) { + // This assertion fails on Servo use-cases, because it creates different + // `RenderApi` instances for layout and compositor. + //assert_eq!(document_id.0, self.namespace_id); + self.api_sender + .send(ApiMsg::UpdateDocuments(vec![self.scene_message(msg, document_id)])) + .unwrap() + } + /// A helper method to send document messages. fn send_frame_msg(&self, document_id: DocumentId, msg: FrameMsg) { // This assertion fails on Servo use-cases, because it creates different @@ -1271,36 +1648,37 @@ impl RenderApi { self.resources.update(&mut transaction); - if transaction.generate_frame.as_bool() { - transaction.profile.start_time(profiler::API_SEND_TIME); - transaction.profile.start_time(profiler::TOTAL_FRAME_CPU_TIME); - } + self.api_sender.send(ApiMsg::UpdateDocuments(vec![transaction])).unwrap(); + } - if transaction.use_scene_builder_thread { - let sender = if transaction.low_priority { - &mut self.low_priority_scene_sender - } else { - &mut self.scene_sender - }; + /// Send multiple transactions. + pub fn send_transactions(&mut self, document_ids: Vec<DocumentId>, mut transactions: Vec<Transaction>) { + debug_assert!(document_ids.len() == transactions.len()); + let msgs = transactions.drain(..).zip(document_ids) + .map(|(txn, id)| { + let mut txn = txn.finalize(id); + self.resources.update(&mut txn); - sender.send(SceneBuilderRequest::Transactions(vec![transaction])).unwrap(); - } else { - self.api_sender.send(ApiMsg::UpdateDocuments(vec![transaction])).unwrap(); - } + txn + }) + .collect(); + + self.api_sender.send(ApiMsg::UpdateDocuments(msgs)).unwrap(); } /// Does a hit test on display items in the specified document, at the given /// point. If a pipeline_id is specified, it is used to further restrict the - /// hit results so that only items inside that pipeline are matched. The vector - /// of hit results will contain all display items that match, ordered from - /// front to back. + /// hit results so that only items inside that pipeline are matched. If the + /// HitTestFlags argument contains the FIND_ALL flag, then the vector of hit + /// results will contain all display items that match, ordered from front + /// to back. pub fn hit_test(&self, - document_id: DocumentId, - pipeline_id: Option<PipelineId>, - point: WorldPoint, - flags: HitTestFlags, - ) -> HitTestResult { - let (tx, rx) = single_msg_channel(); + document_id: DocumentId, + pipeline_id: Option<PipelineId>, + point: WorldPoint, + flags: HitTestFlags) + -> HitTestResult { + let (tx, rx) = channel(); self.send_frame_msg( document_id, @@ -1311,7 +1689,7 @@ impl RenderApi { /// Synchronously request an object that can perform fast hit testing queries. pub fn request_hit_tester(&self, document_id: DocumentId) -> HitTesterRequest { - let (tx, rx) = single_msg_channel(); + let (tx, rx) = channel(); self.send_frame_msg( document_id, FrameMsg::RequestHitTester(tx) @@ -1320,9 +1698,37 @@ impl RenderApi { HitTesterRequest { rx } } + /// Setup the output region in the framebuffer for a given document. + pub fn set_document_view( + &self, + document_id: DocumentId, + device_rect: DeviceIntRect, + device_pixel_ratio: f32, + ) { + self.send_scene_msg( + document_id, + SceneMsg::SetDocumentView { device_rect, device_pixel_ratio }, + ); + } + + /// Setup the output region in the framebuffer for a given document. + /// Enable copying of the output of this pipeline id to + /// an external texture for callers to consume. + pub fn enable_frame_output( + &self, + document_id: DocumentId, + pipeline_id: PipelineId, + enable: bool, + ) { + self.send_scene_msg( + document_id, + SceneMsg::EnableFrameOutput(pipeline_id, enable), + ); + } + /// pub fn get_scroll_node_state(&self, document_id: DocumentId) -> Vec<ScrollNodeState> { - let (tx, rx) = single_msg_channel(); + let (tx, rx) = channel(); self.send_frame_msg(document_id, FrameMsg::GetScrollNodeState(tx)); rx.recv().unwrap() } @@ -1331,16 +1737,16 @@ impl RenderApi { // Buckle up and see APZUpdater.cpp for more info about what this is about. #[doc(hidden)] pub fn wake_scene_builder(&self) { - self.scene_sender.send(SceneBuilderRequest::WakeUp).unwrap(); + self.send_message(ApiMsg::WakeSceneBuilder); } /// Block until a round-trip to the scene builder thread has completed. This /// ensures that any transactions (including ones deferred to the scene /// builder thread) have been processed. pub fn flush_scene_builder(&self) { - let (tx, rx) = single_msg_channel(); - self.low_priority_scene_sender.send(SceneBuilderRequest::Flush(tx)).unwrap(); - rx.recv().unwrap(); // Block until done. + let (tx, rx) = channel(); + self.send_message(ApiMsg::FlushSceneBuilder(tx)); + rx.recv().unwrap(); // block until done } /// Save a capture of the current frame state for debugging. @@ -1355,7 +1761,7 @@ impl RenderApi { // the capture we are about to load. self.flush_scene_builder(); - let (tx, rx) = unbounded_channel(); + let (tx, rx) = channel(); let msg = ApiMsg::DebugCommand(DebugCommand::LoadCapture(path, ids, tx)); self.send_message(msg); @@ -1391,57 +1797,325 @@ impl RenderApi { impl Drop for RenderApi { fn drop(&mut self) { - let msg = SceneBuilderRequest::ClearNamespace(self.namespace_id); - let _ = self.low_priority_scene_sender.send(msg); + let msg = ApiMsg::ClearNamespace(self.namespace_id); + let _ = self.api_sender.send(msg); } } +/// 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: Receiver<Arc<dyn ApiHitTester>>, +} -fn window_size_sanity_check(size: DeviceIntSize) { - // Anything bigger than this will crash later when attempting to create - // a render task. - use crate::render_task::MAX_RENDER_TASK_SIZE; - if size.width > MAX_RENDER_TASK_SIZE || size.height > MAX_RENDER_TASK_SIZE { - panic!("Attempting to create a {}x{} window/document", size.width, size.height); +impl HitTesterRequest { + /// Block until the hit tester is available and return it, consuming teh request. + pub fn resolve(self) -> Arc<dyn ApiHitTester> { + self.rx.recv().unwrap() } } -/// Collection of heap sizes, in bytes. -/// cbindgen:derive-eq=false -/// cbindgen:derive-ostream=false +/// +#[derive(Clone)] +pub struct ScrollNodeState { + /// + pub id: di::ExternalScrollId, + /// + pub scroll_offset: LayoutVector2D, +} + +/// +#[derive(Clone, Copy, Debug)] +pub enum ScrollLocation { + /// Scroll by a certain amount. + Delta(LayoutVector2D), + /// Scroll to very top of element. + Start, + /// Scroll to very bottom of element. + End, +} + +/// Represents a zoom factor. +#[derive(Clone, Copy, Debug)] +pub struct ZoomFactor(f32); + +impl ZoomFactor { + /// Construct a new zoom factor. + pub fn new(scale: f32) -> Self { + ZoomFactor(scale) + } + + /// Get the zoom factor as an untyped float. + pub fn get(self) -> f32 { + self.0 + } +} + +/// A key to identify an animated property binding. #[repr(C)] -#[allow(missing_docs)] -#[derive(AddAssign, Clone, Debug, Default)] -pub struct MemoryReport { - // - // CPU Memory. - // - pub clip_stores: usize, - pub gpu_cache_metadata: usize, - pub gpu_cache_cpu_mirror: usize, - pub render_tasks: usize, - pub hit_testers: usize, - pub fonts: usize, - pub weak_fonts: usize, - pub images: usize, - pub rasterized_blobs: usize, - pub shader_cache: usize, - pub interning: InterningMemoryReport, - pub display_list: usize, - pub upload_staging_memory: usize, - pub swgl: usize, +#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)] +pub struct PropertyBindingId { + namespace: IdNamespace, + uid: u32, +} - // - // GPU memory. - // - pub gpu_cache_textures: usize, - pub vertex_data_textures: usize, - pub render_target_textures: usize, - pub texture_cache_textures: usize, - pub texture_cache_structures: usize, - pub depth_target_textures: usize, - pub texture_upload_pbos: usize, - pub swap_chain: usize, - pub render_texture_hosts: usize, - pub upload_staging_textures: usize, +impl PropertyBindingId { + /// Constructor. + pub fn new(value: u64) -> Self { + PropertyBindingId { + namespace: IdNamespace((value >> 32) as u32), + uid: value as u32, + } + } +} + +/// A unique key that is used for connecting animated property +/// values to bindings in the display list. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub struct PropertyBindingKey<T> { + /// + pub id: PropertyBindingId, + _phantom: PhantomData<T>, +} + +/// Construct a property value from a given key and value. +impl<T: Copy> PropertyBindingKey<T> { + /// + pub fn with(self, value: T) -> PropertyValue<T> { + PropertyValue { key: self, value } + } +} + +impl<T> PropertyBindingKey<T> { + /// Constructor. + pub fn new(value: u64) -> Self { + PropertyBindingKey { + id: PropertyBindingId::new(value), + _phantom: PhantomData, + } + } +} + +/// A binding property can either be a specific value +/// (the normal, non-animated case) or point to a binding location +/// to fetch the current value from. +/// Note that Binding has also a non-animated value, the value is +/// used for the case where the animation is still in-delay phase +/// (i.e. the animation doesn't produce any animation values). +#[repr(C)] +#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] +pub enum PropertyBinding<T> { + /// Non-animated value. + Value(T), + /// Animated binding. + Binding(PropertyBindingKey<T>, T), +} + +impl<T: Default> Default for PropertyBinding<T> { + fn default() -> Self { + PropertyBinding::Value(Default::default()) + } +} + +impl<T> From<T> for PropertyBinding<T> { + fn from(value: T) -> PropertyBinding<T> { + PropertyBinding::Value(value) + } +} + +impl From<PropertyBindingKey<ColorF>> for PropertyBindingKey<ColorU> { + fn from(key: PropertyBindingKey<ColorF>) -> PropertyBindingKey<ColorU> { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From<PropertyBindingKey<ColorU>> for PropertyBindingKey<ColorF> { + fn from(key: PropertyBindingKey<ColorU>) -> PropertyBindingKey<ColorF> { + PropertyBindingKey { + id: key.id.clone(), + _phantom: PhantomData, + } + } +} + +impl From<PropertyBinding<ColorF>> for PropertyBinding<ColorU> { + fn from(value: PropertyBinding<ColorF>) -> PropertyBinding<ColorU> { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + +impl From<PropertyBinding<ColorU>> for PropertyBinding<ColorF> { + fn from(value: PropertyBinding<ColorU>) -> PropertyBinding<ColorF> { + match value { + PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), + PropertyBinding::Binding(k, v) => { + PropertyBinding::Binding(k.into(), v.into()) + } + } + } +} + +/// The current value of an animated property. This is +/// supplied by the calling code. +#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] +pub struct PropertyValue<T> { + /// + pub key: PropertyBindingKey<T>, + /// + pub value: T, +} + +/// When using `generate_frame()`, a list of `PropertyValue` structures +/// can optionally be supplied to provide the current value of any +/// animated properties. +#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)] +pub struct DynamicProperties { + /// + pub transforms: Vec<PropertyValue<LayoutTransform>>, + /// opacity + pub floats: Vec<PropertyValue<f32>>, + /// background color + pub colors: Vec<PropertyValue<ColorF>>, +} + +/// A handler to integrate WebRender with the thread that contains the `Renderer`. +pub trait RenderNotifier: Send { + /// + fn clone(&self) -> Box<dyn RenderNotifier>; + /// Wake the thread containing the `Renderer` up (after updates have been put + /// in the renderer's queue). + fn wake_up(&self); + /// Notify the thread containing the `Renderer` that a new frame is ready. + fn new_frame_ready(&self, _: DocumentId, scrolled: bool, composite_needed: bool, render_time_ns: Option<u64>); + /// A Gecko-specific notification mechanism to get some code executed on the + /// `Renderer`'s thread, mostly replaced by `NotificationHandler`. You should + /// probably use the latter instead. + fn external_event(&self, _evt: ExternalEvent) { + unimplemented!() + } + /// Notify the thread containing the `Renderer` that the render backend has been + /// shut down. + fn shut_down(&self) {} +} + +/// A stage of the rendering pipeline. +#[repr(u32)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Checkpoint { + /// + SceneBuilt, + /// + FrameBuilt, + /// + FrameTexturesUpdated, + /// + FrameRendered, + /// NotificationRequests get notified with this if they get dropped without having been + /// notified. This provides the guarantee that if a request is created it will get notified. + TransactionDropped, +} + +/// A handler to notify when a transaction reaches certain stages of the rendering +/// pipeline. +pub trait NotificationHandler : Send + Sync { + /// Entry point of the handler to implement. Invoked by WebRender. + fn notify(&self, when: Checkpoint); +} + +/// A request to notify a handler when the transaction reaches certain stages of the +/// rendering pipeline. +/// +/// The request is guaranteed to be notified once and only once, even if the transaction +/// is dropped before the requested check-point. +pub struct NotificationRequest { + handler: Option<Box<dyn NotificationHandler>>, + when: Checkpoint, +} + +impl NotificationRequest { + /// Constructor. + pub fn new(when: Checkpoint, handler: Box<dyn NotificationHandler>) -> Self { + NotificationRequest { + handler: Some(handler), + when, + } + } + + /// The specified stage at which point the handler should be notified. + pub fn when(&self) -> Checkpoint { self.when } + + /// Called by WebRender at specified stages to notify the registered handler. + pub fn notify(mut self) { + if let Some(handler) = self.handler.take() { + handler.notify(self.when); + } + } +} + +/// An object that can perform hit-testing without doing synchronous queries to +/// the RenderBackendThread. +pub trait ApiHitTester: Send + Sync { + /// Does a hit test on display items in the specified document, at the given + /// point. If a pipeline_id is specified, it is used to further restrict the + /// hit results so that only items inside that pipeline are matched. If the + /// HitTestFlags argument contains the FIND_ALL flag, then the vector of hit + /// results will contain all display items that match, ordered from front + /// to back. + fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint, flags: HitTestFlags) -> HitTestResult; +} + +impl Drop for NotificationRequest { + fn drop(&mut self) { + if let Some(ref mut handler) = self.handler { + handler.notify(Checkpoint::TransactionDropped); + } + } +} + +// This Clone impl yields an "empty" request because we don't want the requests +// to be notified twice so the request is owned by only one of the API messages +// (the original one) after the clone. +// This works in practice because the notifications requests are used for +// synchronization so we don't need to include them in the recording mechanism +// in wrench that clones the messages. +impl Clone for NotificationRequest { + fn clone(&self) -> Self { + NotificationRequest { + when: self.when, + handler: None, + } + } +} + + +bitflags! { + /// Each bit of the edge AA mask is: + /// 0, when the edge of the primitive needs to be considered for AA + /// 1, when the edge of the segment needs to be considered for AA + /// + /// *Note*: the bit values have to match the shader logic in + /// `write_transform_vertex()` function. + #[cfg_attr(feature = "serialize", derive(Serialize))] + #[cfg_attr(feature = "deserialize", derive(Deserialize))] + #[derive(MallocSizeOf)] + pub struct EdgeAaSegmentMask: u8 { + /// + const LEFT = 0x1; + /// + const TOP = 0x2; + /// + const RIGHT = 0x4; + /// + const BOTTOM = 0x8; + } } diff --git a/third_party/webrender/webrender_api/src/channel.rs b/third_party/webrender/webrender_api/src/channel.rs index 7d21c6e4339..2bc4a5f16b8 100644 --- a/third_party/webrender/webrender_api/src/channel.rs +++ b/third_party/webrender/webrender_api/src/channel.rs @@ -2,19 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::{Epoch, PipelineId}; +use crate::api::{Epoch, PipelineId}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::io::{self, Cursor, Error, ErrorKind, Read}; use std::mem; - -pub use crossbeam_channel as crossbeam; - -#[cfg(not(target_os = "windows"))] -pub use crossbeam_channel::{Sender, Receiver}; - -#[cfg(target_os = "windows")] -pub use std::sync::mpsc::{Sender, Receiver}; +use std::sync::mpsc; #[derive(Clone)] pub struct Payload { @@ -81,7 +74,7 @@ pub type PayloadSender = MsgSender<Payload>; pub type PayloadReceiver = MsgReceiver<Payload>; pub struct MsgReceiver<T> { - rx: Receiver<T>, + rx: mpsc::Receiver<T>, } impl<T> MsgReceiver<T> { @@ -89,14 +82,14 @@ impl<T> MsgReceiver<T> { self.rx.recv().map_err(|e| io::Error::new(ErrorKind::Other, e.to_string())) } - pub fn to_crossbeam_receiver(self) -> Receiver<T> { + pub fn to_mpsc_receiver(self) -> mpsc::Receiver<T> { self.rx } } #[derive(Clone)] pub struct MsgSender<T> { - tx: Sender<T>, + tx: mpsc::Sender<T>, } impl<T> MsgSender<T> { @@ -106,12 +99,12 @@ impl<T> MsgSender<T> { } pub fn payload_channel() -> Result<(PayloadSender, PayloadReceiver), Error> { - let (tx, rx) = unbounded_channel(); + let (tx, rx) = mpsc::channel(); Ok((PayloadSender { tx }, PayloadReceiver { rx })) } pub fn msg_channel<T>() -> Result<(MsgSender<T>, MsgReceiver<T>), Error> { - let (tx, rx) = unbounded_channel(); + let (tx, rx) = mpsc::channel(); Ok((MsgSender { tx }, MsgReceiver { rx })) } @@ -136,45 +129,3 @@ impl<'de, T> Deserialize<'de> for MsgSender<T> { unreachable!(); } } - -/// A create a channel intended for one-shot uses, for example the channels -/// created to block on a synchronous query and then discarded, -#[cfg(not(target_os = "windows"))] -pub fn single_msg_channel<T>() -> (Sender<T>, Receiver<T>) { - crossbeam_channel::bounded(1) -} - -/// A fast MPMC message channel that can hold a fixed number of messages. -/// -/// If the channel is full, the sender will block upon sending extra messages -/// until the receiver has consumed some messages. -/// The capacity parameter should be chosen either: -/// - high enough to avoid blocking on the common cases, -/// - or, on the contrary, using the blocking behavior as a means to prevent -/// fast producers from building up work faster than it is consumed. -#[cfg(not(target_os = "windows"))] -pub fn fast_channel<T>(capacity: usize) -> (Sender<T>, Receiver<T>) { - crossbeam_channel::bounded(capacity) -} - -/// Creates an MPMC channel that is a bit slower than the fast_channel but doesn't -/// have a limit on the number of messages held at a given time and therefore -/// doesn't block when sending. -#[cfg(not(target_os = "windows"))] -pub use crossbeam_channel::unbounded as unbounded_channel; - - -#[cfg(target_os = "windows")] -pub fn fast_channel<T>(_cap: usize) -> (Sender<T>, Receiver<T>) { - std::sync::mpsc::channel() -} - -#[cfg(target_os = "windows")] -pub fn unbounded_channel<T>() -> (Sender<T>, Receiver<T>) { - std::sync::mpsc::channel() -} - -#[cfg(target_os = "windows")] -pub fn single_msg_channel<T>() -> (Sender<T>, Receiver<T>) { - std::sync::mpsc::channel() -} diff --git a/third_party/webrender/webrender_api/src/display_item.rs b/third_party/webrender/webrender_api/src/display_item.rs index 5c425b9ed17..e6b8e036e46 100644 --- a/third_party/webrender/webrender_api/src/display_item.rs +++ b/third_party/webrender/webrender_api/src/display_item.rs @@ -2,16 +2,15 @@ * 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 euclid::{SideOffsets2D, Angle}; +use euclid::SideOffsets2D; use peek_poke::PeekPoke; use std::ops::Not; // local imports use crate::font; -use crate::{PipelineId, PropertyBinding}; +use crate::api::{PipelineId, PropertyBinding}; use crate::color::ColorF; use crate::image::{ColorDepth, ImageKey}; use crate::units::*; -use std::hash::{Hash, Hasher}; // ****************************************************************** // * NOTE: some of these structs have an "IMPLICIT" comment. * @@ -48,10 +47,6 @@ bitflags! { /// compositor surface under certain (implementation specific) conditions. This /// is typically used for large videos, and canvas elements. const PREFER_COMPOSITOR_SURFACE = 1 << 3; - /// If set, this primitive can be passed directly to the compositor via its - /// ExternalImageId, and the compositor will use the native image directly. - /// Used as a further extension on top of PREFER_COMPOSITOR_SURFACE. - const SUPPORTS_EXTERNAL_COMPOSITOR_SURFACE = 1 << 4; } } @@ -73,6 +68,10 @@ pub struct CommonItemProperties { pub clip_id: ClipId, /// The coordinate-space the item is in (yes, it can be really granular) pub spatial_id: SpatialId, + /// Opaque bits for our clients to use for hit-testing. This is the most + /// dubious "common" field, but because it's an Option, it usually only + /// wastes a single byte (for None). + pub hit_info: Option<ItemTag>, /// Various flags describing properties of this primitive. pub flags: PrimitiveFlags, } @@ -87,6 +86,7 @@ impl CommonItemProperties { clip_rect, spatial_id: space_and_clip.spatial_id, clip_id: space_and_clip.clip_id, + hit_info: None, flags: PrimitiveFlags::default(), } } @@ -155,7 +155,6 @@ pub enum DisplayItem { SetFilterOps, SetFilterData, SetFilterPrimitives, - SetPoints, // These marker items terminate a scope introduced by a previous item. PopReferenceFrame, @@ -204,7 +203,6 @@ pub enum DebugDisplayItem { SetFilterOps(Vec<FilterOp>), SetFilterData(FilterData), SetFilterPrimitives(Vec<FilterPrimitive>), - SetPoints(Vec<LayoutPoint>), PopReferenceFrame, PopStackingContext, @@ -216,8 +214,7 @@ pub struct ImageMaskClipDisplayItem { pub id: ClipId, pub parent_space_and_clip: SpaceAndClipInfo, pub image_mask: ImageMask, - pub fill_rule: FillRule, -} // IMPLICIT points: Vec<LayoutPoint> +} #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct RectClipDisplayItem { @@ -309,7 +306,7 @@ pub struct ScrollFrameDisplayItem { pub content_rect: LayoutRect, pub clip_rect: LayoutRect, pub parent_space_and_clip: SpaceAndClipInfo, - pub external_id: ExternalScrollId, + pub external_id: Option<ExternalScrollId>, pub scroll_sensitivity: ScrollSensitivity, /// The amount this scrollframe has already been scrolled by, in the caller. /// This means that all the display items that are inside the scrollframe @@ -341,7 +338,6 @@ pub struct ClearRectangleDisplayItem { #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct HitTestDisplayItem { pub common: CommonItemProperties, - pub tag: ItemTag, } #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] @@ -627,15 +623,6 @@ pub struct Gradient { pub extend_mode: ExtendMode, } // IMPLICIT: stops: Vec<GradientStop> -impl Gradient { - pub fn is_valid(&self) -> bool { - self.start_point.x.is_finite() && - self.start_point.y.is_finite() && - self.end_point.x.is_finite() && - self.end_point.y.is_finite() - } -} - /// The area #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct GradientDisplayItem { @@ -668,15 +655,6 @@ pub struct RadialGradient { pub extend_mode: ExtendMode, } // IMPLICIT stops: Vec<GradientStop> -impl RadialGradient { - pub fn is_valid(&self) -> bool { - self.center.x.is_finite() && - self.center.y.is_finite() && - self.start_offset.is_finite() && - self.end_offset.is_finite() - } -} - #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct ConicGradient { pub center: LayoutPoint, @@ -686,16 +664,6 @@ pub struct ConicGradient { pub extend_mode: ExtendMode, } // IMPLICIT stops: Vec<GradientStop> -impl ConicGradient { - pub fn is_valid(&self) -> bool { - self.center.x.is_finite() && - self.center.y.is_finite() && - self.angle.is_finite() && - self.start_offset.is_finite() && - self.end_offset.is_finite() - } -} - /// Just an abstraction for bundling up a bunch of clips into a "super clip". #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct ClipChainItem { @@ -743,87 +711,23 @@ pub struct ReferenceFrameDisplayListItem { #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] pub enum ReferenceFrameKind { + /// Zoom reference frames must be a scale + translation only + Zoom, /// A normal transform matrix, may contain perspective (the CSS transform property) - Transform { - /// Optionally marks the transform as only ever having a simple 2D scale or translation, - /// allowing for optimizations. - is_2d_scale_translation: bool, - /// Marks that the transform should be snapped. Used for transforms which animate in - /// response to scrolling, eg for zooming or dynamic toolbar fixed-positioning. - should_snap: bool, - }, + Transform, /// A perspective transform, that optionally scrolls relative to a specific scroll node Perspective { scrolling_relative_to: Option<ExternalScrollId>, } } -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] -pub enum Rotation { - Degree0, - Degree90, - Degree180, - Degree270, -} - -impl Rotation { - pub fn to_matrix( - &self, - size: LayoutSize, - ) -> LayoutTransform { - let (shift_center_to_origin, angle) = match self { - Rotation::Degree0 => { - (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(0.)) - }, - Rotation::Degree90 => { - (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(90.)) - }, - Rotation::Degree180 => { - (LayoutTransform::translation(-size.width / 2., -size.height / 2., 0.), Angle::degrees(180.)) - }, - Rotation::Degree270 => { - (LayoutTransform::translation(-size.height / 2., -size.width / 2., 0.), Angle::degrees(270.)) - }, - }; - let shift_origin_to_center = LayoutTransform::translation(size.width / 2., size.height / 2., 0.); - - shift_center_to_origin - .then(&LayoutTransform::rotation(0., 0., 1.0, angle)) - .then(&shift_origin_to_center) - } -} - -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] -pub enum ReferenceTransformBinding { - /// Standard reference frame which contains a precomputed transform. - Static { - binding: PropertyBinding<LayoutTransform>, - }, - /// Computed reference frame which dynamically calculates the transform - /// based on the given parameters. The reference is the content size of - /// the parent iframe, which is affected by snapping. - Computed { - scale_from: Option<LayoutSize>, - vertical_flip: bool, - rotation: Rotation, - }, -} - -impl Default for ReferenceTransformBinding { - fn default() -> Self { - ReferenceTransformBinding::Static { - binding: Default::default(), - } - } -} - #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct ReferenceFrame { pub kind: ReferenceFrameKind, pub transform_style: TransformStyle, /// The transform matrix, either the perspective matrix or the transform /// matrix. - pub transform: ReferenceTransformBinding, + pub transform: PropertyBinding<LayoutTransform>, pub id: SpatialId, } @@ -858,7 +762,7 @@ pub enum TransformStyle { /// when we want to cache the output, and performance is /// important. Note that this is a performance hint only, /// which WR may choose to ignore. -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, MallocSizeOf, Serialize, PeekPoke)] +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize, PeekPoke)] #[repr(u8)] pub enum RasterSpace { // Rasterize in local-space, applying supplied scale to primitives. @@ -881,23 +785,6 @@ impl RasterSpace { } } -impl Eq for RasterSpace {} - -impl Hash for RasterSpace { - fn hash<H: Hasher>(&self, state: &mut H) { - match self { - RasterSpace::Screen => { - 0.hash(state); - } - RasterSpace::Local(scale) => { - // Note: this is inconsistent with the Eq impl for -0.0 (don't care). - 1.hash(state); - scale.to_bits().hash(state); - } - } - } -} - bitflags! { #[repr(C)] #[derive(Deserialize, MallocSizeOf, Serialize, PeekPoke)] @@ -1024,8 +911,7 @@ impl FloodPrimitive { #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)] pub struct BlurPrimitive { pub input: FilterPrimitiveInput, - pub width: f32, - pub height: f32, + pub radius: f32, } #[repr(C)] @@ -1152,7 +1038,7 @@ pub enum FilterOp { /// Filter that does no transformation of the colors, needed for /// debug purposes only. Identity, - Blur(f32, f32), + Blur(f32), Brightness(f32), Contrast(f32), Grayscale(f32), @@ -1344,7 +1230,6 @@ pub enum YuvColorSpace { Rec601 = 0, Rec709 = 1, Rec2020 = 2, - Identity = 3, // aka RGB as per ISO/IEC 23091-2:2019 } #[repr(u8)] @@ -1519,34 +1404,6 @@ impl ComplexClipRegion { } } -pub const POLYGON_CLIP_VERTEX_MAX: usize = 16; - -#[repr(u8)] -#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)] -pub enum FillRule { - Nonzero = 0x1, // Behaves as the SVG fill-rule definition for nonzero. - Evenodd = 0x2, // Behaves as the SVG fill-rule definition for evenodd. -} - -impl From<u8> for FillRule { - fn from(fill_rule: u8) -> Self { - match fill_rule { - 0x1 => FillRule::Nonzero, - 0x2 => FillRule::Evenodd, - _ => panic!("Unexpected FillRule value."), - } - } -} - -impl From<FillRule> for u8 { - fn from(fill_rule: FillRule) -> Self { - match fill_rule { - FillRule::Nonzero => 0x1, - FillRule::Evenodd => 0x2, - } - } -} - #[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize, PeekPoke)] pub struct ClipChainId(pub u64, pub PipelineId); @@ -1674,7 +1531,6 @@ impl DisplayItem { DisplayItem::SetFilterOps => "set_filter_ops", DisplayItem::SetFilterData => "set_filter_data", DisplayItem::SetFilterPrimitives => "set_filter_primitives", - DisplayItem::SetPoints => "set_points", DisplayItem::RadialGradient(..) => "radial_gradient", DisplayItem::Rectangle(..) => "rectangle", DisplayItem::ScrollFrame(..) => "scroll_frame", @@ -1716,13 +1572,8 @@ impl_default_for_enums! { FilterOp => Identity, ComponentTransferFuncType => Identity, ClipMode => Clip, - FillRule => Nonzero, ClipId => ClipId::invalid(), - ReferenceFrameKind => Transform { - is_2d_scale_translation: false, - should_snap: false, - }, - Rotation => Degree0, + ReferenceFrameKind => Transform, TransformStyle => Flat, RasterSpace => Local(f32::default()), MixBlendMode => Normal, diff --git a/third_party/webrender/webrender_api/src/display_list.rs b/third_party/webrender/webrender_api/src/display_list.rs index e2498c5c194..0680a9875d3 100644 --- a/third_party/webrender/webrender_api/src/display_list.rs +++ b/third_party/webrender/webrender_api/src/display_list.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use euclid::SideOffsets2D; -use peek_poke::{ensure_red_zone, peek_from_slice, poke_extend_vec, strip_red_zone}; +use peek_poke::{ensure_red_zone, peek_from_slice, poke_extend_vec}; use peek_poke::{poke_inplace_slice, poke_into_vec, Poke}; #[cfg(feature = "deserialize")] use serde::de::Deserializer; @@ -20,7 +20,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; // local imports use crate::display_item as di; use crate::display_item_cache::*; -use crate::{PipelineId, PropertyBinding}; +use crate::api::{PipelineId, PropertyBinding}; use crate::gradient_builder::GradientBuilder; use crate::color::ColorF; use crate::font::{FontInstanceKey, GlyphInstance, GlyphOptions}; @@ -115,18 +115,6 @@ pub struct BuiltDisplayList { descriptor: BuiltDisplayListDescriptor, } -#[repr(C)] -#[derive(Copy, Clone, Deserialize, Serialize)] -pub enum GeckoDisplayListType { - None, - Partial(f64), - Full(f64), -} - -impl Default for GeckoDisplayListType { - fn default() -> Self { GeckoDisplayListType::None } -} - /// Describes the memory layout of a display list. /// /// A display list consists of some number of display list items, followed by a number of display @@ -134,8 +122,6 @@ impl Default for GeckoDisplayListType { #[repr(C)] #[derive(Copy, Clone, Default, Deserialize, Serialize)] pub struct BuiltDisplayListDescriptor { - /// Gecko specific information about the display list. - gecko_display_list_type: GeckoDisplayListType, /// The first IPC time stamp: before any work has been done builder_start_time: u64, /// The second IPC time stamp: after serialization @@ -182,10 +168,6 @@ impl DisplayListWithCache { self.display_list.descriptor() } - pub fn times(&self) -> (u64, u64, u64) { - self.display_list.times() - } - pub fn data(&self) -> &[u8] { self.display_list.data() } @@ -239,7 +221,6 @@ pub struct BuiltDisplayListIter<'a> { cur_filter_primitives: ItemRange<'a, di::FilterPrimitive>, cur_clip_chain_items: ItemRange<'a, di::ClipId>, cur_complex_clip: ItemRange<'a, di::ComplexClipRegion>, - cur_points: ItemRange<'a, LayoutPoint>, peeking: Peek, /// Should just be initialized but never populated in release builds debug_stats: DebugStats, @@ -282,7 +263,7 @@ impl DebugStats { /// Logs the stats for the given serialized slice #[cfg(feature = "display_list_stats")] - fn log_slice<T: Copy + Default + peek_poke::Peek>( + fn log_slice<T: Peek>( &mut self, slice_name: &'static str, range: &ItemRange<T>, @@ -337,10 +318,6 @@ impl<'a, 'b> DisplayItemRef<'a, 'b> { self.iter.cur_complex_clip } - pub fn points(&self) -> ItemRange<LayoutPoint> { - self.iter.cur_points - } - pub fn glyphs(&self) -> ItemRange<GlyphInstance> { self.iter.glyphs() } @@ -414,14 +391,6 @@ impl BuiltDisplayList { ) } - pub fn gecko_display_list_stats(&self) -> (f64, bool) { - match self.descriptor.gecko_display_list_type { - GeckoDisplayListType::Full(duration) => (duration, true), - GeckoDisplayListType::Partial(duration) => (duration, false), - _ => (0.0, false) - } - } - pub fn total_clip_nodes(&self) -> usize { self.descriptor.total_clip_nodes } @@ -503,9 +472,6 @@ impl BuiltDisplayList { Real::SetGradientStops => Debug::SetGradientStops( item.iter.cur_stops.iter().collect() ), - Real::SetPoints => Debug::SetPoints( - item.iter.cur_points.iter().collect() - ), Real::RectClip(v) => Debug::RectClip(v), Real::RoundedRectClip(v) => Debug::RoundedRectClip(v), Real::ImageMaskClip(v) => Debug::ImageMaskClip(v), @@ -575,7 +541,6 @@ impl<'a> BuiltDisplayListIter<'a> { cur_filter_primitives: ItemRange::default(), cur_clip_chain_items: ItemRange::default(), cur_complex_clip: ItemRange::default(), - cur_points: ItemRange::default(), peeking: Peek::NotPeeking, debug_stats: DebugStats { last_addr: data.as_ptr() as usize, @@ -644,7 +609,6 @@ impl<'a> BuiltDisplayListIter<'a> { self.cur_stops = ItemRange::default(); self.cur_complex_clip = ItemRange::default(); self.cur_clip_chain_items = ItemRange::default(); - self.cur_points = ItemRange::default(); self.cur_filters = ItemRange::default(); self.cur_filter_primitives = ItemRange::default(); self.cur_filter_data.clear(); @@ -655,8 +619,7 @@ impl<'a> BuiltDisplayListIter<'a> { SetGradientStops | SetFilterOps | SetFilterData | - SetFilterPrimitives | - SetPoints => { + SetFilterPrimitives => { // These are marker items for populating other display items, don't yield them. continue; } @@ -718,10 +681,6 @@ impl<'a> BuiltDisplayListIter<'a> { self.cur_filter_primitives = skip_slice::<di::FilterPrimitive>(&mut self.data); self.debug_stats.log_slice("set_filter_primitives.primitives", &self.cur_filter_primitives); } - SetPoints => { - self.cur_points = skip_slice::<LayoutPoint>(&mut self.data); - self.debug_stats.log_slice("set_points.points", &self.cur_points); - } ClipChain(_) => { self.cur_clip_chain_items = skip_slice::<di::ClipId>(&mut self.data); self.debug_stats.log_slice("clip_chain.clip_ids", &self.cur_clip_chain_items); @@ -935,10 +894,6 @@ impl<'de> Deserialize<'de> for BuiltDisplayList { DisplayListBuilder::push_iter_impl(&mut temp, stops); Real::SetGradientStops }, - Debug::SetPoints(points) => { - DisplayListBuilder::push_iter_impl(&mut temp, points); - Real::SetPoints - }, Debug::RectClip(v) => Real::RectClip(v), Debug::RoundedRectClip(v) => Real::RoundedRectClip(v), Debug::ImageMaskClip(v) => Real::ImageMaskClip(v), @@ -976,7 +931,6 @@ impl<'de> Deserialize<'de> for BuiltDisplayList { Ok(BuiltDisplayList { data, descriptor: BuiltDisplayListDescriptor { - gecko_display_list_type: GeckoDisplayListType::None, builder_start_time: 0, builder_finish_time: 1, send_start_time: 1, @@ -1022,6 +976,9 @@ pub struct DisplayListBuilder { next_clip_chain_id: u64, builder_start_time: u64, + /// The size of the content of this display list. This is used to allow scrolling + /// outside the bounds of the display list items themselves. + content_size: LayoutSize, save_state: Option<SaveState>, cache_size: usize, @@ -1029,12 +986,13 @@ pub struct DisplayListBuilder { } impl DisplayListBuilder { - pub fn new(pipeline_id: PipelineId) -> Self { - Self::with_capacity(pipeline_id, 0) + pub fn new(pipeline_id: PipelineId, content_size: LayoutSize) -> Self { + Self::with_capacity(pipeline_id, content_size, 0) } pub fn with_capacity( pipeline_id: PipelineId, + content_size: LayoutSize, capacity: usize, ) -> Self { let start_time = precise_time_ns(); @@ -1051,12 +1009,18 @@ impl DisplayListBuilder { next_spatial_index: FIRST_SPATIAL_NODE_INDEX, next_clip_chain_id: 0, builder_start_time: start_time, + content_size, save_state: None, cache_size: 0, serialized_content_buffer: None, } } + /// Return the content size for this display list + pub fn content_size(&self) -> LayoutSize { + self.content_size + } + /// Saves the current display list state, so it may be `restore()`'d. /// /// # Conditions: @@ -1111,15 +1075,11 @@ impl DisplayListBuilder { W: Write { let mut temp = BuiltDisplayList::default(); - ensure_red_zone::<di::DisplayItem>(&mut self.data); - temp.descriptor.extra_data_offset = self.data.len(); mem::swap(&mut temp.data, &mut self.data); let mut index: usize = 0; { - let mut cache = DisplayItemCache::new(); - cache.update(&temp); - let mut iter = temp.iter_with_cache(&cache); + let mut iter = temp.iter(); while let Some(item) = iter.next_raw() { if index >= range.start.unwrap_or(0) && range.end.map_or(true, |e| index < e) { writeln!(sink, "{}{:?}", " ".repeat(indent), item.item()).unwrap(); @@ -1129,7 +1089,6 @@ impl DisplayListBuilder { } self.data = temp.data; - strip_red_zone::<di::DisplayItem>(&mut self.data); index } @@ -1276,11 +1235,9 @@ impl DisplayListBuilder { pub fn push_hit_test( &mut self, common: &di::CommonItemProperties, - tag: di::ItemTag, ) { let item = di::DisplayItem::HitTest(di::HitTestDisplayItem { common: *common, - tag, }); self.push_item(&item); } @@ -1577,9 +1534,7 @@ impl DisplayListBuilder { origin, reference_frame: di::ReferenceFrame { transform_style, - transform: di::ReferenceTransformBinding::Static { - binding: transform, - }, + transform, kind, id, }, @@ -1589,38 +1544,6 @@ impl DisplayListBuilder { id } - pub fn push_computed_frame( - &mut self, - origin: LayoutPoint, - parent_spatial_id: di::SpatialId, - scale_from: Option<LayoutSize>, - vertical_flip: bool, - rotation: di::Rotation, - ) -> di::SpatialId { - let id = self.generate_spatial_index(); - - let item = di::DisplayItem::PushReferenceFrame(di::ReferenceFrameDisplayListItem { - parent_spatial_id, - origin, - reference_frame: di::ReferenceFrame { - transform_style: di::TransformStyle::Flat, - transform: di::ReferenceTransformBinding::Computed { - scale_from, - vertical_flip, - rotation, - }, - kind: di::ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - }, - id, - }, - }); - - self.push_item(&item); - id - } - pub fn pop_reference_frame(&mut self) { self.push_item(&di::DisplayItem::PopReferenceFrame); } @@ -1773,7 +1696,7 @@ impl DisplayListBuilder { pub fn define_scroll_frame( &mut self, parent_space_and_clip: &di::SpaceAndClipInfo, - external_id: di::ExternalScrollId, + external_id: Option<di::ExternalScrollId>, content_rect: LayoutRect, clip_rect: LayoutRect, scroll_sensitivity: di::ScrollSensitivity, @@ -1819,25 +1742,14 @@ impl DisplayListBuilder { &mut self, parent_space_and_clip: &di::SpaceAndClipInfo, image_mask: di::ImageMask, - points: &[LayoutPoint], - fill_rule: di::FillRule, ) -> di::ClipId { let id = self.generate_clip_index(); let item = di::DisplayItem::ImageMaskClip(di::ImageMaskClipDisplayItem { id, parent_space_and_clip: *parent_space_and_clip, image_mask, - fill_rule, }); - // We only need to supply points if there are at least 3, which is the - // minimum to specify a polygon. BuiltDisplayListIter.next ensures that points - // are cleared between processing other display items, so we'll correctly get - // zero points when no SetPoints item has been pushed. - if points.len() >= 3 { - self.push_item(&di::DisplayItem::SetPoints); - self.push_iter(points); - } self.push_item(&item); id } @@ -2016,7 +1928,7 @@ impl DisplayListBuilder { self.cache_size = cache_size; } - pub fn finalize(mut self) -> (PipelineId, BuiltDisplayList) { + pub fn finalize(mut self) -> (PipelineId, LayoutSize, BuiltDisplayList) { assert!(self.save_state.is_none(), "Finalized DisplayListBuilder with a pending save"); if let Some(content) = self.serialized_content_buffer.take() { @@ -2039,9 +1951,9 @@ impl DisplayListBuilder { let end_time = precise_time_ns(); ( self.pipeline_id, + self.content_size, BuiltDisplayList { descriptor: BuiltDisplayListDescriptor { - gecko_display_list_type: GeckoDisplayListType::None, builder_start_time: self.builder_start_time, builder_finish_time: end_time, send_start_time: end_time, diff --git a/third_party/webrender/webrender_api/src/font.rs b/third_party/webrender/webrender_api/src/font.rs index 3052db8f9e1..7f24736f09e 100644 --- a/third_party/webrender/webrender_api/src/font.rs +++ b/third_party/webrender/webrender_api/src/font.rs @@ -15,11 +15,10 @@ use std::cmp::Ordering; use std::hash::{Hash, Hasher}; #[cfg(not(target_os = "macos"))] use std::path::PathBuf; -use std::sync::{Arc, RwLock, RwLockReadGuard}; +use std::sync::{Arc, RwLock, RwLockReadGuard, mpsc::Sender}; use std::collections::HashMap; // local imports -use crate::IdNamespace; -use crate::channel::Sender; +use crate::api::IdNamespace; use crate::color::ColorU; use crate::units::LayoutPoint; diff --git a/third_party/webrender/webrender_api/src/gradient_builder.rs b/third_party/webrender/webrender_api/src/gradient_builder.rs index 6347396f791..883acbafa38 100644 --- a/third_party/webrender/webrender_api/src/gradient_builder.rs +++ b/third_party/webrender/webrender_api/src/gradient_builder.rs @@ -132,9 +132,7 @@ impl GradientBuilder { let first = *stops.first().unwrap(); let last = *stops.last().unwrap(); - // Express the assertion so that if one of the offsets is NaN, we don't panic - // and instead take the branch that handles degenerate gradients. - assert!(!(first.offset > last.offset)); + assert!(first.offset <= last.offset); let stops_delta = last.offset - first.offset; diff --git a/third_party/webrender/webrender_api/src/image.rs b/third_party/webrender/webrender_api/src/image.rs index 23c660b647e..4a664bddcfd 100644 --- a/third_party/webrender/webrender_api/src/image.rs +++ b/third_party/webrender/webrender_api/src/image.rs @@ -9,7 +9,7 @@ use peek_poke::PeekPoke; use std::ops::{Add, Sub}; use std::sync::Arc; // local imports -use crate::{IdNamespace, TileSize}; +use crate::api::{IdNamespace, PipelineId, TileSize}; use crate::display_item::ImageRendering; use crate::font::{FontInstanceKey, FontInstanceData, FontKey, FontTemplate}; use crate::units::*; @@ -100,24 +100,38 @@ pub trait ExternalImageHandler { fn unlock(&mut self, key: ExternalImageId, channel_index: u8); } +/// Allows callers to receive a texture with the contents of a specific +/// pipeline copied to it. +pub trait OutputImageHandler { + /// Return the native texture handle and the size of the texture. + fn lock(&mut self, pipeline_id: PipelineId) -> Option<(u32, FramebufferIntSize)>; + /// Unlock will only be called if the lock() call succeeds, when WR has issued + /// the GL commands to copy the output to the texture handle. + fn unlock(&mut self, pipeline_id: PipelineId); +} + /// Specifies the type of texture target in driver terms. #[repr(u8)] -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] -pub enum ImageBufferKind { +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +pub enum TextureTarget { /// Standard texture. This maps to GL_TEXTURE_2D in OpenGL. - Texture2D = 0, + Default = 0, + /// Array texture. This maps to GL_TEXTURE_2D_ARRAY in OpenGL. See + /// https://www.khronos.org/opengl/wiki/Array_Texture for background + /// on Array textures. + Array = 1, /// Rectangle texture. This maps to GL_TEXTURE_RECTANGLE in OpenGL. This /// is similar to a standard texture, with a few subtle differences /// (no mipmaps, non-power-of-two dimensions, different coordinate space) /// that make it useful for representing the kinds of textures we use /// in WebRender. See https://www.khronos.org/opengl/wiki/Rectangle_Texture /// for background on Rectangle textures. - TextureRect = 1, + Rect = 2, /// External texture. This maps to GL_TEXTURE_EXTERNAL_OES in OpenGL, which /// is an extension. This is used for image formats that OpenGL doesn't /// understand, particularly YUV. See /// https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image_external.txt - TextureExternal = 2, + External = 3, } /// Storage format identifier for externally-managed images. @@ -125,7 +139,7 @@ pub enum ImageBufferKind { #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Serialize, Deserialize)] pub enum ExternalImageType { /// The image is texture-backed. - TextureHandle(ImageBufferKind), + TextureHandle(TextureTarget), /// The image is heap-allocated by the embedding. Buffer, } @@ -326,9 +340,10 @@ pub enum ImageData { } mod serde_image_data_raw { - use serde::{Deserializer, Serializer}; use serde_bytes; + use std::sync::Arc; + use serde::{Deserializer, Serializer}; pub fn serialize<S: Serializer>(bytes: &Arc<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error> { serde_bytes::serialize(bytes.as_slice(), serializer) diff --git a/third_party/webrender/webrender/src/image_tiling.rs b/third_party/webrender/webrender_api/src/image_tiling.rs index 0d003ccfef9..8fdc82ef24d 100644 --- a/third_party/webrender/webrender/src/image_tiling.rs +++ b/third_party/webrender/webrender_api/src/image_tiling.rs @@ -2,9 +2,8 @@ * 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 crate::api::TileSize; -use crate::api::units::*; -use crate::segment::EdgeAaSegmentMask; +use crate::{TileSize, EdgeAaSegmentMask}; +use crate::units::*; use euclid::{point2, size2}; use std::i32; use std::ops::Range; @@ -93,6 +92,9 @@ pub fn repetitions( visible_rect: &LayoutRect, stride: LayoutSize, ) -> RepetitionIterator { + assert!(stride.width > 0.0); + assert!(stride.height > 0.0); + let visible_rect = match prim_rect.intersection(&visible_rect) { Some(rect) => rect, None => { @@ -109,9 +111,6 @@ pub fn repetitions( } }; - assert!(stride.width > 0.0); - assert!(stride.height > 0.0); - let nx = if visible_rect.origin.x > prim_rect.origin.x { f32::floor((visible_rect.origin.x - prim_rect.origin.x) / stride.width) } else { diff --git a/third_party/webrender/webrender_api/src/lib.rs b/third_party/webrender/webrender_api/src/lib.rs index f5cda1fbf65..848f4740c90 100644 --- a/third_party/webrender/webrender_api/src/lib.rs +++ b/third_party/webrender/webrender_api/src/lib.rs @@ -15,21 +15,25 @@ #![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp, clippy::too_many_arguments))] #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal, clippy::new_without_default))] -pub use crossbeam_channel; -pub use euclid; - #[macro_use] extern crate bitflags; #[cfg(feature = "nightly")] -extern crate core; +use core; +#[cfg(target_os = "macos")] +use core_foundation; +#[cfg(target_os = "macos")] +use core_graphics; +#[macro_use] +extern crate derive_more; +pub use euclid; #[macro_use] extern crate malloc_size_of_derive; #[macro_use] extern crate serde_derive; use malloc_size_of; -use peek_poke; +mod api; pub mod channel; mod color; mod display_item; @@ -38,8 +42,13 @@ mod display_list; mod font; mod gradient_builder; mod image; +mod resources; pub mod units; +#[doc(hidden)] +pub mod image_tiling; + +pub use crate::api::*; pub use crate::color::*; pub use crate::display_item::*; pub use crate::display_item_cache::DisplayItemCache; @@ -47,619 +56,4 @@ pub use crate::display_list::*; pub use crate::font::*; pub use crate::gradient_builder::*; pub use crate::image::*; - -use crate::units::*; -use crate::channel::Receiver; -use std::marker::PhantomData; -use std::sync::Arc; -use std::os::raw::c_void; -use peek_poke::PeekPoke; - -/// Width and height in device pixels of image tiles. -pub type TileSize = u16; - -/// Various settings that the caller can select based on desired tradeoffs -/// between rendering quality and performance / power usage. -#[derive(Copy, Clone, Deserialize, Serialize)] -pub struct QualitySettings { - /// If true, disable creating separate picture cache slices when the - /// scroll root changes. This gives maximum opportunity to find an - /// opaque background, which enables subpixel AA. However, it is - /// usually significantly more expensive to render when scrolling. - pub force_subpixel_aa_where_possible: bool, -} - -impl Default for QualitySettings { - fn default() -> Self { - QualitySettings { - // Prefer performance over maximum subpixel AA quality, since WR - // already enables subpixel AA in more situations than other browsers. - force_subpixel_aa_where_possible: false, - } - } -} - -/// An epoch identifies the state of a pipeline in time. -/// -/// This is mostly used as a synchronization mechanism to observe how/when particular pipeline -/// updates propagate through WebRender and are applied at various stages. -#[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] -pub struct Epoch(pub u32); - -impl Epoch { - /// Magic invalid epoch value. - pub fn invalid() -> Epoch { - Epoch(u32::MAX) - } -} - -/// ID namespaces uniquely identify different users of WebRender's API. -/// -/// For example in Gecko each content process uses a separate id namespace. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Eq, MallocSizeOf, PartialEq, Hash, Ord, PartialOrd, PeekPoke)] -#[derive(Deserialize, Serialize)] -pub struct IdNamespace(pub u32); - -/// A key uniquely identifying a WebRender document. -/// -/// Instances can manage one or several documents (using the same render backend thread). -/// Each document will internally correspond to a single scene, and scenes are made of -/// one or several pipelines. -#[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] -pub struct DocumentId { - /// - pub namespace_id: IdNamespace, - /// - pub id: u32, -} - -impl DocumentId { - /// - pub fn new(namespace_id: IdNamespace, id: u32) -> Self { - DocumentId { - namespace_id, - id, - } - } - - /// - pub const INVALID: DocumentId = DocumentId { namespace_id: IdNamespace(0), id: 0 }; -} - -/// This type carries no valuable semantics for WR. However, it reflects the fact that -/// clients (Servo) may generate pipelines by different semi-independent sources. -/// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`. -/// Having this extra Id field enables them to generate `PipelineId` without collision. -pub type PipelineSourceId = u32; - -/// From the point of view of WR, `PipelineId` is completely opaque and generic as long as -/// it's clonable, serializable, comparable, and hashable. -#[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] -pub struct PipelineId(pub PipelineSourceId, pub u32); - -impl Default for PipelineId { - fn default() -> Self { - PipelineId::dummy() - } -} - -impl PipelineId { - /// - pub fn dummy() -> Self { - PipelineId(!0, !0) - } -} - - -/// An opaque pointer-sized value. -#[repr(C)] -#[derive(Clone)] -pub struct ExternalEvent { - raw: usize, -} - -unsafe impl Send for ExternalEvent {} - -impl ExternalEvent { - /// Creates the event from an opaque pointer-sized value. - pub fn from_raw(raw: usize) -> Self { - ExternalEvent { raw } - } - /// Consumes self to make it obvious that the event should be forwarded only once. - pub fn unwrap(self) -> usize { - self.raw - } -} - -/// Describe whether or not scrolling should be clamped by the content bounds. -#[derive(Clone, Deserialize, Serialize)] -pub enum ScrollClamping { - /// - ToContentBounds, - /// - NoClamping, -} - -/// A handler to integrate WebRender with the thread that contains the `Renderer`. -pub trait RenderNotifier: Send { - /// - fn clone(&self) -> Box<dyn RenderNotifier>; - /// Wake the thread containing the `Renderer` up (after updates have been put - /// in the renderer's queue). - fn wake_up( - &self, - composite_needed: bool, - ); - /// Notify the thread containing the `Renderer` that a new frame is ready. - fn new_frame_ready(&self, _: DocumentId, scrolled: bool, composite_needed: bool, render_time_ns: Option<u64>); - /// A Gecko-specific notification mechanism to get some code executed on the - /// `Renderer`'s thread, mostly replaced by `NotificationHandler`. You should - /// probably use the latter instead. - fn external_event(&self, _evt: ExternalEvent) { - unimplemented!() - } - /// Notify the thread containing the `Renderer` that the render backend has been - /// shut down. - fn shut_down(&self) {} -} - -/// A stage of the rendering pipeline. -#[repr(u32)] -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Checkpoint { - /// - SceneBuilt, - /// - FrameBuilt, - /// - FrameTexturesUpdated, - /// - FrameRendered, - /// NotificationRequests get notified with this if they get dropped without having been - /// notified. This provides the guarantee that if a request is created it will get notified. - TransactionDropped, -} - -/// A handler to notify when a transaction reaches certain stages of the rendering -/// pipeline. -pub trait NotificationHandler : Send + Sync { - /// Entry point of the handler to implement. Invoked by WebRender. - fn notify(&self, when: Checkpoint); -} - -/// A request to notify a handler when the transaction reaches certain stages of the -/// rendering pipeline. -/// -/// The request is guaranteed to be notified once and only once, even if the transaction -/// is dropped before the requested check-point. -pub struct NotificationRequest { - handler: Option<Box<dyn NotificationHandler>>, - when: Checkpoint, -} - -impl NotificationRequest { - /// Constructor. - pub fn new(when: Checkpoint, handler: Box<dyn NotificationHandler>) -> Self { - NotificationRequest { - handler: Some(handler), - when, - } - } - - /// The specified stage at which point the handler should be notified. - pub fn when(&self) -> Checkpoint { self.when } - - /// Called by WebRender at specified stages to notify the registered handler. - pub fn notify(mut self) { - if let Some(handler) = self.handler.take() { - handler.notify(self.when); - } - } -} - -/// An object that can perform hit-testing without doing synchronous queries to -/// the RenderBackendThread. -pub trait ApiHitTester: Send + Sync { - /// Does a hit test on display items in the specified document, at the given - /// point. If a pipeline_id is specified, it is used to further restrict the - /// hit results so that only items inside that pipeline are matched. The vector - /// of hit results will contain all display items that match, ordered from - /// front to back. - fn hit_test(&self, pipeline_id: Option<PipelineId>, point: WorldPoint, flags: HitTestFlags) -> HitTestResult; -} - -/// 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 { - #[doc(hidden)] - pub rx: Receiver<Arc<dyn ApiHitTester>>, -} - -impl HitTesterRequest { - /// Block until the hit tester is available and return it, consuming teh request. - pub fn resolve(self) -> Arc<dyn ApiHitTester> { - self.rx.recv().unwrap() - } -} - -/// Describe an item that matched a hit-test query. -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] -pub struct HitTestItem { - /// The pipeline that the display item that was hit belongs to. - pub pipeline: PipelineId, - - /// The tag of the hit display item. - pub tag: ItemTag, - - /// The hit point in the coordinate space of the "viewport" of the display item. The - /// viewport is the scroll node formed by the root reference frame of the display item's - /// pipeline. - pub point_in_viewport: LayoutPoint, - - /// The coordinates of the original hit test point relative to the origin of this item. - /// This is useful for calculating things like text offsets in the client. - pub point_relative_to_item: LayoutPoint, -} - -/// Returned by `RenderApi::hit_test`. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -pub struct HitTestResult { - /// List of items that are match the hit-test query. - pub items: Vec<HitTestItem>, -} - -bitflags! { - #[derive(Deserialize, MallocSizeOf, Serialize)] - /// - pub struct HitTestFlags: u8 { - /// - const FIND_ALL = 0b00000001; - /// - const POINT_RELATIVE_TO_PIPELINE_VIEWPORT = 0b00000010; - } -} - -impl Drop for NotificationRequest { - fn drop(&mut self) { - if let Some(ref mut handler) = self.handler { - handler.notify(Checkpoint::TransactionDropped); - } - } -} - -// This Clone impl yields an "empty" request because we don't want the requests -// to be notified twice so the request is owned by only one of the API messages -// (the original one) after the clone. -// This works in practice because the notifications requests are used for -// synchronization so we don't need to include them in the recording mechanism -// in wrench that clones the messages. -impl Clone for NotificationRequest { - fn clone(&self) -> Self { - NotificationRequest { - when: self.when, - handler: None, - } - } -} - - -/// A key to identify an animated property binding. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Deserialize, MallocSizeOf, PartialEq, Serialize, Eq, Hash, PeekPoke)] -pub struct PropertyBindingId { - pub namespace: IdNamespace, - pub uid: u32, -} - -impl PropertyBindingId { - /// Constructor. - pub fn new(value: u64) -> Self { - PropertyBindingId { - namespace: IdNamespace((value >> 32) as u32), - uid: value as u32, - } - } -} - -/// A unique key that is used for connecting animated property -/// values to bindings in the display list. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] -pub struct PropertyBindingKey<T> { - /// - pub id: PropertyBindingId, - #[doc(hidden)] - pub _phantom: PhantomData<T>, -} - -/// Construct a property value from a given key and value. -impl<T: Copy> PropertyBindingKey<T> { - /// - pub fn with(self, value: T) -> PropertyValue<T> { - PropertyValue { key: self, value } - } -} - -impl<T> PropertyBindingKey<T> { - /// Constructor. - pub fn new(value: u64) -> Self { - PropertyBindingKey { - id: PropertyBindingId::new(value), - _phantom: PhantomData, - } - } -} - -/// A binding property can either be a specific value -/// (the normal, non-animated case) or point to a binding location -/// to fetch the current value from. -/// Note that Binding has also a non-animated value, the value is -/// used for the case where the animation is still in-delay phase -/// (i.e. the animation doesn't produce any animation values). -#[repr(C)] -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, PeekPoke)] -pub enum PropertyBinding<T> { - /// Non-animated value. - Value(T), - /// Animated binding. - Binding(PropertyBindingKey<T>, T), -} - -impl<T: Default> Default for PropertyBinding<T> { - fn default() -> Self { - PropertyBinding::Value(Default::default()) - } -} - -impl<T> From<T> for PropertyBinding<T> { - fn from(value: T) -> PropertyBinding<T> { - PropertyBinding::Value(value) - } -} - -impl From<PropertyBindingKey<ColorF>> for PropertyBindingKey<ColorU> { - fn from(key: PropertyBindingKey<ColorF>) -> PropertyBindingKey<ColorU> { - PropertyBindingKey { - id: key.id.clone(), - _phantom: PhantomData, - } - } -} - -impl From<PropertyBindingKey<ColorU>> for PropertyBindingKey<ColorF> { - fn from(key: PropertyBindingKey<ColorU>) -> PropertyBindingKey<ColorF> { - PropertyBindingKey { - id: key.id.clone(), - _phantom: PhantomData, - } - } -} - -impl From<PropertyBinding<ColorF>> for PropertyBinding<ColorU> { - fn from(value: PropertyBinding<ColorF>) -> PropertyBinding<ColorU> { - match value { - PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), - PropertyBinding::Binding(k, v) => { - PropertyBinding::Binding(k.into(), v.into()) - } - } - } -} - -impl From<PropertyBinding<ColorU>> for PropertyBinding<ColorF> { - fn from(value: PropertyBinding<ColorU>) -> PropertyBinding<ColorF> { - match value { - PropertyBinding::Value(value) => PropertyBinding::Value(value.into()), - PropertyBinding::Binding(k, v) => { - PropertyBinding::Binding(k.into(), v.into()) - } - } - } -} - -/// The current value of an animated property. This is -/// supplied by the calling code. -#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)] -pub struct PropertyValue<T> { - /// - pub key: PropertyBindingKey<T>, - /// - pub value: T, -} - -/// When using `generate_frame()`, a list of `PropertyValue` structures -/// can optionally be supplied to provide the current value of any -/// animated properties. -#[derive(Clone, Deserialize, Serialize, Debug, PartialEq, Default)] -pub struct DynamicProperties { - /// - pub transforms: Vec<PropertyValue<LayoutTransform>>, - /// opacity - pub floats: Vec<PropertyValue<f32>>, - /// background color - pub colors: Vec<PropertyValue<ColorF>>, -} - -/// A C function that takes a pointer to a heap allocation and returns its size. -/// -/// This is borrowed from the malloc_size_of crate, upon which we want to avoid -/// a dependency from WebRender. -pub type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize; - -bitflags! { - /// Flags to enable/disable various builtin debugging tools. - #[repr(C)] - #[derive(Default, Deserialize, MallocSizeOf, Serialize)] - pub struct DebugFlags: u32 { - /// Display the frame profiler on screen. - const PROFILER_DBG = 1 << 0; - /// Display intermediate render targets on screen. - const RENDER_TARGET_DBG = 1 << 1; - /// Display all texture cache pages on screen. - const TEXTURE_CACHE_DBG = 1 << 2; - /// Display GPU timing results. - const GPU_TIME_QUERIES = 1 << 3; - /// Query the number of pixels that pass the depth test divided and show it - /// in the profiler as a percentage of the number of pixels in the screen - /// (window width times height). - const GPU_SAMPLE_QUERIES = 1 << 4; - /// Render each quad with their own draw call. - /// - /// Terrible for performance but can help with understanding the drawing - /// order when inspecting renderdoc or apitrace recordings. - const DISABLE_BATCHING = 1 << 5; - /// Display the pipeline epochs. - const EPOCHS = 1 << 6; - /// Print driver messages to stdout. - const ECHO_DRIVER_MESSAGES = 1 << 7; - /// Show an overlay displaying overdraw amount. - const SHOW_OVERDRAW = 1 << 8; - /// Display the contents of GPU cache. - const GPU_CACHE_DBG = 1 << 9; - /// Clear evicted parts of the texture cache for debugging purposes. - const TEXTURE_CACHE_DBG_CLEAR_EVICTED = 1 << 10; - /// Show picture caching debug overlay - const PICTURE_CACHING_DBG = 1 << 11; - /// Highlight all primitives with colors based on kind. - const PRIMITIVE_DBG = 1 << 12; - /// Draw a zoom widget showing part of the framebuffer zoomed in. - const ZOOM_DBG = 1 << 13; - /// Scale the debug renderer down for a smaller screen. This will disrupt - /// any mapping between debug display items and page content, so shouldn't - /// be used with overlays like the picture caching or primitive display. - const SMALL_SCREEN = 1 << 14; - /// Disable various bits of the WebRender pipeline, to help narrow - /// down where slowness might be coming from. - const DISABLE_OPAQUE_PASS = 1 << 15; - /// - const DISABLE_ALPHA_PASS = 1 << 16; - /// - const DISABLE_CLIP_MASKS = 1 << 17; - /// - const DISABLE_TEXT_PRIMS = 1 << 18; - /// - const DISABLE_GRADIENT_PRIMS = 1 << 19; - /// - const OBSCURE_IMAGES = 1 << 20; - /// Taint the transparent area of the glyphs with a random opacity to easily - /// see when glyphs are re-rasterized. - const GLYPH_FLASHING = 1 << 21; - /// The profiler only displays information that is out of the ordinary. - const SMART_PROFILER = 1 << 22; - /// If set, dump picture cache invalidation debug to console. - const INVALIDATION_DBG = 1 << 23; - /// Log tile cache to memory for later saving as part of wr-capture - const TILE_CACHE_LOGGING_DBG = 1 << 24; - /// Collect and dump profiler statistics to captures. - const PROFILER_CAPTURE = (1 as u32) << 25; // need "as u32" until we have cbindgen#556 - /// Invalidate picture tiles every frames (useful when inspecting GPU work in external tools). - const FORCE_PICTURE_INVALIDATION = (1 as u32) << 26; - const USE_BATCHED_TEXTURE_UPLOADS = (1 as u32) << 27; - const USE_DRAW_CALLS_FOR_TEXTURE_COPY = (1 as u32) << 28; - } -} - -/// Information specific to a primitive type that -/// uniquely identifies a primitive template by key. -#[derive(Debug, Clone, Eq, MallocSizeOf, PartialEq, Hash, Serialize, Deserialize)] -pub enum PrimitiveKeyKind { - /// Clear an existing rect, used for special effects on some platforms. - Clear, - /// - Rectangle { - /// - color: PropertyBinding<ColorU>, - }, -} - -/// -#[derive(Clone)] -pub struct ScrollNodeState { - /// - pub id: ExternalScrollId, - /// - pub scroll_offset: LayoutVector2D, -} - -/// -#[derive(Clone, Copy, Debug)] -pub enum ScrollLocation { - /// Scroll by a certain amount. - Delta(LayoutVector2D), - /// Scroll to very top of element. - Start, - /// Scroll to very bottom of element. - End, -} - -/// Represents a zoom factor. -#[derive(Clone, Copy, Debug)] -pub struct ZoomFactor(f32); - -impl ZoomFactor { - /// Construct a new zoom factor. - pub fn new(scale: f32) -> Self { - ZoomFactor(scale) - } - - /// Get the zoom factor as an untyped float. - pub fn get(self) -> f32 { - self.0 - } -} - -/// Crash annotations included in crash reports. -#[repr(C)] -#[derive(Clone, Copy)] -pub enum CrashAnnotation { - CompileShader = 0, - DrawShader = 1, -} - -/// Handler to expose support for annotating crash reports. -pub trait CrashAnnotator : Send { - fn set(&self, annotation: CrashAnnotation, value: &std::ffi::CStr); - fn clear(&self, annotation: CrashAnnotation); - fn box_clone(&self) -> Box<dyn CrashAnnotator>; -} - -impl Clone for Box<dyn CrashAnnotator> { - fn clone(&self) -> Box<dyn CrashAnnotator> { - self.box_clone() - } -} - -/// Guard to add a crash annotation at creation, and clear it at destruction. -pub struct CrashAnnotatorGuard<'a> { - annotator: &'a Option<Box<dyn CrashAnnotator>>, - annotation: CrashAnnotation, -} - -impl<'a> CrashAnnotatorGuard<'a> { - pub fn new( - annotator: &'a Option<Box<dyn CrashAnnotator>>, - annotation: CrashAnnotation, - value: &std::ffi::CStr, - ) -> Self { - if let Some(ref annotator) = annotator { - annotator.set(annotation, value); - } - Self { - annotator, - annotation, - } - } -} - -impl<'a> Drop for CrashAnnotatorGuard<'a> { - fn drop(&mut self) { - if let Some(ref annotator) = self.annotator { - annotator.clear(self.annotation); - } - } -} +pub use crate::resources::DEFAULT_TILE_SIZE; diff --git a/third_party/webrender/webrender/src/api_resources.rs b/third_party/webrender/webrender_api/src/resources.rs index 0a48858fc42..c41fdc3009e 100644 --- a/third_party/webrender/webrender/src/api_resources.rs +++ b/third_party/webrender/webrender_api/src/resources.rs @@ -2,15 +2,16 @@ * 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 crate::api::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize}; -use crate::api::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams}; -use crate::api::{BlobImageRequest, BlobImageDescriptor, BlobImageResources}; -use crate::api::{FontKey, FontTemplate, FontInstanceData, FontInstanceKey}; -use crate::api::SharedFontInstanceMap; -use crate::api::units::*; -use crate::render_api::{ResourceUpdate, TransactionMsg, AddFont}; +use crate::{BlobImageKey, ImageDescriptor, DirtyRect, TileSize, ResourceUpdate}; +use crate::{BlobImageHandler, AsyncBlobImageRasterizer, BlobImageData, BlobImageParams}; +use crate::{BlobImageRequest, BlobImageDescriptor, BlobImageResources, TransactionMsg}; +use crate::{FontKey, FontTemplate, FontInstanceData, FontInstanceKey, AddFont}; use crate::image_tiling::*; -use crate::profiler; +use crate::units::*; +use crate::font::SharedFontInstanceMap; +use crate::euclid::{point2, size2}; + +pub const DEFAULT_TILE_SIZE: TileSize = 512; use std::collections::HashMap; use std::sync::Arc; @@ -104,11 +105,7 @@ impl ApiResources { blobs_to_rasterize.push(img.key); } ResourceUpdate::DeleteBlobImage(key) => { - transaction.use_scene_builder_thread = true; self.blob_image_templates.remove(&key); - if let Some(ref mut handler) = self.blob_image_handler { - handler.delete(key); - } } ResourceUpdate::SetBlobImageVisibleArea(ref key, ref area) => { self.update_blob_image(*key, None, None, None, area); @@ -142,14 +139,12 @@ impl ApiResources { ); } ResourceUpdate::DeleteFont(key) => { - transaction.use_scene_builder_thread = true; self.fonts.templates.remove(&key); if let Some(ref mut handler) = self.blob_image_handler { handler.delete_font(key); } } ResourceUpdate::DeleteFontInstance(key) => { - transaction.use_scene_builder_thread = true; // We will delete from the shared font instance map in the resource cache // after scene swap. @@ -157,18 +152,11 @@ impl ApiResources { r.delete_font_instance(key); } } - ResourceUpdate::DeleteImage(..) => { - transaction.use_scene_builder_thread = true; - } _ => {} } } let (rasterizer, requests) = self.create_blob_scene_builder_requests(&blobs_to_rasterize); - transaction.profile.set(profiler::RASTERIZED_BLOBS, blobs_to_rasterize.len()); - transaction.profile.set(profiler::RASTERIZED_BLOB_TILES, requests.len()); - transaction.use_scene_builder_thread |= !requests.is_empty(); - transaction.use_scene_builder_thread |= !transaction.scene_ops.is_empty(); transaction.blob_rasterizer = rasterizer; transaction.blob_requests = requests; } @@ -297,3 +285,43 @@ impl ApiResources { (Some(handler.create_blob_rasterizer()), blob_request_params) } } + +fn compute_valid_tiles_if_bounds_change( + prev_rect: &DeviceIntRect, + new_rect: &DeviceIntRect, + tile_size: u16, +) -> Option<TileRange> { + let intersection = match prev_rect.intersection(new_rect) { + Some(rect) => rect, + None => { + return Some(TileRange::zero()); + } + }; + + let left = prev_rect.min_x() != new_rect.min_x(); + let right = prev_rect.max_x() != new_rect.max_x(); + let top = prev_rect.min_y() != new_rect.min_y(); + let bottom = prev_rect.max_y() != new_rect.max_y(); + + if !left && !right && !top && !bottom { + // Bounds have not changed. + return None; + } + + let tw = 1.0 / (tile_size as f32); + let th = 1.0 / (tile_size as f32); + + let tiles = intersection + .cast::<f32>() + .scale(tw, th); + + let min_x = if left { f32::ceil(tiles.min_x()) } else { f32::floor(tiles.min_x()) }; + let min_y = if top { f32::ceil(tiles.min_y()) } else { f32::floor(tiles.min_y()) }; + let max_x = if right { f32::floor(tiles.max_x()) } else { f32::ceil(tiles.max_x()) }; + let max_y = if bottom { f32::floor(tiles.max_y()) } else { f32::ceil(tiles.max_y()) }; + + Some(TileRange { + origin: point2(min_x as i32, min_y as i32), + size: size2((max_x - min_x) as i32, (max_y - min_y) as i32), + }) +} diff --git a/third_party/webrender/webrender_api/src/units.rs b/third_party/webrender/webrender_api/src/units.rs index 5ec6a80e920..9913e4e6647 100644 --- a/third_party/webrender/webrender_api/src/units.rs +++ b/third_party/webrender/webrender_api/src/units.rs @@ -32,7 +32,6 @@ pub type DeviceIntLength = Length<i32, DevicePixel>; pub type DeviceIntSideOffsets = SideOffsets2D<i32, DevicePixel>; pub type DeviceRect = Rect<f32, DevicePixel>; -pub type DeviceBox2D = Box2D<f32, DevicePixel>; pub type DevicePoint = Point2D<f32, DevicePixel>; pub type DeviceVector2D = Vector2D<f32, DevicePixel>; pub type DeviceSize = Size2D<f32, DevicePixel>; @@ -150,7 +149,7 @@ pub type BlobToDeviceTranslation = Translation2D<i32, LayoutPixel, DevicePixel>; /// may grow. Storing them as texel coords and normalizing /// the UVs in the vertex shader means nothing needs to be /// updated on the CPU when the texture size changes. -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] pub struct TexelRect { pub uv0: DevicePoint, pub uv1: DevicePoint, diff --git a/third_party/webrender/webrender_build/src/shader.rs b/third_party/webrender/webrender_build/src/shader.rs index 331e858bdac..3c15f4f4148 100644 --- a/third_party/webrender/webrender_build/src/shader.rs +++ b/third_party/webrender/webrender_build/src/shader.rs @@ -170,7 +170,6 @@ pub fn build_shader_prefix_string<F: FnMut(&str)>( // GLSL requires that the version number comes first. let gl_version_string = match gl_version { ShaderVersion::Gl => "#version 150\n", - ShaderVersion::Gles if features.contains(&"TEXTURE_EXTERNAL_ESSL1") => "#version 100\n", ShaderVersion::Gles => "#version 300 es\n", }; output(gl_version_string); @@ -194,23 +193,6 @@ pub fn build_shader_prefix_string<F: FnMut(&str)>( }; output(kind_string); - // detect which platform we're targeting - let is_macos = match std::env::var("CARGO_CFG_TARGET_OS") { - Ok(os) => os == "macos", - // if this is not called from build.rs (e.g. the gpu_cache_update shader or - // if the optimized shader pref is disabled) we want to use the runtime value - Err(_) => cfg!(target_os = "macos"), - }; - let is_android = match std::env::var("CARGO_CFG_TARGET_OS") { - Ok(os) => os == "android", - Err(_) => cfg!(target_os = "android"), - }; - if is_macos { - output("#define PLATFORM_MACOS\n"); - } else if is_android { - output("#define PLATFORM_ANDROID\n"); - } - // Define a constant for the vertex texture width. output("#define WR_MAX_VERTEX_TEXTURE_WIDTH "); output(&MAX_VERTEX_TEXTURE_WIDTH_STRING); diff --git a/third_party/webrender/webrender_build/src/shader_features.rs b/third_party/webrender/webrender_build/src/shader_features.rs index 19c4ebb45f1..06e6b0da8a7 100644 --- a/third_party/webrender/webrender_build/src/shader_features.rs +++ b/third_party/webrender/webrender_build/src/shader_features.rs @@ -12,50 +12,25 @@ bitflags! { const ADVANCED_BLEND_EQUATION = 1 << 8; const DUAL_SOURCE_BLENDING = 1 << 9; - const DITHERING = 1 << 10; - const TEXTURE_EXTERNAL = 1 << 11; - const TEXTURE_EXTERNAL_ESSL1 = 1 << 12; - const DEBUG = 1 << 13; + const PIXEL_LOCAL_STORAGE = 1 << 10; + const DITHERING = 1 << 11; + const TEXTURE_EXTERNAL = 1 << 12; } } pub type ShaderFeatures = HashMap<&'static str, Vec<String>>; -/// Builder for a list of features. -#[derive(Clone)] -struct FeatureList<'a> { - list: Vec<&'a str>, +macro_rules! features { + ($($str:expr),*) => { vec![$(String::from($str),)*] as Vec<String> }; } -impl<'a> FeatureList<'a> { - fn new() -> Self { - FeatureList { - list: Vec::new(), - } - } - - fn add(&mut self, feature: &'a str) { - assert!(!feature.contains(',')); - self.list.push(feature); - } - - fn with(&self, feature: &'a str) -> Self { - let mut other = self.clone(); - other.add(feature); - other - } - - fn concat(&self, other: &Self) -> Self { - let mut list = self.list.clone(); - list.extend_from_slice(&other.list); - FeatureList { - list - } - } - - fn finish(&mut self) -> String { - self.list.sort_unstable(); - self.list.join(",") +fn concat_features(a: &str, b: &str) -> String { + if a.is_empty() { + b.to_string() + } else if b.is_empty() { + a.to_string() + } else { + [a, b].join(",") } } @@ -64,72 +39,51 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures { let mut shaders = ShaderFeatures::new(); // Clip shaders - shaders.insert("cs_clip_rectangle", vec![String::new(), "FAST_PATH".to_string()]); - shaders.insert("cs_clip_image", vec!["TEXTURE_2D".to_string()]); - shaders.insert("cs_clip_box_shadow", vec!["TEXTURE_2D".to_string()]); + shaders.insert("cs_clip_rectangle", features!["", "FAST_PATH"]); + for name in &["cs_clip_image", "cs_clip_box_shadow"] { + shaders.insert(name, features![""]); + } // Cache shaders - shaders.insert("cs_blur", vec!["ALPHA_TARGET".to_string(), "COLOR_TARGET".to_string()]); - - for name in &[ - "cs_line_decoration", - "cs_fast_linear_gradient", - "cs_border_segment", - "cs_border_solid", - "cs_svg_filter", - ] { - shaders.insert(name, vec![String::new()]); + shaders.insert("cs_blur", features!["ALPHA_TARGET", "COLOR_TARGET"]); + + for name in &["cs_line_decoration", "cs_gradient", "cs_border_segment", "cs_border_solid", "cs_svg_filter"] { + shaders.insert(name, features![""]); } - for name in &[ - "cs_linear_gradient", - "cs_radial_gradient", - "cs_conic_gradient", - ] { - let mut features = Vec::new(); - features.push(String::new()); - if flags.contains(ShaderFeatureFlags::DITHERING) { - features.push("DITHERING".to_string()); + shaders.insert("cs_scale", features![""]); + + // Pixel local storage shaders + let pls_feature = if flags.contains(ShaderFeatureFlags::PIXEL_LOCAL_STORAGE) { + for name in &["pls_init", "pls_resolve"] { + shaders.insert(name, features!["PIXEL_LOCAL_STORAGE"]); } - shaders.insert(name, features); - } - let mut base_prim_features = FeatureList::new(); + "PIXEL_LOCAL_STORAGE" + } else { + "" + }; // Brush shaders - let mut brush_alpha_features = base_prim_features.with("ALPHA_PASS"); - for name in &["brush_solid", "brush_blend", "brush_mix_blend"] { - let mut features: Vec<String> = Vec::new(); - features.push(base_prim_features.finish()); - features.push(brush_alpha_features.finish()); - features.push("DEBUG_OVERDRAW".to_string()); - shaders.insert(name, features); + let brush_alpha_features = concat_features("ALPHA_PASS", pls_feature); + for name in &["brush_solid", "brush_blend", "brush_mix_blend", "brush_opacity"] { + shaders.insert(name, features!["", &brush_alpha_features, "DEBUG_OVERDRAW"]); } - for name in &["brush_linear_gradient"] { + for name in &["brush_conic_gradient", "brush_radial_gradient", "brush_linear_gradient"] { let mut features: Vec<String> = Vec::new(); - let mut list = FeatureList::new(); - if flags.contains(ShaderFeatureFlags::DITHERING) { - list.add("DITHERING"); - } - features.push(list.concat(&base_prim_features).finish()); - features.push(list.concat(&brush_alpha_features).finish()); - features.push(list.with("DEBUG_OVERDRAW").finish()); + let base = if flags.contains(ShaderFeatureFlags::DITHERING) { + "DITHERING" + } else { + "" + }; + features.push(base.to_string()); + features.push(concat_features(base, &brush_alpha_features)); + features.push(concat_features(base, "DEBUG_OVERDRAW")); shaders.insert(name, features); } - { - let mut features: Vec<String> = Vec::new(); - features.push(base_prim_features.finish()); - features.push(brush_alpha_features.finish()); - features.push(base_prim_features.with("ANTIALIASING").finish()); - features.push(brush_alpha_features.with("ANTIALIASING").finish()); - features.push("ANTIALIASING,DEBUG_OVERDRAW".to_string()); - features.push("DEBUG_OVERDRAW".to_string()); - shaders.insert("brush_opacity", features); - } - // Image brush shaders - let mut texture_types = vec!["TEXTURE_2D"]; + let mut texture_types = vec!["", "TEXTURE_2D"]; if flags.contains(ShaderFeatureFlags::GL) { texture_types.push("TEXTURE_RECT"); } @@ -138,96 +92,60 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures { } let mut image_features: Vec<String> = Vec::new(); for texture_type in &texture_types { - let mut fast = FeatureList::new(); - if !texture_type.is_empty() { - fast.add(texture_type); - } - image_features.push(fast.concat(&base_prim_features).finish()); - image_features.push(fast.concat(&brush_alpha_features).finish()); - image_features.push(fast.with("DEBUG_OVERDRAW").finish()); - let mut slow = fast.clone(); - slow.add("REPETITION"); - slow.add("ANTIALIASING"); - image_features.push(slow.concat(&base_prim_features).finish()); - image_features.push(slow.concat(&brush_alpha_features).finish()); - image_features.push(slow.with("DEBUG_OVERDRAW").finish()); + let fast = texture_type.to_string(); + image_features.push(fast.clone()); + image_features.push(concat_features(&fast, &brush_alpha_features)); + image_features.push(concat_features(&fast, "DEBUG_OVERDRAW")); + let slow = concat_features(texture_type, "REPETITION,ANTIALIASING"); + image_features.push(slow.clone()); + image_features.push(concat_features(&slow, &brush_alpha_features)); + image_features.push(concat_features(&slow, "DEBUG_OVERDRAW")); if flags.contains(ShaderFeatureFlags::ADVANCED_BLEND_EQUATION) { - let advanced_blend_features = brush_alpha_features.with("ADVANCED_BLEND"); - image_features.push(fast.concat(&advanced_blend_features).finish()); - image_features.push(slow.concat(&advanced_blend_features).finish()); + let advanced_blend_features = concat_features(&brush_alpha_features, "ADVANCED_BLEND"); + image_features.push(concat_features(&fast, &advanced_blend_features)); + image_features.push(concat_features(&slow, &advanced_blend_features)); } if flags.contains(ShaderFeatureFlags::DUAL_SOURCE_BLENDING) { - let dual_source_features = brush_alpha_features.with("DUAL_SOURCE_BLENDING"); - image_features.push(fast.concat(&dual_source_features).finish()); - image_features.push(slow.concat(&dual_source_features).finish()); + let dual_source_features = concat_features(&brush_alpha_features, "DUAL_SOURCE_BLENDING"); + image_features.push(concat_features(&fast, &dual_source_features)); + image_features.push(concat_features(&slow, &dual_source_features)); } } shaders.insert("brush_image", image_features); - let mut composite_texture_types = texture_types.clone(); - if flags.contains(ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1) { - composite_texture_types.push("TEXTURE_EXTERNAL_ESSL1"); - } let mut composite_features: Vec<String> = Vec::new(); - for texture_type in &composite_texture_types { - let base = texture_type.to_string(); - composite_features.push(base); + for texture_type in &texture_types { + let base = concat_features("", texture_type); + composite_features.push(base.clone()); } - shaders.insert("cs_scale", composite_features.clone()); - - // YUV image brush and composite shaders + // YUV image brush shaders let mut yuv_features: Vec<String> = Vec::new(); for texture_type in &texture_types { - let mut list = FeatureList::new(); - if !texture_type.is_empty() { - list.add(texture_type); - } - list.add("YUV"); - composite_features.push(list.finish()); - yuv_features.push(list.concat(&base_prim_features).finish()); - yuv_features.push(list.concat(&brush_alpha_features).finish()); - yuv_features.push(list.with("DEBUG_OVERDRAW").finish()); - } - shaders.insert("brush_yuv_image", yuv_features); - - // Fast path composite shaders - for texture_type in &composite_texture_types { - let mut list = FeatureList::new(); - if !texture_type.is_empty() { - list.add(texture_type); - } - list.add("FAST_PATH"); - composite_features.push(list.finish()); + let base = concat_features("YUV", texture_type); + composite_features.push(base.clone()); + yuv_features.push(base.clone()); + yuv_features.push(concat_features(&base, &brush_alpha_features)); + yuv_features.push(concat_features(&base, "DEBUG_OVERDRAW")); } shaders.insert("composite", composite_features); + shaders.insert("brush_yuv_image", yuv_features); // Prim shaders - let mut text_types = vec![""]; + let mut text_types = vec![pls_feature]; if flags.contains(ShaderFeatureFlags::DUAL_SOURCE_BLENDING) { text_types.push("DUAL_SOURCE_BLENDING"); } let mut text_features: Vec<String> = Vec::new(); for text_type in &text_types { - let mut list = base_prim_features.with("TEXTURE_2D"); - if !text_type.is_empty() { - list.add(text_type); - } - let mut alpha_list = list.with("ALPHA_PASS"); - text_features.push(alpha_list.finish()); - text_features.push(alpha_list.with("GLYPH_TRANSFORM").finish()); - text_features.push(list.with("DEBUG_OVERDRAW").finish()); + text_features.push(concat_features(text_type, "ALPHA_PASS")); + text_features.push(concat_features(text_type, "GLYPH_TRANSFORM,ALPHA_PASS")); + text_features.push(concat_features(text_type, "DEBUG_OVERDRAW")); } shaders.insert("ps_text_run", text_features); - shaders.insert("ps_split_composite", vec![base_prim_features.finish()]); - - shaders.insert("ps_clear", vec![base_prim_features.finish()]); + shaders.insert("ps_split_composite", features![pls_feature]); - if flags.contains(ShaderFeatureFlags::DEBUG) { - for name in &["debug_color", "debug_font"] { - shaders.insert(name, vec![String::new()]); - } - } + shaders.insert("ps_clear", features![""]); shaders } diff --git a/third_party/webrender/wr_malloc_size_of/lib.rs b/third_party/webrender/wr_malloc_size_of/lib.rs index abd982ffe80..2a345089740 100644 --- a/third_party/webrender/wr_malloc_size_of/lib.rs +++ b/third_party/webrender/wr_malloc_size_of/lib.rs @@ -17,7 +17,6 @@ use std::hash::{BuildHasher, Hash}; use std::mem::size_of; use std::ops::Range; use std::os::raw::c_void; -use std::path::PathBuf; /// A C function that takes a pointer to a heap allocation and returns its size. type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize; @@ -311,15 +310,6 @@ impl<T> MallocSizeOf for std::marker::PhantomData<T> { } } -impl MallocSizeOf for PathBuf { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - match self.to_str() { - Some(s) => unsafe { ops.malloc_size_of(s.as_ptr()) }, - None => self.as_os_str().len(), - } - } -} - impl<T: MallocSizeOf, Unit> MallocSizeOf for euclid::Length<T, Unit> { fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { self.0.size_of(ops) diff --git a/third_party/webrender/wrench/Cargo.toml b/third_party/webrender/wrench/Cargo.toml index f51e4202ff2..5595ced68e2 100644 --- a/third_party/webrender/wrench/Cargo.toml +++ b/third_party/webrender/wrench/Cargo.toml @@ -7,25 +7,30 @@ license = "MPL-2.0" edition = "2018" [dependencies] -base64 = "0.12" +base64 = "0.10" +bincode = "1.0" +byteorder = "1.0" env_logger = { version = "0.5", optional = true } -gleam = "0.15" +euclid = "0.22" +gleam = "0.12" glutin = "0.21" +app_units = "0.7" clap = { version = "2", features = ["yaml"] } log = "0.4" yaml-rust = "0.4" serde_json = "1.0" +ron = "0.5" time = "0.1" chrono = "0.2" crossbeam = "0.2" osmesa-sys = { version = "0.1.2", optional = true } -osmesa-src = { version = "0.2", git = "https://github.com/servo/osmesa-src", optional = true } -webrender = { path = "../webrender", features = ["capture", "replay", "png", "profiler", "no_static_freetype", "leak_checks"] } +osmesa-src = { git = "https://github.com/servo/osmesa-src", optional = true } +webrender = {path = "../webrender", features=["capture","replay","debugger","png","profiler","no_static_freetype", "leak_checks"]} +webrender_api = {path = "../webrender_api", features=["serialize","deserialize"]} winit = "0.19" -serde = { version = "1.0", features = ["derive"] } +serde = {version = "1.0", features = ["derive"] } semver = "0.9.0" swgl = { path = "../swgl", optional = true } -tracy-rs = "0.1.2" [dependencies.image] version = "0.23" @@ -43,7 +48,7 @@ software = [ "swgl" ] [target.'cfg(target_os = "windows")'.dependencies] dwrote = "0.11" -mozangle = { version = "0.3.2", features = ["egl"] } +mozangle = {version = "0.3.1", features = ["egl"]} [target.'cfg(all(unix, not(target_os = "android")))'.dependencies] font-loader = "0.11" @@ -52,8 +57,7 @@ font-loader = "0.11" [package.metadata.android] package_name = "org.mozilla.wrench" label = "Wrench" -# keep it in sync with android-sdk-version in android-sdk.configure -android_version = 30 +android_version = 29 target_sdk_version = 18 min_sdk_version = 18 fullscreen = true diff --git a/third_party/webrender/wrench/README.md b/third_party/webrender/wrench/README.md index 6a60c6bb56c..1119a27112e 100644 --- a/third_party/webrender/wrench/README.md +++ b/third_party/webrender/wrench/README.md @@ -2,20 +2,29 @@ `wrench` is a tool for debugging webrender outside of a browser engine. -## Build - -Build `wrench` with `cargo build --release` within the `wrench` directory. - ## headless `wrench` has an optional headless mode for use in continuous integration. To run in headless mode, instead of using `cargo run -- args`, use `./headless.py args`. -## `show` +## `replay` and `show` + +Binary recordings can be generated by webrender and replayed with `wrench replay`. Enable binary recording in `RendererOptions`. + +```rust +RendererOptions { + ... + recorder: Some(Box::new(BinaryRecorder::new("wr-frame.bin"))), + ... +} +``` + +If you are working on gecko integration you can enable recording in `webrender_bindings/src/bindings.rs` by setting + +```rust +static ENABLE_RECORDING: bool = true; +``` -If you are working on gecko integration you can capture a frame via the following steps. -* Visit about:support and check that the "Compositing" value in the "Graphics" table says "WebRender". Enable `gfx.webrender.all` in about:config if necessary to enable WebRender. -* Hit ctrl-shift-3 to capture the frame. The data will be put in `~/wr-capture`. -* View the capture with `wrench show ~/wr-capture`. +`wrench replay --save yaml` will convert the recording into frames described in yaml. Frames can then be replayed with `wrench show`. ## `reftest` diff --git a/third_party/webrender/wrench/android.txt b/third_party/webrender/wrench/android.txt index 35f5863c91d..7ff0ac0eb1d 100644 --- a/third_party/webrender/wrench/android.txt +++ b/third_party/webrender/wrench/android.txt @@ -10,10 +10,9 @@ Follow the steps at https://github.com/rust-windowing/android-rs-glue#setting-up - Install both the i686-linux-android and armv7-linux-androideabi rust targets, as the APK will include native libraries with both architectures. - - Don't install currently published version of cargo-apk as it doesn't work with the - version of winit and glutin we are using. - Instead, install the git master version of our fork like so: - cargo install --git https://github.com/jamienicol/android-rs-glue cargo-apk + - Don't install currently published version of cargo-apk (it doesn't support newer NDKs). + Instead, install the git master version like so: + cargo install --git https://github.com/rust-windowing/android-rs-glue cargo-apk - Consider adding ~/.mozbuild/android-sdk-linux/platform-tools to your path, for the adb commands below. diff --git a/third_party/webrender/wrench/benchmarks/large-blur-radius.yaml b/third_party/webrender/wrench/benchmarks/large-blur-radius.yaml index 55cfec76439..927e4237656 100644 --- a/third_party/webrender/wrench/benchmarks/large-blur-radius.yaml +++ b/third_party/webrender/wrench/benchmarks/large-blur-radius.yaml @@ -3,7 +3,7 @@ root: items: - type: stacking-context bounds: 100 100 1024 1024 - filters: blur(100, 100) + filters: blur(100) items: - type: rect bounds: 0 0 1024 1024 diff --git a/third_party/webrender/wrench/invalidation/basic.yaml b/third_party/webrender/wrench/invalidation/basic.yaml deleted file mode 100644 index c7d1b6f4502..00000000000 --- a/third_party/webrender/wrench/invalidation/basic.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -root: - items: - - type: rect - color: red - bounds: 100 100 500 100 diff --git a/third_party/webrender/wrench/invalidation/composite_nop_1.yaml b/third_party/webrender/wrench/invalidation/composite_nop_1.yaml deleted file mode 100644 index ce6cc46977f..00000000000 --- a/third_party/webrender/wrench/invalidation/composite_nop_1.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -root: - items: - - type: rect - color: red - bounds: 100 100 100 100 diff --git a/third_party/webrender/wrench/invalidation/composite_nop_2.yaml b/third_party/webrender/wrench/invalidation/composite_nop_2.yaml deleted file mode 100644 index a99663ed549..00000000000 --- a/third_party/webrender/wrench/invalidation/composite_nop_2.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -root: - items: - - type: rect - color: red - bounds: 100 120 100 100 diff --git a/third_party/webrender/wrench/reftests/aa/reftest.list b/third_party/webrender/wrench/reftests/aa/reftest.list index a7490085d0d..af7f1da1b36 100644 --- a/third_party/webrender/wrench/reftests/aa/reftest.list +++ b/third_party/webrender/wrench/reftests/aa/reftest.list @@ -1,3 +1,3 @@ -skip_on(android) fuzzy(1,1) fuzzy-if(platform(swgl),4,27) == rounded-rects.yaml rounded-rects-ref.png # Too wide for Android +skip_on(android) == rounded-rects.yaml rounded-rects-ref.png # Too wide for Android == aa-dist-bug.yaml aa-dist-bug-ref.yaml == fractional-radii.yaml fractional-radii-ref.yaml diff --git a/third_party/webrender/wrench/reftests/aa/rounded-rects-ref.png b/third_party/webrender/wrench/reftests/aa/rounded-rects-ref.png Binary files differindex 73fb98446b7..441f2ac93d9 100644 --- a/third_party/webrender/wrench/reftests/aa/rounded-rects-ref.png +++ b/third_party/webrender/wrench/reftests/aa/rounded-rects-ref.png diff --git a/third_party/webrender/wrench/reftests/backface/backface-flatten.yaml b/third_party/webrender/wrench/reftests/backface/backface-flatten.yaml deleted file mode 100644 index e754d45bc94..00000000000 --- a/third_party/webrender/wrench/reftests/backface/backface-flatten.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# In this test, there is a flattened ref frame between an element -# and the picture it's rendered into. The element should be considered -# invisible as it's back face oriented at this flattening step. -# If WR only checks the "final" transform, it will consider it visible. - ---- -root: - transform: rotate-x(1) - items: - - type: stacking-context - transform-style: preserve-3d - #transform: rotate-x(1) - items: - - type: stacking-context - bounds: 0 0 200 200 - transform: rotate-y(30) - transform-style: flat - items: - - type: stacking-context - bounds: 0 0 200 200 - transform: rotate-y(-100) - transform-style: preserve-3d - items: - - type: stacking-context - #transform: rotate-x(1) - transform-style: flat - items: - - type: rect - bounds: 0 0 200 200 - color: green - backface-visible: false diff --git a/third_party/webrender/wrench/reftests/backface/backface-vis-3d.yaml b/third_party/webrender/wrench/reftests/backface/backface-vis-3d.yaml deleted file mode 100644 index c58026a5cc0..00000000000 --- a/third_party/webrender/wrench/reftests/backface/backface-vis-3d.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Verifies that a 3d context selects the correct reference (containing block) spatial node. -root: - items: - - type: stacking-context - bounds: 100 0 100 100 - transform: scale-x(-1) - items: - - type: stacking-context - bounds: 0 0 100 100 - transform: rotate-y(180) - transform-style: preserve-3d - items: - - type: rect - bounds: 0 0 100 100 - color: red - backface-visible: false diff --git a/third_party/webrender/wrench/reftests/backface/reftest.list b/third_party/webrender/wrench/reftests/backface/reftest.list index 70ab29b4a45..ec21edb22f9 100644 --- a/third_party/webrender/wrench/reftests/backface/reftest.list +++ b/third_party/webrender/wrench/reftests/backface/reftest.list @@ -5,5 +5,3 @@ == backface-picture.yaml backface-picture-ref.yaml == backface-double-flip.yaml blank.yaml == backface-both-sides.yaml backface-both-sides-ref.yaml -== backface-vis-3d.yaml blank.yaml -== backface-flatten.yaml blank.yaml diff --git a/third_party/webrender/wrench/reftests/blend/mix-blend-complex-transform.png b/third_party/webrender/wrench/reftests/blend/mix-blend-complex-transform.png Binary files differdeleted file mode 100644 index c88dccb864f..00000000000 --- a/third_party/webrender/wrench/reftests/blend/mix-blend-complex-transform.png +++ /dev/null diff --git a/third_party/webrender/wrench/reftests/blend/mix-blend-complex-transform.yaml b/third_party/webrender/wrench/reftests/blend/mix-blend-complex-transform.yaml deleted file mode 100644 index 33a1ab691c0..00000000000 --- a/third_party/webrender/wrench/reftests/blend/mix-blend-complex-transform.yaml +++ /dev/null @@ -1,52 +0,0 @@ ---- -root: - items: - - type: stacking-context - transform: [ - 1, 0, 0, 0, - 0.3443276, 1, 0, 0, - 0, 0, 1, 0, - -1822.09131, 0, 0, 1 - ] - bounds: 12 12 0 0 - items: - - type: clip - bounds: 0 0 1884 1290 - id: 2 - - type: stacking-context - transform: [ - 1, 0, 0, 0, - -0.3443276, 1, 0, 0, - 0, 0, 1, 0, - 444.18262, 0, 0, 1 - ] - items: - - type: stacking-context - clip-node: 2 - transform: [ - 1, 0, 0, 0, - 0.3443276, 1, 0, 0, - 0, 0, 1, 0, - -444.18262, 0, 0, 1 - ] - items: - - type: stacking-context - items: - - type: stacking-context - blend-container: true - items: - - type: stacking-context - mix-blend-mode: multiply - items: - - type: stacking-context - bounds: 1436 0 0 0 - transform: [ - 1, 0, 0, 0, - -0.3443276, 1, 0, 0, - 0, 0, 1, 0, - 444.18262, 0, 0, 1 - ] - items: - - type: rect - color: red - bounds: 0 0 113.025 1290 diff --git a/third_party/webrender/wrench/reftests/blend/mix-blend-invalid-backdrop-ref.yaml b/third_party/webrender/wrench/reftests/blend/mix-blend-invalid-backdrop-ref.yaml deleted file mode 100644 index 1e204cb3385..00000000000 --- a/third_party/webrender/wrench/reftests/blend/mix-blend-invalid-backdrop-ref.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -root: - items: - - type: rect - bounds: [0, 0, 100, 100] - color: red diff --git a/third_party/webrender/wrench/reftests/blend/mix-blend-invalid-backdrop.yaml b/third_party/webrender/wrench/reftests/blend/mix-blend-invalid-backdrop.yaml deleted file mode 100644 index e5f56e74c1b..00000000000 --- a/third_party/webrender/wrench/reftests/blend/mix-blend-invalid-backdrop.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# Test that if the parent surface is clipped such that there -# is no backdrop rect available, no crash occurs and output -# is as expected (a no-op mix-blend) ---- -root: - items: - # Ensure a filter is placed here to force this mix-blend to isolate from - # the tile cache backdrop - otherwise the surface won't get clipped, which - # is what we're trying to test. - - type: stacking-context - filters: [identity] - items: - - type: clip - id: 2 - bounds: [0, 0, 100, 100] - - type: stacking-context - blend-container: true - clip-node: 2 - items: - - type: rect - bounds: [0, 0, 100, 100] - color: red - - type: stacking-context - bounds: [100, 0, 100, 100] - mix-blend-mode: multiply - items: - - type: rect - bounds: [0, 0, 100, 100] - color: green diff --git a/third_party/webrender/wrench/reftests/blend/reftest.list b/third_party/webrender/wrench/reftests/blend/reftest.list index f1621b20903..e533e36ce06 100644 --- a/third_party/webrender/wrench/reftests/blend/reftest.list +++ b/third_party/webrender/wrench/reftests/blend/reftest.list @@ -1,10 +1,10 @@ == multiply.yaml multiply-ref.yaml fuzzy(1,32) == multiply-2.yaml multiply-2-ref.yaml -fuzzy(1,32) == color_targets(3) alpha_targets(0) multiply-3.yaml multiply-2-ref.yaml +fuzzy(1,32) == color_targets(4) alpha_targets(0) multiply-3.yaml multiply-2-ref.yaml == difference.yaml difference-ref.yaml fuzzy(1,30000) == difference-transparent.yaml difference-transparent-ref.yaml -fuzzy-if(platform(swgl),1,10000) == darken.yaml darken-ref.yaml -fuzzy-if(platform(swgl),1,10000) == lighten.yaml lighten-ref.yaml +== darken.yaml darken-ref.yaml +== lighten.yaml lighten-ref.yaml fuzzy(1,32) == repeated-difference.yaml repeated-difference-ref.yaml @@ -21,6 +21,4 @@ fuzzy(3,397) == isolated-2.yaml isolated-2-ref.yaml fuzzy(1,2502) == transparent-composite-1.yaml transparent-composite-1-ref.yaml fuzzy(1,2502) == transparent-composite-2.yaml transparent-composite-2-ref.yaml -fuzzy(2,420) == multi-mix-blend-mode.yaml multi-mix-blend-mode-ref.yaml -== mix-blend-invalid-backdrop.yaml mix-blend-invalid-backdrop-ref.yaml -platform(linux) == mix-blend-complex-transform.yaml mix-blend-complex-transform.png +fuzzy(2,324) == multi-mix-blend-mode.yaml multi-mix-blend-mode-ref.yaml diff --git a/third_party/webrender/wrench/reftests/border/border-clamp-corner-radius.png b/third_party/webrender/wrench/reftests/border/border-clamp-corner-radius.png Binary files differindex cf20fb59902..bdeda438645 100644 --- a/third_party/webrender/wrench/reftests/border/border-clamp-corner-radius.png +++ b/third_party/webrender/wrench/reftests/border/border-clamp-corner-radius.png diff --git a/third_party/webrender/wrench/reftests/border/border-dashed-dotted-caching.png b/third_party/webrender/wrench/reftests/border/border-dashed-dotted-caching.png Binary files differindex 14a79cd475a..652efde994c 100644 --- a/third_party/webrender/wrench/reftests/border/border-dashed-dotted-caching.png +++ b/third_party/webrender/wrench/reftests/border/border-dashed-dotted-caching.png diff --git a/third_party/webrender/wrench/reftests/border/border-gradient-nine-patch.png b/third_party/webrender/wrench/reftests/border/border-gradient-nine-patch.png Binary files differindex c984cf90151..7ab8c3df601 100644 --- a/third_party/webrender/wrench/reftests/border/border-gradient-nine-patch.png +++ b/third_party/webrender/wrench/reftests/border/border-gradient-nine-patch.png diff --git a/third_party/webrender/wrench/reftests/border/border-image-empty-slice-ref.png b/third_party/webrender/wrench/reftests/border/border-image-empty-slice-ref.png Binary files differindex 8ba6808ac90..103fdb57f65 100644 --- a/third_party/webrender/wrench/reftests/border/border-image-empty-slice-ref.png +++ b/third_party/webrender/wrench/reftests/border/border-image-empty-slice-ref.png diff --git a/third_party/webrender/wrench/reftests/border/border-image-fill-2-ref.png b/third_party/webrender/wrench/reftests/border/border-image-fill-2-ref.png Binary files differdeleted file mode 100644 index 5bb7dc8fb2a..00000000000 --- a/third_party/webrender/wrench/reftests/border/border-image-fill-2-ref.png +++ /dev/null diff --git a/third_party/webrender/wrench/reftests/border/border-image-fill-2.yaml b/third_party/webrender/wrench/reftests/border/border-image-fill-2.yaml deleted file mode 100644 index 653c4f34e45..00000000000 --- a/third_party/webrender/wrench/reftests/border/border-image-fill-2.yaml +++ /dev/null @@ -1,55 +0,0 @@ ---- -root: - items: - - type: stacking-context - bounds: [0, 0, 500, 500] - items: - - type: border - bounds: [ 20, 20, 200, 200 ] - width: [20, 40] - border-type: image - image-source: "border-image-src-2.png" - image-width: 100 - image-height: 50 - slice: [ 20, 40 ] - outset: 0 - repeat-vertical: repeat - repeat-horizontal: repeat - fill: true - - type: border - bounds: [ 240, 20, 200, 200 ] - width: [20, 40] - slice: [ 20, 40 ] - border-type: image - image-source: "border-image-src-2.png" - image-width: 100 - image-height: 50 - slice: [ 20, 40 ] - outset: 0 - repeat-vertical: stretch - repeat-horizontal: repeat - fill: true - - type: border - bounds: [ 20, 240, 200, 200 ] - width: [20, 40] - border-type: image - image-source: "border-image-src-2.png" - image-width: 100 - image-height: 50 - slice: [ 20, 40 ] - outset: 0 - repeat-vertical: repeat - repeat-horizontal: stretch - fill: true - - type: border - bounds: [ 240, 240, 200, 200 ] - width: [20, 40] - border-type: image - image-source: "border-image-src-2.png" - image-width: 100 - image-height: 50 - slice: [ 20, 40 ] - outset: 0 - repeat-vertical: stretch - repeat-horizontal: stretch - fill: true diff --git a/third_party/webrender/wrench/reftests/border/border-image-fill-ref.png b/third_party/webrender/wrench/reftests/border/border-image-fill-ref.png Binary files differindex 9d618bc76e6..41627d94557 100644 --- a/third_party/webrender/wrench/reftests/border/border-image-fill-ref.png +++ b/third_party/webrender/wrench/reftests/border/border-image-fill-ref.png diff --git a/third_party/webrender/wrench/reftests/border/border-image-ref.png b/third_party/webrender/wrench/reftests/border/border-image-ref.png Binary files differindex 73e4eb52560..0ae931a7777 100644 --- a/third_party/webrender/wrench/reftests/border/border-image-ref.png +++ b/third_party/webrender/wrench/reftests/border/border-image-ref.png diff --git a/third_party/webrender/wrench/reftests/border/border-image-round-ref.png b/third_party/webrender/wrench/reftests/border/border-image-round-ref.png Binary files differindex 62a18fbba90..46a128f39e5 100644 --- a/third_party/webrender/wrench/reftests/border/border-image-round-ref.png +++ b/third_party/webrender/wrench/reftests/border/border-image-round-ref.png diff --git a/third_party/webrender/wrench/reftests/border/border-image-src-2.png b/third_party/webrender/wrench/reftests/border/border-image-src-2.png Binary files differdeleted file mode 100644 index 5ebebaf21f2..00000000000 --- a/third_party/webrender/wrench/reftests/border/border-image-src-2.png +++ /dev/null diff --git a/third_party/webrender/wrench/reftests/border/border-no-bogus-line-ref.png b/third_party/webrender/wrench/reftests/border/border-no-bogus-line-ref.png Binary files differindex 1310e99f792..fefa2bcbced 100644 --- a/third_party/webrender/wrench/reftests/border/border-no-bogus-line-ref.png +++ b/third_party/webrender/wrench/reftests/border/border-no-bogus-line-ref.png diff --git a/third_party/webrender/wrench/reftests/border/border-radial-gradient-nine-patch.png b/third_party/webrender/wrench/reftests/border/border-radial-gradient-nine-patch.png Binary files differindex 9fa19d215bc..fd1ed84b8db 100644 --- a/third_party/webrender/wrench/reftests/border/border-radial-gradient-nine-patch.png +++ b/third_party/webrender/wrench/reftests/border/border-radial-gradient-nine-patch.png diff --git a/third_party/webrender/wrench/reftests/border/border-radii.png b/third_party/webrender/wrench/reftests/border/border-radii.png Binary files differindex 7a4bc340eba..87d5b674fd9 100644 --- a/third_party/webrender/wrench/reftests/border/border-radii.png +++ b/third_party/webrender/wrench/reftests/border/border-radii.png diff --git a/third_party/webrender/wrench/reftests/border/border-suite-2.png b/third_party/webrender/wrench/reftests/border/border-suite-2.png Binary files differindex cb43146c820..519e43fc52e 100644 --- a/third_party/webrender/wrench/reftests/border/border-suite-2.png +++ b/third_party/webrender/wrench/reftests/border/border-suite-2.png diff --git a/third_party/webrender/wrench/reftests/border/border-suite-3.png b/third_party/webrender/wrench/reftests/border/border-suite-3.png Binary files differindex b2b7d7347cc..dcd6f5d7602 100644 --- a/third_party/webrender/wrench/reftests/border/border-suite-3.png +++ b/third_party/webrender/wrench/reftests/border/border-suite-3.png diff --git a/third_party/webrender/wrench/reftests/border/border-suite.png b/third_party/webrender/wrench/reftests/border/border-suite.png Binary files differindex 8e8870afdf7..8ab844c5856 100644 --- a/third_party/webrender/wrench/reftests/border/border-suite.png +++ b/third_party/webrender/wrench/reftests/border/border-suite.png diff --git a/third_party/webrender/wrench/reftests/border/degenerate-curve.png b/third_party/webrender/wrench/reftests/border/degenerate-curve.png Binary files differindex 9a4eb6736f0..9c3805e33e5 100644 --- a/third_party/webrender/wrench/reftests/border/degenerate-curve.png +++ b/third_party/webrender/wrench/reftests/border/degenerate-curve.png diff --git a/third_party/webrender/wrench/reftests/border/dotted-corner-small-radius.png b/third_party/webrender/wrench/reftests/border/dotted-corner-small-radius.png Binary files differindex 99405da1a33..303e1719471 100644 --- a/third_party/webrender/wrench/reftests/border/dotted-corner-small-radius.png +++ b/third_party/webrender/wrench/reftests/border/dotted-corner-small-radius.png diff --git a/third_party/webrender/wrench/reftests/border/overlapping.png b/third_party/webrender/wrench/reftests/border/overlapping.png Binary files differindex bf12c8d81ab..38dcdc79e22 100644 --- a/third_party/webrender/wrench/reftests/border/overlapping.png +++ b/third_party/webrender/wrench/reftests/border/overlapping.png diff --git a/third_party/webrender/wrench/reftests/border/reftest.list b/third_party/webrender/wrench/reftests/border/reftest.list index a5551e82705..3b64eb89bec 100644 --- a/third_party/webrender/wrench/reftests/border/reftest.list +++ b/third_party/webrender/wrench/reftests/border/reftest.list @@ -1,35 +1,34 @@ platform(linux,mac) == border-clamp-corner-radius.yaml border-clamp-corner-radius.png fuzzy(1,840) == border-gradient-simple.yaml border-gradient-simple-ref.yaml platform(linux,mac) == border-gradient-nine-patch.yaml border-gradient-nine-patch.png -fuzzy-if(platform(swgl),1,4) == border-radial-gradient-simple.yaml border-radial-gradient-simple-ref.yaml +== border-radial-gradient-simple.yaml border-radial-gradient-simple-ref.yaml platform(linux,mac) == border-radial-gradient-nine-patch.yaml border-radial-gradient-nine-patch.png -== fuzzy(1,10) border-radii.yaml border-radii.png +== border-radii.yaml border-radii.png == border-none.yaml border-none-ref.yaml -fuzzy(128,69) fuzzy-if(platform(swgl),2,118) == border-overlapping-corner.yaml border-overlapping-corner-ref.yaml +fuzzy(128,69) == border-overlapping-corner.yaml border-overlapping-corner-ref.yaml == border-overlapping-edge.yaml border-overlapping-edge-ref.yaml == border-invisible.yaml border-invisible-ref.yaml -platform(linux,mac) fuzzy(3,80) == border-suite.yaml border-suite.png -platform(linux,mac) fuzzy(8,105) == border-suite-2.yaml border-suite-2.png -platform(linux,mac) fuzzy(1,10) == border-suite-3.yaml border-suite-3.png +platform(linux,mac) == border-suite.yaml border-suite.png +platform(linux,mac) fuzzy(8,80) == border-suite-2.yaml border-suite-2.png +platform(linux,mac) == border-suite-3.yaml border-suite-3.png skip_on(android,device) == border-double-simple.yaml border-double-simple-ref.yaml # Fails on Pixel2 == border-double-simple-2.yaml border-double-simple-2-ref.yaml skip_on(android,device) fuzzy(64,24) == border-groove-simple.yaml border-groove-simple-ref.yaml # Fails on Pixel2 skip_on(android,device) fuzzy(64,24) == border-ridge-simple.yaml border-ridge-simple-ref.yaml # Fails on Pixel2 -platform(linux,mac) fuzzy(1,26) == degenerate-curve.yaml degenerate-curve.png +platform(linux,mac) fuzzy(1,3) == degenerate-curve.yaml degenerate-curve.png platform(linux,mac) == border-image.yaml border-image-ref.png platform(linux,mac) == border-image-empty-slice.yaml border-image-empty-slice-ref.png platform(linux,mac) == border-image-round.yaml border-image-round-ref.png == border-image-crash.yaml border-image-crash-ref.yaml fuzzy(1,5000) == border-image-fill.yaml border-image-fill-ref.png -fuzzy(2,25360) == border-image-fill-2.yaml border-image-fill-2-ref.png -fuzzy-if(platform(swgl),1,8) == border-no-bogus-line.yaml border-no-bogus-line-ref.png -platform(linux,mac) fuzzy(1,130) == dotted-corner-small-radius.yaml dotted-corner-small-radius.png -fuzzy-if(platform(swgl),1,20) == overlapping.yaml overlapping.png +== border-no-bogus-line.yaml border-no-bogus-line-ref.png +platform(linux,mac) == dotted-corner-small-radius.yaml dotted-corner-small-radius.png +== overlapping.yaml overlapping.png == zero-width.yaml blank.yaml platform(linux,mac) == small-dotted-border.yaml small-dotted-border.png -fuzzy(1,30) == discontinued-dash.yaml discontinued-dash.png +== discontinued-dash.yaml discontinued-dash.png platform(linux,mac) == border-dashed-dotted-caching.yaml border-dashed-dotted-caching.png != small-inset-outset.yaml small-inset-outset-notref.yaml -fuzzy(1,90) == no-aa.yaml green-square.yaml +fuzzy(1,16) == no-aa.yaml green-square.yaml skip_on(android,device) == border-double-1px.yaml border-double-1px-ref.yaml # Fails on Pixel2 == max-scale.yaml max-scale-ref.yaml diff --git a/third_party/webrender/wrench/reftests/border/small-dotted-border.png b/third_party/webrender/wrench/reftests/border/small-dotted-border.png Binary files differindex b867b04c2b9..adfb6eff508 100644 --- a/third_party/webrender/wrench/reftests/border/small-dotted-border.png +++ b/third_party/webrender/wrench/reftests/border/small-dotted-border.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-border-radii.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-border-radii.png Binary files differindex 0741826160a..6143955683e 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-border-radii.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-border-radii.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-cache.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-cache.png Binary files differindex 96d2c7fed3b..e70e13c6918 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-cache.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-cache.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-huge-radius.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-huge-radius.png Binary files differindex f2be685139f..a28981ba0f9 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-huge-radius.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-huge-radius.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-large-blur-radius-2.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-large-blur-radius-2.png Binary files differindex 9111ca44131..edea0e497f4 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-large-blur-radius-2.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-large-blur-radius-2.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-large-blur-radius-3.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-large-blur-radius-3.png Binary files differindex 2b954422808..a723604095d 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-large-blur-radius-3.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-large-blur-radius-3.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-spread.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-spread.png Binary files differindex 7c1910aba45..6fbd03aafff 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-spread.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-spread.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-stretch-mode-x.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-stretch-mode-x.png Binary files differindex 5d631c182bd..662079e9f4f 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-stretch-mode-x.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-stretch-mode-x.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-stretch-mode-y.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-stretch-mode-y.png Binary files differindex d76f58e170f..8ba78979873 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-stretch-mode-y.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-stretch-mode-y.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-suite-blur.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-suite-blur.png Binary files differindex 67b46a609ee..b1433cb9dbb 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-suite-blur.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-suite-blur.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-suite-no-blur.png b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-suite-no-blur.png Binary files differindex 66e6dd9c386..a8c3b1d0cab 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/box-shadow-suite-no-blur.png +++ b/third_party/webrender/wrench/reftests/boxshadow/box-shadow-suite-no-blur.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/boxshadow-spread-only-ref.png b/third_party/webrender/wrench/reftests/boxshadow/boxshadow-spread-only-ref.png Binary files differindex 314b17f9639..a05bf7314eb 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/boxshadow-spread-only-ref.png +++ b/third_party/webrender/wrench/reftests/boxshadow/boxshadow-spread-only-ref.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-alpha.png b/third_party/webrender/wrench/reftests/boxshadow/inset-alpha.png Binary files differindex bc4c0a1f372..4da7a868577 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-alpha.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-alpha.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-border-radius.png b/third_party/webrender/wrench/reftests/boxshadow/inset-border-radius.png Binary files differindex 1572de5b777..b8e3d5034a6 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-border-radius.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-border-radius.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-downscale.png b/third_party/webrender/wrench/reftests/boxshadow/inset-downscale.png Binary files differindex aef79686003..40534805e1d 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-downscale.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-downscale.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-large-offset-ref.png b/third_party/webrender/wrench/reftests/boxshadow/inset-large-offset-ref.png Binary files differindex e1d189eaa08..92263830912 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-large-offset-ref.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-large-offset-ref.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-mask-region.png b/third_party/webrender/wrench/reftests/boxshadow/inset-mask-region.png Binary files differindex b696cbfd419..5145148cd33 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-mask-region.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-mask-region.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-neg-offset.png b/third_party/webrender/wrench/reftests/boxshadow/inset-neg-offset.png Binary files differindex b6b232a1d00..462d46f8503 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-neg-offset.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-neg-offset.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-no-blur-radius-ref.png b/third_party/webrender/wrench/reftests/boxshadow/inset-no-blur-radius-ref.png Binary files differindex 9c6a8d4bcfb..dfc23875e3a 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-no-blur-radius-ref.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-no-blur-radius-ref.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-offset.png b/third_party/webrender/wrench/reftests/boxshadow/inset-offset.png Binary files differindex d5f41456fee..fed26f950ea 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-offset.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-offset.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/inset-subpx.png b/third_party/webrender/wrench/reftests/boxshadow/inset-subpx.png Binary files differindex 89ca1e44b11..b15cf63d80c 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/inset-subpx.png +++ b/third_party/webrender/wrench/reftests/boxshadow/inset-subpx.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/no-stretch.png b/third_party/webrender/wrench/reftests/boxshadow/no-stretch.png Binary files differindex e916a7d3829..c89ec417e6a 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/no-stretch.png +++ b/third_party/webrender/wrench/reftests/boxshadow/no-stretch.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/overlap1.png b/third_party/webrender/wrench/reftests/boxshadow/overlap1.png Binary files differindex a47c3ad6963..0ab363f03fb 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/overlap1.png +++ b/third_party/webrender/wrench/reftests/boxshadow/overlap1.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/overlap2.png b/third_party/webrender/wrench/reftests/boxshadow/overlap2.png Binary files differindex 6b7b1760118..33401618904 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/overlap2.png +++ b/third_party/webrender/wrench/reftests/boxshadow/overlap2.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/reftest.list b/third_party/webrender/wrench/reftests/boxshadow/reftest.list index 4f44cfab3d6..92ff47e495e 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/reftest.list +++ b/third_party/webrender/wrench/reftests/boxshadow/reftest.list @@ -1,21 +1,21 @@ != inset-simple.yaml inset-simple-ref.yaml != inset-spread.yaml inset-spread-ref.yaml -fuzzy-if(platform(swgl),3,2) == inset-no-blur-radius.yaml inset-no-blur-radius-ref.png +== inset-no-blur-radius.yaml inset-no-blur-radius-ref.png == inset-spread-large.yaml inset-spread-large-ref.yaml platform(linux,mac) == inset-alpha.yaml inset-alpha.png platform(linux,mac) == boxshadow-spread-only.yaml boxshadow-spread-only-ref.png == box-shadow-clip.yaml box-shadow-clip-ref.yaml -fuzzy(1,402) fuzzy-if(platform(swgl),2,1208) == inset-large-offset.yaml inset-large-offset-ref.png +fuzzy(1,396) == inset-large-offset.yaml inset-large-offset-ref.png platform(linux,mac) == inset-border-radius.yaml inset-border-radius.png platform(linux,mac) == inset-offset.yaml inset-offset.png platform(linux,mac) == inset-neg-offset.yaml inset-neg-offset.png == box-shadow-empty.yaml blank.yaml platform(linux,mac) == box-shadow-suite-no-blur.yaml box-shadow-suite-no-blur.png platform(linux,mac) == box-shadow-suite-blur.yaml box-shadow-suite-blur.png -fuzzy(1,8) == box-shadow-large-blur-radius.yaml box-shadow-large-blur-radius-ref.yaml +== box-shadow-large-blur-radius.yaml box-shadow-large-blur-radius-ref.yaml fuzzy(1,6388) == rounding.yaml rounding-ref.yaml platform(linux,mac) == box-shadow-border-radii.yaml box-shadow-border-radii.png -skip_on(android) fuzzy-if(platform(swgl),9,34) == box-shadow-spread.yaml box-shadow-spread.png # Too wide for Android +skip_on(android) == box-shadow-spread.yaml box-shadow-spread.png # Too wide for Android == box-shadow-spread-radii.yaml box-shadow-spread-radii-ref.yaml == invalid.yaml invalid-ref.yaml == inset-empty.yaml blank.yaml @@ -23,7 +23,7 @@ platform(linux,mac) == inset-subpx.yaml inset-subpx.png platform(linux,mac) fuzzy(1,4) == inset-downscale.yaml inset-downscale.png platform(linux,mac) fuzzy(1,979) == box-shadow-cache.yaml box-shadow-cache.png platform(linux,mac) fuzzy(1,685) == overlap1.yaml overlap1.png -fuzzy(2,757) fuzzy-if(platform(swgl),2,2414) == overlap2.yaml overlap2.png +fuzzy(2,691) == overlap2.yaml overlap2.png platform(linux,mac) fuzzy(1,48) == no-stretch.yaml no-stretch.png platform(linux,mac) fuzzy(1,9) == box-shadow-stretch-mode-x.yaml box-shadow-stretch-mode-x.png platform(linux,mac) fuzzy(1,41) == box-shadow-stretch-mode-y.yaml box-shadow-stretch-mode-y.png @@ -33,5 +33,5 @@ platform(linux,mac) fuzzy(1,14) == inset-mask-region.yaml inset-mask-region.png fuzzy(1,5) platform(linux,mac) == box-shadow-huge-radius.yaml box-shadow-huge-radius.png platform(linux,mac) == box-shadow-large-blur-radius-2.yaml box-shadow-large-blur-radius-2.png -platform(linux,mac) fuzzy(1,8) == box-shadow-large-blur-radius-3.yaml box-shadow-large-blur-radius-3.png +platform(linux,mac) == box-shadow-large-blur-radius-3.yaml box-shadow-large-blur-radius-3.png platform(linux,mac) fuzzy(1,79) == scale.yaml scale.png diff --git a/third_party/webrender/wrench/reftests/boxshadow/scale.png b/third_party/webrender/wrench/reftests/boxshadow/scale.png Binary files differindex 2f635e91804..49aa8e8e053 100644 --- a/third_party/webrender/wrench/reftests/boxshadow/scale.png +++ b/third_party/webrender/wrench/reftests/boxshadow/scale.png diff --git a/third_party/webrender/wrench/reftests/clip/blend-container-ref.yaml b/third_party/webrender/wrench/reftests/clip/blend-container-ref.yaml deleted file mode 100644 index 2d3464959dd..00000000000 --- a/third_party/webrender/wrench/reftests/clip/blend-container-ref.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -root: - items: - - type: rect - bounds: [ 50, 50, 100, 100 ] - color: blue diff --git a/third_party/webrender/wrench/reftests/clip/blend-container.yaml b/third_party/webrender/wrench/reftests/clip/blend-container.yaml deleted file mode 100644 index 2a18d3a5e3d..00000000000 --- a/third_party/webrender/wrench/reftests/clip/blend-container.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Verify that clip-chains are correctly applied to root level blend containers (which get -# optimized to be tile caches). ---- -root: - items: - - type: clip - id: 2 - bounds: [ 50, 50, 100, 100 ] - - type: stacking-context - blend-container: true - clip-node: 2 - items: - - type: rect - bounds: [ 0, 0, 200, 200 ] - color: blue diff --git a/third_party/webrender/wrench/reftests/clip/border-with-rounded-clip.png b/third_party/webrender/wrench/reftests/clip/border-with-rounded-clip.png Binary files differindex 120b21b6cc3..f3400057149 100644 --- a/third_party/webrender/wrench/reftests/clip/border-with-rounded-clip.png +++ b/third_party/webrender/wrench/reftests/clip/border-with-rounded-clip.png diff --git a/third_party/webrender/wrench/reftests/clip/clip-45-degree-rotation-ref.png b/third_party/webrender/wrench/reftests/clip/clip-45-degree-rotation-ref.png Binary files differindex 7b27e84239d..620eb49e435 100644 --- a/third_party/webrender/wrench/reftests/clip/clip-45-degree-rotation-ref.png +++ b/third_party/webrender/wrench/reftests/clip/clip-45-degree-rotation-ref.png diff --git a/third_party/webrender/wrench/reftests/clip/clip-ellipse.png b/third_party/webrender/wrench/reftests/clip/clip-ellipse.png Binary files differindex 49570d0359b..49624825573 100644 --- a/third_party/webrender/wrench/reftests/clip/clip-ellipse.png +++ b/third_party/webrender/wrench/reftests/clip/clip-ellipse.png diff --git a/third_party/webrender/wrench/reftests/clip/clip-filter-raster-root-ref.yaml b/third_party/webrender/wrench/reftests/clip/clip-filter-raster-root-ref.yaml deleted file mode 100644 index 7fde17f1845..00000000000 --- a/third_party/webrender/wrench/reftests/clip/clip-filter-raster-root-ref.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -root: - items: - - - type: "reference-frame" - transform: [perspective(10), rotate(45)] - transform-origin: 500 100 - items: - - - type: "stacking-context" - filters: [identity] - items: - - - bounds: [100, 100, 200, 200] - type: rect - color: red diff --git a/third_party/webrender/wrench/reftests/clip/clip-filter-raster-root.yaml b/third_party/webrender/wrench/reftests/clip/clip-filter-raster-root.yaml deleted file mode 100644 index 95ffc7dbd05..00000000000 --- a/third_party/webrender/wrench/reftests/clip/clip-filter-raster-root.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Test that a local-space clip when applied to a surface (due to the filter) that is also -# a raster root (due to the perspective) is correctly applied. ---- -root: - items: - - - type: "reference-frame" - transform: [perspective(10), rotate(45)] - transform-origin: 500 100 - items: - - - bounds: [100, 100, 200, 200] - type: clip - id: 2 - - - clip-node: 2 - type: "stacking-context" - filters: [identity] - items: - - - bounds: [0, 0, 400, 400] - type: rect - color: red diff --git a/third_party/webrender/wrench/reftests/clip/clip-mode.png b/third_party/webrender/wrench/reftests/clip/clip-mode.png Binary files differindex e40a9db215c..69f2b8b4ae8 100644 --- a/third_party/webrender/wrench/reftests/clip/clip-mode.png +++ b/third_party/webrender/wrench/reftests/clip/clip-mode.png diff --git a/third_party/webrender/wrench/reftests/clip/clip-rectangle-redundant-sc-ref.yaml b/third_party/webrender/wrench/reftests/clip/clip-rectangle-redundant-sc-ref.yaml deleted file mode 100644 index 5946aeedae6..00000000000 --- a/third_party/webrender/wrench/reftests/clip/clip-rectangle-redundant-sc-ref.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -root: - items: - - - bounds: [0, 0, 100, 100] - type: rect - color: blue diff --git a/third_party/webrender/wrench/reftests/clip/clip-rectangle-redundant-sc.yaml b/third_party/webrender/wrench/reftests/clip/clip-rectangle-redundant-sc.yaml deleted file mode 100644 index ac88edb489d..00000000000 --- a/third_party/webrender/wrench/reftests/clip/clip-rectangle-redundant-sc.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Test that rect clips on redundant stacking contexts are applied to child prims ---- -root: - items: - - - bounds: [0, 0, 100, 100] - type: clip - id: 2 - - - clip-node: 2 - type: "stacking-context" - items: - - - bounds: [0, 0, 200, 200] - type: rect - color: blue diff --git a/third_party/webrender/wrench/reftests/clip/reftest.list b/third_party/webrender/wrench/reftests/clip/reftest.list index 155678562ff..8518911a9f4 100644 --- a/third_party/webrender/wrench/reftests/clip/reftest.list +++ b/third_party/webrender/wrench/reftests/clip/reftest.list @@ -1,10 +1,10 @@ platform(linux,mac) == border-with-rounded-clip.yaml border-with-rounded-clip.png -fuzzy-if(platform(swgl),1,4) == clip-mode.yaml clip-mode.png -fuzzy-if(platform(swgl),1,80) == clip-ellipse.yaml clip-ellipse.png +== clip-mode.yaml clip-mode.png +== clip-ellipse.yaml clip-ellipse.png platform(linux,mac) == clip-45-degree-rotation.yaml clip-45-degree-rotation-ref.png == clip-3d-transform.yaml clip-3d-transform-ref.yaml fuzzy(1,4) == clip-corner-overlap.yaml clip-corner-overlap-ref.yaml -fuzzy(9,60) == custom-clip-chains.yaml custom-clip-chains-ref.yaml +fuzzy(8,60) == custom-clip-chains.yaml custom-clip-chains-ref.yaml == custom-clip-chain-node-ancestors.yaml custom-clip-chain-node-ancestors-ref.yaml == fixed-position-clipping.yaml fixed-position-clipping-ref.yaml platform(linux,mac) == segmentation-with-other-coordinate-system-clip.yaml segmentation-with-other-coordinate-system-clip.png @@ -16,6 +16,3 @@ color_targets(1) alpha_targets(0) == clip-out-rotation.yaml blank.yaml # Unexpec == clipped-occlusion.yaml clipped-occlusion-ref.yaml == clip-empty-inner-rect.yaml clip-empty-inner-rect-ref.yaml == iframe-nested-in-stacking-context.yaml iframe-nested-in-stacking-context-ref.yaml -== clip-rectangle-redundant-sc.yaml clip-rectangle-redundant-sc-ref.yaml -fuzzy(128,707) == clip-filter-raster-root.yaml clip-filter-raster-root-ref.yaml -== blend-container.yaml blend-container-ref.yaml diff --git a/third_party/webrender/wrench/reftests/compositor-surface/basic-ref.yaml b/third_party/webrender/wrench/reftests/compositor-surface/basic-ref.yaml deleted file mode 100644 index 1a567dd9e68..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/basic-ref.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- -root: - items: - - type: rect - bounds: [50, 50, 462, 462] - color: green - - type: rect - bounds: [125, 125, 312, 312] - color: [128, 128, 128, 0.5] - - image: transparent-checkerboard(2,16,16) - bounds: [150, 150, 262, 262] - - type: rect - bounds: [200, 200, 162, 162] - color: [0, 0, 255, 0.5] - - text: "Subpixel AA Text" - origin: 80 100 - size: 20 - color: black diff --git a/third_party/webrender/wrench/reftests/compositor-surface/basic.yaml b/third_party/webrender/wrench/reftests/compositor-surface/basic.yaml deleted file mode 100644 index 0ea9b8a394a..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/basic.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Test that basic functionality of non-opaque compositor surfaces -# is blending correctly, and that subpixel AA is used outside the -# compositor surface region. ---- -root: - items: - - type: rect - bounds: [50, 50, 462, 462] - color: green - - type: rect - bounds: [125, 125, 312, 312] - color: [128, 128, 128, 0.5] - - image: transparent-checkerboard(2,16,16) - bounds: [150, 150, 262, 262] - prefer-compositor-surface: true - - type: rect - bounds: [200, 200, 162, 162] - color: [0, 0, 255, 0.5] - - text: "Subpixel AA Text" - origin: 80 100 - size: 20 - color: black diff --git a/third_party/webrender/wrench/reftests/compositor-surface/blank.yaml b/third_party/webrender/wrench/reftests/compositor-surface/blank.yaml deleted file mode 100644 index c4eb3ab6730..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/blank.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -root: diff --git a/third_party/webrender/wrench/reftests/compositor-surface/mix-blend-ref.yaml b/third_party/webrender/wrench/reftests/compositor-surface/mix-blend-ref.yaml deleted file mode 100644 index c8ce2fb444a..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/mix-blend-ref.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -root: - items: - - type: stacking-context - blend-container: true - items: - - type: rect - bounds: [50, 50, 362, 362] - color: green - - image: transparent-checkerboard(2,16,16) - bounds: [100, 100, 262, 262] - - type: stacking-context - mix-blend-mode: multiply - items: - - type: rect - bounds: [150, 150, 162, 162] - color: red diff --git a/third_party/webrender/wrench/reftests/compositor-surface/mix-blend.yaml b/third_party/webrender/wrench/reftests/compositor-surface/mix-blend.yaml deleted file mode 100644 index fb8afe312d1..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/mix-blend.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Verify that we don't promote compositor surfaces within a root level blend container, -# as that can prevent correct blending with the compositor surface. ---- -root: - items: - - type: stacking-context - blend-container: true - items: - - type: rect - bounds: [50, 50, 362, 362] - color: green - - image: transparent-checkerboard(2,16,16) - bounds: [100, 100, 262, 262] - prefer-compositor-surface: true - - type: stacking-context - mix-blend-mode: multiply - items: - - type: rect - bounds: [150, 150, 162, 162] - color: red diff --git a/third_party/webrender/wrench/reftests/compositor-surface/picture-passthrough.yaml b/third_party/webrender/wrench/reftests/compositor-surface/picture-passthrough.yaml deleted file mode 100644 index cfe73293d29..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/picture-passthrough.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# Ensure that compositor surfaces which exist in child pictures with -# None for composite mode don't get promoted to a compositor surface, -# as we don't currently account for these in `compositor_surface_count`. ---- -root: - items: - - type: stacking-context - backdrop-root: true - items: - - type: backdrop-filter - bounds: 0 0 256 256 - filters: invert(1) - - image: checkerboard(2,16,16) - bounds: [150, 150, 262, 262] - prefer-compositor-surface: true diff --git a/third_party/webrender/wrench/reftests/compositor-surface/reftest.list b/third_party/webrender/wrench/reftests/compositor-surface/reftest.list deleted file mode 100644 index a593c1003a5..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/reftest.list +++ /dev/null @@ -1,4 +0,0 @@ -skip_on(android) fuzzy(2,500) == basic.yaml basic-ref.yaml -== too-many-surfaces.yaml too-many-surfaces-ref.yaml -!= picture-passthrough.yaml blank.yaml -fuzzy(2,1000) == mix-blend.yaml mix-blend-ref.yaml diff --git a/third_party/webrender/wrench/reftests/compositor-surface/too-many-surfaces-ref.yaml b/third_party/webrender/wrench/reftests/compositor-surface/too-many-surfaces-ref.yaml deleted file mode 100644 index 00f9f7da6b7..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/too-many-surfaces-ref.yaml +++ /dev/null @@ -1,31 +0,0 @@ ---- -root: - items: - - type: rect - bounds: [50, 50, 400, 88] - color: green - - image: transparent-checkerboard(2,8,8) - bounds: [60, 60, 68, 68] - - type: rect - bounds: [55, 70, 390, 5] - color: [255, 0, 0, 0.9] - - image: transparent-checkerboard(2,8,8) - bounds: [138, 60, 68, 68] - - type: rect - bounds: [55, 80, 390, 5] - color: [255, 0, 0, 0.9] - - image: transparent-checkerboard(2,8,8) - bounds: [216, 60, 68, 68] - - type: rect - bounds: [55, 90, 390, 5] - color: [255, 0, 0, 0.9] - - image: transparent-checkerboard(2,8,8) - bounds: [294, 60, 68, 68] - - type: rect - bounds: [55, 100, 390, 5] - color: [255, 0, 0, 0.9] - - image: transparent-checkerboard(2,8,8) - bounds: [372, 60, 68, 68] - - type: rect - bounds: [55, 110, 390, 5] - color: [255, 0, 0, 0.9] diff --git a/third_party/webrender/wrench/reftests/compositor-surface/too-many-surfaces.yaml b/third_party/webrender/wrench/reftests/compositor-surface/too-many-surfaces.yaml deleted file mode 100644 index e5744ddedc7..00000000000 --- a/third_party/webrender/wrench/reftests/compositor-surface/too-many-surfaces.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# Test that when there are too many compositor surfaces present that -# the additional compositor surfaces are correctly drawn as regular -# primitives into picture cache content tiles. ---- -root: - items: - - type: rect - bounds: [50, 50, 400, 88] - color: green - - image: transparent-checkerboard(2,8,8) - bounds: [60, 60, 68, 68] - prefer-compositor-surface: true - - type: rect - bounds: [55, 70, 390, 5] - color: [255, 0, 0, 0.9] - - image: transparent-checkerboard(2,8,8) - bounds: [138, 60, 68, 68] - prefer-compositor-surface: true - - type: rect - bounds: [55, 80, 390, 5] - color: [255, 0, 0, 0.9] - - image: transparent-checkerboard(2,8,8) - bounds: [216, 60, 68, 68] - prefer-compositor-surface: true - - type: rect - bounds: [55, 90, 390, 5] - color: [255, 0, 0, 0.9] - - image: transparent-checkerboard(2,8,8) - bounds: [294, 60, 68, 68] - prefer-compositor-surface: true - - type: rect - bounds: [55, 100, 390, 5] - color: [255, 0, 0, 0.9] - - image: transparent-checkerboard(2,8,8) - bounds: [372, 60, 68, 68] - prefer-compositor-surface: true - - type: rect - bounds: [55, 110, 390, 5] - color: [255, 0, 0, 0.9] diff --git a/third_party/webrender/wrench/reftests/filters/backdrop-filter-basic.yaml b/third_party/webrender/wrench/reftests/filters/backdrop-filter-basic.yaml index 837d4039780..e71decc659a 100644 --- a/third_party/webrender/wrench/reftests/filters/backdrop-filter-basic.yaml +++ b/third_party/webrender/wrench/reftests/filters/backdrop-filter-basic.yaml @@ -3,7 +3,6 @@ root: items: - type: stacking-context - backdrop-root: true bounds: 0 0 0 0 items: - type: rect diff --git a/third_party/webrender/wrench/reftests/filters/backdrop-filter-perspective.png b/third_party/webrender/wrench/reftests/filters/backdrop-filter-perspective.png Binary files differindex 34166161664..83e181e970b 100644 --- a/third_party/webrender/wrench/reftests/filters/backdrop-filter-perspective.png +++ b/third_party/webrender/wrench/reftests/filters/backdrop-filter-perspective.png diff --git a/third_party/webrender/wrench/reftests/filters/backdrop-filter-perspective.yaml b/third_party/webrender/wrench/reftests/filters/backdrop-filter-perspective.yaml index ca834e5ff71..43d29ddf1d6 100644 --- a/third_party/webrender/wrench/reftests/filters/backdrop-filter-perspective.yaml +++ b/third_party/webrender/wrench/reftests/filters/backdrop-filter-perspective.yaml @@ -4,7 +4,6 @@ root: items: - type: stacking-context bounds: 0 0 0 0 - backdrop-root: true perspective: 500 items: - type: rect diff --git a/third_party/webrender/wrench/reftests/filters/blend-clipped-raster-root.png b/third_party/webrender/wrench/reftests/filters/blend-clipped-raster-root.png Binary files differdeleted file mode 100644 index 9d334c40f59..00000000000 --- a/third_party/webrender/wrench/reftests/filters/blend-clipped-raster-root.png +++ /dev/null diff --git a/third_party/webrender/wrench/reftests/filters/blend-clipped-raster-root.yaml b/third_party/webrender/wrench/reftests/filters/blend-clipped-raster-root.yaml deleted file mode 100644 index fdb62af4832..00000000000 --- a/third_party/webrender/wrench/reftests/filters/blend-clipped-raster-root.yaml +++ /dev/null @@ -1,22 +0,0 @@ ---- -root: - items: - - - bounds: [0, 100, 1000, 1000] - type: clip - id: 3 - - - bounds: [200, 200, 0, 0] - clip-node: 3 - type: "stacking-context" - transform: rotate(10) - items: - - - type: "stacking-context" - filters: - - opacity(0.5) - items: - - - bounds: [0, 0, 1000, 500] - type: rect - color: green diff --git a/third_party/webrender/wrench/reftests/filters/blend-clipped.png b/third_party/webrender/wrench/reftests/filters/blend-clipped.png Binary files differindex e8240cca611..04b63a8909f 100644 --- a/third_party/webrender/wrench/reftests/filters/blend-clipped.png +++ b/third_party/webrender/wrench/reftests/filters/blend-clipped.png diff --git a/third_party/webrender/wrench/reftests/filters/drop-shadow-inverse-scale.yaml b/third_party/webrender/wrench/reftests/filters/drop-shadow-inverse-scale.yaml deleted file mode 100644 index 229bdaa4b61..00000000000 --- a/third_party/webrender/wrench/reftests/filters/drop-shadow-inverse-scale.yaml +++ /dev/null @@ -1,14 +0,0 @@ -# Ensure that zero sized drop shadows handle reflection scale transforms without crashing ---- -root: - items: - - - type: "stacking-context" - bounds: [100, 100, 100, 100] - filters: drop-shadow([0, 1], 1, red) - transform: scale(-1, 1) - items: - - - type: rect - color: green - bounds: [0, 0, 1, 0] diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur-clamping-ref.yaml b/third_party/webrender/wrench/reftests/filters/filter-blur-clamping-ref.yaml index a06489467a7..cdc6c56a47b 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur-clamping-ref.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-blur-clamping-ref.yaml @@ -4,14 +4,14 @@ root: items: - type: stacking-context bounds: [100, 100, 300, 300] - filters: blur(100, 100) + filters: blur(100) items: - type: rect bounds: [0, 0, 100, 100] color: 0 255 0 1.0 - type: stacking-context bounds: [400, 100, 300, 300] - filters: blur(50, 50) + filters: blur(50) items: - type: rect bounds: [0, 0, 100, 100] diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur-clamping.yaml b/third_party/webrender/wrench/reftests/filters/filter-blur-clamping.yaml index 0d81a616ee9..614aa8ee02a 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur-clamping.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-blur-clamping.yaml @@ -9,7 +9,7 @@ root: - type: stacking-context bounds: [0, 0, 300, 300] # Blur will be 20 * 10(scale) = 200 and it should then be clamped to 100 - filters: blur(20, 20) + filters: blur(20) items: - type: rect bounds: [0, 0, 10, 10] @@ -23,7 +23,7 @@ root: # Blur should be 500 * 0.1(scale) = 50. This tests to make sure clamping # does not occur before applying scale factors, otherwise 500 would be # clamped to 100. - filters: blur(500, 500) + filters: blur(500) items: - type: rect bounds: [0, 0, 1000, 1000] diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur-huge.yaml b/third_party/webrender/wrench/reftests/filters/filter-blur-huge.yaml index 13a4c72fd8f..2f76dd289ed 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur-huge.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-blur-huge.yaml @@ -4,7 +4,7 @@ root: items: - type: stacking-context bounds: [10, 10, 260, 260] - filters: blur(1000000, 1000000) + filters: blur(1000000) items: - image: checkerboard(2, 16, 16) bounds: [10, 10, 260, 260] diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-ref.yaml b/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-ref.yaml index 6a5f314df07..82e5b026824 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-ref.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-ref.yaml @@ -4,7 +4,7 @@ root: items: - type: stacking-context bounds: [0, 0, 500, 500] - filters: blur(10, 10) + filters: blur(10) items: - type: rect bounds: [50, 50, 250, 250] diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-xonly.png b/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-xonly.png Binary files differindex 48f8913752d..6bb0834bc36 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-xonly.png +++ b/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-xonly.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-xonly.yaml b/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-xonly.yaml index 18a897131d4..b244741cda3 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-xonly.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-blur-scaled-xonly.yaml @@ -9,7 +9,7 @@ root: items: - type: stacking-context bounds: [0, 0, 100, 100] - filters: blur(2, 2) + filters: blur(2) items: - type: rect bounds: [10, 10, 50, 50] diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur-scaled.yaml b/third_party/webrender/wrench/reftests/filters/filter-blur-scaled.yaml index 0e8c9cbff97..b99b6c7f83e 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur-scaled.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-blur-scaled.yaml @@ -8,7 +8,7 @@ root: items: - type: stacking-context bounds: [0, 0, 100, 100] - filters: blur(2, 2) + filters: blur(2) items: - type: rect bounds: [10, 10, 50, 50] diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur.png b/third_party/webrender/wrench/reftests/filters/filter-blur.png Binary files differindex 996e79215e3..067f8e4af72 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur.png +++ b/third_party/webrender/wrench/reftests/filters/filter-blur.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-blur.yaml b/third_party/webrender/wrench/reftests/filters/filter-blur.yaml index ee915d7bb1a..42762f56d7b 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-blur.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-blur.yaml @@ -3,7 +3,7 @@ root: items: - type: stacking-context bounds: [100, 100, 300, 300] - filters: blur(10, 10) + filters: blur(10) items: - image: "firefox.png" bounds: 20 20 256 256 diff --git a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip-2.png b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip-2.png Binary files differindex d14182e2312..f58e15c5fcc 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip-2.png +++ b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip-2.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip-3.png b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip-3.png Binary files differindex d1bbd48c8bb..4583f5f730c 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip-3.png +++ b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip-3.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip.png b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip.png Binary files differindex d33eed0faaf..36db8671428 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip.png +++ b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-clip.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-on-viewport-edge.png b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-on-viewport-edge.png Binary files differindex 24de0898ea8..f529db8b01d 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-on-viewport-edge.png +++ b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow-on-viewport-edge.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow.png b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow.png Binary files differindex dd81aa63efa..f5c9253dcc1 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-drop-shadow.png +++ b/third_party/webrender/wrench/reftests/filters/filter-drop-shadow.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-large-blur-radius.png b/third_party/webrender/wrench/reftests/filters/filter-large-blur-radius.png Binary files differindex 24db853d44d..3b0a501ded5 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-large-blur-radius.png +++ b/third_party/webrender/wrench/reftests/filters/filter-large-blur-radius.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-large-blur-radius.yaml b/third_party/webrender/wrench/reftests/filters/filter-large-blur-radius.yaml index 6ce5f4cb223..87ecb096277 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-large-blur-radius.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-large-blur-radius.yaml @@ -3,7 +3,7 @@ root: items: - type: stacking-context bounds: 100 100 512 512 - filters: blur(100, 100) + filters: blur(100) items: - type: rect bounds: 0 0 512 512 diff --git a/third_party/webrender/wrench/reftests/filters/filter-long-chain.png b/third_party/webrender/wrench/reftests/filters/filter-long-chain.png Binary files differindex 6e58dd6300d..4de04ead672 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-long-chain.png +++ b/third_party/webrender/wrench/reftests/filters/filter-long-chain.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-long-chain.yaml b/third_party/webrender/wrench/reftests/filters/filter-long-chain.yaml index a0f9d698bd7..0fe7bb614ec 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-long-chain.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-long-chain.yaml @@ -11,7 +11,7 @@ root: opacity(0.8), saturate(10), sepia(0.4), - "blur(3,3)", + blur(3), "drop-shadow([73, 73], 3, [0, 255, 0, 1])"] items: - type: rect diff --git a/third_party/webrender/wrench/reftests/filters/filter-small-blur-radius.png b/third_party/webrender/wrench/reftests/filters/filter-small-blur-radius.png Binary files differindex e755e2bb6cb..bbace6aa22c 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-small-blur-radius.png +++ b/third_party/webrender/wrench/reftests/filters/filter-small-blur-radius.png diff --git a/third_party/webrender/wrench/reftests/filters/filter-small-blur-radius.yaml b/third_party/webrender/wrench/reftests/filters/filter-small-blur-radius.yaml index d5b550d0ab0..7ebf95545de 100644 --- a/third_party/webrender/wrench/reftests/filters/filter-small-blur-radius.yaml +++ b/third_party/webrender/wrench/reftests/filters/filter-small-blur-radius.yaml @@ -3,7 +3,7 @@ root: items: - type: stacking-context bounds: 100 100 512 512 - filters: blur(2, 2) + filters: blur(2) items: - type: rect bounds: 0 0 512 512 diff --git a/third_party/webrender/wrench/reftests/filters/reftest.list b/third_party/webrender/wrench/reftests/filters/reftest.list index e322dc10a4d..27da91df11c 100644 --- a/third_party/webrender/wrench/reftests/filters/reftest.list +++ b/third_party/webrender/wrench/reftests/filters/reftest.list @@ -2,20 +2,20 @@ platform(linux,mac) == draw_calls(7) color_targets(7) alpha_targets(0) filter-blur.yaml filter-blur.png == isolated.yaml isolated-ref.yaml == invisible.yaml invisible-ref.yaml -fuzzy-if(platform(swgl),1,10000) == opacity.yaml opacity-ref.yaml +== opacity.yaml opacity-ref.yaml fuzzy-range(<=1,*10000) == opacity-combined.yaml opacity-combined-ref.yaml -fuzzy-if(platform(swgl),1,10000) == opacity-overlap.yaml opacity-overlap-ref.yaml +== opacity-overlap.yaml opacity-overlap-ref.yaml == filter-brightness.yaml filter-brightness-ref.yaml == filter-brightness-2.yaml filter-brightness-2-ref.yaml == filter-brightness-3.yaml filter-brightness-3-ref.yaml -fuzzy-if(platform(swgl),1,10000) == filter-brightness-4.yaml filter-brightness-4-ref.yaml +== filter-brightness-4.yaml filter-brightness-4-ref.yaml == filter-component-transfer.yaml filter-component-transfer-ref.yaml skip_on(android,device) == filter-color-matrix.yaml filter-color-matrix-ref.yaml # fails on Pixel2 == filter-contrast-gray-alpha-1.yaml filter-contrast-gray-alpha-1-ref.yaml == filter-invert.yaml filter-invert-ref.yaml == filter-invert-2.yaml filter-invert-2-ref.yaml platform(linux,mac) fuzzy(1,133) == filter-large-blur-radius.yaml filter-large-blur-radius.png -skip_on(android,device) fuzzy(1,12) fuzzy-if(platform(swgl),2,12276) == draw_calls(7) color_targets(6) alpha_targets(0) filter-small-blur-radius.yaml filter-small-blur-radius.png # fails on Pixel2 +skip_on(android,device) fuzzy(1,12) == draw_calls(6) color_targets(6) alpha_targets(0) filter-small-blur-radius.yaml filter-small-blur-radius.png # fails on Pixel2 == filter-saturate-red-1.yaml filter-saturate-red-1-ref.yaml == filter-saturate-red-2.yaml filter-saturate-red-2-ref.yaml == filter-saturate-red-3.yaml filter-saturate-red-3-ref.yaml @@ -30,13 +30,13 @@ skip_on(android,device) fuzzy(1,12) fuzzy-if(platform(swgl),2,12276) == draw_cal == filter-saturate-blue-alpha-1.yaml filter-saturate-blue-alpha-1-ref.yaml fuzzy(1,14) == filter-hue-rotate-1.yaml filter-hue-rotate-1-ref.yaml skip_on(android,device) == filter-hue-rotate-alpha-1.yaml filter-hue-rotate-alpha-1-ref.yaml # Fails on Pixel2 -skip_on(android,device) fuzzy(2,9072) fuzzy-if(platform(swgl),9,109897) == filter-long-chain.yaml filter-long-chain.png # fails on Pixel2 +skip_on(android,device) fuzzy(2,9072) == filter-long-chain.yaml filter-long-chain.png # fails on Pixel2 platform(linux,mac) == filter-drop-shadow.yaml filter-drop-shadow.png platform(linux,mac) == filter-drop-shadow-on-viewport-edge.yaml filter-drop-shadow-on-viewport-edge.png platform(linux,mac) == blend-clipped.yaml blend-clipped.png platform(linux,mac) == filter-drop-shadow-clip.yaml filter-drop-shadow-clip.png fuzzy(2,10) platform(linux,mac) == filter-drop-shadow-clip-2.yaml filter-drop-shadow-clip-2.png -fuzzy(1,26) platform(linux) == filter-drop-shadow-clip-3.yaml filter-drop-shadow-clip-3.png +platform(linux) == filter-drop-shadow-clip-3.yaml filter-drop-shadow-clip-3.png fuzzy(5,100000) == filter-drop-shadow-scaled.yaml filter-drop-shadow-scaled-ref.yaml == filter-segments.yaml filter-segments-ref.yaml == iframe-dropshadow.yaml iframe-dropshadow-ref.yaml @@ -49,7 +49,7 @@ skip_on(android,device) == filter-mix-blend-mode.yaml filter-mix-blend-mode-ref. fuzzy(3,79400) == filter-drop-shadow-blur-clamping.yaml filter-drop-shadow-blur-clamping-ref.yaml == filter-blur-scaled.yaml filter-blur-scaled-ref.yaml == filter-blur-clamping.yaml filter-blur-clamping-ref.yaml -skip_on(android,device) fuzzy(1,104) fuzzy-if(platform(swgl),4,18484) == filter-blur-scaled-xonly.yaml filter-blur-scaled-xonly.png # fails on Pixel2 +skip_on(android,device) fuzzy(1,104) == filter-blur-scaled-xonly.yaml filter-blur-scaled-xonly.png # fails on Pixel2 == svg-filter-component-transfer.yaml filter-component-transfer-ref.yaml == svg-filter-flood.yaml svg-filter-flood-ref.yaml skip_on(android,device) == svg-filter-blend.yaml svg-filter-blend-ref.yaml @@ -57,14 +57,12 @@ skip_on(android,device) == svg-filter-color-matrix.yaml filter-color-matrix-ref. platform(linux,mac) == draw_calls(8) color_targets(8) alpha_targets(0) svg-filter-blur.yaml filter-blur.png # Extra draw call is due to render task graph workaround platform(linux,mac) == svg-filter-drop-shadow.yaml svg-filter-drop-shadow.png == fuzzy(1,10000) svg-srgb-to-linear.yaml srgb-to-linear-ref.yaml -platform(linux,mac) == fuzzy(5,35250) svg-filter-drop-shadow-rotate.yaml svg-filter-drop-shadow-rotate-ref.yaml +platform(linux,mac) == fuzzy(4,28250) svg-filter-drop-shadow-rotate.yaml svg-filter-drop-shadow-rotate-ref.yaml platform(linux,mac) fuzzy(3,3184) == svg-filter-blur-transforms.yaml svg-filter-blur-transforms.png platform(linux,mac) == svg-filter-drop-shadow-on-viewport-edge.yaml svg-filter-drop-shadow-on-viewport-edge.png -fuzzy(1,1) platform(linux,mac) == svg-filter-drop-shadow-perspective.yaml svg-filter-drop-shadow-perspective.png +platform(linux,mac) == svg-filter-drop-shadow-perspective.yaml svg-filter-drop-shadow-perspective.png == backdrop-filter-basic.yaml backdrop-filter-basic-ref.yaml platform(linux,mac) == backdrop-filter-perspective.yaml backdrop-filter-perspective.png platform(linux,max) == svg-filter-offset.yaml svg-filter-offset-ref.yaml skip_on(android,device) == fuzzy(1,100) svg-filter-composite.yaml svg-filter-composite-ref.yaml skip_on(android,device) == filter-mix-blend-scaling.yaml filter-mix-blend-scaling-ref.yaml -platform(linux) == blend-clipped-raster-root.yaml blend-clipped-raster-root.png -== drop-shadow-inverse-scale.yaml blank.yaml diff --git a/third_party/webrender/wrench/reftests/filters/svg-filter-blur-transforms.png b/third_party/webrender/wrench/reftests/filters/svg-filter-blur-transforms.png Binary files differindex 890c815bef3..7bd8b5c4b67 100644 --- a/third_party/webrender/wrench/reftests/filters/svg-filter-blur-transforms.png +++ b/third_party/webrender/wrench/reftests/filters/svg-filter-blur-transforms.png diff --git a/third_party/webrender/wrench/reftests/filters/svg-filter-blur-transforms.yaml b/third_party/webrender/wrench/reftests/filters/svg-filter-blur-transforms.yaml index d78f3ae806e..4962ecbe130 100644 --- a/third_party/webrender/wrench/reftests/filters/svg-filter-blur-transforms.yaml +++ b/third_party/webrender/wrench/reftests/filters/svg-filter-blur-transforms.yaml @@ -6,8 +6,7 @@ root: transform: scale-x(0.1) rotate-z(-45) filter-primitives: - type: blur - width: 10 - height: 10 + radius: 10 in: previous color-space: srgb items: diff --git a/third_party/webrender/wrench/reftests/filters/svg-filter-blur.yaml b/third_party/webrender/wrench/reftests/filters/svg-filter-blur.yaml index 5554688500c..61381d540f6 100644 --- a/third_party/webrender/wrench/reftests/filters/svg-filter-blur.yaml +++ b/third_party/webrender/wrench/reftests/filters/svg-filter-blur.yaml @@ -5,8 +5,7 @@ root: bounds: [100, 100, 300, 300] filter-primitives: - type: blur - width: 10 - height: 10 + radius: 10 in: previous color-space: srgb items: diff --git a/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow-on-viewport-edge.png b/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow-on-viewport-edge.png Binary files differindex dcc09c53cbb..200efc99657 100644 --- a/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow-on-viewport-edge.png +++ b/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow-on-viewport-edge.png diff --git a/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow-perspective.png b/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow-perspective.png Binary files differindex 035c5d46e01..7ae7393f87a 100644 --- a/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow-perspective.png +++ b/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow-perspective.png diff --git a/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow.png b/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow.png Binary files differindex eee62c79d27..e6c2c0e49e4 100644 --- a/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow.png +++ b/third_party/webrender/wrench/reftests/filters/svg-filter-drop-shadow.png diff --git a/third_party/webrender/wrench/reftests/gradient/conic-center.png b/third_party/webrender/wrench/reftests/gradient/conic-center.png Binary files differindex 9843a2efb5e..aeb0c293981 100644 --- a/third_party/webrender/wrench/reftests/gradient/conic-center.png +++ b/third_party/webrender/wrench/reftests/gradient/conic-center.png diff --git a/third_party/webrender/wrench/reftests/gradient/conic-color-wheel.png b/third_party/webrender/wrench/reftests/gradient/conic-color-wheel.png Binary files differindex 3fff3c32d41..368007c74f6 100644 --- a/third_party/webrender/wrench/reftests/gradient/conic-color-wheel.png +++ b/third_party/webrender/wrench/reftests/gradient/conic-color-wheel.png diff --git a/third_party/webrender/wrench/reftests/gradient/conic-large-ref.yaml b/third_party/webrender/wrench/reftests/gradient/conic-large-ref.yaml deleted file mode 100644 index 9441175cf1b..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/conic-large-ref.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -root: - items: - - type: rect - bounds: 50 50 2000 300 - color: blue - - - type: conic-gradient - bounds: 50 50 2000 300 - center: 150 150 - angle: 0.0 - stops: [0.0, red, - 0.125, blue, - 0.375, blue, - 0.5, yellow, - 1.0, red]
\ No newline at end of file diff --git a/third_party/webrender/wrench/reftests/gradient/conic-large.yaml b/third_party/webrender/wrench/reftests/gradient/conic-large.yaml deleted file mode 100644 index 78bf305f54c..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/conic-large.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -root: - items: - - type: conic-gradient - bounds: 50 50 2000 300 - center: 150 150 - angle: 0.0 - stops: [0.0, red, - 0.125, blue, - 0.375, blue, - 0.5, yellow, - 1.0, red]
\ No newline at end of file diff --git a/third_party/webrender/wrench/reftests/gradient/conic-nan.yaml b/third_party/webrender/wrench/reftests/gradient/conic-nan.yaml deleted file mode 100644 index 6cc6234611f..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/conic-nan.yaml +++ /dev/null @@ -1,41 +0,0 @@ ---- -root: - items: - - type: conic-gradient - bounds: 50 50 200 NaN - center: 100 100 - angle: 0.0 - stops: [0.0, red, 0.25, green, 0.5, blue, 0.75, black] - - type: conic-gradient - bounds: 50 50 200 200 - center: NaN 100 - angle: 0.0 - stops: [0.0, red, 0.25, green, 0.5, blue, 0.75, black] - - type: conic-gradient - bounds: 50 50 200 200 - center: 100 100 - angle: NaN - stops: [0.0, red, 0.25, green, 0.5, blue, 0.75, black] - - type: conic-gradient - bounds: 50 50 200 200 - center: 100 100 - angle: 0.0 - stops: [0.0, red, NaN, green, 0.5, blue, 0.75, black] - - type: conic-gradient - bounds: 50 50 200 200 - tile-size: NaN 200 - center: 100 100 - angle: 0.0 - stops: [0.0, red, 0.25, green, 0.5, blue, 0.75, black] - - type: conic-gradient - bounds: 50 50 200 200 - clip-rect: NaN 0 100 100 - center: 100 100 - angle: 0.0 - stops: [0.0, red, 0.25, green, 0.5, blue, 0.75, black] - - type: conic-gradient - bounds: NaN NaN NaN NaN - clip-rect: NaN NaN NaN NaN - center: NaN NaN - angle: NaN - stops: [NaN, red, NaN, green, NaN, blue, NaN, black] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-aligned-border-radius.png b/third_party/webrender/wrench/reftests/gradient/linear-aligned-border-radius.png Binary files differindex 450e0ac56ac..2cdeeb679a8 100644 --- a/third_party/webrender/wrench/reftests/gradient/linear-aligned-border-radius.png +++ b/third_party/webrender/wrench/reftests/gradient/linear-aligned-border-radius.png diff --git a/third_party/webrender/wrench/reftests/gradient/linear-bug-1703141.yaml b/third_party/webrender/wrench/reftests/gradient/linear-bug-1703141.yaml deleted file mode 100644 index 9db333277e6..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-bug-1703141.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -root: - items: - - type: gradient - bounds: -8396 465 9136 3 - tile-size: 10498.667 3 - start: 9448.5 1.5 - end: 10498.333 1.5 - repeat: true - stops: [0.0, [0,0,0,0], 0.5, [0,0,0,0], - 0.5, red, 0.75, red, - 0.75, [0,0,0,0], 1.0, [0,0,0,0]] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-far-endpoints.yaml b/third_party/webrender/wrench/reftests/gradient/linear-far-endpoints.yaml deleted file mode 100644 index 45bb52debcf..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-far-endpoints.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Axis-aligned linear gradient with very far endpoints. It goes through the gradient -# decomposition path which should not choke on overflow or casting failure. ---- -root: - items: - - type: gradient - bounds: 50 50 500 500 - start: -19958788096 0 - end: 19958788096 0 - stops: [0.0, red, 1.0, blue] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-hard-stop-ref.png b/third_party/webrender/wrench/reftests/gradient/linear-hard-stop-ref.png Binary files differindex 4feb5e49937..a863648a3fd 100644 --- a/third_party/webrender/wrench/reftests/gradient/linear-hard-stop-ref.png +++ b/third_party/webrender/wrench/reftests/gradient/linear-hard-stop-ref.png diff --git a/third_party/webrender/wrench/reftests/gradient/linear-large-ref.yaml b/third_party/webrender/wrench/reftests/gradient/linear-large-ref.yaml deleted file mode 100644 index 472f04fd17b..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-large-ref.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- -root: - items: - - type: rect - bounds: 50 50 2000 300 - color: blue - - - type: gradient - bounds: 50 50 400 300 - start: 0 0 - end: 100 20 - stops: [0.0, red, 1.0, blue] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-large.yaml b/third_party/webrender/wrench/reftests/gradient/linear-large.yaml deleted file mode 100644 index cf9c50edd19..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-large.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -root: - items: - - type: gradient - bounds: 50 50 2000 300 - start: 0 0 - end: 100 20 - stops: [0.0, red, 1.0, blue] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-nan.yaml b/third_party/webrender/wrench/reftests/gradient/linear-nan.yaml deleted file mode 100644 index 687a06d372b..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-nan.yaml +++ /dev/null @@ -1,213 +0,0 @@ ---- -root: - items: - # Small-ish gradients - - type: gradient - bounds: 50 50 NaN 200 - start: 0 100 - end: 200 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.5, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 200 200 - start: 0 100 - end: NaN 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.5, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 200 200 - start: 0 NaN - end: 200 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.5, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 200 200 - start: 0 100 - end: 200 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - NaN, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 200 200 - start: 0 100 - end: 200 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, NaN, black] - - type: gradient - bounds: 50 50 200 200 - clip-rect: 50 50 150 NaN - start: 0 100 - end: 200 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 200 200 - tile-size: NaN 200 - start: 0 100 - end: 200 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, 1.0, black] - - # Large-ish gradients - - type: gradient - bounds: 50 50 NaN 500 - start: 0 100 - end: 500 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.5, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 500 500 - start: 0 100 - end: NaN 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.5, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 500 500 - start: 0 NaN - end: 500 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.5, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 500 500 - start: 0 100 - end: 500 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - NaN, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 500 500 - start: 0 100 - end: 500 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, NaN, black] - - type: gradient - bounds: 50 50 500 500 - clip-rect: 50 50 150 NaN - start: 0 100 - end: 500 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 500 500 - tile-size: NaN 500 - start: 0 100 - end: 500 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, 1.0, black] - - # Very large gradients - - type: gradient - bounds: 50 50 10000 10000 - start: 0 100 - end: NaN 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.5, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 10000 10000 - start: 0 NaN - end: 10000 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.5, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 10000 10000 - start: 0 100 - end: 10000 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - NaN, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 10000 10000 - start: 0 100 - end: 10000 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, NaN, black] - - type: gradient - bounds: 50 50 10000 10000 - clip-rect: 50 50 150 NaN - start: 0 100 - end: 10000 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 10000 10000 - tile-size: NaN 10000 - start: 0 100 - end: 10000 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, 1.0, black] - - # Not axis-aligned - - type: gradient - bounds: 50 50 200 200 - tile-size: NaN 200 - start: 0 0 - end: 200 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: NaN 50 200 200 - tile-size: 100 100 - start: 0 0 - end: 200 100 - stops: [0.0, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, 0.75, blue, - 0.75, black, 1.0, black] - - type: gradient - bounds: 50 50 200 200 - start: 0.0 0 - end: 200 100 - stops: [NaN, red, 0.25, red, - 0.25, green, 0.5, green, - 0.0, blue, NaN, blue, - 0.75, black, 1.0, black] - - # Post-apocalyptic gradient - - type: gradient - bounds: NaN NaN NaN NaN - tile-size: NaN NaN - start: NaN NaN - end: NaN NaN - stops: [NaN, red, NaN, red, - NaN, green, NaN, green, - NaN, blue, NaN, blue, - NaN, black, NaN, black] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-ref.png b/third_party/webrender/wrench/reftests/gradient/linear-ref.png Binary files differindex b157ee4befb..999c831000d 100644 --- a/third_party/webrender/wrench/reftests/gradient/linear-ref.png +++ b/third_party/webrender/wrench/reftests/gradient/linear-ref.png diff --git a/third_party/webrender/wrench/reftests/gradient/linear-repeat-clip-ref.yaml b/third_party/webrender/wrench/reftests/gradient/linear-repeat-clip-ref.yaml deleted file mode 100644 index d8fd0b3b326..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-repeat-clip-ref.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -root: - items: - - type: gradient - bounds: 0 0 500 500 - start: 0 -20 - end: 0 520 - stops: [0.0, green, 0.5, blue, 1.0, red] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-repeat-clip.yaml b/third_party/webrender/wrench/reftests/gradient/linear-repeat-clip.yaml deleted file mode 100644 index 9c84edd7140..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-repeat-clip.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# This test has a gradient primitive that is much larger than its local clip -# and some tiling that can be optimized away. The combination of clipping -# and stretching optimizations used to cause produce the wrong clip. ---- -root: - items: - - type: gradient - bounds: -500 0 2000 500 - tile-size: 100 500 - clip-rect: 0 0 500 500 - start: 0 -20 - end: 0 520 - stops: [0.0, green, 0.5, blue, 1.0, red] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-reverse-2-ref.yaml b/third_party/webrender/wrench/reftests/gradient/linear-reverse-2-ref.yaml deleted file mode 100644 index b85d3035030..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-reverse-2-ref.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -root: - items: - - type: gradient - bounds: 50 50 200 200 - start: 50 100 - end: 150 100 - stops: [0.0, green, 0.5, blue, - 0.5, blue, 1.0, red] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-reverse-2.yaml b/third_party/webrender/wrench/reftests/gradient/linear-reverse-2.yaml deleted file mode 100644 index c74c6b88f91..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-reverse-2.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -root: - items: - - type: gradient - bounds: 50 50 200 200 - start: 150 100 - end: 50 100 - stops: [0.0, red, 0.5, blue, - 0.5, blue, 1.0, green] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-reverse-3-ref.yaml b/third_party/webrender/wrench/reftests/gradient/linear-reverse-3-ref.yaml deleted file mode 100644 index 088977f50fa..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-reverse-3-ref.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -root: - items: - - type: gradient - bounds: 50 50 100 100 - start: 0 0 - end: 110 0 - stops: [0.0, white, 1.0, black] - - type: gradient - bounds: 150 50 100 100 - start: 0 0 - end: 110 0 - stops: [0.0, black, 1.0, white] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-reverse-3.yaml b/third_party/webrender/wrench/reftests/gradient/linear-reverse-3.yaml deleted file mode 100644 index 87b675c8ad4..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/linear-reverse-3.yaml +++ /dev/null @@ -1,13 +0,0 @@ ---- -root: - items: - - type: gradient - bounds: 50 50 100 100 - start: 0 0 - end: 110 0 - stops: [0.0, white, 1.0, black] - - type: gradient - bounds: 150 50 100 100 - start: 110 0 - end: 0 0 - stops: [0.0, white, 1.0, black] diff --git a/third_party/webrender/wrench/reftests/gradient/linear-stops-ref.png b/third_party/webrender/wrench/reftests/gradient/linear-stops-ref.png Binary files differindex 844b2440023..ed80ec9988b 100644 --- a/third_party/webrender/wrench/reftests/gradient/linear-stops-ref.png +++ b/third_party/webrender/wrench/reftests/gradient/linear-stops-ref.png diff --git a/third_party/webrender/wrench/reftests/gradient/premultiplied-aligned.png b/third_party/webrender/wrench/reftests/gradient/premultiplied-aligned.png Binary files differindex 35fad64b1a3..6fc18cd8c0e 100644 --- a/third_party/webrender/wrench/reftests/gradient/premultiplied-aligned.png +++ b/third_party/webrender/wrench/reftests/gradient/premultiplied-aligned.png diff --git a/third_party/webrender/wrench/reftests/gradient/premultiplied-angle.png b/third_party/webrender/wrench/reftests/gradient/premultiplied-angle.png Binary files differindex a37120ad7e7..e533843d7d5 100644 --- a/third_party/webrender/wrench/reftests/gradient/premultiplied-angle.png +++ b/third_party/webrender/wrench/reftests/gradient/premultiplied-angle.png diff --git a/third_party/webrender/wrench/reftests/gradient/radial-circle-ref.png b/third_party/webrender/wrench/reftests/gradient/radial-circle-ref.png Binary files differindex 3f9a748a7ed..7b339707a72 100644 --- a/third_party/webrender/wrench/reftests/gradient/radial-circle-ref.png +++ b/third_party/webrender/wrench/reftests/gradient/radial-circle-ref.png diff --git a/third_party/webrender/wrench/reftests/gradient/radial-ellipse-ref.png b/third_party/webrender/wrench/reftests/gradient/radial-ellipse-ref.png Binary files differindex b4786cd3877..fd60ec35b7e 100644 --- a/third_party/webrender/wrench/reftests/gradient/radial-ellipse-ref.png +++ b/third_party/webrender/wrench/reftests/gradient/radial-ellipse-ref.png diff --git a/third_party/webrender/wrench/reftests/gradient/radial-large-ref.png b/third_party/webrender/wrench/reftests/gradient/radial-large-ref.png Binary files differdeleted file mode 100644 index 444b876094e..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-large-ref.png +++ /dev/null diff --git a/third_party/webrender/wrench/reftests/gradient/radial-large.yaml b/third_party/webrender/wrench/reftests/gradient/radial-large.yaml deleted file mode 100644 index c03adec6c1d..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-large.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -root: - items: - - type: radial-gradient - bounds: 50 50 2000 300 - center: 1000 150 - radius: 900 200 - stops: [0, red, 1, blue] diff --git a/third_party/webrender/wrench/reftests/gradient/radial-nan.yaml b/third_party/webrender/wrench/reftests/gradient/radial-nan.yaml deleted file mode 100644 index f8127a8c7a5..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-nan.yaml +++ /dev/null @@ -1,35 +0,0 @@ ---- -root: - items: - - type: radial-gradient - bounds: 50 50 NaN 300 - center: 150 150 - radius: 200 200 - stops: [0, red, 1, blue] - - type: radial-gradient - bounds: 50 50 300 300 - center: 150 NaN - radius: 200 200 - stops: [0, red, 1, blue] - - type: radial-gradient - bounds: 50 50 300 300 - center: 150 150 - radius: NaN 200 - stops: [0, red, 1, blue] - - type: radial-gradient - bounds: 50 50 300 300 - center: 150 150 - radius: 200 200 - stops: [0, red, NaN, blue] - - type: radial-gradient - bounds: 50 50 300 300 - tile-size: 50 NaN - center: 150 150 - radius: 200 200 - stops: [0, red, 1, blue] - - type: radial-gradient - bounds: 50 50 300 300 - clip-rect: 50 10 NaN 300 - center: 150 150 - radius: 200 200 - stops: [0, red, 1, blue] diff --git a/third_party/webrender/wrench/reftests/gradient/radial-optimized-2-ref.yaml b/third_party/webrender/wrench/reftests/gradient/radial-optimized-2-ref.yaml deleted file mode 100644 index 4b900d93fad..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-optimized-2-ref.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -root: - items: - - type: radial-gradient - bounds: 50 50 500 500 - center: 150 150 - radius: 80 160 - stops: [0, red, 1, [0,0,0,0]] diff --git a/third_party/webrender/wrench/reftests/gradient/radial-optimized-2.yaml b/third_party/webrender/wrench/reftests/gradient/radial-optimized-2.yaml deleted file mode 100644 index 3fac9042e21..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-optimized-2.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -root: - items: - - type: radial-gradient - bounds: 50 50 500 500 - center: 150 150 - radius: 20 40 - stops: [0, red, 4, [0,0,0,0]] diff --git a/third_party/webrender/wrench/reftests/gradient/radial-optimized-ref.yaml b/third_party/webrender/wrench/reftests/gradient/radial-optimized-ref.yaml deleted file mode 100644 index 380384ffe66..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-optimized-ref.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# Hand-roll decomposition that webrender would do for radial-optimized.yaml ---- -root: - items: - - type: radial-gradient - bounds: 160 160 20 30 - center: 10 15 - radius: 10 15 - stops: [0, [255.0,0,0,0.7], 1, [0,0,255.0,0.7]] - - type: rect - bounds: 10 20 150 140 - color: [0,0,255.0,0.7] - - type: rect - bounds: 160 20 20 140 - color: [0,0,255.0,0.7] - - type: rect - bounds: 180 20 130 140 - color: [0,0,255.0,0.7] - - type: rect - bounds: 10 160 150 30 - color: [0,0,255.0,0.7] - - type: rect - bounds: 180 160 130 30 - color: [0,0,255.0,0.7] - - type: rect - bounds: 10 190 150 160 - color: [0,0,255.0,0.7] - - type: rect - bounds: 160 190 20 160 - color: [0,0,255.0,0.7] - - type: rect - bounds: 180 190 130 160 - color: [0,0,255.0,0.7] diff --git a/third_party/webrender/wrench/reftests/gradient/radial-optimized.yaml b/third_party/webrender/wrench/reftests/gradient/radial-optimized.yaml deleted file mode 100644 index cfc668bc66b..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-optimized.yaml +++ /dev/null @@ -1,12 +0,0 @@ -# A small radial gradient in a large primitive. -# Most of the primitive is the constant color of the last gradient stop, -# and webrender will try to optimize these parts by drawing them with -# solid color primitives. ---- -root: - items: - - type: radial-gradient - bounds: 10 20 300 330 - center: 160 155 - radius: 10 15 - stops: [0, [255.0,0,0,0.7], 1, [0,0,255.0,0.7]] diff --git a/third_party/webrender/wrench/reftests/gradient/radial-tiling-optimized-ref.yaml b/third_party/webrender/wrench/reftests/gradient/radial-tiling-optimized-ref.yaml deleted file mode 100644 index b5b4957f9be..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-tiling-optimized-ref.yaml +++ /dev/null @@ -1,24 +0,0 @@ ---- -root: - items: - - type: radial-gradient - bounds: 50 40 20 20 - center: 10 10 - radius: 10 10 - stops: [0, red, 1, blue, 1, [0,0,0,0]] - - type: radial-gradient - bounds: 450 40 20 20 - center: 10 10 - radius: 10 10 - stops: [0, red, 1, blue, 1, [0,0,0,0]] - - type: radial-gradient - bounds: 450 340 20 20 - center: 10 10 - radius: 10 10 - stops: [0, red, 1, blue, 1, [0,0,0,0]] - - type: radial-gradient - bounds: 50 340 20 20 - center: 10 10 - radius: 10 10 - stops: [0, red, 1, blue, 1, [0,0,0,0]] - diff --git a/third_party/webrender/wrench/reftests/gradient/radial-tiling-optimized.yaml b/third_party/webrender/wrench/reftests/gradient/radial-tiling-optimized.yaml deleted file mode 100644 index a92fd244797..00000000000 --- a/third_party/webrender/wrench/reftests/gradient/radial-tiling-optimized.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# A small repeated radial gradient in a large primitive. -# There is a lot of fully transparent space that webrender -# will optimize out. ---- -root: - items: - - type: radial-gradient - bounds: 10 10 800 600 - center: 50 40 - radius: 10 10 - stops: [0, red, 1, blue, 1, [0,0,0,0]] - tile-size: 300 200 - tile-spacing: 100 100 diff --git a/third_party/webrender/wrench/reftests/gradient/reftest.list b/third_party/webrender/wrench/reftests/gradient/reftest.list index 9f8dc0cc086..847c06ea8ee 100644 --- a/third_party/webrender/wrench/reftests/gradient/reftest.list +++ b/third_party/webrender/wrench/reftests/gradient/reftest.list @@ -1,5 +1,5 @@ platform(linux,mac) == premultiplied-aligned.yaml premultiplied-aligned.png -fuzzy(1,500) platform(linux,mac) == premultiplied-angle.yaml premultiplied-angle.png +platform(linux,mac) == premultiplied-angle.yaml premultiplied-angle.png platform(linux,mac) == premultiplied-radial.yaml premultiplied-radial.png platform(linux,mac) == premultiplied-conic.yaml premultiplied-conic.png @@ -10,8 +10,6 @@ platform(linux,mac) == premultiplied-conic-2.yaml premultiplied-conic-2.png == linear.yaml linear-ref.png == linear-reverse.yaml linear-ref.png -fuzzy(255,1200) == linear-reverse-2.yaml linear-reverse-2-ref.yaml -== linear-reverse-3.yaml linear-reverse-3-ref.yaml platform(linux,mac) fuzzy(1,35000) == linear-stops.yaml linear-stops-ref.png == linear-clamp-1a.yaml linear-clamp-1-ref.yaml @@ -24,7 +22,7 @@ fuzzy-range(<=1,*4800) == linear-hard-stop.yaml linear-hard-stop-ref.png fuzzy(1,20000) == linear.yaml linear-ref.yaml fuzzy(1,20000) == linear-reverse.yaml linear-ref.yaml -fuzzy(1,40000) == linear-aligned-clip.yaml linear-aligned-clip-ref.yaml +fuzzy(1,15200) == linear-aligned-clip.yaml linear-aligned-clip-ref.yaml platform(linux,mac) fuzzy(1,80000) == radial-circle.yaml radial-circle-ref.png platform(linux,mac) fuzzy(1,80000) == radial-ellipse.yaml radial-ellipse-ref.png @@ -52,8 +50,8 @@ platform(linux,mac) fuzzy(1,80000) == radial-ellipse.yaml radial-ellipse-ref.png # this might be able to be improved fuzzy(255,1200) == repeat-linear.yaml repeat-linear-ref.yaml fuzzy(255,1200) == repeat-linear-reverse.yaml repeat-linear-ref.yaml -fuzzy(255,2666) == repeat-radial.yaml repeat-radial-ref.yaml -fuzzy(255,2666) == repeat-radial-negative.yaml repeat-radial-ref.yaml +fuzzy(255,2664) == repeat-radial.yaml repeat-radial-ref.yaml +fuzzy(255,2664) == repeat-radial-negative.yaml repeat-radial-ref.yaml fuzzy(255,1652) == repeat-conic.yaml repeat-conic-ref.yaml fuzzy(255,1652) == repeat-conic-negative.yaml repeat-conic-ref.yaml @@ -64,19 +62,18 @@ fuzzy(1,62154) == tiling-linear-3.yaml tiling-linear-3-ref.yaml fuzzy(1,17) == tiling-radial-1.yaml tiling-radial-1-ref.yaml fuzzy(1,1) == tiling-radial-2.yaml tiling-radial-2-ref.yaml -fuzzy(1,3) fuzzy-if(platform(swgl),1,1318) == tiling-radial-3.yaml tiling-radial-3-ref.yaml +fuzzy(1,3) == tiling-radial-3.yaml tiling-radial-3-ref.yaml fuzzy(1,17) == tiling-radial-4.yaml tiling-radial-4-ref.yaml fuzzy(1,17) == tiling-conic-1.yaml tiling-conic-1-ref.yaml fuzzy(1,1) == tiling-conic-2.yaml tiling-conic-2-ref.yaml -fuzzy(1,7) == tiling-conic-3.yaml tiling-conic-3-ref.yaml +fuzzy(1,3) == tiling-conic-3.yaml tiling-conic-3-ref.yaml == radial-zero-size-1.yaml radial-zero-size-ref.yaml == radial-zero-size-2.yaml radial-zero-size-ref.yaml == radial-zero-size-3.yaml radial-zero-size-ref.yaml == linear-adjust-tile-size.yaml linear-adjust-tile-size-ref.yaml -== linear-repeat-clip.yaml linear-repeat-clip-ref.yaml platform(linux,mac) == linear-aligned-border-radius.yaml linear-aligned-border-radius.png # interpolation fuzz from sampling texture-baked gradient ramps @@ -84,22 +81,21 @@ platform(linux,mac) fuzzy-range(<=1,*1404) == repeat-border-radius.yaml repeat-b == conic.yaml conic-ref.yaml fuzzy(1,57) == conic-simple.yaml conic-simple.png -fuzzy(255,302) == conic-angle.yaml conic-angle.png +fuzzy(255,166) == conic-angle.yaml conic-angle.png == conic-center.yaml conic-center.png fuzzy(1,2) == conic-angle-wraparound.yaml conic-angle.yaml fuzzy(1,1) == conic-angle-wraparound-negative.yaml conic-angle.yaml -fuzzy(1,333) == conic-color-wheel.yaml conic-color-wheel.png +fuzzy(1,115) == conic-color-wheel.yaml conic-color-wheel.png # gradient caching tests # replaces a computed gradient by a sampled texture, so a lot of off-by-one # variation from interpolation, which is fine: fuzzy-range(<=1,*195000) == gradient_cache_5stops.yaml gradient_cache_5stops_ref.yaml -fuzzy-range(<=1,*171840) == gradient_cache_5stops_vertical.yaml gradient_cache_5stops_vertical_ref.yaml +fuzzy-range(<=1,*169000) == gradient_cache_5stops_vertical.yaml gradient_cache_5stops_vertical_ref.yaml == gradient_cache_hardstop.yaml gradient_cache_hardstop_ref.yaml -fuzzy-range(<=1,1) == gradient_cache_hardstop_clip.yaml gradient_cache_hardstop_clip_ref.yaml +== gradient_cache_hardstop_clip.yaml gradient_cache_hardstop_clip_ref.yaml == gradient_cache_clamp.yaml gradient_cache_clamp_ref.yaml == gradient_cache_repeat.yaml gradient_cache_repeat_ref.yaml -== linear-bug-1703141.yaml linear-bug-1703141.yaml # Recognize opaque tiles with gradient backgrounds == conic-backdrop-with-spacing.yaml conic-backdrop-with-spacing-ref.yaml @@ -108,19 +104,3 @@ fuzzy-range(<=1,1) == gradient_cache_hardstop_clip.yaml gradient_cache_hardstop_ == linear-backdrop.yaml linear-backdrop-ref.yaml == radial-backdrop-with-spacing.yaml radial-backdrop-with-spacing-ref.yaml == radial-backdrop.yaml radial-backdrop-ref.yaml - -# Exercise the radial gradient optimization code path -== radial-optimized.yaml radial-optimized-ref.yaml -== radial-optimized-2.yaml radial-optimized-2-ref.yaml -== radial-tiling-optimized.yaml radial-tiling-optimized-ref.yaml - -# Exercise the cached gradient scaling code path -fuzzy(2,23000) == linear-large.yaml linear-large-ref.yaml -== conic-large.yaml conic-large-ref.yaml -fuzzy(1,7000) == radial-large.yaml radial-large-ref.png - -# crash tests -== linear-far-endpoints.yaml linear-far-endpoints.yaml -== linear-nan.yaml linear-nan.yaml -== radial-nan.yaml radial-nan.yaml -== conic-nan.yaml conic-nan.yaml diff --git a/third_party/webrender/wrench/reftests/gradient/repeat-border-radius.png b/third_party/webrender/wrench/reftests/gradient/repeat-border-radius.png Binary files differindex cc0fa947ae7..7896fa7e2ae 100644 --- a/third_party/webrender/wrench/reftests/gradient/repeat-border-radius.png +++ b/third_party/webrender/wrench/reftests/gradient/repeat-border-radius.png diff --git a/third_party/webrender/wrench/reftests/image/downscale.png b/third_party/webrender/wrench/reftests/image/downscale.png Binary files differindex 460a29df196..877d0e48743 100644 --- a/third_party/webrender/wrench/reftests/image/downscale.png +++ b/third_party/webrender/wrench/reftests/image/downscale.png diff --git a/third_party/webrender/wrench/reftests/image/occlusion.png b/third_party/webrender/wrench/reftests/image/occlusion.png Binary files differindex 56332f2ef54..c62a58cdec8 100644 --- a/third_party/webrender/wrench/reftests/image/occlusion.png +++ b/third_party/webrender/wrench/reftests/image/occlusion.png diff --git a/third_party/webrender/wrench/reftests/image/reftest.list b/third_party/webrender/wrench/reftests/image/reftest.list index c1581d2e89f..5c3db88404c 100644 --- a/third_party/webrender/wrench/reftests/image/reftest.list +++ b/third_party/webrender/wrench/reftests/image/reftest.list @@ -4,12 +4,11 @@ == tile-with-spacing.yaml tile-with-spacing-ref.yaml skip_on(android,device) fuzzy(1,331264) == tile-repeat-prim-or-decompose.yaml tile-repeat-prim-or-decompose-ref.yaml platform(linux,mac) options(allow-mipmaps) == downscale.yaml downscale.png -skip_on(android,device) fuzzy-if(platform(swgl),1,20) == segments.yaml segments.png +skip_on(android,device) == segments.yaml segments.png platform(linux,mac) == yuv.yaml yuv.png -platform(linux,mac) fuzzy(1,6000) fuzzy-if(platform(swgl),1,205000) == yuv-clip.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 # allow slight lerp change where the squares meet, but catch lerping problems on the boundary (should clamp) -fuzzy-range(<=2,*450) == rgb_composite.yaml rgb_composite_ref.yaml +fuzzy-range(<=1,*450) == rgb_composite.yaml rgb_composite_ref.yaml diff --git a/third_party/webrender/wrench/reftests/image/segments.png b/third_party/webrender/wrench/reftests/image/segments.png Binary files differindex 45e4b544d41..168e9348ea0 100644 --- a/third_party/webrender/wrench/reftests/image/segments.png +++ b/third_party/webrender/wrench/reftests/image/segments.png diff --git a/third_party/webrender/wrench/reftests/image/yuv-clip.yaml b/third_party/webrender/wrench/reftests/image/yuv-clip.yaml deleted file mode 100644 index 6ceb7b69407..00000000000 --- a/third_party/webrender/wrench/reftests/image/yuv-clip.yaml +++ /dev/null @@ -1,24 +0,0 @@ -root: - items: - - type: clip - bounds: [0, 0, 1314, 650] - complex: - - rect: [0, 0, 1314, 650] - items: - - type: yuv-image - format: planar - src-y: spacex-y.png - src-u: spacex-u.png - src-v: spacex-v.png - bounds: [10, 10, 427, 640] - - - type: yuv-image - format: interleaved - src: spacex-yuv.png - bounds: [447, 10, 427, 640] - - - type: yuv-image - format: nv12 - src-y: spacex-y.png - src-uv: spacex-uv.png - bounds: [887, 10, 427, 640] diff --git a/third_party/webrender/wrench/reftests/image/yuv.png b/third_party/webrender/wrench/reftests/image/yuv.png Binary files differindex 66519bea8f1..56320e4a6ba 100644 --- a/third_party/webrender/wrench/reftests/image/yuv.png +++ b/third_party/webrender/wrench/reftests/image/yuv.png diff --git a/third_party/webrender/wrench/reftests/invalidation/one-rounded-rect-green.yaml b/third_party/webrender/wrench/reftests/invalidation/one-rounded-rect-green.yaml new file mode 100644 index 00000000000..2366913cbdc --- /dev/null +++ b/third_party/webrender/wrench/reftests/invalidation/one-rounded-rect-green.yaml @@ -0,0 +1,17 @@ +--- +root: + items: + - + bounds: 0 0 1000 1000 + type: stacking-context + cache: true + items: + - type: clip + bounds: [50, 50, 200, 200] + complex: + - rect: [50, 50, 200, 200] + radius: 8 + items: + - type: rect + bounds: 50 50 200 200 + color: green diff --git a/third_party/webrender/wrench/reftests/invalidation/one-rounded-rect.yaml b/third_party/webrender/wrench/reftests/invalidation/one-rounded-rect.yaml new file mode 100644 index 00000000000..988233e9ed2 --- /dev/null +++ b/third_party/webrender/wrench/reftests/invalidation/one-rounded-rect.yaml @@ -0,0 +1,17 @@ +--- +root: + items: + - + bounds: 0 0 1000 1000 + type: stacking-context + cache: true + items: + - type: clip + bounds: [50, 50, 200, 200] + complex: + - rect: [50, 50, 200, 200] + radius: 8 + items: + - type: rect + bounds: 50 50 200 200 + color: red diff --git a/third_party/webrender/wrench/reftests/invalidation/reftest.list b/third_party/webrender/wrench/reftests/invalidation/reftest.list new file mode 100644 index 00000000000..5925e61da96 --- /dev/null +++ b/third_party/webrender/wrench/reftests/invalidation/reftest.list @@ -0,0 +1 @@ +#dirty([(50,50):256x256]) one-rounded-rect.yaml dirty([(50,50):256x256]) one-rounded-rect-green.yaml dirty([(50,50):256x256]) one-rounded-rect.yaml == one-rounded-rect.yaml diff --git a/third_party/webrender/wrench/reftests/invalidation/rounded-rects.yaml b/third_party/webrender/wrench/reftests/invalidation/rounded-rects.yaml new file mode 100644 index 00000000000..8c9b0db2317 --- /dev/null +++ b/third_party/webrender/wrench/reftests/invalidation/rounded-rects.yaml @@ -0,0 +1,42 @@ +--- +root: + items: + - + bounds: 0 0 1000 1000 + type: stacking-context + cache: true + items: + - type: clip + bounds: [50, 50, 200, 200] + complex: + - rect: [50, 50, 200, 200] + radius: 8 + items: + - type: rect + bounds: 50 50 200 200 + color: red + + - type: clip + bounds: [270, 50, 200, 200] + complex: + - rect: [270, 50, 200, 200] + radius: [16, 32, 48, 64] + items: + - type: rect + bounds: 270 50 200 200 + color: green + + - type: clip + bounds: [490, 50, 500, 500] + complex: + - rect: [490, 50, 500, 500] + radius: { + top-left: [32, 16], + top-right: [40, 24], + bottom-left: [48, 64], + bottom-right: [52, 80], + } + items: + - type: rect + bounds: 490 50 500 500 + color: blue diff --git a/third_party/webrender/wrench/reftests/invalidation/two-rounded-rects.yaml b/third_party/webrender/wrench/reftests/invalidation/two-rounded-rects.yaml new file mode 100644 index 00000000000..315d6434cdc --- /dev/null +++ b/third_party/webrender/wrench/reftests/invalidation/two-rounded-rects.yaml @@ -0,0 +1,27 @@ +--- +root: + items: + - + bounds: 0 0 1000 1000 + type: stacking-context + cache: true + items: + - type: clip + bounds: [50, 50, 200, 200] + complex: + - rect: [50, 50, 200, 200] + radius: 8 + items: + - type: rect + bounds: 50 50 200 200 + color: red + + - type: clip + bounds: [270, 50, 200, 200] + complex: + - rect: [270, 50, 200, 200] + radius: [16, 32, 48, 64] + items: + - type: rect + bounds: 270 50 200 200 + color: green diff --git a/third_party/webrender/wrench/reftests/mask/checkerboard.png b/third_party/webrender/wrench/reftests/mask/checkerboard.png Binary files differindex f2c45c31940..e8a05df95be 100644 --- a/third_party/webrender/wrench/reftests/mask/checkerboard.png +++ b/third_party/webrender/wrench/reftests/mask/checkerboard.png diff --git a/third_party/webrender/wrench/reftests/mask/circle.png b/third_party/webrender/wrench/reftests/mask/circle.png Binary files differdeleted file mode 100644 index ea5789e44f6..00000000000 --- a/third_party/webrender/wrench/reftests/mask/circle.png +++ /dev/null diff --git a/third_party/webrender/wrench/reftests/mask/mask-perspective.png b/third_party/webrender/wrench/reftests/mask/mask-perspective.png Binary files differindex 425872c2900..02c0cc93ba3 100644 --- a/third_party/webrender/wrench/reftests/mask/mask-perspective.png +++ b/third_party/webrender/wrench/reftests/mask/mask-perspective.png diff --git a/third_party/webrender/wrench/reftests/mask/reftest.list b/third_party/webrender/wrench/reftests/mask/reftest.list index 8119f4a830d..7a18b4a0108 100644 --- a/third_party/webrender/wrench/reftests/mask/reftest.list +++ b/third_party/webrender/wrench/reftests/mask/reftest.list @@ -1,6 +1,6 @@ -fuzzy(1,10) == mask.yaml mask-ref.yaml -fuzzy(1,10) == mask-tiling.yaml mask-ref.yaml -fuzzy(1,10) == nested-mask.yaml nested-mask-ref.yaml +== mask.yaml mask-ref.yaml +== mask-tiling.yaml mask-ref.yaml +== nested-mask.yaml nested-mask-ref.yaml == nested-mask-tiling.yaml nested-mask-ref.yaml != mask.yaml green.yaml == aligned-layer-rect.yaml aligned-layer-rect-ref.yaml @@ -12,6 +12,5 @@ platform(linux,mac) fuzzy(1,17500) == mask-atomicity-tiling.yaml mask-atomicity- platform(linux,mac) == mask-perspective.yaml mask-perspective.png == fuzzy(1,11) mask-perspective-tiling.yaml mask-perspective.yaml platform(linux,mac) == checkerboard.yaml checkerboard.png -skip_on(android,device) fuzzy(2,1900) == checkerboard.yaml checkerboard-tiling.yaml # Fails on a Pixel2 +skip_on(android,device) == checkerboard.yaml checkerboard-tiling.yaml # Fails on a Pixel2 == missing-mask.yaml missing-mask-ref.yaml -platform(linux) == scaled-filter-raster-root.yaml scaled-filter-raster-root.png diff --git a/third_party/webrender/wrench/reftests/mask/rounded-corners.png b/third_party/webrender/wrench/reftests/mask/rounded-corners.png Binary files differindex 4f8d1d0fa02..19adfe02ec8 100644 --- a/third_party/webrender/wrench/reftests/mask/rounded-corners.png +++ b/third_party/webrender/wrench/reftests/mask/rounded-corners.png diff --git a/third_party/webrender/wrench/reftests/mask/scaled-filter-raster-root.png b/third_party/webrender/wrench/reftests/mask/scaled-filter-raster-root.png Binary files differdeleted file mode 100644 index e9cf49553ed..00000000000 --- a/third_party/webrender/wrench/reftests/mask/scaled-filter-raster-root.png +++ /dev/null diff --git a/third_party/webrender/wrench/reftests/mask/scaled-filter-raster-root.yaml b/third_party/webrender/wrench/reftests/mask/scaled-filter-raster-root.yaml deleted file mode 100644 index b4e9d8574b2..00000000000 --- a/third_party/webrender/wrench/reftests/mask/scaled-filter-raster-root.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Verify that a tiled clip mask is correctly applied to a surface that -# is both a raster root and has a device-pixel ratio different than the -# primary surface (due to the scale transform). ---- -root: - items: - - type: stacking-context - transform-origin: [0, 0] - transform: scale(20) - items: - - type: clip - bounds: [10, 10, 10, 10] - id: 2 - image-mask: - image: "circle.png" - rect: [10, 10, 10, 10] - repeat: false - tile-size: 200 - - type: stacking-context - filters: [opacity(0.5)] - items: - - type: stacking-context - clip-node: 2 - items: - - type: rect - bounds: [10, 10, 10, 10] - color: blue diff --git a/third_party/webrender/wrench/reftests/performance/no-clip-mask.png b/third_party/webrender/wrench/reftests/performance/no-clip-mask.png Binary files differindex 0b1bfca7558..1fc4ef0483a 100644 --- a/third_party/webrender/wrench/reftests/performance/no-clip-mask.png +++ b/third_party/webrender/wrench/reftests/performance/no-clip-mask.png diff --git a/third_party/webrender/wrench/reftests/reftest.list b/third_party/webrender/wrench/reftests/reftest.list index 8d39fcc5a7c..a3194332021 100644 --- a/third_party/webrender/wrench/reftests/reftest.list +++ b/third_party/webrender/wrench/reftests/reftest.list @@ -4,10 +4,10 @@ include blend/reftest.list include border/reftest.list include boxshadow/reftest.list include clip/reftest.list -include compositor-surface/reftest.list include filters/reftest.list include gradient/reftest.list include image/reftest.list +include invalidation/reftest.list include mask/reftest.list include performance/reftest.list include scrolling/reftest.list diff --git a/third_party/webrender/wrench/reftests/scrolling/ancestor-scroll-frames.yaml b/third_party/webrender/wrench/reftests/scrolling/ancestor-scroll-frames.yaml deleted file mode 100644 index 9d5c1d006e0..00000000000 --- a/third_party/webrender/wrench/reftests/scrolling/ancestor-scroll-frames.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# Test that we don't select a nested scroll frame as a scroll root and include -# primitives that are positioned by ancestors of that scroll root, when creating -# a tile cache for a blend container. This test will cause a panic in -# `get_relative_transform_with_face` otherwise. ---- -root: - items: - - type: stacking-context - blend-container: true - items: - - type: scroll-frame - bounds: [0, 0, 200, 200] - content-size: [200, 400] - items: - - type: stacking-context - bounds: [0, 0, 200, 200] - transform: rotate(45) - items: - - type: rect - bounds: [0, 0, 200, 200] - color: red - - type: scroll-frame - bounds: [200, 0, 400, 200] - content-size: [400, 200] - items: - - type: scroll-frame - bounds: [200, 0, 400, 200] - content-size: [400, 400] - items: - - type: rect - bounds: [200, 0, 100, 200] - color: green - backface-visible: true - - type: rect - bounds: [300, 0, 100, 200] - color: blue - backface-visible: false diff --git a/third_party/webrender/wrench/reftests/scrolling/blank.yaml b/third_party/webrender/wrench/reftests/scrolling/blank.yaml deleted file mode 100644 index c4eb3ab6730..00000000000 --- a/third_party/webrender/wrench/reftests/scrolling/blank.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -root: diff --git a/third_party/webrender/wrench/reftests/scrolling/reftest.list b/third_party/webrender/wrench/reftests/scrolling/reftest.list index a62984266e7..7f7241330fa 100644 --- a/third_party/webrender/wrench/reftests/scrolling/reftest.list +++ b/third_party/webrender/wrench/reftests/scrolling/reftest.list @@ -17,5 +17,3 @@ == nested-stickys.yaml nested-stickys-ref.yaml == viewport-offset.yaml viewport-offset-ref.yaml == ext-scroll-offset-1.yaml ext-scroll-offset-1-ref.yaml -== scroll-frame-order.yaml scroll-frame-order-ref.yaml -!= ancestor-scroll-frames.yaml blank.yaml diff --git a/third_party/webrender/wrench/reftests/scrolling/scale-offsets.yaml b/third_party/webrender/wrench/reftests/scrolling/scale-offsets.yaml index fe4cf741319..5bfc2c22c9f 100644 --- a/third_party/webrender/wrench/reftests/scrolling/scale-offsets.yaml +++ b/third_party/webrender/wrench/reftests/scrolling/scale-offsets.yaml @@ -13,3 +13,4 @@ root: - type: rect color: green bounds: [100, 100, 260, 260] + hit-testing-tag: [0, 0] diff --git a/third_party/webrender/wrench/reftests/scrolling/scroll-frame-order-ref.yaml b/third_party/webrender/wrench/reftests/scrolling/scroll-frame-order-ref.yaml deleted file mode 100644 index 5429d052db2..00000000000 --- a/third_party/webrender/wrench/reftests/scrolling/scroll-frame-order-ref.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# Tests that scroll frames can be defined and used in any order. - ---- -root: - items: - - type: scroll-frame - bounds: [0, 0, 50, 50] - content-size: [50, 50] - clip-rect: [0, 0, 50, 50] - id: 123 - - type: scroll-frame - bounds: [50, 0, 50, 50] - content-size: [50, 50] - clip-rect: [50, 0, 50, 50] - scroll-offset: [0, -25] - id: 456 - - type: scroll-frame - bounds: [100, 0, 50, 50] - content-size: [50, 50] - clip-rect: [100, 0, 50, 50] - scroll-offset: [0, -15] - id: 789 - - type: rect - bounds: [0, 0, 50, 50] - color: red - clip-and-scroll: 123 - - type: rect - bounds: [50, 0, 50, 50] - color: green - clip-and-scroll: 456 - - type: rect - bounds: [100, 0, 50, 50] - color: blue - clip-and-scroll: 789 diff --git a/third_party/webrender/wrench/reftests/scrolling/scroll-frame-order.yaml b/third_party/webrender/wrench/reftests/scrolling/scroll-frame-order.yaml deleted file mode 100644 index 1801dbb08ad..00000000000 --- a/third_party/webrender/wrench/reftests/scrolling/scroll-frame-order.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# Tests that scroll frames can be defined and used in any order. - ---- -root: - items: - - type: scroll-frame - bounds: [0, 0, 50, 50] - content-size: [50, 50] - clip-rect: [0, 0, 50, 50] - id: 123 - - type: rect - bounds: [0, 0, 50, 50] - color: red - clip-and-scroll: 123 - - type: scroll-frame - bounds: [50, 0, 50, 50] - content-size: [50, 50] - clip-rect: [50, 0, 50, 50] - scroll-offset: [0, -25] - id: 456 - - type: rect - bounds: [50, 0, 50, 50] - color: green - clip-and-scroll: 456 - - type: scroll-frame - bounds: [100, 0, 50, 50] - content-size: [50, 50] - clip-rect: [100, 0, 50, 50] - scroll-offset: [0, -15] - id: 789 - - type: rect - bounds: [100, 0, 50, 50] - color: blue - clip-and-scroll: 789 - diff --git a/third_party/webrender/wrench/reftests/snap/preserve-3d.png b/third_party/webrender/wrench/reftests/snap/preserve-3d.png Binary files differindex e02c30654ca..d3efa984ee0 100644 --- a/third_party/webrender/wrench/reftests/snap/preserve-3d.png +++ b/third_party/webrender/wrench/reftests/snap/preserve-3d.png diff --git a/third_party/webrender/wrench/reftests/snap/reftest.list b/third_party/webrender/wrench/reftests/snap/reftest.list index 1f35426a57f..84fe6c4979d 100644 --- a/third_party/webrender/wrench/reftests/snap/reftest.list +++ b/third_party/webrender/wrench/reftests/snap/reftest.list @@ -1,4 +1,3 @@ platform(linux,mac) == snap.yaml snap.png == transform.yaml transform.png platform(linux,mac) == preserve-3d.yaml preserve-3d.png -fuzzy(128,200) == subpixel-raster-root.yaml subpixel-raster-root-ref.yaml diff --git a/third_party/webrender/wrench/reftests/snap/snap.png b/third_party/webrender/wrench/reftests/snap/snap.png Binary files differindex 38f7cf33e5f..1f736082d84 100644 --- a/third_party/webrender/wrench/reftests/snap/snap.png +++ b/third_party/webrender/wrench/reftests/snap/snap.png diff --git a/third_party/webrender/wrench/reftests/snap/subpixel-raster-root-ref.yaml b/third_party/webrender/wrench/reftests/snap/subpixel-raster-root-ref.yaml deleted file mode 100644 index 8afe5677883..00000000000 --- a/third_party/webrender/wrench/reftests/snap/subpixel-raster-root-ref.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -root: - items: - - - bounds: [0, 111, 200, 1] - type: rect - color: green diff --git a/third_party/webrender/wrench/reftests/snap/subpixel-raster-root.yaml b/third_party/webrender/wrench/reftests/snap/subpixel-raster-root.yaml deleted file mode 100644 index 494ec42334f..00000000000 --- a/third_party/webrender/wrench/reftests/snap/subpixel-raster-root.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Verify that we don't incorrectly snap surface rects with fractional pixel offsets ---- -root: - items: - - - type: "stacking-context" - transform: translate(0, 100.5, 0) - transform-style: preserve-3d - items: - - - bounds: [0, 10.5, 200, 1] - type: rect - color: green diff --git a/third_party/webrender/wrench/reftests/snap/transform.png b/third_party/webrender/wrench/reftests/snap/transform.png Binary files differindex 73c8feacdfa..4e2709effe9 100644 --- a/third_party/webrender/wrench/reftests/snap/transform.png +++ b/third_party/webrender/wrench/reftests/snap/transform.png diff --git a/third_party/webrender/wrench/reftests/split/reftest.list b/third_party/webrender/wrench/reftests/split/reftest.list index cd9b33c02b3..65692ccd1d9 100644 --- a/third_party/webrender/wrench/reftests/split/reftest.list +++ b/third_party/webrender/wrench/reftests/split/reftest.list @@ -1,4 +1,4 @@ -fuzzy-if(platform(swgl),1,180000) == simple.yaml simple-ref.yaml +== simple.yaml simple-ref.yaml == order-1.yaml order-1-ref.yaml == order-2.yaml order-2-ref.yaml == nested.yaml nested-ref.yaml @@ -10,7 +10,7 @@ fuzzy(35,200) == nested-coord-systems.yaml nested-coord-systems-ref.yaml == intermediate-2.yaml intermediate-1-ref.yaml == split-intersect1.yaml split-intersect1-ref.yaml == ordering.yaml ordering-ref.yaml -fuzzy(1,20) fuzzy-if(platform(swgl),128,39) == near-plane.yaml near-plane.png +fuzzy(1,20) == near-plane.yaml near-plane.png # Note: on windows the image is rendered at a slightly different spot. # similarly, a lot of tests in "transform" are non-windows. TODO: investigate platform(linux,mac) fuzzy(1,20) == same-plane.yaml same-plane.png diff --git a/third_party/webrender/wrench/reftests/split/same-plane.png b/third_party/webrender/wrench/reftests/split/same-plane.png Binary files differindex 3d5baf998fa..414dc5cd84f 100644 --- a/third_party/webrender/wrench/reftests/split/same-plane.png +++ b/third_party/webrender/wrench/reftests/split/same-plane.png diff --git a/third_party/webrender/wrench/reftests/text/alpha-transform.png b/third_party/webrender/wrench/reftests/text/alpha-transform.png Binary files differindex e22066b8c6d..b5af70dbf88 100644 --- a/third_party/webrender/wrench/reftests/text/alpha-transform.png +++ b/third_party/webrender/wrench/reftests/text/alpha-transform.png diff --git a/third_party/webrender/wrench/reftests/text/blurred-shadow-local-clip-rect-ref.png b/third_party/webrender/wrench/reftests/text/blurred-shadow-local-clip-rect-ref.png Binary files differindex 944d0b78477..895d42eaf5c 100644 --- a/third_party/webrender/wrench/reftests/text/blurred-shadow-local-clip-rect-ref.png +++ b/third_party/webrender/wrench/reftests/text/blurred-shadow-local-clip-rect-ref.png diff --git a/third_party/webrender/wrench/reftests/text/border-radius-alpha.png b/third_party/webrender/wrench/reftests/text/border-radius-alpha.png Binary files differindex e20302a562d..cd792b0f673 100644 --- a/third_party/webrender/wrench/reftests/text/border-radius-alpha.png +++ b/third_party/webrender/wrench/reftests/text/border-radius-alpha.png diff --git a/third_party/webrender/wrench/reftests/text/border-radius-subpx.png b/third_party/webrender/wrench/reftests/text/border-radius-subpx.png Binary files differindex 4361f563db4..267b2030a75 100644 --- a/third_party/webrender/wrench/reftests/text/border-radius-subpx.png +++ b/third_party/webrender/wrench/reftests/text/border-radius-subpx.png diff --git a/third_party/webrender/wrench/reftests/text/clipped-transform.png b/third_party/webrender/wrench/reftests/text/clipped-transform.png Binary files differindex 24459d2d3c3..2afb04b8bbd 100644 --- a/third_party/webrender/wrench/reftests/text/clipped-transform.png +++ b/third_party/webrender/wrench/reftests/text/clipped-transform.png diff --git a/third_party/webrender/wrench/reftests/text/colors-alpha.png b/third_party/webrender/wrench/reftests/text/colors-alpha.png Binary files differindex 3e8d985025d..e82f79ddce2 100644 --- a/third_party/webrender/wrench/reftests/text/colors-alpha.png +++ b/third_party/webrender/wrench/reftests/text/colors-alpha.png diff --git a/third_party/webrender/wrench/reftests/text/colors-subpx.png b/third_party/webrender/wrench/reftests/text/colors-subpx.png Binary files differindex 50d5c36be38..8f943b2f80c 100644 --- a/third_party/webrender/wrench/reftests/text/colors-subpx.png +++ b/third_party/webrender/wrench/reftests/text/colors-subpx.png diff --git a/third_party/webrender/wrench/reftests/text/decorations-suite.png b/third_party/webrender/wrench/reftests/text/decorations-suite.png Binary files differindex b9a33033492..dc69e0f6d9b 100644 --- a/third_party/webrender/wrench/reftests/text/decorations-suite.png +++ b/third_party/webrender/wrench/reftests/text/decorations-suite.png diff --git a/third_party/webrender/wrench/reftests/text/isolated-text.png b/third_party/webrender/wrench/reftests/text/isolated-text.png Binary files differindex a0c8ed36506..7b310e78dcb 100644 --- a/third_party/webrender/wrench/reftests/text/isolated-text.png +++ b/third_party/webrender/wrench/reftests/text/isolated-text.png diff --git a/third_party/webrender/wrench/reftests/text/perspective-clip.png b/third_party/webrender/wrench/reftests/text/perspective-clip.png Binary files differindex 7d19eff293c..3a836ecb2a8 100644 --- a/third_party/webrender/wrench/reftests/text/perspective-clip.png +++ b/third_party/webrender/wrench/reftests/text/perspective-clip.png diff --git a/third_party/webrender/wrench/reftests/text/raster-space.png b/third_party/webrender/wrench/reftests/text/raster-space.png Binary files differindex e6a1b38865e..66fcf7377a3 100644 --- a/third_party/webrender/wrench/reftests/text/raster-space.png +++ b/third_party/webrender/wrench/reftests/text/raster-space.png diff --git a/third_party/webrender/wrench/reftests/text/raster-space.yaml b/third_party/webrender/wrench/reftests/text/raster-space.yaml index a4efba37fd9..e6534784123 100644 --- a/third_party/webrender/wrench/reftests/text/raster-space.yaml +++ b/third_party/webrender/wrench/reftests/text/raster-space.yaml @@ -4,7 +4,6 @@ root: transform: scale(5.0) rotate(-45) transform-origin: 300 300 raster-space: local(1.0) - filters: [opacity(0.5)] items: - text: "Local" origin: 20 50 diff --git a/third_party/webrender/wrench/reftests/text/reftest.list b/third_party/webrender/wrench/reftests/text/reftest.list index 3fef2f41db0..d7a51ef42d6 100644 --- a/third_party/webrender/wrench/reftests/text/reftest.list +++ b/third_party/webrender/wrench/reftests/text/reftest.list @@ -14,13 +14,13 @@ fuzzy(1,1) == shadow-huge.yaml shadow-huge-ref.yaml != shadow-clipped-text.yaml blank.yaml != non-opaque.yaml non-opaque-notref.yaml == decorations.yaml decorations-ref.yaml -skip_on(android,device) fuzzy(1,3635) fuzzy-if(platform(swgl),3,13395) == decorations-suite.yaml decorations-suite.png # Fails on Pixel2 +skip_on(android,device) fuzzy(1,3001) == decorations-suite.yaml decorations-suite.png # Fails on Pixel2 == 1658.yaml 1658-ref.yaml -fuzzy(2,405) fuzzy-if(platform(swgl),2,1508) == split-batch.yaml split-batch-ref.yaml +fuzzy(1,6) == split-batch.yaml split-batch-ref.yaml # Next 3 tests affected by bug 1548099 on Android skip_on(android) == shadow-red.yaml shadow-red-ref.yaml -skip_on(android) fuzzy(1,999) fuzzy-if(platform(swgl),2,1324) == shadow-grey.yaml shadow-grey-ref.yaml -skip_on(android) fuzzy(1,828) fuzzy-if(platform(swgl),2,1538) == shadow-grey-transparent.yaml shadow-grey-ref.yaml +skip_on(android) fuzzy(1,735) == shadow-grey.yaml shadow-grey-ref.yaml +skip_on(android) fuzzy(1,663) == shadow-grey-transparent.yaml shadow-grey-ref.yaml == subtle-shadow.yaml subtle-shadow-ref.yaml fuzzy(1,64) == shadow-atomic.yaml shadow-atomic-ref.yaml fuzzy(1,64) == shadow-clip-rect.yaml shadow-atomic-ref.yaml @@ -35,36 +35,36 @@ skip_on(android) != synthetic-italics-custom.yaml synthetic-italics.yaml options(disable-aa) == ahem.yaml ahem-ref.yaml platform(linux) == isolated-text.yaml isolated-text.png platform(mac) skip_on(mac,>=10.14) fuzzy(3,67) == white-opacity.yaml white-opacity.png -fuzzy(1,113) platform(linux) options(disable-subpixel) == colors.yaml colors-alpha.png +fuzzy(1,4) platform(linux) options(disable-subpixel) == colors.yaml colors-alpha.png # Run without dual-source blending path, batches are broken when text colors change. -fuzzy(1,1256) platform(linux) options(disable-dual-source-blending) draw_calls(6) == colors.yaml colors-subpx.png +fuzzy(1,6) platform(linux) options(disable-dual-source-blending) draw_calls(6) == colors.yaml colors-subpx.png # Run with both dual-source blending, ensuring batching is improved. -fuzzy(1,774) platform(linux) draw_calls(3) == colors.yaml colors-subpx.png +fuzzy(1,6) platform(linux) draw_calls(3) == colors.yaml colors-subpx.png platform(linux) options(disable-subpixel) == border-radius.yaml border-radius-alpha.png platform(linux) == border-radius.yaml border-radius-subpx.png options(disable-aa) == transparent-no-aa.yaml transparent-no-aa-ref.yaml != diacritics.yaml diacritics-ref.yaml fuzzy(1,1) platform(linux) options(disable-subpixel) == text-masking.yaml text-masking-alpha.png fuzzy(1,44) platform(linux) == text-masking.yaml text-masking-subpx.png -fuzzy(1,30) platform(linux) options(disable-subpixel) == alpha-transform.yaml alpha-transform.png -fuzzy(1,20) platform(linux) == subpixel-rotate.yaml subpixel-rotate.png -fuzzy(1,72) platform(linux) == subpixel-scale.yaml subpixel-scale.png -fuzzy(1,26) platform(linux) == subpixel-skew.yaml subpixel-skew.png +platform(linux) options(disable-subpixel) == alpha-transform.yaml alpha-transform.png +platform(linux) == subpixel-rotate.yaml subpixel-rotate.png +platform(linux) == subpixel-scale.yaml subpixel-scale.png +platform(linux) == subpixel-skew.yaml subpixel-skew.png fuzzy(1,381) platform(linux) == subpixel-translate.yaml subpixel-translate-ref.yaml != shadow-rotate.yaml blank.yaml platform(linux) == embedded-bitmaps.yaml embedded-bitmaps.png -fuzzy(1,13) platform(linux) == clipped-transform.yaml clipped-transform.png +platform(linux) == clipped-transform.yaml clipped-transform.png platform(mac) fuzzy(195,30) == color-bitmap-shadow.yaml color-bitmap-shadow-ref.yaml platform(linux) == writing-modes.yaml writing-modes-ref.yaml -fuzzy(1,5) platform(linux) == blurred-shadow-local-clip-rect.yaml blurred-shadow-local-clip-rect-ref.png +platform(linux) == blurred-shadow-local-clip-rect.yaml blurred-shadow-local-clip-rect-ref.png fuzzy(1,1) platform(linux) == two-shadows.yaml two-shadows.png == shadow-clip.yaml shadow-clip-ref.yaml == shadow-fast-clip.yaml shadow-fast-clip-ref.yaml skip_on(android,device) == shadow-partial-glyph.yaml shadow-partial-glyph-ref.yaml # Fails on Pixel2 -fuzzy(2,212) platform(linux) == shadow-transforms.yaml shadow-transforms.png -fuzzy(2,370) platform(linux) == raster-space.yaml raster-space.png +fuzzy(1,107) platform(linux) == shadow-transforms.yaml shadow-transforms.png +fuzzy(1,113) platform(linux) == raster-space.yaml raster-space.png skip_on(android) skip_on(mac,>=10.14) != allow-subpixel.yaml allow-subpixel-ref.yaml # Android: we don't enable sub-px aa on this platform. -skip_on(android,device) fuzzy-if(platform(swgl),1,1085) == bg-color.yaml bg-color-ref.yaml # Fails on Pixel2 +skip_on(android,device) == bg-color.yaml bg-color-ref.yaml # Fails on Pixel2 != large-glyphs.yaml blank.yaml != large-line-decoration.yaml blank.yaml skip_on(android,device) == snap-text-offset.yaml snap-text-offset-ref.yaml @@ -72,13 +72,13 @@ fuzzy(5,4435) == shadow-border.yaml shadow-solid-ref.yaml fuzzy(5,4435) == shadow-image.yaml shadow-solid-ref.yaml options(disable-aa) == snap-clip.yaml snap-clip-ref.yaml platform(linux) == perspective-clip.yaml perspective-clip.png -fuzzy(1,150) options(disable-subpixel) == raster-space-snap.yaml raster-space-snap-ref.yaml +fuzzy(1,39) options(disable-subpixel) == raster-space-snap.yaml raster-space-snap-ref.yaml # == intermediate-transform.yaml intermediate-transform-ref.yaml # fails because of AA inavailable with an intermediate surface -fuzzy(1,15) platform(linux) force_subpixel_aa_where_possible(true) == text-fixed-slice.yaml text-fixed-slice-slow.png -fuzzy(1,15) platform(linux) force_subpixel_aa_where_possible(false) == text-fixed-slice.yaml text-fixed-slice-fast.png +platform(linux) force_subpixel_aa_where_possible(true) == text-fixed-slice.yaml text-fixed-slice-slow.png +platform(linux) force_subpixel_aa_where_possible(false) == text-fixed-slice.yaml text-fixed-slice-fast.png # a 8544x8544 raster root vs. 2136x2136 # most pixels are off by a small amount, but a few pixels on the edge vary by a lot, pushing up the fuzzy max-diff; # the main goal of the test is that everything is in the same place, at the same scale, clipped the same way, # despite 4x on-the-fly scale change. -skip_on(android) fuzzy-range(<=3,*21700,<=20,*3500,<=119,*590) fuzzy-if(platform(swgl),108,24907) == raster_root_C_8192.yaml raster_root_C_ref.yaml +skip_on(android) fuzzy-range(<=3,*20569,<=20,*2525,<=115,*543) == raster_root_C_8192.yaml raster_root_C_ref.yaml diff --git a/third_party/webrender/wrench/reftests/text/shadow-transforms.png b/third_party/webrender/wrench/reftests/text/shadow-transforms.png Binary files differindex 05af37e1e71..71d97bff4c8 100644 --- a/third_party/webrender/wrench/reftests/text/shadow-transforms.png +++ b/third_party/webrender/wrench/reftests/text/shadow-transforms.png diff --git a/third_party/webrender/wrench/reftests/text/subpixel-rotate.png b/third_party/webrender/wrench/reftests/text/subpixel-rotate.png Binary files differindex 1eaf89db1ff..2bff307a8ed 100644 --- a/third_party/webrender/wrench/reftests/text/subpixel-rotate.png +++ b/third_party/webrender/wrench/reftests/text/subpixel-rotate.png diff --git a/third_party/webrender/wrench/reftests/text/subpixel-scale.png b/third_party/webrender/wrench/reftests/text/subpixel-scale.png Binary files differindex 5999d2d5dd9..f3c245c25ac 100644 --- a/third_party/webrender/wrench/reftests/text/subpixel-scale.png +++ b/third_party/webrender/wrench/reftests/text/subpixel-scale.png diff --git a/third_party/webrender/wrench/reftests/text/subpixel-skew.png b/third_party/webrender/wrench/reftests/text/subpixel-skew.png Binary files differindex 9dcfde00884..3e713f16711 100644 --- a/third_party/webrender/wrench/reftests/text/subpixel-skew.png +++ b/third_party/webrender/wrench/reftests/text/subpixel-skew.png diff --git a/third_party/webrender/wrench/reftests/text/text-fixed-slice-fast.png b/third_party/webrender/wrench/reftests/text/text-fixed-slice-fast.png Binary files differindex e6d6147219d..7f3ee0ec9d3 100644 --- a/third_party/webrender/wrench/reftests/text/text-fixed-slice-fast.png +++ b/third_party/webrender/wrench/reftests/text/text-fixed-slice-fast.png diff --git a/third_party/webrender/wrench/reftests/text/text-fixed-slice-slow.png b/third_party/webrender/wrench/reftests/text/text-fixed-slice-slow.png Binary files differindex 4ccfff39b20..4eaa7fc3ad5 100644 --- a/third_party/webrender/wrench/reftests/text/text-fixed-slice-slow.png +++ b/third_party/webrender/wrench/reftests/text/text-fixed-slice-slow.png diff --git a/third_party/webrender/wrench/reftests/text/text-fixed-slice.yaml b/third_party/webrender/wrench/reftests/text/text-fixed-slice.yaml index ec0b72e59be..902b7c83a75 100644 --- a/third_party/webrender/wrench/reftests/text/text-fixed-slice.yaml +++ b/third_party/webrender/wrench/reftests/text/text-fixed-slice.yaml @@ -10,12 +10,9 @@ root: - type: rect bounds: [0, 0, 500, 200] color: white - - type: clip + - type: rect bounds: [0, 0, 500, 200] - items: - - type: rect - bounds: [0, 0, 500, 200] - color: white + color: white - text: "The sun has frightened off the night!" origin: 20 40 size: 20 diff --git a/third_party/webrender/wrench/reftests/text/text-masking-alpha.png b/third_party/webrender/wrench/reftests/text/text-masking-alpha.png Binary files differindex 2b816eb288b..1f02f8bac5d 100644 --- a/third_party/webrender/wrench/reftests/text/text-masking-alpha.png +++ b/third_party/webrender/wrench/reftests/text/text-masking-alpha.png diff --git a/third_party/webrender/wrench/reftests/text/text-masking-subpx.png b/third_party/webrender/wrench/reftests/text/text-masking-subpx.png Binary files differindex 791a4c6347f..08db66a5376 100644 --- a/third_party/webrender/wrench/reftests/text/text-masking-subpx.png +++ b/third_party/webrender/wrench/reftests/text/text-masking-subpx.png diff --git a/third_party/webrender/wrench/reftests/text/two-shadows.png b/third_party/webrender/wrench/reftests/text/two-shadows.png Binary files differindex 3907f1be74a..1dae24cb84d 100644 --- a/third_party/webrender/wrench/reftests/text/two-shadows.png +++ b/third_party/webrender/wrench/reftests/text/two-shadows.png diff --git a/third_party/webrender/wrench/reftests/tiles/mix-blend-clip-ref.yaml b/third_party/webrender/wrench/reftests/tiles/mix-blend-clip-ref.yaml deleted file mode 100644 index e31ead5b870..00000000000 --- a/third_party/webrender/wrench/reftests/tiles/mix-blend-clip-ref.yaml +++ /dev/null @@ -1,14 +0,0 @@ ---- -root: - items: - - type: stacking-context - blend-container: true - items: - - type: rect - bounds: [0, 0, 128, 128] - color: magenta - - type: stacking-context - mix-blend-mode: lighten - items: - - image: checkerboard(0, 16, 16, 8, 8) - bounds: [0, 0, 128, 128] diff --git a/third_party/webrender/wrench/reftests/tiles/mix-blend-clip.yaml b/third_party/webrender/wrench/reftests/tiles/mix-blend-clip.yaml deleted file mode 100644 index be41c9b508e..00000000000 --- a/third_party/webrender/wrench/reftests/tiles/mix-blend-clip.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Test that a backdrop which has both a shared clip (on the tile cache) and is also scrolled -# correctly calculates the available backdrop rect for a mix-blend on a child. ---- -root: - items: - - - bounds: [0, 0, 128, 128] - type: clip - id: 2 - - type: stacking-context - blend-container: true - items: - - type: scroll-frame - bounds: 0 0 128 512 - scroll-offset: [0, 128] - id: 3 - items: - - type: rect - bounds: [0, 0, 128, 512] - color: magenta - clip-and-scroll: [3, 2] - - type: stacking-context - mix-blend-mode: lighten - clip-node: 2 - items: - - image: checkerboard(0, 16, 16, 8, 16) - bounds: [0, 0, 128, 256] diff --git a/third_party/webrender/wrench/reftests/tiles/reftest.list b/third_party/webrender/wrench/reftests/tiles/reftest.list index 35bb7875b6c..7c04207b183 100644 --- a/third_party/webrender/wrench/reftests/tiles/reftest.list +++ b/third_party/webrender/wrench/reftests/tiles/reftest.list @@ -1,6 +1,4 @@ ** rect.yaml -fuzzy(1,2000) ** simple-gradient.yaml +** simple-gradient.yaml # TODO: Fix rasterizer inaccuracies so this is the same regardless of tile size! !* prim-suite.yaml -== mix-blend-clip.yaml mix-blend-clip-ref.yaml -platform(linux) == tile-cache-raster-root.yaml tile-cache-raster-root.png diff --git a/third_party/webrender/wrench/reftests/tiles/tile-cache-raster-root.png b/third_party/webrender/wrench/reftests/tiles/tile-cache-raster-root.png Binary files differdeleted file mode 100644 index 120f63a7f07..00000000000 --- a/third_party/webrender/wrench/reftests/tiles/tile-cache-raster-root.png +++ /dev/null diff --git a/third_party/webrender/wrench/reftests/tiles/tile-cache-raster-root.yaml b/third_party/webrender/wrench/reftests/tiles/tile-cache-raster-root.yaml deleted file mode 100644 index 8ff36583c3f..00000000000 --- a/third_party/webrender/wrench/reftests/tiles/tile-cache-raster-root.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# Based on a reduced case from blend-clipped.yaml, ensure that if tile caches don't create -# raster roots (which messes with the special case handling and positioning of them). ---- -root: - items: - - - type: "stacking-context" - items: - - - bounds: [0, 111, 1887, 1971] - type: iframe - id: [1, 3] - id: [1, 1] -pipelines: - - - id: [1, 3] - items: - - - type: "scroll-frame" - id: 2 - bounds: [0, 0, 1887, 1971] - - - "clip-and-scroll": 2 - type: "stacking-context" - items: - - - bounds: [0, -186, 1887, 239] - "clip-and-scroll": 2 - type: clip - id: 4 - - - bounds: [-660.45, -186, 0, 0] - "clip-and-scroll": 4 - type: "stacking-context" - transform: [1, 0, 0, 0, -0.57735026, 1, 0, 0, 0, 0, 1, 0, 68.849, 0, 0, 1] - items: - - - bounds: [0, 0, 1887, 239] - type: rect - color: blue diff --git a/third_party/webrender/wrench/reftests/transforms/border-scale-2.png b/third_party/webrender/wrench/reftests/transforms/border-scale-2.png Binary files differindex b848abfbff9..09e81e0b2ae 100644 --- a/third_party/webrender/wrench/reftests/transforms/border-scale-2.png +++ b/third_party/webrender/wrench/reftests/transforms/border-scale-2.png diff --git a/third_party/webrender/wrench/reftests/transforms/border-scale-3.png b/third_party/webrender/wrench/reftests/transforms/border-scale-3.png Binary files differindex f51553e04f8..de110f5334d 100644 --- a/third_party/webrender/wrench/reftests/transforms/border-scale-3.png +++ b/third_party/webrender/wrench/reftests/transforms/border-scale-3.png diff --git a/third_party/webrender/wrench/reftests/transforms/border-scale-4.png b/third_party/webrender/wrench/reftests/transforms/border-scale-4.png Binary files differindex 6e1d664f9ae..a590cb153a0 100644 --- a/third_party/webrender/wrench/reftests/transforms/border-scale-4.png +++ b/third_party/webrender/wrench/reftests/transforms/border-scale-4.png diff --git a/third_party/webrender/wrench/reftests/transforms/border-scale.png b/third_party/webrender/wrench/reftests/transforms/border-scale.png Binary files differindex 65772686639..cd88307a678 100644 --- a/third_party/webrender/wrench/reftests/transforms/border-scale.png +++ b/third_party/webrender/wrench/reftests/transforms/border-scale.png diff --git a/third_party/webrender/wrench/reftests/transforms/border-zoom.png b/third_party/webrender/wrench/reftests/transforms/border-zoom.png Binary files differindex 0bd7cc21ab3..cb634ef97c4 100644 --- a/third_party/webrender/wrench/reftests/transforms/border-zoom.png +++ b/third_party/webrender/wrench/reftests/transforms/border-zoom.png diff --git a/third_party/webrender/wrench/reftests/transforms/coord-system.png b/third_party/webrender/wrench/reftests/transforms/coord-system.png Binary files differindex af5213f906b..d28a562186a 100644 --- a/third_party/webrender/wrench/reftests/transforms/coord-system.png +++ b/third_party/webrender/wrench/reftests/transforms/coord-system.png diff --git a/third_party/webrender/wrench/reftests/transforms/flatten-all-flat-ref.yaml b/third_party/webrender/wrench/reftests/transforms/flatten-all-flat-ref.yaml deleted file mode 100644 index fd60bccb4bd..00000000000 --- a/third_party/webrender/wrench/reftests/transforms/flatten-all-flat-ref.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- -root: - items: - - type: reference-frame - transform-style: flat - items: - - type: reference-frame - perspective: 1000 - transform-style: flat - items: - - type: reference-frame - transform: rotate-z(-45) rotate-x(-75) - bounds: 50 0 100 100 - transform-style: flat - items: - - type: rect - bounds: 0 0 100 100 - color: green diff --git a/third_party/webrender/wrench/reftests/transforms/flatten-all-flat.yaml b/third_party/webrender/wrench/reftests/transforms/flatten-all-flat.yaml deleted file mode 100644 index d471b052cc1..00000000000 --- a/third_party/webrender/wrench/reftests/transforms/flatten-all-flat.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# This test has a series of flat transforms that affect Z. ---- -root: - items: - - type: reference-frame - transform: rotate-y(0.0001) # this transform used to affect the Z coordinates during flattening - transform-style: flat - items: - - type: reference-frame - perspective: 1000 - transform-style: flat - items: - - type: reference-frame - transform: rotate-z(-45) rotate-x(-75) - bounds: 50 0 100 100 - transform-style: flat - items: - - type: rect - bounds: 0 0 100 100 - color: green diff --git a/third_party/webrender/wrench/reftests/transforms/image-rotated-clip.png b/third_party/webrender/wrench/reftests/transforms/image-rotated-clip.png Binary files differindex 96439dbc6e4..fda2872a3eb 100644 --- a/third_party/webrender/wrench/reftests/transforms/image-rotated-clip.png +++ b/third_party/webrender/wrench/reftests/transforms/image-rotated-clip.png diff --git a/third_party/webrender/wrench/reftests/transforms/local-clip.png b/third_party/webrender/wrench/reftests/transforms/local-clip.png Binary files differindex 3f22d01dbe8..92e25ec5e59 100644 --- a/third_party/webrender/wrench/reftests/transforms/local-clip.png +++ b/third_party/webrender/wrench/reftests/transforms/local-clip.png diff --git a/third_party/webrender/wrench/reftests/transforms/near-plane-clip.png b/third_party/webrender/wrench/reftests/transforms/near-plane-clip.png Binary files differindex e051c725b68..babe22d7482 100644 --- a/third_party/webrender/wrench/reftests/transforms/near-plane-clip.png +++ b/third_party/webrender/wrench/reftests/transforms/near-plane-clip.png diff --git a/third_party/webrender/wrench/reftests/transforms/nested-preserve-3d.png b/third_party/webrender/wrench/reftests/transforms/nested-preserve-3d.png Binary files differindex 686a22cf0a1..3b74c198999 100644 --- a/third_party/webrender/wrench/reftests/transforms/nested-preserve-3d.png +++ b/third_party/webrender/wrench/reftests/transforms/nested-preserve-3d.png diff --git a/third_party/webrender/wrench/reftests/transforms/nested-rotate-x-flat.png b/third_party/webrender/wrench/reftests/transforms/nested-rotate-x-flat.png Binary files differindex ca33ca1336c..2134a8ff77d 100644 --- a/third_party/webrender/wrench/reftests/transforms/nested-rotate-x-flat.png +++ b/third_party/webrender/wrench/reftests/transforms/nested-rotate-x-flat.png diff --git a/third_party/webrender/wrench/reftests/transforms/nested-rotate-x.png b/third_party/webrender/wrench/reftests/transforms/nested-rotate-x.png Binary files differindex 73ad1cec412..4a6cc2352da 100644 --- a/third_party/webrender/wrench/reftests/transforms/nested-rotate-x.png +++ b/third_party/webrender/wrench/reftests/transforms/nested-rotate-x.png diff --git a/third_party/webrender/wrench/reftests/transforms/non-inversible-world-rect.yaml b/third_party/webrender/wrench/reftests/transforms/non-inversible-world-rect.yaml deleted file mode 100644 index f94404f58ed..00000000000 --- a/third_party/webrender/wrench/reftests/transforms/non-inversible-world-rect.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Tests that `get_raster_rects` raster -> world transform is inversible in general, -# but one of the vertices of the world rectangles can't map back to the raster. ---- -root: - items: - - type: stacking-context - bounds: 0 0 400 400 - perspective: 800 - perspective-origin: 50% 200 - items: - - type: stacking-context - bounds: 0 0 400 400 - transform-style: preserve-3d - transform: rotate-z(40) translate(400, 200, 0) - margin: 100 - items: - - type: stacking-context - bounds: 0 0 1000 1000 - transform: rotate-y(-75) translate(0, 0, -500) - items: - - type: rect - bounds: [0, 0, 200, 200] - color: red diff --git a/third_party/webrender/wrench/reftests/transforms/perspective-border-radius.png b/third_party/webrender/wrench/reftests/transforms/perspective-border-radius.png Binary files differindex 12db11efc72..6868bef4308 100644 --- a/third_party/webrender/wrench/reftests/transforms/perspective-border-radius.png +++ b/third_party/webrender/wrench/reftests/transforms/perspective-border-radius.png diff --git a/third_party/webrender/wrench/reftests/transforms/perspective-clip-1.png b/third_party/webrender/wrench/reftests/transforms/perspective-clip-1.png Binary files differindex 2cc992c168d..2708424772d 100644 --- a/third_party/webrender/wrench/reftests/transforms/perspective-clip-1.png +++ b/third_party/webrender/wrench/reftests/transforms/perspective-clip-1.png diff --git a/third_party/webrender/wrench/reftests/transforms/perspective-clip.png b/third_party/webrender/wrench/reftests/transforms/perspective-clip.png Binary files differindex bd31aba99b9..97be10064a1 100644 --- a/third_party/webrender/wrench/reftests/transforms/perspective-clip.png +++ b/third_party/webrender/wrench/reftests/transforms/perspective-clip.png diff --git a/third_party/webrender/wrench/reftests/transforms/perspective-mask.png b/third_party/webrender/wrench/reftests/transforms/perspective-mask.png Binary files differindex a51e6ce2e10..41ef1283dbb 100644 --- a/third_party/webrender/wrench/reftests/transforms/perspective-mask.png +++ b/third_party/webrender/wrench/reftests/transforms/perspective-mask.png diff --git a/third_party/webrender/wrench/reftests/transforms/perspective-origin.png b/third_party/webrender/wrench/reftests/transforms/perspective-origin.png Binary files differindex 48c528a6d0a..bda2d9dac21 100644 --- a/third_party/webrender/wrench/reftests/transforms/perspective-origin.png +++ b/third_party/webrender/wrench/reftests/transforms/perspective-origin.png diff --git a/third_party/webrender/wrench/reftests/transforms/perspective-shadow.png b/third_party/webrender/wrench/reftests/transforms/perspective-shadow.png Binary files differindex 07befbd35ba..2a0d50806c0 100644 --- a/third_party/webrender/wrench/reftests/transforms/perspective-shadow.png +++ b/third_party/webrender/wrench/reftests/transforms/perspective-shadow.png diff --git a/third_party/webrender/wrench/reftests/transforms/perspective.png b/third_party/webrender/wrench/reftests/transforms/perspective.png Binary files differindex 93f38d9a1c9..f4ce971d578 100644 --- a/third_party/webrender/wrench/reftests/transforms/perspective.png +++ b/third_party/webrender/wrench/reftests/transforms/perspective.png diff --git a/third_party/webrender/wrench/reftests/transforms/prim-suite.png b/third_party/webrender/wrench/reftests/transforms/prim-suite.png Binary files differindex 6d0c695e94c..cec3ecc3214 100644 --- a/third_party/webrender/wrench/reftests/transforms/prim-suite.png +++ b/third_party/webrender/wrench/reftests/transforms/prim-suite.png diff --git a/third_party/webrender/wrench/reftests/transforms/raster-root-huge-scale.yaml b/third_party/webrender/wrench/reftests/transforms/raster-root-huge-scale.yaml index fd63c63031c..269c71cccaf 100644 --- a/third_party/webrender/wrench/reftests/transforms/raster-root-huge-scale.yaml +++ b/third_party/webrender/wrench/reftests/transforms/raster-root-huge-scale.yaml @@ -15,8 +15,8 @@ root: transform-style: preserve-3d items: - type: clip - bounds: [0, 0, 60000, 60000] - clip-rect: [0, 0, 60000, 60000] + bounds: [10, 10, 60000, 60000] + clip-rect: [10, 10, 60000, 60000] id: 2 complex: - rect: [0, 0, 100, 100] @@ -28,9 +28,3 @@ root: items: - rect: 0 0 100 100 color: red - - type: box-shadow - bounds: 0 0 100 100 - color: blue - offset: 10 20 - blur-radius: 10 - border-radius: [ 20, 20, 20, 20 ] diff --git a/third_party/webrender/wrench/reftests/transforms/reftest.list b/third_party/webrender/wrench/reftests/transforms/reftest.list index 537ae1e03b8..5931b7d6e52 100644 --- a/third_party/webrender/wrench/reftests/transforms/reftest.list +++ b/third_party/webrender/wrench/reftests/transforms/reftest.list @@ -4,22 +4,22 @@ platform(linux,mac) == rotated-clip-large.yaml rotated-clip-large.png platform(linux,mac) == image-rotated-clip.yaml image-rotated-clip.png # Something leaks the state: the test passes if only run `reftest reftests/transform` # but fails when all the tests are run -platform(linux,mac) fuzzy(1,6) == rotated-image.yaml rotated-image.png +platform(linux,mac) fuzzy(1,4) == rotated-image.yaml rotated-image.png == singular.yaml singular-ref.yaml platform(linux) fuzzy(1,630) == perspective.yaml perspective.png -platform(linux,mac) fuzzy(3,8100) == prim-suite.yaml prim-suite.png -fuzzy-if(platform(swgl),1,2) == segments-bug.yaml segments-bug-ref.yaml +platform(linux,mac) fuzzy(1,156) == prim-suite.yaml prim-suite.png +== segments-bug.yaml segments-bug-ref.yaml platform(linux,mac) == content-offset.yaml content-offset.png platform(linux,mac) == coord-system.yaml coord-system.png -platform(linux,mac) fuzzy(1,15) zoom(4) == border-zoom.yaml border-zoom.png +platform(linux,mac) fuzzy(1,5) zoom(4) == border-zoom.yaml border-zoom.png platform(linux) fuzzy(1,520) == perspective-origin.yaml perspective-origin.png platform(linux,mac) color_targets(3) alpha_targets(0) fuzzy(1,180) == screen-space-blit.yaml screen-space-blit.png -platform(linux,mac) fuzzy(1,346) color_targets(2) alpha_targets(0) == screen-space-blit-trivial.yaml screen-space-blit-trivial.png +platform(linux,mac) fuzzy(1,331) color_targets(2) alpha_targets(0) == screen-space-blit-trivial.yaml screen-space-blit-trivial.png platform(linux) fuzzy(11,4592) == screen-space-blur.yaml screen-space-blur.png -platform(linux,mac) fuzzy(1,25) == nested-rotate-x.yaml nested-rotate-x.png +platform(linux,mac) == nested-rotate-x.yaml nested-rotate-x.png platform(linux,mac) != nested-rotate-x.yaml nested-rotate-x-flat.yaml -platform(linux,mac) fuzzy(1,25) == nested-rotate-x-flat.yaml nested-rotate-x-flat.png -platform(linux,mac) fuzzy(1,25) == nested-preserve-3d.yaml nested-preserve-3d.png +platform(linux,mac) == nested-rotate-x-flat.yaml nested-rotate-x-flat.png +platform(linux,mac) == nested-preserve-3d.yaml nested-preserve-3d.png platform(linux,mac) fuzzy(1,283) == near-plane-clip.yaml near-plane-clip.png platform(linux,mac) == perspective-mask.yaml perspective-mask.png == rotate-clip.yaml rotate-clip-ref.yaml @@ -30,7 +30,7 @@ platform(linux,mac) fuzzy(1,2) == perspective-shadow.yaml perspective-shadow.png # The ref YAML here produces significantly worse quality fuzzy(200,4200) == perspective-box-shadow.yaml perspective-box-shadow-ref.yaml == complex-preserve-3d.yaml blank.yaml -platform(linux,mac) fuzzy(38,348) == perspective-border-radius.yaml perspective-border-radius.png +platform(linux,mac) fuzzy(9,348) == perspective-border-radius.yaml perspective-border-radius.png fuzzy(1,38) == snapped-preserve-3d.yaml snapped-preserve-3d-ref.yaml platform(linux,mac) fuzzy(1,122) == border-scale.yaml border-scale.png platform(linux,mac) fuzzy(1,16) == border-scale-2.yaml border-scale-2.png @@ -40,7 +40,6 @@ platform(linux,mac) fuzzy(1,74) == border-scale-4.yaml border-scale-4.png != large-raster-root.yaml blank.yaml == flatten-preserve-3d-root.yaml flatten-preserve-3d-root-ref.yaml == flatten-twice.yaml flatten-twice-ref.yaml -fuzzy(1,10) == flatten-all-flat.yaml flatten-all-flat-ref.yaml == strange-w.yaml strange-w-ref.yaml == big-axis-aligned-scale.yaml big-axis-aligned-scale-ref.yaml # Compare ~8K raster root (>MAX_SURFACE_SIZE) with ~2K raster root. fuzzy due to lerping on edges. @@ -53,4 +52,3 @@ skip_on(android) == raster-root-scaling.yaml raster-root-scaling-ref.yaml skip_on(android) == raster-root-scaling-2.yaml raster-root-scaling-2-ref.yaml # Make sure we don't panic != raster-root-huge-scale.yaml blank.yaml -!= non-inversible-world-rect.yaml blank.yaml diff --git a/third_party/webrender/wrench/reftests/transforms/rotated-clip-large.png b/third_party/webrender/wrench/reftests/transforms/rotated-clip-large.png Binary files differindex e3c8ce7ab4a..c4e48403709 100644 --- a/third_party/webrender/wrench/reftests/transforms/rotated-clip-large.png +++ b/third_party/webrender/wrench/reftests/transforms/rotated-clip-large.png diff --git a/third_party/webrender/wrench/reftests/transforms/rotated-clip.png b/third_party/webrender/wrench/reftests/transforms/rotated-clip.png Binary files differindex fe6e2aedb00..33d2771d3a9 100644 --- a/third_party/webrender/wrench/reftests/transforms/rotated-clip.png +++ b/third_party/webrender/wrench/reftests/transforms/rotated-clip.png diff --git a/third_party/webrender/wrench/reftests/transforms/rotated-image.png b/third_party/webrender/wrench/reftests/transforms/rotated-image.png Binary files differindex 012ffe60452..2bb0d45e48d 100644 --- a/third_party/webrender/wrench/reftests/transforms/rotated-image.png +++ b/third_party/webrender/wrench/reftests/transforms/rotated-image.png diff --git a/third_party/webrender/wrench/reftests/transforms/screen-space-blit-trivial.png b/third_party/webrender/wrench/reftests/transforms/screen-space-blit-trivial.png Binary files differindex 66c4e214d4a..0c7ed88dd60 100644 --- a/third_party/webrender/wrench/reftests/transforms/screen-space-blit-trivial.png +++ b/third_party/webrender/wrench/reftests/transforms/screen-space-blit-trivial.png diff --git a/third_party/webrender/wrench/reftests/transforms/screen-space-blit.png b/third_party/webrender/wrench/reftests/transforms/screen-space-blit.png Binary files differindex 360db5a6041..6479438fe15 100644 --- a/third_party/webrender/wrench/reftests/transforms/screen-space-blit.png +++ b/third_party/webrender/wrench/reftests/transforms/screen-space-blit.png diff --git a/third_party/webrender/wrench/reftests/transforms/screen-space-blur.png b/third_party/webrender/wrench/reftests/transforms/screen-space-blur.png Binary files differindex 4c718db42e8..e4e3df37318 100644 --- a/third_party/webrender/wrench/reftests/transforms/screen-space-blur.png +++ b/third_party/webrender/wrench/reftests/transforms/screen-space-blur.png diff --git a/third_party/webrender/wrench/reftests/transforms/screen-space-blur.yaml b/third_party/webrender/wrench/reftests/transforms/screen-space-blur.yaml index 6d93260c83a..e9c5f5d32dc 100644 --- a/third_party/webrender/wrench/reftests/transforms/screen-space-blur.yaml +++ b/third_party/webrender/wrench/reftests/transforms/screen-space-blur.yaml @@ -7,14 +7,14 @@ root: - type: "stacking-context" transform-origin: 235 235 transform: rotate-x(-15) - filters: blur(3, 3) + filters: blur(3) items: - image: checkerboard(2, 16, 16) bounds: [100, 100, 260, 260] - type: "stacking-context" transform-origin: 635 235 transform: rotate-z(-45) - filters: blur(3, 3) + filters: blur(3) items: - image: checkerboard(2, 16, 16) bounds: [500, 100, 260, 260] diff --git a/third_party/webrender/wrench/script/headless.py b/third_party/webrender/wrench/script/headless.py index 109fca1cb2e..0b98b138626 100755 --- a/third_party/webrender/wrench/script/headless.py +++ b/third_party/webrender/wrench/script/headless.py @@ -96,16 +96,18 @@ def optimized_build(): def set_osmesa_env(bin_path): """Set proper LD_LIBRARY_PATH and DRIVE for software rendering on Linux and OSX""" - base = find_dep_path_newest('osmesa-src', bin_path) - osmesa_path = path.join(base, "out", "mesa", "src", "gallium", "targets", "osmesa") - os.environ["GALLIUM_DRIVER"] = "llvmpipe" if is_linux(): + osmesa_path = path.join(find_dep_path_newest('osmesa-src', bin_path), "out", "lib", "gallium") print(osmesa_path) os.environ["LD_LIBRARY_PATH"] = osmesa_path + os.environ["GALLIUM_DRIVER"] = "softpipe" elif is_macos(): - osmesa_path = path.join(base, "out", "mesa", "src", "gallium", "targets", "osmesa") - glapi_path = path.join(base, "out", "mesa", "src", "mapi", "shared-glapi") + osmesa_path = path.join(find_dep_path_newest('osmesa-src', bin_path), + "out", "src", "gallium", "targets", "osmesa", ".libs") + glapi_path = path.join(find_dep_path_newest('osmesa-src', bin_path), + "out", "src", "mapi", "shared-glapi", ".libs") os.environ["DYLD_LIBRARY_PATH"] = osmesa_path + ":" + glapi_path + os.environ["GALLIUM_DRIVER"] = "softpipe" extra_flags = os.getenv('CARGOFLAGS', None) @@ -147,6 +149,6 @@ set_osmesa_env(target_folder) # cause 1.0 / 255.0 pixel differences in a subsequent test. For now, we # run tests with no-scissor mode, which ensures a complete target clear # between test runs. But we should investigate this further... -cmd = dbg_cmd + [target_folder + 'wrench', '--no-scissor', '--headless'] + sys.argv[1:] +cmd = dbg_cmd + [target_folder + 'wrench', '--no-scissor', '-h'] + sys.argv[1:] print('Running: `' + ' '.join(cmd) + '`') -subprocess.check_call(cmd, stderr=subprocess.STDOUT) +subprocess.check_call(cmd) diff --git a/third_party/webrender/wrench/src/args.yaml b/third_party/webrender/wrench/src/args.yaml index 46538b2536c..d8a263fcca5 100644 --- a/third_party/webrender/wrench/src/args.yaml +++ b/third_party/webrender/wrench/src/args.yaml @@ -28,14 +28,22 @@ args: short: r long: rebuild help: Rebuild display list from scratch every frame + - save: + long: save + help: 'Save frames, one of: yaml, json, ron, or binary' + takes_value: true - no_subpixel_aa: short: a long: no-subpixel-aa help: Disable subpixel aa + - no_picture_caching: + long: no-picture-caching + help: Disable picture caching - slow_subpixel: long: slow-subpixel help: Disable dual source blending - headless: + short: h long: headless help: Enable headless rendering - angle: @@ -54,6 +62,11 @@ args: long: size help: Window size, specified as widthxheight (e.g. 1024x768), in pixels takes_value: true + - time: + short: t + long: time + help: Time limit (in seconds) + takes_value: true - vsync: long: vsync help: Enable vsync for OpenGL window @@ -80,10 +93,6 @@ args: - no_block: long: no-block help: Don't block on UI events - run event loop as fast as possible. - - profiler_ui: - long: profiler-ui - takes_value: true - help: A string describing what to show on in the profiler HUD (See https://github.com/servo/webrender/wiki/Debugging-WebRender#anchor_6). subcommands: - png: @@ -106,6 +115,11 @@ subcommands: about: show frame(s) described by YAML, binary recording, or capture aliases: ['load', 'replay'] args: + - queue: + short: q + long: queue + help: How many frames to submit to WR ahead of time (default 1) (YAML only) + takes_value: true - include: long: include help: Include the given element type. Can be specified multiple times. (rect/image/text/glyphs/border) (YAML only) @@ -118,6 +132,15 @@ subcommands: short: w long: watch help: Watch the given file, reloading whenever it changes (YAML only) + - api: + long: api + help: Reissue Api messages for each frame (binary recording only) + - skip-uploads: + long: skip-uploads + help: Skip re-uploads while reissuing Api messages (BROKEN) + - play: + long: play + help: Play entire recording through, then quit (useful with --save) (binary recording only) - keyframes: short: k long: keyframes @@ -137,6 +160,13 @@ subcommands: help: The input YAML, binary recording, or capture directory required: true index: 1 + - replay: + about: replay binary recording + args: + - INPUT: + help: The input binary file or directory + required: true + index: 1 - reftest: about: run reftests args: @@ -179,8 +209,6 @@ subcommands: long: sample_count takes_value: true help: number of samples to capture - - test_invalidation: - about: run invalidation tests - compare_perf: about: compare two benchmark files args: @@ -192,5 +220,3 @@ subcommands: help: second benchmark file to compare required: true index: 2 - - test_init: - about: Test for successful initialization then exit immediately diff --git a/third_party/webrender/wrench/src/egl.rs b/third_party/webrender/wrench/src/egl.rs index 4b91d8cd86d..4508646b452 100644 --- a/third_party/webrender/wrench/src/egl.rs +++ b/third_party/webrender/wrench/src/egl.rs @@ -467,7 +467,7 @@ unsafe fn choose_fbconfig(display: ffi::egl::types::EGLDisplay, value } ) - } + }; let desc = PixelFormat { hardware_accelerated: attrib!(display, config_id, ffi::egl::CONFIG_CAVEAT) diff --git a/third_party/webrender/wrench/src/main.rs b/third_party/webrender/wrench/src/main.rs index 843512f5953..9a7dd3bcfea 100644 --- a/third_party/webrender/wrench/src/main.rs +++ b/third_party/webrender/wrench/src/main.rs @@ -8,8 +8,6 @@ extern crate clap; extern crate log; #[macro_use] extern crate serde; -#[macro_use] -extern crate tracy_rs; mod angle; mod blob; @@ -20,7 +18,6 @@ mod png; mod premultiply; mod rawtest; mod reftest; -mod test_invalidation; mod wrench; mod yaml_frame_reader; mod yaml_helper; @@ -47,7 +44,6 @@ use std::slice; use std::sync::mpsc::{channel, Sender, Receiver}; use webrender::DebugFlags; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; use winit::dpi::{LogicalPosition, LogicalSize}; use winit::VirtualKeyCode; @@ -158,8 +154,7 @@ impl WindowWrapper { let gl = self.native_gl(); let tex = gl.gen_textures(1)[0]; gl.bind_texture(gl::TEXTURE_2D, tex); - let (data_ptr, w, h, stride) = swgl.get_color_buffer(0, true); - assert!(stride == w * 4); + 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]; @@ -289,7 +284,7 @@ impl WindowWrapper { #[cfg(feature = "software")] fn update_software(&self, dim: DeviceIntSize) { if let Some(swgl) = self.software_gl() { - swgl.init_default_framebuffer(0, 0, dim.width, dim.height, 0, std::ptr::null_mut()); + swgl.init_default_framebuffer(dim.width, dim.height); } } @@ -305,15 +300,15 @@ impl WindowWrapper { } #[cfg(feature = "software")] -fn make_software_context() -> swgl::Context { +fn make_software_context() -> Option<swgl::Context> { let ctx = swgl::Context::create(); ctx.make_current(); - ctx + Some(ctx) } #[cfg(not(feature = "software"))] -fn make_software_context() -> swgl::Context { - panic!("software feature not enabled") +fn make_software_context() -> Option<swgl::Context> { + None } fn make_window( @@ -326,7 +321,7 @@ fn make_window( software: bool, ) -> WindowWrapper { let sw_ctx = if software { - Some(make_software_context()) + make_software_context() } else { None }; @@ -436,9 +431,7 @@ fn make_window( #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum NotifierEvent { - WakeUp { - composite_needed: bool, - }, + WakeUp, ShutDown, } @@ -454,14 +447,8 @@ impl RenderNotifier for Notifier { }) } - fn wake_up( - &self, - composite_needed: bool, - ) { - let msg = NotifierEvent::WakeUp { - composite_needed, - }; - self.tx.send(msg).unwrap(); + fn wake_up(&self) { + self.tx.send(NotifierEvent::WakeUp).unwrap(); } fn shut_down(&self) { @@ -471,11 +458,11 @@ impl RenderNotifier for Notifier { fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, - composite_needed: bool, + _composite_needed: bool, _render_time: Option<u64>) { // TODO(gw): Refactor wrench so that it can take advantage of cases // where no composite is required when appropriate. - self.wake_up(composite_needed); + self.wake_up(); } } @@ -646,7 +633,7 @@ fn main() { let dp_ratio = dp_ratio.unwrap_or(window.hidpi_factor()); let dim = window.get_inner_size(); - let needs_frame_notifier = ["perf", "reftest", "png", "rawtest", "test_invalidation"] + let needs_frame_notifier = ["perf", "reftest", "png", "rawtest"] .iter() .any(|s| args.subcommand_matches(s).is_some()); let (notifier, rx) = if needs_frame_notifier { @@ -665,6 +652,7 @@ fn main() { dim, args.is_present("rebuild"), args.is_present("no_subpixel_aa"), + args.is_present("no_picture_caching"), args.is_present("verbose"), args.is_present("no_scissor"), args.is_present("no_batch"), @@ -675,11 +663,6 @@ fn main() { dump_shader_source, notifier, ); - - if let Some(ui_str) = args.value_of("profiler_ui") { - wrench.renderer.set_profiler_ui(&ui_str); - } - window.update(&mut wrench); if let Some(window_title) = wrench.take_title() { @@ -706,7 +689,7 @@ fn main() { Some("gpu-cache") => png::ReadSurface::GpuCache, _ => panic!("Unknown surface argument value") }; - let output_path = subargs.value_of("OUTPUT").map(PathBuf::from); + let output_path = subargs.value_of("OUTPUT").map(|s| PathBuf::from(s)); let reader = YamlFrameReader::new_from_args(subargs); png::png(&mut wrench, surface, &mut window, reader, rx.unwrap(), output_path); } else if let Some(subargs) = args.subcommand_matches("reftest") { @@ -749,32 +732,16 @@ fn main() { } harness.run(base_manifest, &filename, as_csv); return; - } else if let Some(_) = args.subcommand_matches("test_invalidation") { - let harness = test_invalidation::TestHarness::new( - &mut wrench, - &mut window, - rx.unwrap(), - ); - - harness.run(); } else if let Some(subargs) = args.subcommand_matches("compare_perf") { let first_filename = subargs.value_of("first_filename").unwrap(); let second_filename = subargs.value_of("second_filename").unwrap(); perf::compare(first_filename, second_filename); return; - } else if let Some(_) = args.subcommand_matches("test_init") { - // Wrench::new() unwraps the Renderer initialization, so if - // we reach this point then we have initialized successfully. - println!("Initialization successful"); } else { panic!("Should never have gotten here! {:?}", args); }; wrench.renderer.deinit(); - - // On android force-exit the process otherwise it stays running forever. - #[cfg(target_os = "android")] - process::exit(0); } fn render<'a>( @@ -832,7 +799,7 @@ fn render<'a>( // Default the profile overlay on for android. if cfg!(target_os = "android") { - debug_flags.toggle(DebugFlags::PROFILER_DBG); + debug_flags.toggle(DebugFlags::PROFILER_DBG | DebugFlags::COMPACT_PROFILER); wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags)); } @@ -874,6 +841,11 @@ fn render<'a>( VirtualKeyCode::Escape => { return winit::ControlFlow::Break; } + VirtualKeyCode::A => { + debug_flags.toggle(DebugFlags::DISABLE_PICTURE_CACHING); + wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags)); + do_render = true; + } VirtualKeyCode::B => { debug_flags.toggle(DebugFlags::INVALIDATION_DBG); wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags)); @@ -894,6 +866,11 @@ fn render<'a>( wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags)); do_render = true; } + VirtualKeyCode::S => { + debug_flags.toggle(DebugFlags::COMPACT_PROFILER); + wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags)); + do_render = true; + } VirtualKeyCode::D => { debug_flags.toggle(DebugFlags::PICTURE_CACHING_DBG); wrench.api.send_debug_cmd(DebugCommand::SetFlags(debug_flags)); @@ -948,6 +925,21 @@ fn render<'a>( let path = PathBuf::from("../captures/wrench"); wrench.api.save_capture(path, CaptureBits::all()); } + VirtualKeyCode::Up | VirtualKeyCode::Down => { + let mut txn = Transaction::new(); + + let offset = match vk { + winit::VirtualKeyCode::Up => LayoutVector2D::new(0.0, 10.0), + winit::VirtualKeyCode::Down => LayoutVector2D::new(0.0, -10.0), + _ => unreachable!("Should not see non directional keys here.") + }; + + txn.scroll(ScrollLocation::Delta(offset), cursor_position); + txn.generate_frame(); + wrench.api.send_transaction(wrench.document_id, txn); + + do_frame = true; + } VirtualKeyCode::Add => { let current_zoom = wrench.get_page_zoom(); let new_zoom_factor = ZoomFactor::new(current_zoom.get() + 0.1); @@ -965,7 +957,7 @@ fn render<'a>( wrench.document_id, None, cursor_position, - HitTestFlags::empty(), + HitTestFlags::FIND_ALL ); println!("Hit test results:"); diff --git a/third_party/webrender/wrench/src/perf.rs b/third_party/webrender/wrench/src/perf.rs index a67298ce29a..bd41ba6016f 100644 --- a/third_party/webrender/wrench/src/perf.rs +++ b/third_party/webrender/wrench/src/perf.rs @@ -14,7 +14,7 @@ use std::sync::mpsc::Receiver; use crate::wrench::{Wrench, WrenchThing}; use crate::yaml_frame_reader::YamlFrameReader; use webrender::DebugFlags; -use webrender::render_api::DebugCommand; +use webrender::api::DebugCommand; const COLOR_DEFAULT: &str = "\x1b[0m"; const COLOR_RED: &str = "\x1b[31m"; diff --git a/third_party/webrender/wrench/src/rawtest.rs b/third_party/webrender/wrench/src/rawtest.rs index b86b5836176..f0315155b17 100644 --- a/third_party/webrender/wrench/src/rawtest.rs +++ b/third_party/webrender/wrench/src/rawtest.rs @@ -7,7 +7,6 @@ use std::sync::Arc; use std::sync::atomic::{AtomicIsize, Ordering}; use std::sync::mpsc::Receiver; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; use crate::{WindowWrapper, NotifierEvent}; use crate::blob; @@ -107,7 +106,7 @@ impl<'a> RawtestHarness<'a> { ); epoch.0 += 1; - txn.generate_frame(0); + txn.generate_frame(); self.wrench.api.send_transaction(self.wrench.document_id, txn); } @@ -118,6 +117,7 @@ impl<'a> RawtestHarness<'a> { clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, flags: PrimitiveFlags::default(), + hit_info: None, } } @@ -132,6 +132,7 @@ impl<'a> RawtestHarness<'a> { clip_id, spatial_id, flags: PrimitiveFlags::default(), + hit_info: None, } } @@ -154,7 +155,7 @@ impl<'a> RawtestHarness<'a> { None, ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0.0, 0.0, 64.0, 64.0)); builder.push_image( @@ -181,7 +182,7 @@ impl<'a> RawtestHarness<'a> { &DirtyRect::All, ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0.0, 0.0, 1024.0, 1024.0)); builder.push_image( @@ -206,7 +207,7 @@ impl<'a> RawtestHarness<'a> { &DirtyRect::All, ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0.0, 0.0, 1024.0, 1024.0)); builder.push_image( @@ -242,7 +243,7 @@ impl<'a> RawtestHarness<'a> { Some(128), ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(448.899994, 74.0, 151.000031, 56.)); @@ -306,7 +307,7 @@ impl<'a> RawtestHarness<'a> { called_inner.fetch_add(1, Ordering::SeqCst); }); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id); let clip_id = builder.define_clip_rect( @@ -319,6 +320,7 @@ impl<'a> RawtestHarness<'a> { clip_id, spatial_id: root_space_and_clip.spatial_id, flags: PrimitiveFlags::default(), + hit_info: None, }; // setup some malicious image size parameters @@ -389,7 +391,7 @@ impl<'a> RawtestHarness<'a> { Some(100), ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let image_size = size2(400.0, 400.0); @@ -404,6 +406,7 @@ impl<'a> RawtestHarness<'a> { clip_id, spatial_id: root_space_and_clip.spatial_id, flags: PrimitiveFlags::default(), + hit_info: None, }; builder.push_repeating_image( @@ -485,7 +488,7 @@ impl<'a> RawtestHarness<'a> { Some(128), ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id); let clip_id = builder.define_clip_rect( @@ -498,6 +501,7 @@ impl<'a> RawtestHarness<'a> { clip_id, spatial_id: root_space_and_clip.spatial_id, flags: PrimitiveFlags::default(), + hit_info: None, }; builder.push_repeating_image( @@ -529,7 +533,7 @@ impl<'a> RawtestHarness<'a> { size: size2(400, 400), }); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id); let clip_id = builder.define_clip_rect( @@ -542,6 +546,7 @@ impl<'a> RawtestHarness<'a> { clip_id, spatial_id: root_space_and_clip.spatial_id, flags: PrimitiveFlags::default(), + hit_info: None, }; builder.push_repeating_image( @@ -575,7 +580,7 @@ impl<'a> RawtestHarness<'a> { Some(128), ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id); let clip_id = builder.define_clip_rect( @@ -588,6 +593,7 @@ impl<'a> RawtestHarness<'a> { clip_id, spatial_id: root_space_and_clip.spatial_id, flags: PrimitiveFlags::default(), + hit_info: None, }; builder.push_repeating_image( @@ -640,7 +646,7 @@ impl<'a> RawtestHarness<'a> { None, ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0., 0.0, 1510., 1510.)); @@ -666,7 +672,7 @@ impl<'a> RawtestHarness<'a> { let mut epoch = Epoch(1); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(-10000., 0.0, 1510., 1510.)); @@ -698,7 +704,7 @@ impl<'a> RawtestHarness<'a> { &rect(10, 10, 100, 100).into(), ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0., 0.0, 1510., 1510.)); @@ -765,7 +771,7 @@ impl<'a> RawtestHarness<'a> { }); // draw the blob the first time - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0)); builder.push_image( @@ -788,7 +794,7 @@ impl<'a> RawtestHarness<'a> { // draw the blob image a second time at a different location // make a new display list that refers to the first image - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(1.0, 60.0, 200.0, 200.0)); builder.push_image( &info, @@ -872,7 +878,7 @@ impl<'a> RawtestHarness<'a> { }); // create two blob images and draw them - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0)); let info2 = self.make_common_properties(rect(200.0, 60.0, 200.0, 200.0)); let push_images = |builder: &mut DisplayListBuilder| { @@ -918,7 +924,7 @@ impl<'a> RawtestHarness<'a> { &rect(100, 100, 100, 100).into(), ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); push_images(&mut builder); self.submit_dl(&mut epoch, layout_size, builder, txn); let _pixels_second = self.render_and_get_pixels(window_rect); @@ -933,7 +939,7 @@ impl<'a> RawtestHarness<'a> { &rect(200, 200, 100, 100).into(), ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); push_images(&mut builder); self.submit_dl(&mut epoch, layout_size, builder, txn); let _pixels_third = self.render_and_get_pixels(window_rect); @@ -972,7 +978,7 @@ impl<'a> RawtestHarness<'a> { }; // draw the blobs the first time - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0)); builder.push_image( @@ -1000,7 +1006,7 @@ impl<'a> RawtestHarness<'a> { ); // make a new display list that refers to the first image - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0)); builder.push_image( &info, @@ -1025,7 +1031,7 @@ impl<'a> RawtestHarness<'a> { ); // make a new display list that refers to the first image - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0)); builder.push_image( &info, @@ -1056,7 +1062,7 @@ impl<'a> RawtestHarness<'a> { let layout_size = LayoutSize::new(400., 400.); let mut do_test = |should_try_and_fail| { - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let spatial_id = SpatialId::root_scroll_node(self.wrench.root_pipeline_id); let clip_id = builder.define_clip_rect( @@ -1104,6 +1110,7 @@ impl<'a> RawtestHarness<'a> { clip_id, spatial_id, flags: PrimitiveFlags::default(), + hit_info: None, }; builder.push_line( &info, @@ -1159,7 +1166,7 @@ impl<'a> RawtestHarness<'a> { let layout_size = LayoutSize::new(400., 400.); let mut do_test = |shadow_is_red| { - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let shadow_color = if shadow_is_red { ColorF::new(1.0, 0.0, 0.0, 1.0) } else { @@ -1218,7 +1225,7 @@ impl<'a> RawtestHarness<'a> { None, ); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(rect(300.0, 70.0, 150.0, 50.0)); builder.push_image( @@ -1239,7 +1246,7 @@ impl<'a> RawtestHarness<'a> { builder.finalize(), false, ); - txn.generate_frame(0); + txn.generate_frame(); self.wrench.api.send_transaction(self.wrench.document_id, txn); @@ -1250,7 +1257,7 @@ impl<'a> RawtestHarness<'a> { // 3. set a different scene - builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let mut txn = Transaction::new(); txn.set_display_list( @@ -1274,7 +1281,7 @@ impl<'a> RawtestHarness<'a> { // 6. rebuild the scene and compare again let mut txn = Transaction::new(); txn.set_root_pipeline(captured.root_pipeline_id.unwrap()); - txn.generate_frame(0); + txn.generate_frame(); self.wrench.api.send_transaction(captured.document_id, txn); let pixels2 = self.render_and_get_pixels(window_rect); self.compare_pixels(pixels0, pixels2, window_rect.size); @@ -1285,9 +1292,9 @@ impl<'a> RawtestHarness<'a> { let layout_size = LayoutSize::new(120.0, 0.0); let window_size = DeviceIntSize::new(layout_size.width as i32, layout_size.height as i32); - let doc_id = self.wrench.api.add_document(window_size); + let doc_id = self.wrench.api.add_document(window_size, 1); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let info = self.make_common_properties(LayoutRect::new(LayoutPoint::zero(), LayoutSize::new(100.0, 100.0))); builder.push_rect( @@ -1305,12 +1312,12 @@ impl<'a> RawtestHarness<'a> { builder.finalize(), false, ); - txn.generate_frame(0); + txn.generate_frame(); self.wrench.api.send_transaction(doc_id, txn); // Ensure we get a notification from rendering the above, even though // there are zero visible pixels - assert!(self.rx.recv().unwrap() == NotifierEvent::WakeUp { composite_needed: true }); + assert!(self.rx.recv().unwrap() == NotifierEvent::WakeUp); } @@ -1318,24 +1325,20 @@ impl<'a> RawtestHarness<'a> { println!("\thit testing test..."); let layout_size = LayoutSize::new(400., 400.); - let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); // Add a rectangle that covers the entire scene. - let info = self.make_common_properties(LayoutRect::new(LayoutPoint::zero(), layout_size)); - builder.push_hit_test( - &info, - (0, 1), - ); + let mut info = self.make_common_properties(LayoutRect::new(LayoutPoint::zero(), layout_size)); + info.hit_info = Some((0, 1)); + builder.push_rect(&info, info.clip_rect, ColorF::new(1.0, 1.0, 1.0, 1.0)); // Add a simple 100x100 rectangle at 100,0. - let info = self.make_common_properties(LayoutRect::new( + let mut info = self.make_common_properties(LayoutRect::new( LayoutPoint::new(100., 0.), LayoutSize::new(100., 100.) )); - builder.push_hit_test( - &info, - (0, 2), - ); + info.hit_info = Some((0, 2)); + builder.push_rect(&info, info.clip_rect, ColorF::new(1.0, 1.0, 1.0, 1.0)); let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id); @@ -1353,14 +1356,16 @@ impl<'a> RawtestHarness<'a> { &space_and_clip, make_rounded_complex_clip(&rect, 20.), ); - builder.push_hit_test( + builder.push_rect( &CommonItemProperties { + hit_info: Some((0, 4)), clip_rect: rect, clip_id: temp_clip_id, spatial_id: space_and_clip.spatial_id, flags: PrimitiveFlags::default(), }, - (0, 4), + rect, + ColorF::new(1.0, 1.0, 1.0, 1.0), ); // Add a rectangle that is clipped by a ClipChain containing a rounded rect. @@ -1370,14 +1375,16 @@ impl<'a> RawtestHarness<'a> { make_rounded_complex_clip(&rect, 20.), ); let clip_chain_id = builder.define_clip_chain(None, vec![clip_id]); - builder.push_hit_test( + builder.push_rect( &CommonItemProperties { + hit_info: Some((0, 5)), clip_rect: rect, clip_id: ClipId::ClipChain(clip_chain_id), spatial_id: space_and_clip.spatial_id, flags: PrimitiveFlags::default(), }, - (0, 5), + rect, + ColorF::new(1.0, 1.0, 1.0, 1.0), ); let mut epoch = Epoch(0); @@ -1393,7 +1400,7 @@ impl<'a> RawtestHarness<'a> { self.wrench.document_id, None, point, - HitTetFlags::empty(), + HitTestFlags::FIND_ALL, ) }; @@ -1444,7 +1451,7 @@ impl<'a> RawtestHarness<'a> { self.wrench.api.send_message(ApiMsg::DebugCommand(DebugCommand::ClearCaches(ClearCache::all()))); let layout_size = LayoutSize::new(400., 400.); - let builder = DisplayListBuilder::new(self.wrench.root_pipeline_id); + let builder = DisplayListBuilder::new(self.wrench.root_pipeline_id, layout_size); let txn = Transaction::new(); let mut epoch = Epoch(0); diff --git a/third_party/webrender/wrench/src/reftest.rs b/third_party/webrender/wrench/src/reftest.rs index 77c20341b79..e05f194105a 100644 --- a/third_party/webrender/wrench/src/reftest.rs +++ b/third_party/webrender/wrench/src/reftest.rs @@ -19,7 +19,6 @@ use std::process::Command; use std::sync::mpsc::Receiver; use webrender::RenderResults; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; use crate::wrench::{Wrench, WrenchThing}; use crate::yaml_frame_reader::YamlFrameReader; @@ -77,6 +76,9 @@ enum ExtraCheck { DrawCalls(usize), AlphaTargets(usize), ColorTargets(usize), + /// Checks the dirty region when rendering the test at |index| in the + /// sequence, and compares its serialization to |region|. + DirtyRegion { index: usize, region: String }, } impl ExtraCheck { @@ -88,6 +90,9 @@ impl ExtraCheck { x == results.last().unwrap().stats.alpha_target_count, ExtraCheck::ColorTargets(x) => x == results.last().unwrap().stats.color_target_count, + ExtraCheck::DirtyRegion { index, ref region } => { + *region == format!("{}", results[index].recorded_dirty_regions[0]) + } } } } @@ -367,6 +372,7 @@ impl ReftestManifest { let mut disable_dual_source_blending = false; let mut zoom_factor = 1.0; let mut allow_mipmaps = false; + let mut dirty_region_index = 0; let mut force_subpixel_aa_where_possible = None; let mut parse_command = |token: &str| -> bool { @@ -429,6 +435,15 @@ impl ReftestManifest { let (_, args, _) = parse_function(function); extra_checks.push(ExtraCheck::ColorTargets(args[0].parse().unwrap())); } + function if function.starts_with("dirty(") => { + let (_, args, _) = parse_function(function); + let region: String = args[0].parse().unwrap(); + extra_checks.push(ExtraCheck::DirtyRegion { + index: dirty_region_index, + region, + }); + dirty_region_index += 1; + } options if options.starts_with("options(") => { let (_, args, _) = parse_function(options); if args.iter().any(|arg| arg == &OPTION_DISABLE_SUBPX) { @@ -497,7 +512,7 @@ impl ReftestManifest { let op = match op { Some(op) => op, None => { - assert!(paths.is_empty(), "paths = {:?}", paths); + assert!(paths.is_empty(), format!("paths = {:?}", paths)); continue; } }; @@ -509,14 +524,6 @@ impl ReftestManifest { let reference = paths.pop().unwrap(); let test = paths; - if environment.platform == "android" { - // Add some fuzz on mobile as we do for non-wrench reftests. - // First remove the ranges with difference <= 2, otherwise they might cause the - // test to fail before the new range is picked up. - fuzziness.retain(|fuzzy| fuzzy.max_difference > 2); - fuzziness.push(RefTestFuzzy { max_difference: 2, num_differences: std::usize::MAX }); - } - // to avoid changing the meaning of existing tests, the case of // only a single (or no) 'fuzzy' keyword means we use the max // of that fuzzy and options.allow_.. (we don't want that to @@ -739,9 +746,7 @@ impl<'a> ReftestHarness<'a> { } fn run_reftest(&mut self, t: &Reftest) -> bool { - let test_name = t.to_string(); - println!("REFTEST {}", test_name); - profile_scope!("wrench reftest", text: &test_name); + println!("REFTEST {}", t); self.wrench .api @@ -838,14 +843,7 @@ impl<'a> ReftestHarness<'a> { } let reference = match reference_image { - Some(image) => { - let save_all_png = false; // flip to true to update all the tests! - if save_all_png { - let img = images.last().unwrap(); - save_flipped(&t.reference, img.data.clone(), img.size); - } - image - } + Some(image) => image, None => { let output = self.render_yaml( &t.reference, @@ -970,7 +968,7 @@ impl<'a> ReftestHarness<'a> { assert!( size.width <= window_size.width && size.height <= window_size.height, - "size={:?} ws={:?}", size, window_size + format!("size={:?} ws={:?}", size, window_size) ); // taking the bottom left sub-rectangle diff --git a/third_party/webrender/wrench/src/test_invalidation.rs b/third_party/webrender/wrench/src/test_invalidation.rs deleted file mode 100644 index badcfae8cf1..00000000000 --- a/third_party/webrender/wrench/src/test_invalidation.rs +++ /dev/null @@ -1,129 +0,0 @@ -/* 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 crate::NotifierEvent; -use crate::WindowWrapper; -use std::path::PathBuf; -use std::sync::mpsc::Receiver; -use crate::wrench::{Wrench, WrenchThing}; -use crate::yaml_frame_reader::YamlFrameReader; -use webrender::{PictureCacheDebugInfo, TileDebugInfo}; -use webrender::api::units::*; - -pub struct TestHarness<'a> { - wrench: &'a mut Wrench, - window: &'a mut WindowWrapper, - rx: Receiver<NotifierEvent>, -} - -struct RenderResult { - pc_debug: PictureCacheDebugInfo, - composite_needed: bool, -} - -// Convenience method to build a picture rect -fn pr(x: f32, y: f32, w: f32, h: f32) -> PictureRect { - PictureRect::new( - PicturePoint::new(x, y), - PictureSize::new(w, h), - ) -} - -impl<'a> TestHarness<'a> { - pub fn new( - wrench: &'a mut Wrench, - window: &'a mut WindowWrapper, - rx: Receiver<NotifierEvent> - ) -> Self { - TestHarness { - wrench, - window, - rx, - } - } - - /// Main entry point for invalidation tests - pub fn run( - mut self, - ) { - // List all invalidation tests here - self.test_basic(); - self.test_composite_nop(); - } - - /// Simple validation / proof of concept of invalidation testing - fn test_basic( - &mut self, - ) { - // Render basic.yaml, ensure that the valid/dirty rects are as expected - let results = self.render_yaml("basic"); - let tile_info = results.pc_debug.slice(0).tile(0, 0).as_dirty(); - assert_eq!( - tile_info.local_valid_rect, - pr(100.0, 100.0, 500.0, 100.0), - ); - assert_eq!( - tile_info.local_dirty_rect, - pr(100.0, 100.0, 500.0, 100.0), - ); - - // Render it again and ensure the tile was considered valid (no rasterization was done) - let results = self.render_yaml("basic"); - assert_eq!(*results.pc_debug.slice(0).tile(0, 0), TileDebugInfo::Valid); - } - - /// Ensure WR detects composites are needed for position changes within a single tile. - fn test_composite_nop( - &mut self, - ) { - // Render composite_nop_1.yaml, ensure that the valid/dirty rects are as expected - let results = self.render_yaml("composite_nop_1"); - let tile_info = results.pc_debug.slice(0).tile(0, 0).as_dirty(); - assert_eq!( - tile_info.local_valid_rect, - pr(100.0, 100.0, 100.0, 100.0), - ); - assert_eq!( - tile_info.local_dirty_rect, - pr(100.0, 100.0, 100.0, 100.0), - ); - - // Render composite_nop_2.yaml, ensure that the valid/dirty rects are as expected - let results = self.render_yaml("composite_nop_2"); - let tile_info = results.pc_debug.slice(0).tile(0, 0).as_dirty(); - assert_eq!( - tile_info.local_valid_rect, - pr(100.0, 120.0, 100.0, 100.0), - ); - assert_eq!( - tile_info.local_dirty_rect, - pr(100.0, 120.0, 100.0, 100.0), - ); - - // Main part of this test - ensure WR detects a composite is required in this case - assert!(results.composite_needed); - } - - /// Render a YAML file, and return the picture cache debug info - fn render_yaml( - &mut self, - filename: &str, - ) -> RenderResult { - let path = format!("invalidation/{}.yaml", filename); - let mut reader = YamlFrameReader::new(&PathBuf::from(path)); - - reader.do_frame(self.wrench); - let composite_needed = match self.rx.recv().unwrap() { - NotifierEvent::WakeUp { composite_needed } => composite_needed, - NotifierEvent::ShutDown => unreachable!(), - }; - let results = self.wrench.render(); - self.window.swap_buffers(); - - RenderResult { - pc_debug: results.picture_cache_debug, - composite_needed, - } - } -} diff --git a/third_party/webrender/wrench/src/wrench.rs b/third_party/webrender/wrench/src/wrench.rs index 9ae57c4af07..bce7ef6e476 100644 --- a/third_party/webrender/wrench/src/wrench.rs +++ b/third_party/webrender/wrench/src/wrench.rs @@ -17,7 +17,6 @@ use std::sync::mpsc::Receiver; use time; use webrender; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; use webrender::{DebugFlags, RenderResults, ShaderPrecacheFlags}; use crate::{WindowWrapper, NotifierEvent}; @@ -96,7 +95,7 @@ impl RenderNotifier for Notifier { Box::new(Notifier(self.0.clone())) } - fn wake_up(&self, _composite_needed: bool) { + fn wake_up(&self) { self.update(false); } @@ -230,6 +229,7 @@ impl Wrench { size: DeviceIntSize, do_rebuild: bool, no_subpixel_aa: bool, + no_picture_caching: bool, verbose: bool, no_scissor: bool, no_batch: bool, @@ -263,14 +263,12 @@ impl Wrench { precache_flags, blob_image_handler: Some(Box::new(blob::CheckerboardRenderer::new(callbacks.clone()))), chase_primitive, + enable_picture_caching: !no_picture_caching, testing: true, - max_internal_texture_size: Some(8196), // Needed for rawtest::test_resize_image. + max_texture_size: Some(8196), // Needed for rawtest::test_resize_image. allow_dual_source_blending: !disable_dual_source_blending, - allow_advanced_blend_equation: window.is_software(), + allow_advanced_blend_equation: true, dump_shader_source, - // SWGL doesn't support the GL_ALWAYS depth comparison function used by - // `clear_caches_with_quads`, but scissored clears work well. - clear_caches_with_quads: !window.is_software(), ..Default::default() }; @@ -291,10 +289,11 @@ impl Wrench { notifier, opts, None, + size, ).unwrap(); let api = sender.create_api(); - let document_id = api.add_document(size); + let document_id = api.add_document(size, 0); let graphics_api = renderer.get_graphics_api_info(); let zoom_factor = ZoomFactor::new(zoom_factor); @@ -586,7 +585,7 @@ impl Wrench { pub fn send_lists( &mut self, frame_number: u32, - display_lists: Vec<(PipelineId, BuiltDisplayList)>, + display_lists: Vec<(PipelineId, LayoutSize, BuiltDisplayList)>, scroll_offsets: &HashMap<ExternalScrollId, LayoutPoint>, ) { let root_background_color = Some(ColorF::new(1.0, 1.0, 1.0, 1.0)); @@ -606,7 +605,7 @@ impl Wrench { txn.scroll_node_with_id(*offset, *id, ScrollClamping::NoClamping); } - txn.generate_frame(0); + txn.generate_frame(); self.api.send_transaction(self.document_id, txn); } @@ -620,14 +619,14 @@ impl Wrench { self.renderer.update(); let _ = self.renderer.flush_pipeline_info(); self.renderer - .render(self.window_size, 0) + .render(self.window_size) .expect("errors encountered during render!") } pub fn refresh(&mut self) { self.begin_frame(); let mut txn = Transaction::new(); - txn.generate_frame(0); + txn.generate_frame(); self.api.send_transaction(self.document_id, txn); } diff --git a/third_party/webrender/wrench/src/yaml_frame_reader.rs b/third_party/webrender/wrench/src/yaml_frame_reader.rs index 492f155b667..e3bf04ed42e 100644 --- a/third_party/webrender/wrench/src/yaml_frame_reader.rs +++ b/third_party/webrender/wrench/src/yaml_frame_reader.rs @@ -15,9 +15,7 @@ use std::io::Read; use std::path::{Path, PathBuf}; use std::usize; use webrender::api::*; -use webrender::render_api::*; use webrender::api::units::*; -use webrender::api::FillRule; use crate::wrench::{FontDescriptor, Wrench, WrenchThing}; use crate::yaml_helper::{StringEnum, YamlHelper, make_perspective}; use yaml_rust::{Yaml, YamlLoader}; @@ -106,7 +104,7 @@ impl LocalExternalImageHandler { pub fn add_image(&mut self, device: &webrender::Device, desc: ImageDescriptor, - target: ImageBufferKind, + target: TextureTarget, image_data: ImageData, ) -> ImageData { let (image_id, channel_idx) = match image_data { @@ -236,13 +234,8 @@ fn generate_checkerboard_image( } } - let flags = match kind { - CheckerboardKind::BlackGrey => ImageDescriptorFlags::IS_OPAQUE, - CheckerboardKind::BlackTransparent => ImageDescriptorFlags::empty(), - }; - ( - ImageDescriptor::new(width as i32, height as i32, ImageFormat::BGRA8, flags), + ImageDescriptor::new(width as i32, height as i32, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE), ImageData::new(pixels), ) } @@ -329,7 +322,7 @@ pub struct YamlFrameReader { aux_dir: PathBuf, frame_count: u32, - display_lists: Vec<(PipelineId, BuiltDisplayList)>, + display_lists: Vec<(PipelineId, LayoutSize, BuiltDisplayList)>, include_only: Vec<String>, @@ -339,7 +332,6 @@ pub struct YamlFrameReader { /// A HashMap of offsets which specify what scroll offsets particular /// scroll layers should be initialized with. scroll_offsets: HashMap<ExternalScrollId, LayoutPoint>, - next_external_scroll_id: u64, image_map: HashMap<(PathBuf, Option<i64>), (ImageKey, LayoutSize)>, @@ -390,7 +382,6 @@ impl YamlFrameReader { built_frame: usize::MAX, keyframes: None, external_image_handler: Some(Box::new(LocalExternalImageHandler::new())), - next_external_scroll_id: 1000, // arbitrary to easily see in logs which are implicit } } @@ -485,12 +476,14 @@ impl YamlFrameReader { self.spatial_id_stack.clear(); self.spatial_id_stack.push(SpatialId::root_scroll_node(pipeline_id)); - let mut builder = DisplayListBuilder::new(pipeline_id); + let content_size = self.get_root_size_from_yaml(wrench, yaml); + let mut builder = DisplayListBuilder::new(pipeline_id, content_size); let mut info = CommonItemProperties { clip_rect: LayoutRect::zero(), clip_id: ClipId::invalid(), spatial_id: SpatialId::new(0, PipelineId::dummy()), flags: PrimitiveFlags::default(), + hit_info: None, }; self.add_stacking_context_from_yaml(&mut builder, wrench, yaml, true, &mut info); self.display_lists.push(builder.finalize()); @@ -762,12 +755,13 @@ impl YamlFrameReader { // ensure it gets created as such let external_target = match item["external-target"].as_str() { Some(ref s) => match &s[..] { - "2d" => ImageBufferKind::Texture2D, - "rect" => ImageBufferKind::TextureRect, + "2d" => TextureTarget::Default, + "array" => TextureTarget::Array, + "rect" => TextureTarget::Rect, _ => panic!("Unsupported external texture target."), } None => { - ImageBufferKind::Texture2D + TextureTarget::Default } }; @@ -1004,12 +998,7 @@ impl YamlFrameReader { &info.clip_rect ); - if let Some(tag) = self.to_hit_testing_tag(&item["hit-testing-tag"]) { - dl.push_hit_test( - &info, - tag, - ); - } + dl.push_hit_test(&info); } fn handle_line( @@ -1754,6 +1743,7 @@ impl YamlFrameReader { clip_rect, clip_id: space_and_clip.clip_id, spatial_id: space_and_clip.spatial_id, + hit_info: self.to_hit_testing_tag(&item["hit-testing-tag"]), flags, }; @@ -1812,12 +1802,11 @@ impl YamlFrameReader { let numeric_id = yaml["id"].as_i64().map(|id| id as u64); - let external_id = ExternalScrollId(self.next_external_scroll_id, dl.pipeline_id); - self.next_external_scroll_id += 1; - - if let Some(size) = yaml["scroll-offset"].as_point() { - self.scroll_offsets.insert(external_id, LayoutPoint::new(size.x, size.y)); - } + let external_id = yaml["scroll-offset"].as_point().map(|size| { + let id = ExternalScrollId((self.scroll_offsets.len() + 1) as u64, dl.pipeline_id); + self.scroll_offsets.insert(id, LayoutPoint::new(size.x, size.y)); + id + }); let space_and_clip = dl.define_scroll_frame( &self.top_space_and_clip(), @@ -1983,8 +1972,6 @@ impl YamlFrameReader { space_and_clip.clip_id = dl.define_clip_image_mask( &space_and_clip, image_mask, - &vec![], - FillRule::Nonzero, ); } @@ -2007,6 +1994,13 @@ impl YamlFrameReader { } } + fn get_root_size_from_yaml(&mut self, wrench: &mut Wrench, yaml: &Yaml) -> LayoutSize { + yaml["bounds"] + .as_rect() + .map(|rect| rect.size) + .unwrap_or(wrench.window_size_f32()) + } + fn push_reference_frame( &mut self, dl: &mut DisplayListBuilder, @@ -2041,10 +2035,7 @@ impl YamlFrameReader { let reference_frame_kind = if !yaml["perspective"].is_badvalue() { ReferenceFrameKind::Perspective { scrolling_relative_to: None } } else { - ReferenceFrameKind::Transform { - is_2d_scale_translation: false, - should_snap: false, - } + ReferenceFrameKind::Transform }; let transform = yaml["transform"] @@ -2201,8 +2192,7 @@ impl WrenchThing for YamlFrameReader { // If YAML isn't read yet, or watching source file, reload from disk. if self.yaml_string.is_empty() || self.watch_source { - let mut file = File::open(&self.yaml_path) - .unwrap_or_else(|_| panic!("YAML '{:?}' doesn't exist", self.yaml_path)); + let mut file = File::open(&self.yaml_path).unwrap(); self.yaml_string.clear(); file.read_to_string(&mut self.yaml_string).unwrap(); should_build_yaml = true; diff --git a/third_party/webrender/wrench/src/yaml_helper.rs b/third_party/webrender/wrench/src/yaml_helper.rs index f9974efdc75..d82f863e9cc 100644 --- a/third_party/webrender/wrench/src/yaml_helper.rs +++ b/third_party/webrender/wrench/src/yaml_helper.rs @@ -52,8 +52,6 @@ fn string_to_color(color: &str) -> Option<ColorF> { "white" => Some(ColorF::new(1.0, 1.0, 1.0, 1.0)), "black" => Some(ColorF::new(0.0, 0.0, 0.0, 1.0)), "yellow" => Some(ColorF::new(1.0, 1.0, 0.0, 1.0)), - "cyan" => Some(ColorF::new(0.0, 1.0, 1.0, 1.0)), - "magenta" => Some(ColorF::new(1.0, 0.0, 1.0, 1.0)), "transparent" => Some(ColorF::new(1.0, 1.0, 1.0, 0.0)), s => { let items: Vec<f32> = s.split_whitespace() @@ -563,8 +561,8 @@ impl YamlHelper for Yaml { ("component-transfer", _, _) => { Some(FilterOp::ComponentTransfer) } - ("blur", ref args, _) if args.len() == 2 => { - Some(FilterOp::Blur(args[0].parse().unwrap(), args[1].parse().unwrap())) + ("blur", ref args, _) if args.len() == 1 => { + Some(FilterOp::Blur(args[0].parse().unwrap())) } ("brightness", ref args, _) if args.len() == 1 => { Some(FilterOp::Brightness(args[0].parse().unwrap())) @@ -719,8 +717,7 @@ impl YamlHelper for Yaml { "blur" => { FilterPrimitiveKind::Blur(BlurPrimitive { input: self["in"].as_filter_input().unwrap(), - width: self["width"].as_f32().unwrap(), - height: self["height"].as_f32().unwrap(), + radius: self["radius"].as_f32().unwrap(), }) } "opacity" => { |