diff --git a/Cargo.lock b/Cargo.lock index eaddf11..1af69e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,15 +19,6 @@ dependencies = [ "version_check", ] -[[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" @@ -506,19 +497,6 @@ dependencies = [ "wio", ] -[[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" @@ -756,12 +734,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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" @@ -1236,7 +1208,7 @@ dependencies = [ "raw-window-handle 0.3.4", "raw-window-handle 0.5.0", "roxmltree", - "winit", + "winit 0.27.5", ] [[package]] @@ -1303,15 +1275,11 @@ name = "piet-wgsl" 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]] @@ -1520,23 +1488,6 @@ dependencies = [ "bitflags", ] -[[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" @@ -2251,6 +2202,17 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +[[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" diff --git a/Cargo.toml b/Cargo.toml index dc40744..8d022fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "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..2b85877 100644 --- a/piet-gpu/src/samples.rs +++ b/piet-gpu/src/samples.rs @@ -82,7 +82,7 @@ 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!("../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..266c582 100644 --- a/piet-gpu/src/simple_text.rs +++ b/piet-gpu/src/simple_text.rs @@ -22,7 +22,7 @@ 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!("../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 @@ edition = "2021" [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 98% 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 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 @@ 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"); +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_svg(sb: &mut SceneBuilder, svg: &PicoSvg, print_stats: bool) { 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 { - 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 { - 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)>, -} - -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 { - 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..967f86d --- /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; + +/// Specialization of `Result` for our catch-all error type. +pub type Result = std::result::Result; + +/// Renders a scene into a texture or surface. +pub struct Renderer { + engine: Engine, + shaders: FullShaders, + blit: BlitPipeline, + target: Option, +} + +impl Renderer { + /// Creates a new renderer for the specified device. + pub fn new(device: &Device) -> Result { + 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 { + // Generate a full screen quad in NDCs + var vertex = vec2(-1.0, 1.0); + switch ix { + case 1u: { + vertex = vec2(-1.0, -1.0); + } + case 2u, 4u: { + vertex = vec2(1.0, -1.0); + } + case 5u: { + vertex = vec2(1.0, 1.0); + } + default: {} + } + return vec4(vertex, 0.0, 1.0); + } + + @group(0) @binding(0) + var fine_output: texture_2d; + + @fragment + fn fs_main(@builtin(position) pos: vec4) -> @location(0) vec4 { + return textureLoad(fine_output, vec2(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, - pub surface_config: SurfaceConfiguration, -} - -impl WgpuState { - pub async fn new(window: Option<&Window>) -> Result> { - 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 { - // Generate a full screen quad in NDCs - var vertex = vec2(-1.0, 1.0); - switch ix { - case 1u: { - vertex = vec2(-1.0, -1.0); - } - case 2u, 4u: { - vertex = vec2(1.0, -1.0); - } - case 5u: { - vertex = vec2(1.0, 1.0); - } - default: {} - } - return vec4(vertex, 0.0, 1.0); - } - - @group(0) @binding(0) - var fine_output: texture_2d; - - @fragment - fn fs_main(@builtin(position) pos: vec4) -> @location(0) vec4 { - return textureLoad(fine_output, vec2(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 piet_scene::Scene; use crate::{ engine::{BufProxy, ImageFormat, ImageProxy, Recording, ResourceProxy}, shaders::{self, FullShaders, Shaders}, - Dimensions, }; const TAG_MONOID_SIZE: u64 = 12; @@ -62,6 +61,7 @@ pub const fn next_multiple_of(val: u32, rhs: u32) -> u32 { } } +#[allow(unused)] fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) { let mut recording = Recording::default(); let data = scene.data(); @@ -140,7 +140,8 @@ fn render(scene: &Scene, shaders: &Shaders) -> (Recording, BufProxy) { 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 @@ pub fn render_full( 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 @@ pub fn render_full( 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/util.rs b/piet-wgsl/src/util.rs new file mode 100644 index 0000000..20d3e9b --- /dev/null +++ b/piet-wgsl/src/util.rs @@ -0,0 +1,85 @@ +// 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 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> { + 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(&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, +}