merge from dev branch - dev
diff --git a/Cargo.lock b/Cargo.lock
index eaddf11..1af69e3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -20,15 +20,6 @@
]
[[package]]
-name = "aho-corasick"
-version = "0.7.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -507,19 +498,6 @@
]
[[package]]
-name = "env_logger"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c90bf5f19754d10198ccb95b70664fc925bd1fc090a0fd9a6ebc54acc8cd6272"
-dependencies = [
- "atty",
- "humantime",
- "log",
- "regex",
- "termcolor",
-]
-
-[[package]]
name = "expat-sys"
version = "2.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -757,12 +735,6 @@
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
-[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1236,7 +1208,7 @@
"raw-window-handle 0.3.4",
"raw-window-handle 0.5.0",
"roxmltree",
- "winit",
+ "winit 0.27.5",
]
[[package]]
@@ -1303,15 +1275,11 @@
version = "0.1.0"
dependencies = [
"bytemuck",
- "env_logger",
"futures-intrusive",
"parking_lot",
"piet-scene",
- "png",
- "pollster",
- "roxmltree",
+ "raw-window-handle 0.5.0",
"wgpu",
- "winit",
]
[[package]]
@@ -1521,23 +1489,6 @@
]
[[package]]
-name = "regex"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
-
-[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2253,6 +2204,17 @@
[[package]]
name = "winit"
+version = "0.1.0"
+dependencies = [
+ "piet-scene",
+ "piet-wgsl",
+ "pollster",
+ "roxmltree",
+ "winit 0.27.5",
+]
+
+[[package]]
+name = "winit"
version = "0.27.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c"
diff --git a/Cargo.toml b/Cargo.toml
index dc40744..8d022fb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,7 @@
"piet-gpu-types",
"piet-scene",
"piet-wgsl",
+ "piet-wgsl/examples/winit",
"tests",
]
diff --git a/piet-gpu/src/samples.rs b/piet-gpu/src/samples.rs
index fd6ea6b..e4f7bc8 100644
--- a/piet-gpu/src/samples.rs
+++ b/piet-gpu/src/samples.rs
@@ -82,7 +82,10 @@
#[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!("../Ghostscript_Tiger.svg")).unwrap();
+ let xml_str = std::str::from_utf8(include_bytes!(
+ "../../piet-wgsl/examples/assets/Ghostscript_Tiger.svg"
+ ))
+ .unwrap();
let start = std::time::Instant::now();
let svg = PicoSvg::load(xml_str, 8.0).unwrap();
if print_stats {
diff --git a/piet-gpu/src/simple_text.rs b/piet-gpu/src/simple_text.rs
index 00eefaa..674099e 100644
--- a/piet-gpu/src/simple_text.rs
+++ b/piet-gpu/src/simple_text.rs
@@ -22,7 +22,8 @@
// 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!("../third-party/Roboto-Regular.ttf");
+const FONT_DATA: &[u8] =
+ include_bytes!("../../piet-wgsl/examples/assets/third-party/Roboto-Regular.ttf");
pub struct SimpleText {
gcx: GlyphContext,
diff --git a/piet-wgsl/Cargo.toml b/piet-wgsl/Cargo.toml
index d10aa41..d74eb15 100644
--- a/piet-wgsl/Cargo.toml
+++ b/piet-wgsl/Cargo.toml
@@ -7,17 +7,8 @@
[dependencies]
wgpu = "0.14"
-env_logger = "0.9.1"
-pollster = "0.2.5"
+raw-window-handle = "0.5"
futures-intrusive = "0.5.0"
parking_lot = "0.12"
bytemuck = { version = "1.12.1", features = ["derive"] }
-png = "0.17.6"
-
piet-scene = { path = "../piet-scene" }
-
-# for picosvg, should be split out
-roxmltree = "0.13"
-
-# move this to an example
-winit = "0.27.5"
diff --git a/piet-gpu/Ghostscript_Tiger.svg b/piet-wgsl/examples/assets/Ghostscript_Tiger.svg
similarity index 100%
rename from piet-gpu/Ghostscript_Tiger.svg
rename to piet-wgsl/examples/assets/Ghostscript_Tiger.svg
diff --git a/piet-gpu/third-party/LICENSE.txt b/piet-wgsl/examples/assets/third-party/LICENSE.txt
similarity index 99%
rename from piet-gpu/third-party/LICENSE.txt
rename to piet-wgsl/examples/assets/third-party/LICENSE.txt
index 75b5248..d645695 100644
--- a/piet-gpu/third-party/LICENSE.txt
+++ b/piet-wgsl/examples/assets/third-party/LICENSE.txt
@@ -1,202 +1,202 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- 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
-
- http://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.
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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
+
+ http://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.
diff --git a/piet-gpu/third-party/Roboto-Regular.ttf b/piet-wgsl/examples/assets/third-party/Roboto-Regular.ttf
similarity index 100%
rename from piet-gpu/third-party/Roboto-Regular.ttf
rename to piet-wgsl/examples/assets/third-party/Roboto-Regular.ttf
Binary files differ
diff --git a/piet-wgsl/examples/winit/Cargo.toml b/piet-wgsl/examples/winit/Cargo.toml
new file mode 100644
index 0000000..e112839
--- /dev/null
+++ b/piet-wgsl/examples/winit/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "winit"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+piet-wgsl = { path = "../../../piet-wgsl" }
+piet-scene = { path = "../../../piet-scene" }
+winit = "0.27.5"
+pollster = "0.2.5"
+# for picosvg
+roxmltree = "0.13"
diff --git a/piet-wgsl/examples/winit/src/main.rs b/piet-wgsl/examples/winit/src/main.rs
new file mode 100644
index 0000000..ba6226c
--- /dev/null
+++ b/piet-wgsl/examples/winit/src/main.rs
@@ -0,0 +1,106 @@
+// Copyright 2022 Google LLC
+//
+// 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 pico_svg;
+mod simple_text;
+mod test_scene;
+
+use piet_scene::{Scene, SceneBuilder};
+use piet_wgsl::{util::RenderContext, Renderer, Result};
+
+async fn run() -> Result<()> {
+ 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 render_cx = RenderContext::new().await?;
+ let size = window.inner_size();
+ let mut surface = render_cx.create_surface(&window, size.width, size.height);
+ let mut renderer = Renderer::new(&render_cx.device)?;
+ let mut simple_text = simple_text::SimpleText::new();
+ let mut current_frame = 0usize;
+ let mut scene_ix = 0usize;
+ let mut scene = Scene::default();
+ 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) => {
+ render_cx.resize_surface(&mut surface, size.width, size.height);
+ window.request_redraw();
+ }
+ _ => {}
+ },
+ Event::MainEventsCleared => {
+ window.request_redraw();
+ }
+ Event::RedrawRequested(_) => {
+ current_frame += 1;
+ let width = surface.config.width;
+ let height = surface.config.height;
+ 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 surface_texture = surface
+ .surface
+ .get_current_texture()
+ .expect("failed to get surface texture");
+ renderer
+ .render_to_surface(
+ &render_cx.device,
+ &render_cx.queue,
+ &scene,
+ &surface_texture,
+ width,
+ height,
+ )
+ .expect("failed to render to surface");
+ surface_texture.present();
+ }
+ _ => {}
+ });
+}
+
+fn main() {
+ pollster::block_on(run()).unwrap();
+}
diff --git a/piet-wgsl/src/pico_svg.rs b/piet-wgsl/examples/winit/src/pico_svg.rs
similarity index 100%
rename from piet-wgsl/src/pico_svg.rs
rename to piet-wgsl/examples/winit/src/pico_svg.rs
diff --git a/piet-wgsl/src/simple_text.rs b/piet-wgsl/examples/winit/src/simple_text.rs
similarity index 96%
rename from piet-wgsl/src/simple_text.rs
rename to piet-wgsl/examples/winit/src/simple_text.rs
index 9b2d4c8..f35ef56 100644
--- a/piet-wgsl/src/simple_text.rs
+++ b/piet-wgsl/examples/winit/src/simple_text.rs
@@ -22,7 +22,7 @@
// 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");
+const FONT_DATA: &[u8] = include_bytes!("../../assets/third-party/Roboto-Regular.ttf");
pub struct SimpleText {
gcx: GlyphContext,
diff --git a/piet-wgsl/src/test_scene.rs b/piet-wgsl/examples/winit/src/test_scene.rs
similarity index 98%
rename from piet-wgsl/src/test_scene.rs
rename to piet-wgsl/examples/winit/src/test_scene.rs
index d5a744d..4c90829 100644
--- a/piet-wgsl/src/test_scene.rs
+++ b/piet-wgsl/examples/winit/src/test_scene.rs
@@ -1,9 +1,8 @@
-use super::PicoSvg;
+use crate::pico_svg::PicoSvg;
+use crate::simple_text::SimpleText;
use piet_scene::kurbo::{Affine, BezPath, Ellipse, PathEl, Point, Rect};
use piet_scene::*;
-use crate::SimpleText;
-
pub fn render_funky_paths(sb: &mut SceneBuilder) {
use PathEl::*;
let missing_movetos = [
@@ -83,7 +82,7 @@
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();
+ std::str::from_utf8(include_bytes!("../../assets/Ghostscript_Tiger.svg")).unwrap();
let start = std::time::Instant::now();
let svg = PicoSvg::load(xml_str, 6.0).unwrap();
if print_stats {
diff --git a/piet-wgsl/src/debug.rs b/piet-wgsl/src/debug.rs
deleted file mode 100644
index 964c8ee..0000000
--- a/piet-wgsl/src/debug.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#![allow(dead_code)]
-
-pub mod clip;
-pub mod draw;
-pub mod fine;
diff --git a/piet-wgsl/src/debug/clip.rs b/piet-wgsl/src/debug/clip.rs
deleted file mode 100644
index 8b2a0e3..0000000
--- a/piet-wgsl/src/debug/clip.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use bytemuck::{Pod, Zeroable};
-
-#[derive(Copy, Clone, Debug, Zeroable, Pod)]
-#[repr(C)]
-pub struct ClipEl {
- pub parent_ix: u32,
- pub pad: [u32; 3],
- pub bbox: [f32; 4],
-}
-
-pub fn parse_clip_els(data: &[u8]) -> Vec<ClipEl> {
- Vec::from(bytemuck::cast_slice(data))
-}
diff --git a/piet-wgsl/src/debug/draw.rs b/piet-wgsl/src/debug/draw.rs
deleted file mode 100644
index ab56ed5..0000000
--- a/piet-wgsl/src/debug/draw.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-use bytemuck::{Pod, Zeroable};
-
-#[derive(Copy, Clone, Debug, Zeroable, Pod)]
-#[repr(C)]
-pub struct DrawMonoid {
- pub path_ix: u32,
- pub clip_ix: u32,
- pub scene_offset: u32,
- pub info_offset: u32,
-}
-
-pub fn parse_draw_monoids(data: &[u8]) -> Vec<DrawMonoid> {
- Vec::from(bytemuck::cast_slice(data))
-}
diff --git a/piet-wgsl/src/debug/fine.rs b/piet-wgsl/src/debug/fine.rs
deleted file mode 100644
index d9f05f0..0000000
--- a/piet-wgsl/src/debug/fine.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct Fill {
- pub tile: u32,
- pub backdrop: i32,
-}
-
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct Stroke {
- pub tile: u32,
- pub half_width: f32,
-}
-
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct Color {
- abgr: [u8; 4],
-}
-
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct LinGrad {
- pub index: u32,
- pub line_x: f32,
- pub line_y: f32,
- pub line_c: f32,
-}
-
-#[derive(Copy, Clone, Debug)]
-#[repr(C)]
-pub struct RadGrad {
- pub index: u32,
- pub matrix: [f32; 4],
- pub xlat: [f32; 2],
- pub c1: [f32; 2],
- pub ra: f32,
- pub roff: f32,
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum Command {
- Fill(Fill),
- Stroke(Stroke),
- Solid,
- Color(Color),
- LinGrad(LinGrad),
- RadGrad(RadGrad),
- BeginClip,
- EndClip(u32),
- End,
-}
-
-const PTCL_INITIAL_ALLOC: usize = 64;
-
-#[derive(Debug)]
-pub struct CommandList {
- pub tiles: Vec<(u32, u32, Vec<Command>)>,
-}
-
-impl CommandList {
- pub fn parse(width: usize, height: usize, ptcl: &[u8]) -> Self {
- let mut tiles = vec![];
- let width_tiles = width / 16;
- let height_tiles = height / 16;
- for y in 0..height_tiles {
- for x in 0..width_tiles {
- let tile_ix = y * width_tiles + x;
- let ix = tile_ix * PTCL_INITIAL_ALLOC;
- let commands = parse_commands(ptcl, ix);
- if !commands.is_empty() {
- tiles.push((x as u32, y as u32, commands));
- }
- }
- }
- Self { tiles }
- }
-}
-
-fn parse_commands(ptcl: &[u8], mut ix: usize) -> Vec<Command> {
- let mut commands = vec![];
- let words: &[u32] = bytemuck::cast_slice(ptcl);
- while ix < words.len() {
- let tag = words[ix];
- ix += 1;
- match tag {
- 0 => break,
- 1 => {
- commands.push(Command::Fill(Fill {
- tile: words[ix],
- backdrop: words[ix + 1] as i32,
- }));
- ix += 2;
- }
- 2 => {
- commands.push(Command::Stroke(Stroke {
- tile: words[ix],
- half_width: bytemuck::cast(words[ix + 1]),
- }));
- ix += 2;
- }
- 3 => {
- commands.push(Command::Solid);
- }
- 5 => {
- commands.push(Command::Color(Color {
- abgr: bytemuck::cast(words[ix]),
- }));
- ix += 1;
- }
- 6 => {
- commands.push(Command::LinGrad(LinGrad {
- index: words[ix],
- line_x: bytemuck::cast(words[ix + 1]),
- line_y: bytemuck::cast(words[ix + 2]),
- line_c: bytemuck::cast(words[ix + 3]),
- }));
- ix += 4;
- }
- 7 => {
- let matrix = [
- bytemuck::cast(words[ix + 1]),
- bytemuck::cast(words[ix + 2]),
- bytemuck::cast(words[ix + 3]),
- bytemuck::cast(words[ix + 4]),
- ];
- let xlat = [bytemuck::cast(words[ix + 5]), bytemuck::cast(words[ix + 6])];
- let c1 = [bytemuck::cast(words[ix + 7]), bytemuck::cast(words[ix + 8])];
- commands.push(Command::RadGrad(RadGrad {
- index: words[ix],
- matrix,
- xlat,
- c1,
- ra: bytemuck::cast(words[ix + 9]),
- roff: bytemuck::cast(words[ix + 10]),
- }));
- ix += 11;
- }
- 9 => {
- commands.push(Command::BeginClip);
- }
- 10 => {
- commands.push(Command::EndClip(words[ix]));
- ix += 1;
- }
- 11 => {
- ix = words[ix] as usize;
- }
- _ => {}
- }
- }
- commands
-}
diff --git a/piet-wgsl/src/lib.rs b/piet-wgsl/src/lib.rs
new file mode 100644
index 0000000..59641eb
--- /dev/null
+++ b/piet-wgsl/src/lib.rs
@@ -0,0 +1,274 @@
+// Copyright 2022 Google LLC
+//
+// 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 engine;
+mod ramp;
+mod render;
+mod shaders;
+
+pub mod util;
+
+use engine::{Engine, ExternalResource};
+use shaders::FullShaders;
+
+use piet_scene::Scene;
+use wgpu::{Device, Queue, SurfaceTexture, Texture, TextureFormat, TextureView};
+
+/// Catch-all error type.
+pub type Error = Box<dyn std::error::Error>;
+
+/// Specialization of `Result` for our catch-all error type.
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Renders a scene into a texture or surface.
+pub struct Renderer {
+ engine: Engine,
+ shaders: FullShaders,
+ blit: BlitPipeline,
+ target: Option<TargetTexture>,
+}
+
+impl Renderer {
+ /// Creates a new renderer for the specified device.
+ pub fn new(device: &Device) -> Result<Self> {
+ let mut engine = Engine::new();
+ let shaders = shaders::full_shaders(device, &mut engine)?;
+ let blit = BlitPipeline::new(device, TextureFormat::Bgra8Unorm);
+ Ok(Self {
+ engine,
+ shaders,
+ blit,
+ target: None,
+ })
+ }
+
+ /// Renders a scene to the target texture.
+ ///
+ /// The texture is assumed to be of the specified dimensions and have been created with
+ /// the [wgpu::TextureFormat::Rgba8Unorm] format and the [wgpu::TextureUsages::STORAGE_BINDING]
+ /// flag set.
+ pub fn render_to_texture(
+ &mut self,
+ device: &Device,
+ queue: &Queue,
+ scene: &Scene,
+ texture: &TextureView,
+ width: u32,
+ height: u32,
+ ) -> Result<()> {
+ let (recording, target) = render::render_full(&scene, &self.shaders, width, height);
+ let external_resources = [ExternalResource::Image(
+ *target.as_image().unwrap(),
+ texture,
+ )];
+ let _ = self
+ .engine
+ .run_recording(device, queue, &recording, &external_resources)?;
+ Ok(())
+ }
+
+ /// Renders a scene to the target surface.
+ ///
+ /// This renders to an intermediate texture and then runs a render pass to blit to the
+ /// specified surface texture.
+ ///
+ /// The surface is assumed to be of the specified dimensions and have been created with the
+ /// [wgpu::TextureFormat::Bgra8Unorm] format.
+ pub fn render_to_surface(
+ &mut self,
+ device: &Device,
+ queue: &Queue,
+ scene: &Scene,
+ surface: &SurfaceTexture,
+ width: u32,
+ height: u32,
+ ) -> Result<()> {
+ let mut target = self
+ .target
+ .take()
+ .unwrap_or_else(|| TargetTexture::new(device, width, height));
+ // TODO: implement clever resizing semantics here to avoid thrashing the memory allocator
+ // during resize, specifically on metal.
+ if target.width != width || target.height != height {
+ target = TargetTexture::new(device, width, height);
+ }
+ self.render_to_texture(device, queue, scene, &target.view, width, height)?;
+ let mut encoder =
+ device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+ {
+ let surface_view = surface
+ .texture
+ .create_view(&wgpu::TextureViewDescriptor::default());
+ let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
+ label: None,
+ layout: &self.blit.bind_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(&self.blit.pipeline);
+ render_pass.set_bind_group(0, &bind_group, &[]);
+ render_pass.draw(0..6, 0..1);
+ }
+ queue.submit(Some(encoder.finish()));
+ self.target = Some(target);
+ Ok(())
+ }
+}
+
+struct TargetTexture {
+ texture: Texture,
+ view: TextureView,
+ width: u32,
+ height: u32,
+}
+
+impl TargetTexture {
+ pub fn new(device: &Device, width: u32, height: u32) -> Self {
+ let texture = device.create_texture(&wgpu::TextureDescriptor {
+ label: None,
+ size: wgpu::Extent3d {
+ width,
+ 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());
+ Self {
+ texture,
+ view,
+ width,
+ height,
+ }
+ }
+}
+
+struct BlitPipeline {
+ bind_layout: wgpu::BindGroupLayout,
+ pipeline: wgpu::RenderPipeline,
+}
+
+impl BlitPipeline {
+ fn new(device: &Device, format: TextureFormat) -> Self {
+ 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 = device.create_shader_module(wgpu::ShaderModuleDescriptor {
+ label: Some("blit shaders"),
+ source: wgpu::ShaderSource::Wgsl(SHADERS.into()),
+ });
+ let bind_layout = 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 = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
+ label: None,
+ bind_group_layouts: &[&bind_layout],
+ push_constant_ranges: &[],
+ });
+ let pipeline = 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,
+ 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,
+ });
+ Self {
+ bind_layout,
+ pipeline,
+ }
+ }
+}
diff --git a/piet-wgsl/src/main.rs b/piet-wgsl/src/main.rs
deleted file mode 100644
index 8d735d4..0000000
--- a/piet-wgsl/src/main.rs
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright 2022 Google LLC
-//
-// 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.
-
-//! A simple application to run a compute shader.
-
-use engine::{Engine, Error, ExternalResource};
-
-use piet_scene::{Scene, SceneBuilder};
-use wgpu::{Device, Instance, Limits, Queue, Surface, SurfaceConfiguration};
-use winit::window::Window;
-
-mod debug;
-mod engine;
-mod pico_svg;
-mod ramp;
-mod render;
-mod shaders;
-mod simple_text;
-mod test_scene;
-
-use pico_svg::PicoSvg;
-use simple_text::SimpleText;
-
-pub struct Dimensions {
- width: u32,
- height: u32,
-}
-
-pub struct WgpuState {
- pub instance: Instance,
- pub device: Device,
- pub queue: Queue,
- pub surface: Option<Surface>,
- pub surface_config: SurfaceConfiguration,
-}
-
-impl WgpuState {
- pub async fn new(window: Option<&Window>) -> Result<Self, Box<dyn std::error::Error>> {
- let instance = Instance::new(wgpu::Backends::PRIMARY);
- 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)
-}
diff --git a/piet-wgsl/src/render.rs b/piet-wgsl/src/render.rs
index e747d41..aef46c1 100644
--- a/piet-wgsl/src/render.rs
+++ b/piet-wgsl/src/render.rs
@@ -6,7 +6,6 @@
use crate::{
engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy},
shaders::{self, FullShaders, Shaders},
- Dimensions,
};
const TAG_MONOID_SIZE: u64 = 12;
@@ -62,6 +61,7 @@
}
}
+#[allow(unused)]
fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) {
let mut recording = Recording::default();
let data = scene.data();
@@ -140,7 +140,8 @@
pub fn render_full(
scene: &Scene,
shaders: &FullShaders,
- dimensions: &Dimensions,
+ width: u32,
+ height: u32,
) -> (Recording, ResourceProxy) {
let mut recording = Recording::default();
let mut ramps = crate::ramp::RampCache::default();
@@ -209,15 +210,15 @@
let n_drawobj = n_path;
let n_clip = data.n_clip;
- let new_width = next_multiple_of(dimensions.width, 16);
- let new_height = next_multiple_of(dimensions.height, 16);
+ let new_width = next_multiple_of(width, 16);
+ let new_height = next_multiple_of(height, 16);
let config = Config {
// TODO: Replace with div_ceil once stable
width_in_tiles: new_width / 16,
height_in_tiles: new_height / 16,
- target_width: dimensions.width,
- target_height: dimensions.height,
+ target_width: width,
+ target_height: height,
n_drawobj,
n_path,
n_clip,
@@ -402,7 +403,7 @@
ptcl_buf,
],
);
- let out_image = ImageProxy::new(dimensions.width, dimensions.height, ImageFormat::Rgba8);
+ let out_image = ImageProxy::new(width, height, ImageFormat::Rgba8);
recording.dispatch(
shaders.fine,
(config.width_in_tiles, config.height_in_tiles, 1),
diff --git a/piet-wgsl/src/shaders.rs b/piet-wgsl/src/shaders.rs
index a011c58..8cb6129 100644
--- a/piet-wgsl/src/shaders.rs
+++ b/piet-wgsl/src/shaders.rs
@@ -18,7 +18,7 @@
mod preprocess;
-use std::{collections::HashSet, fs, path::Path};
+use std::collections::HashSet;
use wgpu::Device;
@@ -30,6 +30,12 @@
pub const PATH_DRAWOBJ_WG: u32 = 256;
pub const CLIP_REDUCE_WG: u32 = 256;
+macro_rules! shader {
+ ($name:expr) => {
+ include_str!(concat!(concat!("../shader/", $name), ".wgsl"))
+ };
+}
+
pub struct Shaders {
pub pathtag_reduce: ShaderId,
pub pathtag_scan: ShaderId,
@@ -57,14 +63,14 @@
}
pub fn init_shaders(device: &Device, engine: &mut Engine) -> Result<Shaders, Error> {
- let shader_dir = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/shader"));
- let imports = preprocess::get_imports(shader_dir);
- let read_shader =
- |path: &str| fs::read_to_string(shader_dir.join(path.to_string() + ".wgsl")).unwrap();
+ let imports = SHARED_SHADERS
+ .iter()
+ .copied()
+ .collect::<std::collections::HashMap<_, _>>();
let empty = HashSet::new();
let pathtag_reduce = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("pathtag_reduce"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("pathtag_reduce"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -73,7 +79,7 @@
)?;
let pathtag_scan = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("pathtag_scan"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("pathtag_scan"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -86,7 +92,7 @@
let path_coarse = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("path_coarse"), &path_coarse_config, &imports).into(),
+ preprocess::preprocess(shader!("path_coarse"), &path_coarse_config, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -97,12 +103,12 @@
)?;
let backdrop = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("backdrop"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("backdrop"), &empty, &imports).into(),
&[BindType::BufReadOnly, BindType::Buffer],
)?;
let fine = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("fine"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("fine"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -120,16 +126,16 @@
}
pub fn full_shaders(device: &Device, engine: &mut Engine) -> Result<FullShaders, Error> {
- let shader_dir = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/shader"));
- let imports = preprocess::get_imports(shader_dir);
- let read_shader =
- |path: &str| fs::read_to_string(shader_dir.join(path.to_string() + ".wgsl")).unwrap();
+ let imports = SHARED_SHADERS
+ .iter()
+ .copied()
+ .collect::<std::collections::HashMap<_, _>>();
let empty = HashSet::new();
let mut full_config = HashSet::new();
full_config.insert("full".into());
let pathtag_reduce = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("pathtag_reduce"), &full_config, &imports).into(),
+ preprocess::preprocess(shader!("pathtag_reduce"), &full_config, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -138,7 +144,7 @@
)?;
let pathtag_scan = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("pathtag_scan"), &full_config, &imports).into(),
+ preprocess::preprocess(shader!("pathtag_scan"), &full_config, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -148,12 +154,12 @@
)?;
let bbox_clear = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("bbox_clear"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("bbox_clear"), &empty, &imports).into(),
&[BindType::BufReadOnly, BindType::Buffer],
)?;
let pathseg = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("pathseg"), &full_config, &imports).into(),
+ preprocess::preprocess(shader!("pathseg"), &full_config, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -164,7 +170,7 @@
)?;
let draw_reduce = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("draw_reduce"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("draw_reduce"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -173,7 +179,7 @@
)?;
let draw_leaf = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("draw_leaf"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("draw_leaf"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -186,7 +192,7 @@
)?;
let clip_reduce = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("clip_reduce"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("clip_reduce"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -197,7 +203,7 @@
)?;
let clip_leaf = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("clip_leaf"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("clip_leaf"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -210,7 +216,7 @@
)?;
let binning = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("binning"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("binning"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -224,7 +230,7 @@
)?;
let tile_alloc = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("tile_alloc"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("tile_alloc"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -237,7 +243,7 @@
let path_coarse = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("path_coarse_full"), &full_config, &imports).into(),
+ preprocess::preprocess(shader!("path_coarse_full"), &full_config, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -251,7 +257,7 @@
)?;
let backdrop = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("backdrop_dyn"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("backdrop_dyn"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -260,7 +266,7 @@
)?;
let coarse = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("coarse"), &empty, &imports).into(),
+ preprocess::preprocess(shader!("coarse"), &empty, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -276,7 +282,7 @@
)?;
let fine = engine.add_shader(
device,
- preprocess::preprocess(&read_shader("fine"), &full_config, &imports).into(),
+ preprocess::preprocess(shader!("fine"), &full_config, &imports).into(),
&[
BindType::BufReadOnly,
BindType::BufReadOnly,
@@ -303,3 +309,27 @@
fine,
})
}
+
+macro_rules! shared_shader {
+ ($name:expr) => {
+ (
+ $name,
+ include_str!(concat!(concat!("../shader/shared/", $name), ".wgsl")),
+ )
+ };
+}
+
+const SHARED_SHADERS: &[(&str, &str)] = &[
+ shared_shader!("bbox"),
+ shared_shader!("blend"),
+ shared_shader!("bump"),
+ shared_shader!("clip"),
+ shared_shader!("config"),
+ shared_shader!("cubic"),
+ shared_shader!("bbox"),
+ shared_shader!("drawtag"),
+ shared_shader!("pathtag"),
+ shared_shader!("ptcl"),
+ shared_shader!("segment"),
+ shared_shader!("tile"),
+];
diff --git a/piet-wgsl/src/shaders/preprocess.rs b/piet-wgsl/src/shaders/preprocess.rs
index 8da2e28..ac731c2 100644
--- a/piet-wgsl/src/shaders/preprocess.rs
+++ b/piet-wgsl/src/shaders/preprocess.rs
@@ -34,11 +34,7 @@
else_passed: bool,
}
-pub fn preprocess(
- input: &str,
- defines: &HashSet<String>,
- imports: &HashMap<String, String>,
-) -> String {
+pub fn preprocess(input: &str, defines: &HashSet<String>, imports: &HashMap<&str, &str>) -> String {
let mut output = String::with_capacity(input.len());
let mut stack = vec![];
'all_lines: for (line_number, mut line) in input.lines().enumerate() {
diff --git a/piet-wgsl/src/util.rs b/piet-wgsl/src/util.rs
new file mode 100644
index 0000000..1422f7a
--- /dev/null
+++ b/piet-wgsl/src/util.rs
@@ -0,0 +1,87 @@
+// Copyright 2022 Google LLC
+//
+// 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.
+
+//! Simple helpers for managing wgpu state and surfaces.
+
+use super::Result;
+
+use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
+use wgpu::{Device, Instance, Limits, Queue, Surface, SurfaceConfiguration};
+
+/// Simple render context that maintains wgpu state for rendering the pipeline.
+pub struct RenderContext {
+ pub instance: Instance,
+ pub device: Device,
+ pub queue: Queue,
+}
+
+impl RenderContext {
+ pub async fn new() -> Result<Self> {
+ let instance = Instance::new(wgpu::Backends::PRIMARY);
+ 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?;
+ Ok(Self {
+ instance,
+ device,
+ queue,
+ })
+ }
+
+ /// Creates a new surface for the specified window and dimensions.
+ pub fn create_surface<W>(&self, window: &W, width: u32, height: u32) -> RenderSurface
+ where
+ W: HasRawWindowHandle + HasRawDisplayHandle,
+ {
+ let surface = unsafe { self.instance.create_surface(window) };
+ let format = wgpu::TextureFormat::Bgra8Unorm;
+ let config = wgpu::SurfaceConfiguration {
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+ format,
+ width,
+ height,
+ present_mode: wgpu::PresentMode::Fifo,
+ alpha_mode: wgpu::CompositeAlphaMode::Auto,
+ };
+ surface.configure(&self.device, &config);
+ RenderSurface { surface, config }
+ }
+
+ /// Resizes the surface to the new dimensions.
+ pub fn resize_surface(&self, surface: &mut RenderSurface, width: u32, height: u32) {
+ surface.config.width = width;
+ surface.config.height = height;
+ surface.surface.configure(&self.device, &surface.config);
+ }
+}
+
+/// Combination of surface and its configuration.
+pub struct RenderSurface {
+ pub surface: Surface,
+ pub config: SurfaceConfiguration,
+}