Merge pull request #168 from dfrg/fragments

New scene/resource API
This commit is contained in:
Chad Brokaw 2022-05-11 15:45:12 -04:00 committed by GitHub
commit f5a721d92b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 3057 additions and 122 deletions

399
Cargo.lock generated
View file

@ -29,9 +29,9 @@ dependencies = [
[[package]]
name = "ansi_term"
version = "0.11.0"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
@ -48,7 +48,7 @@ version = "0.33.3+1.2.191"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4f1d82f164f838ae413296d1131aa6fa79b917d25bebaa7033d25620c09219"
dependencies = [
"libloading 0.7.1",
"libloading 0.7.3",
]
[[package]]
@ -58,7 +58,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12f91ce4c6be1a2ba99d3d6cd57d5bae9ac6d6f903b5ae53d6b1dee2edf872af"
dependencies = [
"ash",
"raw-window-handle",
"raw-window-handle 0.3.4",
"raw-window-metal",
]
@ -75,9 +75,9 @@ dependencies = [
[[package]]
name = "autocfg"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
@ -93,18 +93,18 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "bytemuck"
version = "1.7.2"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72957246c41db82b8ef88a5486143830adeb8227ef9837740bdec67724cf2c5b"
checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.0.1"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54"
checksum = "562e382481975bc61d11275ac5e62a19abd00b0547d99516a415336f183dcd0e"
dependencies = [
"proc-macro2",
"quote",
@ -128,10 +128,29 @@ dependencies = [
]
[[package]]
name = "cc"
version = "1.0.72"
name = "cbindgen"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
checksum = "51e3973b165dc0f435831a9e426de67e894de532754ff7a3f307c03ee5dec7dc"
dependencies = [
"clap",
"heck",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn",
"tempfile",
"toml",
]
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
@ -147,9 +166,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
@ -169,7 +188,7 @@ dependencies = [
"bitflags",
"block",
"cocoa-foundation",
"core-foundation 0.9.2",
"core-foundation 0.9.3",
"core-graphics 0.22.3",
"foreign-types",
"libc",
@ -184,7 +203,7 @@ checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
dependencies = [
"bitflags",
"block",
"core-foundation 0.9.2",
"core-foundation 0.9.3",
"core-graphics-types",
"foreign-types",
"libc",
@ -203,9 +222,9 @@ dependencies = [
[[package]]
name = "core-foundation"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
dependencies = [
"core-foundation-sys 0.8.3",
"libc",
@ -242,7 +261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
dependencies = [
"bitflags",
"core-foundation 0.9.2",
"core-foundation 0.9.3",
"core-graphics-types",
"foreign-types",
"libc",
@ -255,7 +274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
dependencies = [
"bitflags",
"core-foundation 0.9.2",
"core-foundation 0.9.3",
"foreign-types",
"libc",
]
@ -275,9 +294,9 @@ dependencies = [
[[package]]
name = "crc32fast"
version = "1.2.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if 1.0.0",
]
@ -298,9 +317,9 @@ dependencies = [
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@ -319,10 +338,11 @@ dependencies = [
[[package]]
name = "crossbeam-epoch"
version = "0.9.5"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"crossbeam-utils",
"lazy_static",
@ -332,9 +352,9 @@ dependencies = [
[[package]]
name = "crossbeam-queue"
version = "0.3.2"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9"
checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
@ -342,14 +362,20 @@ dependencies = [
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
"cfg-if 1.0.0",
"lazy_static",
]
[[package]]
name = "cty"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "darling"
version = "0.10.2"
@ -395,31 +421,20 @@ dependencies = [
"byteorder",
]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dirs"
version = "3.0.2"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
@ -447,7 +462,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
dependencies = [
"libloading 0.7.1",
"libloading 0.7.3",
]
[[package]]
@ -456,6 +471,15 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "fastrand"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
"instant",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -490,9 +514,9 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.3"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [
"cfg-if 1.0.0",
"libc",
@ -505,6 +529,21 @@ version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -520,6 +559,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -529,6 +578,12 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "itoa"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "jni-sys"
version = "0.3.0"
@ -552,9 +607,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.107"
version = "0.2.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
[[package]]
name = "libloading"
@ -568,9 +623,9 @@ dependencies = [
[[package]]
name = "libloading"
version = "0.7.1"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
dependencies = [
"cfg-if 1.0.0",
"winapi",
@ -578,18 +633,19 @@ dependencies = [
[[package]]
name = "lock_api"
version = "0.4.5"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.14"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
dependencies = [
"cfg-if 1.0.0",
]
@ -611,9 +667,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "memchr"
version = "2.4.1"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memmap2"
@ -626,9 +682,9 @@ dependencies = [
[[package]]
name = "memoffset"
version = "0.6.4"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
@ -677,9 +733,9 @@ dependencies = [
[[package]]
name = "mio-misc"
version = "1.2.1"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ddf05411bb159cdb5801bb10002afb66cb4572be656044315e363460ce69dc2"
checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c"
dependencies = [
"crossbeam",
"crossbeam-queue",
@ -696,6 +752,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "moscato"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8372f6cdc8b2c431750a9c4edbc8d9c511ef1a68472aaa02500493414a407c64"
dependencies = [
"pinot",
]
[[package]]
name = "ndk"
version = "0.3.0"
@ -737,9 +802,9 @@ dependencies = [
[[package]]
name = "ndk-sys"
version = "0.2.1"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d"
checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121"
[[package]]
name = "nix"
@ -767,41 +832,39 @@ dependencies = [
[[package]]
name = "nom"
version = "7.1.0"
version = "7.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
dependencies = [
"memchr",
"minimal-lexical",
"version_check",
]
[[package]]
name = "ntapi"
version = "0.3.6"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
dependencies = [
"winapi",
]
[[package]]
name = "num_enum"
version = "0.5.4"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f"
checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9"
dependencies = [
"derivative",
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.4"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9"
checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce"
dependencies = [
"proc-macro-crate 1.1.0",
"proc-macro-crate 1.1.3",
"proc-macro2",
"quote",
"syn",
@ -828,9 +891,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.8.0"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "owned_ttf_parser"
@ -872,6 +935,19 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pgpu-render"
version = "0.1.0"
dependencies = [
"cbindgen",
"cocoa",
"metal",
"objc",
"piet-gpu",
"piet-gpu-hal",
"piet-scene",
]
[[package]]
name = "piet"
version = "0.2.0"
@ -896,7 +972,7 @@ dependencies = [
"piet-gpu-types",
"png",
"rand",
"raw-window-handle",
"raw-window-handle 0.3.4",
"roxmltree",
"swash",
"winit",
@ -924,7 +1000,7 @@ dependencies = [
"foreign-types",
"metal",
"objc",
"raw-window-handle",
"raw-window-handle 0.3.4",
"smallvec",
"winapi",
"wio",
@ -951,10 +1027,26 @@ dependencies = [
]
[[package]]
name = "pkg-config"
version = "0.3.22"
name = "piet-scene"
version = "0.1.0"
dependencies = [
"bytemuck",
"moscato",
"pinot",
"smallvec",
]
[[package]]
name = "pinot"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f"
checksum = "6ba3013ff85036c414a4a3cf826135db204de2bd80d684728550e7130421809a"
[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]]
name = "png"
@ -970,9 +1062,9 @@ dependencies = [
[[package]]
name = "ppv-lite86"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro-crate"
@ -985,9 +1077,9 @@ dependencies = [
[[package]]
name = "proc-macro-crate"
version = "1.1.0"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
dependencies = [
"thiserror",
"toml",
@ -995,18 +1087,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
@ -1054,11 +1146,21 @@ dependencies = [
[[package]]
name = "raw-window-handle"
version = "0.3.3"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211"
checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76"
dependencies = [
"libc",
"raw-window-handle 0.4.3",
]
[[package]]
name = "raw-window-handle"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41"
dependencies = [
"cty",
]
[[package]]
@ -1070,26 +1172,36 @@ dependencies = [
"cocoa",
"core-graphics 0.22.3",
"objc",
"raw-window-handle",
"raw-window-handle 0.3.4",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.0"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.3",
"getrandom 0.2.6",
"redox_syscall",
"thiserror",
]
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
@ -1111,6 +1223,12 @@ dependencies = [
"owned_ttf_parser",
]
[[package]]
name = "ryu"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "same-file"
version = "1.0.6"
@ -1134,15 +1252,40 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.130"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.137"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.7.0"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "smithay-client-toolkit"
@ -1187,15 +1330,29 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.81"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if 1.0.0",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
@ -1207,18 +1364,18 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.30"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
"proc-macro2",
"quote",
@ -1227,9 +1384,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.5.8"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
dependencies = [
"serde",
]
@ -1291,6 +1448,12 @@ dependencies = [
"unic-common",
]
[[package]]
name = "unicode-segmentation"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
[[package]]
name = "unicode-width"
version = "0.1.9"
@ -1309,12 +1472,6 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "walkdir"
version = "2.3.2"
@ -1450,7 +1607,7 @@ checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8"
dependencies = [
"bitflags",
"cocoa",
"core-foundation 0.9.2",
"core-foundation 0.9.3",
"core-graphics 0.22.3",
"core-video-sys",
"dispatch",
@ -1466,7 +1623,7 @@ dependencies = [
"objc",
"parking_lot",
"percent-encoding",
"raw-window-handle",
"raw-window-handle 0.3.4",
"scopeguard",
"smithay-client-toolkit",
"wayland-client",
@ -1505,9 +1662,9 @@ dependencies = [
[[package]]
name = "xdg"
version = "2.4.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803"
checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6"
dependencies = [
"dirs",
]

View file

@ -1,9 +1,11 @@
[workspace]
members = [
"pgpu-render",
"piet-gpu",
"piet-gpu-derive",
"piet-gpu-hal",
"piet-gpu-types",
"piet-scene",
"tests"
]

21
pgpu-render/Cargo.toml Normal file
View file

@ -0,0 +1,21 @@
[package]
name = "pgpu-render"
version = "0.1.0"
description = "C interface for glyph rendering using piet-gpu."
license = "MIT/Apache-2.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
piet-gpu = { path = "../piet-gpu" }
piet-gpu-hal = { path = "../piet-gpu-hal" }
piet-scene = { path = "../piet-scene" }
metal = "0.22"
cocoa = "0.24.0"
objc = "0.2.5"
[build-dependencies]
cbindgen = "0.20.0"

29
pgpu-render/build.rs Normal file
View file

@ -0,0 +1,29 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
extern crate cbindgen;
use std::env;
fn main() {
let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_header("/** Automatically generated from pgpu-render/src/lib.rs with cbindgen. **/")
.generate()
.expect("Unable to generate bindings")
.write_to_file("pgpu.h");
}

142
pgpu-render/pgpu.h Normal file
View file

@ -0,0 +1,142 @@
/** Automatically generated from pgpu-render/src/lib.rs with cbindgen. **/
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <ostream>
#include <new>
/// Encoded (possibly color) outline for a glyph.
struct PgpuGlyph;
/// Context for loading and scaling glyphs.
struct PgpuGlyphContext;
/// Context for loading a scaling glyphs from a specific font.
struct PgpuGlyphProvider;
/// State and resources for rendering a scene.
struct PgpuRenderer;
/// Encoded streams and resources describing a vector graphics scene.
struct PgpuScene;
/// Builder for constructing an encoded scene.
struct PgpuSceneBuilder;
/// Tag and value for a font variation axis.
struct PgpuFontVariation {
/// Tag that specifies the axis.
uint32_t tag;
/// Requested setting for the axis.
float value;
};
/// Description of a font.
struct PgpuFontDesc {
/// Pointer to the context of the font file.
const uint8_t *data;
/// Size of the font file data in bytes.
uintptr_t data_len;
/// Index of the requested font in the font file.
uint32_t index;
/// Unique identifier for the font.
uint64_t unique_id;
/// Requested size in pixels per em unit. Set to 0.0 for
/// unscaled outlines.
float ppem;
/// Pointer to array of font variation settings.
const PgpuFontVariation *variations;
/// Number of font variation settings.
uintptr_t variations_len;
};
/// Rectangle defined by minimum and maximum points.
struct PgpuRect {
float x0;
float y0;
float x1;
float y1;
};
extern "C" {
/// Creates a new piet-gpu renderer for the specified Metal device and
/// command queue.
///
/// device: MTLDevice*
/// queue: MTLCommandQueue*
PgpuRenderer *pgpu_renderer_new(void *device, void *queue);
/// Renders a prepared scene into a texture target. Commands for rendering are
/// recorded into the specified command buffer. Returns an id representing
/// resources that may have been allocated during this process. After the
/// command buffer has been retired, call `pgpu_renderer_release` with this id
/// to drop any associated resources.
///
/// target: MTLTexture*
/// cmdbuf: MTLCommandBuffer*
uint32_t pgpu_renderer_render(PgpuRenderer *renderer,
const PgpuScene *scene,
void *target,
void *cmdbuf);
/// Releases the internal resources associated with the specified id from a
/// previous render operation.
void pgpu_renderer_release(PgpuRenderer *renderer, uint32_t id);
/// Destroys the piet-gpu renderer.
void pgpu_renderer_destroy(PgpuRenderer *renderer);
/// Creates a new, empty piet-gpu scene.
PgpuScene *pgpu_scene_new();
/// Destroys the piet-gpu scene.
void pgpu_scene_destroy(PgpuScene *scene);
/// Creates a new builder for filling a piet-gpu scene. The specified scene
/// should not be accessed while the builder is live.
PgpuSceneBuilder *pgpu_scene_builder_new(PgpuScene *scene);
/// Adds a glyph with the specified transform to the underlying scene.
void pgpu_scene_builder_add_glyph(PgpuSceneBuilder *builder,
const PgpuGlyph *glyph,
const float (*transform)[6]);
/// Finalizes the scene builder, making the underlying scene ready for
/// rendering. This takes ownership and consumes the builder.
void pgpu_scene_builder_finish(PgpuSceneBuilder *builder);
/// Creates a new context for loading glyph outlines.
PgpuGlyphContext *pgpu_glyph_context_new();
/// Destroys the glyph context.
void pgpu_glyph_context_destroy(PgpuGlyphContext *gcx);
/// Creates a new glyph provider for the specified glyph context and font
/// descriptor. May return nullptr if the font data is invalid. Only one glyph
/// provider may be live for a glyph context.
PgpuGlyphProvider *pgpu_glyph_provider_new(PgpuGlyphContext *gcx, const PgpuFontDesc *font);
/// Returns an encoded outline for the specified glyph provider and glyph id.
/// May return nullptr if the requested glyph is not available.
PgpuGlyph *pgpu_glyph_provider_get(PgpuGlyphProvider *provider, uint16_t gid);
/// Returns an encoded color outline for the specified glyph provider, color
/// palette index and glyph id. May return nullptr if the requested glyph is
/// not available.
PgpuGlyph *pgpu_glyph_provider_get_color(PgpuGlyphProvider *provider,
uint16_t palette_index,
uint16_t gid);
/// Destroys the glyph provider.
void pgpu_glyph_provider_destroy(PgpuGlyphProvider *provider);
/// Computes the bounding box for the glyph after applying the specified
/// transform.
PgpuRect pgpu_glyph_bbox(const PgpuGlyph *glyph, const float (*transform)[6]);
/// Destroys the glyph.
void pgpu_glyph_destroy(PgpuGlyph *glyph);
} // extern "C"

233
pgpu-render/src/lib.rs Normal file
View file

@ -0,0 +1,233 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
mod render;
use render::*;
use std::ffi::c_void;
use std::mem::transmute;
/// Creates a new piet-gpu renderer for the specified Metal device and
/// command queue.
///
/// device: MTLDevice*
/// queue: MTLCommandQueue*
#[no_mangle]
pub unsafe extern "C" fn pgpu_renderer_new(
device: *mut c_void,
queue: *mut c_void,
) -> *mut PgpuRenderer {
let device: &metal::DeviceRef = transmute(device);
let queue: &metal::CommandQueueRef = transmute(queue);
Box::into_raw(Box::new(PgpuRenderer::new(device, queue)))
}
/// Renders a prepared scene into a texture target. Commands for rendering are
/// recorded into the specified command buffer. Returns an id representing
/// resources that may have been allocated during this process. After the
/// command buffer has been retired, call `pgpu_renderer_release` with this id
/// to drop any associated resources.
///
/// target: MTLTexture*
/// cmdbuf: MTLCommandBuffer*
#[no_mangle]
pub unsafe extern "C" fn pgpu_renderer_render(
renderer: *mut PgpuRenderer,
scene: *const PgpuScene,
target: *mut c_void,
cmdbuf: *mut c_void,
) -> u32 {
let cmdbuf: &metal::CommandBufferRef = transmute(cmdbuf);
let target: &metal::TextureRef = transmute(target);
(*renderer).render(&*scene, cmdbuf, target)
}
/// Releases the internal resources associated with the specified id from a
/// previous render operation.
#[no_mangle]
pub unsafe extern "C" fn pgpu_renderer_release(renderer: *mut PgpuRenderer, id: u32) {
(*renderer).release(id);
}
/// Destroys the piet-gpu renderer.
#[no_mangle]
pub unsafe extern "C" fn pgpu_renderer_destroy(renderer: *mut PgpuRenderer) {
Box::from_raw(renderer);
}
/// Creates a new, empty piet-gpu scene.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_new() -> *mut PgpuScene {
Box::into_raw(Box::new(PgpuScene::new()))
}
/// Destroys the piet-gpu scene.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_destroy(scene: *mut PgpuScene) {
Box::from_raw(scene);
}
/// Creates a new builder for filling a piet-gpu scene. The specified scene
/// should not be accessed while the builder is live.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_builder_new(
scene: *mut PgpuScene,
) -> *mut PgpuSceneBuilder<'static> {
Box::into_raw(Box::new((*scene).build()))
}
/// Adds a glyph with the specified transform to the underlying scene.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_builder_add_glyph(
builder: *mut PgpuSceneBuilder<'static>,
glyph: *const PgpuGlyph,
transform: &[f32; 6],
) {
let transform = piet_scene::geometry::Affine::new(transform);
(*builder).add_glyph(&*glyph, &transform);
}
/// Finalizes the scene builder, making the underlying scene ready for
/// rendering. This takes ownership and consumes the builder.
#[no_mangle]
pub unsafe extern "C" fn pgpu_scene_builder_finish(builder: *mut PgpuSceneBuilder<'static>) {
let builder = Box::from_raw(builder);
builder.finish();
}
/// Creates a new context for loading glyph outlines.
#[no_mangle]
pub unsafe extern "C" fn pgpu_glyph_context_new() -> *mut PgpuGlyphContext {
Box::into_raw(Box::new(PgpuGlyphContext::new()))
}
/// Destroys the glyph context.
#[no_mangle]
pub unsafe extern "C" fn pgpu_glyph_context_destroy(gcx: *mut PgpuGlyphContext) {
Box::from_raw(gcx);
}
/// Description of a font.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct PgpuFontDesc {
/// Pointer to the context of the font file.
data: *const u8,
/// Size of the font file data in bytes.
data_len: usize,
/// Index of the requested font in the font file.
index: u32,
/// Unique identifier for the font.
unique_id: u64,
/// Requested size in pixels per em unit. Set to 0.0 for
/// unscaled outlines.
ppem: f32,
/// Pointer to array of font variation settings.
variations: *const PgpuFontVariation,
/// Number of font variation settings.
variations_len: usize,
}
/// Creates a new glyph provider for the specified glyph context and font
/// descriptor. May return nullptr if the font data is invalid. Only one glyph
/// provider may be live for a glyph context.
#[no_mangle]
pub unsafe extern "C" fn pgpu_glyph_provider_new(
gcx: *mut PgpuGlyphContext,
font: *const PgpuFontDesc,
) -> *mut PgpuGlyphProvider<'static> {
let font = &*font;
let font_data = std::slice::from_raw_parts(font.data, font.data_len);
let variations = std::slice::from_raw_parts(font.variations, font.variations_len);
if let Some(provider) = (*gcx).new_provider(
font_data,
font.index,
font.unique_id,
font.ppem,
false,
variations,
) {
Box::into_raw(Box::new(provider))
} else {
std::ptr::null_mut()
}
}
/// Returns an encoded outline for the specified glyph provider and glyph id.
/// May return nullptr if the requested glyph is not available.
#[no_mangle]
pub unsafe extern "C" fn pgpu_glyph_provider_get(
provider: *mut PgpuGlyphProvider,
gid: u16,
) -> *mut PgpuGlyph {
if let Some(glyph) = (*provider).get(gid) {
Box::into_raw(Box::new(glyph))
} else {
std::ptr::null_mut()
}
}
/// Returns an encoded color outline for the specified glyph provider, color
/// palette index and glyph id. May return nullptr if the requested glyph is
/// not available.
#[no_mangle]
pub unsafe extern "C" fn pgpu_glyph_provider_get_color(
provider: *mut PgpuGlyphProvider,
palette_index: u16,
gid: u16,
) -> *mut PgpuGlyph {
if let Some(glyph) = (*provider).get_color(palette_index, gid) {
Box::into_raw(Box::new(glyph))
} else {
std::ptr::null_mut()
}
}
/// Destroys the glyph provider.
#[no_mangle]
pub unsafe extern "C" fn pgpu_glyph_provider_destroy(provider: *mut PgpuGlyphProvider) {
Box::from_raw(provider);
}
/// Rectangle defined by minimum and maximum points.
#[derive(Copy, Clone, Default)]
#[repr(C)]
pub struct PgpuRect {
pub x0: f32,
pub y0: f32,
pub x1: f32,
pub y1: f32,
}
/// Computes the bounding box for the glyph after applying the specified
/// transform.
#[no_mangle]
pub unsafe extern "C" fn pgpu_glyph_bbox(glyph: *const PgpuGlyph, transform: &[f32; 6]) -> PgpuRect {
let transform = piet_scene::geometry::Affine::new(transform);
let rect = (*glyph).bbox(Some(transform));
PgpuRect {
x0: rect.min.x,
y0: rect.min.y,
x1: rect.max.x,
y1: rect.max.y,
}
}
/// Destroys the glyph.
#[no_mangle]
pub unsafe extern "C" fn pgpu_glyph_destroy(glyph: *mut PgpuGlyph) {
Box::from_raw(glyph);
}

222
pgpu-render/src/render.rs Normal file
View file

@ -0,0 +1,222 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
use piet_gpu::{EncodedSceneRef, PixelFormat, RenderConfig};
use piet_gpu_hal::{QueryPool, Session};
use piet_scene::glyph::pinot::{types::Tag, FontDataRef};
use piet_scene::geometry::{Affine, Rect};
use piet_scene::glyph::{GlyphContext, GlyphProvider};
use piet_scene::resource::ResourceContext;
use piet_scene::scene::{Fragment, Scene};
/// State and resources for rendering a scene.
pub struct PgpuRenderer {
session: Session,
pgpu_renderer: Option<piet_gpu::Renderer>,
query_pool: QueryPool,
width: u32,
height: u32,
is_color: bool,
}
impl PgpuRenderer {
pub fn new(device: &metal::DeviceRef, queue: &metal::CommandQueueRef) -> Self {
let piet_device = piet_gpu_hal::Device::new_from_raw_mtl(device, &queue);
let session = Session::new(piet_device);
let query_pool = session.create_query_pool(12).unwrap();
Self {
session,
pgpu_renderer: None,
query_pool,
width: 0,
height: 0,
is_color: false,
}
}
pub fn render(
&mut self,
scene: &PgpuScene,
cmdbuf: &metal::CommandBufferRef,
target: &metal::TextureRef,
) -> u32 {
let is_color = target.pixel_format() != metal::MTLPixelFormat::A8Unorm;
let width = target.width() as u32;
let height = target.height() as u32;
if self.pgpu_renderer.is_none()
|| self.width != width
|| self.height != height
|| self.is_color != is_color
{
self.width = width;
self.height = height;
self.is_color = is_color;
let format = if is_color {
PixelFormat::Rgba8
} else {
PixelFormat::A8
};
let config = RenderConfig::new(width as usize, height as usize).pixel_format(format);
unsafe {
self.pgpu_renderer =
piet_gpu::Renderer::new_from_config(&self.session, config, 1).ok();
}
}
unsafe {
let mut cmd_buf = self.session.cmd_buf_from_raw_mtl(cmdbuf);
let dst_image = self
.session
.image_from_raw_mtl(target, self.width, self.height);
if let Some(renderer) = &mut self.pgpu_renderer {
renderer.upload_scene(&scene.encoded_scene(), 0).unwrap();
renderer.record(&mut cmd_buf, &self.query_pool, 0);
// TODO later: we can bind the destination image and avoid the copy.
cmd_buf.blit_image(&renderer.image_dev, &dst_image);
}
}
0
}
pub fn release(&mut self, _id: u32) {
// TODO: worry about freeing resources / managing overlapping submits
}
}
/// Encoded streams and resources describing a vector graphics scene.
pub struct PgpuScene {
scene: Scene,
rcx: ResourceContext,
}
impl PgpuScene {
pub fn new() -> Self {
Self {
scene: Scene::default(),
rcx: ResourceContext::new(),
}
}
pub fn build(&mut self) -> PgpuSceneBuilder {
self.rcx.advance();
PgpuSceneBuilder(piet_scene::scene::build_scene(
&mut self.scene,
&mut self.rcx,
))
}
fn encoded_scene<'a>(&'a self) -> EncodedSceneRef<'a, piet_scene::geometry::Affine> {
let d = self.scene.data();
EncodedSceneRef {
transform_stream: &d.transform_stream,
tag_stream: &d.tag_stream,
pathseg_stream: &d.pathseg_stream,
linewidth_stream: &d.linewidth_stream,
drawtag_stream: &d.drawtag_stream,
drawdata_stream: &d.drawdata_stream,
n_path: d.n_path,
n_pathseg: d.n_pathseg,
n_clip: d.n_clip,
ramp_data: self.rcx.ramp_data(),
}
}
}
/// Builder for constructing an encoded scene.
pub struct PgpuSceneBuilder<'a>(piet_scene::scene::Builder<'a>);
impl<'a> PgpuSceneBuilder<'a> {
pub fn add_glyph(&mut self, glyph: &PgpuGlyph, transform: &piet_scene::geometry::Affine) {
self.0.push_transform(*transform);
self.0.append(&glyph.fragment);
self.0.pop_transform();
}
pub fn finish(self) {
self.0.finish();
}
}
/// Tag and value for a font variation axis.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct PgpuFontVariation {
/// Tag that specifies the axis.
pub tag: u32,
/// Requested setting for the axis.
pub value: f32,
}
/// Context for loading and scaling glyphs.
pub struct PgpuGlyphContext(GlyphContext);
impl PgpuGlyphContext {
pub fn new() -> Self {
Self(GlyphContext::new())
}
pub fn new_provider<'a>(
&'a mut self,
font_data: &'a [u8],
font_index: u32,
font_id: u64,
ppem: f32,
hint: bool,
variations: &[PgpuFontVariation],
) -> Option<PgpuGlyphProvider> {
let font = FontDataRef::new(font_data).and_then(|f| f.get(font_index))?;
Some(PgpuGlyphProvider(
self.0.new_provider(
&font,
Some(font_id),
ppem,
hint,
variations
.iter()
.map(|variation| (Tag(variation.tag), variation.value)),
),
))
}
}
/// Context for loading a scaling glyphs from a specific font.
pub struct PgpuGlyphProvider<'a>(GlyphProvider<'a>);
impl<'a> PgpuGlyphProvider<'a> {
pub fn get(&mut self, gid: u16) -> Option<PgpuGlyph> {
let fragment = self.0.get(gid)?;
Some(PgpuGlyph { fragment })
}
pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option<PgpuGlyph> {
let fragment = self.0.get_color(palette_index, gid)?;
Some(PgpuGlyph { fragment })
}
}
/// Encoded (possibly color) outline for a glyph.
pub struct PgpuGlyph {
fragment: Fragment,
}
impl PgpuGlyph {
pub fn bbox(&self, transform: Option<Affine>) -> Rect {
if let Some(transform) = &transform {
Rect::from_points(self.fragment.points().iter().map(|p| p.transform(transform)))
} else {
Rect::from_points(self.fragment.points())
}
}
}

View file

@ -350,7 +350,7 @@ impl crate::backend::Device for MtlDevice {
//desc.set_mipmap_level_count(1);
let mtl_format = match format {
ImageFormat::A8 => metal::MTLPixelFormat::R8Unorm,
ImageFormat::Rgba8 => metal::MTLPixelFormat::RGBA8Unorm,
ImageFormat::Rgba8 => metal::MTLPixelFormat::BGRA8Unorm,
};
desc.set_pixel_format(mtl_format);
desc.set_usage(metal::MTLTextureUsage::ShaderRead | metal::MTLTextureUsage::ShaderWrite);

View file

@ -37,6 +37,147 @@ pub struct Encoder {
n_clip: u32,
}
#[derive(Copy, Clone, Debug)]
pub struct EncodedSceneRef<'a, T: Copy + Pod> {
pub transform_stream: &'a [T],
pub tag_stream: &'a [u8],
pub pathseg_stream: &'a [u8],
pub linewidth_stream: &'a [f32],
pub drawtag_stream: &'a [u32],
pub drawdata_stream: &'a [u8],
pub n_path: u32,
pub n_pathseg: u32,
pub n_clip: u32,
pub ramp_data: &'a [u32],
}
impl<'a, T: Copy + Pod> EncodedSceneRef<'a, T> {
/// Return a config for the element processing pipeline.
///
/// This does not include further pipeline processing. Also returns the
/// beginning of free memory.
pub fn stage_config(&self) -> (Config, usize) {
// Layout of scene buffer
let drawtag_offset = 0;
let n_drawobj = self.n_drawobj();
let n_drawobj_padded = align_up(n_drawobj, DRAW_PART_SIZE as usize);
let drawdata_offset = drawtag_offset + n_drawobj_padded * DRAWTAG_SIZE;
let trans_offset = drawdata_offset + self.drawdata_stream.len();
let n_trans = self.transform_stream.len();
let n_trans_padded = align_up(n_trans, TRANSFORM_PART_SIZE as usize);
let linewidth_offset = trans_offset + n_trans_padded * TRANSFORM_SIZE;
let n_linewidth = self.linewidth_stream.len();
let pathtag_offset = linewidth_offset + n_linewidth * LINEWIDTH_SIZE;
let n_pathtag = self.tag_stream.len();
let n_pathtag_padded = align_up(n_pathtag, PATHSEG_PART_SIZE as usize);
let pathseg_offset = pathtag_offset + n_pathtag_padded;
// Layout of memory
let mut alloc = 0;
let trans_alloc = alloc;
alloc += trans_alloc + n_trans_padded * TRANSFORM_SIZE;
let pathseg_alloc = alloc;
alloc += pathseg_alloc + self.n_pathseg as usize * PATHSEG_SIZE;
let path_bbox_alloc = alloc;
let n_path = self.n_path as usize;
alloc += path_bbox_alloc + n_path * PATH_BBOX_SIZE;
let drawmonoid_alloc = alloc;
alloc += n_drawobj_padded * DRAWMONOID_SIZE;
let anno_alloc = alloc;
alloc += n_drawobj * ANNOTATED_SIZE;
let clip_alloc = alloc;
let n_clip = self.n_clip as usize;
const CLIP_SIZE: usize = 4;
alloc += n_clip * CLIP_SIZE;
let clip_bic_alloc = alloc;
const CLIP_BIC_SIZE: usize = 8;
// This can round down, as we only reduce the prefix
alloc += (n_clip / CLIP_PART_SIZE as usize) * CLIP_BIC_SIZE;
let clip_stack_alloc = alloc;
const CLIP_EL_SIZE: usize = 20;
alloc += n_clip * CLIP_EL_SIZE;
let clip_bbox_alloc = alloc;
const CLIP_BBOX_SIZE: usize = 16;
alloc += align_up(n_clip as usize, CLIP_PART_SIZE as usize) * CLIP_BBOX_SIZE;
let draw_bbox_alloc = alloc;
alloc += n_drawobj * DRAW_BBOX_SIZE;
let drawinfo_alloc = alloc;
// TODO: not optimized; it can be accumulated during encoding or summed from drawtags
const MAX_DRAWINFO_SIZE: usize = 44;
alloc += n_drawobj * MAX_DRAWINFO_SIZE;
let config = Config {
n_elements: n_drawobj as u32,
n_pathseg: self.n_pathseg,
pathseg_alloc: pathseg_alloc as u32,
anno_alloc: anno_alloc as u32,
trans_alloc: trans_alloc as u32,
path_bbox_alloc: path_bbox_alloc as u32,
drawmonoid_alloc: drawmonoid_alloc as u32,
clip_alloc: clip_alloc as u32,
clip_bic_alloc: clip_bic_alloc as u32,
clip_stack_alloc: clip_stack_alloc as u32,
clip_bbox_alloc: clip_bbox_alloc as u32,
draw_bbox_alloc: draw_bbox_alloc as u32,
drawinfo_alloc: drawinfo_alloc as u32,
n_trans: n_trans as u32,
n_path: self.n_path,
n_clip: self.n_clip,
trans_offset: trans_offset as u32,
linewidth_offset: linewidth_offset as u32,
pathtag_offset: pathtag_offset as u32,
pathseg_offset: pathseg_offset as u32,
drawtag_offset: drawtag_offset as u32,
drawdata_offset: drawdata_offset as u32,
..Default::default()
};
(config, alloc)
}
pub fn write_scene(&self, buf: &mut BufWrite) {
buf.extend_slice(&self.drawtag_stream);
let n_drawobj = self.drawtag_stream.len();
buf.fill_zero(padding(n_drawobj, DRAW_PART_SIZE as usize) * DRAWTAG_SIZE);
buf.extend_slice(&self.drawdata_stream);
buf.extend_slice(&self.transform_stream);
let n_trans = self.transform_stream.len();
buf.fill_zero(padding(n_trans, TRANSFORM_PART_SIZE as usize) * TRANSFORM_SIZE);
buf.extend_slice(&self.linewidth_stream);
buf.extend_slice(&self.tag_stream);
let n_pathtag = self.tag_stream.len();
buf.fill_zero(padding(n_pathtag, PATHSEG_PART_SIZE as usize));
buf.extend_slice(&self.pathseg_stream);
}
/// The number of draw objects in the draw object stream.
pub(crate) fn n_drawobj(&self) -> usize {
self.drawtag_stream.len()
}
/// The number of paths.
pub(crate) fn n_path(&self) -> u32 {
self.n_path
}
/// The number of path segments.
pub(crate) fn n_pathseg(&self) -> u32 {
self.n_pathseg
}
pub(crate) fn n_transform(&self) -> usize {
self.transform_stream.len()
}
/// The number of tags in the path stream.
pub(crate) fn n_pathtag(&self) -> usize {
self.tag_stream.len()
}
pub(crate) fn n_clip(&self) -> u32 {
self.n_clip
}
}
/// A scene fragment encoding a glyph.
///
/// This is a reduced version of the full encoder.

View file

@ -8,9 +8,11 @@ pub mod stages;
pub mod test_scenes;
mod text;
use bytemuck::Pod;
use std::convert::TryInto;
pub use blend::{Blend, BlendMode, CompositionMode};
pub use encoder::EncodedSceneRef;
pub use render_ctx::PietGpuRenderContext;
pub use gradient::Colrv1RadialGradient;
@ -406,6 +408,61 @@ impl Renderer {
Ok(())
}
pub fn upload_scene<T: Copy + Pod>(
&mut self,
scene: &EncodedSceneRef<T>,
buf_ix: usize,
) -> Result<(), Error> {
let (mut config, mut alloc) = scene.stage_config();
let n_drawobj = scene.n_drawobj();
// TODO: be more consistent in size types
let n_path = scene.n_path() as usize;
self.n_paths = n_path;
self.n_transform = scene.n_transform();
self.n_drawobj = scene.n_drawobj();
self.n_pathseg = scene.n_pathseg() as usize;
self.n_pathtag = scene.n_pathtag();
self.n_clip = scene.n_clip();
// These constants depend on encoding and may need to be updated.
// Perhaps we can plumb these from piet-gpu-derive?
const PATH_SIZE: usize = 12;
const BIN_SIZE: usize = 8;
let width_in_tiles = self.width / TILE_W;
let height_in_tiles = self.height / TILE_H;
let tile_base = alloc;
alloc += ((n_path + 3) & !3) * PATH_SIZE;
let bin_base = alloc;
alloc += ((n_drawobj + 255) & !255) * BIN_SIZE;
let ptcl_base = alloc;
alloc += width_in_tiles * height_in_tiles * PTCL_INITIAL_ALLOC;
config.width_in_tiles = width_in_tiles as u32;
config.height_in_tiles = height_in_tiles as u32;
config.tile_alloc = tile_base as u32;
config.bin_alloc = bin_base as u32;
config.ptcl_alloc = ptcl_base as u32;
unsafe {
// TODO: reallocate scene buffer if size is inadequate
{
let mut mapped_scene = self.scene_bufs[buf_ix].map_write(..)?;
scene.write_scene(&mut mapped_scene);
}
self.config_bufs[buf_ix].write(&[config])?;
self.memory_buf_host[buf_ix].write(&[alloc as u32, 0 /* Overflow flag */])?;
// Upload gradient data.
if !scene.ramp_data.is_empty() {
assert!(
self.gradient_bufs[buf_ix].size() as usize
>= std::mem::size_of_val(&*scene.ramp_data)
);
self.gradient_bufs[buf_ix].write(scene.ramp_data)?;
}
}
Ok(())
}
pub unsafe fn record(&self, cmd_buf: &mut CmdBuf, query_pool: &QueryPool, buf_ix: usize) {
cmd_buf.copy_buffer(&self.config_bufs[buf_ix], &self.config_buf);
cmd_buf.copy_buffer(&self.memory_buf_host[buf_ix], &self.memory_buf_dev);

11
piet-scene/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "piet-scene"
version = "0.1.0"
license = "MIT/Apache-2.0"
edition = "2021"
[dependencies]
bytemuck = { version = "1.7.2", features = ["derive"] }
smallvec = "1.8.0"
pinot = "0.1.5"
moscato = "0.1.2"

View file

@ -0,0 +1,41 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Color {
pub const fn rgb8(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b, a: 255 }
}
pub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
pub fn to_premul_u32(self) -> u32 {
let a = self.a as f64 * (1.0 / 255.0);
let r = (self.r as f64 * a).round() as u32;
let g = (self.g as f64 * a).round() as u32;
let b = (self.b as f64 * a).round() as u32;
(r << 24) | (g << 16) | (b << 8) | self.a as u32
}
}

View file

@ -0,0 +1,78 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
use super::color::Color;
use crate::geometry::Point;
use smallvec::SmallVec;
use std::hash::{Hash, Hasher};
#[derive(Copy, Clone, PartialOrd, Default, Debug)]
pub struct Stop {
pub offset: f32,
pub color: Color,
}
impl Hash for Stop {
fn hash<H: Hasher>(&self, state: &mut H) {
self.offset.to_bits().hash(state);
self.color.hash(state);
}
}
// Override PartialEq to use to_bits for the offset to match with the Hash impl
impl std::cmp::PartialEq for Stop {
fn eq(&self, other: &Self) -> bool {
self.offset.to_bits() == other.offset.to_bits() && self.color == other.color
}
}
impl std::cmp::Eq for Stop {}
pub type StopVec = SmallVec<[Stop; 4]>;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Extend {
Pad,
Repeat,
Reflect,
}
#[derive(Clone, Debug)]
pub struct LinearGradient {
pub start: Point,
pub end: Point,
pub stops: StopVec,
pub extend: Extend,
}
#[derive(Clone, Debug)]
pub struct RadialGradient {
pub center0: Point,
pub radius0: f32,
pub center1: Point,
pub radius1: f32,
pub stops: StopVec,
pub extend: Extend,
}
#[derive(Clone, Debug)]
pub struct SweepGradient {
pub center: Point,
pub start_angle: f32,
pub end_angle: f32,
pub stops: StopVec,
pub extend: Extend,
}

View file

@ -0,0 +1,96 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
use std::result::Result;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Format {
A8,
Rgba8,
}
impl Format {
pub fn data_size(self, width: u32, height: u32) -> Option<usize> {
(width as usize)
.checked_mul(height as usize)
.and_then(|size| {
size.checked_mul(match self {
Self::A8 => 1,
Self::Rgba8 => 4,
})
})
}
}
#[derive(Clone, Debug)]
pub struct Image(Arc<Inner>);
#[derive(Clone, Debug)]
struct Inner {
id: u64,
format: Format,
width: u32,
height: u32,
data: Vec<u8>,
}
impl Image {
pub fn new(
format: Format,
width: u32,
height: u32,
mut data: Vec<u8>,
) -> Result<Self, DataSizeError> {
let data_size = format.data_size(width, height).ok_or(DataSizeError)?;
if data.len() < data_size {
return Err(DataSizeError);
}
data.truncate(data_size);
static ID: AtomicU64 = AtomicU64::new(1);
Ok(Self(Arc::new(Inner {
id: ID.fetch_add(1, Ordering::Relaxed),
format,
width,
height,
data,
})))
}
pub fn id(&self) -> u64 {
self.0.id
}
pub fn format(&self) -> Format {
self.0.format
}
pub fn width(&self) -> u32 {
self.0.width
}
pub fn height(&self) -> u32 {
self.0.height
}
pub fn data(&self) -> &[u8] {
&self.0.data
}
}
#[derive(Clone, Debug)]
pub struct DataSizeError;

View file

@ -0,0 +1,35 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
mod color;
mod gradient;
mod image;
pub use color::Color;
pub use gradient::*;
pub use image::*;
use crate::resource::PersistentBrush;
#[derive(Clone, Debug)]
pub enum Brush {
Solid(Color),
LinearGradient(LinearGradient),
RadialGradient(RadialGradient),
SweepGradient(SweepGradient),
Image(Image),
Persistent(PersistentBrush),
}

227
piet-scene/src/geometry.rs Normal file
View file

@ -0,0 +1,227 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
// This module is based in part on kurbo (https://github.com/linebender/kurbo)
use bytemuck::{Pod, Zeroable};
use core::borrow::Borrow;
use core::hash::{Hash, Hasher};
/// Two dimensional point.
#[derive(Copy, Clone, PartialEq, PartialOrd, Default, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct Point {
pub x: f32,
pub y: f32,
}
impl Hash for Point {
fn hash<H: Hasher>(&self, state: &mut H) {
self.x.to_bits().hash(state);
self.y.to_bits().hash(state);
}
}
impl Point {
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
pub fn transform(&self, affine: &Affine) -> Self {
Self {
x: self.x * affine.xx + self.y * affine.yx + affine.dx,
y: self.y * affine.yy + self.y * affine.xy + affine.dy,
}
}
}
impl From<[f32; 2]> for Point {
fn from(value: [f32; 2]) -> Self {
Self::new(value[0], value[1])
}
}
impl From<(f32, f32)> for Point {
fn from(value: (f32, f32)) -> Self {
Self::new(value.0, value.1)
}
}
/// Affine transformation matrix.
#[derive(Copy, Clone, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct Affine {
pub xx: f32,
pub yx: f32,
pub xy: f32,
pub yy: f32,
pub dx: f32,
pub dy: f32,
}
impl Affine {
pub const IDENTITY: Self = Self {
xx: 1.0,
yx: 0.0,
xy: 0.0,
yy: 1.0,
dx: 0.0,
dy: 0.0,
};
pub const fn new(elements: &[f32; 6]) -> Self {
Self {
xx: elements[0],
yx: elements[1],
xy: elements[2],
yy: elements[3],
dx: elements[4],
dy: elements[5],
}
}
/// Creates a new affine transform representing the specified scale along the
/// x and y axes.
pub fn scale(x: f32, y: f32) -> Self {
Self::new(&[x, 0., 0., y, 0., 0.])
}
/// Creates a new affine transform representing the specified translation.
pub fn translate(x: f32, y: f32) -> Self {
Self::new(&[1., 0., 0., 1., x, y])
}
/// Creates a new affine transform representing a counter-clockwise
/// rotation for the specified angle in radians.
pub fn rotate(th: f32) -> Self {
let (s, c) = th.sin_cos();
Self::new(&[c, s, -s, c, 0., 0.])
}
/// Creates a new skew transform
pub fn skew(x: f32, y: f32) -> Self {
Self::new(&[1., x.tan(), y.tan(), 1., 0., 0.])
}
pub fn around_center(&self, x: f32, y: f32) -> Self {
Self::translate(x, y) * *self * Self::translate(-x, -y)
}
/// Transforms the specified point.
pub fn transform_point(&self, point: Point) -> Point {
Point {
x: point.x * self.xx + point.y * self.yx + self.dx,
y: point.y * self.yy + point.y * self.xy + self.dy,
}
}
/// Compute the determinant of this transform.
pub fn determinant(self) -> f32 {
self.xx * self.yy - self.yx * self.xy
}
/// Compute the inverse transform.
///
/// Produces NaN values when the determinant is zero.
pub fn inverse(self) -> Self {
let inv_det = self.determinant().recip();
Self::new(&[
inv_det * self.yy,
-inv_det * self.yx,
-inv_det * self.xy,
inv_det * self.xx,
inv_det * (self.xy * self.dy - self.yy * self.dx),
inv_det * (self.yx * self.dx - self.xx * self.dy),
])
}
}
impl Default for Affine {
fn default() -> Self {
Self::IDENTITY
}
}
impl std::ops::Mul for Affine {
type Output = Self;
fn mul(self, other: Self) -> Self {
Self::new(&[
self.xx * other.xx + self.xy * other.yx,
self.yx * other.xx + self.yy * other.yx,
self.xx * other.xy + self.xy * other.yy,
self.yx * other.xy + self.yy * other.yy,
self.xx * other.dx + self.xy * other.dy + self.dx,
self.yx * other.dx + self.yy * other.dy + self.dy,
])
}
}
/// Axis-aligned rectangle represented as minimum and maximum points.
#[derive(Copy, Clone, Default, Debug, Pod, Zeroable)]
#[repr(C)]
pub struct Rect {
pub min: Point,
pub max: Point,
}
impl Rect {
/// Creates a new rectangle that encloses the specified collection of
/// points.
pub fn from_points<I>(points: I) -> Self
where
I: IntoIterator,
I::Item: Borrow<Point>,
{
let mut rect = Self {
min: Point::new(f32::MAX, f32::MAX),
max: Point::new(f32::MIN, f32::MIN),
};
let mut count = 0;
for point in points {
rect.add(*point.borrow());
count += 1;
}
if count != 0 {
rect
} else {
Self::default()
}
}
/// Returns the width of the rectangle.
pub fn width(&self) -> f32 {
self.max.x - self.min.x
}
/// Returns the height of the rectangle.
pub fn height(&self) -> f32 {
self.max.y - self.min.y
}
/// Extends the rectangle to include the specified point.
pub fn add(&mut self, point: Point) {
self.min.x = self.min.x.min(point.x);
self.min.y = self.min.y.min(point.y);
self.max.x = self.max.x.max(point.x);
self.max.y = self.max.y.max(point.y);
}
/// Returns a new rectangle that encloses the minimum and maximum points
/// of this rectangle after applying the specified transform to each.
pub fn transform(&self, affine: &Affine) -> Self {
Self::from_points([self.min.transform(affine), self.max.transform(affine)])
}
}

322
piet-scene/src/glyph/mod.rs Normal file
View file

@ -0,0 +1,322 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
pub use pinot;
use crate::brush::{Brush, Color};
use crate::geometry::Affine;
use crate::path::Element;
use crate::scene::{build_fragment, Fill, Fragment};
use moscato::{Context, Scaler};
use pinot::{types::Tag, FontRef};
use smallvec::SmallVec;
/// General context for creating scene fragments for glyph outlines.
pub struct GlyphContext {
ctx: Context,
}
impl GlyphContext {
/// Creates a new context.
pub fn new() -> Self {
Self {
ctx: Context::new(),
}
}
/// Creates a new provider for generating scene fragments for glyphs from
/// the specified font and settings.
pub fn new_provider<'a, V>(
&'a mut self,
font: &FontRef<'a>,
font_id: Option<u64>,
ppem: f32,
hint: bool,
variations: V,
) -> GlyphProvider<'a>
where
V: IntoIterator,
V::Item: Into<(Tag, f32)>,
{
let scaler = if let Some(font_id) = font_id {
self.ctx
.new_scaler_with_id(font, font_id)
.size(ppem)
.hint(hint)
.variations(variations)
.build()
} else {
self.ctx
.new_scaler(font)
.size(ppem)
.hint(hint)
.variations(variations)
.build()
};
GlyphProvider { scaler }
}
}
/// Generator for scene fragments containing glyph outlines for a specific
/// font.
pub struct GlyphProvider<'a> {
scaler: Scaler<'a>,
}
impl<'a> GlyphProvider<'a> {
/// Returns a scene fragment containing the commands to render the
/// specified glyph.
pub fn get(&mut self, gid: u16) -> Option<Fragment> {
let glyph = self.scaler.glyph(gid)?;
let path = glyph.path(0)?;
let mut fragment = Fragment::default();
let mut builder = build_fragment(&mut fragment);
builder.fill(
Fill::NonZero,
&Brush::Solid(Color::rgb8(255, 255, 255)),
None,
convert_path(path.elements()),
);
Some(fragment)
}
/// Returns a scene fragment containing the commands and resources to
/// render the specified color glyph.
pub fn get_color(&mut self, palette_index: u16, gid: u16) -> Option<Fragment> {
use crate::geometry::*;
use moscato::Command;
let glyph = self.scaler.color_glyph(palette_index, gid)?;
let mut fragment = Fragment::default();
let mut builder = build_fragment(&mut fragment);
let mut xform_stack: SmallVec<[Affine; 8]> = SmallVec::new();
for command in glyph.commands() {
match command {
Command::PushTransform(xform) => {
let xform = if let Some(parent) = xform_stack.last() {
convert_transform(xform) * *parent
} else {
convert_transform(xform)
};
xform_stack.push(xform);
}
Command::PopTransform => { xform_stack.pop(); },
Command::PushClip(path_index) => {
let path = glyph.path(*path_index)?;
if let Some(xform) = xform_stack.last() {
builder.push_layer(
Default::default(),
convert_transformed_path(path.elements(), xform),
);
} else {
builder.push_layer(Default::default(), convert_path(path.elements()));
}
}
Command::PopClip => builder.pop_layer(),
Command::PushLayer(bounds) => {
let mut rect = Rect {
min: Point::new(bounds.min.x, bounds.min.y),
max: Point::new(bounds.max.x, bounds.max.y),
};
if let Some(xform) = xform_stack.last() {
rect = rect.transform(xform);
}
builder.push_layer(Default::default(), rect.elements());
}
Command::PopLayer => builder.pop_layer(),
Command::BeginBlend(bounds, mode) => {
let mut rect = Rect {
min: Point::new(bounds.min.x, bounds.min.y),
max: Point::new(bounds.max.x, bounds.max.y),
};
if let Some(xform) = xform_stack.last() {
rect = rect.transform(xform);
}
builder.push_layer(convert_blend(*mode), rect.elements())
}
Command::EndBlend => builder.pop_layer(),
Command::SimpleFill(path_index, brush, brush_xform) => {
let path = glyph.path(*path_index)?;
let brush = convert_brush(brush);
let brush_xform = brush_xform.map(|xform| convert_transform(&xform));
if let Some(xform) = xform_stack.last() {
builder.fill(
Fill::NonZero,
&brush,
brush_xform.map(|x| x * *xform),
convert_transformed_path(path.elements(), xform),
);
} else {
builder.fill(
Fill::NonZero,
&brush,
brush_xform,
convert_path(path.elements()),
);
}
}
Command::Fill(brush, brush_xform) => {
// TODO: this needs to compute a bounding box for
// the parent clips
}
}
}
Some(fragment)
}
}
fn convert_path(
path: impl Iterator<Item = moscato::Element> + Clone,
) -> impl Iterator<Item = Element> + Clone {
use crate::geometry::Point;
path.map(|el| match el {
moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y)),
moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y)),
moscato::Element::QuadTo(p0, p1) => {
Element::QuadTo(Point::new(p0.x, p0.y), Point::new(p1.x, p1.y))
}
moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo(
Point::new(p0.x, p0.y),
Point::new(p1.x, p1.y),
Point::new(p2.x, p2.y),
),
moscato::Element::Close => Element::Close,
})
}
fn convert_transformed_path(
path: impl Iterator<Item = moscato::Element> + Clone,
xform: &Affine,
) -> impl Iterator<Item = Element> + Clone {
use crate::geometry::Point;
let xform = *xform;
path.map(move |el| match el {
moscato::Element::MoveTo(p0) => Element::MoveTo(Point::new(p0.x, p0.y).transform(&xform)),
moscato::Element::LineTo(p0) => Element::LineTo(Point::new(p0.x, p0.y).transform(&xform)),
moscato::Element::QuadTo(p0, p1) => Element::QuadTo(
Point::new(p0.x, p0.y).transform(&xform),
Point::new(p1.x, p1.y).transform(&xform),
),
moscato::Element::CurveTo(p0, p1, p2) => Element::CurveTo(
Point::new(p0.x, p0.y).transform(&xform),
Point::new(p1.x, p1.y).transform(&xform),
Point::new(p2.x, p2.y).transform(&xform),
),
moscato::Element::Close => Element::Close,
})
}
fn convert_blend(mode: moscato::CompositeMode) -> crate::scene::Blend {
use crate::scene::{Blend, Compose, Mix};
use moscato::CompositeMode;
let mut mix = Mix::Normal;
let mut compose = Compose::SrcOver;
match mode {
CompositeMode::Clear => compose = Compose::Clear,
CompositeMode::Src => compose = Compose::Copy,
CompositeMode::Dest => compose = Compose::Dest,
CompositeMode::SrcOver => {}
CompositeMode::DestOver => compose = Compose::DestOver,
CompositeMode::SrcIn => compose = Compose::SrcIn,
CompositeMode::DestIn => compose = Compose::DestIn,
CompositeMode::SrcOut => compose = Compose::SrcOut,
CompositeMode::DestOut => compose = Compose::DestOut,
CompositeMode::SrcAtop => compose = Compose::SrcAtop,
CompositeMode::DestAtop => compose = Compose::DestAtop,
CompositeMode::Xor => compose = Compose::Xor,
CompositeMode::Plus => compose = Compose::Plus,
CompositeMode::Screen => mix = Mix::Screen,
CompositeMode::Overlay => mix = Mix::Overlay,
CompositeMode::Darken => mix = Mix::Darken,
CompositeMode::Lighten => mix = Mix::Lighten,
CompositeMode::ColorDodge => mix = Mix::ColorDodge,
CompositeMode::ColorBurn => mix = Mix::ColorBurn,
CompositeMode::HardLight => mix = Mix::HardLight,
CompositeMode::SoftLight => mix = Mix::SoftLight,
CompositeMode::Difference => mix = Mix::Difference,
CompositeMode::Exclusion => mix = Mix::Exclusion,
CompositeMode::Multiply => mix = Mix::Multiply,
CompositeMode::HslHue => mix = Mix::Hue,
CompositeMode::HslSaturation => mix = Mix::Saturation,
CompositeMode::HslColor => mix = Mix::Color,
CompositeMode::HslLuminosity => mix = Mix::Luminosity,
}
Blend { mix, compose }
}
fn convert_transform(xform: &moscato::Transform) -> crate::geometry::Affine {
crate::geometry::Affine {
xx: xform.xx,
yx: xform.yx,
xy: xform.xy,
yy: xform.yy,
dx: xform.dx,
dy: xform.dy,
}
}
fn convert_brush(brush: &moscato::Brush) -> crate::brush::Brush {
use crate::brush::*;
use crate::geometry::*;
match brush {
moscato::Brush::Solid(color) => Brush::Solid(Color {
r: color.r,
g: color.g,
b: color.b,
a: color.a,
}),
moscato::Brush::LinearGradient(grad) => Brush::LinearGradient(LinearGradient {
start: Point::new(grad.start.x, grad.start.y),
end: Point::new(grad.end.x, grad.end.y),
stops: convert_stops(&grad.stops),
extend: convert_extend(grad.extend),
}),
moscato::Brush::RadialGradient(grad) => Brush::RadialGradient(RadialGradient {
center0: Point::new(grad.center0.x, grad.center0.y),
center1: Point::new(grad.center1.x, grad.center1.y),
radius0: grad.radius0,
radius1: grad.radius1,
stops: convert_stops(&grad.stops),
extend: convert_extend(grad.extend),
}),
}
}
fn convert_stops(stops: &[moscato::ColorStop]) -> crate::brush::StopVec {
use crate::brush::Stop;
stops
.iter()
.map(|stop| Stop {
offset: stop.offset,
color: Color {
r: stop.color.r,
g: stop.color.g,
b: stop.color.b,
a: stop.color.a,
},
})
.collect()
}
fn convert_extend(extend: moscato::ExtendMode) -> crate::brush::Extend {
use crate::brush::Extend::*;
match extend {
moscato::ExtendMode::Pad => Pad,
moscato::ExtendMode::Repeat => Repeat,
moscato::ExtendMode::Reflect => Reflect,
}
}

22
piet-scene/src/lib.rs Normal file
View file

@ -0,0 +1,22 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
pub mod brush;
pub mod geometry;
pub mod glyph;
pub mod path;
pub mod resource;
pub mod scene;

63
piet-scene/src/path.rs Normal file
View file

@ -0,0 +1,63 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
use super::geometry::{Point, Rect};
/// Action of a path element.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Verb {
MoveTo,
LineTo,
QuadTo,
CurveTo,
Close,
}
/// Element of a path represented by a verb and its associated points.
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Element {
MoveTo(Point),
LineTo(Point),
QuadTo(Point, Point),
CurveTo(Point, Point, Point),
Close,
}
impl Element {
/// Returns the verb that describes the action of the path element.
pub fn verb(&self) -> Verb {
match self {
Self::MoveTo(..) => Verb::MoveTo,
Self::LineTo(..) => Verb::LineTo,
Self::QuadTo(..) => Verb::QuadTo,
Self::CurveTo(..) => Verb::CurveTo,
Self::Close => Verb::Close,
}
}
}
impl Rect {
pub fn elements(&self) -> impl Iterator<Item = Element> + Clone {
let elements = [
Element::MoveTo((self.min.x, self.min.y).into()),
Element::LineTo((self.max.x, self.min.y).into()),
Element::LineTo((self.max.x, self.max.y).into()),
Element::LineTo((self.min.x, self.max.y).into()),
Element::Close,
];
(0..5).map(move |i| elements[i])
}
}

View file

@ -0,0 +1,138 @@
use crate::brush::{Color, Stop, StopVec};
use std::collections::HashMap;
const N_SAMPLES: usize = 512;
const RETAINED_COUNT: usize = 64;
#[derive(Default)]
pub struct RampCache {
epoch: u64,
map: HashMap<StopVec, (u32, u64)>,
data: Vec<u32>,
}
impl RampCache {
pub fn new() -> Self {
Self::default()
}
pub fn advance(&mut self) {
self.epoch += 1;
if self.map.len() > RETAINED_COUNT {
self.map
.retain(|_key, value| value.0 < RETAINED_COUNT as u32);
self.data.truncate(RETAINED_COUNT * N_SAMPLES);
}
}
pub fn clear(&mut self) {
self.epoch = 0;
self.map.clear();
self.data.clear();
}
pub fn add(&mut self, stops: &[Stop]) -> u32 {
if let Some(entry) = self.map.get_mut(stops) {
entry.1 = self.epoch;
entry.0
} else if self.map.len() < RETAINED_COUNT {
let id = (self.data.len() / N_SAMPLES) as u32;
self.data.extend(make_ramp(stops));
self.map.insert(stops.into(), (id, self.epoch));
id
} else {
let mut reuse = None;
for (stops, (id, epoch)) in &self.map {
if *epoch + 2 < self.epoch {
reuse = Some((stops.to_owned(), *id));
break;
}
}
if let Some((old_stops, id)) = reuse {
self.map.remove(&old_stops);
let start = id as usize * N_SAMPLES;
for (dst, src) in self.data[start..start + N_SAMPLES]
.iter_mut()
.zip(make_ramp(stops))
{
*dst = src;
}
self.map.insert(stops.into(), (id, self.epoch));
id
} else {
let id = (self.data.len() / N_SAMPLES) as u32;
self.data.extend(make_ramp(stops));
self.map.insert(stops.into(), (id, self.epoch));
id
}
}
}
pub fn data(&self) -> &[u32] {
&self.data
}
}
fn make_ramp<'a>(stops: &'a [Stop]) -> impl Iterator<Item = u32> + 'a {
let mut last_u = 0.0;
let mut last_c = ColorF64::from_color(stops[0].color);
let mut this_u = last_u;
let mut this_c = last_c;
let mut j = 0;
(0..N_SAMPLES).map(move |i| {
let u = (i as f64) / (N_SAMPLES - 1) as f64;
while u > this_u {
last_u = this_u;
last_c = this_c;
if let Some(s) = stops.get(j + 1) {
this_u = s.offset as f64;
this_c = ColorF64::from_color(s.color);
j += 1;
} else {
break;
}
}
let du = this_u - last_u;
let c = if du < 1e-9 {
this_c
} else {
last_c.lerp(&this_c, (u - last_u) / du)
};
c.to_premul_u32()
})
}
#[derive(Copy, Clone, Debug)]
struct ColorF64([f64; 4]);
impl ColorF64 {
fn from_color(color: Color) -> Self {
Self([
color.r as f64 / 255.0,
color.g as f64 / 255.0,
color.b as f64 / 255.0,
color.a as f64 / 255.0,
])
}
fn lerp(&self, other: &Self, a: f64) -> Self {
fn l(x: f64, y: f64, a: f64) -> f64 {
x * (1.0 - a) + y * a
}
Self([
l(self.0[0], other.0[0], a),
l(self.0[1], other.0[1], a),
l(self.0[2], other.0[2], a),
l(self.0[3], other.0[3], a),
])
}
fn to_premul_u32(&self) -> u32 {
let a = self.0[3].min(1.0).max(0.0);
let r = ((self.0[0] * a).min(1.0).max(0.0) * 255.0) as u32;
let g = ((self.0[1] * a).min(1.0).max(0.0) * 255.0) as u32;
let b = ((self.0[2] * a).min(1.0).max(0.0) * 255.0) as u32;
let a = (a * 255.0) as u32;
b | (g << 8) | (r << 16) | (a << 24)
}
}

View file

@ -0,0 +1,56 @@
mod gradient;
use crate::brush::{Brush, Stop};
use gradient::RampCache;
use std::collections::HashMap;
/// Context for caching resources across rendering operations.
#[derive(Default)]
pub struct ResourceContext {
ramps: RampCache,
persistent_map: HashMap<u64, PersistentBrushData>,
}
impl ResourceContext {
pub fn new() -> Self {
Self::default()
}
pub fn advance(&mut self) {
self.ramps.advance();
}
pub fn clear(&mut self) {
self.ramps.clear();
self.persistent_map.clear();
}
pub fn add_ramp(&mut self, stops: &[Stop]) -> u32 {
self.ramps.add(stops)
}
pub fn create_brush(&mut self, brush: &Brush) -> PersistentBrush {
match brush {
Brush::Persistent(dup) => return *dup,
_ => {}
}
PersistentBrush { kind: 0, id: 0 }
}
pub fn destroy_brush(&mut self, brush: PersistentBrush) {}
pub fn ramp_data(&self) -> &[u32] {
&self.ramps.data()
}
}
/// Handle for a brush that is managed by the resource context.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct PersistentBrush {
kind: u8,
id: u64,
}
struct PersistentBrushData {
brush: Brush,
}

View file

@ -0,0 +1,102 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
/// Defines the color mixing function for a blend operation.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(C)]
pub enum Mix {
Normal = 0,
Multiply = 1,
Screen = 2,
Overlay = 3,
Darken = 4,
Lighten = 5,
ColorDodge = 6,
ColorBurn = 7,
HardLight = 8,
SoftLight = 9,
Difference = 10,
Exclusion = 11,
Hue = 12,
Saturation = 13,
Color = 14,
Luminosity = 15,
}
/// Defines the layer composition function for a blend operation.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(C)]
pub enum Compose {
Clear = 0,
Copy = 1,
Dest = 2,
SrcOver = 3,
DestOver = 4,
SrcIn = 5,
DestIn = 6,
SrcOut = 7,
DestOut = 8,
SrcAtop = 9,
DestAtop = 10,
Xor = 11,
Plus = 12,
PlusDarker = 13,
PlusLighter = 14,
}
/// Blend mode consisting of mixing and composition functions.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Blend {
pub mix: Mix,
pub compose: Compose,
}
impl Blend {
pub fn new(mix: Mix, compose: Compose) -> Self {
Self { mix, compose }
}
pub fn pack(&self) -> u32 {
(self.mix as u32) << 8 | self.compose as u32
}
}
impl Default for Blend {
fn default() -> Self {
Self {
mix: Mix::Normal,
compose: Compose::SrcOver,
}
}
}
impl From<Mix> for Blend {
fn from(mix: Mix) -> Self {
Self {
mix,
compose: Compose::SrcOver,
}
}
}
impl From<Compose> for Blend {
fn from(compose: Compose) -> Self {
Self {
mix: Mix::Normal,
compose,
}
}
}

View file

@ -0,0 +1,554 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
use super::style::{Fill, Stroke};
use super::{Affine, Blend, Element, Fragment, FragmentResources, ResourcePatch, Scene, SceneData};
use crate::brush::*;
use crate::resource::ResourceContext;
use bytemuck::{Pod, Zeroable};
use core::borrow::Borrow;
const MAX_BLEND_STACK: usize = 256;
/// Creates a new builder for constructing a scene.
pub fn build_scene<'a>(scene: &'a mut Scene, resources: &'a mut ResourceContext) -> Builder<'a> {
Builder::new(&mut scene.data, ResourceData::Scene(resources))
}
/// Creates a new builder for construction a scene fragment.
pub fn build_fragment<'a>(fragment: &'a mut Fragment) -> Builder<'a> {
Builder::new(
&mut fragment.data,
ResourceData::Fragment(&mut fragment.resources),
)
}
/// Builder for constructing a scene or scene fragment.
pub struct Builder<'a> {
scene: &'a mut SceneData,
resources: ResourceData<'a>,
layers: Vec<Blend>,
transforms: Vec<Affine>,
}
impl<'a> Builder<'a> {
/// Creates a new builder for constructing a scene.
fn new(scene: &'a mut SceneData, mut resources: ResourceData<'a>) -> Self {
let is_fragment = match resources {
ResourceData::Fragment(_) => true,
_ => false,
};
scene.reset(is_fragment);
resources.clear();
Self {
scene,
resources,
layers: vec![],
transforms: vec![],
}
}
/// Pushes a transform matrix onto the stack.
pub fn push_transform(&mut self, transform: Affine) {
self.transform(transform);
self.transforms.push(transform);
}
/// Pops the current transform matrix.
pub fn pop_transform(&mut self) {
if let Some(transform) = self.transforms.pop() {
self.transform(transform.inverse());
}
}
/// Pushes a new layer bound by the specifed shape and composed with
/// previous layers using the specified blend mode.
pub fn push_layer<'s, E>(&mut self, blend: Blend, elements: E)
where
E: IntoIterator,
E::IntoIter: Clone,
E::Item: Borrow<Element>,
{
self.linewidth(-1.0);
let elements = elements.into_iter();
self.encode_path(elements, true);
self.begin_clip(Some(blend));
if self.layers.len() >= MAX_BLEND_STACK {
panic!("Maximum clip/blend stack size {} exceeded", MAX_BLEND_STACK);
}
self.layers.push(blend);
}
/// Pops the current layer.
pub fn pop_layer(&mut self) {
if let Some(layer) = self.layers.pop() {
self.end_clip(Some(layer));
}
}
/// Fills a shape using the specified style and brush.
pub fn fill<'s, E>(
&mut self,
style: Fill,
brush: &Brush,
brush_transform: Option<Affine>,
elements: E,
) where
E: IntoIterator,
E::IntoIter: Clone,
E::Item: Borrow<Element>,
{
self.linewidth(-1.0);
let elements = elements.into_iter();
self.encode_path(elements, true);
if let Some(brush_transform) = brush_transform {
self.transform(brush_transform);
self.swap_last_tags();
self.encode_brush(brush);
self.transform(brush_transform.inverse());
} else {
self.encode_brush(brush);
}
}
/// Strokes a shape using the specified style and brush.
pub fn stroke<'s, D, E>(
&mut self,
style: &Stroke<D>,
brush: &Brush,
brush_transform: Option<Affine>,
elements: E,
) where
D: Borrow<[f32]>,
E: IntoIterator,
E::IntoIter: Clone,
E::Item: Borrow<Element>,
{
self.linewidth(style.width);
let elements = elements.into_iter();
self.encode_path(elements, false);
if let Some(brush_transform) = brush_transform {
self.transform(brush_transform);
self.swap_last_tags();
self.encode_brush(brush);
self.transform(brush_transform.inverse());
} else {
self.encode_brush(brush);
}
}
/// Appends a fragment to the scene.
pub fn append(&mut self, fragment: &Fragment) {
let drawdata_base = self.scene.drawdata_stream.len();
self.scene.append(&fragment.data);
match &mut self.resources {
ResourceData::Scene(res) => {
for patch in &fragment.resources.patches {
match patch {
ResourcePatch::Ramp {
drawdata_offset,
stops,
} => {
let stops = &fragment.resources.stops[stops.clone()];
let ramp_id = res.add_ramp(stops);
let patch_base = *drawdata_offset + drawdata_base;
(&mut self.scene.drawdata_stream[patch_base..patch_base + 4])
.copy_from_slice(bytemuck::bytes_of(&ramp_id));
}
}
}
}
ResourceData::Fragment(res) => {
let stops_base = res.stops.len();
res.stops.extend_from_slice(&fragment.resources.stops);
res.patches.extend(fragment.resources.patches.iter().map(
|pending| match pending {
ResourcePatch::Ramp {
drawdata_offset,
stops,
} => ResourcePatch::Ramp {
drawdata_offset: drawdata_offset + drawdata_base,
stops: stops.start + stops_base..stops.end + stops_base,
},
},
));
}
}
}
/// Completes construction and finalizes the underlying scene.
pub fn finish(mut self) {
while let Some(layer) = self.layers.pop() {
self.end_clip(Some(layer));
}
match self.resources {
ResourceData::Fragment(_) => {
// Make sure the transform state is invariant for fragments
while !self.transforms.is_empty() {
self.pop_transform();
}
}
_ => {}
}
}
}
impl<'a> Builder<'a> {
fn encode_path<E>(&mut self, elements: E, is_fill: bool)
where
E: Iterator,
E::Item: Borrow<Element>,
{
if is_fill {
self.encode_path_inner(
elements
.map(|el| *el.borrow())
.flat_map(|el| {
match el {
Element::MoveTo(..) => Some(Element::Close),
_ => None,
}
.into_iter()
.chain(Some(el))
})
.chain(Some(Element::Close)),
)
} else {
self.encode_path_inner(elements.map(|el| *el.borrow()))
}
}
fn encode_path_inner(&mut self, elements: impl Iterator<Item = Element>) {
let mut b = PathBuilder::new(&mut self.scene.tag_stream, &mut self.scene.pathseg_stream);
for el in elements {
match el {
Element::MoveTo(p0) => b.move_to(p0.x, p0.y),
Element::LineTo(p0) => b.line_to(p0.x, p0.y),
Element::QuadTo(p0, p1) => b.quad_to(p0.x, p0.y, p1.x, p1.y),
Element::CurveTo(p0, p1, p2) => b.cubic_to(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y),
Element::Close => b.close_path(),
}
}
b.path();
let n_pathseg = b.n_pathseg();
self.scene.n_path += 1;
self.scene.n_pathseg += n_pathseg;
}
fn transform(&mut self, transform: Affine) {
self.scene.tag_stream.push(0x20);
self.scene.transform_stream.push(transform);
}
// Swap the last two tags in the tag stream; used for transformed
// gradients.
fn swap_last_tags(&mut self) {
let len = self.scene.tag_stream.len();
self.scene.tag_stream.swap(len - 1, len - 2);
}
// -1.0 means "fill"
fn linewidth(&mut self, linewidth: f32) {
self.scene.tag_stream.push(0x40);
self.scene.linewidth_stream.push(linewidth);
}
fn encode_brush(&mut self, brush: &Brush) {
match brush {
Brush::Solid(color) => {
self.scene.drawtag_stream.push(DRAWTAG_FILLCOLOR);
let rgba_color = color.to_premul_u32();
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&FillColor { rgba_color }));
}
Brush::LinearGradient(gradient) => {
let index = self.add_ramp(&gradient.stops);
self.scene.drawtag_stream.push(DRAWTAG_FILLLINGRADIENT);
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&FillLinGradient {
index,
p0: [gradient.start.x, gradient.start.y],
p1: [gradient.end.x, gradient.end.y],
}));
}
Brush::RadialGradient(gradient) => {
let index = self.add_ramp(&gradient.stops);
self.scene.drawtag_stream.push(DRAWTAG_FILLRADGRADIENT);
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&FillRadGradient {
index,
p0: [gradient.center0.x, gradient.center0.y],
p1: [gradient.center1.x, gradient.center1.y],
r0: gradient.radius0,
r1: gradient.radius1,
}));
}
Brush::SweepGradient(_gradient) => todo!("sweep gradients aren't done yet!"),
Brush::Image(_image) => todo!("images aren't done yet!"),
Brush::Persistent(_) => todo!("persistent brushes aren't done yet!"),
}
}
fn add_ramp(&mut self, stops: &[Stop]) -> u32 {
match &mut self.resources {
ResourceData::Scene(res) => res.add_ramp(stops),
ResourceData::Fragment(res) => {
let stops_start = res.stops.len();
res.stops.extend_from_slice(stops);
let id = res.patches.len() as u32;
res.patches.push(ResourcePatch::Ramp {
drawdata_offset: self.scene.drawdata_stream.len(),
stops: stops_start..stops_start + stops.len(),
});
id
}
}
}
/// Start a clip.
fn begin_clip(&mut self, blend: Option<Blend>) {
self.scene.drawtag_stream.push(DRAWTAG_BEGINCLIP);
let element = Clip {
blend: blend.unwrap_or(Blend::default()).pack(),
};
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&element));
self.scene.n_clip += 1;
}
fn end_clip(&mut self, blend: Option<Blend>) {
self.scene.drawtag_stream.push(DRAWTAG_ENDCLIP);
let element = Clip {
blend: blend.unwrap_or(Blend::default()).pack(),
};
self.scene
.drawdata_stream
.extend(bytemuck::bytes_of(&element));
// This is a dummy path, and will go away with the new clip impl.
self.scene.tag_stream.push(0x10);
self.scene.n_path += 1;
self.scene.n_clip += 1;
}
}
enum ResourceData<'a> {
Fragment(&'a mut FragmentResources),
Scene(&'a mut ResourceContext),
}
impl ResourceData<'_> {
fn clear(&mut self) {
match self {
Self::Fragment(res) => {
res.patches.clear();
res.stops.clear();
}
_ => {}
}
}
}
// Tags for draw objects. See shader/drawtag.h for the authoritative source.
const DRAWTAG_FILLCOLOR: u32 = 0x44;
const DRAWTAG_FILLLINGRADIENT: u32 = 0x114;
const DRAWTAG_FILLRADGRADIENT: u32 = 0x2dc;
const DRAWTAG_BEGINCLIP: u32 = 0x05;
const DRAWTAG_ENDCLIP: u32 = 0x25;
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct FillColor {
rgba_color: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct FillLinGradient {
index: u32,
p0: [f32; 2],
p1: [f32; 2],
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct FillRadGradient {
index: u32,
p0: [f32; 2],
p1: [f32; 2],
r0: f32,
r1: f32,
}
#[allow(unused)]
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct FillImage {
index: u32,
// [i16; 2]
offset: u32,
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default, Zeroable, Pod)]
pub struct Clip {
blend: u32,
}
struct PathBuilder<'a> {
tag_stream: &'a mut Vec<u8>,
// If we're never going to use the i16 encoding, it might be
// slightly faster to store this as Vec<u32>, we'd get aligned
// stores on ARM etc.
pathseg_stream: &'a mut Vec<u8>,
first_pt: [f32; 2],
state: PathState,
n_pathseg: u32,
}
#[derive(PartialEq)]
enum PathState {
Start,
MoveTo,
NonemptySubpath,
}
impl<'a> PathBuilder<'a> {
pub fn new(tags: &'a mut Vec<u8>, pathsegs: &'a mut Vec<u8>) -> PathBuilder<'a> {
PathBuilder {
tag_stream: tags,
pathseg_stream: pathsegs,
first_pt: [0.0, 0.0],
state: PathState::Start,
n_pathseg: 0,
}
}
pub fn move_to(&mut self, x: f32, y: f32) {
let buf = [x, y];
let bytes = bytemuck::bytes_of(&buf);
self.first_pt = buf;
if self.state == PathState::MoveTo {
let new_len = self.pathseg_stream.len() - 8;
self.pathseg_stream.truncate(new_len);
}
if self.state == PathState::NonemptySubpath {
if let Some(tag) = self.tag_stream.last_mut() {
*tag |= 4;
}
}
self.pathseg_stream.extend_from_slice(bytes);
self.state = PathState::MoveTo;
}
pub fn line_to(&mut self, x: f32, y: f32) {
if self.state == PathState::Start {
// should warn or error
return;
}
let buf = [x, y];
let bytes = bytemuck::bytes_of(&buf);
self.pathseg_stream.extend_from_slice(bytes);
self.tag_stream.push(9);
self.state = PathState::NonemptySubpath;
self.n_pathseg += 1;
}
pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) {
if self.state == PathState::Start {
return;
}
let buf = [x1, y1, x2, y2];
let bytes = bytemuck::bytes_of(&buf);
self.pathseg_stream.extend_from_slice(bytes);
self.tag_stream.push(10);
self.state = PathState::NonemptySubpath;
self.n_pathseg += 1;
}
pub fn cubic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) {
if self.state == PathState::Start {
return;
}
let buf = [x1, y1, x2, y2, x3, y3];
let bytes = bytemuck::bytes_of(&buf);
self.pathseg_stream.extend_from_slice(bytes);
self.tag_stream.push(11);
self.state = PathState::NonemptySubpath;
self.n_pathseg += 1;
}
pub fn close_path(&mut self) {
match self.state {
PathState::Start => return,
PathState::MoveTo => {
let new_len = self.pathseg_stream.len() - 8;
self.pathseg_stream.truncate(new_len);
self.state = PathState::Start;
return;
}
PathState::NonemptySubpath => (),
}
let len = self.pathseg_stream.len();
if len < 8 {
// can't happen
return;
}
let first_bytes = bytemuck::bytes_of(&self.first_pt);
if &self.pathseg_stream[len - 8..len] != first_bytes {
self.pathseg_stream.extend_from_slice(first_bytes);
self.tag_stream.push(13);
self.n_pathseg += 1;
} else {
if let Some(tag) = self.tag_stream.last_mut() {
*tag |= 4;
}
}
self.state = PathState::Start;
}
fn finish(&mut self) {
if self.state == PathState::MoveTo {
let new_len = self.pathseg_stream.len() - 8;
self.pathseg_stream.truncate(new_len);
}
if let Some(tag) = self.tag_stream.last_mut() {
*tag |= 4;
}
}
/// Finish encoding a path.
///
/// Encode this after encoding path segments.
pub fn path(&mut self) {
self.finish();
// maybe don't encode if path is empty? might throw off sync though
self.tag_stream.push(0x10);
}
/// Get the number of path segments.
///
/// This is the number of path segments that will be written by the
/// path stage; use this for allocating the output buffer.
///
/// Also note: it takes `self` for lifetime reasons.
pub fn n_pathseg(self) -> u32 {
self.n_pathseg
}
}

