mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-10 12:41:30 +11:00
wgsl port in realtime
* add writeable image support to engine * add external resources to engine * update fine to output to a texture * copy over original piet-gpu test scenes * put it all in a pretty (resizable!) window
This commit is contained in:
parent
fddd019567
commit
ff4f71ef3c
208
Cargo.lock
generated
208
Cargo.lock
generated
|
@ -46,6 +46,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
@ -243,6 +249,15 @@ dependencies = [
|
||||||
"os_str_bytes",
|
"os_str_bytes",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cmake"
|
||||||
|
version = "0.1.49"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cocoa"
|
name = "cocoa"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
|
@ -254,7 +269,7 @@ dependencies = [
|
||||||
"cocoa-foundation",
|
"cocoa-foundation",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics",
|
"core-graphics",
|
||||||
"foreign-types",
|
"foreign-types 0.3.2",
|
||||||
"libc",
|
"libc",
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
@ -269,7 +284,7 @@ dependencies = [
|
||||||
"block",
|
"block",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types 0.3.2",
|
||||||
"libc",
|
"libc",
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
@ -309,7 +324,7 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types 0.3.2",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -321,7 +336,19 @@ checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"foreign-types",
|
"foreign-types 0.3.2",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-text"
|
||||||
|
version = "19.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation",
|
||||||
|
"core-graphics",
|
||||||
|
"foreign-types 0.3.2",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -334,6 +361,29 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossfont"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21fd3add36ea31aba1520aa5288714dd63be506106753226d0eb387a93bc9c45"
|
||||||
|
dependencies = [
|
||||||
|
"cocoa",
|
||||||
|
"core-foundation",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"core-graphics",
|
||||||
|
"core-text",
|
||||||
|
"dwrote",
|
||||||
|
"foreign-types 0.5.0",
|
||||||
|
"freetype-rs",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"objc",
|
||||||
|
"once_cell",
|
||||||
|
"pkg-config",
|
||||||
|
"servo-fontconfig",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cty"
|
name = "cty"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -442,6 +492,20 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dwrote"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"winapi",
|
||||||
|
"wio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_logger"
|
name = "env_logger"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
|
@ -455,6 +519,16 @@ dependencies = [
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "expat-sys"
|
||||||
|
version = "2.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa"
|
||||||
|
dependencies = [
|
||||||
|
"cmake",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -486,7 +560,28 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"foreign-types-shared",
|
"foreign-types-shared 0.1.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-macros",
|
||||||
|
"foreign-types-shared 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-macros"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8469d0d40519bc608ec6863f1cc88f3f1deee15913f2f3b3e573d81ed38cccc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -495,6 +590,34 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "freetype-rs"
|
||||||
|
version = "0.26.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"freetype-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "freetype-sys"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a"
|
||||||
|
dependencies = [
|
||||||
|
"cmake",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.25"
|
version = "0.3.25"
|
||||||
|
@ -800,7 +923,7 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"block",
|
"block",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types 0.3.2",
|
||||||
"log",
|
"log",
|
||||||
"objc",
|
"objc",
|
||||||
]
|
]
|
||||||
|
@ -1136,7 +1259,7 @@ dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cocoa-foundation",
|
"cocoa-foundation",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"foreign-types",
|
"foreign-types 0.3.2",
|
||||||
"metal",
|
"metal",
|
||||||
"objc",
|
"objc",
|
||||||
"raw-window-handle 0.5.0",
|
"raw-window-handle 0.5.0",
|
||||||
|
@ -1188,6 +1311,7 @@ dependencies = [
|
||||||
"pollster",
|
"pollster",
|
||||||
"roxmltree",
|
"roxmltree",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1449,6 +1573,15 @@ version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "safe_arch"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scoped-tls"
|
name = "scoped-tls"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -1461,6 +1594,18 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sctk-adwaita"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61270629cc6b4d77ec1907db1033d5c2e1a404c412743621981a871dc9c12339"
|
||||||
|
dependencies = [
|
||||||
|
"crossfont",
|
||||||
|
"log",
|
||||||
|
"smithay-client-toolkit",
|
||||||
|
"tiny-skia",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.147"
|
version = "1.0.147"
|
||||||
|
@ -1492,6 +1637,27 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "servo-fontconfig"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"servo-fontconfig-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "servo-fontconfig-sys"
|
||||||
|
version = "5.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388"
|
||||||
|
dependencies = [
|
||||||
|
"expat-sys",
|
||||||
|
"freetype-sys",
|
||||||
|
"pkg-config",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slotmap"
|
name = "slotmap"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
@ -1629,6 +1795,31 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-skia"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"arrayvec 0.5.2",
|
||||||
|
"bytemuck",
|
||||||
|
"cfg-if",
|
||||||
|
"png",
|
||||||
|
"safe_arch",
|
||||||
|
"tiny-skia-path",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-skia-path"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.9"
|
version = "0.5.9"
|
||||||
|
@ -1895,7 +2086,7 @@ dependencies = [
|
||||||
"block",
|
"block",
|
||||||
"core-graphics-types",
|
"core-graphics-types",
|
||||||
"d3d12",
|
"d3d12",
|
||||||
"foreign-types",
|
"foreign-types 0.3.2",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"glow",
|
"glow",
|
||||||
"gpu-alloc",
|
"gpu-alloc",
|
||||||
|
@ -2083,6 +2274,7 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"raw-window-handle 0.4.3",
|
"raw-window-handle 0.4.3",
|
||||||
"raw-window-handle 0.5.0",
|
"raw-window-handle 0.5.0",
|
||||||
|
"sctk-adwaita",
|
||||||
"smithay-client-toolkit",
|
"smithay-client-toolkit",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
|
|
|
@ -18,3 +18,6 @@ piet-scene = { path = "../piet-scene" }
|
||||||
|
|
||||||
# for picosvg, should be split out
|
# for picosvg, should be split out
|
||||||
roxmltree = "0.13"
|
roxmltree = "0.13"
|
||||||
|
|
||||||
|
# move this to an example
|
||||||
|
winit = "0.27.5"
|
||||||
|
|
|
@ -21,10 +21,6 @@ var<storage> tiles: array<Tile>;
|
||||||
@group(0) @binding(2)
|
@group(0) @binding(2)
|
||||||
var<storage> segments: array<Segment>;
|
var<storage> segments: array<Segment>;
|
||||||
|
|
||||||
// This will become a texture, but keeping things simple for now
|
|
||||||
@group(0) @binding(3)
|
|
||||||
var<storage, read_write> output: array<u32>;
|
|
||||||
|
|
||||||
#ifdef full
|
#ifdef full
|
||||||
|
|
||||||
#import blend
|
#import blend
|
||||||
|
@ -33,6 +29,9 @@ var<storage, read_write> output: array<u32>;
|
||||||
let GRADIENT_WIDTH = 512;
|
let GRADIENT_WIDTH = 512;
|
||||||
let BLEND_STACK_SPLIT = 4u;
|
let BLEND_STACK_SPLIT = 4u;
|
||||||
|
|
||||||
|
@group(0) @binding(3)
|
||||||
|
var output: texture_storage_2d<rgba8unorm, write>;
|
||||||
|
|
||||||
@group(0) @binding(4)
|
@group(0) @binding(4)
|
||||||
var<storage> ptcl: array<u32>;
|
var<storage> ptcl: array<u32>;
|
||||||
|
|
||||||
|
@ -78,6 +77,11 @@ fn read_rad_grad(cmd_ix: u32) -> CmdRadGrad {
|
||||||
return CmdRadGrad(index, matrx, xlat, c1, ra, roff);
|
return CmdRadGrad(index, matrx, xlat, c1, ra, roff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
@group(0) @binding(3)
|
||||||
|
var output: texture_storage_2d<r8, write>;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
let PIXELS_PER_THREAD = 4u;
|
let PIXELS_PER_THREAD = 4u;
|
||||||
|
@ -273,20 +277,23 @@ fn main(
|
||||||
default: {}
|
default: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let out_ix = global_id.y * (config.width_in_tiles * TILE_WIDTH) + global_id.x * PIXELS_PER_THREAD;
|
let xy_uint = vec2<u32>(xy);
|
||||||
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
|
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
|
||||||
let fg = rgba[i];
|
let coords = xy_uint + vec2(i, 0u);
|
||||||
let a_inv = 1.0 / (fg.a + 1e-6);
|
if coords.x < config.target_width && coords.y < config.target_height {
|
||||||
let rgba_sep = vec4(fg.r * a_inv, fg.g * a_inv, fg.b * a_inv, fg.a);
|
textureStore(output, vec2<i32>(coords), rgba[i]);
|
||||||
let bytes = pack4x8unorm(rgba_sep);
|
}
|
||||||
output[out_ix + i] = bytes;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
let tile = tiles[tile_ix];
|
let tile = tiles[tile_ix];
|
||||||
let area = fill_path(tile, xy);
|
let area = fill_path(tile, xy);
|
||||||
|
|
||||||
let bytes = pack4x8unorm(vec4(area[0], area[1], area[2], area[3]));
|
let xy_uint = vec2<u32>(xy);
|
||||||
let out_ix = global_id.y * (config.width_in_tiles * 4u) + global_id.x;
|
for (var i = 0u; i < PIXELS_PER_THREAD; i += 1u) {
|
||||||
output[out_ix] = bytes;
|
let coords = xy_uint + vec2(i, 0u);
|
||||||
|
if coords.x < config.target_width && coords.y < config.target_height {
|
||||||
|
textureStore(output, vec2<i32>(coords), vec4(area[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ struct Config {
|
||||||
width_in_tiles: u32,
|
width_in_tiles: u32,
|
||||||
height_in_tiles: u32,
|
height_in_tiles: u32,
|
||||||
|
|
||||||
|
target_width: u32,
|
||||||
|
target_height: u32,
|
||||||
|
|
||||||
n_drawobj: u32,
|
n_drawobj: u32,
|
||||||
n_path: u32,
|
n_path: u32,
|
||||||
n_clip: u32,
|
n_clip: u32,
|
||||||
|
|
|
@ -59,11 +59,17 @@ pub struct BufProxy {
|
||||||
id: Id,
|
id: Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ImageFormat {
|
||||||
|
Rgba8,
|
||||||
|
Bgra8,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct ImageProxy {
|
pub struct ImageProxy {
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
// TODO: format
|
format: ImageFormat,
|
||||||
id: Id,
|
id: Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,6 +79,11 @@ pub enum ResourceProxy {
|
||||||
Image(ImageProxy),
|
Image(ImageProxy),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ExternalResource<'a> {
|
||||||
|
Buf(BufProxy, &'a Buffer),
|
||||||
|
Image(ImageProxy, &'a TextureView),
|
||||||
|
}
|
||||||
|
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Upload(BufProxy, Vec<u8>),
|
Upload(BufProxy, Vec<u8>),
|
||||||
UploadImage(ImageProxy, Vec<u8>),
|
UploadImage(ImageProxy, Vec<u8>),
|
||||||
|
@ -97,11 +108,9 @@ pub enum BindType {
|
||||||
/// A storage buffer with read only access.
|
/// A storage buffer with read only access.
|
||||||
BufReadOnly,
|
BufReadOnly,
|
||||||
/// A storage image.
|
/// A storage image.
|
||||||
#[allow(unused)] // TODO
|
Image(ImageFormat),
|
||||||
Image,
|
|
||||||
/// A storage image with read only access.
|
/// A storage image with read only access.
|
||||||
#[allow(unused)] // TODO
|
ImageRead(ImageFormat),
|
||||||
ImageRead,
|
|
||||||
// TODO: Uniform, Sampler, maybe others
|
// TODO: Uniform, Sampler, maybe others
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,16 +158,26 @@ impl Engine {
|
||||||
},
|
},
|
||||||
count: None,
|
count: None,
|
||||||
},
|
},
|
||||||
BindType::ImageRead => wgpu::BindGroupLayoutEntry {
|
BindType::Image(format) | BindType::ImageRead(format) => {
|
||||||
binding: i as u32,
|
wgpu::BindGroupLayoutEntry {
|
||||||
visibility: wgpu::ShaderStages::COMPUTE,
|
binding: i as u32,
|
||||||
ty: wgpu::BindingType::Texture {
|
visibility: wgpu::ShaderStages::COMPUTE,
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
ty: if *bind_type == BindType::ImageRead(*format) {
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
wgpu::BindingType::Texture {
|
||||||
multisampled: false,
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
},
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
count: None,
|
multisampled: false,
|
||||||
},
|
}
|
||||||
|
} else {
|
||||||
|
wgpu::BindingType::StorageTexture {
|
||||||
|
access: wgpu::StorageTextureAccess::WriteOnly,
|
||||||
|
format: format.to_wgpu(),
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -192,6 +211,7 @@ impl Engine {
|
||||||
device: &Device,
|
device: &Device,
|
||||||
queue: &Queue,
|
queue: &Queue,
|
||||||
recording: &Recording,
|
recording: &Recording,
|
||||||
|
external_resources: &[ExternalResource],
|
||||||
) -> Result<Downloads, Error> {
|
) -> Result<Downloads, Error> {
|
||||||
let mut bind_map = BindMap::default();
|
let mut bind_map = BindMap::default();
|
||||||
let mut downloads = Downloads::default();
|
let mut downloads = Downloads::default();
|
||||||
|
@ -226,7 +246,7 @@ impl Engine {
|
||||||
sample_count: 1,
|
sample_count: 1,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
|
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
|
||||||
format: TextureFormat::Rgba8Unorm,
|
format: image_proxy.format.to_wgpu(),
|
||||||
});
|
});
|
||||||
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor {
|
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
|
@ -262,10 +282,14 @@ impl Engine {
|
||||||
bind_map.insert_image(image_proxy.id, texture, texture_view)
|
bind_map.insert_image(image_proxy.id, texture, texture_view)
|
||||||
}
|
}
|
||||||
Command::Dispatch(shader_id, wg_size, bindings) => {
|
Command::Dispatch(shader_id, wg_size, bindings) => {
|
||||||
println!("dispatching {:?} with {} bindings", wg_size, bindings.len());
|
// println!("dispatching {:?} with {} bindings", wg_size, bindings.len());
|
||||||
let shader = &self.shaders[shader_id.0];
|
let shader = &self.shaders[shader_id.0];
|
||||||
let bind_group =
|
let bind_group = bind_map.create_bind_group(
|
||||||
bind_map.create_bind_group(device, &shader.bind_group_layout, bindings)?;
|
device,
|
||||||
|
&shader.bind_group_layout,
|
||||||
|
bindings,
|
||||||
|
external_resources,
|
||||||
|
)?;
|
||||||
let mut cpass = encoder.begin_compute_pass(&Default::default());
|
let mut cpass = encoder.begin_compute_pass(&Default::default());
|
||||||
cpass.set_pipeline(&shader.pipeline);
|
cpass.set_pipeline(&shader.pipeline);
|
||||||
cpass.set_bind_group(0, &bind_group, &[]);
|
cpass.set_bind_group(0, &bind_group, &[]);
|
||||||
|
@ -309,10 +333,11 @@ impl Recording {
|
||||||
&mut self,
|
&mut self,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
|
format: ImageFormat,
|
||||||
data: impl Into<Vec<u8>>,
|
data: impl Into<Vec<u8>>,
|
||||||
) -> ImageProxy {
|
) -> ImageProxy {
|
||||||
let data = data.into();
|
let data = data.into();
|
||||||
let image_proxy = ImageProxy::new(width, height);
|
let image_proxy = ImageProxy::new(width, height, format);
|
||||||
self.push(Command::UploadImage(image_proxy, data));
|
self.push(Command::UploadImage(image_proxy, data));
|
||||||
image_proxy
|
image_proxy
|
||||||
}
|
}
|
||||||
|
@ -348,10 +373,24 @@ impl BufProxy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImageFormat {
|
||||||
|
pub fn to_wgpu(self) -> wgpu::TextureFormat {
|
||||||
|
match self {
|
||||||
|
Self::Rgba8 => wgpu::TextureFormat::Rgba8Unorm,
|
||||||
|
Self::Bgra8 => wgpu::TextureFormat::Bgra8Unorm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ImageProxy {
|
impl ImageProxy {
|
||||||
pub fn new(width: u32, height: u32) -> Self {
|
pub fn new(width: u32, height: u32, format: ImageFormat) -> Self {
|
||||||
let id = Id::next();
|
let id = Id::next();
|
||||||
ImageProxy { width, height, id }
|
ImageProxy {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
format,
|
||||||
|
id,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,8 +399,8 @@ impl ResourceProxy {
|
||||||
Self::Buf(BufProxy::new(size))
|
Self::Buf(BufProxy::new(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_image(width: u32, height: u32) -> Self {
|
pub fn new_image(width: u32, height: u32, format: ImageFormat) -> Self {
|
||||||
Self::Image(ImageProxy::new(width, height))
|
Self::Image(ImageProxy::new(width, height, format))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_buf(&self) -> Option<&BufProxy> {
|
pub fn as_buf(&self) -> Option<&BufProxy> {
|
||||||
|
@ -413,10 +452,44 @@ impl BindMap {
|
||||||
device: &Device,
|
device: &Device,
|
||||||
layout: &BindGroupLayout,
|
layout: &BindGroupLayout,
|
||||||
bindings: &[ResourceProxy],
|
bindings: &[ResourceProxy],
|
||||||
|
external_resources: &[ExternalResource],
|
||||||
) -> Result<BindGroup, Error> {
|
) -> Result<BindGroup, Error> {
|
||||||
|
// These functions are ugly and linear, but the remap array should generally be
|
||||||
|
// small. Should find a better solution for this.
|
||||||
|
fn find_buf<'a>(
|
||||||
|
resources: &[ExternalResource<'a>],
|
||||||
|
proxy: &BufProxy,
|
||||||
|
) -> Option<&'a Buffer> {
|
||||||
|
for resource in resources {
|
||||||
|
match resource {
|
||||||
|
ExternalResource::Buf(p, buf) if p.id == proxy.id => {
|
||||||
|
return Some(buf);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn find_image<'a>(
|
||||||
|
resources: &[ExternalResource<'a>],
|
||||||
|
proxy: &ImageProxy,
|
||||||
|
) -> Option<&'a TextureView> {
|
||||||
|
for resource in resources {
|
||||||
|
match resource {
|
||||||
|
ExternalResource::Image(p, view) if p.id == proxy.id => {
|
||||||
|
return Some(view);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
for proxy in bindings {
|
for proxy in bindings {
|
||||||
match proxy {
|
match proxy {
|
||||||
ResourceProxy::Buf(proxy) => {
|
ResourceProxy::Buf(proxy) => {
|
||||||
|
if find_buf(external_resources, proxy).is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if let Entry::Vacant(v) = self.buf_map.entry(proxy.id) {
|
if let Entry::Vacant(v) = self.buf_map.entry(proxy.id) {
|
||||||
let buf = device.create_buffer(&wgpu::BufferDescriptor {
|
let buf = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
|
@ -430,6 +503,9 @@ impl BindMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResourceProxy::Image(proxy) => {
|
ResourceProxy::Image(proxy) => {
|
||||||
|
if find_image(external_resources, proxy).is_some() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if let Entry::Vacant(v) = self.image_map.entry(proxy.id) {
|
if let Entry::Vacant(v) = self.image_map.entry(proxy.id) {
|
||||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
|
@ -442,7 +518,7 @@ impl BindMap {
|
||||||
sample_count: 1,
|
sample_count: 1,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
|
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
|
||||||
format: TextureFormat::Rgba8Unorm,
|
format: proxy.format.to_wgpu(),
|
||||||
});
|
});
|
||||||
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor {
|
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
|
@ -452,7 +528,7 @@ impl BindMap {
|
||||||
base_mip_level: 0,
|
base_mip_level: 0,
|
||||||
base_array_layer: 0,
|
base_array_layer: 0,
|
||||||
array_layer_count: None,
|
array_layer_count: None,
|
||||||
format: Some(TextureFormat::Rgba8Unorm),
|
format: Some(proxy.format.to_wgpu()),
|
||||||
});
|
});
|
||||||
v.insert((texture, texture_view));
|
v.insert((texture, texture_view));
|
||||||
}
|
}
|
||||||
|
@ -464,17 +540,21 @@ impl BindMap {
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, proxy)| match proxy {
|
.map(|(i, proxy)| match proxy {
|
||||||
ResourceProxy::Buf(proxy) => {
|
ResourceProxy::Buf(proxy) => {
|
||||||
let buf = self.buf_map.get(&proxy.id).unwrap();
|
let buf = find_buf(external_resources, proxy)
|
||||||
|
.or_else(|| self.buf_map.get(&proxy.id))
|
||||||
|
.unwrap();
|
||||||
Ok(wgpu::BindGroupEntry {
|
Ok(wgpu::BindGroupEntry {
|
||||||
binding: i as u32,
|
binding: i as u32,
|
||||||
resource: buf.as_entire_binding(),
|
resource: buf.as_entire_binding(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
ResourceProxy::Image(proxy) => {
|
ResourceProxy::Image(proxy) => {
|
||||||
let texture = self.image_map.get(&proxy.id).unwrap();
|
let view = find_image(external_resources, proxy)
|
||||||
|
.or_else(|| self.image_map.get(&proxy.id).map(|v| &v.1))
|
||||||
|
.unwrap();
|
||||||
Ok(wgpu::BindGroupEntry {
|
Ok(wgpu::BindGroupEntry {
|
||||||
binding: i as u32,
|
binding: i as u32,
|
||||||
resource: wgpu::BindingResource::TextureView(&texture.1),
|
resource: wgpu::BindingResource::TextureView(view),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -16,12 +16,11 @@
|
||||||
|
|
||||||
//! A simple application to run a compute shader.
|
//! A simple application to run a compute shader.
|
||||||
|
|
||||||
use std::{fs::File, io::BufWriter};
|
use engine::{Engine, Error, ExternalResource};
|
||||||
|
|
||||||
use engine::Engine;
|
use piet_scene::{Scene, SceneBuilder};
|
||||||
|
use wgpu::{Device, Instance, Limits, Queue, Surface, SurfaceConfiguration};
|
||||||
use render::next_multiple_of;
|
use winit::window::Window;
|
||||||
use wgpu::{Device, Limits, Queue};
|
|
||||||
|
|
||||||
mod debug;
|
mod debug;
|
||||||
mod engine;
|
mod engine;
|
||||||
|
@ -29,103 +28,321 @@ mod pico_svg;
|
||||||
mod ramp;
|
mod ramp;
|
||||||
mod render;
|
mod render;
|
||||||
mod shaders;
|
mod shaders;
|
||||||
|
mod simple_text;
|
||||||
mod test_scene;
|
mod test_scene;
|
||||||
|
|
||||||
async fn run(dimensions: &Dimensions) -> Result<(), Box<dyn std::error::Error>> {
|
use pico_svg::PicoSvg;
|
||||||
let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
|
use simple_text::SimpleText;
|
||||||
let adapter = instance.request_adapter(&Default::default()).await.unwrap();
|
|
||||||
let features = adapter.features();
|
|
||||||
let mut limits = Limits::default();
|
|
||||||
limits.max_storage_buffers_per_shader_stage = 16;
|
|
||||||
let (device, queue) = adapter
|
|
||||||
.request_device(
|
|
||||||
&wgpu::DeviceDescriptor {
|
|
||||||
label: None,
|
|
||||||
features: features & wgpu::Features::TIMESTAMP_QUERY,
|
|
||||||
limits,
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let mut engine = Engine::new();
|
|
||||||
do_render(&device, &queue, &mut engine, dimensions).await?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dump_buf(buf: &[u32]) {
|
|
||||||
for (i, val) in buf.iter().enumerate() {
|
|
||||||
if *val != 0 {
|
|
||||||
let lo = val & 0x7fff_ffff;
|
|
||||||
if lo >= 0x3000_0000 && lo < 0x5000_0000 {
|
|
||||||
println!("{}: {:x} {}", i, val, f32::from_bits(*val));
|
|
||||||
} else {
|
|
||||||
println!("{}: {:x}", i, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn do_render(
|
|
||||||
device: &Device,
|
|
||||||
queue: &Queue,
|
|
||||||
engine: &mut Engine,
|
|
||||||
dimensions: &Dimensions,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
#[allow(unused)]
|
|
||||||
let shaders = shaders::init_shaders(device, engine)?;
|
|
||||||
let full_shaders = shaders::full_shaders(device, engine)?;
|
|
||||||
let scene = test_scene::gen_test_scene();
|
|
||||||
//test_scene::dump_scene_info(&scene);
|
|
||||||
//let (recording, buf) = render::render(&scene, &shaders);
|
|
||||||
let (recording, buf) = render::render_full(&scene, &full_shaders, dimensions);
|
|
||||||
let downloads = engine.run_recording(&device, &queue, &recording)?;
|
|
||||||
let mapped = downloads.map();
|
|
||||||
device.poll(wgpu::Maintain::Wait);
|
|
||||||
let buf = mapped.get_mapped(buf).await?;
|
|
||||||
|
|
||||||
if false {
|
|
||||||
dump_buf(bytemuck::cast_slice(&buf));
|
|
||||||
} else {
|
|
||||||
let file = File::create("image.png")?;
|
|
||||||
let w = BufWriter::new(file);
|
|
||||||
let mut encoder = png::Encoder::new(w, dimensions.width, dimensions.height);
|
|
||||||
encoder.set_color(png::ColorType::Rgba);
|
|
||||||
let mut writer = encoder.write_header()?;
|
|
||||||
let expected_size = (dimensions.width * dimensions.height * 4) as usize;
|
|
||||||
|
|
||||||
let new_width = next_multiple_of(dimensions.width, 16) as usize;
|
|
||||||
|
|
||||||
if expected_size == buf.len() {
|
|
||||||
writer.write_image_data(&buf)?;
|
|
||||||
} else {
|
|
||||||
let mut output = Vec::<u8>::with_capacity(expected_size * 4);
|
|
||||||
for height in 0..(dimensions.height as usize) {
|
|
||||||
output.extend_from_slice(
|
|
||||||
&buf[height * new_width * 4..][..dimensions.width as usize * 4],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
writer.write_image_data(&output)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Dimensions {
|
pub struct Dimensions {
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
pub struct WgpuState {
|
||||||
let mut args = std::env::args();
|
pub instance: Instance,
|
||||||
args.next();
|
pub device: Device,
|
||||||
let width = args
|
pub queue: Queue,
|
||||||
.next()
|
pub surface: Option<Surface>,
|
||||||
.and_then(|it| it.parse::<u32>().ok())
|
pub surface_config: SurfaceConfiguration,
|
||||||
.unwrap_or(1024);
|
}
|
||||||
let height = args
|
|
||||||
.next()
|
impl WgpuState {
|
||||||
.and_then(|it| it.parse::<u32>().ok())
|
pub async fn new(window: Option<&Window>) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
.unwrap_or(1024);
|
let instance = Instance::new(wgpu::Backends::PRIMARY);
|
||||||
pollster::block_on(run(&Dimensions { width, height })).unwrap();
|
let adapter = instance.request_adapter(&Default::default()).await.unwrap();
|
||||||
|
let features = adapter.features();
|
||||||
|
let mut limits = Limits::default();
|
||||||
|
limits.max_storage_buffers_per_shader_stage = 16;
|
||||||
|
let (device, queue) = adapter
|
||||||
|
.request_device(
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
label: None,
|
||||||
|
features: features
|
||||||
|
& (wgpu::Features::TIMESTAMP_QUERY | wgpu::Features::CLEAR_TEXTURE),
|
||||||
|
limits,
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let (surface, surface_config) = if let Some(window) = window {
|
||||||
|
let surface = unsafe { instance.create_surface(window) };
|
||||||
|
let size = window.inner_size();
|
||||||
|
// let format = surface.get_supported_formats(&adapter)[0];
|
||||||
|
let format = wgpu::TextureFormat::Bgra8Unorm;
|
||||||
|
println!("surface: {:?} {:?}", size, format);
|
||||||
|
let surface_config = wgpu::SurfaceConfiguration {
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
format,
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
present_mode: wgpu::PresentMode::Fifo,
|
||||||
|
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||||
|
};
|
||||||
|
surface.configure(&device, &surface_config);
|
||||||
|
(Some(surface), surface_config)
|
||||||
|
} else {
|
||||||
|
let surface_config = wgpu::SurfaceConfiguration {
|
||||||
|
usage: wgpu::TextureUsages::empty(),
|
||||||
|
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
present_mode: wgpu::PresentMode::Fifo,
|
||||||
|
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||||
|
};
|
||||||
|
(None, surface_config)
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
instance,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
surface,
|
||||||
|
surface_config,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_target_texture(&self) -> (wgpu::Texture, wgpu::TextureView) {
|
||||||
|
let texture = self.device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: None,
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: self.surface_config.width,
|
||||||
|
height: self.surface_config.height,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
|
||||||
|
format: wgpu::TextureFormat::Rgba8Unorm,
|
||||||
|
});
|
||||||
|
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
(texture, view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_interactive() -> Result<(), Error> {
|
||||||
|
use winit::{
|
||||||
|
dpi::LogicalSize,
|
||||||
|
event::*,
|
||||||
|
event_loop::{ControlFlow, EventLoop},
|
||||||
|
window::WindowBuilder,
|
||||||
|
};
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_inner_size(LogicalSize::new(1044, 800))
|
||||||
|
.with_resizable(true)
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
let mut state = WgpuState::new(Some(&window)).await?;
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
let full_shaders = shaders::full_shaders(&state.device, &mut engine)?;
|
||||||
|
let (blit_layout, blit_pipeline) = create_blit_pipeline(&state);
|
||||||
|
let mut simple_text = SimpleText::new();
|
||||||
|
let mut current_frame = 0usize;
|
||||||
|
let mut scene_ix = 0usize;
|
||||||
|
let (mut _target_texture, mut target_view) = state.create_target_texture();
|
||||||
|
event_loop.run(move |event, _, control_flow| match event {
|
||||||
|
Event::WindowEvent {
|
||||||
|
ref event,
|
||||||
|
window_id,
|
||||||
|
} if window_id == window.id() => match event {
|
||||||
|
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||||
|
WindowEvent::KeyboardInput { input, .. } => {
|
||||||
|
if input.state == ElementState::Pressed {
|
||||||
|
match input.virtual_keycode {
|
||||||
|
Some(VirtualKeyCode::Left) => scene_ix = scene_ix.saturating_sub(1),
|
||||||
|
Some(VirtualKeyCode::Right) => scene_ix = scene_ix.saturating_add(1),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::Resized(size) => {
|
||||||
|
state.surface_config.width = size.width;
|
||||||
|
state.surface_config.height = size.height;
|
||||||
|
state
|
||||||
|
.surface
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.configure(&state.device, &state.surface_config);
|
||||||
|
let (t, v) = state.create_target_texture();
|
||||||
|
_target_texture = t;
|
||||||
|
target_view = v;
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
Event::MainEventsCleared => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
Event::RedrawRequested(_) => {
|
||||||
|
current_frame += 1;
|
||||||
|
let surface_texture = state
|
||||||
|
.surface
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get_current_texture()
|
||||||
|
.unwrap();
|
||||||
|
let dimensions = Dimensions {
|
||||||
|
width: state.surface_config.width,
|
||||||
|
height: state.surface_config.height,
|
||||||
|
};
|
||||||
|
let mut scene = Scene::default();
|
||||||
|
let mut builder = SceneBuilder::for_scene(&mut scene);
|
||||||
|
const N_SCENES: usize = 6;
|
||||||
|
match scene_ix % N_SCENES {
|
||||||
|
0 => test_scene::render_anim_frame(&mut builder, &mut simple_text, current_frame),
|
||||||
|
1 => test_scene::render_blend_grid(&mut builder),
|
||||||
|
2 => test_scene::render_tiger(&mut builder, false),
|
||||||
|
3 => test_scene::render_brush_transform(&mut builder, current_frame),
|
||||||
|
4 => test_scene::render_funky_paths(&mut builder),
|
||||||
|
_ => test_scene::render_scene(&mut builder),
|
||||||
|
}
|
||||||
|
builder.finish();
|
||||||
|
let (recording, target) = render::render_full(&scene, &full_shaders, &dimensions);
|
||||||
|
let external_resources = [ExternalResource::Image(
|
||||||
|
*target.as_image().unwrap(),
|
||||||
|
&target_view,
|
||||||
|
)];
|
||||||
|
let _ = engine
|
||||||
|
.run_recording(&state.device, &state.queue, &recording, &external_resources)
|
||||||
|
.unwrap();
|
||||||
|
let mut encoder = state
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
|
||||||
|
{
|
||||||
|
let surface_view = surface_texture
|
||||||
|
.texture
|
||||||
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
let bind_group = state.device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: &blit_layout,
|
||||||
|
entries: &[wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&target_view),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: None,
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &surface_view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color::default()),
|
||||||
|
store: true,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
render_pass.set_pipeline(&blit_pipeline);
|
||||||
|
render_pass.set_bind_group(0, &bind_group, &[]);
|
||||||
|
render_pass.draw(0..6, 0..1);
|
||||||
|
}
|
||||||
|
state.queue.submit(Some(encoder.finish()));
|
||||||
|
surface_texture.present();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
pollster::block_on(run_interactive()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fit this into the recording code somehow?
|
||||||
|
fn create_blit_pipeline(state: &WgpuState) -> (wgpu::BindGroupLayout, wgpu::RenderPipeline) {
|
||||||
|
const SHADERS: &str = r#"
|
||||||
|
@vertex
|
||||||
|
fn vs_main(@builtin(vertex_index) ix: u32) -> @builtin(position) vec4<f32> {
|
||||||
|
// Generate a full screen quad in NDCs
|
||||||
|
var vertex = vec2<f32>(-1.0, 1.0);
|
||||||
|
switch ix {
|
||||||
|
case 1u: {
|
||||||
|
vertex = vec2<f32>(-1.0, -1.0);
|
||||||
|
}
|
||||||
|
case 2u, 4u: {
|
||||||
|
vertex = vec2<f32>(1.0, -1.0);
|
||||||
|
}
|
||||||
|
case 5u: {
|
||||||
|
vertex = vec2<f32>(1.0, 1.0);
|
||||||
|
}
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
return vec4<f32>(vertex, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var fine_output: texture_2d<f32>;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(@builtin(position) pos: vec4<f32>) -> @location(0) vec4<f32> {
|
||||||
|
return textureLoad(fine_output, vec2<i32>(pos.xy), 0);
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let shader = state
|
||||||
|
.device
|
||||||
|
.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: Some("blit shaders"),
|
||||||
|
source: wgpu::ShaderSource::Wgsl(SHADERS.into()),
|
||||||
|
});
|
||||||
|
let bind_group_layout =
|
||||||
|
state
|
||||||
|
.device
|
||||||
|
.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
entries: &[wgpu::BindGroupLayoutEntry {
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
binding: 0,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
multisampled: false,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
let pipeline_layout = state
|
||||||
|
.device
|
||||||
|
.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: None,
|
||||||
|
bind_group_layouts: &[&bind_group_layout],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
let pipeline = state
|
||||||
|
.device
|
||||||
|
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: None,
|
||||||
|
layout: Some(&pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "vs_main",
|
||||||
|
buffers: &[],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: "fs_main",
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: state.surface_config.format,
|
||||||
|
blend: None,
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: Some(wgpu::Face::Back),
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
unclipped_depth: false,
|
||||||
|
conservative: false,
|
||||||
|
},
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: wgpu::MultisampleState {
|
||||||
|
count: 1,
|
||||||
|
mask: !0,
|
||||||
|
alpha_to_coverage_enabled: false,
|
||||||
|
},
|
||||||
|
multiview: None,
|
||||||
|
});
|
||||||
|
(bind_group_layout, pipeline)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use bytemuck::{Pod, Zeroable};
|
||||||
use piet_scene::Scene;
|
use piet_scene::Scene;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::{BufProxy, Recording, ResourceProxy},
|
engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy},
|
||||||
shaders::{self, FullShaders, Shaders},
|
shaders::{self, FullShaders, Shaders},
|
||||||
Dimensions,
|
Dimensions,
|
||||||
};
|
};
|
||||||
|
@ -29,6 +29,8 @@ const BIN_HEADER_SIZE: u64 = 8;
|
||||||
struct Config {
|
struct Config {
|
||||||
width_in_tiles: u32,
|
width_in_tiles: u32,
|
||||||
height_in_tiles: u32,
|
height_in_tiles: u32,
|
||||||
|
target_width: u32,
|
||||||
|
target_height: u32,
|
||||||
n_drawobj: u32,
|
n_drawobj: u32,
|
||||||
n_path: u32,
|
n_path: u32,
|
||||||
n_clip: u32,
|
n_clip: u32,
|
||||||
|
@ -76,6 +78,8 @@ fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
width_in_tiles: 64,
|
width_in_tiles: 64,
|
||||||
height_in_tiles: 64,
|
height_in_tiles: 64,
|
||||||
|
target_width: 64 * 16,
|
||||||
|
target_height: 64 * 16,
|
||||||
pathtag_base,
|
pathtag_base,
|
||||||
pathdata_base,
|
pathdata_base,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -137,7 +141,7 @@ pub fn render_full(
|
||||||
scene: &Scene,
|
scene: &Scene,
|
||||||
shaders: &FullShaders,
|
shaders: &FullShaders,
|
||||||
dimensions: &Dimensions,
|
dimensions: &Dimensions,
|
||||||
) -> (Recording, BufProxy) {
|
) -> (Recording, ResourceProxy) {
|
||||||
let mut recording = Recording::default();
|
let mut recording = Recording::default();
|
||||||
let mut ramps = crate::ramp::RampCache::default();
|
let mut ramps = crate::ramp::RampCache::default();
|
||||||
let mut drawdata_patches: Vec<(usize, u32)> = vec![];
|
let mut drawdata_patches: Vec<(usize, u32)> = vec![];
|
||||||
|
@ -153,19 +157,19 @@ pub fn render_full(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let gradient_image = if drawdata_patches.is_empty() {
|
let gradient_image = if drawdata_patches.is_empty() {
|
||||||
ResourceProxy::new_image(1, 1)
|
ResourceProxy::new_image(1, 1, ImageFormat::Rgba8)
|
||||||
} else {
|
} else {
|
||||||
let data = ramps.data();
|
let data = ramps.data();
|
||||||
let width = ramps.width();
|
let width = ramps.width();
|
||||||
let height = ramps.height();
|
let height = ramps.height();
|
||||||
let data: &[u8] = bytemuck::cast_slice(data);
|
let data: &[u8] = bytemuck::cast_slice(data);
|
||||||
println!(
|
// println!(
|
||||||
"gradient image: {}x{} ({} bytes)",
|
// "gradient image: {}x{} ({} bytes)",
|
||||||
width,
|
// width,
|
||||||
height,
|
// height,
|
||||||
data.len()
|
// data.len()
|
||||||
);
|
// );
|
||||||
ResourceProxy::Image(recording.upload_image(width, height, data))
|
ResourceProxy::Image(recording.upload_image(width, height, ImageFormat::Rgba8, data))
|
||||||
};
|
};
|
||||||
let n_pathtag = data.tag_stream.len();
|
let n_pathtag = data.tag_stream.len();
|
||||||
let pathtag_padded = align_up(n_pathtag, 4 * shaders::PATHTAG_REDUCE_WG);
|
let pathtag_padded = align_up(n_pathtag, 4 * shaders::PATHTAG_REDUCE_WG);
|
||||||
|
@ -206,12 +210,14 @@ pub fn render_full(
|
||||||
let n_clip = data.n_clip;
|
let n_clip = data.n_clip;
|
||||||
|
|
||||||
let new_width = next_multiple_of(dimensions.width, 16);
|
let new_width = next_multiple_of(dimensions.width, 16);
|
||||||
let new_height = next_multiple_of(dimensions.width, 16);
|
let new_height = next_multiple_of(dimensions.height, 16);
|
||||||
|
|
||||||
let config = Config {
|
let config = Config {
|
||||||
// TODO: Replace with div_ceil once stable
|
// TODO: Replace with div_ceil once stable
|
||||||
width_in_tiles: new_width / 16,
|
width_in_tiles: new_width / 16,
|
||||||
height_in_tiles: new_height / 16,
|
height_in_tiles: new_height / 16,
|
||||||
|
target_width: dimensions.width,
|
||||||
|
target_height: dimensions.height,
|
||||||
n_drawobj,
|
n_drawobj,
|
||||||
n_path,
|
n_path,
|
||||||
n_clip,
|
n_clip,
|
||||||
|
@ -222,7 +228,7 @@ pub fn render_full(
|
||||||
transform_base,
|
transform_base,
|
||||||
linewidth_base,
|
linewidth_base,
|
||||||
};
|
};
|
||||||
println!("{:?}", config);
|
// println!("{:?}", config);
|
||||||
let scene_buf = ResourceProxy::Buf(recording.upload(scene));
|
let scene_buf = ResourceProxy::Buf(recording.upload(scene));
|
||||||
let config_buf = ResourceProxy::Buf(recording.upload(bytemuck::bytes_of(&config).to_owned()));
|
let config_buf = ResourceProxy::Buf(recording.upload(bytemuck::bytes_of(&config).to_owned()));
|
||||||
|
|
||||||
|
@ -396,8 +402,7 @@ pub fn render_full(
|
||||||
ptcl_buf,
|
ptcl_buf,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
let out_buf_size = config.width_in_tiles * config.height_in_tiles * 1024;
|
let out_image = ImageProxy::new(dimensions.width, dimensions.height, ImageFormat::Rgba8);
|
||||||
let out_buf = BufProxy::new(out_buf_size as u64);
|
|
||||||
recording.dispatch(
|
recording.dispatch(
|
||||||
shaders.fine,
|
shaders.fine,
|
||||||
(config.width_in_tiles, config.height_in_tiles, 1),
|
(config.width_in_tiles, config.height_in_tiles, 1),
|
||||||
|
@ -405,15 +410,12 @@ pub fn render_full(
|
||||||
config_buf,
|
config_buf,
|
||||||
tile_buf,
|
tile_buf,
|
||||||
segments_buf,
|
segments_buf,
|
||||||
ResourceProxy::Buf(out_buf),
|
ResourceProxy::Image(out_image),
|
||||||
ptcl_buf,
|
ptcl_buf,
|
||||||
gradient_image,
|
gradient_image,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
(recording, ResourceProxy::Image(out_image))
|
||||||
let download_buf = out_buf;
|
|
||||||
recording.download(download_buf);
|
|
||||||
(recording, download_buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn align_up(len: usize, alignment: u32) -> usize {
|
pub fn align_up(len: usize, alignment: u32) -> usize {
|
||||||
|
|
|
@ -22,7 +22,7 @@ use std::{collections::HashSet, fs, path::Path};
|
||||||
|
|
||||||
use wgpu::Device;
|
use wgpu::Device;
|
||||||
|
|
||||||
use crate::engine::{BindType, Engine, Error, ShaderId};
|
use crate::engine::{BindType, Engine, Error, ImageFormat, ShaderId};
|
||||||
|
|
||||||
pub const PATHTAG_REDUCE_WG: u32 = 256;
|
pub const PATHTAG_REDUCE_WG: u32 = 256;
|
||||||
pub const PATH_BBOX_WG: u32 = 256;
|
pub const PATH_BBOX_WG: u32 = 256;
|
||||||
|
@ -281,9 +281,9 @@ pub fn full_shaders(device: &Device, engine: &mut Engine) -> Result<FullShaders,
|
||||||
BindType::BufReadOnly,
|
BindType::BufReadOnly,
|
||||||
BindType::BufReadOnly,
|
BindType::BufReadOnly,
|
||||||
BindType::BufReadOnly,
|
BindType::BufReadOnly,
|
||||||
BindType::Buffer,
|
BindType::Image(ImageFormat::Rgba8),
|
||||||
BindType::BufReadOnly,
|
BindType::BufReadOnly,
|
||||||
BindType::ImageRead,
|
BindType::ImageRead(ImageFormat::Rgba8),
|
||||||
],
|
],
|
||||||
)?;
|
)?;
|
||||||
Ok(FullShaders {
|
Ok(FullShaders {
|
||||||
|
|
81
piet-wgsl/src/simple_text.rs
Normal file
81
piet-wgsl/src/simple_text.rs
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
// 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_scene::glyph::{pinot, pinot::TableProvider, GlyphContext};
|
||||||
|
use piet_scene::kurbo::Affine;
|
||||||
|
use piet_scene::{Brush, SceneBuilder};
|
||||||
|
|
||||||
|
pub use pinot::FontRef;
|
||||||
|
|
||||||
|
// This is very much a hack to get things working.
|
||||||
|
// On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji
|
||||||
|
const FONT_DATA: &[u8] = include_bytes!("../../piet-gpu/third-party/Roboto-Regular.ttf");
|
||||||
|
|
||||||
|
pub struct SimpleText {
|
||||||
|
gcx: GlyphContext,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleText {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
gcx: GlyphContext::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(
|
||||||
|
&mut self,
|
||||||
|
builder: &mut SceneBuilder,
|
||||||
|
font: Option<&FontRef>,
|
||||||
|
size: f32,
|
||||||
|
brush: Option<&Brush>,
|
||||||
|
transform: Affine,
|
||||||
|
text: &str,
|
||||||
|
) {
|
||||||
|
let font = font.unwrap_or(&FontRef {
|
||||||
|
data: FONT_DATA,
|
||||||
|
offset: 0,
|
||||||
|
});
|
||||||
|
if let Some(cmap) = font.cmap() {
|
||||||
|
if let Some(hmtx) = font.hmtx() {
|
||||||
|
let upem = font.head().map(|head| head.units_per_em()).unwrap_or(1000) as f64;
|
||||||
|
let scale = size as f64 / upem;
|
||||||
|
let vars: [(pinot::types::Tag, f32); 0] = [];
|
||||||
|
let mut provider = self.gcx.new_provider(font, None, size, false, vars);
|
||||||
|
let hmetrics = hmtx.hmetrics();
|
||||||
|
let default_advance = hmetrics
|
||||||
|
.get(hmetrics.len().saturating_sub(1))
|
||||||
|
.map(|h| h.advance_width)
|
||||||
|
.unwrap_or(0);
|
||||||
|
let mut pen_x = 0f64;
|
||||||
|
for ch in text.chars() {
|
||||||
|
let gid = cmap.map(ch as u32).unwrap_or(0);
|
||||||
|
let advance = hmetrics
|
||||||
|
.get(gid as usize)
|
||||||
|
.map(|h| h.advance_width)
|
||||||
|
.unwrap_or(default_advance) as f64
|
||||||
|
* scale;
|
||||||
|
if let Some(glyph) = provider.get(gid, brush) {
|
||||||
|
let xform = transform
|
||||||
|
* Affine::translate((pen_x, 0.0))
|
||||||
|
* Affine::scale_non_uniform(1.0, -1.0);
|
||||||
|
builder.append(&glyph, Some(xform));
|
||||||
|
}
|
||||||
|
pen_x += advance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,79 +1,58 @@
|
||||||
// Copyright 2022 Google LLC
|
use super::PicoSvg;
|
||||||
//
|
use piet_scene::kurbo::{Affine, BezPath, Ellipse, PathEl, Point, Rect};
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
use piet_scene::*;
|
||||||
// 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_scene::kurbo::{Affine, Ellipse, PathEl, Point, Rect};
|
use crate::SimpleText;
|
||||||
use piet_scene::{
|
|
||||||
BlendMode, Brush, Color, Fill, LinearGradient, Mix, RadialGradient, Scene, SceneBuilder,
|
|
||||||
SceneFragment, Stroke,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::pico_svg::PicoSvg;
|
pub fn render_funky_paths(sb: &mut SceneBuilder) {
|
||||||
|
use PathEl::*;
|
||||||
pub fn gen_test_scene() -> Scene {
|
let missing_movetos = [
|
||||||
let mut scene = Scene::default();
|
LineTo((100.0, 100.0).into()),
|
||||||
let mut builder = SceneBuilder::for_scene(&mut scene);
|
LineTo((100.0, 200.0).into()),
|
||||||
let scene_ix = 1;
|
ClosePath,
|
||||||
match scene_ix {
|
LineTo((0.0, 400.0).into()),
|
||||||
0 => {
|
LineTo((100.0, 400.0).into()),
|
||||||
let path = [
|
];
|
||||||
PathEl::MoveTo(Point::new(100.0, 100.0)),
|
let only_movetos = [MoveTo((0.0, 0.0).into()), MoveTo((100.0, 100.0).into())];
|
||||||
PathEl::LineTo(Point::new(500.0, 120.0)),
|
let empty: [PathEl; 0] = [];
|
||||||
PathEl::LineTo(Point::new(300.0, 150.0)),
|
sb.fill(
|
||||||
PathEl::LineTo(Point::new(200.0, 260.0)),
|
Fill::NonZero,
|
||||||
PathEl::LineTo(Point::new(150.0, 210.0)),
|
Affine::translate((100.0, 100.0)),
|
||||||
];
|
Color::rgb8(0, 0, 255),
|
||||||
let brush = Brush::Solid(Color::rgb8(0x40, 0x40, 0xff));
|
None,
|
||||||
builder.fill(Fill::NonZero, Affine::IDENTITY, &brush, None, &path);
|
&missing_movetos,
|
||||||
let transform = Affine::translate((50.0, 50.0));
|
);
|
||||||
let brush = Brush::Solid(Color::rgba8(0xff, 0xff, 0x00, 0x80));
|
sb.fill(
|
||||||
builder.fill(Fill::NonZero, transform, &brush, None, &path);
|
Fill::NonZero,
|
||||||
let transform = Affine::translate((100.0, 100.0));
|
Affine::IDENTITY,
|
||||||
let style = Stroke::new(1.0);
|
Color::rgb8(0, 0, 255),
|
||||||
let brush = Brush::Solid(Color::rgb8(0xa0, 0x00, 0x00));
|
None,
|
||||||
builder.stroke(&style, transform, &brush, None, &path);
|
&empty,
|
||||||
}
|
);
|
||||||
1 => {
|
sb.fill(
|
||||||
render_blend_grid(&mut builder);
|
Fill::NonZero,
|
||||||
}
|
Affine::IDENTITY,
|
||||||
_ => {
|
Color::rgb8(0, 0, 255),
|
||||||
let xml_str =
|
None,
|
||||||
std::str::from_utf8(include_bytes!("../../piet-gpu/Ghostscript_Tiger.svg"))
|
&only_movetos,
|
||||||
.unwrap();
|
);
|
||||||
let svg = PicoSvg::load(xml_str, 6.0).unwrap();
|
sb.stroke(
|
||||||
render_svg(&mut builder, &svg, false);
|
&Stroke::new(8.0),
|
||||||
}
|
Affine::translate((100.0, 100.0)),
|
||||||
}
|
Color::rgb8(0, 255, 255),
|
||||||
builder.finish();
|
None,
|
||||||
scene
|
&missing_movetos,
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn dump_scene_info(scene: &Scene) {
|
|
||||||
let data = scene.data();
|
|
||||||
println!("tags {:?}", data.tag_stream);
|
|
||||||
println!(
|
|
||||||
"pathsegs {:?}",
|
|
||||||
bytemuck::cast_slice::<u8, f32>(&data.pathseg_stream)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
const N_CIRCLES: usize = 0;
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
||||||
use crate::pico_svg::*;
|
use crate::pico_svg::*;
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
for item in svg.items.iter() {
|
for item in &svg.items {
|
||||||
match item {
|
match item {
|
||||||
Item::Fill(fill) => {
|
Item::Fill(fill) => {
|
||||||
sb.fill(
|
sb.fill(
|
||||||
|
@ -100,6 +79,117 @@ pub fn render_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn render_tiger(sb: &mut SceneBuilder, print_stats: bool) {
|
||||||
|
use super::pico_svg::*;
|
||||||
|
let xml_str =
|
||||||
|
std::str::from_utf8(include_bytes!("../../piet-gpu/Ghostscript_Tiger.svg")).unwrap();
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
let svg = PicoSvg::load(xml_str, 6.0).unwrap();
|
||||||
|
if print_stats {
|
||||||
|
println!("parsing time: {:?}", start.elapsed());
|
||||||
|
}
|
||||||
|
render_svg(sb, &svg, print_stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_scene(sb: &mut SceneBuilder) {
|
||||||
|
render_cardioid(sb);
|
||||||
|
render_clip_test(sb);
|
||||||
|
render_alpha_test(sb);
|
||||||
|
//render_tiger(sb, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn render_cardioid(sb: &mut SceneBuilder) {
|
||||||
|
let n = 601;
|
||||||
|
let dth = std::f64::consts::PI * 2.0 / (n as f64);
|
||||||
|
let center = Point::new(1024.0, 768.0);
|
||||||
|
let r = 750.0;
|
||||||
|
let mut path = BezPath::new();
|
||||||
|
for i in 1..n {
|
||||||
|
let mut p0 = center;
|
||||||
|
let a0 = i as f64 * dth;
|
||||||
|
p0.x += a0.cos() * r;
|
||||||
|
p0.y += a0.sin() * r;
|
||||||
|
let mut p1 = center;
|
||||||
|
let a1 = ((i * 2) % n) as f64 * dth;
|
||||||
|
p1.x += a1.cos() * r;
|
||||||
|
p1.y += a1.sin() * r;
|
||||||
|
path.push(PathEl::MoveTo(p0));
|
||||||
|
path.push(PathEl::LineTo(p1));
|
||||||
|
}
|
||||||
|
sb.stroke(
|
||||||
|
&Stroke::new(2.0),
|
||||||
|
Affine::IDENTITY,
|
||||||
|
Color::rgb8(0, 0, 0),
|
||||||
|
None,
|
||||||
|
&path,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn render_clip_test(sb: &mut SceneBuilder) {
|
||||||
|
const N: usize = 16;
|
||||||
|
const X0: f64 = 50.0;
|
||||||
|
const Y0: f64 = 450.0;
|
||||||
|
// Note: if it gets much larger, it will exceed the 1MB scratch buffer.
|
||||||
|
// But this is a pretty demanding test.
|
||||||
|
const X1: f64 = 550.0;
|
||||||
|
const Y1: f64 = 950.0;
|
||||||
|
let step = 1.0 / ((N + 1) as f64);
|
||||||
|
for i in 0..N {
|
||||||
|
let t = ((i + 1) as f64) * step;
|
||||||
|
let path = [
|
||||||
|
PathEl::MoveTo((X0, Y0).into()),
|
||||||
|
PathEl::LineTo((X1, Y0).into()),
|
||||||
|
PathEl::LineTo((X1, Y0 + t * (Y1 - Y0)).into()),
|
||||||
|
PathEl::LineTo((X1 + t * (X0 - X1), Y1).into()),
|
||||||
|
PathEl::LineTo((X0, Y1).into()),
|
||||||
|
PathEl::ClosePath,
|
||||||
|
];
|
||||||
|
sb.push_layer(Mix::Clip, Affine::IDENTITY, &path);
|
||||||
|
}
|
||||||
|
let rect = Rect::new(X0, Y0, X1, Y1);
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Brush::Solid(Color::rgb8(0, 0, 0)),
|
||||||
|
None,
|
||||||
|
&rect,
|
||||||
|
);
|
||||||
|
for _ in 0..N {
|
||||||
|
sb.pop_layer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn render_alpha_test(sb: &mut SceneBuilder) {
|
||||||
|
// Alpha compositing tests.
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
Color::rgb8(255, 0, 0),
|
||||||
|
None,
|
||||||
|
&make_diamond(1024.0, 100.0),
|
||||||
|
);
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
Color::rgba8(0, 255, 0, 0x80),
|
||||||
|
None,
|
||||||
|
&make_diamond(1024.0, 125.0),
|
||||||
|
);
|
||||||
|
sb.push_layer(Mix::Clip, Affine::IDENTITY, &make_diamond(1024.0, 150.0));
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
Color::rgba8(0, 0, 255, 0x80),
|
||||||
|
None,
|
||||||
|
&make_diamond(1024.0, 175.0),
|
||||||
|
);
|
||||||
|
sb.pop_layer();
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
pub fn render_blend_grid(sb: &mut SceneBuilder) {
|
||||||
const BLEND_MODES: &[Mix] = &[
|
const BLEND_MODES: &[Mix] = &[
|
||||||
|
@ -181,3 +271,83 @@ fn blend_square(blend: BlendMode) -> SceneFragment {
|
||||||
sb.finish();
|
sb.finish();
|
||||||
fragment
|
fragment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn render_anim_frame(sb: &mut SceneBuilder, text: &mut SimpleText, i: usize) {
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Brush::Solid(Color::rgb8(128, 128, 128)),
|
||||||
|
None,
|
||||||
|
&Rect::from_origin_size(Point::new(0.0, 0.0), (1000.0, 1000.0)),
|
||||||
|
);
|
||||||
|
let text_size = 60.0 + 40.0 * (0.01 * i as f32).sin();
|
||||||
|
let s = "\u{1f600}hello piet-gpu text!";
|
||||||
|
text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
text_size,
|
||||||
|
None,
|
||||||
|
Affine::translate((110.0, 600.0)),
|
||||||
|
s,
|
||||||
|
);
|
||||||
|
text.add(
|
||||||
|
sb,
|
||||||
|
None,
|
||||||
|
text_size,
|
||||||
|
None,
|
||||||
|
Affine::translate((110.0, 700.0)),
|
||||||
|
s,
|
||||||
|
);
|
||||||
|
let th = (std::f64::consts::PI / 180.0) * (i as f64);
|
||||||
|
let center = Point::new(500.0, 500.0);
|
||||||
|
let mut p1 = center;
|
||||||
|
p1.x += 400.0 * th.cos();
|
||||||
|
p1.y += 400.0 * th.sin();
|
||||||
|
sb.stroke(
|
||||||
|
&Stroke::new(5.0),
|
||||||
|
Affine::IDENTITY,
|
||||||
|
&Brush::Solid(Color::rgb8(128, 0, 0)),
|
||||||
|
None,
|
||||||
|
&&[PathEl::MoveTo(center), PathEl::LineTo(p1)][..],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
pub fn render_brush_transform(sb: &mut SceneBuilder, i: usize) {
|
||||||
|
let th = (std::f64::consts::PI / 180.0) * (i as f64);
|
||||||
|
let linear = LinearGradient::new((0.0, 0.0), (0.0, 200.0)).stops([
|
||||||
|
Color::RED,
|
||||||
|
Color::GREEN,
|
||||||
|
Color::BLUE,
|
||||||
|
]);
|
||||||
|
sb.fill(
|
||||||
|
Fill::NonZero,
|
||||||
|
Affine::translate((200.0, 200.0)),
|
||||||
|
&linear,
|
||||||
|
Some(around_center(Affine::rotate(th), Point::new(200.0, 100.0))),
|
||||||
|
&Rect::from_origin_size(Point::default(), (400.0, 200.0)),
|
||||||
|
);
|
||||||
|
sb.stroke(
|
||||||
|
&Stroke::new(40.0),
|
||||||
|
Affine::translate((800.0, 200.0)),
|
||||||
|
&linear,
|
||||||
|
Some(around_center(Affine::rotate(th), Point::new(200.0, 100.0))),
|
||||||
|
&Rect::from_origin_size(Point::default(), (400.0, 200.0)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn around_center(xform: Affine, center: Point) -> Affine {
|
||||||
|
Affine::translate(center.to_vec2()) * xform * Affine::translate(-center.to_vec2())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_diamond(cx: f64, cy: f64) -> [PathEl; 5] {
|
||||||
|
const SIZE: f64 = 50.0;
|
||||||
|
[
|
||||||
|
PathEl::MoveTo(Point::new(cx, cy - SIZE)),
|
||||||
|
PathEl::LineTo(Point::new(cx + SIZE, cy)),
|
||||||
|
PathEl::LineTo(Point::new(cx, cy + SIZE)),
|
||||||
|
PathEl::LineTo(Point::new(cx - SIZE, cy)),
|
||||||
|
PathEl::ClosePath,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue