diff --git a/Cargo.toml b/Cargo.toml index 169be54a1ddbd..d588eed797aff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -305,6 +305,9 @@ bevy_post_process = ["bevy_internal/bevy_post_process"] # Provides various anti-aliasing solutions bevy_anti_alias = ["bevy_internal/bevy_anti_alias"] +# Provides the ability to extract entities from an ECS main world to a sub world +bevy_extract = ["bevy_internal/bevy_extract"] + # Adds gamepad support bevy_gilrs = ["gamepad", "bevy_internal/bevy_gilrs"] @@ -753,6 +756,7 @@ bytemuck = "1" bevy_animation = { path = "crates/bevy_animation", version = "0.20.0-dev", default-features = false } bevy_asset = { path = "crates/bevy_asset", version = "0.20.0-dev", default-features = false } bevy_ecs = { path = "crates/bevy_ecs", version = "0.20.0-dev", default-features = false } +bevy_extract = { path = "crates/bevy_extract", version = "0.20.0-dev", default-features = false } bevy_gizmos = { path = "crates/bevy_gizmos", version = "0.20.0-dev", default-features = false } bevy_image = { path = "crates/bevy_image", version = "0.20.0-dev", default-features = false } bevy_reflect = { path = "crates/bevy_reflect", version = "0.20.0-dev", default-features = false } diff --git a/_release-content/migration-guides/extract-extract.md b/_release-content/migration-guides/extract-extract.md index 7ee65a4aae1a1..2a10080d95a9d 100644 --- a/_release-content/migration-guides/extract-extract.md +++ b/_release-content/migration-guides/extract-extract.md @@ -1,6 +1,6 @@ --- title: Extract Extract -pull_requests: [24419, 24420, 24423] +pull_requests: [24419, 24420, 24423, 22852] --- Extraction used to be specific of Main World to Render World, but will now be generic @@ -35,3 +35,11 @@ You can now extract a component from the main subapp to multiple subapps. To ext #[extract_app(RenderApp, AudioApp)] struct SomeComponent; ``` + +All of the above has moved to the new crate `bevy_extract`. + +Most extraction parts are re-exported by `bevy_render` . + +Some migrations are needed: + +- `bevy_render::extract_plugin::extract()` has moved to `bevy_extract::extract_plugin::extract()` diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 0350bfde288be..2ed423ff459b9 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -17,6 +17,7 @@ seq-macro = "0.3.6" # Bevy crates bevy_app = { path = "../crates/bevy_app" } bevy_ecs = { path = "../crates/bevy_ecs", features = ["multi_threaded"] } +bevy_extract = { path = "../crates/bevy_extract" } bevy_math = { path = "../crates/bevy_math" } bevy_picking = { path = "../crates/bevy_picking", features = ["mesh_picking"] } bevy_reflect = { path = "../crates/bevy_reflect", features = ["functions"] } diff --git a/benches/benches/bevy_render/extract_render_asset.rs b/benches/benches/bevy_render/extract_render_asset.rs index 8c55844271836..fdb6c92359b33 100644 --- a/benches/benches/bevy_render/extract_render_asset.rs +++ b/benches/benches/bevy_render/extract_render_asset.rs @@ -88,7 +88,7 @@ fn extract_render_asset_bench(c: &mut Criterion) { // Measuring the extract call let start = Instant::now(); - bevy_render::extract_plugin::extract(main.world_mut(), render_world); + bevy_extract::extract_plugin::extract(main.world_mut(), render_world); total += Instant::now().duration_since(start); // Run a standard app update to allow Bevy's internal systems to flush/clear the message queues. diff --git a/crates/bevy_anti_alias/Cargo.toml b/crates/bevy_anti_alias/Cargo.toml index 20f0e619a20c4..e0b61ca1bac89 100644 --- a/crates/bevy_anti_alias/Cargo.toml +++ b/crates/bevy_anti_alias/Cargo.toml @@ -29,6 +29,7 @@ bevy_image = { path = "../bevy_image", version = "0.20.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.20.0-dev" } bevy_shader = { path = "../bevy_shader", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.20.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.20.0-dev" } diff --git a/crates/bevy_core_pipeline/Cargo.toml b/crates/bevy_core_pipeline/Cargo.toml index 0f9e1f64a0153..48a2c52536d65 100644 --- a/crates/bevy_core_pipeline/Cargo.toml +++ b/crates/bevy_core_pipeline/Cargo.toml @@ -22,6 +22,7 @@ bevy_color = { path = "../bevy_color", version = "0.20.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.20.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } bevy_image = { path = "../bevy_image", version = "0.20.0-dev" } bevy_log = { path = "../bevy_log", version = "0.20.0-dev" } bevy_light = { path = "../bevy_light", version = "0.20.0-dev" } diff --git a/crates/bevy_core_pipeline/src/skybox/mod.rs b/crates/bevy_core_pipeline/src/skybox/mod.rs index 5d11c4d4ccfbb..123b74397990d 100644 --- a/crates/bevy_core_pipeline/src/skybox/mod.rs +++ b/crates/bevy_core_pipeline/src/skybox/mod.rs @@ -38,7 +38,7 @@ impl Plugin for SkyboxPlugin { embedded_asset!(app, "skybox.wesl"); app.add_plugins(( - SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), UniformComponentPlugin::::default(), )); diff --git a/crates/bevy_dev_tools/Cargo.toml b/crates/bevy_dev_tools/Cargo.toml index 7efb87f888614..373ff0878be4c 100644 --- a/crates/bevy_dev_tools/Cargo.toml +++ b/crates/bevy_dev_tools/Cargo.toml @@ -27,6 +27,7 @@ bevy_color = { path = "../bevy_color", version = "0.20.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.20.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } bevy_image = { path = "../bevy_image", version = "0.20.0-dev" } bevy_input = { path = "../bevy_input", version = "0.20.0-dev" } bevy_light = { path = "../bevy_light", version = "0.20.0-dev" } diff --git a/crates/bevy_extract/Cargo.toml b/crates/bevy_extract/Cargo.toml new file mode 100644 index 0000000000000..1aa03ddd2dcbb --- /dev/null +++ b/crates/bevy_extract/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "bevy_extract" +version = "0.20.0-dev" +edition = "2024" +description = "Provides extract functionality between ECS worlds for Bevy Engine" +homepage = "https://bevy.org" +repository = "https://github.com/bevyengine/bevy" +license = "MIT OR Apache-2.0" +keywords = ["bevy"] + +[features] +default = [] +trace = [] + +[dependencies] +# bevy +bevy_app = { path = "../bevy_app", version = "0.20.0-dev" } +bevy_camera = { path = "../bevy_camera", version = "0.20.0-dev" } +bevy_derive = { path = "../bevy_derive", version = "0.20.0-dev" } +bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract_macros = { path = "../bevy_extract/macros", version = "0.20.0-dev" } +bevy_log = { path = "../bevy_log", version = "0.20.0-dev" } +bevy_platform = { path = "../bevy_platform", version = "0.20.0-dev" } +bevy_reflect = { path = "../bevy_reflect", version = "0.20.0-dev" } +bevy_time = { path = "../bevy_time", version = "0.20.0-dev" } +bevy_utils = { path = "../bevy_utils", version = "0.20.0-dev" } + +[lints] +workspace = true + +[package.metadata.docs.rs] +rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"] +all-features = true diff --git a/crates/bevy_extract/LICENSE-APACHE b/crates/bevy_extract/LICENSE-APACHE new file mode 100644 index 0000000000000..d9a10c0d8e868 --- /dev/null +++ b/crates/bevy_extract/LICENSE-APACHE @@ -0,0 +1,176 @@ + 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 diff --git a/crates/bevy_extract/LICENSE-MIT b/crates/bevy_extract/LICENSE-MIT new file mode 100644 index 0000000000000..9cf106272ac3b --- /dev/null +++ b/crates/bevy_extract/LICENSE-MIT @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/bevy_extract/README.md b/crates/bevy_extract/README.md new file mode 100644 index 0000000000000..53c782c8d241f --- /dev/null +++ b/crates/bevy_extract/README.md @@ -0,0 +1,9 @@ +# Bevy App + +[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license) +[![Crates.io](https://img.shields.io/crates/v/bevy.svg)](https://crates.io/crates/bevy_extract) +[![Downloads](https://img.shields.io/crates/d/bevy_extract.svg)](https://crates.io/crates/bevy_extract) +[![Docs](https://docs.rs/bevy_extract/badge.svg)](https://docs.rs/bevy_extract/latest/bevy_extract/) +[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy) + +This crate makes it easy to copy out main world state to a sub app's world. diff --git a/crates/bevy_extract/macros/Cargo.toml b/crates/bevy_extract/macros/Cargo.toml new file mode 100644 index 0000000000000..f0395e72b662c --- /dev/null +++ b/crates/bevy_extract/macros/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "bevy_extract_macros" +version = "0.20.0-dev" +edition = "2024" +description = "Derive implementations for bevy_extract" +homepage = "https://bevy.org" +repository = "https://github.com/bevyengine/bevy" +license = "MIT OR Apache-2.0" +keywords = ["bevy"] + +[lib] +proc-macro = true + +[dependencies] +bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.20.0-dev" } + +syn = { version = "2.0", features = ["full"] } +proc-macro2 = "1.0" +quote = "1.0" + +[lints] +workspace = true + +[package.metadata.docs.rs] +rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"] +all-features = true diff --git a/crates/bevy_extract/macros/LICENSE-APACHE b/crates/bevy_extract/macros/LICENSE-APACHE new file mode 100644 index 0000000000000..d9a10c0d8e868 --- /dev/null +++ b/crates/bevy_extract/macros/LICENSE-APACHE @@ -0,0 +1,176 @@ + 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 diff --git a/crates/bevy_extract/macros/LICENSE-MIT b/crates/bevy_extract/macros/LICENSE-MIT new file mode 100644 index 0000000000000..9cf106272ac3b --- /dev/null +++ b/crates/bevy_extract/macros/LICENSE-MIT @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/bevy_render/macros/src/extract_component.rs b/crates/bevy_extract/macros/src/extract_component.rs similarity index 89% rename from crates/bevy_render/macros/src/extract_component.rs rename to crates/bevy_extract/macros/src/extract_component.rs index c9bae62641ba1..9d373bb6edb6c 100644 --- a/crates/bevy_render/macros/src/extract_component.rs +++ b/crates/bevy_extract/macros/src/extract_component.rs @@ -5,7 +5,7 @@ use syn::{parse_macro_input, parse_quote, punctuated::Punctuated, DeriveInput, P pub fn derive_extract_component(input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); - let bevy_render_path: Path = crate::bevy_render_path(); + let bevy_extract_path: Path = crate::bevy_extract_path(); let bevy_ecs_path: Path = bevy_macro_utils::BevyManifest::shared(|manifest| { manifest .maybe_get_path("bevy_ecs") @@ -83,11 +83,11 @@ pub fn derive_extract_component(input: TokenStream) -> TokenStream { app_labels.iter().map(|app_label| TokenStream::from(quote! { - impl #impl_generics #bevy_render_path::sync_component::SyncComponent<#app_label> for #struct_name #type_generics #where_clause { + impl #impl_generics #bevy_extract_path::sync_component::SyncComponent<#app_label> for #struct_name #type_generics #where_clause { type Target = #sync_target; } - impl #impl_generics #bevy_render_path::extract_component::ExtractComponent<#app_label> for #struct_name #type_generics #where_clause { + impl #impl_generics #bevy_extract_path::extract_component::ExtractComponent<#app_label> for #struct_name #type_generics #where_clause { type QueryData = &'static Self; type QueryFilter = #filter; diff --git a/crates/bevy_render/macros/src/extract_resource.rs b/crates/bevy_extract/macros/src/extract_resource.rs similarity index 85% rename from crates/bevy_render/macros/src/extract_resource.rs rename to crates/bevy_extract/macros/src/extract_resource.rs index 2f05734ad7f3d..7fe1b886e5200 100644 --- a/crates/bevy_render/macros/src/extract_resource.rs +++ b/crates/bevy_extract/macros/src/extract_resource.rs @@ -5,7 +5,7 @@ use syn::{parse_macro_input, parse_quote, DeriveInput, Path}; pub fn derive_extract_resource(input: TokenStream) -> TokenStream { let mut ast = parse_macro_input!(input as DeriveInput); - let bevy_render_path: Path = crate::bevy_render_path(); + let bevy_extract_path: Path = crate::bevy_extract_path(); ast.generics .make_where_clause() @@ -31,7 +31,7 @@ pub fn derive_extract_resource(input: TokenStream) -> TokenStream { }; TokenStream::from(quote! { - impl #impl_generics #bevy_render_path::extract_resource::ExtractResource<#app_label> for #struct_name #type_generics #where_clause { + impl #impl_generics #bevy_extract_path::extract_resource::ExtractResource<#app_label> for #struct_name #type_generics #where_clause { type Source = Self; fn extract_resource(source: &Self::Source) -> Self { diff --git a/crates/bevy_extract/macros/src/lib.rs b/crates/bevy_extract/macros/src/lib.rs new file mode 100644 index 0000000000000..8e9a349c2af68 --- /dev/null +++ b/crates/bevy_extract/macros/src/lib.rs @@ -0,0 +1,54 @@ +#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")] +#![cfg_attr(docsrs, feature(doc_cfg))] + +mod extract_component; +mod extract_resource; + +use bevy_macro_utils::BevyManifest; +use proc_macro::TokenStream; + +pub(crate) fn bevy_extract_path() -> syn::Path { + BevyManifest::shared(|manifest| manifest.get_path("bevy_extract")) +} + +#[proc_macro_derive(ExtractResource, attributes(extract_app))] +pub fn derive_extract_resource(input: TokenStream) -> TokenStream { + extract_resource::derive_extract_resource(input) +} + +/// Implements `ExtractComponent` trait for a component. +/// +/// The component must implement [`Clone`]. +/// The component will be extracted into the sub world via cloning. +/// Note that this only enables extraction of the component, it does not execute the extraction. +/// See `ExtractComponentPlugin` to actually perform the extraction. +/// +/// If you only want to extract a component conditionally, you may use the `extract_component_filter` attribute. +/// To specify `SyncComponent::Target`, you can use the `extract_component_sync_target` attribute. +/// +/// # Example +/// +/// ```no_compile +/// use bevy_ecs::component::Component; +/// use bevy_extract_macros::ExtractComponent; +/// +/// #[derive(Component, Clone, ExtractComponent)] +/// #[extract_component_filter(With)] +/// #[extract_component_sync_target((Self, OtherNeedsCleanup))] +/// pub struct Foo { +/// pub should_foo: bool, +/// } +/// +/// // Without a filter (unconditional). +/// #[derive(Component, Clone, ExtractComponent)] +/// pub struct Bar { +/// pub should_bar: bool, +/// } +/// ``` +#[proc_macro_derive( + ExtractComponent, + attributes(extract_component_filter, extract_component_sync_target, extract_app) +)] +pub fn derive_extract_component(input: TokenStream) -> TokenStream { + extract_component::derive_extract_component(input) +} diff --git a/crates/bevy_render/src/extract_component.rs b/crates/bevy_extract/src/extract_component.rs similarity index 82% rename from crates/bevy_render/src/extract_component.rs rename to crates/bevy_extract/src/extract_component.rs index 1d5dab54d7d4e..5c8fb71a81cea 100644 --- a/crates/bevy_render/src/extract_component.rs +++ b/crates/bevy_extract/src/extract_component.rs @@ -1,7 +1,7 @@ use crate::{ sync_component::{SyncComponent, SyncComponentPlugin}, sync_world::SubEntity, - Extract, ExtractSchedule, RenderApp, + Extract, ExtractSchedule, }; use bevy_app::{App, AppLabel, Plugin}; use bevy_camera::visibility::ViewVisibility; @@ -12,13 +12,11 @@ use bevy_ecs::{ }; use core::marker::PhantomData; -pub use crate::uniform::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}; +pub use bevy_extract_macros::ExtractComponent; -pub use bevy_render_macros::ExtractComponent; - -/// Describes how a component gets extracted for rendering. +/// Describes how a component gets extracted from the main app to a sub app. /// -/// Therefore the component is transferred from the "app world" into the "render +/// Therefore the component is transferred from the "app world" into the "sub /// world" in the [`ExtractSchedule`] step. This functionality is enabled by /// adding [`ExtractComponentPlugin`] with the component type. /// @@ -34,20 +32,20 @@ pub trait ExtractComponent: SyncComponent { type QueryFilter: QueryFilter; /// The output from extraction, i.e. [`ExtractComponent::extract_component`]. /// - /// The output components won't be removed automatically from the render world if the implementing component is removed, + /// The output components won't be removed automatically from the sub world if the implementing component is removed, /// unless you set them in the [`SyncComponent::Target`]. type Out: Bundle; // TODO: https://github.com/rust-lang/rust/issues/29661 // type Out: Bundle = Self; - /// Defines how the component is transferred into the "render world". + /// Defines how the component is transferred into the "sub world". /// /// Returning `None` based on the queried item will remove the [`SyncComponent::Target`] from the entity in - /// the render world. + /// the sub world. fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option; } -/// This plugin extracts the components into the render world for synced +/// This plugin extracts the components into the sub world for synced /// entities. To do so, it sets up the [`ExtractSchedule`] step for the /// specified [`ExtractComponent`]. /// @@ -57,13 +55,11 @@ pub trait ExtractComponent: SyncComponent { /// The marker type `F` is only used as a way to bypass the orphan rules. To /// implement the trait for a foreign type you can use a local type as the /// marker, e.g. the type of the plugin that calls [`ExtractComponentPlugin`]. -pub struct ExtractComponentPlugin { +pub struct ExtractComponentPlugin { only_extract_visible: bool, marker: PhantomData (C, L, F)>, } -// pub type ExtractComponentPlugin = ExtractComponentPlugin; - impl Default for ExtractComponentPlugin { fn default() -> Self { Self { @@ -91,17 +87,17 @@ impl< fn build(&self, app: &mut App) { app.add_plugins(SyncComponentPlugin::::default()); - if let Some(render_app) = app.get_sub_app_mut(L::default()) { + if let Some(sub_app) = app.get_sub_app_mut(L::default()) { if self.only_extract_visible { - render_app.add_systems(ExtractSchedule, extract_visible_components::); + sub_app.add_systems(ExtractSchedule, extract_visible_components::); } else { - render_app.add_systems(ExtractSchedule, extract_components::); + sub_app.add_systems(ExtractSchedule, extract_components::); } } } } -/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`crate::sync_world::SyncToRenderWorld`]. +/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are synced via [`crate::sync_world::SyncToSubWorld`]. fn extract_components, L: AppLabel + Clone + Copy + Eq, F>( mut commands: Commands, mut previous_len: Local, @@ -119,7 +115,7 @@ fn extract_components, L: AppLabel + Clone + Copy + Eq commands.try_insert_batch(values); } -/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are visible and synced via [`crate::sync_world::SyncToRenderWorld`]. +/// This system extracts all components of the corresponding [`ExtractComponent`], for entities that are visible and synced via [`crate::sync_world::SyncToSubWorld`]. fn extract_visible_components, L: AppLabel + Clone + Copy + Eq, F>( mut commands: Commands, mut previous_len: Local, diff --git a/crates/bevy_render/src/extract_instances.rs b/crates/bevy_extract/src/extract_instances.rs similarity index 100% rename from crates/bevy_render/src/extract_instances.rs rename to crates/bevy_extract/src/extract_instances.rs diff --git a/crates/bevy_render/src/extract_param.rs b/crates/bevy_extract/src/extract_param.rs similarity index 93% rename from crates/bevy_render/src/extract_param.rs rename to crates/bevy_extract/src/extract_param.rs index 0659cec965403..d0ac00ccde564 100644 --- a/crates/bevy_render/src/extract_param.rs +++ b/crates/bevy_extract/src/extract_param.rs @@ -23,8 +23,8 @@ use core::ops::{Deref, DerefMut}; /// ## Context /// /// [`ExtractSchedule`] is used to extract (move) data from the simulation world ([`MainWorld`]) to the -/// render world. The render world drives rendering each frame (generally to a `Window`). -/// This design is used to allow performing calculations related to rendering a prior frame at the same +/// sub world. +/// This design is used to allow performing calculations related to a prior frame at the same /// time as the next frame is simulated, which increases throughput (FPS). /// /// [`Extract`] is used to get data from the main world during [`ExtractSchedule`]. @@ -33,12 +33,15 @@ use core::ops::{Deref, DerefMut}; /// /// ``` /// use bevy_ecs::prelude::*; -/// use bevy_render::Extract; -/// use bevy_render::sync_world::RenderEntity; +/// use bevy_extract::Extract; +/// use bevy_extract::sync_world::SubEntity; +/// use bevy_derive::AppLabel; +/// # #[derive(AppLabel, Debug, Hash, PartialEq, Eq, Clone, Default, Copy)] +/// # struct ExtractApp; /// # #[derive(Component)] /// // Do make sure to sync the cloud entities before extracting them. /// # struct Cloud; -/// fn extract_clouds(mut commands: Commands, clouds: Extract>>) { +/// fn extract_clouds(mut commands: Commands, clouds: Extract, With>>) { /// for cloud in &clouds { /// commands.entity(cloud).insert(Cloud); /// } @@ -46,7 +49,6 @@ use core::ops::{Deref, DerefMut}; /// ``` /// /// [`ExtractSchedule`]: crate::ExtractSchedule -/// [Window]: bevy_window::Window pub struct Extract<'w, 's, P> where P: ReadOnlySystemParam + 'static, diff --git a/crates/bevy_render/src/extract_plugin.rs b/crates/bevy_extract/src/extract_plugin.rs similarity index 87% rename from crates/bevy_render/src/extract_plugin.rs rename to crates/bevy_extract/src/extract_plugin.rs index 64937f17b770d..e66627b28689e 100644 --- a/crates/bevy_render/src/extract_plugin.rs +++ b/crates/bevy_extract/src/extract_plugin.rs @@ -13,12 +13,12 @@ use bevy_ecs::{ }; use bevy_utils::default; -/// Plugin that sets up the [`RenderApp`](`crate::RenderApp`) and handles extracting data from the -/// main world to the render world. +/// Plugin that sets up the sub app for the [`AppLabel`] and handles extracting data from the +/// main world to the sub world. pub struct ExtractPlugin { /// Function that gets run at the beginning of each extraction. /// - /// Gets the main world and render world as arguments (in that order). + /// Gets the main world and sub world as arguments (in that order). pub pre_extract: fn(&mut World, &mut World), marker: PhantomData, @@ -54,25 +54,25 @@ impl Plugin for ExtractPlugin { app.add_plugins(SyncWorldPlugin::::default()); app.init_resource::(); - let mut render_app = SubApp::new(); + let mut sub_app = SubApp::new(); let mut extract_schedule = Schedule::new(ExtractSchedule); // We skip applying any commands during the ExtractSchedule - // so commands can be applied on the render thread. + // so commands can be applied on the sub app’s thread. extract_schedule.set_build_settings(ScheduleBuildSettings { auto_insert_apply_deferred: false, ..default() }); extract_schedule.set_apply_final_deferred(false); - render_app + sub_app .add_schedule((self.base_schedule)()) .add_schedule(extract_schedule) .allow_ambiguous_resource::() .add_systems( self.schedule_label, ( - // This set applies the commands from the extract schedule while the render schedule + // This set applies the commands from the extract schedule while the sub schedule // is running in parallel with the main app. apply_extract_commands.in_set(self.extract_set), despawn_temporary_entities::.in_set(self.despawn_set), @@ -80,42 +80,42 @@ impl Plugin for ExtractPlugin { ); let pre_extract = self.pre_extract; - render_app.set_extract(move |main_world, render_world| { - pre_extract(main_world, render_world); + sub_app.set_extract(move |main_world, sub_world| { + pre_extract(main_world, sub_world); { #[cfg(feature = "trace")] let _stage_span = bevy_log::info_span!("entity_sync").entered(); - entity_sync_system::(main_world, render_world); + entity_sync_system::(main_world, sub_world); } // run extract schedule - extract(main_world, render_world); + extract(main_world, sub_world); }); - app.insert_sub_app(L::default(), render_app); + app.insert_sub_app(L::default(), sub_app); } } -/// Schedule in which data from the main world is 'extracted' into the render world. +/// Schedule in which data from the main world is 'extracted' into the sub world. /// /// This step should be kept as short as possible to increase the "pipelining potential" for -/// running the next frame while rendering the current frame. +/// running the next frame while processing the current frame. /// -/// This schedule is run on the render world, but it also has access to the main world. +/// This schedule is run on the sub world, but it also has access to the main world. /// See [`MainWorld`] and [`Extract`](crate::Extract) for details on how to access main world data from this schedule. #[derive(ScheduleLabel, PartialEq, Eq, Debug, Clone, Hash, Default)] pub struct ExtractSchedule; /// Applies the commands from the extract schedule. This happens during -/// the render schedule rather than during extraction to allow the commands to run in parallel with the -/// main app when pipelined rendering is enabled. -fn apply_extract_commands(render_world: &mut World) { - render_world.resource_scope(|render_world, mut schedules: Mut| { +/// the sub schedule rather than during extraction to allow the commands to run in parallel with the +/// main app when pipelined processing is enabled. +fn apply_extract_commands(sub_world: &mut World) { + sub_world.resource_scope(|sub_world, mut schedules: Mut| { schedules .get_mut(ExtractSchedule) .unwrap() - .apply_deferred(render_world); + .apply_deferred(sub_world); }); } /// The simulation [`World`] of the application, stored as a resource. @@ -131,17 +131,17 @@ pub struct MainWorld(World); #[derive(Resource, Default)] struct ScratchMainWorld(World); -/// Executes the [`ExtractSchedule`] step of the renderer. -/// This updates the render world with the extracted ECS data of the current frame. -pub fn extract(main_world: &mut World, render_world: &mut World) { - // temporarily add the app world to the render world as a resource +/// Executes the [`ExtractSchedule`] step of the processor. +/// This updates the sub world with the extracted ECS data of the current frame. +pub fn extract(main_world: &mut World, sub_world: &mut World) { + // temporarily add the app world to the sub world as a resource let scratch_world = main_world.remove_resource::().unwrap(); let inserted_world = core::mem::replace(main_world, scratch_world.0); - render_world.insert_resource(MainWorld(inserted_world)); - render_world.run_schedule(ExtractSchedule); + sub_world.insert_resource(MainWorld(inserted_world)); + sub_world.run_schedule(ExtractSchedule); // move the app world back, as if nothing happened. - let inserted_world = render_world.remove_resource::().unwrap(); + let inserted_world = sub_world.remove_resource::().unwrap(); let scratch_world = core::mem::replace(main_world, inserted_world.0); main_world.insert_resource(ScratchMainWorld(scratch_world)); } @@ -156,7 +156,6 @@ mod test { extract_plugin::ExtractPlugin, sync_component::SyncComponent, sync_world::MainEntity, - RenderApp, }; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] @@ -165,11 +164,14 @@ mod test { PostCleanup, } + #[derive(AppLabel, Debug, Hash, PartialEq, Eq, Clone, Default, Copy)] + pub struct ExtractApp; + #[derive(ScheduleLabel, Debug, Hash, PartialEq, Eq, Clone, Default)] pub struct MySchedule; impl MySchedule { - /// Sets up the base structure of the rendering [`Schedule`]. + /// Sets up the base structure of the processing [`Schedule`]. /// /// The sets defined in this enum are configured to run in order. pub fn base_schedule() -> Schedule { @@ -190,17 +192,17 @@ mod test { struct RenderComponentExtra; #[derive(Component, Clone, Debug, ExtractComponent)] - #[extract_app(RenderApp)] + #[extract_app(ExtractApp)] struct RenderComponentSeparate; #[derive(Component, Clone, Debug)] struct RenderComponentNoExtract; - impl SyncComponent for RenderComponent { + impl SyncComponent for RenderComponent { type Target = (RenderComponent, RenderComponentExtra); } - impl ExtractComponent for RenderComponent { + impl ExtractComponent for RenderComponent { type QueryData = &'static Self; type QueryFilter = (); type Out = (RenderComponent, RenderComponentExtra); @@ -216,26 +218,26 @@ mod test { fn extraction_works() { let mut app = App::new(); - app.add_plugins(ExtractPlugin::::new( + app.add_plugins(ExtractPlugin::::new( |_, _| {}, MySchedule::base_schedule, MySchedule.intern(), MyScheduleSystems::ExtractCommands.intern(), MyScheduleSystems::PostCleanup.intern(), )); - app.add_plugins(ExtractComponentPlugin::::default()); - app.add_plugins(ExtractComponentPlugin::::default()); + app.add_plugins(ExtractComponentPlugin::::default()); + app.add_plugins(ExtractComponentPlugin::::default()); app.add_systems(Startup, |mut commands: Commands| { commands.spawn((RenderComponent, RenderComponentSeparate)); }); - let render_app = app.get_sub_app_mut(RenderApp).unwrap(); + let sub_app = app.get_sub_app_mut(ExtractApp).unwrap(); // Normally RenderPlugin sets the RenderRecovery schedule as update, but for // testing we just use the Render schedule directly. - render_app.update_schedule = Some(MySchedule.intern()); + sub_app.update_schedule = Some(MySchedule.intern()); - render_app.world_mut().add_observer( + sub_app.world_mut().add_observer( |event: On>, mut commands: Commands| { // Simulate data that's not extracted commands @@ -248,8 +250,8 @@ mod test { // Check that all components have been extracted { - let render_app = app.get_sub_app_mut(RenderApp).unwrap(); - render_app + let sub_app = app.get_sub_app_mut(ExtractApp).unwrap(); + sub_app .world_mut() .run_system_cached( |entity: Single<( @@ -283,8 +285,8 @@ mod test { // Check that the extracted components have been removed { - let render_app = app.get_sub_app_mut(RenderApp).unwrap(); - render_app + let sub_app = app.get_sub_app_mut(ExtractApp).unwrap(); + sub_app .world_mut() .run_system_cached( |entity: Single<( diff --git a/crates/bevy_render/src/extract_resource.rs b/crates/bevy_extract/src/extract_resource.rs similarity index 75% rename from crates/bevy_render/src/extract_resource.rs rename to crates/bevy_extract/src/extract_resource.rs index 019c07fbf46a0..f15e46c79ad42 100644 --- a/crates/bevy_render/src/extract_resource.rs +++ b/crates/bevy_extract/src/extract_resource.rs @@ -2,14 +2,14 @@ use core::marker::PhantomData; use bevy_app::{App, AppLabel, Plugin}; use bevy_ecs::{component::Mutable, prelude::*}; -pub use bevy_render_macros::ExtractResource; +pub use bevy_extract_macros::ExtractResource; use bevy_utils::once; -use crate::{Extract, ExtractSchedule, RenderApp}; +use crate::{Extract, ExtractSchedule}; -/// Describes how a resource gets extracted for rendering. +/// Describes how a resource gets extracted for processing. /// -/// Therefore the resource is transferred from the "main world" into the "render world" +/// Therefore the resource is transferred from the "main world" into the "sub world" /// in the [`ExtractSchedule`] step. /// /// The marker type `F` is only used as a way to bypass the orphan rules. To @@ -18,11 +18,11 @@ use crate::{Extract, ExtractSchedule, RenderApp}; pub trait ExtractResource: Resource { type Source: Resource; - /// Defines how the resource is transferred into the "render world". + /// Defines how the resource is transferred into the "sub world". fn extract_resource(source: &Self::Source) -> Self; } -/// This plugin extracts the resources into the "render world". +/// This plugin extracts the resources into the "sub world". /// /// Therefore it sets up the[`ExtractSchedule`] step /// for the specified [`Resource`]. @@ -30,12 +30,10 @@ pub trait ExtractResource: Resource { /// The marker type `F` is only used as a way to bypass the orphan rules. To /// implement the trait for a foreign type you can use a local type as the /// marker, e.g. the type of the plugin that calls [`ExtractResourcePlugin`]. -pub struct ExtractResourcePlugin, L: AppLabel = RenderApp, F = ()>( +pub struct ExtractResourcePlugin, L: AppLabel, F = ()>( PhantomData<(R, L, F)>, ); -// pub type ExtractResourcePlugin = ExtractResourcePlugin; - impl, L: AppLabel, F> Default for ExtractResourcePlugin { fn default() -> Self { Self(PhantomData) @@ -49,11 +47,11 @@ impl< > Plugin for ExtractResourcePlugin { fn build(&self, app: &mut App) { - if let Some(render_app) = app.get_sub_app_mut(L::default()) { - render_app.add_systems(ExtractSchedule, extract_resource::); + if let Some(sub_app) = app.get_sub_app_mut(L::default()) { + sub_app.add_systems(ExtractSchedule, extract_resource::); } else { once!(bevy_log::error!( - "Render app did not exist when trying to add `extract_resource` for <{}>.", + "Sub app did not exist when trying to add `extract_resource` for <{}>.", core::any::type_name::() )); } @@ -75,7 +73,7 @@ pub fn extract_resource, L: AppLa #[cfg(debug_assertions)] if !main_resource.is_added() { once!(bevy_log::warn!( - "Removing resource {} from render world not expected, adding using `Commands`. + "Removing resource {} from sub world not expected, adding using `Commands`. This may decrease performance", core::any::type_name::() )); diff --git a/crates/bevy_extract/src/lib.rs b/crates/bevy_extract/src/lib.rs new file mode 100644 index 0000000000000..17e9839547dfa --- /dev/null +++ b/crates/bevy_extract/src/lib.rs @@ -0,0 +1,59 @@ +#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")] +#![cfg_attr( + any(docsrs, docsrs_dep), + expect( + internal_features, + reason = "rustdoc_internals is needed for fake_variadic" + ) +)] +#![cfg_attr(any(docsrs, docsrs_dep), feature(doc_cfg, rustdoc_internals))] +#![doc( + html_logo_url = "https://bevy.org/assets/icon.png", + html_favicon_url = "https://bevy.org/assets/icon.png" +)] +#![expect(unsafe_code, reason = "Unsafe code is used to improve performance.")] + +//! This crate provides a way to extract component information from +//! an app’s main world into a sub world. +//! +//! The easiest way to set up extract is to add the [`ExtractPlugin`] for your [`AppLabel`](`bevy_app::AppLabel`). +//! +//! Then derive `ExtractComponent` or `ExtractResource` - ensure that you specify the `extract_app` attribute. +//! +//! ```ignore +//! #[derive(Component, Clone, Debug, ExtractComponent)] +//! #[extract_app(SomeApp)] +//! struct SomeComponent; +//! ``` +//! +//! This adds `SyncComponent` to first sync the entities from the main world to the sub world. +//! And then sync the component data from the main entity to the sub entity. +//! +//! More complex use cases may want to manually implement the `ExtractComponent` or `ExtractResource` traits directly. +//! +//! For higher performance needs use the [`ExtractInstance`](`crate::extract_instances::ExtractInstance`) trait. +//! +//! The sub app can access the main world in the [`ExtractSchedule`](`crate::ExtractSchedule`). +//! Adding a system with a query wrapped in [`Extract`](`crate::Extract`) and it will run against the main app world. +//! +//! [`SyncComponent`]: crate::sync_component::SyncComponent +//! [`ExtractComponent`]: crate::extract_component::ExtractComponent +//! [`ExtractResource`]: crate::extract_resource::ExtractResource + +extern crate alloc; + +pub mod extract_component; +pub mod extract_instances; +pub mod extract_param; +pub mod extract_plugin; +pub mod extract_resource; +pub mod sync_component; +pub mod sync_world; + +pub use extract_param::Extract; +pub use extract_plugin::*; +pub use extract_plugin::{ExtractSchedule, MainWorld}; +pub use sync_world::*; + +// Required to make proc macros work in bevy itself. +extern crate self as bevy_extract; diff --git a/crates/bevy_render/src/sync_component.rs b/crates/bevy_extract/src/sync_component.rs similarity index 82% rename from crates/bevy_render/src/sync_component.rs rename to crates/bevy_extract/src/sync_component.rs index 4ee72a23c3642..c6115b541665e 100644 --- a/crates/bevy_render/src/sync_component.rs +++ b/crates/bevy_extract/src/sync_component.rs @@ -9,14 +9,11 @@ use bevy_ecs::{ system::ResMut, }; -use crate::{ - sync_world::{EntityRecord, PendingSyncEntity, SyncToSubWorld}, - RenderApp, -}; +use crate::sync_world::{EntityRecord, PendingSyncEntity, SyncToSubWorld}; use bevy_log::warn_once; -/// Plugin that registers a component for automatic sync to the render world. See [`SyncWorldPlugin`] for more information. +/// Plugin that registers a component for automatic sync to the sub world. See [`SyncWorldPlugin`] for more information. /// /// This plugin is automatically added by [`ExtractComponentPlugin`], and only needs to be added for manual extraction implementations. /// @@ -27,11 +24,11 @@ use bevy_log::warn_once; /// # Implementation details /// /// It adds [`SyncToSubWorld`] as a required component to make the [`SyncWorldPlugin`] aware of the component, and -/// handles cleanup of the component in the render world when it is removed from an entity. +/// handles cleanup of the component in the sub world when it is removed from an entity. /// /// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin /// [`SyncWorldPlugin`]: crate::sync_world::SyncWorldPlugin -pub struct SyncComponentPlugin(PhantomData<(C, L, F)>); +pub struct SyncComponentPlugin(PhantomData<(C, L, F)>); // pub type SyncComponentPlugin = SyncComponentPlugin; @@ -42,7 +39,7 @@ impl, L: AppLabel, F> Default for SyncComponentPlugin, L: AppLabel, F> Default for SyncComponentPlugin: Component { - /// Describes what components should be removed from the render world if the + /// Describes what components should be removed from the sub world if the /// implementing component is removed. type Target: Bundle; // TODO: https://github.com/rust-lang/rust/issues/29661 @@ -85,16 +82,18 @@ impl< #[cfg(test)] mod tests { - use bevy_app::App; + use bevy_app::{App, AppLabel}; use bevy_ecs::component::Component; use super::{SyncComponent, SyncComponentPlugin}; - use crate::RenderApp; + + #[derive(AppLabel, Debug, Hash, PartialEq, Eq, Clone, Default, Copy)] + pub struct ExtractApp; #[derive(Component)] struct TestSyncComponent; - impl SyncComponent for TestSyncComponent { + impl SyncComponent for TestSyncComponent { type Target = Self; } @@ -104,7 +103,7 @@ mod tests { #[test] fn remove_synced_component_without_render_world() { let mut app = App::new(); - app.add_plugins(SyncComponentPlugin::::default()); + app.add_plugins(SyncComponentPlugin::::default()); let entity = app.world_mut().spawn(TestSyncComponent).id(); app.world_mut().despawn(entity); diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_extract/src/sync_world.rs similarity index 94% rename from crates/bevy_render/src/sync_world.rs rename to crates/bevy_extract/src/sync_world.rs index cb615cdc1cff6..9ed9bf0e15297 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_extract/src/sync_world.rs @@ -17,7 +17,7 @@ use bevy_ecs::{ }; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -/// A plugin that synchronizes entities with [`SyncToSubWorld`] between the main world and the render world. +/// A plugin that synchronizes entities with [`SyncToSubWorld`] between the main world and the sub world. /// /// All entities with the [`SyncToSubWorld`] component are kept in sync. It /// is automatically added as a required component by [`ExtractComponentPlugin`] @@ -26,30 +26,26 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect}; /// /// # Implementation /// -/// Bevy's renderer is architected independently from the main app. -/// It operates in its own separate ECS [`World`], so the renderer logic can run in parallel with the main world logic. -/// This is called "Pipelined Rendering", see [`PipelinedRenderingPlugin`] for more information. -/// /// [`SyncWorldPlugin`] is the first thing that runs every frame and it maintains an entity-to-entity mapping -/// between the main world and the render world. -/// It does so by spawning and despawning entities in the render world, to match spawned and despawned entities in the main world. +/// between the main world and the sub world. +/// It does so by spawning and despawning entities in the sub world, to match spawned and despawned entities in the main world. /// The link between synced entities is maintained by the [`SubEntity`] and [`MainEntity`] components. /// -/// The [`SubEntity`] contains the corresponding render world entity of a main world entity, while [`MainEntity`] contains -/// the corresponding main world entity of a render world entity. +/// The [`SubEntity`] contains the corresponding sub world entity of a main world entity, while [`MainEntity`] contains +/// the corresponding main world entity of a sub world entity. /// For convenience, [`QueryData`](bevy_ecs::query::QueryData) implementations are provided for both components: /// adding [`MainEntity`] to a query (without a `&`) will return the corresponding main world [`Entity`], -/// and adding [`SubEntity`] will return the corresponding render world [`Entity`]. +/// and adding [`SubEntity`] will return the corresponding sub world [`Entity`]. /// If you have access to the component itself, the underlying entities can be accessed by calling `.id()`. /// /// Synchronization is necessary preparation for extraction ([`ExtractSchedule`](crate::ExtractSchedule)), which copies over component data from the main -/// to the render world for these entities. +/// to the sub world for these entities. /// /// ```text /// |--------------------------------------------------------------------| /// | | | Main world update | /// | sync | extract |---------------------------------------------------| -/// | | | Render world update | +/// | | | Sub world update | /// |--------------------------------------------------------------------| /// ``` /// @@ -63,7 +59,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect}; /// | ID: 18v1 | PointLight | SubEntity(ID: 5V1) | SyncToSubWorld | /// |-------------------------------------------------------------------| /// -/// |----------Render World-----------| +/// |----------Sub world-----------| /// | Entity | Component | /// |---------------------------------| /// | ID: 3v1 | MainEntity(ID: 1V1) | @@ -72,23 +68,23 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect}; /// /// ``` /// -/// Note that this effectively establishes a link between the main world entity and the render world entity. +/// Note that this effectively establishes a link between the main world entity and the sub world entity. /// Not every entity needs to be synchronized, however; only entities with the [`SyncToSubWorld`] component are synced. /// Adding [`SyncToSubWorld`] to a main world component will establish such a link. -/// Once a synchronized main entity is despawned, its corresponding render entity will be automatically +/// Once a synchronized main entity is despawned, its corresponding sub entity will be automatically /// despawned in the next `sync`. /// /// The sync step does not copy any of component data between worlds, since its often not necessary to transfer over all /// the components of a main world entity. -/// The render world probably cares about a `Position` component, but not a `Velocity` component. +/// A sub world may care about a `Position` component, but may not care about a `Velocity` component. /// The extraction happens in its own step, independently from, and after synchronization. /// -/// Moreover, [`SyncWorldPlugin`] only synchronizes *entities*. [`RenderAsset`](crate::render_asset::RenderAsset)s like meshes and textures are handled +/// Moreover, [`SyncWorldPlugin`] only synchronizes *entities*. [`Asset`]s like meshes and textures are handled /// differently. /// -/// [`PipelinedRenderingPlugin`]: crate::pipelined_rendering::PipelinedRenderingPlugin /// [`ExtractComponentPlugin`]: crate::extract_component::ExtractComponentPlugin /// [`SyncComponentPlugin`]: crate::sync_component::SyncComponentPlugin +/// [`Asset`]: https://docs.rs/bevy/latest/bevy/asset/trait.Asset.html #[derive(Default)] pub struct SyncWorldPlugin(PhantomData); @@ -126,8 +122,6 @@ impl Plugin for SyncWorldPlugin { #[component(storage = "SparseSet")] pub struct SyncToSubWorld(PhantomData); -pub type SyncToRenderWorld = SyncToSubWorld; - /// Component added on the main world entities that are synced to the Sub World in order to keep track of the corresponding sub world entity. /// /// Can also be used as a newtype wrapper for sub world entities. @@ -135,8 +129,6 @@ pub type SyncToRenderWorld = SyncToSubWorld; #[component(clone_behavior = Ignore)] pub struct SubEntity(#[deref] Entity, PhantomData); -pub type RenderEntity = SubEntity; - impl SubEntity { #[inline] pub fn id(&self) -> Entity { @@ -204,8 +196,6 @@ pub type MainEntityHashSet = EntityEquivalentHashSet; #[reflect(Component, Default, Clone)] pub struct TemporaryEntity(PhantomData); -pub type TemporaryRenderEntity = TemporaryEntity; - /// A record enum to what entities with [`SyncToSubWorld`] have been added or removed. #[derive(Debug)] pub(crate) enum EntityRecord { @@ -572,7 +562,7 @@ mod sub_entities_world_query_impls { mod tests { use core::marker::PhantomData; - use bevy_app::AppLabel; + use bevy_derive::AppLabel; use bevy_ecs::{ component::Component, entity::Entity, diff --git a/crates/bevy_gizmos_render/Cargo.toml b/crates/bevy_gizmos_render/Cargo.toml index 12c0e103446b6..b40a88d80a934 100644 --- a/crates/bevy_gizmos_render/Cargo.toml +++ b/crates/bevy_gizmos_render/Cargo.toml @@ -20,6 +20,7 @@ bevy_app = { path = "../bevy_app", version = "0.20.0-dev" } bevy_gizmos = { path = "../bevy_gizmos", version = "0.20.0-dev" } bevy_camera = { path = "../bevy_camera", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } bevy_image = { path = "../bevy_image", version = "0.20.0-dev" } bevy_mesh = { path = "../bevy_mesh", version = "0.20.0-dev" } bevy_math = { path = "../bevy_math", version = "0.20.0-dev" } diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 9793aa3bde9ad..0a807762af703 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -16,6 +16,7 @@ trace = [ "bevy_asset?/trace", "bevy_core_pipeline?/trace", "bevy_ecs/trace", + "bevy_extract/trace", "bevy_log/trace", "bevy_pbr?/trace", "bevy_post_process?/trace", @@ -226,6 +227,7 @@ morph = ["bevy_mesh?/morph", "bevy_render?/morph"] # Enables bevy_mesh and bevy_animation morph weight support morph_animation = ["morph", "bevy_animation?/bevy_mesh"] +bevy_extract = ["dep:bevy_extract"] bevy_shader = ["dep:bevy_shader"] bevy_image = ["dep:bevy_image", "bevy_color", "bevy_asset"] bevy_sprite = ["dep:bevy_sprite", "bevy_camera"] @@ -248,6 +250,7 @@ bevy_light = ["dep:bevy_light", "bevy_camera"] bevy_render = [ "dep:bevy_render", "bevy_camera", + "bevy_extract", "bevy_shader", "bevy_color/wgpu-types", "bevy_color/encase", @@ -537,6 +540,7 @@ bevy_post_process = { path = "../bevy_post_process", optional = true, version = bevy_ui_widgets = { path = "../bevy_ui_widgets", optional = true, version = "0.20.0-dev" } bevy_anti_alias = { path = "../bevy_anti_alias", optional = true, version = "0.20.0-dev" } bevy_dev_tools = { path = "../bevy_dev_tools", optional = true, version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", optional = true, version = "0.20.0-dev" } bevy_gilrs = { path = "../bevy_gilrs", optional = true, version = "0.20.0-dev" } bevy_gizmos = { path = "../bevy_gizmos", optional = true, version = "0.20.0-dev", default-features = false } bevy_gizmos_render = { path = "../bevy_gizmos_render", optional = true, version = "0.20.0-dev", default-features = false } diff --git a/crates/bevy_pbr/Cargo.toml b/crates/bevy_pbr/Cargo.toml index 06c375a0bd775..7c44d8b9f4c86 100644 --- a/crates/bevy_pbr/Cargo.toml +++ b/crates/bevy_pbr/Cargo.toml @@ -44,6 +44,7 @@ bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.20.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.20.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } bevy_gltf = { path = "../bevy_gltf", version = "0.20.0-dev", optional = true } bevy_light = { path = "../bevy_light", version = "0.20.0-dev" } bevy_log = { path = "../bevy_log", version = "0.20.0-dev" } diff --git a/crates/bevy_pbr/src/cluster/gpu.rs b/crates/bevy_pbr/src/cluster/gpu.rs index 75b059f54bb85..c9405de226ee4 100644 --- a/crates/bevy_pbr/src/cluster/gpu.rs +++ b/crates/bevy_pbr/src/cluster/gpu.rs @@ -129,7 +129,6 @@ impl Plugin for GpuClusteringPlugin { app.add_plugins(ExtractResourcePlugin::< GlobalClusterSettings, - RenderApp, GpuClusteringPlugin, >::default()); } diff --git a/crates/bevy_pbr/src/decal/clustered.rs b/crates/bevy_pbr/src/decal/clustered.rs index d02b730aa939b..5024e572f73e0 100644 --- a/crates/bevy_pbr/src/decal/clustered.rs +++ b/crates/bevy_pbr/src/decal/clustered.rs @@ -161,7 +161,7 @@ impl Plugin for ClusteredDecalPlugin { fn build(&self, app: &mut App) { load_shader_library!(app, "clustered.wesl"); - app.add_plugins(SyncComponentPlugin::::default()); + app.add_plugins(SyncComponentPlugin::::default()); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index fdc6e7aee8c07..3929587482c11 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -221,7 +221,7 @@ impl Plugin for PbrPlugin { ScreenSpaceAmbientOcclusionPlugin, FogPlugin, ExtractResourcePlugin::::default(), - SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), LightmapPlugin, LightProbePlugin, GpuMeshPreprocessPlugin { @@ -235,11 +235,11 @@ impl Plugin for PbrPlugin { )) .add_plugins(( decal::ForwardDecalPlugin, - SyncComponentPlugin::::default(), - SyncComponentPlugin::::default(), - SyncComponentPlugin::::default(), - SyncComponentPlugin::::default(), - SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), + SyncComponentPlugin::::default(), )) .add_plugins(( ScatteringMediumPlugin, diff --git a/crates/bevy_pbr/src/light_probe/environment_map.rs b/crates/bevy_pbr/src/light_probe/environment_map.rs index 0c037b6485a9e..1f66f1f103e35 100644 --- a/crates/bevy_pbr/src/light_probe/environment_map.rs +++ b/crates/bevy_pbr/src/light_probe/environment_map.rs @@ -48,11 +48,11 @@ use bevy_ecs::{ query::{QueryData, QueryItem}, system::lifetimeless::Read, }; +use bevy_extract::extract_instances::ExtractInstance; use bevy_image::Image; use bevy_light::{EnvironmentMapLight, ParallaxCorrection}; use bevy_math::{Affine3A, Quat, Vec3}; use bevy_render::{ - extract_instances::ExtractInstance, render_asset::RenderAssets, render_resource::{ binding_types, BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, TextureSampleType, diff --git a/crates/bevy_pbr/src/light_probe/generate.rs b/crates/bevy_pbr/src/light_probe/generate.rs index e9df56b25d71d..fbc63be4bc693 100644 --- a/crates/bevy_pbr/src/light_probe/generate.rs +++ b/crates/bevy_pbr/src/light_probe/generate.rs @@ -128,11 +128,7 @@ impl Plugin for EnvironmentMapGenerationPlugin { embedded_asset!(app, "environment_filter.wesl"); embedded_asset!(app, "copy.wesl"); - app.add_plugins(SyncComponentPlugin::< - GeneratedEnvironmentMapLight, - RenderApp, - Self, - >::default()) + app.add_plugins(SyncComponentPlugin::::default()) .add_systems(Update, generate_environment_map_light); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index 2b3e17f128e65..f764073ec035a 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -12,6 +12,7 @@ use bevy_ecs::{ schedule::IntoScheduleConfigs, system::{Commands, Local, Query, Res, ResMut}, }; +use bevy_extract::extract_instances::ExtractInstancesPlugin; use bevy_image::Image; use bevy_light::{ cluster::ClusterVisibilityClass, EnvironmentMapLight, IrradianceVolume, LightProbe, @@ -19,7 +20,6 @@ use bevy_light::{ use bevy_math::{Affine3A, FloatOrd, Mat4, Quat, Vec3, Vec4}; use bevy_platform::collections::HashMap; use bevy_render::{ - extract_instances::ExtractInstancesPlugin, render_asset::RenderAssets, render_resource::{DynamicUniformBuffer, Sampler, ShaderType, TextureView}, renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue, WgpuWrapper}, diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index c9e0ae74f000f..20097401f5876 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -53,7 +53,6 @@ use bevy_render::{ batching::gpu_preprocessing::GpuPreprocessingSupport, extract_resource::ExtractResource, mesh::RenderMesh, - prelude::*, render_phase::*, render_resource::*, renderer::RenderDevice, diff --git a/crates/bevy_pbr/src/volumetric_fog/mod.rs b/crates/bevy_pbr/src/volumetric_fog/mod.rs index 2d522b175d24c..8813cce6101a3 100644 --- a/crates/bevy_pbr/src/volumetric_fog/mod.rs +++ b/crates/bevy_pbr/src/volumetric_fog/mod.rs @@ -70,7 +70,7 @@ impl Plugin for VolumetricFogPlugin { let plane_mesh = meshes.add(Plane3d::new(Vec3::Z, Vec2::ONE).mesh()); let cube_mesh = meshes.add(Cuboid::new(1.0, 1.0, 1.0).mesh()); - app.add_plugins(SyncComponentPlugin::::default()); + app.add_plugins(SyncComponentPlugin::::default()); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; diff --git a/crates/bevy_pbr/src/wireframe.rs b/crates/bevy_pbr/src/wireframe.rs index b51f128546c6d..93a4daf7133a0 100644 --- a/crates/bevy_pbr/src/wireframe.rs +++ b/crates/bevy_pbr/src/wireframe.rs @@ -38,7 +38,6 @@ use bevy_render::{ allocator::{MeshAllocator, MeshAllocatorSettings, MeshSlabs}, RenderMesh, RenderMeshBufferInfo, }, - prelude::*, render_asset::{ prepare_assets, PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets, }, @@ -55,7 +54,8 @@ use bevy_render::{ ExtractedView, NoIndirectDrawing, RenderVisibilityRanges, RenderVisibleEntities, RetainedViewEntity, ViewDepthStencilTexture, ViewTarget, }, - Extract, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, RenderStartup, RenderSystems, + Extract, ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, + RenderStartup, RenderSystems, }; use bevy_shader::Shader; use bytemuck::{Pod, Zeroable}; diff --git a/crates/bevy_post_process/Cargo.toml b/crates/bevy_post_process/Cargo.toml index 48b76de3acdb2..0ba981e438e09 100644 --- a/crates/bevy_post_process/Cargo.toml +++ b/crates/bevy_post_process/Cargo.toml @@ -21,6 +21,7 @@ bevy_color = { path = "../bevy_color", version = "0.20.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.20.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } bevy_image = { path = "../bevy_image", version = "0.20.0-dev" } bevy_camera = { path = "../bevy_camera", version = "0.20.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.20.0-dev" } diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index 4eb05f6618d3a..ea980fc26e80a 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -55,6 +55,8 @@ bevy_derive = { path = "../bevy_derive", version = "0.20.0-dev" } bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } bevy_encase_derive = { path = "../bevy_encase_derive", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } +bevy_extract_macros = { path = "../bevy_extract/macros", version = "0.20.0-dev" } bevy_math = { path = "../bevy_math", version = "0.20.0-dev" } bevy_material = { path = "../bevy_material", version = "0.20.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.20.0-dev" } diff --git a/crates/bevy_render/macros/Cargo.toml b/crates/bevy_render/macros/Cargo.toml index f4af598044443..724d9759746ca 100644 --- a/crates/bevy_render/macros/Cargo.toml +++ b/crates/bevy_render/macros/Cargo.toml @@ -13,6 +13,7 @@ proc-macro = true [dependencies] bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.20.0-dev" } +bevy_extract_macros = { path = "../../bevy_extract/macros", version = "0.20.0-dev" } syn = { version = "2.0", features = ["full"] } proc-macro2 = "1.0" diff --git a/crates/bevy_render/macros/src/lib.rs b/crates/bevy_render/macros/src/lib.rs index aa858cc5b1280..922e17eb96ec7 100644 --- a/crates/bevy_render/macros/src/lib.rs +++ b/crates/bevy_render/macros/src/lib.rs @@ -2,8 +2,6 @@ #![cfg_attr(docsrs, feature(doc_cfg))] mod as_bind_group; -mod extract_component; -mod extract_resource; mod specializer; use bevy_macro_utils::{derive_label, BevyManifest}; @@ -19,48 +17,6 @@ pub(crate) fn bevy_ecs_path() -> syn::Path { BevyManifest::shared(|manifest| manifest.get_path("bevy_ecs")) } -#[proc_macro_derive(ExtractResource, attributes(extract_app))] -pub fn derive_extract_resource(input: TokenStream) -> TokenStream { - extract_resource::derive_extract_resource(input) -} - -/// Implements `ExtractComponent` trait for a component. -/// -/// The component must implement [`Clone`]. -/// The component will be extracted into the render world via cloning. -/// Note that this only enables extraction of the component, it does not execute the extraction. -/// See `ExtractComponentPlugin` to actually perform the extraction. -/// -/// If you only want to extract a component conditionally, you may use the `extract_component_filter` attribute. -/// To specify `SyncComponent::Target`, you can use the `extract_component_sync_target` attribute. -/// -/// # Example -/// -/// ```no_compile -/// use bevy_ecs::component::Component; -/// use bevy_render_macros::ExtractComponent; -/// -/// #[derive(Component, Clone, ExtractComponent)] -/// #[extract_component_filter(With)] -/// #[extract_component_sync_target((Self, OtherNeedsCleanup))] -/// pub struct Foo { -/// pub should_foo: bool, -/// } -/// -/// // Without a filter (unconditional). -/// #[derive(Component, Clone, ExtractComponent)] -/// pub struct Bar { -/// pub should_bar: bool, -/// } -/// ``` -#[proc_macro_derive( - ExtractComponent, - attributes(extract_component_filter, extract_component_sync_target, extract_app) -)] -pub fn derive_extract_component(input: TokenStream) -> TokenStream { - extract_component::derive_extract_component(input) -} - #[proc_macro_derive( AsBindGroup, attributes( diff --git a/crates/bevy_render/src/gpu_readback.rs b/crates/bevy_render/src/gpu_readback.rs index a09dc58159d57..b7892c1db3642 100644 --- a/crates/bevy_render/src/gpu_readback.rs +++ b/crates/bevy_render/src/gpu_readback.rs @@ -25,11 +25,11 @@ use bevy_ecs::{ system::{Commands, Query, Res}, }; use bevy_ecs::{schedule::IntoScheduleConfigs, template::FromTemplate}; +use bevy_extract_macros::ExtractComponent; use bevy_image::{Image, TextureFormatPixelInfo}; use bevy_log::{debug, warn}; use bevy_platform::collections::HashMap; use bevy_reflect::Reflect; -use bevy_render_macros::ExtractComponent; use encase::internal::ReadFrom; use encase::private::Reader; use encase::ShaderType; diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index e8bb7d0c67980..04608acec1a27 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -42,11 +42,23 @@ pub mod combined_bind_group; pub mod diagnostic; pub mod erased_render_asset; pub mod error_handler; -pub mod extract_component; -pub mod extract_instances; -mod extract_param; -pub mod extract_plugin; -pub mod extract_resource; +pub mod extract_component { + pub type ExtractComponentPlugin = + bevy_extract::extract_component::ExtractComponentPlugin; + + pub use crate::uniform::{ComponentUniforms, DynamicUniformIndex, UniformComponentPlugin}; + + pub use bevy_extract::extract_component::ExtractComponent; +} +pub mod extract_plugin { + pub use bevy_extract::extract_plugin::ExtractPlugin; +} +pub mod extract_resource { + pub type ExtractResourcePlugin = + bevy_extract::extract_resource::ExtractResourcePlugin; + + pub use bevy_extract::extract_resource::{extract_resource, ExtractResource}; +} pub mod globals; pub mod gpu_component_array_buffer; pub mod gpu_readback; @@ -62,8 +74,21 @@ pub mod renderer; pub mod settings; pub mod slab_allocator; pub mod storage; -pub mod sync_component; -pub mod sync_world; +pub mod sync_component { + pub type SyncComponentPlugin = + bevy_extract::sync_component::SyncComponentPlugin; + + pub use bevy_extract::sync_component::SyncComponent; +} +pub mod sync_world { + pub type SyncToRenderWorld = bevy_extract::sync_world::SyncToSubWorld; + + pub type RenderEntity = bevy_extract::sync_world::SubEntity; + + pub type TemporaryRenderEntity = bevy_extract::sync_world::TemporaryEntity; + + pub use bevy_extract::sync_world::{MainEntity, MainEntityHashMap, MainEntityHashSet}; +} #[cfg(test)] pub(crate) mod test_utils; pub mod texture; @@ -80,13 +105,14 @@ pub mod prelude { view::Msaa, ExtractSchedule, }; } -pub use extract_param::Extract; -pub use extract_plugin::{ExtractSchedule, MainWorld}; +pub use bevy_extract::{ + extract_param::Extract, + extract_plugin::{ExtractSchedule, MainWorld}, +}; use crate::{ camera::CameraPlugin, error_handler::{RenderErrorHandler, RenderState}, - extract_plugin::ExtractPlugin, gpu_readback::GpuReadbackPlugin, material_bind_groups::MaterialBindGroupPlugin, mesh::{MeshRenderAssetPlugin, RenderMesh}, @@ -107,6 +133,7 @@ use bevy_ecs::{ prelude::*, schedule::{InternedScheduleLabel, ScheduleLabel}, }; +use bevy_extract::ExtractPlugin; use bevy_platform::time::Instant; use bevy_shader::{load_shader_library, Shader, ShaderLoader}; use bevy_time::TimeSender; diff --git a/crates/bevy_render/src/pipelined_rendering.rs b/crates/bevy_render/src/pipelined_rendering.rs index babaf098cb0c8..438875ec6d225 100644 --- a/crates/bevy_render/src/pipelined_rendering.rs +++ b/crates/bevy_render/src/pipelined_rendering.rs @@ -105,7 +105,7 @@ impl Drop for RenderAppChannels { /// - And finally the `main app schedule` is run. /// - Once both the `main app schedule` and the `render schedule` are finished running, `extract` is run again. /// -/// [`SyncWorldPlugin`]: crate::sync_world::SyncWorldPlugin +/// [`SyncWorldPlugin`]: bevy_extract::sync_world::SyncWorldPlugin #[derive(Default)] pub struct PipelinedRenderingPlugin; diff --git a/crates/bevy_render/src/texture/manual_texture_view.rs b/crates/bevy_render/src/texture/manual_texture_view.rs index 17d7d0bcdb6fd..67b7ade119b65 100644 --- a/crates/bevy_render/src/texture/manual_texture_view.rs +++ b/crates/bevy_render/src/texture/manual_texture_view.rs @@ -1,8 +1,8 @@ use bevy_camera::ManualTextureViewHandle; use bevy_ecs::resource::Resource; +use bevy_extract_macros::ExtractResource; use bevy_math::UVec2; use bevy_platform::collections::HashMap; -use bevy_render_macros::ExtractResource; use wgpu::TextureFormat; use crate::{render_resource::TextureView, RenderApp}; diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index bc58de76c4072..c0b3763d7b452 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -31,11 +31,11 @@ use bevy_app::{App, Plugin}; use bevy_color::{LinearRgba, Oklaba, Srgba}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{prelude::*, VariantDefaults}; +use bevy_extract_macros::ExtractComponent; use bevy_image::ToExtents; use bevy_math::{mat3, vec2, vec3, Mat3, Mat4, UVec4, Vec2, Vec3, Vec4, Vec4Swizzles}; use bevy_platform::collections::{hash_map::Entry, HashMap}; use bevy_reflect::{std_traits::ReflectDefault, Reflect}; -use bevy_render_macros::ExtractComponent; use bevy_transform::components::GlobalTransform; use core::{ ops::Range, diff --git a/crates/bevy_sprite_render/Cargo.toml b/crates/bevy_sprite_render/Cargo.toml index 7153220e0ad22..557261cc3326c 100644 --- a/crates/bevy_sprite_render/Cargo.toml +++ b/crates/bevy_sprite_render/Cargo.toml @@ -20,6 +20,7 @@ bevy_asset = { path = "../bevy_asset", version = "0.20.0-dev" } bevy_color = { path = "../bevy_color", version = "0.20.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } bevy_image = { path = "../bevy_image", version = "0.20.0-dev" } bevy_camera = { path = "../bevy_camera", version = "0.20.0-dev" } bevy_mesh = { path = "../bevy_mesh", version = "0.20.0-dev" } diff --git a/crates/bevy_sprite_render/src/mesh2d/wireframe2d.rs b/crates/bevy_sprite_render/src/mesh2d/wireframe2d.rs index 7e810667c1bd6..b13211de46245 100644 --- a/crates/bevy_sprite_render/src/mesh2d/wireframe2d.rs +++ b/crates/bevy_sprite_render/src/mesh2d/wireframe2d.rs @@ -33,7 +33,6 @@ use bevy_render::{ allocator::{MeshAllocator, MeshSlabId, MeshSlabs}, RenderMesh, }, - prelude::*, render_asset::{ prepare_assets, PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets, }, @@ -50,7 +49,8 @@ use bevy_render::{ ExtractedView, RenderVisibleEntities, RetainedViewEntity, ViewDepthStencilTexture, ViewTarget, }, - Extract, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, RenderStartup, RenderSystems, + Extract, ExtractSchedule, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, + RenderStartup, RenderSystems, }; use bevy_shader::Shader; use core::{hash::Hash, ops::Range}; diff --git a/crates/bevy_ui_render/Cargo.toml b/crates/bevy_ui_render/Cargo.toml index 005c03093ef83..0ead3aaa56cab 100644 --- a/crates/bevy_ui_render/Cargo.toml +++ b/crates/bevy_ui_render/Cargo.toml @@ -17,6 +17,7 @@ bevy_color = { path = "../bevy_color", version = "0.20.0-dev" } bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.20.0-dev" } bevy_derive = { path = "../bevy_derive", version = "0.20.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.20.0-dev" } +bevy_extract = { path = "../bevy_extract", version = "0.20.0-dev" } bevy_image = { path = "../bevy_image", version = "0.20.0-dev" } bevy_input_focus = { path = "../bevy_input_focus", version = "0.20.0-dev" } bevy_math = { path = "../bevy_math", version = "0.20.0-dev" } diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 4ba9902ceabf6..286b01b3f9336 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -79,6 +79,7 @@ This is the complete `bevy` cargo feature list, without "profiles" or "collectio |bevy_core_pipeline|Provides cameras and other basic render pipeline features| |bevy_debug_stepping|Enable stepping-based debugging of Bevy systems| |bevy_dev_tools|Provides a collection of developer tools| +|bevy_extract|Provides the ability to extract entities from an ECS main world to a sub world| |bevy_feathers|Feathers widget collection.| |bevy_gilrs|Adds gamepad support| |bevy_gizmos|Adds support for gizmos|