115
piet-scene/src/scene/mod.rs Normal file
View file

@ -0,0 +1,115 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
mod blend;
mod builder;
mod style;
pub use blend::{Blend, Compose, Mix};
pub use builder::{build_fragment, build_scene, Builder};
pub use style::*;
use super::brush::*;
use super::geometry::{Affine, Point, Rect};
use super::path::Element;
use core::ops::Range;
#[derive(Default)]
pub struct SceneData {
pub transform_stream: Vec<Affine>,
pub tag_stream: Vec<u8>,
pub pathseg_stream: Vec<u8>,
pub linewidth_stream: Vec<f32>,
pub drawtag_stream: Vec<u32>,
pub drawdata_stream: Vec<u8>,
pub n_path: u32,
pub n_pathseg: u32,
pub n_clip: u32,
}
impl SceneData {
fn reset(&mut self, is_fragment: bool) {
self.transform_stream.clear();
self.tag_stream.clear();
self.pathseg_stream.clear();
self.linewidth_stream.clear();
self.drawtag_stream.clear();
self.drawdata_stream.clear();
self.n_path = 0;
self.n_pathseg = 0;
self.n_clip = 0;
if !is_fragment {
self.transform_stream
.push(Affine::new(&[1.0, 0.0, 0.0, 1.0, 0.0, 0.0]));
self.linewidth_stream.push(-1.0);
}
}
fn append(&mut self, other: &SceneData) {
self.transform_stream
.extend_from_slice(&other.transform_stream);
self.tag_stream.extend_from_slice(&other.tag_stream);
self.pathseg_stream.extend_from_slice(&other.pathseg_stream);
self.linewidth_stream
.extend_from_slice(&other.linewidth_stream);
self.drawtag_stream.extend_from_slice(&other.drawtag_stream);
self.drawdata_stream
.extend_from_slice(&other.drawdata_stream);
self.n_path += other.n_path;
self.n_pathseg += other.n_pathseg;
self.n_clip += other.n_clip;
}
}
/// Encoded definition of a scene that is ready for rendering when paired with
/// an associated resource context.
#[derive(Default)]
pub struct Scene {
data: SceneData,
}
impl Scene {
pub fn data(&self) -> &SceneData {
&self.data
}
}
/// Encoded definition of a scene fragment and associated resources.
#[derive(Default)]
pub struct Fragment {
data: SceneData,
resources: FragmentResources,
}
impl Fragment {
pub fn points(&self) -> &[Point] {
bytemuck::cast_slice(&self.data.pathseg_stream)
}
}
#[derive(Default)]
struct FragmentResources {
patches: Vec<ResourcePatch>,
stops: Vec<Stop>,
}
enum ResourcePatch {
Ramp {
drawdata_offset: usize,
stops: Range<usize>,
},
}

View file

@ -0,0 +1,71 @@
// Copyright 2022 The piet-gpu authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Also licensed under MIT license, at your choice.
use core::borrow::Borrow;
/// Describes the winding rule that determines the interior portion of a path.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Fill {
NonZero,
EvenOdd,
}
/// Defines the connection between two segments of a stroke.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Join {
/// A straight line connecting the segments.
Bevel,
/// The segments are extended to their natural intersection point.
Miter,
/// An arc between the segments.
Round,
}
/// Defines the shape to be drawn at the ends of a stroke.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Cap {
/// Flat cap.
Butt,
/// Square cap with dimensions equal to half the stroke width.
Square,
/// Rounded cap with radius equal to half the stroke width.
Round,
}
/// Describes the visual style of a stroke.
#[derive(Copy, Clone, Debug)]
pub struct Stroke<D>
where
D: Borrow<[f32]>,
{
/// Width of the stroke.
pub width: f32,
/// Style for connecting segments of the stroke.
pub join: Join,
/// Limit for miter joins.
pub miter_limit: f32,
/// Style for capping the beginning of an open subpath.
pub start_cap: Cap,
/// Style for capping the end of an open subpath.
pub end_cap: Cap,
/// Lengths of dashes in alternating on/off order.
pub dash_pattern: D,
/// Offset of the first dash.
pub dash_offset: f32,
/// True if the stroke width should be affected by the scale of a
/// transform.
pub scale: bool,
}