diff --git a/.cargo/config_fast_builds b/.cargo/config_fast_builds index 8d0a98d9e0096..cfbcd697c8f03 100644 --- a/.cargo/config_fast_builds +++ b/.cargo/config_fast_builds @@ -1,20 +1,23 @@ -# Rename this file to `config.toml` to enable "fast build" configuration. Please read the notes below. +# Add the contents of this file to `config.toml` to enable "fast build" configuration. Please read the notes below. # NOTE: For maximum performance, build using a nightly compiler # If you are using rust stable, remove the "-Zshare-generics=y" below. [target.x86_64-unknown-linux-gnu] -linker = "/usr/bin/clang" +linker = "clang" rustflags = ["-Clink-arg=-fuse-ld=lld", "-Zshare-generics=y"] # NOTE: you must manually install https://github.com/michaeleisel/zld on mac. you can easily do this with the "brew" package manager: # `brew install michaeleisel/zld/zld` [target.x86_64-apple-darwin] -rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/zld", "-Zshare-generics=y", "-Zrun-dsymutil=no"] +rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/zld", "-Zshare-generics=y"] + +[target.aarch64-apple-darwin] +rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/bin/zld", "-Zshare-generics=y"] [target.x86_64-pc-windows-msvc] linker = "rust-lld.exe" -rustflags = ["-Zshare-generics=y"] +rustflags = ["-Zshare-generics=n"] # Optional: Uncommenting the following improves compile times, but reduces the amount of debug info to 'line number tables only' # In most cases the gains are negligible, but if you are on macos and have slow compile times you should see significant gains. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000..e030b9d97e2d0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,19 @@ +# From: https://docs.github.com/en/github/getting-started-with-github/configuring-git-to-handle-line-endings +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.rs text eol=lf +*.toml text eol=lf +*.frag text eol=lf +*.vert text eol=lf +*.wgsl text eol=lf + +# Declare files that will always have CRLF line endings on checkout. +*.sln text eol=crlf + +# Denote all files that are truly binary and should not be modified. +*.png binary +*.jpg binary +*.ttf binary diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..432363bdcc764 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,55 @@ +--- +name: Bug Report +about: Report a bug to help us improve! +title: '' +labels: C-Bug, S-Needs-Triage +assignees: '' +--- + +## Bevy version + +The release number or commit hash of the version you're using. + +## \[Optional\] Relevant system information + +If you cannot get Bevy to build or run on your machine, please include: + +- the Rust version you're using (you can get this by running `cargo --version`) + - Bevy relies on the "latest stable release" of Rust + - nightly should generally work, but there are sometimes regressions: please let us know! +- the operating system or browser used, including its version + - e.g. Windows 10, Ubuntu 18.04, iOS 14 + +If your bug is rendering-related, copy the adapter info that appears when you run Bevy. + +```ignore +`AdapterInfo { name: "NVIDIA GeForce RTX 2070", vendor: 4318, device: 7938, device_type: DiscreteGpu, backend: Vulkan }` +``` + +You should also consider testing the examples of our upstream dependencies to help isolate any setup-specific issue: + +- [`wgpu`](https://github.com/gfx-rs/wgpu) for rendering problems +- [`winit`](https://github.com/rust-windowing/winit) for input and window management +- [`gilrs`](https://docs.rs/gilrs/latest/gilrs/) for gamepad inputs + +## What you did + +Describe how you arrived at the problem. If you can, consider providing a code snippet or link. + +## What went wrong + +If it's not clear, break this out into: + +- what were you expecting? +- what actually happened? + +## Additional information + +Other information that can be used to further reproduce or isolate the problem. +This commonly includes: + +- screenshots +- logs +- theories about what might be going wrong +- workarounds that you used +- links to related bugs, PRs or discussions diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000000..ef173eb32400e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +contact_links: + - name: Question + url: https://github.com/bevyengine/bevy/discussions/categories/q-a + about: Questions about how to use or contribute to Bevy belong in Github Discussions. + You can use the search to check if someone already answered your question! diff --git a/.github/ISSUE_TEMPLATE/docs_improvement.md b/.github/ISSUE_TEMPLATE/docs_improvement.md new file mode 100644 index 0000000000000..1bae3c9cf634c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/docs_improvement.md @@ -0,0 +1,13 @@ +--- +name: Docs Improvement +about: Help us write better docs to catch common issues! +title: '' +labels: C-Docs, S-Needs-Triage +assignees: '' +--- + +## How can Bevy's documentation be improved? + +Provide a link to the documentation and describe how it could be improved. In what ways is it incomplete, incorrect, or misleading? + +If you have suggestions on exactly what the new docs should say, feel free to include them here. Or alternatively, make the changes yourself and [create a pull request](https://bevyengine.org/learn/book/contributing/code/) instead. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..cc6e73125dab9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature Request +about: Propose a new feature! +title: '' +labels: C-Enhancement, S-Needs-Triage +assignees: '' +--- + +## What problem does this solve or what need does it fill? + +A description of why this particular feature should be added. + +## What solution would you like? + +The solution you propose for the problem presented. + +## What alternative(s) have you considered? + +Other solutions to solve and/or work around the problem presented. + +## Additional context + +Any other information you would like to add such as related previous work, +screenshots, benchmarks, etc. diff --git a/.github/bors.toml b/.github/bors.toml new file mode 100644 index 0000000000000..0380d3003e1a9 --- /dev/null +++ b/.github/bors.toml @@ -0,0 +1,20 @@ +status = [ + "build (windows-latest)", + "build (ubuntu-latest)", + "build (macos-latest)", + "build-wasm", + "build-android", + "markdownlint", + "run-examples", + "run-examples-on-wasm", + "check-doc", + "check-missing-examples-in-docs", + # "check-unused-dependencies", + "ci", + "check-compiles", + "build-and-install-on-iOS", + "run-examples-on-windows-dx12", +] + +use_squash_merge = true +block_labels = ["S-Pre-Relicense"] diff --git a/.github/contributing/engine_style_guide.md b/.github/contributing/engine_style_guide.md new file mode 100644 index 0000000000000..36b3781bd97dc --- /dev/null +++ b/.github/contributing/engine_style_guide.md @@ -0,0 +1,38 @@ +# Style guide: Engine + +## Contributing + +For more advice on contributing to the engine, see the [relevant section](../../CONTRIBUTING.md#Contributing-your-own-ideas) of `CONTRIBUTING.md`. + +## General guidelines + +1. Prefer granular imports over glob imports like `bevy_ecs::prelude::*`. +2. Use a consistent comment style: + 1. `///` doc comments belong above `#[derive(Trait)]` invocations. + 2. `//` comments should generally go above the line in question, rather than in-line. + 3. Avoid `/* */` block comments, even when writing long comments. + 4. Use \`variable_name\` code blocks in comments to signify that you're referring to specific types and variables. + 5. Start comments with capital letters. End them with a period if they are sentence-like. +3. Use comments to organize long and complex stretches of code that can't sensibly be refactored into separate functions. + +## Rust API guidelines + +As a reference for our API development we are using the [Rust API guidelines][Rust API guidelines]. Generally, these should be followed, except for the following areas of disagreement: + +### Areas of disagreements + +Some areas mentioned in the [Rust API guidelines][Rust API guidelines] we do not agree with. These areas will be expanded whenever we find something else we do not agree with, so be sure to check these from time to time. + +> All items have a rustdoc example + +- This guideline is too strong and not applicable for everything inside of the Bevy game engine. For functionality that requires more context or needs a more interactive demonstration (such as rendering or input features), make use of the `examples` folder instead. + +> Examples use ?, not try!, not unwrap + +- This guideline is usually reasonable, but not always required. + +> Only smart pointers implement Deref and DerefMut + +- Generally a good rule of thumb, but we're probably going to deliberately violate this for single-element wrapper types like `Life(u32)`. The behavior is still predictable and it significantly improves ergonomics / new user comprehension. + +[Rust API guidelines]: https://rust-lang.github.io/api-guidelines/about.html diff --git a/.github/contributing/example_style_guide.md b/.github/contributing/example_style_guide.md new file mode 100644 index 0000000000000..8b6ee4c7af0cd --- /dev/null +++ b/.github/contributing/example_style_guide.md @@ -0,0 +1,64 @@ +# Style guide: Examples + +For more advice on writing examples, see the [relevant section](../../CONTRIBUTING.md#writing-examples) of CONTRIBUTING.md. + +## Organization + +1. Examples should live in an appropriate subfolder of `/examples`. +2. Examples should be a single file if possible. +3. Assets live in `./assets`. Try to avoid adding new assets unless strictly necessary to keep the repo small. Don't add "large" asset files. +4. Each example should try to follow this order: + 1. Imports + 2. A `fn main()` block + 3. Example logic +5. Try to structure app / plugin construction in the same fashion as the actual code. +6. Examples should typically not have tests, as they are not directly reusable by the Bevy user. + +## Stylistic preferences + +1. Use simple, descriptive variable names. + 1. Avoid names like `MyComponent` in favor of more descriptive terms like `Events`. + 2. Prefer single letter differentiators like `EventsA` and `EventsB` to nonsense words like `EventsFoo` and `EventsBar`. + 3. Avoid repeating the type of variables in their name where possible. For example, `Color` should be preferred to `ColorComponent`. +2. Prefer glob imports of `bevy::prelude::*` and `bevy::sub_crate::*` over granular imports (for terseness). +3. Use a consistent comment style: + 1. `///` doc comments belong above `#[derive(Trait)]` invocations. + 2. `//` comments should generally go above the line in question, rather than in-line. + 3. Avoid `/* */` block comments, even when writing long comments. + 4. Use \`variable_name\` code blocks in comments to signify that you're referring to specific types and variables. + 5. Start comments with capital letters; end them with a period if they are sentence-like. +4. Use comments to organize long and complex stretches of code that can't sensibly be refactored into separate functions. +5. Avoid making variables `pub` unless it is needed for your example. + +## Code conventions + +1. Refactor configurable values ("magic numbers") out into constants with clear names. +2. Prefer `for` loops over `.for_each`. The latter is faster (for now), but it is less clear for beginners, less idiomatic, and less flexible. +3. Use `.single` and `.single_mut` where appropriate. +4. In Queries, prefer `With` filters over actually fetching unused data with `&T`. +5. Prefer disjoint queries using `With` and `Without` over param sets when you need more than one query in a single system. +6. Prefer structs with named fields over tuple structs except in the case of single-field wrapper types. +7. Use enum-labels over string-labels for system / stage / etc. labels. + +## "Feature" examples + +These examples demonstrate the usage of specific engine features in clear, minimal ways. + +1. Focus on demonstrating exactly one feature in an example +2. Try to keep your names divorced from the context of a specific game, and focused on the feature you are demonstrating. +3. Where they exist, show good alternative approaches to accomplish the same task and explain why you may prefer one over the other. +4. Examples should have a visible effect when run, either in the command line or a graphical window. + +## "Game" examples + +These examples show how to build simple games in Bevy in a cohesive way. + +1. Each of these examples lives in the [/examples/games] folder. +2. Aim for minimum but viable status: the game should be playable and not obviously buggy but does not need to be polished, featureful, or terribly fun. +3. Focus on code quality and demonstrating good, extensible patterns for users. + 1. Make good use of enums and states to organize your game logic. + 2. Keep components as small as possible but no smaller: all of the data on a component should generally be accessed at once. + 3. Keep systems small: they should have a clear single purpose. + 4. Avoid duplicating logic across similar entities whenever possible by sharing systems and components. +4. Use `///` doc comments to explain what each function / struct does as if the example were part of a polished production codebase. +5. Arrange your code into modules within the same file to allow for simple code folding / organization. diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 97cb4e89ebb7c..72fbf07616bbd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,8 +4,11 @@ updates: directory: / schedule: interval: weekly - + labels: + - "C-Dependencies" - package-ecosystem: github-actions directory: / schedule: interval: weekly + labels: + - "C-Dependencies" diff --git a/.github/example-run/alien_cake_addict.ron b/.github/example-run/alien_cake_addict.ron new file mode 100644 index 0000000000000..d170958d73bad --- /dev/null +++ b/.github/example-run/alien_cake_addict.ron @@ -0,0 +1,3 @@ +( + exit_after: Some(300) +) diff --git a/.github/example-run/breakout.ron b/.github/example-run/breakout.ron new file mode 100644 index 0000000000000..1d78f6a73ad80 --- /dev/null +++ b/.github/example-run/breakout.ron @@ -0,0 +1,3 @@ +( + exit_after: Some(900) +) diff --git a/.github/example-run/contributors.ron b/.github/example-run/contributors.ron new file mode 100644 index 0000000000000..1d78f6a73ad80 --- /dev/null +++ b/.github/example-run/contributors.ron @@ -0,0 +1,3 @@ +( + exit_after: Some(900) +) diff --git a/.github/example-run/load_gltf.ron b/.github/example-run/load_gltf.ron new file mode 100644 index 0000000000000..d170958d73bad --- /dev/null +++ b/.github/example-run/load_gltf.ron @@ -0,0 +1,3 @@ +( + exit_after: Some(300) +) diff --git a/.github/example-run/minimising.ron b/.github/example-run/minimising.ron new file mode 100644 index 0000000000000..e8577916525db --- /dev/null +++ b/.github/example-run/minimising.ron @@ -0,0 +1,3 @@ +( + exit_after: Some(90) +) diff --git a/.github/example-run/no_renderer.ron b/.github/example-run/no_renderer.ron new file mode 100644 index 0000000000000..22e43495b5e42 --- /dev/null +++ b/.github/example-run/no_renderer.ron @@ -0,0 +1,3 @@ +( + exit_after: Some(100) +) diff --git a/.github/example-run/resizing.ron b/.github/example-run/resizing.ron new file mode 100644 index 0000000000000..f4914e281bcd6 --- /dev/null +++ b/.github/example-run/resizing.ron @@ -0,0 +1,4 @@ +( + // Ensures that the full cycle will run + exit_after: Some(410) +) diff --git a/.github/example-run/scene.ron b/.github/example-run/scene.ron new file mode 100644 index 0000000000000..22e43495b5e42 --- /dev/null +++ b/.github/example-run/scene.ron @@ -0,0 +1,3 @@ +( + exit_after: Some(100) +) diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml new file mode 100644 index 0000000000000..7348131969853 --- /dev/null +++ b/.github/linters/.markdown-lint.yml @@ -0,0 +1,3 @@ +{ + "MD013": false +} \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000000..722f3aabe3aa6 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,27 @@ +# Objective + +- Describe the objective or issue this PR addresses. +- If you're fixing a specific issue, say "Fixes #X". + +## Solution + +- Describe the solution used to achieve the objective above. + +--- + +## Changelog + +> This section is optional. If this was a trivial fix, or has no externally-visible impact, you can delete this section. + +- What changed as a result of this PR? +- If applicable, organize changes under "Added", "Changed", or "Fixed" sub-headings +- Stick to one or two sentences. If more detail is needed for a particular change, consider adding it to the "Solution" section + - If you can't summarize the work, your change may be unreasonably large / unrelated. Consider splitting your PR to make it easier to review and merge! + +## Migration Guide + +> This section is optional. If there are no breaking changes, you can delete this section. + +- If this PR is a breaking change (relative to the last release of Bevy), describe how a user might need to migrate their code to support these changes +- Simply adding new functionality is not a breaking change. +- Fixing behavior that was definitely a bug, rather than a questionable design choice is not a breaking change. diff --git a/.github/start-wasm-example/.gitignore b/.github/start-wasm-example/.gitignore new file mode 100644 index 0000000000000..75e854d8dcf7a --- /dev/null +++ b/.github/start-wasm-example/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/.github/start-wasm-example/package-lock.json b/.github/start-wasm-example/package-lock.json new file mode 100644 index 0000000000000..4a39b73a25660 --- /dev/null +++ b/.github/start-wasm-example/package-lock.json @@ -0,0 +1,76 @@ +{ + "name": "start-wasm-example", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "start-wasm-example", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^16.0.1" + }, + "devDependencies": { + "@playwright/test": "^1.22.1" + } + }, + "node_modules/@playwright/test": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.22.1.tgz", + "integrity": "sha512-8ouMBUboYslHom41W8bnSEn0TwlAMHhCACwOZeuiAgzukj7KobpZ+UBwrGE0jJ0UblJbKAQNRHXL+z7sDSkb6g==", + "dev": true, + "dependencies": { + "playwright-core": "1.22.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/dotenv": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", + "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/playwright-core": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.22.1.tgz", + "integrity": "sha512-H+ZUVYnceWNXrRf3oxTEKAr81QzFsCKu5Fp//fEjQvqgKkfA1iX3E9DBrPJpPNOrgVzcE+IqeI0fDmYJe6Ynnw==", + "dev": true, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + } + }, + "dependencies": { + "@playwright/test": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.22.1.tgz", + "integrity": "sha512-8ouMBUboYslHom41W8bnSEn0TwlAMHhCACwOZeuiAgzukj7KobpZ+UBwrGE0jJ0UblJbKAQNRHXL+z7sDSkb6g==", + "dev": true, + "requires": { + "playwright-core": "1.22.1" + } + }, + "dotenv": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz", + "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==" + }, + "playwright-core": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.22.1.tgz", + "integrity": "sha512-H+ZUVYnceWNXrRf3oxTEKAr81QzFsCKu5Fp//fEjQvqgKkfA1iX3E9DBrPJpPNOrgVzcE+IqeI0fDmYJe6Ynnw==", + "dev": true + } + } +} diff --git a/.github/start-wasm-example/package.json b/.github/start-wasm-example/package.json new file mode 100644 index 0000000000000..9e10e8134e657 --- /dev/null +++ b/.github/start-wasm-example/package.json @@ -0,0 +1,16 @@ +{ + "name": "start-wasm-example", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": {}, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.22.1" + }, + "dependencies": { + "dotenv": "^16.0.1" + } +} diff --git a/.github/start-wasm-example/playwright.config.ts b/.github/start-wasm-example/playwright.config.ts new file mode 100644 index 0000000000000..f5988c74a2695 --- /dev/null +++ b/.github/start-wasm-example/playwright.config.ts @@ -0,0 +1,107 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './tests', + /* Maximum time one test can run for. */ + timeout: 300_000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000 + }, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: 0, + /* Opt out of parallel tests on CI. */ + workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'list', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + + { + name: 'firefox', + use: { + ...devices['Desktop Firefox'], + }, + }, + + { + name: 'webkit', + use: { + ...devices['Desktop Safari'], + }, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { + // ...devices['Pixel 5'], + // }, + // }, + // { + // name: 'Mobile Safari', + // use: { + // ...devices['iPhone 12'], + // }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { + // channel: 'msedge', + // }, + // }, + // { + // name: 'Google Chrome', + // use: { + // channel: 'chrome', + // }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // port: 3000, + // }, +}; + +export default config; diff --git a/.github/start-wasm-example/tests/wasm_example.spec.ts b/.github/start-wasm-example/tests/wasm_example.spec.ts new file mode 100644 index 0000000000000..32bb4d9d2c6e3 --- /dev/null +++ b/.github/start-wasm-example/tests/wasm_example.spec.ts @@ -0,0 +1,51 @@ +import { test, expect, Page } from '@playwright/test'; + +test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:8000/'); +}); + +const MAX_TIMEOUT_FOR_TEST = 300_000; + +test.describe('WASM example', () => { + test('Wait for success', async ({ page }, test_info) => { + let start = new Date().getTime(); + + let found = false; + while (new Date().getTime() - start < MAX_TIMEOUT_FOR_TEST) { + let msg = await promise_with_timeout(100, on_console(page), "no log found"); + if (msg.includes("no log found")) { + continue; + } + console.log(msg); + if (msg.includes("Test successful")) { + let prefix = process.env.SCREENSHOT_PREFIX === undefined ? "screenshot" : process.env.SCREENSHOT_PREFIX; + await page.screenshot({ path: `${prefix}-${test_info.project.name}.png`, fullPage: true }); + found = true; + break; + } + } + + expect(found).toBe(true); + }); + +}); + +function on_console(page) { + return new Promise(resolve => { + page.on('console', msg => resolve(msg.text())); + }); +} + +async function promise_with_timeout(time_limit, task, failure_value) { + let timeout; + const timeout_promise = new Promise((resolve, reject) => { + timeout = setTimeout(() => { + resolve(failure_value); + }, time_limit); + }); + const response = await Promise.race([task, timeout_promise]); + if (timeout) { + clearTimeout(timeout); + } + return response; +} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6189a6406a269..41184438f9616 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,88 +1,292 @@ name: CI on: - push: - branches: [master] pull_request: - branches: [master] + push: + branches-ignore: + - 'dependabot/**' + - staging-squash-merge.tmp env: CARGO_TERM_COLOR: always + NIGHTLY_TOOLCHAIN: nightly jobs: build: strategy: matrix: - toolchain: [stable, nightly] - os: [windows-2019, ubuntu-20.04, macos-10.15] - exclude: - - os: macos-10.15 - toolchain: nightly + os: [windows-latest, ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} - needs: clean steps: - - uses: actions/checkout@v2 - + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-build-stable-${{ hashFiles('**/Cargo.toml') }} - uses: actions-rs/toolchain@v1 with: - toolchain: ${{ matrix.toolchain }} + toolchain: stable override: true + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev + if: runner.os == 'linux' + - name: Build & run tests + # See tools/ci/src/main.rs for the commands this runs + run: cargo run -p ci -- test + env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-C debuginfo=0 -D warnings" - - uses: actions/cache@v2 + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 with: path: | - target - key: ${{ runner.os }}-cargo-check-test-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.lock') }} - - - name: Install alsa - run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev - if: ${{ runner.os == 'Linux' }} - - - name: Install udev - run: sudo apt-get update; sudo apt-get install --no-install-recommends libudev-dev - if: ${{ runner.os == 'Linux' }} + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-ci-${{ hashFiles('**/Cargo.toml') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: rustfmt, clippy + override: true + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + - name: CI job + # See tools/ci/src/main.rs for the commands this runs + run: cargo run -p ci -- lints - - name: Build - run: cargo check + miri: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-miri-${{ hashFiles('**/Cargo.toml') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.NIGHTLY_TOOLCHAIN }} + components: miri + override: true + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + - name: CI job + # To run the tests one item at a time for troubleshooting, use + # cargo --quiet test --lib -- --list | sed 's/: test$//' | MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-permissive-provenance -Zmiri-disable-weak-memory-emulation" xargs -n1 cargo miri test -p bevy_ecs --lib -- --exact + run: cargo miri test -p bevy_ecs env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-C debuginfo=0 -D warnings" + # -Zrandomize-layout makes sure we dont rely on the layout of anything that might change + RUSTFLAGS: -Zrandomize-layout + # https://github.com/rust-lang/miri#miri--z-flags-and-environment-variables + # -Zmiri-disable-isolation is needed because our executor uses `fastrand` which accesses system time. + # -Zmiri-permissive-provenance disables warnings against int2ptr casts (since those are used by once_cell) + # -Zmiri-ignore-leaks is necessary because a bunch of tests don't join all threads before finishing. + MIRIFLAGS: -Zmiri-ignore-leaks -Zmiri-disable-isolation -Zmiri-permissive-provenance - - name: Run tests - run: cargo test --workspace - if: ${{ runner.os == 'Linux' }} - env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-C debuginfo=0 -D warnings" + check-compiles: + runs-on: ubuntu-latest + needs: ci + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + crates/bevy_ecs_compile_fail_tests/target/ + key: ${{ runner.os }}-cargo-check-compiles-${{ hashFiles('**/Cargo.toml') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev + - name: Check Compile + # See tools/ci/src/main.rs for the commands this runs + run: cargo run -p ci -- compile + build-wasm: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ubuntu-assets-cargo-build-wasm-stable-${{ hashFiles('**/Cargo.toml') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: wasm32-unknown-unknown + override: true - name: Check wasm uses: actions-rs/cargo@v1 with: command: check - target: wasm32-unknown-unknown - override: true + args: --target wasm32-unknown-unknown - clean: + markdownlint: runs-on: ubuntu-latest + needs: check-examples-readme-update-needed + if: always() steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + # Full git history is needed to get a proper list of changed files within `super-linter` + fetch-depth: 0 + - name: Run Markdown Lint + uses: docker://ghcr.io/github/super-linter:slim-v4 + env: + MULTI_STATUS: false + VALIDATE_ALL_CODEBASE: false + VALIDATE_MARKDOWN: true + DEFAULT_BRANCH: main + # Not needed here as only one Linter is used. + #GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run-examples: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Install Bevy dependencies + run: | + sudo apt-get update; + DEBIAN_FRONTEND=noninteractive sudo apt-get install --no-install-recommends -yq \ + libasound2-dev libudev-dev; + - name: install xvfb, llvmpipe and lavapipe + run: | + sudo apt-get update -y -qq + sudo add-apt-repository ppa:oibaf/graphics-drivers -y + sudo apt-get update + sudo apt install -y xvfb libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-run-examples-${{ hashFiles('**/Cargo.toml') }} - uses: actions-rs/toolchain@v1 with: - toolchain: nightly - components: rustfmt, clippy - override: true - - - name: Install alsa - run: sudo apt-get install --no-install-recommends libasound2-dev + toolchain: stable + - name: Build bevy + # this uses the same command as when running the example to ensure build is reused + run: | + TRACE_CHROME=trace-alien_cake_addict.json CI_TESTING_CONFIG=.github/example-run/alien_cake_addict.ron cargo build --example alien_cake_addict --features "bevy_ci_testing,trace,trace_chrome" + - name: Run examples + run: | + for example in .github/example-run/*.ron; do + example_name=`basename $example .ron` + echo "running $example_name - "`date` + time TRACE_CHROME=trace-$example_name.json CI_TESTING_CONFIG=$example xvfb-run cargo run --example $example_name --features "bevy_ci_testing,trace,trace_chrome" + sleep 10 + done + zip traces.zip trace*.json + - name: save traces + uses: actions/upload-artifact@v3 + with: + name: example-traces.zip + path: traces.zip + + check-doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-check-doc-${{ hashFiles('**/Cargo.toml') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + if: runner.os == 'linux' + - name: Build and check doc + # See tools/ci/src/main.rs for the commands this runs + run: cargo run -p ci -- doc + env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-C debuginfo=0" + # This currently report a lot of false positives + # Enable it again once it's fixed - https://github.com/bevyengine/bevy/issues/1983 + # - name: Installs cargo-deadlinks + # run: cargo install --force cargo-deadlinks + # - name: Checks dead links + # run: cargo deadlinks --dir target/doc/bevy + # continue-on-error: true - - name: Install udev - run: sudo apt-get install --no-install-recommends libudev-dev + check-missing-examples-in-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: cargo run -p build-example-pages -- check-missing - - name: Check the format - run: cargo +nightly fmt --all -- --check + check-examples-readme-update-needed: + needs: check-missing-examples-in-docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: cargo run -p build-example-pages -- update + - name: Check for modified files + run: | + echo "if this step fails, run the following command and commit the changed file on your PR." + echo " > cargo run -p build-example-pages -- update" + git diff --quiet HEAD -- - # type complexity must be ignored because we use huge templates for queries - # -A clippy::manual-strip: strip_prefix support was added in 1.45. we want to support earlier rust versions - - name: Run clippy - run: cargo clippy --all-targets --all-features -- -D warnings -A clippy::type_complexity -A clippy::manual-strip + check-unused-dependencies: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-check-unused-dependencies-${{ hashFiles('**/Cargo.toml') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.NIGHTLY_TOOLCHAIN }} + override: true + - name: Installs cargo-udeps + run: cargo install --force cargo-udeps + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev + - name: Run cargo udeps + run: cargo udeps diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml new file mode 100644 index 0000000000000..9ef408201a54a --- /dev/null +++ b/.github/workflows/dependencies.yml @@ -0,0 +1,72 @@ +name: Dependencies + +on: + pull_request: + paths: + - '**/Cargo.toml' + - 'deny.toml' + push: + paths: + - '**/Cargo.toml' + - 'deny.toml' + branches-ignore: + - 'dependabot/**' + - staging-squash-merge.tmp + schedule: + - cron: "0 0 * * 0" + +env: + CARGO_TERM_COLOR: always + +jobs: + check-advisories: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Install cargo-deny + run: cargo install cargo-deny + - name: Check for security advisories and unmaintained crates + run: cargo deny check advisories + + check-bans: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Install cargo-deny + run: cargo install cargo-deny + - name: Check for banned and duplicated dependencies + run: cargo deny check bans + + check-licenses: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Install cargo-deny + run: cargo install cargo-deny + - name: Check for unauthorized licenses + run: cargo deny check licenses + + check-sources: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Install cargo-deny + run: cargo install cargo-deny + - name: Checked for unauthorized crate sources + run: cargo deny check sources diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000000000..a2b1c29d4220a --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,58 @@ +name: Deploy Docs + +on: + push: + branches: + - 'main' + +env: + CARGO_TERM_COLOR: always + RUSTDOCFLAGS: --html-in-header header.html + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + + # This does the following: + # - Replaces the docs icon with one that clearly denotes it's not the released package on crates.io + # - Adds a meta tag that forces Google not to index any page on the site. + - name: Pre-docs-build + run: | + sed -i.bak "s/icon.png/icon-docs-dev.png/" src/lib.rs + echo "" > header.html + + - name: Build docs + run: cargo doc --all-features --no-deps -p bevy + + # This adds the following: + # - A top level redirect to the bevy crate documentation + # - A CNAME file for redirecting the docs domain to the API reference + # - A robots.txt file to forbid any crawling of the site (to defer to the docs.rs site on search engines). + # - A .nojekyll file to disable Jekyll GitHub Pages builds. + - name: Finalize documentation + run: | + echo "" > target/doc/index.html + echo "dev-docs.bevyengine.org" > target/doc/CNAME + echo "User-Agent: *\nDisallow: /" > target/doc/robots.txt + touch target/doc/.nojekyll + + - name: Deploy + uses: JamesIves/github-pages-deploy-action@v4 + with: + branch: gh-pages + folder: target/doc + single-commit: true + force: true diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml new file mode 100644 index 0000000000000..05f76605d4818 --- /dev/null +++ b/.github/workflows/post-release.yml @@ -0,0 +1,59 @@ +name: Post-release version bump + +# how to trigger: https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow +on: + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install cargo-release + run: cargo install cargo-release + + - name: Setup post-release version bump + run: | + # Set the commit author to the github-actions bot. See discussion here for more information: + # https://github.com/actions/checkout/issues/13#issuecomment-724415212 + # https://github.community/t/github-actions-bot-email-address/17204/6 + git config user.name 'Bevy Auto Releaser' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + # Read the current version from Cargo.toml + current_version=$(cargo metadata --format-version 1 --no-deps | \ + jq --raw-output '.packages | .[] | select(.name == "bevy").version') + # Sanity check: current version should be 0.X.Y + if ! grep -q '^0\.[0-9]\+\.[0-9]\+$' <<< "${current_version}"; then + echo "Invalid version (not in 0.X.Y format): ${current_version}" + exit 1 + fi + minor_version=$(sed 's/^0\.\([0-9]\+\).*/\1/' <<< "${current_version}") + next_version=0.$((minor_version + 1)).0-dev + echo "Bumping version to ${next_version}" + # See release.yml for meaning of these arguments + cargo release "${next_version}" \ + --workspace \ + --no-publish \ + --execute \ + --no-tag \ + --no-confirm \ + --no-push \ + --exclude ci \ + --exclude errors \ + --exclude bevy-ios-example \ + --exclude spancmp \ + --exclude build-wasm-example + + - name: Create PR + uses: peter-evans/create-pull-request@v4 + with: + delete-branch: true + base: "main" + title: "Bump Version after Release" + body: | + Bump version after release + This PR has been auto-generated diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000000..ac86f77b2e694 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,56 @@ +name: Release + +# how to trigger: https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow +on: + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install cargo-release + run: cargo install cargo-release + + - name: Setup release + run: | + # Set the commit author to the github-actions bot. See discussion here for more information: + # https://github.com/actions/checkout/issues/13#issuecomment-724415212 + # https://github.community/t/github-actions-bot-email-address/17204/6 + git config user.name 'Bevy Auto Releaser' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + # release: remove the dev suffix, like going from 0.X.0-dev to 0.X.0 + # --workspace: updating all crates in the workspace + # --no-publish: do not publish to crates.io + # --execute: not a dry run + # --no-tag: do not push tag for each new version + # --no-push: do not push the update commits + # --dependent-version upgrade: change 0.X.0-dev in internal dependencies to 0.X.0 + # --exclude: ignore those packages + cargo release release \ + --workspace \ + --no-publish \ + --execute \ + --no-tag \ + --no-confirm \ + --no-push \ + --dependent-version upgrade \ + --exclude ci \ + --exclude errors \ + --exclude bevy-ios-example \ + --exclude spancmp \ + --exclude build-wasm-example + + - name: Create PR + uses: peter-evans/create-pull-request@v4 + with: + delete-branch: true + base: "main" + title: "Preparing Next Release" + body: | + Preparing next release + This PR has been auto-generated diff --git a/.github/workflows/validation-jobs.yml b/.github/workflows/validation-jobs.yml new file mode 100644 index 0000000000000..b97b669a7af09 --- /dev/null +++ b/.github/workflows/validation-jobs.yml @@ -0,0 +1,156 @@ +name: validation jobs + +on: + push: + branches: + - staging + - trying + - main + +env: + CARGO_TERM_COLOR: always + +jobs: + build-and-install-on-iOS: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - uses: actions/cache@v3 + with: + path: | + target + key: ${{ runner.os }}-ios-install-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.lock') }} + + - name: Add iOS targets + run: rustup target add aarch64-apple-ios x86_64-apple-ios + + - name: Build and install iOS app in iOS Simulator. + run: cd examples/ios && make install + + build-android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-build-android-${{ hashFiles('**/Cargo.toml') }} + + - name: Install Android targets + run: rustup target add aarch64-linux-android armv7-linux-androideabi + + - name: Install Cargo APK + run: cargo install --force cargo-apk + + - name: Build APK + run: ANDROID_NDK_ROOT=$ANDROID_NDK_LATEST_HOME cargo apk build --example android_example + + run-examples-on-windows-dx12: + runs-on: windows-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v3 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-windows-run-examples-${{ hashFiles('**/Cargo.toml') }} + + - name: Build bevy + shell: bash + # this uses the same command as when running the example to ensure build is reused + run: | + WGPU_BACKEND=dx12 CI_TESTING_CONFIG=.github/example-run/alien_cake_addict.ron cargo build --example alien_cake_addict --features "bevy_ci_testing" + + - name: Run examples + shell: bash + run: | + for example in .github/example-run/*.ron; do + example_name=`basename $example .ron` + echo "running $example_name - "`date` + time WGPU_BACKEND=dx12 CI_TESTING_CONFIG=$example cargo run --example $example_name --features "bevy_ci_testing" + sleep 10 + done + + run-examples-on-wasm: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v3 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + target: wasm32-unknown-unknown + override: true + + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + ~/.github/start-wasm-example/node_modules + target/ + key: ${{ runner.os }}-wasm-run-examples-${{ hashFiles('**/Cargo.toml') }} + + - name: install xvfb, llvmpipe and lavapipe + run: | + sudo apt-get update -y -qq + sudo add-apt-repository ppa:oibaf/graphics-drivers -y + sudo apt-get update + sudo apt install -y xvfb libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers + + - name: Install wasm-bindgen + run: cargo install --force wasm-bindgen-cli + + - name: Setup playwright + run: | + cd .github/start-wasm-example + npm install + npx playwright install --with-deps + cd ../.. + + - name: First WASM build + run: | + cargo build --release --example ui --target wasm32-unknown-unknown + + - name: Run examples + shell: bash + run: | + # start a webserver + python3 -m http.server --directory examples/wasm & + + xvfb-run cargo run -p build-wasm-example -- --browsers chromium --browsers firefox --frames 25 --test shapes lighting text_debug breakout + + - name: Save screenshots + uses: actions/upload-artifact@v3 + with: + name: screenshots + path: .github/start-wasm-example/screenshot-*.png diff --git a/.gitignore b/.gitignore index e690fffaad245..48bbc7e0e1c91 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ crates/*/target **/*.rs.bk Cargo.lock .cargo/config +.cargo/config.toml /.idea /.vscode -/benches/target \ No newline at end of file +/benches/target diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d56cb46f13cd..efeaf2945eda8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,2056 @@ + + + # Changelog -## Unreleased +While we try to keep the `Unreleased` changes updated, it is often behind and does not include +all merged pull requests. To see a list of all changes since the latest release, you may compare +current changes on git with [previous release tags][git_tag_comparison]. + +[git_tag_comparison]: https://github.com/bevyengine/bevy/compare/v0.6.0...main + +## Version 0.8.0 (2022-07-30) + +### Added + +- [Callable PBR functions][4939] +- [Spotlights][4715] +- [Camera Driven Rendering][4745] +- [Camera Driven Viewports][4898] +- [Visibilty Inheritance, universal `ComputedVisibility`, and `RenderLayers` support][5310] +- [Better Materials: `AsBindGroup` trait and derive, simpler `Material` trait][5053] +- [Derive `AsBindGroup` Improvements: Better errors, more options, update examples][5364] +- [Support `AsBindGroup` for 2d materials as well][5312] +- [Parallel Frustum Culling][4489] +- [Hierarchy commandization][4197] +- [Generate vertex tangents using mikktspace][3872] +- [Add a `SpatialBundle` with `Visibility` and `Transform` components][5344] +- [Add `RegularPolygon` and `Circle` meshes][3730] +- [Add a `SceneBundle` to spawn a scene][2424] +- [Allow higher order systems][4833] +- [Add global `init()` and `get()` accessors for all newtyped `TaskPools`][2250] +- [Add reusable shader functions for transforming position/normal/tangent][4901] +- [Add support for vertex colors][4528] +- [Add support for removing attributes from meshes][5254] +- [Add option to center a window][4999] +- [Add `depth_load_op` configuration field to `Camera3d`][4904] +- [Refactor `Camera` methods and add viewport rect][4948] +- [Add `TextureFormat::R16Unorm` support for `Image`][5249] +- [Add a `VisibilityBundle` with `Visibility` and `ComputedVisibility` components][5335] +- [Add ExtractResourcePlugin][3745] +- [Add depth_bias to SpecializedMaterial][4101] +- [Added `offset` parameter to `TextureAtlas::from_grid_with_padding`][4836] +- [Add the possibility to create custom 2d orthographic cameras][4048] +- [bevy_render: Add `attributes` and `attributes_mut` methods to `Mesh`][3927] +- [Add helper methods for rotating `Transform`s][5151] +- [Enable wgpu profiling spans when using bevy's trace feature][5182] +- [bevy_pbr: rework `extract_meshes`][4240] +- [Add `inverse_projection` and `inverse_view_proj` fields to shader view uniform][5119] +- [Add `ViewRangefinder3d` to reduce boilerplate when enqueuing standard 3D `PhaseItems`][5014] +- [Create `bevy_ptr` standalone crate][4653] +- [Add `IntoIterator` impls for `&Query` and `&mut Query`][4692] +- [Add untyped APIs for `Components` and `Resources`][4447] +- [Add infallible resource getters for `WorldCell`][4104] +- [Add `get_change_ticks` method to `EntityRef` and `EntityMut`][2539] +- [Add comparison methods to `FilteredAccessSet`][4211] +- [Add `Commands::new_from_entities`][4423] +- [Add `QueryState::get_single_unchecked_manual` and its family members][4841] +- [Add `ParallelCommands` system parameter][4749] +- [Add methods for querying lists of entities][4879] +- [Implement `FusedIterator` for eligible `Iterator` types][4942] +- [Add `component_id()` function to `World` and `Components`][5066] +- [Add ability to inspect entity's components][5136] +- [Add a more helpful error to help debug panicking command on despawned entity][5198] +- [Add `ExactSizeIterator` implementation for `QueryCombinatonIter`][5148] +- [Added the `ignore_fields` attribute to the derive macros for `*Label` types][5366] +- [Exact sized event iterators][3863] +- [Add a `clear()` method to the `EventReader` that consumes the iterator][4693] +- [Add helpers to send `Events` from `World`][5355] +- [Add file metadata to `AssetIo`][2123] +- [Add missing audio/ogg file extensions: .oga, .spx][4703] +- [Add `reload_asset` method to `AssetServer`][5106] +- [Allow specifying chrome tracing file path using an environment variable][4618] +- [Create a simple tool to compare traces between executions][4628] +- [Add a tracing span for run criteria][4709] +- [Add tracing spans for `Query::par_for_each` and its variants.][4711] +- [Add a `release_all` method on `Input`][5011] +- [Add a `reset_all` method on `Input`][5015] +- [Add a helper tool to build examples for wasm][4776] +- [bevy_reflect: add a `ReflectFromPtr` type to create `&dyn Reflect` from a `*const ()`][4475] +- [Add a `ReflectDefault` type and add `#[reflect(Default)]` to all component types that implement Default and are user facing][3733] +- [Add a macro to implement `Reflect` for struct types and migrate glam types to use this for reflection][4540] +- [bevy_reflect: reflect arrays][4701] +- [bevy_reflect: reflect char][4790] +- [bevy_reflect: add `GetTypeRegistration` impl for reflected tuples][4226] +- [Add reflection for `Resources`][5175] +- [bevy_reflect: add `as_reflect` and `as_reflect_mut` methods on `Reflect`][4350] +- [Add an `apply_or_insert` method to `ReflectResource` and `ReflectComponent`][5201] +- [bevy_reflect: `IntoIter` for `DynamicList` and `DynamicMap`][4108] +- [bevy_reflect: Add `PartialEq` to reflected `f32`s and `f64`s][4217] +- [Create mutable versions of `TypeRegistry` methods][4484] +- [bevy_reflect: add a `get_boxed` method to `reflect_trait`][4120] +- [bevy_reflect: add `#[reflect(default)]` attribute for `FromReflect`][4140] +- [bevy_reflect: add statically available type info for reflected types][4042] +- [Add an `assert_is_exclusive_system` function][5275] +- [bevy_ui: add a multi-windows check for `Interaction` (we dont yet support multiple windows)][5225] + +### Changed + +- [Depend on Taffy (a Dioxus and Bevy-maintained fork of Stretch)][4716] +- [Use lifetimed, type erased pointers in bevy_ecs][3001] +- [Migrate to `encase` from `crevice`][4339] +- [Update `wgpu` to 0.13][5168] +- [Pointerfication followup: Type safety and cleanup][4621] +- [bevy_ptr works in no_std environments][4760] +- [Fail to compile on 16-bit platforms][4736] +- [Improve ergonomics and reduce boilerplate around creating text elements][5343] +- [Don't cull `Ui` nodes that have a rotation][5389] +- [Rename `ElementState` to `ButtonState`][4314] +- [Move `Size` to `bevy_ui`][4285] +- [Move `Rect` to `bevy_ui` and rename it to `UiRect`][4276] +- [Modify `FontAtlas` so that it can handle fonts of any size][3592] +- [Rename `CameraUi`][5234] +- [Remove `task_pool` parameter from `par_for_each(_mut)`][4705] +- [Copy `TaskPool` resoures to sub-Apps][4792] +- [Allow closing windows at runtime][3575] +- [Move the configuration of the `WindowPlugin` to a `Resource`][5227] +- [Optionally resize `Window` canvas element to fit parent element][4726] +- [Change window resolution types from tuple to `Vec2`][5276] +- [Update time by sending frame `Instant` through a channel][4744] +- [Split time functionality into `bevy_time`][4187] +- [Split mesh shader files to make the shaders more reusable][4867] +- [Set `naga` capabilities corresponding to `wgpu` features][4824] +- [Separate out PBR lighting, shadows, clustered forward, and utils from pbr.wgsl][4938] +- [Separate PBR and tonemapping into 2 functions][5078] +- [Make `RenderStage::Extract` run on the render world][4402] +- [Change default `FilterMode` of `Image` to `Linear`][4465] +- [bevy_render: Fix KTX2 UASTC format mapping][4569] +- [Allow rendering meshes without UV coordinate data][5222] +- [Validate vertex attribute format on insertion][5259] +- [Use `Affine3A` for `GlobalTransform`to allow any affine transformation][4379] +- [Recalculate entity `AABB`s when meshes change][4944] +- [Change `check_visibility` to use thread-local queues instead of a channel][4663] +- [Allow unbatched render phases to use unstable sorts][5049] +- [Extract resources into their target location][5271] +- [Enable loading textures of unlimited size][5305] +- [Do not create nor execute render passes which have no `PhaseItems` to draw][4643] +- [Filter material handles on extraction][4178] +- [Apply vertex colors to `ColorMaterial` and `Mesh2D`][4812] +- [Make `MaterialPipelineKey` fields public][4508] +- [Simplified API to get NDC from camera and world position][4041] +- [Set `alpha_mode` based on alpha value][4658] +- [Make `Wireframe` respect `VisibleEntities`][4660] +- [Use const `Vec2` in lights cluster and bounding box when possible][4602] +- [Make accessors for mesh vertices and indices public][3906] +- [Use `BufferUsages::UNIFORM` for `SkinnedMeshUniform`][4816] +- [Place origin of `OrthographicProjection` at integer pixel when using `ScalingMode::WindowSize`][4085] +- [Make `ScalingMode` more flexible][3253] +- [Move texture sample out of branch in `prepare_normal`][5129] +- [Make the fields of the `Material2dKey` public][5212] +- [Use collect to build mesh attributes][5255] +- [Replace `ReadOnlyFetch` with `ReadOnlyWorldQuery`][4626] +- [Replace `ComponentSparseSet`'s internals with a `Column`][4909] +- [Remove QF generics from all `Query/State` methods and types][5170] +- [Remove `.system()`][4499] +- [Make change lifespan deterministic and update docs][3956] +- [Make derived `SystemParam` readonly if possible][4650] +- [Merge `matches_archetype` and `matches_table`][4807] +- [Allows conversion of mutable queries to immutable queries][5376] +- [Skip `drop` when `needs_drop` is `false`][4773] +- [Use u32 over usize for `ComponentSparseSet` indicies][4723] +- [Remove redundant `ComponentId` in `Column`][4855] +- [Directly copy moved `Table` components to the target location][5056] +- [`SystemSet::before` and `SystemSet::after` now take `AsSystemLabel`][4503] +- [Converted exclusive systems to parallel systems wherever possible][2774] +- [Improve `size_hint` on `QueryIter`][4244] +- [Improve debugging tools for change detection][4160] +- [Make `RunOnce` a non-manual `System` impl][3922] +- [Apply buffers in `ParamSet`][4677] +- [Don't allocate for `ComponentDescriptors` of non-dynamic component types][4725] +- [Mark mutable APIs under ECS storage as `pub(crate)`][5065] +- [Update `ExactSizeIterator` impl to support archetypal filters (`With`, `Without`)][5124] +- [Removed world cell from places where split multable access is not needed][5167] +- [Add Events to `bevy_ecs` prelude][5159] +- [Improve `EntityMap` API][5231] +- [Implement `From` for `ShouldRun`.][5306] +- [Allow iter combinations on custom world queries][5286] +- [Simplify design for `*Label`s][4957] +- [Tidy up the code of events][4713] +- [Rename `send_default_event` to `send_event_default` on world][5383] +- [enable optional dependencies to stay optional][5023] +- [Remove the dependency cycles][5171] +- [Enforce type safe usage of Handle::get][4794] +- [Export anyhow::error for custom asset loaders][5359] +- [Update `shader_material_glsl` example to include texture sampling][5215] +- [Remove unused code in game of life shader][5349] +- [Make the contributor birbs bounce to the window height][5274] +- [Improve Gamepad D-Pad Button Detection][5220] +- [bevy_reflect: support map insertio][5173] +- [bevy_reflect: improve debug formatting for reflected types][4218] +- [bevy_reflect_derive: big refactor tidying up the code][4712] +- [bevy_reflect: small refactor and default `Reflect` methods][4739] +- [Make `Reflect` safe to implement][5010] +- [`bevy_reflect`: put `serialize` into external `ReflectSerialize` type][4782] +- [Remove `Serialize` impl for `dyn Array` and friends][4780] +- [Re-enable `#[derive(TypeUuid)]` for generics][4118] +- [Move primitive type registration into `bevy_reflect`][4844] +- [Implement reflection for more `glam` types][5194] +- [Make `reflect_partial_eq` return more accurate results][5210] +- [Make public macros more robust with `$crate`][4655] +- [Ensure that the parent is always the expected entity][4717] +- [Support returning data out of `with_children`][4708] +- [Remove `EntityMut::get_unchecked`][4547] +- [Diagnostics: meaningful error when graph node has wrong number of inputs][4924] +- [Remove redundant `Size` import][5339] +- [Export and register `Mat2`.][5324] +- [Implement `Debug` for `Gamepads`][5291] +- [Update codebase to use `IntoIterator` where possible.][5269] +- [Rename `headless_defaults` example to `no_renderer` for clarity][5263] +- [Remove dead `SystemLabelMarker` struct][5190] +- [bevy_reflect: remove `glam` from a test which is active without the glam feature][5195] +- [Disable vsync for stress tests][5187] +- [Move `get_short_name` utility method from `bevy_reflect` into `bevy_utils`][5174] +- [Derive `Default` for enums where possible][5158] +- [Implement `Eq` and `PartialEq` for `MouseScrollUnit`][5048] +- [Some cleanup for `bevy_ptr`][4668] +- [Move float_ord from `bevy_core` to `bevy_utils`][4189] +- [Remove unused `CountdownEvent`][4290] +- [Some minor cleanups of asset_server][4604] +- [Use `elapsed()` on `Instant`][4599] +- [Make paused `Timers` update `just_finished` on tick][4445] +- [bevy_utils: remove hardcoded log level limit][4580] +- [Make `Time::update_with_instant` public for use in tests][4469] +- [Do not impl Component for Task][4113] +- [Remove nonexistent `WgpuResourceDiagnosticsPlugin`][4541] +- [Update ndk-glue requirement from 0.5 to 0.6][3624] +- [Update tracing-tracy requirement from 0.8.0 to 0.9.0][4786] +- [update image to 0.24][4121] +- [update xshell to 0.2][4789] +- [Update gilrs to v0.9][4848] +- [bevy_log: upgrade to tracing-tracy 0.10.0][4991] +- [update hashbrown to 0.12][5035] +- [Update `clap` to 3.2 in tools using `value_parser`][5031] +- [Updated `glam` to `0.21`.][5142] +- [Update Notify Dependency][5396] + +### Fixed + +- [bevy_ui: keep `Color` as 4 `f32`s][4494] +- [Fix issues with bevy on android other than the rendering][5130] +- [Update layout/style when scale factor changes too][4689] +- [Fix `Overflow::Hidden` so it works correctly with `scale_factor_override`][3854] +- [Fix `bevy_ui` touch input][4099] +- [Fix physical viewport calculation][5055] +- [Minimally fix the known unsoundness in `bevy_mikktspace`][5299] +- [Make `Transform` propagation correct in the presence of updated children][4608] +- [`StorageBuffer` uses wrong type to calculate the buffer size.][4557] +- [Fix confusing near and far fields in Camera][4457] +- [Allow minimising window if using a 2d camera][4527] +- [WGSL: use correct syntax for matrix access][5039] +- [Gltf: do not import `IoTaskPool` in wasm][5038] +- [Fix skinned mesh normal handling in mesh shader][5095] +- [Don't panic when `StandardMaterial` `normal_map` hasn't loaded yet][5307] +- [Fix incorrect rotation in `Transform::rotate_around`][5300] +- [Fix `extract_wireframes`][5301] +- [Fix type parameter name conflicts of `#[derive(Bundle)]`][4636] +- [Remove unnecessary `unsafe impl` of `Send+Sync` for `ParallelSystemContainer`][5137] +- [Fix line material shader][5348] +- [Fix `mouse_clicked` check for touch][2029] +- [Fix unsoundness with `Or`/`AnyOf`/`Option` component access][4659] +- [Improve soundness of `CommandQueue`][4863] +- [Fix some memory leaks detected by miri][4959] +- [Fix Android example icon][4076] +- [Fix broken `WorldCell` test][5009] +- [Bugfix `State::set` transition condition infinite loop][4890] +- [Fix crash when using `Duration::MAX`][4900] +- [Fix release builds: Move asserts under `#[cfg(debug_assertions)]`][4871] +- [Fix frame count being a float][4493] +- [Fix "unused" warnings when compiling with `render` feature but without `animation`][4714] +- [Fix re-adding a plugin to a `PluginGroup`][2039] +- [Fix torus normals][4520] +- [Add `NO_STORAGE_BUFFERS_SUPPORT` shaderdef when needed][4949] + +[2029]: https://github.com/bevyengine/bevy/pull/2029 +[2039]: https://github.com/bevyengine/bevy/pull/2039 +[2123]: https://github.com/bevyengine/bevy/pull/2123 +[2250]: https://github.com/bevyengine/bevy/pull/2250 +[2424]: https://github.com/bevyengine/bevy/pull/2424 +[2539]: https://github.com/bevyengine/bevy/pull/2539 +[2774]: https://github.com/bevyengine/bevy/pull/2774 +[3001]: https://github.com/bevyengine/bevy/pull/3001 +[3253]: https://github.com/bevyengine/bevy/pull/3253 +[3575]: https://github.com/bevyengine/bevy/pull/3575 +[3592]: https://github.com/bevyengine/bevy/pull/3592 +[3624]: https://github.com/bevyengine/bevy/pull/3624 +[3730]: https://github.com/bevyengine/bevy/pull/3730 +[3733]: https://github.com/bevyengine/bevy/pull/3733 +[3745]: https://github.com/bevyengine/bevy/pull/3745 +[3854]: https://github.com/bevyengine/bevy/pull/3854 +[3863]: https://github.com/bevyengine/bevy/pull/3863 +[3872]: https://github.com/bevyengine/bevy/pull/3872 +[3906]: https://github.com/bevyengine/bevy/pull/3906 +[3922]: https://github.com/bevyengine/bevy/pull/3922 +[3927]: https://github.com/bevyengine/bevy/pull/3927 +[3956]: https://github.com/bevyengine/bevy/pull/3956 +[4041]: https://github.com/bevyengine/bevy/pull/4041 +[4042]: https://github.com/bevyengine/bevy/pull/4042 +[4048]: https://github.com/bevyengine/bevy/pull/4048 +[4076]: https://github.com/bevyengine/bevy/pull/4076 +[4085]: https://github.com/bevyengine/bevy/pull/4085 +[4099]: https://github.com/bevyengine/bevy/pull/4099 +[4101]: https://github.com/bevyengine/bevy/pull/4101 +[4104]: https://github.com/bevyengine/bevy/pull/4104 +[4108]: https://github.com/bevyengine/bevy/pull/4108 +[4113]: https://github.com/bevyengine/bevy/pull/4113 +[4118]: https://github.com/bevyengine/bevy/pull/4118 +[4120]: https://github.com/bevyengine/bevy/pull/4120 +[4121]: https://github.com/bevyengine/bevy/pull/4121 +[4140]: https://github.com/bevyengine/bevy/pull/4140 +[4160]: https://github.com/bevyengine/bevy/pull/4160 +[4178]: https://github.com/bevyengine/bevy/pull/4178 +[4187]: https://github.com/bevyengine/bevy/pull/4187 +[4189]: https://github.com/bevyengine/bevy/pull/4189 +[4197]: https://github.com/bevyengine/bevy/pull/4197 +[4211]: https://github.com/bevyengine/bevy/pull/4211 +[4217]: https://github.com/bevyengine/bevy/pull/4217 +[4218]: https://github.com/bevyengine/bevy/pull/4218 +[4226]: https://github.com/bevyengine/bevy/pull/4226 +[4240]: https://github.com/bevyengine/bevy/pull/4240 +[4244]: https://github.com/bevyengine/bevy/pull/4244 +[4276]: https://github.com/bevyengine/bevy/pull/4276 +[4285]: https://github.com/bevyengine/bevy/pull/4285 +[4290]: https://github.com/bevyengine/bevy/pull/4290 +[4314]: https://github.com/bevyengine/bevy/pull/4314 +[4339]: https://github.com/bevyengine/bevy/pull/4339 +[4350]: https://github.com/bevyengine/bevy/pull/4350 +[4379]: https://github.com/bevyengine/bevy/pull/4379 +[4402]: https://github.com/bevyengine/bevy/pull/4402 +[4423]: https://github.com/bevyengine/bevy/pull/4423 +[4445]: https://github.com/bevyengine/bevy/pull/4445 +[4447]: https://github.com/bevyengine/bevy/pull/4447 +[4457]: https://github.com/bevyengine/bevy/pull/4457 +[4465]: https://github.com/bevyengine/bevy/pull/4465 +[4469]: https://github.com/bevyengine/bevy/pull/4469 +[4475]: https://github.com/bevyengine/bevy/pull/4475 +[4484]: https://github.com/bevyengine/bevy/pull/4484 +[4489]: https://github.com/bevyengine/bevy/pull/4489 +[4493]: https://github.com/bevyengine/bevy/pull/4493 +[4494]: https://github.com/bevyengine/bevy/pull/4494 +[4499]: https://github.com/bevyengine/bevy/pull/4499 +[4503]: https://github.com/bevyengine/bevy/pull/4503 +[4508]: https://github.com/bevyengine/bevy/pull/4508 +[4520]: https://github.com/bevyengine/bevy/pull/4520 +[4527]: https://github.com/bevyengine/bevy/pull/4527 +[4528]: https://github.com/bevyengine/bevy/pull/4528 +[4540]: https://github.com/bevyengine/bevy/pull/4540 +[4541]: https://github.com/bevyengine/bevy/pull/4541 +[4547]: https://github.com/bevyengine/bevy/pull/4547 +[4557]: https://github.com/bevyengine/bevy/pull/4557 +[4569]: https://github.com/bevyengine/bevy/pull/4569 +[4580]: https://github.com/bevyengine/bevy/pull/4580 +[4599]: https://github.com/bevyengine/bevy/pull/4599 +[4602]: https://github.com/bevyengine/bevy/pull/4602 +[4604]: https://github.com/bevyengine/bevy/pull/4604 +[4608]: https://github.com/bevyengine/bevy/pull/4608 +[4618]: https://github.com/bevyengine/bevy/pull/4618 +[4621]: https://github.com/bevyengine/bevy/pull/4621 +[4626]: https://github.com/bevyengine/bevy/pull/4626 +[4628]: https://github.com/bevyengine/bevy/pull/4628 +[4636]: https://github.com/bevyengine/bevy/pull/4636 +[4643]: https://github.com/bevyengine/bevy/pull/4643 +[4650]: https://github.com/bevyengine/bevy/pull/4650 +[4653]: https://github.com/bevyengine/bevy/pull/4653 +[4655]: https://github.com/bevyengine/bevy/pull/4655 +[4658]: https://github.com/bevyengine/bevy/pull/4658 +[4659]: https://github.com/bevyengine/bevy/pull/4659 +[4660]: https://github.com/bevyengine/bevy/pull/4660 +[4663]: https://github.com/bevyengine/bevy/pull/4663 +[4668]: https://github.com/bevyengine/bevy/pull/4668 +[4677]: https://github.com/bevyengine/bevy/pull/4677 +[4689]: https://github.com/bevyengine/bevy/pull/4689 +[4692]: https://github.com/bevyengine/bevy/pull/4692 +[4693]: https://github.com/bevyengine/bevy/pull/4693 +[4701]: https://github.com/bevyengine/bevy/pull/4701 +[4703]: https://github.com/bevyengine/bevy/pull/4703 +[4705]: https://github.com/bevyengine/bevy/pull/4705 +[4708]: https://github.com/bevyengine/bevy/pull/4708 +[4709]: https://github.com/bevyengine/bevy/pull/4709 +[4711]: https://github.com/bevyengine/bevy/pull/4711 +[4712]: https://github.com/bevyengine/bevy/pull/4712 +[4713]: https://github.com/bevyengine/bevy/pull/4713 +[4714]: https://github.com/bevyengine/bevy/pull/4714 +[4715]: https://github.com/bevyengine/bevy/pull/4715 +[4716]: https://github.com/bevyengine/bevy/pull/4716 +[4717]: https://github.com/bevyengine/bevy/pull/4717 +[4723]: https://github.com/bevyengine/bevy/pull/4723 +[4725]: https://github.com/bevyengine/bevy/pull/4725 +[4726]: https://github.com/bevyengine/bevy/pull/4726 +[4736]: https://github.com/bevyengine/bevy/pull/4736 +[4739]: https://github.com/bevyengine/bevy/pull/4739 +[4744]: https://github.com/bevyengine/bevy/pull/4744 +[4745]: https://github.com/bevyengine/bevy/pull/4745 +[4749]: https://github.com/bevyengine/bevy/pull/4749 +[4760]: https://github.com/bevyengine/bevy/pull/4760 +[4773]: https://github.com/bevyengine/bevy/pull/4773 +[4776]: https://github.com/bevyengine/bevy/pull/4776 +[4780]: https://github.com/bevyengine/bevy/pull/4780 +[4782]: https://github.com/bevyengine/bevy/pull/4782 +[4786]: https://github.com/bevyengine/bevy/pull/4786 +[4789]: https://github.com/bevyengine/bevy/pull/4789 +[4790]: https://github.com/bevyengine/bevy/pull/4790 +[4792]: https://github.com/bevyengine/bevy/pull/4792 +[4794]: https://github.com/bevyengine/bevy/pull/4794 +[4807]: https://github.com/bevyengine/bevy/pull/4807 +[4812]: https://github.com/bevyengine/bevy/pull/4812 +[4816]: https://github.com/bevyengine/bevy/pull/4816 +[4824]: https://github.com/bevyengine/bevy/pull/4824 +[4833]: https://github.com/bevyengine/bevy/pull/4833 +[4836]: https://github.com/bevyengine/bevy/pull/4836 +[4841]: https://github.com/bevyengine/bevy/pull/4841 +[4844]: https://github.com/bevyengine/bevy/pull/4844 +[4848]: https://github.com/bevyengine/bevy/pull/4848 +[4855]: https://github.com/bevyengine/bevy/pull/4855 +[4863]: https://github.com/bevyengine/bevy/pull/4863 +[4867]: https://github.com/bevyengine/bevy/pull/4867 +[4871]: https://github.com/bevyengine/bevy/pull/4871 +[4879]: https://github.com/bevyengine/bevy/pull/4879 +[4890]: https://github.com/bevyengine/bevy/pull/4890 +[4898]: https://github.com/bevyengine/bevy/pull/4898 +[4900]: https://github.com/bevyengine/bevy/pull/4900 +[4901]: https://github.com/bevyengine/bevy/pull/4901 +[4904]: https://github.com/bevyengine/bevy/pull/4904 +[4909]: https://github.com/bevyengine/bevy/pull/4909 +[4924]: https://github.com/bevyengine/bevy/pull/4924 +[4938]: https://github.com/bevyengine/bevy/pull/4938 +[4939]: https://github.com/bevyengine/bevy/pull/4939 +[4942]: https://github.com/bevyengine/bevy/pull/4942 +[4944]: https://github.com/bevyengine/bevy/pull/4944 +[4948]: https://github.com/bevyengine/bevy/pull/4948 +[4949]: https://github.com/bevyengine/bevy/pull/4949 +[4957]: https://github.com/bevyengine/bevy/pull/4957 +[4959]: https://github.com/bevyengine/bevy/pull/4959 +[4991]: https://github.com/bevyengine/bevy/pull/4991 +[4999]: https://github.com/bevyengine/bevy/pull/4999 +[5009]: https://github.com/bevyengine/bevy/pull/5009 +[5010]: https://github.com/bevyengine/bevy/pull/5010 +[5011]: https://github.com/bevyengine/bevy/pull/5011 +[5014]: https://github.com/bevyengine/bevy/pull/5014 +[5015]: https://github.com/bevyengine/bevy/pull/5015 +[5023]: https://github.com/bevyengine/bevy/pull/5023 +[5031]: https://github.com/bevyengine/bevy/pull/5031 +[5035]: https://github.com/bevyengine/bevy/pull/5035 +[5038]: https://github.com/bevyengine/bevy/pull/5038 +[5039]: https://github.com/bevyengine/bevy/pull/5039 +[5048]: https://github.com/bevyengine/bevy/pull/5048 +[5049]: https://github.com/bevyengine/bevy/pull/5049 +[5053]: https://github.com/bevyengine/bevy/pull/5053 +[5055]: https://github.com/bevyengine/bevy/pull/5055 +[5056]: https://github.com/bevyengine/bevy/pull/5056 +[5065]: https://github.com/bevyengine/bevy/pull/5065 +[5066]: https://github.com/bevyengine/bevy/pull/5066 +[5078]: https://github.com/bevyengine/bevy/pull/5078 +[5095]: https://github.com/bevyengine/bevy/pull/5095 +[5106]: https://github.com/bevyengine/bevy/pull/5106 +[5119]: https://github.com/bevyengine/bevy/pull/5119 +[5124]: https://github.com/bevyengine/bevy/pull/5124 +[5129]: https://github.com/bevyengine/bevy/pull/5129 +[5130]: https://github.com/bevyengine/bevy/pull/5130 +[5136]: https://github.com/bevyengine/bevy/pull/5136 +[5137]: https://github.com/bevyengine/bevy/pull/5137 +[5142]: https://github.com/bevyengine/bevy/pull/5142 +[5148]: https://github.com/bevyengine/bevy/pull/5148 +[5151]: https://github.com/bevyengine/bevy/pull/5151 +[5158]: https://github.com/bevyengine/bevy/pull/5158 +[5159]: https://github.com/bevyengine/bevy/pull/5159 +[5167]: https://github.com/bevyengine/bevy/pull/5167 +[5168]: https://github.com/bevyengine/bevy/pull/5168 +[5170]: https://github.com/bevyengine/bevy/pull/5170 +[5171]: https://github.com/bevyengine/bevy/pull/5171 +[5173]: https://github.com/bevyengine/bevy/pull/5173 +[5174]: https://github.com/bevyengine/bevy/pull/5174 +[5175]: https://github.com/bevyengine/bevy/pull/5175 +[5182]: https://github.com/bevyengine/bevy/pull/5182 +[5187]: https://github.com/bevyengine/bevy/pull/5187 +[5190]: https://github.com/bevyengine/bevy/pull/5190 +[5194]: https://github.com/bevyengine/bevy/pull/5194 +[5195]: https://github.com/bevyengine/bevy/pull/5195 +[5198]: https://github.com/bevyengine/bevy/pull/5198 +[5201]: https://github.com/bevyengine/bevy/pull/5201 +[5210]: https://github.com/bevyengine/bevy/pull/5210 +[5212]: https://github.com/bevyengine/bevy/pull/5212 +[5215]: https://github.com/bevyengine/bevy/pull/5215 +[5220]: https://github.com/bevyengine/bevy/pull/5220 +[5222]: https://github.com/bevyengine/bevy/pull/5222 +[5225]: https://github.com/bevyengine/bevy/pull/5225 +[5227]: https://github.com/bevyengine/bevy/pull/5227 +[5231]: https://github.com/bevyengine/bevy/pull/5231 +[5234]: https://github.com/bevyengine/bevy/pull/5234 +[5249]: https://github.com/bevyengine/bevy/pull/5249 +[5254]: https://github.com/bevyengine/bevy/pull/5254 +[5255]: https://github.com/bevyengine/bevy/pull/5255 +[5259]: https://github.com/bevyengine/bevy/pull/5259 +[5263]: https://github.com/bevyengine/bevy/pull/5263 +[5269]: https://github.com/bevyengine/bevy/pull/5269 +[5271]: https://github.com/bevyengine/bevy/pull/5271 +[5274]: https://github.com/bevyengine/bevy/pull/5274 +[5275]: https://github.com/bevyengine/bevy/pull/5275 +[5276]: https://github.com/bevyengine/bevy/pull/5276 +[5286]: https://github.com/bevyengine/bevy/pull/5286 +[5291]: https://github.com/bevyengine/bevy/pull/5291 +[5299]: https://github.com/bevyengine/bevy/pull/5299 +[5300]: https://github.com/bevyengine/bevy/pull/5300 +[5301]: https://github.com/bevyengine/bevy/pull/5301 +[5305]: https://github.com/bevyengine/bevy/pull/5305 +[5306]: https://github.com/bevyengine/bevy/pull/5306 +[5307]: https://github.com/bevyengine/bevy/pull/5307 +[5310]: https://github.com/bevyengine/bevy/pull/5310 +[5312]: https://github.com/bevyengine/bevy/pull/5312 +[5324]: https://github.com/bevyengine/bevy/pull/5324 +[5335]: https://github.com/bevyengine/bevy/pull/5335 +[5339]: https://github.com/bevyengine/bevy/pull/5339 +[5343]: https://github.com/bevyengine/bevy/pull/5343 +[5344]: https://github.com/bevyengine/bevy/pull/5344 +[5348]: https://github.com/bevyengine/bevy/pull/5348 +[5349]: https://github.com/bevyengine/bevy/pull/5349 +[5355]: https://github.com/bevyengine/bevy/pull/5355 +[5359]: https://github.com/bevyengine/bevy/pull/5359 +[5364]: https://github.com/bevyengine/bevy/pull/5364 +[5366]: https://github.com/bevyengine/bevy/pull/5366 +[5376]: https://github.com/bevyengine/bevy/pull/5376 +[5383]: https://github.com/bevyengine/bevy/pull/5383 +[5389]: https://github.com/bevyengine/bevy/pull/5389 +[5396]: https://github.com/bevyengine/bevy/pull/5396 + +## Version 0.7.0 (2022-04-15) + +### Added + +- [Mesh Skinning][4238] +- [Animation Player][4375] +- [Gltf animations][3751] +- [Mesh vertex buffer layouts][3959] +- [Render to a texture][3412] +- [KTX2/DDS/.basis compressed texture support][3884] +- [Audio control - play, pause, volume, speed, loop][3948] +- [Auto-label function systems with SystemTypeIdLabel][4224] +- [Query::get_many][4298] +- [Dynamic light clusters][3968] +- [Always update clusters and remove per-frame allocations][4169] +- [`ParamSet` for conflicting `SystemParam`:s][2765] +- [default() shorthand][4071] +- [use marker components for cameras instead of name strings][3635] +- [Implement `WorldQuery` derive macro][2713] +- [Implement AnyOf queries][2889] +- [Compute Pipeline Specialization][3979] +- [Make get_resource (and friends) infallible][4047] +- [bevy_pbr: Support flipping tangent space normal map y for DirectX normal maps][4433] +- [Faster view frustum culling][4181] +- [Use storage buffers for clustered forward point lights][3989] +- [Add &World as SystemParam][2923] +- [Add text wrapping support to Text2d][4347] +- [Scene Viewer to display glTF files][4183] +- [Internal Asset Hot Reloading][3966] +- [Add FocusPolicy to NodeBundle and ImageBundle][3952] +- [Allow iter combinations on queries with filters][3656] +- [bevy_render: Support overriding wgpu features and limits][3912] +- [bevy_render: Use RenderDevice to get limits/features and expose AdapterInfo][3931] +- [Reduce power usage with configurable event loop][3974] +- [can specify an anchor for a sprite][3463] +- [Implement len and is_empty for EventReaders][2969] +- [Add more FromWorld implementations][3945] +- [Add cart's fork of ecs_bench_suite][4225] +- [bevy_derive: Add derives for `Deref` and `DerefMut`][4328] +- [Add clear_schedule][3941] +- [Add Query::contains][3090] +- [bevy_render: Support removal of nodes, edges, subgraphs][3048] +- [Implement init_resource for `Commands` and `World`][3079] +- [Added method to restart the current state][3328] +- [Simplify sending empty events][2935] +- [impl Command for `impl FnOnce(&mut World)`][2996] +- [Useful error message when two assets have the save UUID][3739] +- [bevy_asset: Add AssetServerSettings watch_for_changes member][3643] +- [Add conversio from Color to u32][4088] +- [Introduce `SystemLabel`'s for `RenderAssetPlugin`, and change `Image` preparation system to run before others][3917] +- [Add a helper for storage buffers similar to `UniformVec`][4079] +- [StandardMaterial: expose a cull_mode option][3982] +- [Expose draw indirect][4056] +- [Add view transform to view uniform][3885] +- [Add a size method on Image.][3696] +- [add Visibility for lights][3958] +- [bevy_render: Provide a way to opt-out of the built-in frustum culling][3711] +- [use error scope to handle errors on shader module creation][3675] +- [include sources in shader validation error][3724] +- [insert the gltf mesh name on the entity if there is one][4119] +- [expose extras from gltf nodes][2154] +- [gltf: add a name to nodes without names][4396] +- [Enable drag-and-drop events on windows][3772] +- [Add transform hierarchy stress test][4170] +- [Add TransformBundle][3054] +- [Add Transform::rotate_around method][3107] +- [example on how to create an animation in code][4399] +- [Add examples for Transforms][2441] +- [Add mouse grab example][4114] +- [examples: add screenspace texture shader example][4063] +- [Add generic systems example][2636] +- [add examples on how to have a data source running in another thread / in a task pool thread][2915] +- [Simple 2d rotation example][3065] +- [Add move sprite example.][2414] +- [add an example using UI & states to create a game menu][2960] +- [CI runs `cargo miri test -p bevy_ecs`][4310] +- [Tracy spans around main 3D passes][4182] +- [Add automatic docs deployment to GitHub Pages][3535] + +### Changed + +- [Proper prehashing][3963] +- [Move import_path definitions into shader source][3976] +- [Make `System` responsible for updating its own archetypes][4115] +- [Some small changes related to run criteria piping][3923] +- [Remove unnecessary system labels][4340] +- [Increment last event count on next instead of iter][2382] +- [Obviate the need for `RunSystem`, and remove it][3817] +- [Cleanup some things which shouldn't be components][2982] +- [Remove the config api][3633] +- [Deprecate `.system`][3302] +- [Hide docs for concrete impls of Fetch, FetchState, and SystemParamState][4250] +- [Move the CoreStage::Startup to a seperate StartupSchedule label][2434] +- [`iter_mut` on Assets: send modified event only when asset is iterated over][3565] +- [check if resource for asset already exists before adding it][3560] +- [bevy_render: Batch insertion for prepare_uniform_components][4179] +- [Change default `ColorMaterial` color to white][3981] +- [bevy_render: Only auto-disable mappable primary buffers for discrete GPUs][3803] +- [bevy_render: Do not automatically enable MAPPABLE_PRIMARY_BUFFERS][3698] +- [increase the maximum number of point lights with shadows to the max supported by the device][4435] +- [perf: only recalculate frusta of changed lights][4086] +- [bevy_pbr: Optimize assign_lights_to_clusters][3984] +- [improve error messages for render graph runner][3930] +- [Skinned extraction speedup][4428] +- [Sprites - keep color as 4 f32][4361] +- [Change scaling mode to FixedHorizontal][4055] +- [Replace VSync with PresentMode][3812] +- [do not set cursor grab on window creation if not asked for][3617] +- [bevy_transform: Use Changed in the query for much faster transform_propagate_system][4180] +- [Split bevy_hierarchy out from bevy_transform][4168] +- [Make transform builder methods const][3045] +- [many_cubes: Add a cube pattern suitable for benchmarking culling changes][4126] +- [Make many_cubes example more interesting][4015] +- [Run tests (including doc tests) in `cargo run -p ci` command][3849] +- [Use more ergonomic span syntax][4246] + +### Fixed + +- [Remove unsound lifetime annotations on `EntityMut`][4096] +- [Remove unsound lifetime annotations on `Query` methods][4243] +- [Remove `World::components_mut`][4092] +- [unsafeify `World::entities_mut`][4093] +- [Use ManuallyDrop instead of forget in insert_resource_with_id][2947] +- [Backport soundness fix][3685] +- [Fix clicked UI nodes getting reset when hovering child nodes][4194] +- [Fix ui interactions when cursor disappears suddenly][3926] +- [Fix node update][3785] +- [Fix derive(SystemParam) macro][4400] +- [SystemParam Derive fixes][2838] +- [Do not crash if RenderDevice doesn't exist][4427] +- [Fixed case of R == G, following original conversion formula][4383] +- [Fixed the frustum-sphere collision and added tests][4035] +- [bevy_render: Fix Quad flip][3741] +- [Fix HDR asset support][3795] +- [fix cluster tiling calculations][4148] +- [bevy_pbr: Do not panic when more than 256 point lights are added the scene][3697] +- [fix issues with too many point lights][3916] +- [shader preprocessor - do not import if scope is not valid][4012] +- [support all line endings in shader preprocessor][3603] +- [Fix animation: shadow and wireframe support][4367] +- [add AnimationPlayer component only on scene roots that are also animation roots][4417] +- [Fix loading non-TriangleList meshes without normals in gltf loader][4376] +- [gltf-loader: disable backface culling if material is double-sided][4270] +- [Fix glTF perspective camera projection][4006] +- [fix mul_vec3 transformation order: should be scale -> rotate -> translate][3811] + +[2154]: https://github.com/bevyengine/bevy/pull/2154 +[2382]: https://github.com/bevyengine/bevy/pull/2382 +[2414]: https://github.com/bevyengine/bevy/pull/2414 +[2434]: https://github.com/bevyengine/bevy/pull/2434 +[2441]: https://github.com/bevyengine/bevy/pull/2441 +[2636]: https://github.com/bevyengine/bevy/pull/2636 +[2713]: https://github.com/bevyengine/bevy/pull/2713 +[2765]: https://github.com/bevyengine/bevy/pull/2765 +[2838]: https://github.com/bevyengine/bevy/pull/2838 +[2889]: https://github.com/bevyengine/bevy/pull/2889 +[2915]: https://github.com/bevyengine/bevy/pull/2915 +[2923]: https://github.com/bevyengine/bevy/pull/2923 +[2935]: https://github.com/bevyengine/bevy/pull/2935 +[2947]: https://github.com/bevyengine/bevy/pull/2947 +[2960]: https://github.com/bevyengine/bevy/pull/2960 +[2969]: https://github.com/bevyengine/bevy/pull/2969 +[2982]: https://github.com/bevyengine/bevy/pull/2982 +[2996]: https://github.com/bevyengine/bevy/pull/2996 +[3045]: https://github.com/bevyengine/bevy/pull/3045 +[3048]: https://github.com/bevyengine/bevy/pull/3048 +[3054]: https://github.com/bevyengine/bevy/pull/3054 +[3065]: https://github.com/bevyengine/bevy/pull/3065 +[3079]: https://github.com/bevyengine/bevy/pull/3079 +[3090]: https://github.com/bevyengine/bevy/pull/3090 +[3107]: https://github.com/bevyengine/bevy/pull/3107 +[3302]: https://github.com/bevyengine/bevy/pull/3302 +[3328]: https://github.com/bevyengine/bevy/pull/3328 +[3412]: https://github.com/bevyengine/bevy/pull/3412 +[3463]: https://github.com/bevyengine/bevy/pull/3463 +[3535]: https://github.com/bevyengine/bevy/pull/3535 +[3560]: https://github.com/bevyengine/bevy/pull/3560 +[3565]: https://github.com/bevyengine/bevy/pull/3565 +[3603]: https://github.com/bevyengine/bevy/pull/3603 +[3617]: https://github.com/bevyengine/bevy/pull/3617 +[3633]: https://github.com/bevyengine/bevy/pull/3633 +[3635]: https://github.com/bevyengine/bevy/pull/3635 +[3643]: https://github.com/bevyengine/bevy/pull/3643 +[3656]: https://github.com/bevyengine/bevy/pull/3656 +[3675]: https://github.com/bevyengine/bevy/pull/3675 +[3685]: https://github.com/bevyengine/bevy/pull/3685 +[3696]: https://github.com/bevyengine/bevy/pull/3696 +[3697]: https://github.com/bevyengine/bevy/pull/3697 +[3698]: https://github.com/bevyengine/bevy/pull/3698 +[3711]: https://github.com/bevyengine/bevy/pull/3711 +[3724]: https://github.com/bevyengine/bevy/pull/3724 +[3739]: https://github.com/bevyengine/bevy/pull/3739 +[3741]: https://github.com/bevyengine/bevy/pull/3741 +[3751]: https://github.com/bevyengine/bevy/pull/3751 +[3772]: https://github.com/bevyengine/bevy/pull/3772 +[3785]: https://github.com/bevyengine/bevy/pull/3785 +[3795]: https://github.com/bevyengine/bevy/pull/3795 +[3803]: https://github.com/bevyengine/bevy/pull/3803 +[3811]: https://github.com/bevyengine/bevy/pull/3811 +[3812]: https://github.com/bevyengine/bevy/pull/3812 +[3817]: https://github.com/bevyengine/bevy/pull/3817 +[3849]: https://github.com/bevyengine/bevy/pull/3849 +[3884]: https://github.com/bevyengine/bevy/pull/3884 +[3885]: https://github.com/bevyengine/bevy/pull/3885 +[3912]: https://github.com/bevyengine/bevy/pull/3912 +[3916]: https://github.com/bevyengine/bevy/pull/3916 +[3917]: https://github.com/bevyengine/bevy/pull/3917 +[3923]: https://github.com/bevyengine/bevy/pull/3923 +[3926]: https://github.com/bevyengine/bevy/pull/3926 +[3930]: https://github.com/bevyengine/bevy/pull/3930 +[3931]: https://github.com/bevyengine/bevy/pull/3931 +[3941]: https://github.com/bevyengine/bevy/pull/3941 +[3945]: https://github.com/bevyengine/bevy/pull/3945 +[3948]: https://github.com/bevyengine/bevy/pull/3948 +[3952]: https://github.com/bevyengine/bevy/pull/3952 +[3958]: https://github.com/bevyengine/bevy/pull/3958 +[3959]: https://github.com/bevyengine/bevy/pull/3959 +[3963]: https://github.com/bevyengine/bevy/pull/3963 +[3966]: https://github.com/bevyengine/bevy/pull/3966 +[3968]: https://github.com/bevyengine/bevy/pull/3968 +[3974]: https://github.com/bevyengine/bevy/pull/3974 +[3976]: https://github.com/bevyengine/bevy/pull/3976 +[3979]: https://github.com/bevyengine/bevy/pull/3979 +[3981]: https://github.com/bevyengine/bevy/pull/3981 +[3982]: https://github.com/bevyengine/bevy/pull/3982 +[3984]: https://github.com/bevyengine/bevy/pull/3984 +[3989]: https://github.com/bevyengine/bevy/pull/3989 +[4006]: https://github.com/bevyengine/bevy/pull/4006 +[4012]: https://github.com/bevyengine/bevy/pull/4012 +[4015]: https://github.com/bevyengine/bevy/pull/4015 +[4035]: https://github.com/bevyengine/bevy/pull/4035 +[4047]: https://github.com/bevyengine/bevy/pull/4047 +[4055]: https://github.com/bevyengine/bevy/pull/4055 +[4056]: https://github.com/bevyengine/bevy/pull/4056 +[4063]: https://github.com/bevyengine/bevy/pull/4063 +[4071]: https://github.com/bevyengine/bevy/pull/4071 +[4079]: https://github.com/bevyengine/bevy/pull/4079 +[4086]: https://github.com/bevyengine/bevy/pull/4086 +[4088]: https://github.com/bevyengine/bevy/pull/4088 +[4092]: https://github.com/bevyengine/bevy/pull/4092 +[4093]: https://github.com/bevyengine/bevy/pull/4093 +[4096]: https://github.com/bevyengine/bevy/pull/4096 +[4114]: https://github.com/bevyengine/bevy/pull/4114 +[4115]: https://github.com/bevyengine/bevy/pull/4115 +[4119]: https://github.com/bevyengine/bevy/pull/4119 +[4126]: https://github.com/bevyengine/bevy/pull/4126 +[4148]: https://github.com/bevyengine/bevy/pull/4148 +[4168]: https://github.com/bevyengine/bevy/pull/4168 +[4169]: https://github.com/bevyengine/bevy/pull/4169 +[4170]: https://github.com/bevyengine/bevy/pull/4170 +[4179]: https://github.com/bevyengine/bevy/pull/4179 +[4180]: https://github.com/bevyengine/bevy/pull/4180 +[4181]: https://github.com/bevyengine/bevy/pull/4181 +[4182]: https://github.com/bevyengine/bevy/pull/4182 +[4183]: https://github.com/bevyengine/bevy/pull/4183 +[4194]: https://github.com/bevyengine/bevy/pull/4194 +[4224]: https://github.com/bevyengine/bevy/pull/4224 +[4225]: https://github.com/bevyengine/bevy/pull/4225 +[4238]: https://github.com/bevyengine/bevy/pull/4238 +[4243]: https://github.com/bevyengine/bevy/pull/4243 +[4246]: https://github.com/bevyengine/bevy/pull/4246 +[4250]: https://github.com/bevyengine/bevy/pull/4250 +[4252]: https://github.com/bevyengine/bevy/pull/4252 +[4270]: https://github.com/bevyengine/bevy/pull/4270 +[4298]: https://github.com/bevyengine/bevy/pull/4298 +[4310]: https://github.com/bevyengine/bevy/pull/4310 +[4328]: https://github.com/bevyengine/bevy/pull/4328 +[4332]: https://github.com/bevyengine/bevy/pull/4332 +[4340]: https://github.com/bevyengine/bevy/pull/4340 +[4347]: https://github.com/bevyengine/bevy/pull/4347 +[4361]: https://github.com/bevyengine/bevy/pull/4361 +[4367]: https://github.com/bevyengine/bevy/pull/4367 +[4375]: https://github.com/bevyengine/bevy/pull/4375 +[4376]: https://github.com/bevyengine/bevy/pull/4376 +[4383]: https://github.com/bevyengine/bevy/pull/4383 +[4396]: https://github.com/bevyengine/bevy/pull/4396 +[4399]: https://github.com/bevyengine/bevy/pull/4399 +[4400]: https://github.com/bevyengine/bevy/pull/4400 +[4403]: https://github.com/bevyengine/bevy/pull/4403 +[4405]: https://github.com/bevyengine/bevy/pull/4405 +[4417]: https://github.com/bevyengine/bevy/pull/4417 +[4420]: https://github.com/bevyengine/bevy/pull/4420 +[4426]: https://github.com/bevyengine/bevy/pull/4426 +[4427]: https://github.com/bevyengine/bevy/pull/4427 +[4428]: https://github.com/bevyengine/bevy/pull/4428 +[4433]: https://github.com/bevyengine/bevy/pull/4433 +[4435]: https://github.com/bevyengine/bevy/pull/4435 + +## Version 0.6.0 (2022-01-08) + +### Added + +- [New Renderer][3175] +- [Clustered forward rendering][3153] +- [Frustum culling][2861] +- [Sprite Batching][3060] +- [Materials and MaterialPlugin][3428] +- [2D Meshes and Materials][3460] +- [WebGL2 support][3039] +- [Pipeline Specialization, Shader Assets, and Shader Preprocessing][3031] +- [Modular Rendering][2831] +- [Directional light and shadow][c6] +- [Directional light][2112] +- [Use the infinite reverse right-handed perspective projection][2543] +- [Implement and require `#[derive(Component)]` on all component structs][2254] +- [Shader Imports. Decouple Mesh logic from PBR][3137] +- [Add support for opaque, alpha mask, and alpha blend modes][3072] +- [bevy_gltf: Load light names from gltf][3553] +- [bevy_gltf: Add support for loading lights][3506] +- [Spherical Area Lights][1901] +- [Shader Processor: process imported shader][3290] +- [Add support for not casting/receiving shadows][2726] +- [Add support for configurable shadow map sizes][2700] +- [Implement the `Overflow::Hidden` style property for UI][3296] +- [SystemState][2283] +- [Add a method `iter_combinations` on query to iterate over combinations of query results][1763] +- [Add FromReflect trait to convert dynamic types to concrete types][1395] +- [More pipelined-rendering shader examples][3041] +- [Configurable wgpu features/limits priority][3452] +- [Cargo feature for bevy UI][3546] +- [Spherical area lights example][3498] +- [Implement ReflectValue serialization for Duration][3318] +- [bevy_ui: register Overflow type][3443] +- [Add Visibility component to UI][3426] +- [Implement non-indexed mesh rendering][3415] +- [add tracing spans for parallel executor and system overhead][3416] +- [RemoveChildren command][1925] +- [report shader processing errors in `RenderPipelineCache`][3289] +- [enable Webgl2 optimisation in pbr under feature][3291] +- [Implement Sub-App Labels][2695] +- [Added `set_cursor_icon(...)` to `Window`][3395] +- [Support topologies other than TriangleList][3349] +- [Add an example 'showcasing' using multiple windows][3367] +- [Add an example to draw a rectangle][2957] +- [Added set_scissor_rect to tracked render pass.][3320] +- [Add RenderWorld to Extract step][2555] +- [re-export ClearPassNode][3336] +- [add default standard material in PbrBundle][3325] +- [add methods to get reads and writes of `Access`][3166] +- [Add despawn_children][2903] +- [More Bevy ECS schedule spans][3281] +- [Added transparency to window builder][3105] +- [Add Gamepads resource][3257] +- [Add support for #else for shader defs][3206] +- [Implement iter() for mutable Queries][2305] +- [add shadows in examples][3201] +- [Added missing wgpu image render resources.][3171] +- [Per-light toggleable shadow mapping][3126] +- [Support nested shader defs][3113] +- [use bytemuck crate instead of Byteable trait][2183] +- [`iter_mut()` for Assets type][3118] +- [EntityRenderCommand and PhaseItemRenderCommand][3111] +- [add position to WindowDescriptor][3070] +- [Add System Command apply and RenderGraph node spans][3069] +- [Support for normal maps including from glTF models][2741] +- [MSAA example][3049] +- [Add MSAA to new renderer][3042] +- [Add support for IndexFormat::Uint16][2990] +- [Apply labels to wgpu resources for improved debugging/profiling][2912] +- [Add tracing spans around render subapp and stages][2907] +- [Add set_stencil_reference to TrackedRenderPass][2885] +- [Add despawn_recursive to EntityMut][2855] +- [Add trace_tracy feature for Tracy profiling][2832] +- [Expose wgpu's StencilOperation with bevy][2819] +- [add get_single variant][2793] +- [Add builder methods to Transform][2778] +- [add get_history function to Diagnostic][2772] +- [Add convenience methods for checking a set of inputs][2760] +- [Add error messages for the spooky insertions][2581] +- [Add Deref implementation for ComputePipeline][2759] +- [Derive thiserror::Error for HexColorError][2740] +- [Spawn specific entities: spawn or insert operations, refactor spawn internals, world clearing][2673] +- [Add ClearColor Resource to Pipelined Renderer][2631] +- [remove_component for ReflectComponent][2682] +- [Added ComputePipelineDescriptor][2628] +- [Added StorageTextureAccess to the exposed wgpu API][2614] +- [Add sprite atlases into the new renderer.][2560] +- [Log adapter info on initialization][2542] +- [Add feature flag to enable wasm for bevy_audio][2397] +- [Allow `Option>` and `Option>` as SystemParam][2345] +- [Added helpful adders for systemsets][2366] +- [Derive Clone for Time][2360] +- [Implement Clone for Fetches][2641] +- [Implement IntoSystemDescriptor for SystemDescriptor][2718] +- [implement DetectChanges for NonSendMut][2326] +- [Log errors when loading textures from a gltf file][2260] +- [expose texture/image conversions as From/TryFrom][2175] +- [[ecs] implement is_empty for queries][2271] +- [Add audio to ios example][1007] +- [Example showing how to use AsyncComputeTaskPool and Tasks][2180] +- [Expose set_changed() on ResMut and Mut][2208] +- [Impl AsRef+AsMut for Res, ResMut, and Mut][2189] +- [Add exit_on_esc_system to examples with window][2121] +- [Implement rotation for Text2d][2084] +- [Mesh vertex attributes for skinning and animation][1831] +- [load zeroed UVs as fallback in gltf loader][1803] +- [Implement direct mutable dereferencing][2100] +- [add a span for frames][2053] +- [Add an alias mouse position -> cursor position][2038] +- [Adding `WorldQuery` for `WithBundle`][2024] +- [Automatic System Spans][2033] +- [Add system sets and run criteria example][1909] +- [EnumVariantMeta derive][1972] +- [Added TryFrom for VertexAttributeValues][1963] +- [add render_to_texture example][1927] +- [Added example of entity sorting by components][1817] +- [calculate flat normals for mesh if missing][1808] +- [Add animate shaders example][1765] +- [examples on how to tests systems][1714] +- [Add a UV sphere implementation][1887] +- [Add additional vertex formats][1878] +- [gltf-loader: support data url for images][1828] +- [glTF: added color attribute support][1775] +- [Add synonyms for transform relative vectors][1667] + +### Changed + +- [Relicense Bevy under the dual MIT or Apache-2.0 license][2509] +- [[ecs] Improve `Commands` performance][2332] +- [Merge AppBuilder into App][2531] +- [Use a special first depth slice for clustered forward rendering][3545] +- [Add a separate ClearPass][3209] +- [bevy_pbr2: Improve lighting units and documentation][2704] +- [gltf loader: do not use the taskpool for only one task][3577] +- [System Param Lifetime Split][2605] +- [Optional `.system`][2398] +- [Optional `.system()`, part 2][2403] +- [Optional `.system()`, part 3][2422] +- [Optional `.system()`, part 4 (run criteria)][2431] +- [Optional `.system()`, part 6 (chaining)][2494] +- [Make the `iter_combinators` examples prettier][3075] +- [Remove dead anchor.rs code][3551] +- [gltf: load textures asynchronously using io task pool][1767] +- [Use fully-qualified type names in Label derive.][3544] +- [Remove Bytes, FromBytes, Labels, EntityLabels][3521] +- [StorageType parameter removed from ComponentDescriptor::new_resource][3495] +- [remove dead code: ShaderDefs derive][3490] +- [Enable Msaa for webgl by default][3489] +- [Renamed Entity::new to Entity::from_raw][3465] +- [bevy::scene::Entity renamed to bevy::scene::DynamicEntity.][3448] +- [make `sub_app` return an `&App` and add `sub_app_mut() -> &mut App`][3309] +- [use ogg by default instead of mp3][3421] +- [enable `wasm-bindgen` feature on gilrs][3420] +- [Use EventWriter for gilrs_system][3413] +- [Add some of the missing methods to `TrackedRenderPass`][3401] +- [Only bevy_render depends directly on wgpu][3393] +- [Update wgpu to 0.12 and naga to 0.8][3375] +- [Improved bevymark: no bouncing offscreen and spawn waves from CLI][3364] +- [Rename render UiSystem to RenderUiSystem][3371] +- [Use updated window size in bevymark example][3335] +- [Enable trace feature for subfeatures using it][3337] +- [Schedule gilrs system before input systems][2989] +- [Rename fixed timestep state and add a test][3260] +- [Port bevy_ui to pipelined-rendering][2653] +- [update wireframe rendering to new renderer][3193] +- [Allow `String` and `&String` as `Id` for `AssetServer.get_handle(id)`][3280] +- [Ported WgpuOptions to new renderer][3282] +- [Down with the system!][2496] +- [Update dependencies `ron` `winit`& fix `cargo-deny` lists][3244] +- [Improve contributors example quality][3258] +- [Expose command encoders][3271] +- [Made Time::time_since_startup return from last tick.][3264] +- [Default image used in PipelinedSpriteBundle to be able to render without loading a texture][3270] +- [make texture from sprite pipeline filterable][3236] +- [iOS: replace cargo-lipo, and update for new macOS][3109] +- [increase light intensity in pbr example][3182] +- [Faster gltf loader][3189] +- [Use crevice std140_size_static everywhere][3168] +- [replace matrix swizzles in pbr shader with index accesses][3122] +- [Disable default features from `bevy_asset` and `bevy_ecs`][3097] +- [Update tracing-subscriber requirement from 0.2.22 to 0.3.1][3076] +- [Update vendored Crevice to 0.8.0 + PR for arrays][3059] +- [change texture atlas sprite indexing to usize][2887] +- [Update derive(DynamicPlugin) to edition 2021][3038] +- [Update to edition 2021 on master][3028] +- [Add entity ID to expect() message][2943] +- [Use RenderQueue in BufferVec][2847] +- [removed unused RenderResourceId and SwapChainFrame][2890] +- [Unique WorldId][2827] +- [add_texture returns index to texture][2864] +- [Update hexasphere requirement from 4.0.0 to 5.0.0][2880] +- [enable change detection for hierarchy maintenance][2411] +- [Make events reuse buffers][2850] +- [Replace `.insert_resource(T::default())` calls with `init_resource::()`][2807] +- [Improve many sprites example][2785] +- [Update glam requirement from 0.17.3 to 0.18.0][2748] +- [update ndk-glue to 0.4][2684] +- [Remove Need for Sprite Size Sync System][2632] +- [Pipelined separate shadow vertex shader][2727] +- [Sub app label changes][2717] +- [Use Explicit Names for Flex Direction][2672] +- [Make default near plane more sensible at 0.1][2703] +- [Reduce visibility of various types and fields][2690] +- [Cleanup FromResources][2601] +- [Better error message for unsupported shader features Fixes #869][2598] +- [Change definition of `ScheduleRunnerPlugin`][2606] +- [Re-implement Automatic Sprite Sizing][2613] +- [Remove with bundle filter][2623] +- [Remove bevy_dynamic_plugin as a default][2578] +- [Port bevy_gltf to pipelined-rendering][2537] +- [Bump notify to 5.0.0-pre.11][2564] +- [Add 's (state) lifetime to `Fetch`][2515] +- [move bevy_core_pipeline to its own plugin][2552] +- [Refactor ECS to reduce the dependency on a 1-to-1 mapping between components and real rust types][2490] +- [Inline world get][2520] +- [Dedupe move logic in remove_bundle and remove_bundle_intersection][2521] +- [remove .system from pipelined code][2538] +- [Scale normal bias by texel size][c26] +- [Make Remove Command's fields public][2449] +- [bevy_utils: Re-introduce `with_capacity()`.][2393] +- [Update rodio requirement from 0.13 to 0.14][2244] +- [Optimize Events::extend and impl std::iter::Extend][2207] +- [Bump winit to 0.25][2186] +- [Improve legibility of RunOnce::run_unsafe param][2181] +- [Update gltf requirement from 0.15.2 to 0.16.0][2196] +- [Move to smallvec v1.6][2074] +- [Update rectangle-pack requirement from 0.3 to 0.4][2086] +- [Make Commands public?][2034] +- [Monomorphize various things][1914] +- [Detect camera projection changes][2015] +- [support assets of any size][1997] +- [Separate Query filter access from fetch access during initial evaluation][1977] +- [Provide better error message when missing a render backend][1965] +- [par_for_each: split batches when iterating on a sparse query][1945] +- [Allow deriving `SystemParam` on private types][1936] +- [Angle bracket annotated types to support generics][1919] +- [More detailed errors when resource not found][1864] +- [Moved events to ECS][1823] +- [Use a sorted Map for vertex buffer attributes][1796] +- [Error message improvements for shader compilation/gltf loading][1786] +- [Rename Light => PointLight and remove unused properties][1778] +- [Override size_hint for all Iterators and add ExactSizeIterator where applicable][1734] +- [Change breakout to use fixed timestamp][1541] + +### Fixed + +- [Fix shadows for non-TriangleLists][3581] +- [Fix error message for the `Component` macro's `component` `storage` attribute.][3534] +- [do not add plugin ExtractComponentPlugin twice for StandardMaterial][3502] +- [load spirv using correct API][3466] +- [fix shader compilation error reporting for non-wgsl shaders][3441] +- [bevy_ui: Check clip when handling interactions][3461] +- [crevice derive macro: fix path to render_resource when importing from bevy][3438] +- [fix parenting of scenes][2410] +- [Do not panic on failed setting of GameOver state in AlienCakeAddict][3411] +- [Fix minimization crash because of cluster updates.][3369] +- [Fix custom mesh pipelines][3381] +- [Fix hierarchy example panic][3378] +- [Fix double drop in BlobVec::replace_unchecked (#2597)][2848] +- [Remove vestigial derives][3343] +- [Fix crash with disabled winit][3330] +- [Fix clustering for orthographic projections][3316] +- [Run a clear pass on Windows without any Views][3304] +- [Remove some superfluous unsafe code][3297] +- [clearpass: also clear views without depth (2d)][3286] +- [Check for NaN in `Camera::world_to_screen()`][3268] +- [Fix sprite hot reloading in new renderer][3207] +- [Fix path used by macro not considering that we can use a sub-crate][3178] +- [Fix torus normals][3549] +- [enable alpha mode for textures materials that are transparent][3202] +- [fix calls to as_rgba_linear][3200] +- [Fix shadow logic][3186] +- [fix: as_rgba_linear used wrong variant][3192] +- [Fix MIME type support for glTF buffer Data URIs][3101] +- [Remove wasm audio feature flag for 2021][3000] +- [use correct size of pixel instead of 4][2977] +- [Fix custom_shader_pipelined example shader][2992] +- [Fix scale factor for cursor position][2932] +- [fix window resize after wgpu 0.11 upgrade][2953] +- [Fix unsound lifetime annotation on `Query::get_component`][2964] +- [Remove double Events::update in bevy-gilrs][2894] +- [Fix bevy_ecs::schedule::executor_parallel::system span management][2905] +- [Avoid some format! into immediate format!][2913] +- [Fix panic on is_resource_* calls (#2828)][2863] +- [Fix window size change panic][2858] +- [fix `Default` implementation of `Image` so that size and data match][2833] +- [Fix scale_factor_override in the winit backend][2784] +- [Fix breakout example scoreboard][2770] +- [Fix `Option>` and `Option>`][2757] +- [fix missing paths in ECS SystemParam derive macro v2][2550] +- [Add missing bytemuck feature][2625] +- [Update EntityMut's location in push_children() and insert_children()][2604] +- [Fixed issue with how texture arrays were uploaded with write_texture.][c24] +- [Don't update when suspended to avoid GPU use on iOS.][2482] +- [update archetypes for run criterias][2177] +- [Fix AssetServer::get_asset_loader deadlock][2395] +- [Fix unsetting RenderLayers bit in without fn][2409] +- [Fix view vector in pbr frag to work in ortho][2370] +- [Fixes Timer Precision Error Causing Panic][2362] +- [[assets] Fix `AssetServer::get_handle_path`][2310] +- [Fix bad bounds for NonSend SystemParams][2325] +- [Add minimum sizes to textures to prevent crash][2300] +- [[assets] set LoadState properly and more testing!][2226] +- [[assets] properly set `LoadState` with invalid asset extension][2318] +- [Fix Bevy crashing if no audio device is found][2269] +- [Fixes dropping empty BlobVec][2295] +- [[assets] fix Assets being set as 'changed' each frame][2280] +- [drop overwritten component data on double insert][2227] +- [Despawn with children doesn't need to remove entities from parents children when parents are also removed][2278] +- [reduce tricky unsafety and simplify table structure][2221] +- [Use bevy_reflect as path in case of no direct references][1875] +- [Fix Events:: bug][2206] +- [small ecs cleanup and remove_bundle drop bugfix][2172] +- [Fix PBR regression for unlit materials][2197] +- [prevent memory leak when dropping ParallelSystemContainer][2176] +- [fix diagnostic length for asset count][2165] +- [Fixes incorrect `PipelineCompiler::compile_pipeline()` step_mode][2126] +- [Asset re-loading while it's being deleted][2011] +- [Bevy derives handling generics in impl definitions.][2044] +- [Fix unsoundness in `Query::for_each_mut`][2045] +- [Fix mesh with no vertex attributes causing panic][2036] +- [Fix alien_cake_addict: cake should not be at height of player's location][1954] +- [fix memory size for PointLightBundle][1940] +- [Fix unsoundness in query component access][1929] +- [fixing compilation error on macos aarch64][1905] +- [Fix SystemParam handling of Commands][1899] +- [Fix IcoSphere UV coordinates][1871] +- [fix 'attempted to subtract with overflow' for State::inactives][1668] + +[1007]: https://github.com/bevyengine/bevy/pull/1007 +[1395]: https://github.com/bevyengine/bevy/pull/1395 +[1541]: https://github.com/bevyengine/bevy/pull/1541 +[1667]: https://github.com/bevyengine/bevy/pull/1667 +[1668]: https://github.com/bevyengine/bevy/pull/1668 +[1714]: https://github.com/bevyengine/bevy/pull/1714 +[1734]: https://github.com/bevyengine/bevy/pull/1734 +[1763]: https://github.com/bevyengine/bevy/pull/1763 +[1765]: https://github.com/bevyengine/bevy/pull/1765 +[1767]: https://github.com/bevyengine/bevy/pull/1767 +[1775]: https://github.com/bevyengine/bevy/pull/1775 +[1778]: https://github.com/bevyengine/bevy/pull/1778 +[1786]: https://github.com/bevyengine/bevy/pull/1786 +[1796]: https://github.com/bevyengine/bevy/pull/1796 +[1803]: https://github.com/bevyengine/bevy/pull/1803 +[1808]: https://github.com/bevyengine/bevy/pull/1808 +[1817]: https://github.com/bevyengine/bevy/pull/1817 +[1823]: https://github.com/bevyengine/bevy/pull/1823 +[1828]: https://github.com/bevyengine/bevy/pull/1828 +[1831]: https://github.com/bevyengine/bevy/pull/1831 +[1864]: https://github.com/bevyengine/bevy/pull/1864 +[1871]: https://github.com/bevyengine/bevy/pull/1871 +[1875]: https://github.com/bevyengine/bevy/pull/1875 +[1878]: https://github.com/bevyengine/bevy/pull/1878 +[1887]: https://github.com/bevyengine/bevy/pull/1887 +[1899]: https://github.com/bevyengine/bevy/pull/1899 +[1901]: https://github.com/bevyengine/bevy/pull/1901 +[1905]: https://github.com/bevyengine/bevy/pull/1905 +[1909]: https://github.com/bevyengine/bevy/pull/1909 +[1914]: https://github.com/bevyengine/bevy/pull/1914 +[1919]: https://github.com/bevyengine/bevy/pull/1919 +[1925]: https://github.com/bevyengine/bevy/pull/1925 +[1927]: https://github.com/bevyengine/bevy/pull/1927 +[1929]: https://github.com/bevyengine/bevy/pull/1929 +[1936]: https://github.com/bevyengine/bevy/pull/1936 +[1940]: https://github.com/bevyengine/bevy/pull/1940 +[1945]: https://github.com/bevyengine/bevy/pull/1945 +[1954]: https://github.com/bevyengine/bevy/pull/1954 +[1963]: https://github.com/bevyengine/bevy/pull/1963 +[1965]: https://github.com/bevyengine/bevy/pull/1965 +[1972]: https://github.com/bevyengine/bevy/pull/1972 +[1977]: https://github.com/bevyengine/bevy/pull/1977 +[1997]: https://github.com/bevyengine/bevy/pull/1997 +[2011]: https://github.com/bevyengine/bevy/pull/2011 +[2015]: https://github.com/bevyengine/bevy/pull/2015 +[2024]: https://github.com/bevyengine/bevy/pull/2024 +[2033]: https://github.com/bevyengine/bevy/pull/2033 +[2034]: https://github.com/bevyengine/bevy/pull/2034 +[2036]: https://github.com/bevyengine/bevy/pull/2036 +[2038]: https://github.com/bevyengine/bevy/pull/2038 +[2044]: https://github.com/bevyengine/bevy/pull/2044 +[2045]: https://github.com/bevyengine/bevy/pull/2045 +[2053]: https://github.com/bevyengine/bevy/pull/2053 +[2074]: https://github.com/bevyengine/bevy/pull/2074 +[2084]: https://github.com/bevyengine/bevy/pull/2084 +[2086]: https://github.com/bevyengine/bevy/pull/2086 +[2100]: https://github.com/bevyengine/bevy/pull/2100 +[2112]: https://github.com/bevyengine/bevy/pull/2112 +[2121]: https://github.com/bevyengine/bevy/pull/2121 +[2126]: https://github.com/bevyengine/bevy/pull/2126 +[2165]: https://github.com/bevyengine/bevy/pull/2165 +[2172]: https://github.com/bevyengine/bevy/pull/2172 +[2175]: https://github.com/bevyengine/bevy/pull/2175 +[2176]: https://github.com/bevyengine/bevy/pull/2176 +[2177]: https://github.com/bevyengine/bevy/pull/2177 +[2180]: https://github.com/bevyengine/bevy/pull/2180 +[2181]: https://github.com/bevyengine/bevy/pull/2181 +[2183]: https://github.com/bevyengine/bevy/pull/2183 +[2186]: https://github.com/bevyengine/bevy/pull/2186 +[2189]: https://github.com/bevyengine/bevy/pull/2189 +[2196]: https://github.com/bevyengine/bevy/pull/2196 +[2197]: https://github.com/bevyengine/bevy/pull/2197 +[2206]: https://github.com/bevyengine/bevy/pull/2206 +[2207]: https://github.com/bevyengine/bevy/pull/2207 +[2208]: https://github.com/bevyengine/bevy/pull/2208 +[2221]: https://github.com/bevyengine/bevy/pull/2221 +[2226]: https://github.com/bevyengine/bevy/pull/2226 +[2227]: https://github.com/bevyengine/bevy/pull/2227 +[2244]: https://github.com/bevyengine/bevy/pull/2244 +[2254]: https://github.com/bevyengine/bevy/pull/2254 +[2260]: https://github.com/bevyengine/bevy/pull/2260 +[2269]: https://github.com/bevyengine/bevy/pull/2269 +[2271]: https://github.com/bevyengine/bevy/pull/2271 +[2278]: https://github.com/bevyengine/bevy/pull/2278 +[2280]: https://github.com/bevyengine/bevy/pull/2280 +[2283]: https://github.com/bevyengine/bevy/pull/2283 +[2295]: https://github.com/bevyengine/bevy/pull/2295 +[2300]: https://github.com/bevyengine/bevy/pull/2300 +[2305]: https://github.com/bevyengine/bevy/pull/2305 +[2310]: https://github.com/bevyengine/bevy/pull/2310 +[2318]: https://github.com/bevyengine/bevy/pull/2318 +[2325]: https://github.com/bevyengine/bevy/pull/2325 +[2326]: https://github.com/bevyengine/bevy/pull/2326 +[2332]: https://github.com/bevyengine/bevy/pull/2332 +[2345]: https://github.com/bevyengine/bevy/pull/2345 +[2360]: https://github.com/bevyengine/bevy/pull/2360 +[2362]: https://github.com/bevyengine/bevy/pull/2362 +[2366]: https://github.com/bevyengine/bevy/pull/2366 +[2370]: https://github.com/bevyengine/bevy/pull/2370 +[2393]: https://github.com/bevyengine/bevy/pull/2393 +[2395]: https://github.com/bevyengine/bevy/pull/2395 +[2397]: https://github.com/bevyengine/bevy/pull/2397 +[2398]: https://github.com/bevyengine/bevy/pull/2398 +[2403]: https://github.com/bevyengine/bevy/pull/2403 +[2409]: https://github.com/bevyengine/bevy/pull/2409 +[2410]: https://github.com/bevyengine/bevy/pull/2410 +[2411]: https://github.com/bevyengine/bevy/pull/2411 +[2422]: https://github.com/bevyengine/bevy/pull/2422 +[2431]: https://github.com/bevyengine/bevy/pull/2431 +[2449]: https://github.com/bevyengine/bevy/pull/2449 +[2482]: https://github.com/bevyengine/bevy/pull/2482 +[2490]: https://github.com/bevyengine/bevy/pull/2490 +[2494]: https://github.com/bevyengine/bevy/pull/2494 +[2496]: https://github.com/bevyengine/bevy/pull/2496 +[2509]: https://github.com/bevyengine/bevy/pull/2509 +[2515]: https://github.com/bevyengine/bevy/pull/2515 +[2520]: https://github.com/bevyengine/bevy/pull/2520 +[2521]: https://github.com/bevyengine/bevy/pull/2521 +[2531]: https://github.com/bevyengine/bevy/pull/2531 +[2537]: https://github.com/bevyengine/bevy/pull/2537 +[2538]: https://github.com/bevyengine/bevy/pull/2538 +[2542]: https://github.com/bevyengine/bevy/pull/2542 +[2543]: https://github.com/bevyengine/bevy/pull/2543 +[2550]: https://github.com/bevyengine/bevy/pull/2550 +[2552]: https://github.com/bevyengine/bevy/pull/2552 +[2555]: https://github.com/bevyengine/bevy/pull/2555 +[2560]: https://github.com/bevyengine/bevy/pull/2560 +[2564]: https://github.com/bevyengine/bevy/pull/2564 +[2578]: https://github.com/bevyengine/bevy/pull/2578 +[2581]: https://github.com/bevyengine/bevy/pull/2581 +[2598]: https://github.com/bevyengine/bevy/pull/2598 +[2601]: https://github.com/bevyengine/bevy/pull/2601 +[2604]: https://github.com/bevyengine/bevy/pull/2604 +[2605]: https://github.com/bevyengine/bevy/pull/2605 +[2606]: https://github.com/bevyengine/bevy/pull/2606 +[2613]: https://github.com/bevyengine/bevy/pull/2613 +[2614]: https://github.com/bevyengine/bevy/pull/2614 +[2623]: https://github.com/bevyengine/bevy/pull/2623 +[2625]: https://github.com/bevyengine/bevy/pull/2625 +[2628]: https://github.com/bevyengine/bevy/pull/2628 +[2631]: https://github.com/bevyengine/bevy/pull/2631 +[2632]: https://github.com/bevyengine/bevy/pull/2632 +[2641]: https://github.com/bevyengine/bevy/pull/2641 +[2653]: https://github.com/bevyengine/bevy/pull/2653 +[2672]: https://github.com/bevyengine/bevy/pull/2672 +[2673]: https://github.com/bevyengine/bevy/pull/2673 +[2682]: https://github.com/bevyengine/bevy/pull/2682 +[2684]: https://github.com/bevyengine/bevy/pull/2684 +[2690]: https://github.com/bevyengine/bevy/pull/2690 +[2695]: https://github.com/bevyengine/bevy/pull/2695 +[2700]: https://github.com/bevyengine/bevy/pull/2700 +[2703]: https://github.com/bevyengine/bevy/pull/2703 +[2704]: https://github.com/bevyengine/bevy/pull/2704 +[2717]: https://github.com/bevyengine/bevy/pull/2717 +[2718]: https://github.com/bevyengine/bevy/pull/2718 +[2726]: https://github.com/bevyengine/bevy/pull/2726 +[2727]: https://github.com/bevyengine/bevy/pull/2727 +[2740]: https://github.com/bevyengine/bevy/pull/2740 +[2741]: https://github.com/bevyengine/bevy/pull/2741 +[2748]: https://github.com/bevyengine/bevy/pull/2748 +[2757]: https://github.com/bevyengine/bevy/pull/2757 +[2759]: https://github.com/bevyengine/bevy/pull/2759 +[2760]: https://github.com/bevyengine/bevy/pull/2760 +[2770]: https://github.com/bevyengine/bevy/pull/2770 +[2772]: https://github.com/bevyengine/bevy/pull/2772 +[2778]: https://github.com/bevyengine/bevy/pull/2778 +[2784]: https://github.com/bevyengine/bevy/pull/2784 +[2785]: https://github.com/bevyengine/bevy/pull/2785 +[2793]: https://github.com/bevyengine/bevy/pull/2793 +[2807]: https://github.com/bevyengine/bevy/pull/2807 +[2819]: https://github.com/bevyengine/bevy/pull/2819 +[2827]: https://github.com/bevyengine/bevy/pull/2827 +[2831]: https://github.com/bevyengine/bevy/pull/2831 +[2832]: https://github.com/bevyengine/bevy/pull/2832 +[2833]: https://github.com/bevyengine/bevy/pull/2833 +[2847]: https://github.com/bevyengine/bevy/pull/2847 +[2848]: https://github.com/bevyengine/bevy/pull/2848 +[2850]: https://github.com/bevyengine/bevy/pull/2850 +[2855]: https://github.com/bevyengine/bevy/pull/2855 +[2858]: https://github.com/bevyengine/bevy/pull/2858 +[2861]: https://github.com/bevyengine/bevy/pull/2861 +[2863]: https://github.com/bevyengine/bevy/pull/2863 +[2864]: https://github.com/bevyengine/bevy/pull/2864 +[2880]: https://github.com/bevyengine/bevy/pull/2880 +[2885]: https://github.com/bevyengine/bevy/pull/2885 +[2887]: https://github.com/bevyengine/bevy/pull/2887 +[2890]: https://github.com/bevyengine/bevy/pull/2890 +[2894]: https://github.com/bevyengine/bevy/pull/2894 +[2903]: https://github.com/bevyengine/bevy/pull/2903 +[2905]: https://github.com/bevyengine/bevy/pull/2905 +[2907]: https://github.com/bevyengine/bevy/pull/2907 +[2912]: https://github.com/bevyengine/bevy/pull/2912 +[2913]: https://github.com/bevyengine/bevy/pull/2913 +[2932]: https://github.com/bevyengine/bevy/pull/2932 +[2943]: https://github.com/bevyengine/bevy/pull/2943 +[2953]: https://github.com/bevyengine/bevy/pull/2953 +[2957]: https://github.com/bevyengine/bevy/pull/2957 +[2964]: https://github.com/bevyengine/bevy/pull/2964 +[2977]: https://github.com/bevyengine/bevy/pull/2977 +[2989]: https://github.com/bevyengine/bevy/pull/2989 +[2990]: https://github.com/bevyengine/bevy/pull/2990 +[2992]: https://github.com/bevyengine/bevy/pull/2992 +[3000]: https://github.com/bevyengine/bevy/pull/3000 +[3028]: https://github.com/bevyengine/bevy/pull/3028 +[3031]: https://github.com/bevyengine/bevy/pull/3031 +[3038]: https://github.com/bevyengine/bevy/pull/3038 +[3039]: https://github.com/bevyengine/bevy/pull/3039 +[3041]: https://github.com/bevyengine/bevy/pull/3041 +[3042]: https://github.com/bevyengine/bevy/pull/3042 +[3049]: https://github.com/bevyengine/bevy/pull/3049 +[3059]: https://github.com/bevyengine/bevy/pull/3059 +[3060]: https://github.com/bevyengine/bevy/pull/3060 +[3069]: https://github.com/bevyengine/bevy/pull/3069 +[3070]: https://github.com/bevyengine/bevy/pull/3070 +[3072]: https://github.com/bevyengine/bevy/pull/3072 +[3075]: https://github.com/bevyengine/bevy/pull/3075 +[3076]: https://github.com/bevyengine/bevy/pull/3076 +[3097]: https://github.com/bevyengine/bevy/pull/3097 +[3101]: https://github.com/bevyengine/bevy/pull/3101 +[3105]: https://github.com/bevyengine/bevy/pull/3105 +[3109]: https://github.com/bevyengine/bevy/pull/3109 +[3111]: https://github.com/bevyengine/bevy/pull/3111 +[3113]: https://github.com/bevyengine/bevy/pull/3113 +[3118]: https://github.com/bevyengine/bevy/pull/3118 +[3122]: https://github.com/bevyengine/bevy/pull/3122 +[3126]: https://github.com/bevyengine/bevy/pull/3126 +[3137]: https://github.com/bevyengine/bevy/pull/3137 +[3153]: https://github.com/bevyengine/bevy/pull/3153 +[3166]: https://github.com/bevyengine/bevy/pull/3166 +[3168]: https://github.com/bevyengine/bevy/pull/3168 +[3171]: https://github.com/bevyengine/bevy/pull/3171 +[3175]: https://github.com/bevyengine/bevy/pull/3175 +[3178]: https://github.com/bevyengine/bevy/pull/3178 +[3182]: https://github.com/bevyengine/bevy/pull/3182 +[3186]: https://github.com/bevyengine/bevy/pull/3186 +[3189]: https://github.com/bevyengine/bevy/pull/3189 +[3192]: https://github.com/bevyengine/bevy/pull/3192 +[3193]: https://github.com/bevyengine/bevy/pull/3193 +[3200]: https://github.com/bevyengine/bevy/pull/3200 +[3201]: https://github.com/bevyengine/bevy/pull/3201 +[3202]: https://github.com/bevyengine/bevy/pull/3202 +[3206]: https://github.com/bevyengine/bevy/pull/3206 +[3207]: https://github.com/bevyengine/bevy/pull/3207 +[3209]: https://github.com/bevyengine/bevy/pull/3209 +[3236]: https://github.com/bevyengine/bevy/pull/3236 +[3244]: https://github.com/bevyengine/bevy/pull/3244 +[3257]: https://github.com/bevyengine/bevy/pull/3257 +[3258]: https://github.com/bevyengine/bevy/pull/3258 +[3260]: https://github.com/bevyengine/bevy/pull/3260 +[3264]: https://github.com/bevyengine/bevy/pull/3264 +[3268]: https://github.com/bevyengine/bevy/pull/3268 +[3270]: https://github.com/bevyengine/bevy/pull/3270 +[3271]: https://github.com/bevyengine/bevy/pull/3271 +[3280]: https://github.com/bevyengine/bevy/pull/3280 +[3281]: https://github.com/bevyengine/bevy/pull/3281 +[3282]: https://github.com/bevyengine/bevy/pull/3282 +[3286]: https://github.com/bevyengine/bevy/pull/3286 +[3289]: https://github.com/bevyengine/bevy/pull/3289 +[3290]: https://github.com/bevyengine/bevy/pull/3290 +[3291]: https://github.com/bevyengine/bevy/pull/3291 +[3296]: https://github.com/bevyengine/bevy/pull/3296 +[3297]: https://github.com/bevyengine/bevy/pull/3297 +[3304]: https://github.com/bevyengine/bevy/pull/3304 +[3309]: https://github.com/bevyengine/bevy/pull/3309 +[3316]: https://github.com/bevyengine/bevy/pull/3316 +[3318]: https://github.com/bevyengine/bevy/pull/3318 +[3320]: https://github.com/bevyengine/bevy/pull/3320 +[3325]: https://github.com/bevyengine/bevy/pull/3325 +[3330]: https://github.com/bevyengine/bevy/pull/3330 +[3335]: https://github.com/bevyengine/bevy/pull/3335 +[3336]: https://github.com/bevyengine/bevy/pull/3336 +[3337]: https://github.com/bevyengine/bevy/pull/3337 +[3343]: https://github.com/bevyengine/bevy/pull/3343 +[3349]: https://github.com/bevyengine/bevy/pull/3349 +[3364]: https://github.com/bevyengine/bevy/pull/3364 +[3367]: https://github.com/bevyengine/bevy/pull/3367 +[3369]: https://github.com/bevyengine/bevy/pull/3369 +[3371]: https://github.com/bevyengine/bevy/pull/3371 +[3375]: https://github.com/bevyengine/bevy/pull/3375 +[3378]: https://github.com/bevyengine/bevy/pull/3378 +[3381]: https://github.com/bevyengine/bevy/pull/3381 +[3393]: https://github.com/bevyengine/bevy/pull/3393 +[3395]: https://github.com/bevyengine/bevy/pull/3395 +[3401]: https://github.com/bevyengine/bevy/pull/3401 +[3411]: https://github.com/bevyengine/bevy/pull/3411 +[3413]: https://github.com/bevyengine/bevy/pull/3413 +[3415]: https://github.com/bevyengine/bevy/pull/3415 +[3416]: https://github.com/bevyengine/bevy/pull/3416 +[3420]: https://github.com/bevyengine/bevy/pull/3420 +[3421]: https://github.com/bevyengine/bevy/pull/3421 +[3426]: https://github.com/bevyengine/bevy/pull/3426 +[3428]: https://github.com/bevyengine/bevy/pull/3428 +[3438]: https://github.com/bevyengine/bevy/pull/3438 +[3441]: https://github.com/bevyengine/bevy/pull/3441 +[3443]: https://github.com/bevyengine/bevy/pull/3443 +[3448]: https://github.com/bevyengine/bevy/pull/3448 +[3452]: https://github.com/bevyengine/bevy/pull/3452 +[3460]: https://github.com/bevyengine/bevy/pull/3460 +[3461]: https://github.com/bevyengine/bevy/pull/3461 +[3465]: https://github.com/bevyengine/bevy/pull/3465 +[3466]: https://github.com/bevyengine/bevy/pull/3466 +[3489]: https://github.com/bevyengine/bevy/pull/3489 +[3490]: https://github.com/bevyengine/bevy/pull/3490 +[3495]: https://github.com/bevyengine/bevy/pull/3495 +[3498]: https://github.com/bevyengine/bevy/pull/3498 +[3502]: https://github.com/bevyengine/bevy/pull/3502 +[3506]: https://github.com/bevyengine/bevy/pull/3506 +[3521]: https://github.com/bevyengine/bevy/pull/3521 +[3534]: https://github.com/bevyengine/bevy/pull/3534 +[3544]: https://github.com/bevyengine/bevy/pull/3544 +[3545]: https://github.com/bevyengine/bevy/pull/3545 +[3546]: https://github.com/bevyengine/bevy/pull/3546 +[3549]: https://github.com/bevyengine/bevy/pull/3549 +[3551]: https://github.com/bevyengine/bevy/pull/3551 +[3553]: https://github.com/bevyengine/bevy/pull/3553 +[3577]: https://github.com/bevyengine/bevy/pull/3577 +[3581]: https://github.com/bevyengine/bevy/pull/3581 +[c6]: https://github.com/cart/bevy/pull/6 +[c24]: https://github.com/cart/bevy/pull/24 +[c26]: https://github.com/cart/bevy/pull/26 + +## Version 0.5.0 (2021-04-06) ### Added -- [Another fast compile flag for macOS][552] +- [PBR Rendering][1554] +- [PBR Textures][1632] +- [HIDPI Text][1132] +- [Rich text][1245] +- [Wireframe Rendering Pipeline][562] +- [Render Layers][1209] +- [Add Sprite Flipping][1407] +- [OrthographicProjection scaling mode + camera bundle refactoring][400] +- [3D OrthographicProjection improvements + new example][1361] +- [Flexible camera bindings][1689] +- [Render text in 2D scenes][1122] +- [`Text2d` render quality][1171] +- [System sets and run criteria v2][1675] +- [System sets and parallel executor v2][1144] +- [Many-to-many system labels][1576] +- [Non-string labels (#1423 continued)][1473] +- [Make `EventReader` a `SystemParam`][1244] +- [Add `EventWriter`][1575] +- [Reliable change detection][1471] +- [Redo State architecture][1424] +- [`Query::get_unique`][1263] +- [gltf: load normal and occlusion as linear textures][1762] +- [Add separate brightness field to AmbientLight][1605] +- [world coords to screen space][1258] +- [Experimental Frustum Culling (for Sprites)][1492] +- [Enable wgpu device limits][1544] +- [bevy_render: add torus and capsule shape][1223] +- [New mesh attribute: color][1194] +- [Minimal change to support instanced rendering][1262] +- [Add support for reading from mapped buffers][1274] +- [Texture atlas format and conversion][1365] +- [enable wgpu device features][547] +- [Subpixel text positioning][1196] +- [make more information available from loaded GLTF model][1020] +- [use `Name` on node when loading a gltf file][1183] +- [GLTF loader: support mipmap filters][1639] +- [Add support for gltf::Material::unlit][1341] +- [Implement `Reflect` for tuples up to length 12][1218] +- [Process Asset File Extensions With Multiple Dots][1277] +- [Update Scene Example to Use scn.ron File][1339] +- [3d game example][1252] +- [Add keyboard modifier example (#1656)][1657] +- [Count number of times a repeating Timer wraps around in a tick][1112] +- [recycle `Timer` refactor to duration.sparkles Add `Stopwatch` struct.][1151] +- [add scene instance entity iteration][1058] +- [Make `Commands` and `World` apis consistent][1703] +- [Add `insert_children` and `push_children` to `EntityMut`][1728] +- [Extend `AppBuilder` api with `add_system_set` and similar methods][1453] +- [add labels and ordering for transform and parent systems in `POST_UPDATE` stage][1456] +- [Explicit execution order ambiguities API][1469] +- [Resolve (most) internal system ambiguities][1606] +- [Change 'components' to 'bundles' where it makes sense semantically][1257] +- [add `Flags` as a query to get flags of component][1172] +- [Rename `add_resource` to `insert_resource`][1356] +- [Update `init_resource` to not overwrite][1349] +- [Enable dynamic mutable access to component data][1284] +- [Get rid of `ChangedRes`][1313] +- [impl `SystemParam` for `Option>` / `Option>`][1494] +- [Add Window Resize Constraints][1409] +- [Add basic file drag and drop support][1096] +- [Modify Derive to allow unit structs for `RenderResources`.][1089] +- [bevy_render: load .spv assets][1104] +- [Expose wgpu backend in WgpuOptions and allow it to be configured from the environment][1042] +- [updates on diagnostics (log + new diagnostics)][1085] +- [enable change detection for labels][1155] +- [Name component with fast comparisons][1109] +- [Support for `!Send` tasks][1216] +- [Add missing `spawn_local` method to `Scope` in the single threaded executor case][1266] +- [Add bmp as a supported texture format][1081] +- [Add an alternative winit runner that can be started when not on the main thread][1063] +- [Added `use_dpi` setting to `WindowDescriptor`][1131] +- [Implement `Copy` for `ElementState`][1154] +- [Mutable mesh accessors: `indices_mut` and `attribute_mut`][1164] +- [Add support for OTF fonts][1200] +- [Add `from_xyz` to `Transform`][1212] +- [Adding `copy_texture_to_buffer` and `copy_texture_to_texture`][1236] +- [Added `set_minimized` and `set_position` to `Window`][1292] +- [Example for 2D Frustum Culling][1503] +- [Add remove resource to commands][1478] + +### Changed + +- [Bevy ECS V2][1525] +- [Fix Reflect serialization of tuple structs][1366] +- [color spaces and representation][1572] +- [Make vertex buffers optional][1485] +- [add to lower case to make asset loading case insensitive][1427] +- [Replace right/up/forward and counter parts with `local_x`/`local_y` and `local_z`][1476] +- [Use valid keys to initialize `AHasher` in `FixedState`][1268] +- [Change `Name` to take `Into` instead of `String`][1283] +- [Update to wgpu-rs 0.7][542] +- [Update glam to 0.13.0.][1550] +- [use std clamp instead of Bevy's][1644] +- [Make `Reflect` impls unsafe (`Reflect::any` must return `self`)][1679] + +### Fixed + +- [convert grayscale images to rgb][1524] +- [Glb textures should use bevy_render to load images][1454] +- [Don't panic on error when loading assets][1286] +- [Prevent ImageBundles from causing constant layout recalculations][1299] +- [do not check for focus until cursor position has been set][1070] +- [Fix lock order to remove the chance of deadlock][1121] +- [Prevent double panic in the Drop of TaksPoolInner][1064] +- [Ignore events when receiving unknown WindowId][1072] +- [Fix potential bug when using multiple lights.][1055] +- [remove panics when mixing UI and non UI entities in hierarchy][1180] +- [fix label to load gltf scene][1204] +- [fix repeated gamepad events][1221] +- [Fix iOS touch location][1224] +- [Don't panic if there's no index buffer and call draw][1229] +- [Fix Bug in Asset Server Error Message Formatter][1340] +- [add_stage now checks Stage existence][1346] +- [Fix Un-Renamed add_resource Compile Error][1357] +- [Fix Interaction not resetting to None sometimes][1315] +- [Fix regression causing "flipped" sprites to be invisible][1399] +- [revert default vsync mode to Fifo][1416] +- [Fix missing paths in ECS SystemParam derive macro][1434] +- [Fix staging buffer required size calculation (fixes #1056)][1509] + +[400]: https://github.com/bevyengine/bevy/pull/400 +[542]: https://github.com/bevyengine/bevy/pull/542 +[547]: https://github.com/bevyengine/bevy/pull/547 +[562]: https://github.com/bevyengine/bevy/pull/562 +[1020]: https://github.com/bevyengine/bevy/pull/1020 +[1042]: https://github.com/bevyengine/bevy/pull/1042 +[1055]: https://github.com/bevyengine/bevy/pull/1055 +[1058]: https://github.com/bevyengine/bevy/pull/1058 +[1063]: https://github.com/bevyengine/bevy/pull/1063 +[1064]: https://github.com/bevyengine/bevy/pull/1064 +[1070]: https://github.com/bevyengine/bevy/pull/1070 +[1072]: https://github.com/bevyengine/bevy/pull/1072 +[1081]: https://github.com/bevyengine/bevy/pull/1081 +[1085]: https://github.com/bevyengine/bevy/pull/1085 +[1089]: https://github.com/bevyengine/bevy/pull/1089 +[1096]: https://github.com/bevyengine/bevy/pull/1096 +[1104]: https://github.com/bevyengine/bevy/pull/1104 +[1109]: https://github.com/bevyengine/bevy/pull/1109 +[1112]: https://github.com/bevyengine/bevy/pull/1112 +[1121]: https://github.com/bevyengine/bevy/pull/1121 +[1122]: https://github.com/bevyengine/bevy/pull/1122 +[1131]: https://github.com/bevyengine/bevy/pull/1131 +[1132]: https://github.com/bevyengine/bevy/pull/1132 +[1144]: https://github.com/bevyengine/bevy/pull/1144 +[1151]: https://github.com/bevyengine/bevy/pull/1151 +[1154]: https://github.com/bevyengine/bevy/pull/1154 +[1155]: https://github.com/bevyengine/bevy/pull/1155 +[1164]: https://github.com/bevyengine/bevy/pull/1164 +[1171]: https://github.com/bevyengine/bevy/pull/1171 +[1172]: https://github.com/bevyengine/bevy/pull/1172 +[1180]: https://github.com/bevyengine/bevy/pull/1180 +[1183]: https://github.com/bevyengine/bevy/pull/1183 +[1194]: https://github.com/bevyengine/bevy/pull/1194 +[1196]: https://github.com/bevyengine/bevy/pull/1196 +[1200]: https://github.com/bevyengine/bevy/pull/1200 +[1204]: https://github.com/bevyengine/bevy/pull/1204 +[1209]: https://github.com/bevyengine/bevy/pull/1209 +[1212]: https://github.com/bevyengine/bevy/pull/1212 +[1216]: https://github.com/bevyengine/bevy/pull/1216 +[1218]: https://github.com/bevyengine/bevy/pull/1218 +[1221]: https://github.com/bevyengine/bevy/pull/1221 +[1223]: https://github.com/bevyengine/bevy/pull/1223 +[1224]: https://github.com/bevyengine/bevy/pull/1224 +[1229]: https://github.com/bevyengine/bevy/pull/1229 +[1236]: https://github.com/bevyengine/bevy/pull/1236 +[1244]: https://github.com/bevyengine/bevy/pull/1244 +[1245]: https://github.com/bevyengine/bevy/pull/1245 +[1252]: https://github.com/bevyengine/bevy/pull/1252 +[1257]: https://github.com/bevyengine/bevy/pull/1257 +[1258]: https://github.com/bevyengine/bevy/pull/1258 +[1262]: https://github.com/bevyengine/bevy/pull/1262 +[1263]: https://github.com/bevyengine/bevy/pull/1263 +[1266]: https://github.com/bevyengine/bevy/pull/1266 +[1268]: https://github.com/bevyengine/bevy/pull/1268 +[1274]: https://github.com/bevyengine/bevy/pull/1274 +[1277]: https://github.com/bevyengine/bevy/pull/1277 +[1283]: https://github.com/bevyengine/bevy/pull/1283 +[1284]: https://github.com/bevyengine/bevy/pull/1284 +[1286]: https://github.com/bevyengine/bevy/pull/1286 +[1292]: https://github.com/bevyengine/bevy/pull/1292 +[1299]: https://github.com/bevyengine/bevy/pull/1299 +[1313]: https://github.com/bevyengine/bevy/pull/1313 +[1315]: https://github.com/bevyengine/bevy/pull/1315 +[1339]: https://github.com/bevyengine/bevy/pull/1339 +[1340]: https://github.com/bevyengine/bevy/pull/1340 +[1341]: https://github.com/bevyengine/bevy/pull/1341 +[1346]: https://github.com/bevyengine/bevy/pull/1346 +[1349]: https://github.com/bevyengine/bevy/pull/1349 +[1356]: https://github.com/bevyengine/bevy/pull/1356 +[1357]: https://github.com/bevyengine/bevy/pull/1357 +[1361]: https://github.com/bevyengine/bevy/pull/1361 +[1365]: https://github.com/bevyengine/bevy/pull/1365 +[1366]: https://github.com/bevyengine/bevy/pull/1366 +[1399]: https://github.com/bevyengine/bevy/pull/1399 +[1407]: https://github.com/bevyengine/bevy/pull/1407 +[1409]: https://github.com/bevyengine/bevy/pull/1409 +[1416]: https://github.com/bevyengine/bevy/pull/1416 +[1424]: https://github.com/bevyengine/bevy/pull/1424 +[1427]: https://github.com/bevyengine/bevy/pull/1427 +[1434]: https://github.com/bevyengine/bevy/pull/1434 +[1453]: https://github.com/bevyengine/bevy/pull/1453 +[1454]: https://github.com/bevyengine/bevy/pull/1454 +[1456]: https://github.com/bevyengine/bevy/pull/1456 +[1469]: https://github.com/bevyengine/bevy/pull/1469 +[1471]: https://github.com/bevyengine/bevy/pull/1471 +[1473]: https://github.com/bevyengine/bevy/pull/1473 +[1476]: https://github.com/bevyengine/bevy/pull/1476 +[1478]: https://github.com/bevyengine/bevy/pull/1478 +[1485]: https://github.com/bevyengine/bevy/pull/1485 +[1492]: https://github.com/bevyengine/bevy/pull/1492 +[1494]: https://github.com/bevyengine/bevy/pull/1494 +[1503]: https://github.com/bevyengine/bevy/pull/1503 +[1509]: https://github.com/bevyengine/bevy/pull/1509 +[1524]: https://github.com/bevyengine/bevy/pull/1524 +[1525]: https://github.com/bevyengine/bevy/pull/1525 +[1544]: https://github.com/bevyengine/bevy/pull/1544 +[1550]: https://github.com/bevyengine/bevy/pull/1550 +[1554]: https://github.com/bevyengine/bevy/pull/1554 +[1572]: https://github.com/bevyengine/bevy/pull/1572 +[1575]: https://github.com/bevyengine/bevy/pull/1575 +[1576]: https://github.com/bevyengine/bevy/pull/1576 +[1605]: https://github.com/bevyengine/bevy/pull/1605 +[1606]: https://github.com/bevyengine/bevy/pull/1606 +[1632]: https://github.com/bevyengine/bevy/pull/1632 +[1639]: https://github.com/bevyengine/bevy/pull/1639 +[1644]: https://github.com/bevyengine/bevy/pull/1644 +[1657]: https://github.com/bevyengine/bevy/pull/1657 +[1675]: https://github.com/bevyengine/bevy/pull/1675 +[1679]: https://github.com/bevyengine/bevy/pull/1679 +[1689]: https://github.com/bevyengine/bevy/pull/1689 +[1703]: https://github.com/bevyengine/bevy/pull/1703 +[1728]: https://github.com/bevyengine/bevy/pull/1728 +[1762]: https://github.com/bevyengine/bevy/pull/1762 + +## Version 0.4.0 (2020-12-19) + +### Added + +- [add bevymark benchmark example][273] +- [gltf: support camera and fix hierarchy][772] +- [Add tracing spans to schedules, stages, systems][789] +- [add example that represents contributors as bevy icons][801] +- [Add received character][805] +- [Add bevy_dylib to force dynamic linking of bevy][808] +- [Added RenderPass::set_scissor_rect][815] +- [`bevy_log`][836] + - Adds logging functionality as a Plugin. + - Changes internal logging to work with the new implementation. +- [cross-platform main function][847] +- [Controllable ambient light color][852] + - Added a resource to change the current ambient light color for PBR. +- [Added more basic color constants][859] +- [Add box shape][883] +- [Expose an EventId for events][894] +- [System Inputs, Outputs, and Chaining][876] +- [Expose an `EventId` for events][894] +- [Added `set_cursor_position` to `Window`][917] +- [Added new Bevy reflection system][926] + - Replaces the properties system +- [Add support for Apple Silicon][928] +- [Live reloading of shaders][937] +- [Store mouse cursor position in Window][940] +- [Add removal_detection example][945] +- [Additional vertex attribute value types][946] +- [Added WindowFocused event][956] +- [Tracing chrome span names][979] +- [Allow windows to be maximized][1004] +- [GLTF: load default material][1016] +- [can spawn a scene from a ChildBuilder, or directly set its parent when spawning it][1026] +- [add ability to load `.dds`, `.tga`, and `.jpeg` texture formats][1038] +- [add ability to provide custom a `AssetIo` implementation][1037] + +### Changed + +- [delegate layout reflection to RenderResourceContext][691] +- [Fall back to remove components one by one when failing to remove a bundle][719] +- [Port hecs derive macro improvements][761] +- [Use glyph_brush_layout and add text alignment support][765] +- [upgrade glam and hexasphere][791] +- [Flexible ECS Params][798] +- [Make Timer.tick return &Self][820] +- [FileAssetIo includes full path on error][821] +- [Removed ECS query APIs that could easily violate safety from the public interface][829] +- [Changed Query filter API to be easier to understand][834] +- [bevy_render: delegate buffer aligning to render_resource_context][842] +- [wasm32: non-spirv shader specialization][843] +- [Renamed XComponents to XBundle][863] +- [Check for conflicting system resource parameters][864] +- [Tweaks to TextureAtlasBuilder.finish()][887] +- [do not spend time drawing text with is_visible = false][893] +- [Extend the Texture asset type to support 3D data][903] +- [Breaking changes to timer API][914] + - Created getters and setters rather than exposing struct members. +- [Removed timer auto-ticking system][931] + - Added an example of how to tick timers manually. +- [When a task scope produces <= 1 task to run, run it on the calling thread immediately][932] +- [Breaking changes to Time API][934] + - Created getters to get `Time` state and made members private. + - Modifying `Time`'s values directly is no longer possible outside of bevy. +- [Use `mailbox` instead of `fifo` for vsync on supported systems][920] +- [switch winit size to logical to be dpi independent][947] +- [Change bevy_input::Touch API to match similar APIs][952] +- [Run parent-update and transform-propagation during the "post-startup" stage (instead of "startup")][955] +- [Renderer Optimization Round 1][958] +- [Change`TextureAtlasBuilder` into expected Builder conventions][969] +- [Optimize Text rendering / SharedBuffers][972] +- [hidpi swap chains][973] +- [optimize asset gpu data transfer][987] +- [naming coherence for cameras][995] +- [Schedule v2][1021] +- [Use shaderc for aarch64-apple-darwin][1027] +- [update `Window`'s `width` & `height` methods to return `f32`][1033] +- [Break out Visible component from Draw][1034] + - Users setting `Draw::is_visible` or `Draw::is_transparent` should now set `Visible::is_visible` and `Visible::is_transparent` +- [`winit` upgraded from version 0.23 to version 0.24][1043] +- [set is_transparent to true by default for UI bundles][1071] +### Fixed + +- [Fixed typos in KeyCode identifiers][857] +- [Remove redundant texture copies in TextureCopyNode][871] +- [Fix a deadlock that can occur when using scope() on ComputeTaskPool from within a system][892] +- [Don't draw text that isn't visible][893] +- [Use `instant::Instant` for WASM compatibility][895] +- [Fix pixel format conversion in bevy_gltf][897] +- [Fixed duplicated children when spawning a Scene][904] +- [Corrected behaviour of the UI depth system][905] +- [Allow despawning of hierarchies in threadlocal systems][908] +- [Fix `RenderResources` index slicing][948] +- [Run parent-update and transform-propagation during the "post-startup" stage][955] +- [Fix collision detection by comparing abs() penetration depth][966] +- [deal with rounding issue when creating the swap chain][997] +- [only update components for entities in map][1023] +- [Don't panic when attempting to set shader defs from an asset that hasn't loaded yet][1035] + +[273]: https://github.com/bevyengine/bevy/pull/273 +[691]: https://github.com/bevyengine/bevy/pull/691 +[719]: https://github.com/bevyengine/bevy/pull/719 +[761]: https://github.com/bevyengine/bevy/pull/761 +[761]: https://github.com/bevyengine/bevy/pull/761 +[765]: https://github.com/bevyengine/bevy/pull/765 +[772]: https://github.com/bevyengine/bevy/pull/772 +[772]: https://github.com/bevyengine/bevy/pull/772 +[789]: https://github.com/bevyengine/bevy/pull/789 +[791]: https://github.com/bevyengine/bevy/pull/791 +[798]: https://github.com/bevyengine/bevy/pull/798 +[801]: https://github.com/bevyengine/bevy/pull/801 +[801]: https://github.com/bevyengine/bevy/pull/801 +[805]: https://github.com/bevyengine/bevy/pull/805 +[808]: https://github.com/bevyengine/bevy/pull/808 +[815]: https://github.com/bevyengine/bevy/pull/815 +[820]: https://github.com/bevyengine/bevy/pull/820 +[821]: https://github.com/bevyengine/bevy/pull/821 +[821]: https://github.com/bevyengine/bevy/pull/821 +[829]: https://github.com/bevyengine/bevy/pull/829 +[829]: https://github.com/bevyengine/bevy/pull/829 +[834]: https://github.com/bevyengine/bevy/pull/834 +[834]: https://github.com/bevyengine/bevy/pull/834 +[836]: https://github.com/bevyengine/bevy/pull/836 +[836]: https://github.com/bevyengine/bevy/pull/836 +[842]: https://github.com/bevyengine/bevy/pull/842 +[843]: https://github.com/bevyengine/bevy/pull/843 +[847]: https://github.com/bevyengine/bevy/pull/847 +[852]: https://github.com/bevyengine/bevy/pull/852 +[852]: https://github.com/bevyengine/bevy/pull/852 +[857]: https://github.com/bevyengine/bevy/pull/857 +[857]: https://github.com/bevyengine/bevy/pull/857 +[859]: https://github.com/bevyengine/bevy/pull/859 +[859]: https://github.com/bevyengine/bevy/pull/859 +[863]: https://github.com/bevyengine/bevy/pull/863 +[864]: https://github.com/bevyengine/bevy/pull/864 +[871]: https://github.com/bevyengine/bevy/pull/871 +[876]: https://github.com/bevyengine/bevy/pull/876 +[876]: https://github.com/bevyengine/bevy/pull/876 +[883]: https://github.com/bevyengine/bevy/pull/883 +[887]: https://github.com/bevyengine/bevy/pull/887 +[892]: https://github.com/bevyengine/bevy/pull/892 +[893]: https://github.com/bevyengine/bevy/pull/893 +[893]: https://github.com/bevyengine/bevy/pull/893 +[893]: https://github.com/bevyengine/bevy/pull/893 +[894]: https://github.com/bevyengine/bevy/pull/894 +[894]: https://github.com/bevyengine/bevy/pull/894 +[894]: https://github.com/bevyengine/bevy/pull/894 +[895]: https://github.com/bevyengine/bevy/pull/895 +[895]: https://github.com/bevyengine/bevy/pull/895 +[897]: https://github.com/bevyengine/bevy/pull/897 +[903]: https://github.com/bevyengine/bevy/pull/903 +[904]: https://github.com/bevyengine/bevy/pull/904 +[904]: https://github.com/bevyengine/bevy/pull/904 +[905]: https://github.com/bevyengine/bevy/pull/905 +[905]: https://github.com/bevyengine/bevy/pull/905 +[908]: https://github.com/bevyengine/bevy/pull/908 +[914]: https://github.com/bevyengine/bevy/pull/914 +[914]: https://github.com/bevyengine/bevy/pull/914 +[917]: https://github.com/bevyengine/bevy/pull/917 +[917]: https://github.com/bevyengine/bevy/pull/917 +[920]: https://github.com/bevyengine/bevy/pull/920 +[920]: https://github.com/bevyengine/bevy/pull/920 +[926]: https://github.com/bevyengine/bevy/pull/926 +[926]: https://github.com/bevyengine/bevy/pull/926 +[928]: https://github.com/bevyengine/bevy/pull/928 +[928]: https://github.com/bevyengine/bevy/pull/928 +[931]: https://github.com/bevyengine/bevy/pull/931 +[931]: https://github.com/bevyengine/bevy/pull/931 +[932]: https://github.com/bevyengine/bevy/pull/932 +[934]: https://github.com/bevyengine/bevy/pull/934 +[934]: https://github.com/bevyengine/bevy/pull/934 +[937]: https://github.com/bevyengine/bevy/pull/937 +[940]: https://github.com/bevyengine/bevy/pull/940 +[945]: https://github.com/bevyengine/bevy/pull/945 +[945]: https://github.com/bevyengine/bevy/pull/945 +[946]: https://github.com/bevyengine/bevy/pull/946 +[947]: https://github.com/bevyengine/bevy/pull/947 +[948]: https://github.com/bevyengine/bevy/pull/948 +[952]: https://github.com/bevyengine/bevy/pull/952 +[955]: https://github.com/bevyengine/bevy/pull/955 +[955]: https://github.com/bevyengine/bevy/pull/955 +[955]: https://github.com/bevyengine/bevy/pull/955 +[956]: https://github.com/bevyengine/bevy/pull/956 +[958]: https://github.com/bevyengine/bevy/pull/958 +[966]: https://github.com/bevyengine/bevy/pull/966 +[969]: https://github.com/bevyengine/bevy/pull/969 +[972]: https://github.com/bevyengine/bevy/pull/972 +[973]: https://github.com/bevyengine/bevy/pull/973 +[979]: https://github.com/bevyengine/bevy/pull/979 +[987]: https://github.com/bevyengine/bevy/pull/987 +[995]: https://github.com/bevyengine/bevy/pull/995 +[997]: https://github.com/bevyengine/bevy/pull/997 +[1004]: https://github.com/bevyengine/bevy/pull/1004 +[1016]: https://github.com/bevyengine/bevy/pull/1016 +[1021]: https://github.com/bevyengine/bevy/pull/1021 +[1023]: https://github.com/bevyengine/bevy/pull/1023 +[1026]: https://github.com/bevyengine/bevy/pull/1026 +[1027]: https://github.com/bevyengine/bevy/pull/1027 +[1033]: https://github.com/bevyengine/bevy/pull/1033 +[1034]: https://github.com/bevyengine/bevy/pull/1034 +[1034]: https://github.com/bevyengine/bevy/pull/1034 +[1035]: https://github.com/bevyengine/bevy/pull/1035 +[1037]: https://github.com/bevyengine/bevy/pull/1037 +[1038]: https://github.com/bevyengine/bevy/pull/1038 +[1043]: https://github.com/bevyengine/bevy/pull/1043 +[1043]: https://github.com/bevyengine/bevy/pull/1043 +[1071]: https://github.com/bevyengine/bevy/pull/1071 + +## Version 0.3.0 (2020-11-03) + +### Added + +- [Touch Input][696] +- [iOS XCode Project][539] +- [Android Example and use bevy-glsl-to-spirv 0.2.0][740] +- [Introduce Mouse capture API][679] +- [`bevy_input::touch`: implement touch input][696] +- [D-pad support on MacOS][653] +- [Support for Android file system][723] +- [app: PluginGroups and DefaultPlugins][744] + - `PluginGroup` is a collection of plugins where each plugin can be enabled or disabled. +- [Support to get gamepad button/trigger values using `Axis`][683] +- [Expose Winit decorations][627] +- [Enable changing window settings at runtime][644] +- [Expose a pointer of EventLoopProxy to process custom messages][674] +- [Add a way to specify padding/ margins between sprites in a TextureAtlas][460] +- [Add `bevy_ecs::Commands::remove` for bundles][579] +- [impl `Default` for `TextureFormat`][675] +- [Expose current_entity in ChildBuilder][595] +- [`AppBuilder::add_thread_local_resource`][671] +- [`Commands::write_world_boxed` takes a pre-boxed world writer to the ECS's command queue][661] +- [`FrameTimeDiagnosticsPlugin` now shows "frame count" in addition to "frame time" and "fps"][678] +- [Add hierarchy example][565] +- [`WgpuPowerOptions` for choosing between low power, high performance, and adaptive power][397] +- Derive `Debug` for more types: [#597][597], [#632][632] +- Index buffer specialization + - [Allows the use of U32 indices in Mesh index buffers in addition to the usual U16 indices][568] + - [Switch to u32 indices by default][572] +- More instructions for system dependencies + - [Add `systemd-devel` for Fedora Linux dependencies][528] + - [Add `libudev-dev` to Ubuntu dependencies][538] + - [Add Void Linux to linux dependencies file][645] + - [WSL2 instructions][727] +- [Suggest `-Zrun-dsymutil-no` for faster compilation on MacOS][552] + +### Changed + +- [ecs: ergonomic query.iter(), remove locks, add QuerySets][741] + - `query.iter()` is now a real iterator! + - `QuerySet` allows working with conflicting queries and is checked at compile-time. +- [Rename `query.entity()` and `query.get()`][752] + - `query.get::(entity)` is now `query.get_component::(entity)` + - `query.entity(entity)` is now `query.get(entity)` +- [Asset system rework and GLTF scene loading][693] +- [Introduces WASM implementation of `AssetIo`][703] +- [Move transform data out of Mat4][596] +- [Separate gamepad state code from gamepad event code and other customizations][700] +- [gamepad: expose raw and filtered gamepad events][711] +- [Do not depend on `spirv-reflect` on `wasm32` target][689] +- [Move dynamic plugin loading to its own optional crate][544] +- [Add field to `WindowDescriptor` on wasm32 targets to optionally provide an existing canvas element as winit window][515] +- [Adjust how `ArchetypeAccess` tracks mutable & immutable deps][660] +- [Use `FnOnce` in `Commands` and `ChildBuilder` where possible][535] +- [Runners explicitly call `App.initialize()`][690] +- [sRGB awareness for `Color`][616] + - Color is now assumed to be provided in the non-linear sRGB colorspace. + Constructors such as `Color::rgb` and `Color::rgba` will be converted to linear sRGB. + - New methods `Color::rgb_linear` and `Color::rgba_linear` will accept colors already in linear sRGB (the old behavior) + - Individual color-components must now be accessed through setters and getters. +- [`Mesh` overhaul with custom vertex attributes][599] + - Any vertex attribute can now be added over `mesh.attributes.insert()`. + - See `example/shader/mesh_custom_attribute.rs` + - Removed `VertexAttribute`, `Vertex`, `AsVertexBufferDescriptor`. + - For missing attributes (requested by shader, but not defined by mesh), Bevy will provide a zero-filled fallback buffer. +- Despawning an entity multiple times causes a debug-level log message to be emitted instead of a panic: [#649][649], [#651][651] +- [Migrated to Rodio 0.12][692] + - New method of playing audio can be found in the examples. +- Added support for inserting custom initial values for `Local` system resources [#745][745] + +### Fixed + +- [Properly update bind group ids when setting dynamic bindings][560] +- [Properly exit the app on AppExit event][610] +- [Fix FloatOrd hash being different for different NaN values][618] +- [Fix Added behavior for QueryOne get][543] +- [Update camera_system to fix issue with late camera addition][488] +- [Register `IndexFormat` as a property][664] +- [Fix breakout example bug][685] +- [Fix PreviousParent lag by merging parent update systems][713] +- [Fix bug of connection event of gamepad at startup][730] +- [Fix wavy text][725] + +[397]: https://github.com/bevyengine/bevy/pull/397 +[460]: https://github.com/bevyengine/bevy/pull/460 +[488]: https://github.com/bevyengine/bevy/pull/488 +[515]: https://github.com/bevyengine/bevy/pull/515 +[528]: https://github.com/bevyengine/bevy/pull/528 +[535]: https://github.com/bevyengine/bevy/pull/535 +[538]: https://github.com/bevyengine/bevy/pull/538 +[539]: https://github.com/bevyengine/bevy/pull/539 +[543]: https://github.com/bevyengine/bevy/pull/543 +[544]: https://github.com/bevyengine/bevy/pull/544 [552]: https://github.com/bevyengine/bevy/pull/552 +[560]: https://github.com/bevyengine/bevy/pull/560 +[565]: https://github.com/bevyengine/bevy/pull/565 +[568]: https://github.com/bevyengine/bevy/pull/568 +[572]: https://github.com/bevyengine/bevy/pull/572 +[579]: https://github.com/bevyengine/bevy/pull/579 +[595]: https://github.com/bevyengine/bevy/pull/595 +[596]: https://github.com/bevyengine/bevy/pull/596 +[597]: https://github.com/bevyengine/bevy/pull/597 +[599]: https://github.com/bevyengine/bevy/pull/599 +[610]: https://github.com/bevyengine/bevy/pull/610 +[616]: https://github.com/bevyengine/bevy/pull/616 +[618]: https://github.com/bevyengine/bevy/pull/618 +[627]: https://github.com/bevyengine/bevy/pull/627 +[632]: https://github.com/bevyengine/bevy/pull/632 +[644]: https://github.com/bevyengine/bevy/pull/644 +[645]: https://github.com/bevyengine/bevy/pull/645 +[649]: https://github.com/bevyengine/bevy/pull/649 +[651]: https://github.com/bevyengine/bevy/pull/651 +[653]: https://github.com/bevyengine/bevy/pull/653 +[660]: https://github.com/bevyengine/bevy/pull/660 +[661]: https://github.com/bevyengine/bevy/pull/661 +[664]: https://github.com/bevyengine/bevy/pull/664 +[671]: https://github.com/bevyengine/bevy/pull/671 +[674]: https://github.com/bevyengine/bevy/pull/674 +[675]: https://github.com/bevyengine/bevy/pull/675 +[678]: https://github.com/bevyengine/bevy/pull/678 +[679]: https://github.com/bevyengine/bevy/pull/679 +[683]: https://github.com/bevyengine/bevy/pull/683 +[685]: https://github.com/bevyengine/bevy/pull/685 +[689]: https://github.com/bevyengine/bevy/pull/689 +[690]: https://github.com/bevyengine/bevy/pull/690 +[692]: https://github.com/bevyengine/bevy/pull/692 +[693]: https://github.com/bevyengine/bevy/pull/693 +[696]: https://github.com/bevyengine/bevy/pull/696 +[700]: https://github.com/bevyengine/bevy/pull/700 +[703]: https://github.com/bevyengine/bevy/pull/703 +[711]: https://github.com/bevyengine/bevy/pull/711 +[713]: https://github.com/bevyengine/bevy/pull/713 +[723]: https://github.com/bevyengine/bevy/pull/723 +[725]: https://github.com/bevyengine/bevy/pull/725 +[727]: https://github.com/bevyengine/bevy/pull/727 +[730]: https://github.com/bevyengine/bevy/pull/730 +[740]: https://github.com/bevyengine/bevy/pull/740 +[741]: https://github.com/bevyengine/bevy/pull/741 +[744]: https://github.com/bevyengine/bevy/pull/744 +[745]: https://github.com/bevyengine/bevy/pull/745 +[752]: https://github.com/bevyengine/bevy/pull/752 ## Version 0.2.1 (2020-9-20) @@ -15,8 +2059,8 @@ - [Remove UI queue print][521] - [Use async executor 1.3.0][526] -[521]: [https://github.com/bevyengine/bevy/pull/521] -[526]: [https://github.com/bevyengine/bevy/pull/526] +[521]: https://github.com/bevyengine/bevy/pull/521 +[526]: https://github.com/bevyengine/bevy/pull/526 ## Version 0.2.0 (2020-9-19) @@ -29,7 +2073,7 @@ - e.g. `query.iter().par_iter(batch_size).for_each(/* ... */)` - [Added gamepad support using Gilrs][280] - [Implement WASM support for bevy_winit][503] -- [Create winit canvas under WebAssembly][506] +- [Create winit canvas under WebAssembly][506] - [Implement single threaded task scheduler for WebAssembly][496] - [Support for binary glTF (.glb).][271] - [Support for `Or` in ECS queries.][358] @@ -58,7 +2102,7 @@ - [Add `AppBuilder::add_startup_stage_|before/after`][505] ### Changed - + - [Transform rewrite][374] - [Use generational entity ids and other optimizations][504] - [Optimize transform systems to only run on changes.][417] @@ -88,6 +2132,7 @@ - [do not assume font handle is present in assets][490] ### Internal Improvements + - Many improvements to Bevy's CI [#325][325], [#349][349], [#357][357], [#373][373], [#423][423] [145]: https://github.com/bevyengine/bevy/pull/145 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000000..efe4b499e4bef --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,321 @@ +# Contributing to Bevy + +Hey, so you're interested in contributing to Bevy! +Feel free to pitch in on whatever interests you and we'll be happy to help you contribute. + +Check out our community's [Code of Conduct](https://github.com/bevyengine/bevy/blob/main/CODE_OF_CONDUCT.md) and feel free to say hi on [Discord] if you'd like. +It's a nice place to chat about Bevy development, ask questions, and get to know the other contributors and users in a less formal setting. + +Read on if you're looking for: + +* the high-level design goals of Bevy +* conventions and informal practices we follow when developing Bevy +* general advice on good open source collaboration practices +* concrete ways you can help us, no matter your background or skill level + +We're thrilled to have you along as we build! + +## Getting oriented + +Bevy, like any general-purpose game engine, is a large project! +It can be a bit overwhelming to start, so here's the bird's-eye view. + +The [Bevy Engine Organization](https://github.com/bevyengine) has 4 primary repos: + +1. [**`bevy`**](https://github.com/bevyengine/bevy): This is where the engine itself lives. The bulk of development work occurs here. +2. [**`bevy-website`**](https://github.com/bevyengine/bevy-website): Where the [official website](https://bevyengine.org/), release notes, Bevy Book, and Bevy Assets are hosted. It is created using the Zola static site generator. +3. [**`bevy-assets`**](https://github.com/bevyengine/bevy-assets): A collection of community-made tutorials, plugins, crates, games, and tools! Make a PR if you want to showcase your projects there! +4. [**`rfcs`**](https://github.com/bevyengine/rfcs): A place to collaboratively build and reach consensus on designs for large or controversial features. + +The `bevy` repo itself contains many smaller subcrates. Most of them can be used by themselves and many of them can be modularly replaced. This enables developers to pick and choose the parts of Bevy that they want to use. + +Some crates of interest: + +* [**`bevy_ecs`**](./crates/bevy_ecs): The core data model for Bevy. Most Bevy features are implemented on top of it. It is also fully functional as a stand-alone ECS, which can be very valuable if you're looking to integrate it with other game engines or use it for non-game executables. +* [**`bevy_app`**](./crates/bevy_app): The api used to define Bevy Plugins and compose them together into Bevy Apps. +* [**`bevy_tasks`**](./crates/bevy_tasks): Our light-weight async executor. This drives most async and parallel code in Bevy. +* [**`bevy_render`**](./crates/bevy_render): Our core renderer API. It handles interaction with the GPU, such as the creation of Meshes, Textures, and Shaders. It also exposes a modular Render Graph for composing render pipelines. All 2D and 3D render features are implemented on top of this crate. + +## What we're trying to build + +Bevy is a completely free and open source game engine built in Rust. It currently has the following design goals: + +* **Capable**: Offer a complete 2D and 3D feature set +* **Simple**: Easy for newbies to pick up, but infinitely flexible for power users +* **Data Focused**: Data-oriented architecture using the Entity Component System paradigm +* **Modular**: Use only what you need. Replace what you don't like +* **Fast**: App logic should run quickly, and when possible, in parallel +* **Productive**: Changes should compile quickly ... waiting isn't fun + +Bevy also currently has the following "development process" goals: + +* **Rapid experimentation over API stability**: We need the freedom to experiment and iterate in order to build the best engine we can. This will change over time as APIs prove their staying power. +* **Consistent vision**: The engine needs to feel consistent and cohesive. This takes precedent over democratic and/or decentralized processes. See [*How we're organized*](#how-were-organized) for more details. +* **Flexibility over bureaucracy**: Developers should feel productive and unencumbered by development processes. +* **Focus**: The Bevy Org should focus on building a small number of features excellently over merging every new community-contributed feature quickly. Sometimes this means pull requests will sit unmerged for a long time. This is the price of focus and we are willing to pay it. Fortunately Bevy is modular to its core. 3rd party plugins are a great way to work around this policy. +* **User-facing API ergonomics come first**: Solid user experience should receive significant focus and investment. It should rarely be compromised in the interest of internal implementation details. +* **Modularity over deep integration**: Individual crates and features should be "pluggable" whenever possible. Don't tie crates, features, or types together that don't need to be. +* **Don't merge everything ... don't merge too early**: Every feature we add increases maintenance burden and compile times. Only merge features that are "generally" useful. Don't merge major changes or new features unless we have relative consensus that the design is correct *and* that we have the developer capacity to support it. When possible, make a 3rd party Plugin / crate first, then consider merging once the API has been tested in the wild. Bevy's modular structure means that the only difference between "official engine features" and "third party plugins" is our endorsement and the repo the code lives in. We should take advantage of that whenever possible. +* **Control and consistency over 3rd party code reuse**: Only add a dependency if it is *absolutely* necessary. Every dependency we add decreases our autonomy and consistency. Dependencies also have the potential to increase compile times and risk pulling in sub-dependencies we don't want / need. +* **Don't re-invent every wheel**: As a counter to the previous point, don't re-invent everything at all costs. If there is a crate in the Rust ecosystem that is the "de-facto" standard (ex: wgpu, winit, cpal), we should heavily consider using it. Bevy should be a positive force in the ecosystem. We should drive the improvements we need into these core ecosystem crates. +* **Rust-first**: Engine and user-facing code should optimize and encourage Rust-only workflows. Adding additional languages increases internal complexity, fractures the Bevy ecosystem, and makes it harder for users to understand the engine. Never compromise a Rust interface in the interest of compatibility with other languages. +* **Thoughtful public interfaces over maximal configurability**: Symbols and apis should be private by default. Every public API should be thoughtfully and consistently designed. Don't expose unnecessary internal implementation details. Don't allow users to "shoot themselves in the foot". Favor one "happy path" api over multiple apis for different use cases. +* **Welcome new contributors**: Invest in new contributors. Help them fill knowledge and skill gaps. Don't ever gatekeep Bevy development according to notions of required skills or credentials. Help new developers find their niche. +* **Civil discourse**: We need to collectively discuss ideas and the best ideas *should* win. But conversations need to remain respectful at all times. Remember that we're all in this together. Always follow our [Code of Conduct](https://github.com/bevyengine/bevy/blob/main/CODE_OF_CONDUCT.md). +* **Test what you need to**: Write useful tests. Don't write tests that aren't useful. We *generally* aren't strict about unit testing every line of code. We don't want you to waste your time. But at the same time: + * Most new features should have at least one minimal [example](https://github.com/bevyengine/bevy/tree/main/examples). These also serve as simple integration tests, as they are run as part of our CI process. + * The more complex or "core" a feature is, the more strict we are about unit tests. Use your best judgement here. We will let you know if your pull request needs more tests. We use [Rust's built in testing framework](https://doc.rust-lang.org/book/ch11-01-writing-tests.html). + +## How we're organized + +@cart is, for now, our singular Benevolent Dictator and project lead. +He makes the final decision on both design and code changes within Bevy in order to ensure a coherent vision and consistent quality of code. + +In practice, @cart serves as a shockingly accountable dictator: open to new ideas and to changing his mind in the face of compelling arguments or community consensus. +Check out the next section for details on how this plays out. + +[Bevy Org members](https://github.com/orgs/bevyengine/people) are contributors who: + +1. Have actively engaged with Bevy development. +2. Have demonstrated themselves to be polite and welcoming representatives of the project with an understanding of our goals and direction. +3. Have asked to join the Bevy Org. Reach out to @cart on [Discord] or email us at bevyengine@gmail.com if you are interested. Everyone is welcome to do this. We generally accept membership requests, so don't hesitate if you are interested! + +All Bevy Org members are also Triage Team members. These people can label and close issues and PRs but do not have merge rights or any special authority within the community. + +Merge rights within the org are relatively centralized: this requires a large amount of trust when it comes to ethics, technical ability, and ability to enforce consistent project direction. + +The current structure is as follows: + +* @cart is our project lead, and has final say on controversial decisions +* There is a small group of other maintainers (@alice-i-cecile, @mockersf and @superdump), who have merge rights but abide by the following rules: + * Trivial PRs can be merged without approvals. + * Relatively uncontroversial PRs can be merged following approval from at least two other community members with appropriate expertise. + * Controversial PRs are added to a backlog for @cart to address once two maintainers agree that they are ready. + * If 45 days elapse without action on a controversial PR (approval, feedback or an explicit request to defer), they can be merged without project lead approval. +* The Bevy org is made up of trusted community contributors: this is a relatively low bar, and org members help triage and maintain the project. +* Community contributors (this means you!) can freely open issues, submit PRs and review PRs to improve Bevy. + * As discussed above, community reviews on PRs are incredibly helpful to enable maintainers to merge in uncontroversial PRs in a timely fashion. + +### Classifying PRs + +This strategy relies on a classification of PRs into three categories: **trivial**, **uncontroversial** and **controversial**. +When making PRs, try to split out more controversial changes from less controversial ones, in order to make your work easier to review and merge. +PRs that are deemed controversial will receive the `S-Controversial` label, and will have to go through the more thorough review process. + +PRs are trivial if there is no reasonable argument against them. This might include: + +* Fixing dead links. +* Removing dead code or dependencies. +* Typo and grammar fixes. + +PRs are controversial if there is serious design discussion required, or a large impact to contributors or users. Factors that increase controversy include: + +1. Changes to project-wide workflow or style. +2. New architecture for a large feature. +3. PRs where a serious tradeoff must be made. +4. Heavy user impact. +5. New ways for users to make mistakes (footguns). +6. Introductions of `unsafe` code. +7. Large-scale code reorganization. +8. High levels of technical complexity. +9. Adding a dependency. +10. Touching licensing information (due to the level of precision required). +11. Adding root-level files (due to the high level of visibility). + +Finally, changes are "relatively uncontroversial" if they are neither trivial or controversial. +Most PRs should fall into this category. + +## How we work together + +Making a game engine is a huge project and facilitating collaboration is a lot of work. +At the moment @cart is our only paid contributor, so [go sponsor him!](https://github.com/sponsors/cart) +We track issues and pull requests that must be included in releases using [Milestones](https://github.com/bevyengine/bevy/milestones). + +### Making changes to Bevy + +Most changes don't require much "process". If your change is relatively straightforward, just do the following: + +1. A community member (that's you!) creates one of the following: + * [GitHub Discussions]: An informal discussion with the community. This is the place to start if you want to propose a feature or specific implementation. + * [Issue](https://github.com/bevyengine/bevy/issues): A formal way for us to track a bug or feature. Please look for duplicates before opening a new issue and consider starting with a Discussion. + * [Pull Request](https://github.com/bevyengine/bevy/pulls) (or PR for short): A request to merge code changes. This starts our "review process". You are welcome to start with a pull request, but consider starting with an Issue or Discussion for larger changes (or if you aren't certain about a design). We don't want anyone to waste their time on code that didn't have a chance to be merged! But conversely, sometimes PRs are the most efficient way to propose a change. Just use your own judgement here. +2. Other community members review and comment in an ad-hoc fashion. Active subject matter experts may be pulled into a thread using `@mentions`. If your PR has been quiet for a while and is ready for review, feel free to leave a message to "bump" the thread, or bring it up on [Discord] in an appropriate engine development channel. +3. Once they're content with the pull request (design, code quality, documentation, tests), individual reviewers leave "Approved" reviews. +4. After consensus has been reached (typically two approvals from the community or one for extremely simple changes) and CI passes, the [S-Ready-For-Final-Review](https://github.com/bevyengine/bevy/issues?q=is%3Aopen+is%3Aissue+label%3AS-Ready-For-Final-Review) label is added. +5. When they find time, [someone with merge rights](#how-were-organized) performs a final code review and merges the PR using [Bors](https://bors.tech/) by typing `bors r+`. + +### Complex changes + +Individual contributors often lead major new features and reworks. However these changes require more design work and scrutiny. Complex changes like this tend to go through the following lifecycle: + +1. A need or opportunity is identified and an issue is made, laying out the general problem. +2. As needed, this is discussed further on that issue thread, in cross-linked [GitHub Discussion] threads, or on [Discord] in the Engine Development channels. +3. Either a Draft Pull Request or an RFC is made. As discussed in the [RFC repo](https://github.com/bevyengine/rfcs), complex features need RFCs, but these can be submitted before or after prototyping work has been started. +4. The community as a whole helps improve the Draft PR and/or RFC, leaving comments, making suggestions, and submitting pull requests to the original branch. +5. Once the RFC is merged and/or the Draft Pull Request is transitioned out of draft mode, the [normal change process outlined in the previous section](#making-changes-to-bevy) can begin. + +## How you can help + +If you've made it to this page, you're probably already convinced that Bevy is a project you'd like to see thrive. +But how can *you* help? + +No matter your experience level with Bevy or Rust or your level of commitment, there are ways to meaningfully contribute. +Take a look at the sections that follow to pick a route (or five) that appeal to you. + +If you ever find yourself at a loss for what to do, or in need of mentorship or advice on how to contribute to Bevy, feel free to ask in [Discord] and one of our more experienced community members will be happy to help. + +### Battle-testing Bevy + +Ultimately, Bevy is a tool that's designed to help people make cool games. +By using Bevy, you can help us catch bugs, prioritize new features, polish off the rough edges, and promote the project. + +If you need help, don't hesitate to ask for help on [GitHub Discussions], [Discord], or [reddit](https://www.reddit.com/r/bevy). Generally you should prefer asking questions as [GitHub Discussions] as they are more searchable. + +When you think you've found a bug, missing documentation, or a feature that would help you make better games, please [file an issue](https://github.com/bevyengine/bevy/issues/new/choose) on the main `bevy` repo. + +Do your best to search for duplicate issues, but if you're unsure, open a new issue and link to other related issues on the thread you make. + +Once you've made something that you're proud of, feel free to drop a link, video, or screenshot in `#showcase` on [Discord]! +If you release a game on [itch.io](https://itch.io/games/tag-bevy) we'd be thrilled if you tagged it with `bevy`. + +### Teaching others + +Bevy is still very young, and light on documentation, tutorials and accumulated expertise. +By helping others with their issues, and teaching them about Bevy, you will naturally learn the engine and codebase in greater depth while also making our community better! + +Some of the best ways to do this are: + +* Answering questions on [GitHub Discussions], [Discord], and [reddit](https://www.reddit.com/r/bevy). +* Writing tutorials, guides, and other informal documentation and sharing them on [Bevy Assets](https://github.com/bevyengine/bevy-assets). +* Streaming, writing blog posts about creating your game, and creating videos. Share these in the `#devlogs` channel on [Discord]! + +### Writing plugins + +You can improve Bevy's ecosystem by building your own Bevy Plugins and crates. + +Non-trivial, reusable functionality that works well with itself is a good candidate for a plugin. +If it's closer to a snippet or design pattern, you may want to share it with the community on [Discord], Reddit, or [GitHub Discussions] instead. + +Check out our [plugin guidelines](https://github.com/bevyengine/bevy/blob/main/docs/plugins_guidelines.md) for helpful tips and patterns. + +### Fixing bugs + +Bugs in Bevy (or the associated website / book) are filed on the issue tracker using the [`C-Bug`](https://github.com/bevyengine/bevy/issues?q=is%3Aissue+is%3Aopen+label%3AC-Bug) label. + +If you're looking for an easy place to start, take a look at the [`D-Good-First-Issue`](https://github.com/bevyengine/bevy/issues?q=is%3Aopen+is%3Aissue+label%3AD-Good-First-Issue) label, and feel free to ask questions on that issue's thread in question or on [Discord]. +You don't need anyone's permission to try fixing a bug or adding a simple feature, but stating that you'd like to tackle an issue can be helpful to avoid duplicated work. + +When you make a pull request that fixes an issue, include a line that says `Fixes #X` (or "Closes"), where `X` is the issue number. +This will cause the issue in question to be closed when your PR is merged. + +General improvements to code quality are also welcome! +Bevy can always be safer, better tested, and more idiomatic. + +### Writing docs + +Like every other large, rapidly developing open source library you've ever used, Bevy's documentation can always use improvement. +This is incredibly valuable, easily distributed work, but requires a bit of guidance: + +* Inaccurate documentation is worse than no documentation: prioritize fixing broken docs. +* Bevy is remarkably unstable: before tackling a new major documentation project, check in with the community on Discord or GitHub (making an issue about specific missing docs is a great way to plan) about the stability of that feature and upcoming plans to save yourself heartache. +* Code documentation (doc examples and in the examples folder) is easier to maintain because the compiler will tell us when it breaks. +* Inline documentation should be technical and to the point. Link relevant examples or other explanations if broader context is useful. +* The Bevy book is hosted on the `bevy-website` repo and targeted towards beginners who are just getting to know Bevy (and perhaps Rust!). +* Accepted RFCs are not documentation: they serve only as a record of accepted decisions. + +[docs.rs](https://docs.rs/bevy) is built from out of the last release's documentation, which is written right in-line directly above the code it documents. +To view the current docs on `main` before you contribute, clone the `bevy` repo, and run `cargo doc --open`. + +### Writing examples + +Mostย [examplesย inย Bevy](https://github.com/bevyengine/bevy/tree/main/examples)ย aimย toย clearlyย demonstrateย aย singleย feature,ย groupย ofย closelyย relatedย smallย features, orย showย howย toย accomplishย aย particularย taskย (suchย asย assetย loading,ย creatingย aย customย shaderย orย testing yourย app). +Inย rareย cases, creatingย newย "game"ย examplesย isย justifiedย in order toย demonstrateย newย features +thatย openย aย complexย classย ofย functionalityย inย aย wayย that'sย hardย toย demonstrateย inย isolationย orย requiresย additionalย integrationย testing. + +Examplesย inย Bevyย shouldย be: + +1. **Working:**ย Theyย mustย compileย andย run,ย andย any introduced errors in them shouldย beย obviousย (through tests, simple results or clearly displayed behavior). +2. **Clear:**ย Theyย mustย useย descriptiveย variableย names,ย beย formatted,ย andย beย appropriatelyย commented. Try your best to showcase best practices when it doesn't obscure the point of the example. +3. **Relevant:**ย Theyย shouldย explain, through comments or variable names, what they do and how this can be useful to a game developer. +4. **Minimal:** Theyย shouldย beย noย largerย orย complexย thanย isย neededย toย meet the goals of the example. + +When you add a new example, be sure to update `examples/README.md` with the new example and add it to the root `Cargo.toml` file. +Use a generous sprinkling of keywords in your description: these are commonly used to search for a specific example. +See the [example style guide](.github/contributing/example_style_guide.md) to help make sure the style of your example matches what we're already using. + +Moreย complexย demonstrationsย ofย functionalityย areย alsoย welcome, but these should be submitted to [bevy-assets](https://github.com/bevyengine/bevy-assets). + +### Reviewing others' work + +With the sheer volume of activity in Bevy's community, reviewing others work with the aim of improving it is one of the most valuable things you can do. +You don't need to be an Elder Rustacean to be useful here: anyone can catch missing tests, unclear docs, logic errors, and so on. +If you have specific skills (e.g. advanced familiarity with `unsafe` code, rendering knowledge or web development experience) or personal experience with a problem, try to prioritize those areas to ensure we can get appropriate expertise where we need it. + +Focus on giving constructive, actionable feedback that results in real improvements to code quality or end-user experience. +If you don't understand why an approach was taken, please ask! + +Provide actual code suggestions when that is helpful. Small changes work well as comments or in-line suggestions on specific lines of codes. +Larger changes deserve a comment in the main thread, or a pull request to the original author's branch (but please mention that you've made one). +When in doubt about a matter of architectural philosophy, refer back to [*What we're trying to build*](#what-were-trying-to-build) for guidance. + +Once you're happy with the work and feel you're reasonably qualified to assess quality in this particular area, leave your `Approved` review on the PR. +If you're new to GitHub, check out the [Pull Request Review documentation](https://docs.github.com/en/github/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/about-pull-request-reviews). Anyone can leave reviews ... no special permissions are required! + +There are a two main places you can check for things to review: + +1. Pull requests on [bevy](https://github.com/bevyengine/bevy/pulls) and the [bevy-website](https://github.com/bevyengine/bevy-website/pulls) repos. +2. [RFCs](https://github.com/bevyengine/rfcs), which need extensive thoughtful community input on their design. + +Official focus areas and work done by @cart go through this review process as well. +Not even our project lead is exempt from reviews and RFCs! +By giving feedback on this work (and related supporting work), you can help us make sure our releases are both high-quality and timely. + +Finally, if nothing brings you more satisfaction than seeing every last issue labeled and all resolved issues closed, feel free to message @cart for a Bevy org role to help us keep things tidy. +As discussed in [*How we're organized*](#how-were-organized), this role only requires good faith and a basic understanding of our development process. + +### Contributing code + +Bevy is actively open to code contributions from community members. +If you're new to Bevy, here's the workflow we use: + +1. Fork the `bevyengine/bevy` repository on GitHub. You'll need to create a GitHub account if you don't have one already. +2. Make your changes in a local clone of your fork, typically in its own new branch. + 1. Try to split your work into separate commits, each with a distinct purpose. Be particularly mindful of this when responding to reviews so it's easy to see what's changed. +3. To test CI validations locally, run the `cargo run -p ci` command. This will run most checks that happen in CI, but can take some time. You can also run sub-commands to iterate faster depending on what you're contributing: + * `cargo run -p ci -- lints` - to run formatting and clippy + * `cargo run -p ci -- test` - to run tests + * `cargo run -p ci -- doc` - to run doc tests and doc checks + * `cargo run -p ci -- compile` - to check that everything that must compile still does (examples and benches), and that some that shouldn't still don't ([`crates/bevy_ecs_compile_fail_tests`](./crates/bevy_ecs_compile_fail_tests)) + * to get more informations on commands available and what is run, check the [tools/ci crate](./tools/ci) + +4. When working with Markdown (`.md`) files, Bevy's CI will check markdown files (like this one) using [markdownlint](https://github.com/DavidAnson/markdownlint). +To locally lint your files using the same workflow as our CI: + 1. Install [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli). + 2. Run `markdownlint -f -c .github/linters/.markdown-lint.yml .` in the root directory of the Bevy project. +5. Push your changes to your fork on Github and open a Pull Request. +6. If your account is new on github, one of the Bevy org members [will need to manually trigger CI for your PR](https://github.blog/changelog/2021-04-22-github-actions-maintainers-must-approve-first-time-contributor-workflow-runs/) using the `bors try` command. +7. Respond to any CI failures or review feedback. While CI failures must be fixed before we can merge your PR, you do not need to *agree* with all feedback from your reviews, merely acknowledge that it was given. If you cannot come to an agreement, leave the thread open and defer to @cart's final judgement. +8. When your PR is ready to merge, @cart will review it and suggest final changes. If those changes are minimal he may even apply them directly to speed up merging. + +If you end up adding a new official Bevy crate to the `bevy` repo: + +1. Add the new crate to the [./tools/publish.sh](./tools/publish.sh) file. +2. Check if a new cargo feature was added, update [cargo_features.md](https://github.com/bevyengine/bevy/blob/main/docs/cargo_features.md) as needed. + +When contributing, please: + +* Try to loosely follow the workflow in [*How we work together*](#how-we-work-together). +* Consult the [style guide](.github/contributing/engine_style_guide.md) to help keep our code base tidy. +* Explain what you're doing and why. +* Document new code with doc comments. +* Include clear, simple tests. +* Add or improve the examples when adding new user-facing functionality. +* Break work into digestible chunks. +* Ask for any help that you need! + +Your first PR will be merged in no time! + +No matter how you're helping: thanks for contributing to Bevy! + +[GitHub Discussions]: https://github.com/bevyengine/bevy/discussions "GitHub Discussions" +[Discord]: https://discord.gg/bevy "Discord" diff --git a/CREDITS.md b/CREDITS.md index da4bccd579993..5571f75c20915 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -5,14 +5,23 @@ * hecs * legion_transform * wgpu-rs examples -* yaks: ArchetypeSet, borrowed some ideas from their scheduler implementation +* yaks: ArchetypeSet, borrowed some ideas from their scheduler implementation ## Inspiration * game engines: amethyst, coffee -* ecs: legion, shipyard, yaks +* ecs: legion, shipyard, yaks ## Assets * Generic RPG Pack (CC0 license) by [Bakudas](https://twitter.com/bakudas) and [Gabe Fern](https://twitter.com/_Gabrielfer) -* Environment maps (`.hdr` files) from [HDRIHaven](https://hdrihaven.com) (CC0 license) \ No newline at end of file +* Environment maps (`.hdr` files) from [HDRIHaven](https://hdrihaven.com) (CC0 license) +* Alien from [Kenney's Space Kit](https://www.kenney.nl/assets/space-kit) (CC0 1.0 Universal) +* Cake from [Kenney's Food Kit](https://www.kenney.nl/assets/food-kit) (CC0 1.0 Universal) +* Ground tile from [Kenney's Tower Defense Kit](https://www.kenney.nl/assets/tower-defense-kit) (CC0 1.0 Universal) +* Game icons from [Kenney's Game Icons](https://www.kenney.nl/assets/game-icons) (CC0 1.0 Universal) +* Space ships from [Kenny's Simple Space Kit](https://www.kenney.nl/assets/simple-space) (CC0 1.0 Universal) +* glTF animated fox from [glTF Sample Models](https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Fox) + * Low poly fox [by PixelMannen](https://opengameart.org/content/fox-and-shiba) (CC0 1.0 Universal) + * Rigging and animation [by @tomkranis on Sketchfab](https://sketchfab.com/models/371dea88d7e04a76af5763f2a36866bc) ([CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/)) +* FiraMono by The Mozilla Foundation and Telefonica S.A (SIL Open Font License, Version 1.1: assets/fonts/FiraMono-LICENSE) diff --git a/Cargo.toml b/Cargo.toml index 89acfcf0864c8..b0920d9ecd0fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,289 +1,1557 @@ [package] name = "bevy" -version = "0.2.1" -edition = "2018" -authors = [ - "Bevy Contributors ", - "Carter Anderson ", -] +version = "0.9.0-dev" +edition = "2021" +categories = ["game-engines", "graphics", "gui", "rendering"] description = "A refreshingly simple data-driven game engine and app framework" +exclude = ["assets/", "tools/", ".github/", "crates/", "examples/wasm/assets/"] homepage = "https://bevyengine.org" -repository = "https://github.com/bevyengine/bevy" -license = "MIT" keywords = ["game", "engine", "gamedev", "graphics", "bevy"] -categories = ["game-engines", "graphics", "gui", "rendering"] +license = "MIT OR Apache-2.0" readme = "README.md" -exclude = ["assets/**/*", "tools/**/*", ".github/**/*", "crates/**/*"] +repository = "https://github.com/bevyengine/bevy" + +[workspace] +exclude = ["benches", "crates/bevy_ecs_compile_fail_tests"] +members = [ + "crates/*", + "examples/ios", + "tools/ci", + "tools/spancmp", + "tools/build-example-pages", + "tools/build-wasm-example", + "errors", +] [features] default = [ - "bevy_audio", - "bevy_gilrs", - "bevy_gltf", - "bevy_wgpu", - "bevy_winit", - "render", - "dynamic_plugins", - "png", - "hdr", - "mp3", - "x11", -] -profiler = ["bevy_ecs/profiler", "bevy_diagnostic/profiler"] -wgpu_trace = ["bevy_wgpu/trace"] -dynamic_plugins = [ - "bevy_core/dynamic_plugins", - "bevy_app/dynamic_plugins", - "bevy_type_registry/dynamic_plugins", + "animation", + "bevy_asset", + "bevy_audio", + "bevy_gilrs", + "bevy_scene", + "bevy_winit", + "render", + "png", + "hdr", + "vorbis", + "x11", + "filesystem_watcher", ] + +# Force dynamic linking, which improves iterative compile times +dynamic = ["bevy_dylib"] + # Rendering support -render = ["bevy_pbr", "bevy_render", "bevy_sprite", "bevy_text", "bevy_ui"] +render = [ + "bevy_internal/bevy_core_pipeline", + "bevy_internal/bevy_pbr", + "bevy_internal/bevy_gltf", + "bevy_internal/bevy_render", + "bevy_internal/bevy_sprite", + "bevy_internal/bevy_text", + "bevy_internal/bevy_ui", +] + +# Optional bevy crates +bevy_animation = ["bevy_internal/bevy_animation"] +bevy_asset = ["bevy_internal/bevy_asset"] +bevy_audio = ["bevy_internal/bevy_audio"] +bevy_core_pipeline = ["bevy_internal/bevy_core_pipeline"] +bevy_dynamic_plugin = ["bevy_internal/bevy_dynamic_plugin"] +bevy_gilrs = ["bevy_internal/bevy_gilrs"] +bevy_gltf = ["bevy_internal/bevy_gltf"] +bevy_pbr = ["bevy_internal/bevy_pbr"] +bevy_render = ["bevy_internal/bevy_render"] +bevy_scene = ["bevy_internal/bevy_scene"] +bevy_sprite = ["bevy_internal/bevy_sprite"] +bevy_text = ["bevy_internal/bevy_text"] +bevy_ui = ["bevy_internal/bevy_ui"] +bevy_winit = ["bevy_internal/bevy_winit"] + +# Tracing features +trace_chrome = ["trace", "bevy_internal/trace_chrome"] +trace_tracy = ["trace", "bevy_internal/trace_tracy"] +trace = ["bevy_internal/trace"] +wgpu_trace = ["bevy_internal/wgpu_trace"] + # Image format support for texture loading (PNG and HDR are enabled by default) -png = ["bevy_render/png"] -hdr = ["bevy_render/hdr"] +hdr = ["bevy_internal/hdr"] +png = ["bevy_internal/png"] +tga = ["bevy_internal/tga"] +jpeg = ["bevy_internal/jpeg"] +bmp = ["bevy_internal/bmp"] +basis-universal = ["bevy_internal/basis-universal"] +dds = ["bevy_internal/dds"] +ktx2 = ["bevy_internal/ktx2"] +# For ktx2 supercompression +zlib = ["bevy_internal/zlib"] +zstd = ["bevy_internal/zstd"] + +# Audio format support (vorbis is enabled by default) +flac = ["bevy_internal/flac"] +mp3 = ["bevy_internal/mp3"] +vorbis = ["bevy_internal/vorbis"] +wav = ["bevy_internal/wav"] -# Audio format support (MP3 is enabled by default) -mp3 = ["bevy_audio/mp3"] -flac = ["bevy_audio/flac"] -wav = ["bevy_audio/wav"] -vorbis = ["bevy_audio/vorbis"] +# Enable watching file system for asset hot reload +filesystem_watcher = ["bevy_internal/filesystem_watcher"] -serialize = ["bevy_input/serialize"] +serialize = ["bevy_internal/serialize"] # Display server protocol support (X11 is enabled by default) -wayland = ["bevy_winit/wayland"] -x11 = ["bevy_winit/x11"] +wayland = ["bevy_internal/wayland"] +x11 = ["bevy_internal/x11"] -[workspace] -members = ["crates/*", "crates/bevy_ecs/hecs"] -exclude = ["benches"] +# Enable rendering of font glyphs using subpixel accuracy +subpixel_glyph_atlas = ["bevy_internal/subpixel_glyph_atlas"] + +# Enable systems that allow for automated testing on CI +bevy_ci_testing = ["bevy_internal/bevy_ci_testing"] + +# Enable the "debug asset server" for hot reloading internal assets +debug_asset_server = ["bevy_internal/debug_asset_server"] + +# Enable animation support, and glTF animation loading +animation = ["bevy_internal/animation"] [dependencies] -# bevy -bevy_app = { path = "crates/bevy_app", version = "0.2.1" } -bevy_asset = { path = "crates/bevy_asset", version = "0.2.1" } -bevy_type_registry = { path = "crates/bevy_type_registry", version = "0.2.1" } -bevy_core = { path = "crates/bevy_core", version = "0.2.1" } -bevy_diagnostic = { path = "crates/bevy_diagnostic", version = "0.2.1" } -bevy_ecs = { path = "crates/bevy_ecs", version = "0.2.1" } -bevy_input = { path = "crates/bevy_input", version = "0.2.1" } -bevy_math = { path = "crates/bevy_math", version = "0.2.1" } -bevy_property = { path = "crates/bevy_property", version = "0.2.1" } -bevy_scene = { path = "crates/bevy_scene", version = "0.2.1" } -bevy_transform = { path = "crates/bevy_transform", version = "0.2.1" } -bevy_utils = { path = "crates/bevy_utils", version = "0.2.1" } -bevy_window = { path = "crates/bevy_window", version = "0.2.1" } -bevy_tasks = { path = "crates/bevy_tasks", version = "0.2.1" } -# bevy (optional) -bevy_audio = { path = "crates/bevy_audio", optional = true, version = "0.2.1" } -bevy_gltf = { path = "crates/bevy_gltf", optional = true, version = "0.2.1" } -bevy_pbr = { path = "crates/bevy_pbr", optional = true, version = "0.2.1" } -bevy_render = { path = "crates/bevy_render", optional = true, version = "0.2.1" } -bevy_sprite = { path = "crates/bevy_sprite", optional = true, version = "0.2.1" } -bevy_text = { path = "crates/bevy_text", optional = true, version = "0.2.1" } -bevy_ui = { path = "crates/bevy_ui", optional = true, version = "0.2.1" } -bevy_wgpu = { path = "crates/bevy_wgpu", optional = true, version = "0.2.1" } -bevy_winit = { path = "crates/bevy_winit", optional = true, version = "0.2.1" } -bevy_gilrs = { path = "crates/bevy_gilrs", optional = true, version = "0.2.1" } +bevy_dylib = { path = "crates/bevy_dylib", version = "0.9.0-dev", default-features = false, optional = true } +bevy_internal = { path = "crates/bevy_internal", version = "0.9.0-dev", default-features = false } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +bevy_internal = { path = "crates/bevy_internal", version = "0.9.0-dev", default-features = false, features = [ + "webgl", +] } [dev-dependencies] -rand = "0.7.3" +anyhow = "1.0.4" +rand = "0.8.0" +ron = "0.7.0" serde = { version = "1", features = ["derive"] } -log = "0.4" - -#wasm -console_error_panic_hook = "0.1.6" -console_log = { version = "0.2", features = ["color"] } -anyhow = "1.0" +bytemuck = "1.7" +# Needed to poll Task examples +futures-lite = "1.11.3" +crossbeam-channel = "0.5.0" [[example]] name = "hello_world" path = "examples/hello_world.rs" +[package.metadata.example.hello_world] +hidden = true + +# 2D Rendering +[[example]] +name = "move_sprite" +path = "examples/2d/move_sprite.rs" + +[package.metadata.example.move_sprite] +name = "Move Sprite" +description = "Changes the transform of a sprite" +category = "2D Rendering" +wasm = true + +[[example]] +name = "rotation" +path = "examples/2d/rotation.rs" + +[package.metadata.example.rotation] +name = "2D Rotation" +description = "Demonstrates rotating entities in 2D with quaternions" +category = "2D Rendering" +wasm = true + +[[example]] +name = "mesh2d" +path = "examples/2d/mesh2d.rs" + +[package.metadata.example.mesh2d] +name = "Mesh 2D" +description = "Renders a 2d mesh" +category = "2D Rendering" +wasm = true + +[[example]] +name = "mesh2d_manual" +path = "examples/2d/mesh2d_manual.rs" + +[package.metadata.example.mesh2d_manual] +name = "Manual Mesh 2D" +description = "Renders a custom mesh \"manually\" with \"mid-level\" renderer apis" +category = "2D Rendering" +wasm = true + +[[example]] +name = "mesh2d_vertex_color_texture" +path = "examples/2d/mesh2d_vertex_color_texture.rs" + +[package.metadata.example.mesh2d_vertex_color_texture] +name = "Mesh 2D With Vertex Colors" +description = "Renders a 2d mesh with vertex color attributes" +category = "2D Rendering" +wasm = true + +[[example]] +name = "shapes" +path = "examples/2d/shapes.rs" + +[package.metadata.example.shapes] +name = "Shapes" +description = "Renders a rectangle, circle, and hexagon" +category = "2D Rendering" +wasm = true + [[example]] name = "sprite" path = "examples/2d/sprite.rs" +[package.metadata.example.sprite] +name = "Sprite" +description = "Renders a sprite" +category = "2D Rendering" +wasm = true + +[[example]] +name = "sprite_flipping" +path = "examples/2d/sprite_flipping.rs" + +[package.metadata.example.sprite_flipping] +name = "Sprite Flipping" +description = "Renders a sprite flipped along an axis" +category = "2D Rendering" +wasm = true + [[example]] name = "sprite_sheet" path = "examples/2d/sprite_sheet.rs" +[package.metadata.example.sprite_sheet] +name = "Sprite Sheet" +description = "Renders an animated sprite" +category = "2D Rendering" +wasm = true + +[[example]] +name = "text2d" +path = "examples/2d/text2d.rs" + +[package.metadata.example.text2d] +name = "Text 2D" +description = "Generates text in 2D" +category = "2D Rendering" +wasm = true + [[example]] name = "texture_atlas" path = "examples/2d/texture_atlas.rs" +[package.metadata.example.texture_atlas] +name = "Texture Atlas" +description = "Generates a texture atlas (sprite sheet) from individual sprites" +category = "2D Rendering" +wasm = true + +[[example]] +name = "transparency_2d" +path = "examples/2d/transparency_2d.rs" + +[package.metadata.example.transparency_2d] +name = "Transparency in 2D" +description = "Demonstrates transparency in 2d" +category = "2D Rendering" +wasm = true + +# 3D Rendering +[[example]] +name = "3d_scene" +path = "examples/3d/3d_scene.rs" + +[package.metadata.example.3d_scene] +name = "3D Scene" +description = "Simple 3D scene with basic shapes and lighting" +category = "3D Rendering" +wasm = true + +[[example]] +name = "3d_shapes" +path = "examples/3d/shapes.rs" + +[package.metadata.example.3d_shapes] +name = "3D Shapes" +description = "A scene showcasing the built-in 3D shapes" +category = "3D Rendering" +wasm = true + [[example]] -name = "load_model" -path = "examples/3d/load_model.rs" +name = "lighting" +path = "examples/3d/lighting.rs" + +[package.metadata.example.lighting] +name = "Lighting" +description = "Illustrates various lighting options in a simple scene" +category = "3D Rendering" +wasm = true + +[[example]] +name = "lines" +path = "examples/3d/lines.rs" + +[package.metadata.example.lines] +name = "Lines" +description = "Create a custom material to draw 3d lines" +category = "3D Rendering" +wasm = true + +[[example]] +name = "spotlight" +path = "examples/3d/spotlight.rs" + +[package.metadata.example.spotlight] +name = "Spotlight" +description = "Illustrates spot lights" +category = "3D Rendering" +wasm = true + +[[example]] +name = "load_gltf" +path = "examples/3d/load_gltf.rs" + +[package.metadata.example.load_gltf] +name = "Load glTF" +description = "Loads and renders a glTF file as a scene" +category = "3D Rendering" +wasm = true [[example]] name = "msaa" path = "examples/3d/msaa.rs" +[package.metadata.example.msaa] +name = "MSAA" +description = "Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges" +category = "3D Rendering" +wasm = true + +[[example]] +name = "orthographic" +path = "examples/3d/orthographic.rs" + +[package.metadata.example.orthographic] +name = "Orthographic View" +description = "Shows how to create a 3D orthographic view (for isometric-look in games or CAD applications)" +category = "3D Rendering" +wasm = true + [[example]] name = "parenting" path = "examples/3d/parenting.rs" +[package.metadata.example.parenting] +name = "Parenting" +description = "Demonstrates parent->child relationships and relative transformations" +category = "3D Rendering" +wasm = true + [[example]] -name = "3d_scene" -path = "examples/3d/3d_scene.rs" +name = "pbr" +path = "examples/3d/pbr.rs" + +[package.metadata.example.pbr] +name = "Physically Based Rendering" +description = "Demonstrates use of Physically Based Rendering (PBR) properties" +category = "3D Rendering" +wasm = true + +[[example]] +name = "render_to_texture" +path = "examples/3d/render_to_texture.rs" + +[package.metadata.example.render_to_texture] +name = "Render to Texture" +description = "Shows how to render to a texture, useful for mirrors, UI, or exporting images" +category = "3D Rendering" +wasm = true + +[[example]] +name = "shadow_biases" +path = "examples/3d/shadow_biases.rs" + +[package.metadata.example.shadow_biases] +name = "Shadow Biases" +description = "Demonstrates how shadow biases affect shadows in a 3d scene" +category = "3D Rendering" +wasm = true [[example]] -name = "spawner" -path = "examples/3d/spawner.rs" +name = "shadow_caster_receiver" +path = "examples/3d/shadow_caster_receiver.rs" + +[package.metadata.example.shadow_caster_receiver] +name = "Shadow Caster and Receiver" +description = "Demonstrates how to prevent meshes from casting/receiving shadows in a 3d scene" +category = "3D Rendering" +wasm = true + +[[example]] +name = "skybox" +path = "examples/3d/skybox.rs" +required-features = ["ktx2", "zstd"] + +[package.metadata.example.skybox] +name = "Skybox" +description = "Load a cubemap texture onto a cube like a skybox and cycle through different compressed texture formats." +category = "3D Rendering" +wasm = false + +[[example]] +name = "spherical_area_lights" +path = "examples/3d/spherical_area_lights.rs" + +[package.metadata.example.spherical_area_lights] +name = "Spherical Area Lights" +description = "Demonstrates how point light radius values affect light behavior" +category = "3D Rendering" +wasm = true + +[[example]] +name = "split_screen" +path = "examples/3d/split_screen.rs" + +[package.metadata.example.split_screen] +name = "Split Screen" +description = "Demonstrates how to render two cameras to the same window to accomplish \"split screen\"" +category = "3D Rendering" +wasm = true [[example]] name = "texture" path = "examples/3d/texture.rs" +[package.metadata.example.texture] +name = "Texture" +description = "Shows configuration of texture materials" +category = "3D Rendering" +wasm = true + +[[example]] +name = "transparency_3d" +path = "examples/3d/transparency_3d.rs" + +[package.metadata.example.transparency_3d] +name = "Transparency in 3D" +description = "Demonstrates transparency in 3d" +category = "3D Rendering" +wasm = true + [[example]] -name = "z_sort_debug" -path = "examples/3d/z_sort_debug.rs" +name = "two_passes" +path = "examples/3d/two_passes.rs" + +[package.metadata.example.two_passes] +name = "Two Passes" +description = "Renders two 3d passes to the same window from different perspectives" +category = "3D Rendering" +wasm = true [[example]] -name = "empty_defaults" -path = "examples/app/empty_defaults.rs" +name = "update_gltf_scene" +path = "examples/3d/update_gltf_scene.rs" + +[package.metadata.example.update_gltf_scene] +name = "Update glTF Scene" +description = "Update a scene from a glTF file, either by spawning the scene as a child of another entity, or by accessing the entities of the scene" +category = "3D Rendering" +wasm = true + +[[example]] +name = "vertex_colors" +path = "examples/3d/vertex_colors.rs" + +[package.metadata.example.vertex_colors] +name = "Vertex Colors" +description = "Shows the use of vertex colors" +category = "3D Rendering" +wasm = true + +[[example]] +name = "wireframe" +path = "examples/3d/wireframe.rs" + +[package.metadata.example.wireframe] +name = "Wireframe" +description = "Showcases wireframe rendering" +category = "3D Rendering" +wasm = true + +# Animation +[[example]] +name = "animated_fox" +path = "examples/animation/animated_fox.rs" + +[package.metadata.example.animated_fox] +name = "Animated Fox" +description = "Plays an animation from a skinned glTF" +category = "Animation" +wasm = true + +[[example]] +name = "animated_transform" +path = "examples/animation/animated_transform.rs" + +[package.metadata.example.animated_transform] +name = "Animated Transform" +description = "Create and play an animation defined by code that operates on the `Transform` component" +category = "Animation" +wasm = true + +[[example]] +name = "custom_skinned_mesh" +path = "examples/animation/custom_skinned_mesh.rs" + +[package.metadata.example.custom_skinned_mesh] +name = "Custom Skinned Mesh" +description = "Skinned mesh example with mesh and joints data defined in code" +category = "Animation" +wasm = true + +[[example]] +name = "gltf_skinned_mesh" +path = "examples/animation/gltf_skinned_mesh.rs" + +[package.metadata.example.gltf_skinned_mesh] +name = "glTF Skinned Mesh" +description = "Skinned mesh example with mesh and joints data loaded from a glTF file" +category = "Animation" +wasm = true + +# Application +[[example]] +name = "custom_loop" +path = "examples/app/custom_loop.rs" + +[package.metadata.example.custom_loop] +name = "Custom Loop" +description = "Demonstrates how to create a custom runner (to update an app manually)" +category = "Application" +wasm = false + +[[example]] +name = "drag_and_drop" +path = "examples/app/drag_and_drop.rs" + +[package.metadata.example.drag_and_drop] +name = "Drag and Drop" +description = "An example that shows how to handle drag and drop in an app" +category = "Application" +wasm = false [[example]] name = "empty" path = "examples/app/empty.rs" +[package.metadata.example.empty] +name = "Empty" +description = "An empty application (does nothing)" +category = "Application" +wasm = false + +[[example]] +name = "empty_defaults" +path = "examples/app/empty_defaults.rs" + +[package.metadata.example.empty_defaults] +name = "Empty with Defaults" +description = "An empty application with default plugins" +category = "Application" +wasm = true + [[example]] name = "headless" path = "examples/app/headless.rs" +[package.metadata.example.headless] +name = "Headless" +description = "An application that runs without default plugins" +category = "Application" +wasm = false + +[[example]] +name = "logs" +path = "examples/app/logs.rs" + +[package.metadata.example.logs] +name = "Logs" +description = "Illustrate how to use generate log output" +category = "Application" +wasm = true + [[example]] name = "plugin" path = "examples/app/plugin.rs" +[package.metadata.example.plugin] +name = "Plugin" +description = "Demonstrates the creation and registration of a custom plugin" +category = "Application" +wasm = true + +[[example]] +name = "plugin_group" +path = "examples/app/plugin_group.rs" + +[package.metadata.example.plugin_group] +name = "Plugin Group" +description = "Demonstrates the creation and registration of a custom plugin group" +category = "Application" +wasm = true + [[example]] name = "return_after_run" path = "examples/app/return_after_run.rs" +[package.metadata.example.return_after_run] +name = "Return after Run" +description = "Show how to return to main after the Bevy app has exited" +category = "Application" +wasm = false + [[example]] name = "thread_pool_resources" path = "examples/app/thread_pool_resources.rs" +[package.metadata.example.thread_pool_resources] +name = "Thread Pool Resources" +description = "Creates and customizes the internal thread pool" +category = "Application" +wasm = false + [[example]] -name = "hot_asset_reloading" -path = "examples/asset/hot_asset_reloading.rs" +name = "no_renderer" +path = "examples/app/no_renderer.rs" + +[package.metadata.example.no_renderer] +name = "No Renderer" +description = "An application that runs with default plugins and displays an empty window, but without an actual renderer" +category = "Application" +wasm = false +[[example]] +name = "without_winit" +path = "examples/app/without_winit.rs" + +[package.metadata.example.without_winit] +name = "Without Winit" +description = "Create an application without winit (runs single time, no event loop)" +category = "Application" +wasm = false + +# Assets [[example]] name = "asset_loading" path = "examples/asset/asset_loading.rs" +[package.metadata.example.asset_loading] +name = "Asset Loading" +description = "Demonstrates various methods to load assets" +category = "Assets" +wasm = true + +[[example]] +name = "custom_asset" +path = "examples/asset/custom_asset.rs" + +[package.metadata.example.custom_asset] +name = "Custom Asset" +description = "Implements a custom asset loader" +category = "Assets" +wasm = true + +[[example]] +name = "custom_asset_io" +path = "examples/asset/custom_asset_io.rs" + +[package.metadata.example.custom_asset_io] +name = "Custom Asset IO" +description = "Implements a custom asset io loader" +category = "Assets" +wasm = true + +[[example]] +name = "hot_asset_reloading" +path = "examples/asset/hot_asset_reloading.rs" + +[package.metadata.example.hot_asset_reloading] +name = "Hot Reloading of Assets" +description = "Demonstrates automatic reloading of assets when modified on disk" +category = "Assets" +wasm = true + +# Async Tasks +[[example]] +name = "async_compute" +path = "examples/async_tasks/async_compute.rs" + +[package.metadata.example.async_compute] +name = "Async Compute" +description = "How to use `AsyncComputeTaskPool` to complete longer running tasks" +category = "Async Tasks" +wasm = false + +[[example]] +name = "external_source_external_thread" +path = "examples/async_tasks/external_source_external_thread.rs" + +[package.metadata.example.external_source_external_thread] +name = "External Source of Data on an External Thread" +description = "How to use an external thread to run an infinite task and communicate with a channel" +category = "Async Tasks" +wasm = false + +# Audio [[example]] name = "audio" path = "examples/audio/audio.rs" +[package.metadata.example.audio] +name = "Audio" +description = "Shows how to load and play an audio file" +category = "Audio" +wasm = true + +[[example]] +name = "audio_control" +path = "examples/audio/audio_control.rs" + +[package.metadata.example.audio_control] +name = "Audio Control" +description = "Shows how to load and play an audio file, and control how it's played" +category = "Audio" +wasm = true + +# Diagnostics +[[example]] +name = "log_diagnostics" +path = "examples/diagnostics/log_diagnostics.rs" + +[package.metadata.example.log_diagnostics] +name = "Log Diagnostics" +description = "Add a plugin that logs diagnostics, like frames per second (FPS), to the console" +category = "Diagnostics" +wasm = true + [[example]] name = "custom_diagnostic" path = "examples/diagnostics/custom_diagnostic.rs" +[package.metadata.example.custom_diagnostic] +name = "Custom Diagnostic" +description = "Shows how to create a custom diagnostic" +category = "Diagnostics" +wasm = true + +# ECS (Entity Component System) +[[example]] +name = "ecs_guide" +path = "examples/ecs/ecs_guide.rs" + +[package.metadata.example.ecs_guide] +name = "ECS Guide" +description = "Full guide to Bevy's ECS" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "component_change_detection" +path = "examples/ecs/component_change_detection.rs" + +[package.metadata.example.component_change_detection] +name = "Component Change Detection" +description = "Change detection on components" +category = "ECS (Entity Component System)" +wasm = false + [[example]] -name = "print_diagnostics" -path = "examples/diagnostics/print_diagnostics.rs" +name = "custom_query_param" +path = "examples/ecs/custom_query_param.rs" + +[package.metadata.example.custom_query_param] +name = "Custom Query Parameters" +description = "Groups commonly used compound queries and query filters into a single type" +category = "ECS (Entity Component System)" +wasm = false [[example]] name = "event" path = "examples/ecs/event.rs" +[package.metadata.example.event] +name = "Event" +description = "Illustrates event creation, activation, and reception" +category = "ECS (Entity Component System)" +wasm = false + [[example]] -name = "startup_system" -path = "examples/ecs/startup_system.rs" +name = "fixed_timestep" +path = "examples/ecs/fixed_timestep.rs" + +[package.metadata.example.fixed_timestep] +name = "Fixed Timestep" +description = "Shows how to create systems that run every fixed timestep, rather than every tick" +category = "ECS (Entity Component System)" +wasm = false [[example]] -name = "ecs_guide" -path = "examples/ecs/ecs_guide.rs" +name = "generic_system" +path = "examples/ecs/generic_system.rs" + +[package.metadata.example.generic_system] +name = "Generic System" +description = "Shows how to create systems that can be reused with different types" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "hierarchy" +path = "examples/ecs/hierarchy.rs" + +[package.metadata.example.hierarchy] +name = "Hierarchy" +description = "Creates a hierarchy of parents and children entities" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "iter_combinations" +path = "examples/ecs/iter_combinations.rs" + +[package.metadata.example.iter_combinations] +name = "Iter Combinations" +description = "Shows how to iterate over combinations of query results" +category = "ECS (Entity Component System)" +wasm = true [[example]] name = "parallel_query" path = "examples/ecs/parallel_query.rs" +[package.metadata.example.parallel_query] +name = "Parallel Query" +description = "Illustrates parallel queries with `ParallelIterator`" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "removal_detection" +path = "examples/ecs/removal_detection.rs" + +[package.metadata.example.removal_detection] +name = "Removal Detection" +description = "Query for entities that had a specific component removed in a previous stage during the current frame" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "startup_system" +path = "examples/ecs/startup_system.rs" + +[package.metadata.example.startup_system] +name = "Startup System" +description = "Demonstrates a startup system (one that runs once when the app starts up)" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "state" +path = "examples/ecs/state.rs" + +[package.metadata.example.state] +name = "State" +description = "Illustrates how to use States to control transitioning from a Menu state to an InGame state" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "system_chaining" +path = "examples/ecs/system_chaining.rs" + +[package.metadata.example.system_chaining] +name = "System Chaining" +description = "Chain two systems together, specifying a return type in a system (such as `Result`)" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "system_closure" +path = "examples/ecs/system_closure.rs" + +[package.metadata.example.system_closure] +name = "System Closure" +description = "Show how to use closures as systems, and how to configure `Local` variables by capturing external state" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "system_param" +path = "examples/ecs/system_param.rs" + +[package.metadata.example.system_param] +name = "System Parameter" +description = "Illustrates creating custom system parameters with `SystemParam`" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "system_sets" +path = "examples/ecs/system_sets.rs" + +[package.metadata.example.system_sets] +name = "System Sets" +description = "Shows `SystemSet` use along with run criterion" +category = "ECS (Entity Component System)" +wasm = false + +[[example]] +name = "timers" +path = "examples/ecs/timers.rs" + +[package.metadata.example.timers] +name = "Timers" +description = "Illustrates ticking `Timer` resources inside systems and handling their state" +category = "ECS (Entity Component System)" +wasm = false + +# Games +[[example]] +name = "alien_cake_addict" +path = "examples/games/alien_cake_addict.rs" + +[package.metadata.example.alien_cake_addict] +name = "Alien Cake Addict" +description = "Eat the cakes. Eat them all. An example 3D game" +category = "Games" +wasm = true + [[example]] name = "breakout" -path = "examples/game/breakout.rs" +path = "examples/games/breakout.rs" + +[package.metadata.example.breakout] +name = "Breakout" +description = "An implementation of the classic game \"Breakout\"" +category = "Games" +wasm = true [[example]] -name = "mouse_input" -path = "examples/input/mouse_input.rs" +name = "contributors" +path = "examples/games/contributors.rs" + +[package.metadata.example.contributors] +name = "Contributors" +description = "Displays each contributor as a bouncy bevy-ball!" +category = "Games" +wasm = true [[example]] -name = "mouse_input_events" -path = "examples/input/mouse_input_events.rs" +name = "game_menu" +path = "examples/games/game_menu.rs" + +[package.metadata.example.game_menu] +name = "Game Menu" +description = "A simple game menu" +category = "Games" +wasm = true + +# Input +[[example]] +name = "char_input_events" +path = "examples/input/char_input_events.rs" + +[package.metadata.example.char_input_events] +name = "Char Input Events" +description = "Prints out all chars as they are inputted" +category = "Input" +wasm = false + +[[example]] +name = "gamepad_input" +path = "examples/input/gamepad_input.rs" + +[package.metadata.example.gamepad_input] +name = "Gamepad Input" +description = "Shows handling of gamepad input, connections, and disconnections" +category = "Input" +wasm = false + +[[example]] +name = "gamepad_input_events" +path = "examples/input/gamepad_input_events.rs" + +[package.metadata.example.gamepad_input_events] +name = "Gamepad Input Events" +description = "Iterates and prints gamepad input and connection events" +category = "Input" +wasm = false [[example]] name = "keyboard_input" path = "examples/input/keyboard_input.rs" +[package.metadata.example.keyboard_input] +name = "Keyboard Input" +description = "Demonstrates handling a key press/release" +category = "Input" +wasm = false + +[[example]] +name = "keyboard_modifiers" +path = "examples/input/keyboard_modifiers.rs" + +[package.metadata.example.keyboard_modifiers] +name = "Keyboard Modifiers" +description = "Demonstrates using key modifiers (ctrl, shift)" +category = "Input" +wasm = false + [[example]] name = "keyboard_input_events" path = "examples/input/keyboard_input_events.rs" +[package.metadata.example.keyboard_input_events] +name = "Keyboard Input Events" +description = "Prints out all keyboard events" +category = "Input" +wasm = false + [[example]] -name = "gamepad_input" -path = "examples/input/gamepad_input.rs" +name = "mouse_input" +path = "examples/input/mouse_input.rs" + +[package.metadata.example.mouse_input] +name = "Mouse Input" +description = "Demonstrates handling a mouse button press/release" +category = "Input" +wasm = false + +[[example]] +name = "mouse_input_events" +path = "examples/input/mouse_input_events.rs" + +[package.metadata.example.mouse_input_events] +name = "Mouse Input Events" +description = "Prints out all mouse events (buttons, movement, etc.)" +category = "Input" +wasm = false + +[[example]] +name = "mouse_grab" +path = "examples/input/mouse_grab.rs" + +[package.metadata.example.mouse_grab] +name = "Mouse Grab" +description = "Demonstrates how to grab the mouse, locking the cursor to the app's screen" +category = "Input" +wasm = false + +[[example]] +name = "touch_input" +path = "examples/input/touch_input.rs" + +[package.metadata.example.touch_input] +name = "Touch Input" +description = "Displays touch presses, releases, and cancels" +category = "Input" +wasm = false + +[[example]] +name = "touch_input_events" +path = "examples/input/touch_input_events.rs" + +[package.metadata.example.touch_input_events] +name = "Touch Input Events" +description = "Prints out all touch inputs" +category = "Input" +wasm = false + +# Reflection +[[example]] +name = "reflection" +path = "examples/reflection/reflection.rs" + +[package.metadata.example.reflection] +name = "Reflection" +description = "Demonstrates how reflection in Bevy provides a way to dynamically interact with Rust types" +category = "Reflection" +wasm = false + +[[example]] +name = "generic_reflection" +path = "examples/reflection/generic_reflection.rs" + +[package.metadata.example.generic_reflection] +name = "Generic Reflection" +description = "Registers concrete instances of generic types that may be used with reflection" +category = "Reflection" +wasm = false + +[[example]] +name = "reflection_types" +path = "examples/reflection/reflection_types.rs" + +[package.metadata.example.reflection_types] +name = "Reflection Types" +description = "Illustrates the various reflection types available" +category = "Reflection" +wasm = false + +[[example]] +name = "trait_reflection" +path = "examples/reflection/trait_reflection.rs" + +[package.metadata.example.trait_reflection] +name = "Trait Reflection" +description = "Allows reflection with trait objects" +category = "Reflection" +wasm = false +# Scene [[example]] name = "scene" path = "examples/scene/scene.rs" +[package.metadata.example.scene] +name = "Scene" +description = "Demonstrates loading from and saving scenes to files" +category = "Scene" +wasm = false + +# Shaders +[[package.metadata.category]] +name = "Shaders" +description = """ +These examples demonstrate how to implement different shaders in user code. + +A shader in its most common usage is a small program that is run by the GPU per-vertex in a mesh (a vertex shader) or per-affected-screen-fragment (a fragment shader.) The GPU executes these programs in a highly parallel way. + +There are also compute shaders which are used for more general processing leveraging the GPU's parallelism. +""" + [[example]] -name = "properties" -path = "examples/scene/properties.rs" +name = "custom_vertex_attribute" +path = "examples/shader/custom_vertex_attribute.rs" + +[package.metadata.example.custom_vertex_attribute] +name = "Custom Vertex Attribute" +description = "A shader that reads a mesh's custom vertex attribute" +category = "Shaders" +wasm = true [[example]] -name = "shader_custom_material" -path = "examples/shader/shader_custom_material.rs" +name = "post_processing" +path = "examples/shader/post_processing.rs" + +[package.metadata.example.post_processing] +name = "Post Processing" +description = "A custom post processing effect, using two cameras, with one reusing the render texture of the first one" +category = "Shaders" +wasm = true [[example]] name = "shader_defs" path = "examples/shader/shader_defs.rs" +[package.metadata.example.shader_defs] +name = "Shader Defs" +description = "A shader that uses \"shaders defs\" (a bevy tool to selectively toggle parts of a shader)" +category = "Shaders" +wasm = true + +[[example]] +name = "shader_material" +path = "examples/shader/shader_material.rs" + +[package.metadata.example.shader_material] +name = "Material" +description = "A shader and a material that uses it" +category = "Shaders" +wasm = true + +[[example]] +name = "shader_material_screenspace_texture" +path = "examples/shader/shader_material_screenspace_texture.rs" + +[package.metadata.example.shader_material_screenspace_texture] +name = "Material - Screenspace Texture" +description = "A shader that samples a texture with view-independent UV coordinates" +category = "Shaders" +wasm = true + +[[example]] +name = "shader_material_glsl" +path = "examples/shader/shader_material_glsl.rs" + +[package.metadata.example.shader_material_glsl] +name = "Material - GLSL" +description = "A shader that uses the GLSL shading language" +category = "Shaders" +wasm = true + +[[example]] +name = "shader_instancing" +path = "examples/shader/shader_instancing.rs" + +[package.metadata.example.shader_instancing] +name = "Instancing" +description = "A shader that renders a mesh multiple times in one draw call" +category = "Shaders" +wasm = true + +[[example]] +name = "animate_shader" +path = "examples/shader/animate_shader.rs" + +[package.metadata.example.animate_shader] +name = "Animated" +description = "A shader that uses dynamic data like the time since startup" +category = "Shaders" +wasm = true + +[[example]] +name = "compute_shader_game_of_life" +path = "examples/shader/compute_shader_game_of_life.rs" + +[package.metadata.example.compute_shader_game_of_life] +name = "Compute - Game of Life" +description = "A compute shader that simulates Conway's Game of Life" +category = "Shaders" +wasm = false + +[[example]] +name = "array_texture" +path = "examples/shader/array_texture.rs" + +[package.metadata.example.array_texture] +name = "Array Texture" +description = "A shader that shows how to reuse the core bevy PBR shading functionality in a custom material that obtains the base color from an array texture." +category = "Shaders" +wasm = true + +# Stress tests +[[package.metadata.category]] +name = "Stress Tests" +description = """ +These examples are used to test the performance and stability of various parts of the engine in an isolated way. + +Due to the focus on performance it's recommended to run the stress tests in release mode: + +```sh +cargo run --release --example +``` +""" + +[[example]] +name = "bevymark" +path = "examples/stress_tests/bevymark.rs" + +[package.metadata.example.bevymark] +name = "Bevymark" +description = "A heavy sprite rendering workload to benchmark your system with Bevy" +category = "Stress Tests" +wasm = true + +[[example]] +name = "many_animated_sprites" +path = "examples/stress_tests/many_animated_sprites.rs" + +[package.metadata.example.many_animated_sprites] +name = "Many Animated Sprites" +description = "Displays many animated sprites in a grid arrangement with slight offsets to their animation timers. Used for performance testing." +category = "Stress Tests" +wasm = true + +[[example]] +name = "many_buttons" +path = "examples/stress_tests/many_buttons.rs" + +[package.metadata.example.many_buttons] +name = "Many Buttons" +description = "Test rendering of many UI elements" +category = "Stress Tests" +wasm = true + +[[example]] +name = "many_cubes" +path = "examples/stress_tests/many_cubes.rs" + +[package.metadata.example.many_cubes] +name = "Many Cubes" +description = "Simple benchmark to test per-entity draw overhead. Run with the `sphere` argument to test frustum culling" +category = "Stress Tests" +wasm = true + +[[example]] +name = "many_foxes" +path = "examples/stress_tests/many_foxes.rs" + +[package.metadata.example.many_foxes] +name = "Many Foxes" +description = "Loads an animated fox model and spawns lots of them. Good for testing skinned mesh performance. Takes an unsigned integer argument for the number of foxes to spawn. Defaults to 1000" +category = "Stress Tests" +wasm = true + +[[example]] +name = "many_lights" +path = "examples/stress_tests/many_lights.rs" + +[package.metadata.example.many_lights] +name = "Many Lights" +description = "Simple benchmark to test rendering many point lights. Run with `WGPU_SETTINGS_PRIO=webgl2` to restrict to uniform buffers and max 256 lights" +category = "Stress Tests" +wasm = true + +[[example]] +name = "many_sprites" +path = "examples/stress_tests/many_sprites.rs" + +[package.metadata.example.many_sprites] +name = "Many Sprites" +description = "Displays many sprites in a grid arrangement! Used for performance testing. Use `--colored` to enable color tinted sprites." +category = "Stress Tests" +wasm = true + +[[example]] +name = "transform_hierarchy" +path = "examples/stress_tests/transform_hierarchy.rs" + +[package.metadata.example.transform_hierarchy] +name = "Transform Hierarchy" +description = "Various test cases for hierarchy and transform propagation performance" +category = "Stress Tests" +wasm = true + +# Tools +[[example]] +name = "scene_viewer" +path = "examples/tools/scene_viewer.rs" + +[package.metadata.example.scene_viewer] +name = "Scene Viewer" +description = "A simple way to view glTF models with Bevy. Just run `cargo run --release --example scene_viewer /path/to/model.gltf#Scene0`, replacing the path as appropriate. With no arguments it will load the FieldHelmet glTF model from the repository assets subdirectory" +category = "Tools" +wasm = true + +# Transforms +[[example]] +name = "global_vs_local_translation" +path = "examples/transforms/global_vs_local_translation.rs" + +[package.metadata.example.global_vs_local_translation] +name = "Global / Local Translation" +description = "Illustrates the difference between direction of a translation in respect to local object or global object Transform" +category = "Transforms" +wasm = true + +[[example]] +name = "3d_rotation" +path = "examples/transforms/3d_rotation.rs" + +[package.metadata.example.3d_rotation] +name = "3D Rotation" +description = "Illustrates how to (constantly) rotate an object around an axis" +category = "Transforms" +wasm = true + +[[example]] +name = "scale" +path = "examples/transforms/scale.rs" + +[package.metadata.example.scale] +name = "Scale" +description = "Illustrates how to scale an object in each direction" +category = "Transforms" +wasm = true + +[[example]] +name = "transform" +path = "examples/transforms/transform.rs" + +[package.metadata.example.transform] +name = "Transform" +description = "Shows multiple transformations of objects" +category = "Transforms" +wasm = true + +[[example]] +name = "translation" +path = "examples/transforms/translation.rs" + +[package.metadata.example.translation] +name = "Translation" +description = "Illustrates how to move an object along an axis" +category = "Transforms" +wasm = true + +# UI (User Interface) [[example]] name = "button" path = "examples/ui/button.rs" +[package.metadata.example.button] +name = "Button" +description = "Illustrates creating and updating a button" +category = "UI (User Interface)" +wasm = true + +[[example]] +name = "font_atlas_debug" +path = "examples/ui/font_atlas_debug.rs" + +[package.metadata.example.font_atlas_debug] +name = "Font Atlas Debug" +description = "Illustrates how FontAtlases are populated (used to optimize text rendering internally)" +category = "UI (User Interface)" +wasm = true + [[example]] name = "text" path = "examples/ui/text.rs" +[package.metadata.example.text] +name = "Text" +description = "Illustrates creating and updating text" +category = "UI (User Interface)" +wasm = true + [[example]] -name = "font_atlas_debug" -path = "examples/ui/font_atlas_debug.rs" +name = "text_debug" +path = "examples/ui/text_debug.rs" + +[package.metadata.example.text_debug] +name = "Text Debug" +description = "An example for debugging text layout" +category = "UI (User Interface)" +wasm = true + +[[example]] +name = "transparency_ui" +path = "examples/ui/transparency_ui.rs" + +[package.metadata.example.transparency_ui] +name = "Transparency UI" +description = "Demonstrates transparency for UI" +category = "UI (User Interface)" +wasm = true [[example]] name = "ui" path = "examples/ui/ui.rs" +[package.metadata.example.ui] +name = "UI" +description = "Illustrates various features of Bevy UI" +category = "UI (User Interface)" +wasm = true + +# Window [[example]] name = "clear_color" path = "examples/window/clear_color.rs" +[package.metadata.example.clear_color] +name = "Clear Color" +description = "Creates a solid color window" +category = "Window" +wasm = true + +[[example]] +name = "low_power" +path = "examples/window/low_power.rs" + +[package.metadata.example.low_power] +name = "Low Power" +description = "Demonstrates settings to reduce power use for bevy applications" +category = "Window" +wasm = true + [[example]] name = "multiple_windows" path = "examples/window/multiple_windows.rs" +[package.metadata.example.multiple_windows] +name = "Multiple Windows" +description = "Demonstrates creating multiple windows, and rendering to them" +category = "Window" +wasm = false + +[[example]] +name = "scale_factor_override" +path = "examples/window/scale_factor_override.rs" + +[package.metadata.example.scale_factor_override] +name = "Scale Factor Override" +description = "Illustrates how to customize the default window settings" +category = "Window" +wasm = true + +[[example]] +name = "transparent_window" +path = "examples/window/transparent_window.rs" + +[package.metadata.example.transparent_window] +name = "Transparent Window" +description = "Illustrates making the window transparent and hiding the window decoration" +category = "Window" +wasm = false + [[example]] name = "window_settings" path = "examples/window/window_settings.rs" -[[example]] -name = "hello_wasm" -path = "examples/wasm/hello_wasm.rs" -required-features = [] +[package.metadata.example.window_settings] +name = "Window Settings" +description = "Demonstrates customizing default window settings" +category = "Window" +wasm = true [[example]] -name = "headless_wasm" -path = "examples/wasm/headless_wasm.rs" -required-features = [] +name = "resizing" +path = "tests/window/resizing.rs" + +[package.metadata.example.resizing] +hidden = true [[example]] -name = "winit_wasm" -path = "examples/wasm/winit_wasm.rs" -required-features = ["bevy_winit"] +name = "minimising" +path = "tests/window/minimising.rs" +[package.metadata.example.minimising] +hidden = true + +# Android [[example]] -name = "assets_wasm" -path = "examples/wasm/assets_wasm.rs" -required-features = ["bevy_winit"] +crate-type = ["cdylib"] +name = "android_example" +path = "examples/android/android.rs" + +[package.metadata.example.android_example] +hidden = true + +[package.metadata.android] +package = "org.bevyengine.example" +apk_name = "bevyexample" +assets = "assets" +resources = "assets/android-res" +build_targets = ["aarch64-linux-android", "armv7-linux-androideabi"] + +[package.metadata.android.sdk] +target_sdk_version = 31 + +[package.metadata.android.application] +icon = "@mipmap/ic_launcher" +label = "Bevy Example" + +[profile.wasm-release] +inherits = "release" +opt-level = "z" +lto = "fat" +codegen-units = 1 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 9d1f4a3d52339..0000000000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Carter Anderson - -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. \ No newline at end of file diff --git a/crates/bevy_ecs/hecs/LICENSE b/LICENSE-APACHE similarity index 89% rename from crates/bevy_ecs/hecs/LICENSE rename to LICENSE-APACHE index d645695673349..d9a10c0d8e868 100644 --- a/crates/bevy_ecs/hecs/LICENSE +++ b/LICENSE-APACHE @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -175,28 +174,3 @@ 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/crates/bevy_transform/LICENSE b/LICENSE-MIT similarity index 96% rename from crates/bevy_transform/LICENSE rename to LICENSE-MIT index 1ab7771823d44..9cf106272ac3b 100644 --- a/crates/bevy_transform/LICENSE +++ b/LICENSE-MIT @@ -1,7 +1,5 @@ MIT License -Copyright (c) 2019 Alec Thilenius - 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 diff --git a/README.md b/README.md index df26fce21ac53..c73b0b6c639ca 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ -# [![Bevy](assets/branding/bevy_logo_light_small.svg)](https://bevyengine.org) +# [![Bevy](assets/branding/bevy_logo_light_dark_and_dimmed.svg)](https://bevyengine.org) + [![Crates.io](https://img.shields.io/crates/v/bevy.svg)](https://crates.io/crates/bevy) -[![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bevyengine/bevy/blob/master/LICENSE) +[![MIT/Apache 2.0](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license) [![Crates.io](https://img.shields.io/crates/d/bevy.svg)](https://crates.io/crates/bevy) [![Rust](https://github.com/bevyengine/bevy/workflows/CI/badge.svg)](https://github.com/bevyengine/bevy/actions) +![iOS cron CI](https://github.com/bevyengine/bevy/workflows/iOS%20cron%20CI/badge.svg) +[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy) ## What is Bevy? @@ -10,13 +13,16 @@ Bevy is a refreshingly simple data-driven game engine built in Rust. It is free ## WARNING -Bevy is still in the _very_ early stages of development. APIs can and will change (now is the time to make suggestions!). Important features are missing. Documentation is sparse. Please don't build any serious projects in Bevy unless you are prepared to be broken by api changes constantly. +Bevy is still in the _very_ early stages of development. APIs can and will change (now is the time to make suggestions!). Important features are missing. Documentation is sparse. Please don't build any serious projects in Bevy unless you are prepared to be broken by API changes constantly. + +**MSRV:** Bevy relies heavily on improvements in the Rust language and compiler. +As a result, the Minimum Supported Rust Version (MSRV) is "the latest stable release" of Rust. ## Design Goals -* **Capable**: Offer a complete 2D and 3D feature set +* **Capable**: Offer a complete 2D and 3D feature set * **Simple**: Easy for newbies to pick up, but infinitely flexible for power users -* **Data Focused**: Data-oriented architecture using the Entity Component System paradigm +* **Data Focused**: Data-oriented architecture using the Entity Component System paradigm * **Modular**: Use only what you need. Replace what you don't like * **Fast**: App logic should run quickly, and when possible, in parallel * **Productive**: Changes should compile quickly ... waiting isn't fun @@ -24,54 +30,66 @@ Bevy is still in the _very_ early stages of development. APIs can and will chang ## About * **[Features](https://bevyengine.org):** A quick overview of Bevy's features. -* **[Roadmap](https://github.com/bevyengine/bevy/projects/1):** The Bevy team's development plan. -* **[Introducing Bevy](https://bevyengine.org/news/introducing-bevy/)**: A blog post covering some of Bevy's features +* **[News](https://bevyengine.org/news/)**: A development blog that covers our progress, plans and shiny new features. ## Docs -* **[The Bevy Book](https://bevyengine.org/learn/book/introduction):** Bevy's official documentation. The best place to start learning Bevy. +* **[The Bevy Book](https://bevyengine.org/learn/book/introduction):** Bevy's official documentation. The best place to start learning Bevy. * **[Bevy Rust API Docs](https://docs.rs/bevy):** Bevy's Rust API docs, which are automatically generated from the doc comments in this repo. +* **[Official Examples](https://github.com/bevyengine/bevy/tree/latest/examples):** Bevy's dedicated, runnable examples, which are great for digging into specific concepts. +* **[Community-Made Learning Resources](https://bevyengine.org/assets/#learning)**: More tutorials, documentation, and examples made by the Bevy community. ## Community -Before contributing or participating in discussions with the community, you should familiarize yourself with our **[Code of Conduct](https://github.com/bevyengine/bevy/blob/master/CODE_OF_CONDUCT.md)** -* **[Discord](https://discord.gg/gMUk5Ph):** Bevy's official discord server. +Before contributing or participating in discussions with the community, you should familiarize yourself with our [**Code of Conduct**](./CODE_OF_CONDUCT.md). + +* **[Discord](https://discord.gg/bevy):** Bevy's official discord server. * **[Reddit](https://reddit.com/r/bevy):** Bevy's official subreddit. -* **[Stack Overflow](https://stackoverflow.com/questions/tagged/bevy):** Questions tagged Bevy on Stack Overflow. -* **[Awesome Bevy](https://github.com/bevyengine/awesome-bevy):** A collection of awesome Bevy projects. +* **[GitHub Discussions](https://github.com/bevyengine/bevy/discussions):** The best place for questions about Bevy, answered right here! +* **[Bevy Assets](https://bevyengine.org/assets/):** A collection of awesome Bevy projects, tools, plugins and learning materials. + +If you'd like to help build Bevy, check out the **[Contributor's Guide](https://github.com/bevyengine/bevy/blob/main/CONTRIBUTING.md)**. +For simple problems, feel free to open an issue or PR and tackle it yourself! + +For more complex architecture decisions and experimental mad science, please open an [RFC](https://github.com/bevyengine/rfcs) (Request For Comments) so we can brainstorm together effectively! ## Getting Started We recommend checking out [The Bevy Book](https://bevyengine.org/learn/book/introduction) for a full tutorial. -Follow the [Setup guide](https://bevyengine.org/learn/book/getting-started/setup/) to ensure your development environment is setup correctly. -Once setup, you can quickly try out the [examples](/examples) by cloning this repo and running the following command: +Follow the [Setup guide](https://bevyengine.org/learn/book/getting-started/setup/) to ensure your development environment is set up correctly. +Once set up, you can quickly try out the [examples](https://github.com/bevyengine/bevy/tree/latest/examples) by cloning this repo and running the following commands: ```sh +# Switch to the correct version (latest release, default is main development branch) +git checkout latest # Runs the "breakout" example cargo run --example breakout ``` -### Fast Compiles +To draw a window with standard functionality enabled, use: -Bevy can be built just fine using default configuration on stable Rust. However for really fast iterative compiles, you should enable the "fast compiles" setup by [following the instructions here](http://bevyengine.org/learn/book/getting-started/setup/). +```rust +use bevy::prelude::*; -## Focus Areas +fn main(){ + App::new() + .add_plugins(DefaultPlugins) + .run(); +} +``` -Bevy has the following [Focus Areas](https://github.com/bevyengine/bevy/labels/focus-area). We are currently focusing our development efforts in these areas and they will receive priority for Bevy developers' time. If you would like to contribute to Bevy, you are heavily encouraged to join in on these efforts: +### Fast Compiles -### [Editor-Ready UI](https://github.com/bevyengine/bevy/issues/254) -### [PBR / Clustered Forward Rendering](https://github.com/bevyengine/bevy/issues/179) -### [Scenes](https://github.com/bevyengine/bevy/issues/255) +Bevy can be built just fine using default configuration on stable Rust. However for really fast iterative compiles, you should enable the "fast compiles" setup by [following the instructions here](http://bevyengine.org/learn/book/getting-started/setup/). ## Libraries Used Bevy is only possible because of the hard work put into these foundational technologies: -* [wgpu-rs](https://github.com/gfx-rs/wgpu-rs): modern / low-level / cross platform graphics library inspired by Vulkan +* [wgpu](https://wgpu.rs/): modern / low-level / cross-platform graphics library inspired by Vulkan * [glam-rs](https://github.com/bitshifter/glam-rs): a simple and fast 3D math library for games and graphics -* [winit](https://github.com/rust-windowing/winit): cross platform window creation and management in Rust -* [spirv-reflect](https://github.com/gwihlidal/spirv-reflect-rs): Reflection API in rust for SPIR-V shader byte code +* [winit](https://github.com/rust-windowing/winit): cross-platform window creation and management in Rust ## [Bevy Cargo Features][cargo_features] @@ -79,6 +97,43 @@ This [list][cargo_features] outlines the different cargo features supported by B [cargo_features]: docs/cargo_features.md +## [Third Party Plugins][plugin_guidelines] + +Plugins are very welcome to extend Bevy's features. [Guidelines][plugin_guidelines] are available to help integration and usage. + +[plugin_guidelines]: docs/plugins_guidelines.md + ## Thanks and Alternatives -Additionally, we would like to thank the [Amethyst](https://github.com/amethyst/amethyst), [macroquad](https://github.com/not-fl3/macroquad), [coffee](https://github.com/hecrj/coffee), [ggez](https://github.com/ggez/ggez), [rg3d](https://github.com/mrDIMAS/rg3d), and [Piston](https://github.com/PistonDevelopers/piston) projects for providing solid examples of game engine development in Rust. If you are looking for a Rust game engine, it is worth considering all of your options. Each engine has different design goals and some will likely resonate with you more than others. +Additionally, we would like to thank the [Amethyst](https://github.com/amethyst/amethyst), [macroquad](https://github.com/not-fl3/macroquad), [coffee](https://github.com/hecrj/coffee), [ggez](https://github.com/ggez/ggez), [Fyrox](https://github.com/FyroxEngine/Fyrox), and [Piston](https://github.com/PistonDevelopers/piston) projects for providing solid examples of game engine development in Rust. If you are looking for a Rust game engine, it is worth considering all of your options. Each engine has different design goals, and some will likely resonate with you more than others. + +## License + +Bevy is free, open source and permissively licensed! +Except where noted (below and/or in individual files), all code in this repository is dual-licensed under either: + +* MIT License ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) +* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) + +at your option. +This means you can select the license you prefer! +This dual-licensing approach is the de-facto standard in the Rust ecosystem and there are [very good reasons](https://github.com/bevyengine/bevy/issues/2373) to include both. + +Some of the engine's code carries additional copyright notices and license terms due to their external origins. +These are generally BSD-like, but exact details vary by crate: +If the README of a crate contains a 'License' header (or similar), the additional copyright notices and license terms applicable to that crate will be listed. +The above licensing requirement still applies to contributions to those crates, and sections of those crates will carry those license terms. +The [license](https://doc.rust-lang.org/cargo/reference/manifest.html#the-license-and-license-file-fields) field of each crate will also reflect this. +For example, [`bevy_mikktspace`](./crates/bevy_mikktspace/README.md#license-agreement) has code under the Zlib license (as well as a copyright notice when choosing the MIT license). + +The [assets](assets) included in this repository (for our [examples](./examples/README.md)) typically fall under different open licenses. +These will not be included in your game (unless copied in by you), and they are not distributed in the published bevy crates. +See [CREDITS.md](CREDITS.md) for the details of the licenses of those files. + +### Your contributions + +Unless you explicitly state otherwise, +any contribution intentionally submitted for inclusion in the work by you, +as defined in the Apache-2.0 license, +shall be dual licensed as above, +without any additional terms or conditions. diff --git a/assets/android-res/mipmap-mdpi/ic_launcher.png b/assets/android-res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000..5e72defd21325 Binary files /dev/null and b/assets/android-res/mipmap-mdpi/ic_launcher.png differ diff --git a/assets/branding/banner-text.png b/assets/branding/banner-text.png deleted file mode 100644 index 2f8b2198b4ddb..0000000000000 Binary files a/assets/branding/banner-text.png and /dev/null differ diff --git a/assets/branding/banner.png b/assets/branding/banner.png index 1d5fad437bdc3..3c5db79ac20f8 100644 Binary files a/assets/branding/banner.png and b/assets/branding/banner.png differ diff --git a/assets/branding/bevy_bird_simpleicons.svg b/assets/branding/bevy_bird_simpleicons.svg new file mode 100644 index 0000000000000..dc9c07bb9db74 --- /dev/null +++ b/assets/branding/bevy_bird_simpleicons.svg @@ -0,0 +1 @@ +Bevy Engine \ No newline at end of file diff --git a/assets/branding/bevy_logo_dark.png b/assets/branding/bevy_logo_dark.png index b7fcdd7ba40d6..3e2cbdf519829 100644 Binary files a/assets/branding/bevy_logo_dark.png and b/assets/branding/bevy_logo_dark.png differ diff --git a/assets/branding/bevy_logo_dark.svg b/assets/branding/bevy_logo_dark.svg index ab9bf8a71919d..241509d32d06b 100644 --- a/assets/branding/bevy_logo_dark.svg +++ b/assets/branding/bevy_logo_dark.svg @@ -1,19 +1,18 @@ + width="146.71918mm" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#"> + id="base" + inkscape:pagecheckerboard="0" /> - image/svg+xml - - - + rdf:about="" /> + d="m 2191.1465,2276.7832 c -5.9729,-0.035 -12.0979,2.348 -17.3613,7.459 -6.9129,6.7127 -9.0602,12.7555 -7.8477,20.2949 l 0.332,2.0684 -2.0664,-0.336 c -15.1877,-2.4609 -33.9847,-1.2178 -55.3711,7.4336 6.2868,2.6948 17.8259,7.1926 30.6309,13.3418 l 4.0605,1.9512 -4.414,0.8945 c -16.9087,3.4274 -36.9729,13.3275 -55.2989,34.9336 8.1981,-0.6372 24.9531,-2.6089 42.4278,-2.582 9.7138,0.015 19.2869,0.687 27.0859,2.709 7.7991,2.022 14.8874,6.6498 15.8861,10.0406 0.9987,3.3908 0.432,5.1761 -0.5519,7.8285 -0.9839,2.6524 -4.0098,6.6817 -8.1953,9.3418 -4.1855,2.6601 -9.4961,4.9849 -15.0137,6.9609 -11.0352,3.9521 -22.7798,6.4773 -27.9648,6.959 -1.1021,0.1024 -1.5421,0.4983 -1.9668,1.2696 -0.4247,0.7712 -0.659,1.9824 -0.6934,3.25 -0.046,1.6926 0.217,2.576 0.6949,3.246 0.4779,0.67 1.2243,0.9381 1.9934,0.9902 32.5822,2.2052 56.9441,-5.9907 74.6379,-13.0116 20.3508,-9.3311 33.2134,-27.7577 36.0058,-44.3477 1.7499,-10.395 1.3746,-15.4894 -0.3124,-19.8281 -1.6873,-4.3387 -4.9223,-8.1914 -9.0254,-15.5488 -2.6368,-4.7281 -4.1077,-9.367 -5.0196,-13.6875 l -0.1933,-0.9102 0.7265,-0.582 c 7.5403,-6.0446 13.6809,-12.6444 15.9102,-17.4492 -4.5742,-4.8648 -12.4787,-5.893 -21.3223,-4.9473 l -0.7265,0.076 -0.5118,-0.5215 c -4.7125,-4.8006 -10.5615,-7.2614 -16.5351,-7.2969 z m 2.6484,11.2324 c 2.593,-0.041 4.8808,1.7566 5.502,4.3223 0.7307,3.0216 -1.0812,6.0525 -4.0469,6.7695 -2.9656,0.7176 -5.9625,-1.1502 -6.6934,-4.1719 -0.7307,-3.0216 1.0812,-6.0525 4.0469,-6.7695 0.3902,-0.094 0.7897,-0.1445 1.1914,-0.1504 z" + transform="translate(5.0092774e-5,-757.87625)" + sodipodi:nodetypes="cccccccccccczzzcscczscccsccccccccccccccc" /> + viewBox="0 0 148.52753 37.72488" + height="37.72488mm" + width="148.52753mm" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#"> + id="defs2315" /> + inkscape:window-x="0" + inkscape:window-height="1440" + inkscape:window-width="3440" + showgrid="false" + inkscape:document-rotation="0" + inkscape:current-layer="layer1" + inkscape:document-units="mm" + inkscape:cy="91.696428" + inkscape:cx="126.96429" + inkscape:zoom="5.6" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:pagecheckerboard="0" /> + id="metadata2318"> - image/svg+xml - - - + rdf:about="" /> + inkscape:groupmode="layer" + inkscape:label="Layer 1"> + style="opacity:1;fill:#c3c3c3;fill-opacity:1;stroke:#dadada;stroke-width:1.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"> + transform="matrix(-0.55180403,-0.23802315,-0.23802315,0.55180403,1946.7322,-620.612)" /> + style="opacity:1;fill:#282828;fill-opacity:1;stroke:#dadada;stroke-width:4.15748;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"> + sodipodi:nodetypes="cccccccccccczzzcscczscccsccccccccccccccc" /> + id="g1121-7-7-2-3-3-7-4-5-8-2-5-9-5" + style="fill:#757575;fill-opacity:1;stroke:#dadada;stroke-width:2.1586;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"> + style="opacity:1;fill:#757575;fill-opacity:1;stroke:#dadada;stroke-width:4.15748;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"> + transform="translate(1.2499985e-4,-757.87627)" /> @@ -130,31 +128,31 @@ diff --git a/assets/branding/bevy_logo_light_small.svg b/assets/branding/bevy_logo_light_dark_and_dimmed.svg similarity index 59% rename from assets/branding/bevy_logo_light_small.svg rename to assets/branding/bevy_logo_light_dark_and_dimmed.svg index 4dfbd89630830..9d1e1e1ea5db1 100644 --- a/assets/branding/bevy_logo_light_small.svg +++ b/assets/branding/bevy_logo_light_dark_and_dimmed.svg @@ -1,161 +1,183 @@ + viewBox="0 0 148.52753 37.72488" + height="37.72488mm" + width="148.52753mm" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#"> + id="defs2315" /> + id="base" + inkscape:pagecheckerboard="0" /> + id="metadata2318"> - image/svg+xml - - - + rdf:about="" /> + + + + + inkscape:export-ydpi="81.422768" + style="opacity:1;fill:#c3c3c3;fill-opacity:1;stroke:#ffffff;stroke-width:1.1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"> + inkscape:export-xdpi="81.422768" + inkscape:export-ydpi="81.422768"> + transform="matrix(-0.55180403,-0.23802315,-0.23802315,0.55180403,1946.7322,-620.612)" /> + inkscape:export-ydpi="81.422768"> + style="opacity:1;fill:#282828;fill-opacity:1;stroke:#ffffff;stroke-width:4.15748;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"> + sodipodi:nodetypes="cccccccccccczzzcscczscccsccccccccccccccc" /> + inkscape:export-ydpi="81.422768"> + style="fill:#757575;fill-opacity:1;stroke:#ffffff;stroke-width:2.1586;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" + id="g1129-5-9-8-0-7-2-7-3-8-61-1-6-8" + transform="translate(-20.244579,-6.1209206)"> + style="fill:#757575;fill-opacity:1;stroke:#ffffff;stroke-width:2.1586;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" + id="g1344-1-3-9-3-8-8-8-3-5-4-8" + transform="translate(61.54776,-5.6726683)"> + id="g1121-7-7-2-3-3-7-4-5-8-2-5-9-5" + style="fill:#757575;fill-opacity:1;stroke:#ffffff;stroke-width:2.1586;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"> + style="opacity:1;fill:#757575;fill-opacity:1;stroke:#ffffff;stroke-width:4.15748;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill"> + transform="translate(1.2499985e-4,-757.87627)" /> + inkscape:export-ydpi="81.422768"> + d="m 87.399725,202.26207 q 0.344867,0.0985 0.886803,0.1478 0.541936,0.0247 1.034604,0.0247 0.985338,0 1.625808,-0.39414 0.665102,-0.41877 0.665102,-1.28094 0,-0.8129 -0.541936,-1.15776 -0.541936,-0.34488 -1.650441,-0.34488 h -2.01994 z m 0,-6.01056 h 1.79824 q 1.059239,0 1.527274,-0.39413 0.492668,-0.39414 0.492668,-1.15777 0,-0.6651 -0.56657,-1.05925 -0.541934,-0.39413 -1.650439,-0.39413 -0.369502,0 -0.86217,0.0247 -0.468036,0.0246 -0.739003,0.0739 z m 1.601173,9.48388 q -0.418769,0 -0.985337,-0.0247 -0.56657,-0.0246 -1.182405,-0.0985 -0.591203,-0.0739 -1.182406,-0.19708 -0.591202,-0.0985 -1.083871,-0.29559 -1.354839,-0.5173 -1.354839,-1.79824 v -11.50381 q 0,-0.51731 0.270969,-0.78828 0.2956,-0.2956 0.788269,-0.46804 0.837536,-0.29559 2.093842,-0.41877 1.256306,-0.1478 2.586512,-0.1478 3.153079,0 4.852786,1.05924 1.699706,1.05923 1.699706,3.27625 0,1.10851 -0.640469,1.92141 -0.640469,0.78827 -1.72434,1.15777 1.231672,0.34487 2.044575,1.25631 0.837537,0.91143 0.837537,2.29091 0,2.4387 -1.822875,3.62111 -1.79824,1.15778 -5.197654,1.15778 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.9748px;font-family:'Baloo Bhaijaan';-inkscape-font-specification:'Baloo Bhaijaan, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#282828;fill-opacity:1;stroke:#ffffff;stroke-width:0.626519;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" + id="path1940-83-0-6-3-1-1-8-3-1-6-8-3" + inkscape:connector-curvature="0" /> + d="m 96.48021,192.7043 q 0,-1.05924 0.615839,-1.67508 0.615835,-0.61582 1.675075,-0.61582 h 8.129006 q 0.17244,0.27096 0.29561,0.71435 0.1478,0.44341 0.1478,0.93609 0,0.93606 -0.41877,1.33019 -0.39413,0.39414 -1.05924,0.39414 h -5.19763 v 2.29091 h 5.5425 q 0.17243,0.27097 0.29561,0.71437 0.1478,0.41877 0.1478,0.91144 0,0.93607 -0.39414,1.3302 -0.39414,0.39413 -1.05925,0.39413 h -4.53252 v 2.58653 h 6.33077 q 0.17244,0.27095 0.2956,0.71435 0.1478,0.44341 0.1478,0.93608 0,0.93607 -0.41876,1.35484 -0.39415,0.39413 -1.05924,0.39413 h -7.192946 q -1.05924,0 -1.675075,-0.61583 -0.615839,-0.61584 -0.615839,-1.67508 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.9748px;font-family:'Baloo Bhaijaan';-inkscape-font-specification:'Baloo Bhaijaan, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#282828;fill-opacity:1;stroke:#ffffff;stroke-width:0.626519;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" + id="path1942-3-4-06-1-4-5-1-0-0-9-7-8" + inkscape:connector-curvature="0" /> + d="m 117.84176,204.84858 q -0.34487,0.2956 -1.08387,0.5173 -0.71437,0.22171 -1.57654,0.22171 -1.03461,0 -1.77361,-0.29561 -0.739,-0.32023 -1.00997,-0.86217 -0.27097,-0.56657 -0.66509,-1.45337 -0.39414,-0.91144 -0.83755,-2.01994 -0.41877,-1.10851 -0.8868,-2.34018 -0.4434,-1.2563 -0.86217,-2.53724 -0.41877,-1.28094 -0.78827,-2.51261 -0.3695,-1.23167 -0.64047,-2.31554 0.34487,-0.34487 0.98534,-0.61584 0.66511,-0.2956 1.40411,-0.2956 0.91144,0 1.47801,0.39414 0.59119,0.3695 0.86215,1.4041 0.66511,2.41408 1.33022,4.63109 0.68973,2.19237 1.42873,4.58182 h 0.1478 q 0.66511,-2.31555 1.35485,-5.04985 0.71437,-2.73432 1.35483,-5.444 0.44341,-0.2217 0.93607,-0.3695 0.51731,-0.1478 1.15777,-0.1478 0.91144,0 1.55191,0.41877 0.64047,0.39413 0.64047,1.3302 0,0.54195 -0.27097,1.57655 -0.24634,1.0346 -0.6651,2.34017 -0.39413,1.28094 -0.91143,2.68504 -0.49268,1.4041 -1.00997,2.66042 -0.49268,1.23168 -0.93608,2.19237 -0.4434,0.93607 -0.71437,1.30557 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.9748px;font-family:'Baloo Bhaijaan';-inkscape-font-specification:'Baloo Bhaijaan, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#282828;fill-opacity:1;stroke:#ffffff;stroke-width:0.626519;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" + id="path1944-3-1-2-9-9-5-0-9-7-3-1-5" + inkscape:connector-curvature="0" /> + d="m 131.98507,205.24272 q -0.27096,0.0739 -0.83753,0.1478 -0.56657,0.0739 -1.10851,0.0739 -1.13314,0 -1.69971,-0.36951 -0.56657,-0.39412 -0.56657,-1.6258 v -3.32552 q -0.61583,-0.91143 -1.3302,-2.01993 -0.71437,-1.10851 -1.4041,-2.26629 -0.68974,-1.15776 -1.28094,-2.26627 -0.5912,-1.13314 -0.96071,-2.0692 0.32024,-0.44342 0.86218,-0.81291 0.56657,-0.36951 1.37947,-0.36951 0.9607,0 1.5519,0.39414 0.61583,0.39413 1.15777,1.478 l 2.04458,4.11379 h 0.1478 q 0.34486,-0.76364 0.5912,-1.37947 0.27097,-0.64047 0.51731,-1.25631 0.24633,-0.64046 0.5173,-1.30557 0.27096,-0.68973 0.61583,-1.57654 0.4434,-0.2217 0.98534,-0.34487 0.54194,-0.12317 1.0346,-0.12317 0.86218,0 1.45337,0.46804 0.61584,0.44341 0.61584,1.35484 0,0.2956 -0.12317,0.71437 -0.12317,0.41877 -0.56656,1.28093 -0.44341,0.83754 -1.30558,2.29092 -0.83753,1.45337 -2.29091,3.79355 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.9748px;font-family:'Baloo Bhaijaan';-inkscape-font-specification:'Baloo Bhaijaan, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#282828;fill-opacity:1;stroke:#ffffff;stroke-width:0.626519;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:markers stroke fill" + id="path1946-80-0-6-4-2-4-3-2-5-7-7-6" + inkscape:connector-curvature="0" /> diff --git a/assets/branding/icon.png b/assets/branding/icon.png index 5e72defd21325..8c9640bf2793c 100644 Binary files a/assets/branding/icon.png and b/assets/branding/icon.png differ diff --git a/assets/branding/icon.svg b/assets/branding/icon.svg new file mode 100644 index 0000000000000..c7c0381928635 --- /dev/null +++ b/assets/branding/icon.svg @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/data/asset.custom b/assets/data/asset.custom new file mode 100644 index 0000000000000..0a53b2d09a586 --- /dev/null +++ b/assets/data/asset.custom @@ -0,0 +1,3 @@ +CustomAsset ( + value: 42 +) \ No newline at end of file diff --git a/assets/fonts/FiraMono-LICENSE b/assets/fonts/FiraMono-LICENSE new file mode 100644 index 0000000000000..5e4608f24a4b0 --- /dev/null +++ b/assets/fonts/FiraMono-LICENSE @@ -0,0 +1,93 @@ +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/models/AlienCake/alien.glb b/assets/models/AlienCake/alien.glb new file mode 100644 index 0000000000000..f61295e4b9b07 Binary files /dev/null and b/assets/models/AlienCake/alien.glb differ diff --git a/assets/models/AlienCake/cakeBirthday.glb b/assets/models/AlienCake/cakeBirthday.glb new file mode 100644 index 0000000000000..e65cdcfcfd7a0 Binary files /dev/null and b/assets/models/AlienCake/cakeBirthday.glb differ diff --git a/assets/models/AlienCake/tile.glb b/assets/models/AlienCake/tile.glb new file mode 100644 index 0000000000000..daf5ead75e6ee Binary files /dev/null and b/assets/models/AlienCake/tile.glb differ diff --git a/assets/models/FlightHelmet/FlightHelmet.bin b/assets/models/FlightHelmet/FlightHelmet.bin new file mode 100644 index 0000000000000..3a878be10d32e Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet.bin differ diff --git a/assets/models/FlightHelmet/FlightHelmet.gltf b/assets/models/FlightHelmet/FlightHelmet.gltf new file mode 100644 index 0000000000000..37687d8e9a3ab --- /dev/null +++ b/assets/models/FlightHelmet/FlightHelmet.gltf @@ -0,0 +1,705 @@ +{ + "asset": { + "version": "2.0", + "generator": "babylon.js glTF exporter for Maya 2018 v20200228.3 (with minor hand modifications)" + }, + "scene": 0, + "scenes": [ + { + "nodes": [ + 0, + 1, + 2, + 3, + 4, + 5 + ] + } + ], + "nodes": [ + { + "mesh": 0, + "name": "Hose_low" + }, + { + "mesh": 1, + "name": "RubberWood_low" + }, + { + "mesh": 2, + "name": "GlassPlastic_low" + }, + { + "mesh": 3, + "name": "MetalParts_low" + }, + { + "mesh": 4, + "name": "LeatherParts_low" + }, + { + "mesh": 5, + "name": "Lenses_low" + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 1, + "TANGENT": 2, + "NORMAL": 3, + "TEXCOORD_0": 4 + }, + "indices": 0, + "material": 0 + } + ], + "name": "Hose_low" + }, + { + "primitives": [ + { + "attributes": { + "POSITION": 6, + "TANGENT": 7, + "NORMAL": 8, + "TEXCOORD_0": 9 + }, + "indices": 5, + "material": 1 + } + ], + "name": "RubberWood_low" + }, + { + "primitives": [ + { + "attributes": { + "POSITION": 11, + "TANGENT": 12, + "NORMAL": 13, + "TEXCOORD_0": 14 + }, + "indices": 10, + "material": 2 + } + ], + "name": "GlassPlastic_low" + }, + { + "primitives": [ + { + "attributes": { + "POSITION": 16, + "TANGENT": 17, + "NORMAL": 18, + "TEXCOORD_0": 19 + }, + "indices": 15, + "material": 3 + } + ], + "name": "MetalParts_low" + }, + { + "primitives": [ + { + "attributes": { + "POSITION": 21, + "TANGENT": 22, + "NORMAL": 23, + "TEXCOORD_0": 24 + }, + "indices": 20, + "material": 4 + } + ], + "name": "LeatherParts_low" + }, + { + "primitives": [ + { + "attributes": { + "POSITION": 26, + "TANGENT": 27, + "NORMAL": 28, + "TEXCOORD_0": 29 + }, + "indices": 25, + "material": 5 + } + ], + "name": "Lenses_low" + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5123, + "count": 59040, + "type": "SCALAR", + "name": "accessorIndices" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 10472, + "max": [ + 0.10810829, + 0.356580257, + 0.190409869 + ], + "min": [ + -0.07221258, + 0.104120225, + -0.088394776 + ], + "type": "VEC3", + "name": "accessorPositions" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 10472, + "type": "VEC4", + "name": "accessorTangents" + }, + { + "bufferView": 1, + "byteOffset": 125664, + "componentType": 5126, + "count": 10472, + "type": "VEC3", + "name": "accessorNormals" + }, + { + "bufferView": 3, + "componentType": 5126, + "count": 10472, + "type": "VEC2", + "name": "accessorUVs" + }, + { + "bufferView": 0, + "byteOffset": 118080, + "componentType": 5123, + "count": 72534, + "type": "SCALAR", + "name": "accessorIndices" + }, + { + "bufferView": 1, + "byteOffset": 251328, + "componentType": 5126, + "count": 13638, + "max": [ + 0.162940636, + 0.7025226, + 0.200029165 + ], + "min": [ + -0.158857465, + -2.14242937E-05, + -0.171545789 + ], + "type": "VEC3", + "name": "accessorPositions" + }, + { + "bufferView": 2, + "byteOffset": 167552, + "componentType": 5126, + "count": 13638, + "type": "VEC4", + "name": "accessorTangents" + }, + { + "bufferView": 1, + "byteOffset": 414984, + "componentType": 5126, + "count": 13638, + "type": "VEC3", + "name": "accessorNormals" + }, + { + "bufferView": 3, + "byteOffset": 83776, + "componentType": 5126, + "count": 13638, + "type": "VEC2", + "name": "accessorUVs" + }, + { + "bufferView": 0, + "byteOffset": 263148, + "componentType": 5123, + "count": 24408, + "type": "SCALAR", + "name": "accessorIndices" + }, + { + "bufferView": 1, + "byteOffset": 578640, + "componentType": 5126, + "count": 4676, + "max": [ + 0.140494063, + 0.61828655, + 0.147373646 + ], + "min": [ + -0.140846014, + 0.440957, + -0.107818365 + ], + "type": "VEC3", + "name": "accessorPositions" + }, + { + "bufferView": 2, + "byteOffset": 385760, + "componentType": 5126, + "count": 4676, + "type": "VEC4", + "name": "accessorTangents" + }, + { + "bufferView": 1, + "byteOffset": 634752, + "componentType": 5126, + "count": 4676, + "type": "VEC3", + "name": "accessorNormals" + }, + { + "bufferView": 3, + "byteOffset": 192880, + "componentType": 5126, + "count": 4676, + "type": "VEC2", + "name": "accessorUVs" + }, + { + "bufferView": 0, + "byteOffset": 311964, + "componentType": 5123, + "count": 60288, + "type": "SCALAR", + "name": "accessorIndices" + }, + { + "bufferView": 1, + "byteOffset": 690864, + "componentType": 5126, + "count": 13636, + "max": [ + 0.132708371, + 0.6024364, + 0.199477077 + ], + "min": [ + -0.203642711, + 0.02116075, + -0.147512689 + ], + "type": "VEC3", + "name": "accessorPositions" + }, + { + "bufferView": 2, + "byteOffset": 460576, + "componentType": 5126, + "count": 13636, + "type": "VEC4", + "name": "accessorTangents" + }, + { + "bufferView": 1, + "byteOffset": 854496, + "componentType": 5126, + "count": 13636, + "type": "VEC3", + "name": "accessorNormals" + }, + { + "bufferView": 3, + "byteOffset": 230288, + "componentType": 5126, + "count": 13636, + "type": "VEC2", + "name": "accessorUVs" + }, + { + "bufferView": 0, + "byteOffset": 432540, + "componentType": 5123, + "count": 65688, + "type": "SCALAR", + "name": "accessorIndices" + }, + { + "bufferView": 1, + "byteOffset": 1018128, + "componentType": 5126, + "count": 12534, + "max": [ + 0.124933377, + 0.716000438, + 0.129168555 + ], + "min": [ + -0.125863016, + 0.2958266, + -0.1541516 + ], + "type": "VEC3", + "name": "accessorPositions" + }, + { + "bufferView": 2, + "byteOffset": 678752, + "componentType": 5126, + "count": 12534, + "type": "VEC4", + "name": "accessorTangents" + }, + { + "bufferView": 1, + "byteOffset": 1168536, + "componentType": 5126, + "count": 12534, + "type": "VEC3", + "name": "accessorNormals" + }, + { + "bufferView": 3, + "byteOffset": 339376, + "componentType": 5126, + "count": 12534, + "type": "VEC2", + "name": "accessorUVs" + }, + { + "bufferView": 0, + "byteOffset": 563916, + "componentType": 5123, + "count": 2208, + "type": "SCALAR", + "name": "accessorIndices" + }, + { + "bufferView": 1, + "byteOffset": 1318944, + "componentType": 5126, + "count": 436, + "max": [ + 0.101920746, + 0.5936986, + 0.152926728 + ], + "min": [ + -0.101920947, + 0.5300429, + 0.090174824 + ], + "type": "VEC3", + "name": "accessorPositions" + }, + { + "bufferView": 2, + "byteOffset": 879296, + "componentType": 5126, + "count": 436, + "type": "VEC4", + "name": "accessorTangents" + }, + { + "bufferView": 1, + "byteOffset": 1324176, + "componentType": 5126, + "count": 436, + "type": "VEC3", + "name": "accessorNormals" + }, + { + "bufferView": 3, + "byteOffset": 439648, + "componentType": 5126, + "count": 436, + "type": "VEC2", + "name": "accessorUVs" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 568332, + "name": "bufferViewScalar" + }, + { + "buffer": 0, + "byteOffset": 568332, + "byteLength": 1329408, + "byteStride": 12, + "name": "bufferViewFloatVec3" + }, + { + "buffer": 0, + "byteOffset": 1897740, + "byteLength": 886272, + "byteStride": 16, + "name": "bufferViewFloatVec4" + }, + { + "buffer": 0, + "byteOffset": 2784012, + "byteLength": 443136, + "byteStride": 8, + "name": "bufferViewFloatVec2" + } + ], + "buffers": [ + { + "uri": "FlightHelmet.bin", + "byteLength": 3227148 + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 2 + }, + "metallicRoughnessTexture": { + "index": 1 + } + }, + "normalTexture": { + "index": 0 + }, + "occlusionTexture": { + "index": 1 + }, + "doubleSided": true, + "name": "HoseMat" + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 2 + }, + "metallicRoughnessTexture": { + "index": 1 + } + }, + "normalTexture": { + "index": 0 + }, + "occlusionTexture": { + "index": 1 + }, + "name": "RubberWoodMat" + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 5 + }, + "metallicRoughnessTexture": { + "index": 4 + } + }, + "normalTexture": { + "index": 3 + }, + "occlusionTexture": { + "index": 4 + }, + "name": "GlassPlasticMat" + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 8 + }, + "metallicRoughnessTexture": { + "index": 7 + } + }, + "normalTexture": { + "index": 6 + }, + "occlusionTexture": { + "index": 7 + }, + "name": "MetalPartsMat" + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 11 + }, + "metallicRoughnessTexture": { + "index": 10 + } + }, + "normalTexture": { + "index": 9 + }, + "occlusionTexture": { + "index": 10 + }, + "name": "LeatherPartsMat" + }, + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 14 + }, + "metallicRoughnessTexture": { + "index": 13 + } + }, + "normalTexture": { + "index": 12 + }, + "occlusionTexture": { + "index": 13 + }, + "alphaMode": "BLEND", + "name": "LensesMat" + } + ], + "textures": [ + { + "sampler": 0, + "source": 0, + "name": "FlightHelmet_Materials_RubberWoodMat_Normal.png" + }, + { + "sampler": 0, + "source": 1, + "name": "FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png" + }, + { + "sampler": 0, + "source": 2, + "name": "FlightHelmet_Materials_RubberWoodMat_BaseColor.png" + }, + { + "sampler": 0, + "source": 3, + "name": "FlightHelmet_Materials_GlassPlasticMat_Normal.png" + }, + { + "sampler": 0, + "source": 4, + "name": "FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png" + }, + { + "sampler": 0, + "source": 5, + "name": "FlightHelmet_Materials_GlassPlasticMat_BaseColor.png" + }, + { + "sampler": 0, + "source": 6, + "name": "FlightHelmet_Materials_MetalPartsMat_Normal.png" + }, + { + "sampler": 0, + "source": 7, + "name": "FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png" + }, + { + "sampler": 0, + "source": 8, + "name": "FlightHelmet_Materials_MetalPartsMat_BaseColor.png" + }, + { + "sampler": 0, + "source": 9, + "name": "FlightHelmet_Materials_LeatherPartsMat_Normal.png" + }, + { + "sampler": 0, + "source": 10, + "name": "FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png" + }, + { + "sampler": 0, + "source": 11, + "name": "FlightHelmet_Materials_LeatherPartsMat_BaseColor.png" + }, + { + "sampler": 0, + "source": 12, + "name": "FlightHelmet_Materials_LensesMat_Normal.png" + }, + { + "sampler": 0, + "source": 13, + "name": "FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png" + }, + { + "sampler": 0, + "source": 14, + "name": "FlightHelmet_Materials_LensesMat_BaseColor.png" + } + ], + "images": [ + { + "uri": "FlightHelmet_Materials_RubberWoodMat_Normal.png" + }, + { + "uri": "FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png" + }, + { + "uri": "FlightHelmet_Materials_RubberWoodMat_BaseColor.png" + }, + { + "uri": "FlightHelmet_Materials_GlassPlasticMat_Normal.png" + }, + { + "uri": "FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png" + }, + { + "uri": "FlightHelmet_Materials_GlassPlasticMat_BaseColor.png" + }, + { + "uri": "FlightHelmet_Materials_MetalPartsMat_Normal.png" + }, + { + "uri": "FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png" + }, + { + "uri": "FlightHelmet_Materials_MetalPartsMat_BaseColor.png" + }, + { + "uri": "FlightHelmet_Materials_LeatherPartsMat_Normal.png" + }, + { + "uri": "FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png" + }, + { + "uri": "FlightHelmet_Materials_LeatherPartsMat_BaseColor.png" + }, + { + "uri": "FlightHelmet_Materials_LensesMat_Normal.png" + }, + { + "uri": "FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png" + }, + { + "uri": "FlightHelmet_Materials_LensesMat_BaseColor.png" + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987 + } + ] +} diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png b/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png new file mode 100644 index 0000000000000..f79eafe421608 Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_BaseColor.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png new file mode 100644 index 0000000000000..06e70ef10b61c Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_Normal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png new file mode 100644 index 0000000000000..3a03d7bb2ad40 Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_GlassPlasticMat_OcclusionRoughMetal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png b/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png new file mode 100644 index 0000000000000..dbee2d41accc0 Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_BaseColor.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png new file mode 100644 index 0000000000000..467e2ca9187fc Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_Normal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png new file mode 100644 index 0000000000000..24058ff8fc90d Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_LeatherPartsMat_OcclusionRoughMetal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_BaseColor.png b/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_BaseColor.png new file mode 100644 index 0000000000000..81b29d8e5eab4 Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_BaseColor.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_Normal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_Normal.png new file mode 100644 index 0000000000000..ed6502e341fcd Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_Normal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png new file mode 100644 index 0000000000000..bc7dd395a17c2 Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_LensesMat_OcclusionRoughMetal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png b/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png new file mode 100644 index 0000000000000..33b9159225d7d Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_BaseColor.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_Normal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_Normal.png new file mode 100644 index 0000000000000..b977bac8ca176 Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_Normal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png new file mode 100644 index 0000000000000..9cde90c3254b7 Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_MetalPartsMat_OcclusionRoughMetal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png b/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png new file mode 100644 index 0000000000000..c60cc95646c6f Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_BaseColor.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_Normal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_Normal.png new file mode 100644 index 0000000000000..bc5669cf8a3a5 Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_Normal.png differ diff --git a/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png b/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png new file mode 100644 index 0000000000000..f846b0551638e Binary files /dev/null and b/assets/models/FlightHelmet/FlightHelmet_Materials_RubberWoodMat_OcclusionRoughMetal.png differ diff --git a/assets/models/SimpleSkin/SimpleSkin.gltf b/assets/models/SimpleSkin/SimpleSkin.gltf new file mode 100644 index 0000000000000..6e68616c72762 --- /dev/null +++ b/assets/models/SimpleSkin/SimpleSkin.gltf @@ -0,0 +1 @@ +{"scenes":[{"nodes":[0]}],"nodes":[{"skin":0,"mesh":0,"children":[1]},{"children":[2],"translation":[0,1,0]},{"rotation":[0,0,0,1]}],"meshes":[{"primitives":[{"attributes":{"POSITION":1,"JOINTS_0":2,"WEIGHTS_0":3},"indices":0}]}],"skins":[{"inverseBindMatrices":4,"joints":[1,2]}],"animations":[{"channels":[{"sampler":0,"target":{"node":2,"path":"rotation"}}],"samplers":[{"input":5,"interpolation":"LINEAR","output":6}]}],"buffers":[{"uri":"data:application/gltf-buffer;base64,AAABAAMAAAADAAIAAgADAAUAAgAFAAQABAAFAAcABAAHAAYABgAHAAkABgAJAAgAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAD8AAAAAAACAPwAAAD8AAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAAAAAwD8AAAAAAACAPwAAwD8AAAAAAAAAAAAAAEAAAAAAAACAPwAAAEAAAAAA","byteLength":168},{"uri":"data:application/gltf-buffer;base64,AAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAABAPwAAgD4AAAAAAAAAAAAAQD8AAIA+AAAAAAAAAAAAAAA/AAAAPwAAAAAAAAAAAAAAPwAAAD8AAAAAAAAAAAAAgD4AAEA/AAAAAAAAAAAAAIA+AABAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAA=","byteLength":320},{"uri":"data:application/gltf-buffer;base64,AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAvwAAgL8AAAAAAACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAL8AAIC/AAAAAAAAgD8=","byteLength":128},{"uri":"data:application/gltf-buffer;base64,AAAAAAAAAD8AAIA/AADAPwAAAEAAACBAAABAQAAAYEAAAIBAAACQQAAAoEAAALBAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAPT9ND/0/TQ/AAAAAAAAAAD0/TQ/9P00PwAAAAAAAAAAkxjEPkSLbD8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAPT9NL/0/TQ/AAAAAAAAAAD0/TS/9P00PwAAAAAAAAAAkxjEvkSLbD8AAAAAAAAAAAAAAAAAAIA/","byteLength":240}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":48,"target":34963},{"buffer":0,"byteOffset":48,"byteLength":120,"target":34962},{"buffer":1,"byteOffset":0,"byteLength":320,"byteStride":16},{"buffer":2,"byteOffset":0,"byteLength":128},{"buffer":3,"byteOffset":0,"byteLength":240}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":24,"type":"SCALAR","max":[9],"min":[0]},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":10,"type":"VEC3","max":[1,2,0],"min":[0,0,0]},{"bufferView":2,"byteOffset":0,"componentType":5123,"count":10,"type":"VEC4","max":[0,1,0,0],"min":[0,1,0,0]},{"bufferView":2,"byteOffset":160,"componentType":5126,"count":10,"type":"VEC4","max":[1,1,0,0],"min":[0,0,0,0]},{"bufferView":3,"byteOffset":0,"componentType":5126,"count":2,"type":"MAT4","max":[1,0,0,0,0,1,0,0,0,0,1,0,-0.5,-1,0,1],"min":[1,0,0,0,0,1,0,0,0,0,1,0,-0.5,-1,0,1]},{"bufferView":4,"byteOffset":0,"componentType":5126,"count":12,"type":"SCALAR","max":[5.5],"min":[0]},{"bufferView":4,"byteOffset":48,"componentType":5126,"count":12,"type":"VEC4","max":[0,0,0.707,1],"min":[0,0,-0.707,0.707]}],"asset":{"version":"2.0"}} \ No newline at end of file diff --git a/assets/models/animated/Fox.glb b/assets/models/animated/Fox.glb new file mode 100644 index 0000000000000..2bb946e2d4815 Binary files /dev/null and b/assets/models/animated/Fox.glb differ diff --git a/assets/models/monkey/Monkey.bin b/assets/models/monkey/Monkey.bin deleted file mode 100644 index 472d25376e6fb..0000000000000 Binary files a/assets/models/monkey/Monkey.bin and /dev/null differ diff --git a/assets/models/monkey/Monkey.glb b/assets/models/monkey/Monkey.glb deleted file mode 100644 index 1998f1dbe1cf3..0000000000000 Binary files a/assets/models/monkey/Monkey.glb and /dev/null differ diff --git a/assets/models/monkey/Monkey.gltf b/assets/models/monkey/Monkey.gltf index 7a3836529d683..f7b34ed3f0c40 100644 --- a/assets/models/monkey/Monkey.gltf +++ b/assets/models/monkey/Monkey.gltf @@ -1,6 +1,6 @@ { "asset" : { - "generator" : "Khronos glTF Blender I/O v1.2.75", + "generator" : "Khronos glTF Blender I/O v1.3.48", "version" : "2.0" }, "scene" : 0, @@ -18,6 +18,27 @@ "name" : "Suzanne" } ], + "materials" : [ + { + "doubleSided" : true, + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Material", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.7612724900245667, + 0.5813313126564026, + 0.41983750462532043, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4000000059604645 + } + } + ], "meshes" : [ { "name" : "Suzanne", @@ -28,7 +49,8 @@ "NORMAL" : 1, "TEXCOORD_0" : 2 }, - "indices" : 3 + "indices" : 3, + "material" : 0 } ] } @@ -37,7 +59,7 @@ { "bufferView" : 0, "componentType" : 5126, - "count" : 3321, + "count" : 2012, "max" : [ 1.325934886932373, 0.9392361640930176, @@ -53,13 +75,13 @@ { "bufferView" : 1, "componentType" : 5126, - "count" : 3321, + "count" : 2012, "type" : "VEC3" }, { "bufferView" : 2, "componentType" : 5126, - "count" : 3321, + "count" : 2012, "type" : "VEC2" }, { @@ -72,29 +94,29 @@ "bufferViews" : [ { "buffer" : 0, - "byteLength" : 39852, + "byteLength" : 24144, "byteOffset" : 0 }, { "buffer" : 0, - "byteLength" : 39852, - "byteOffset" : 39852 + "byteLength" : 24144, + "byteOffset" : 24144 }, { "buffer" : 0, - "byteLength" : 26568, - "byteOffset" : 79704 + "byteLength" : 16096, + "byteOffset" : 48288 }, { "buffer" : 0, "byteLength" : 23616, - "byteOffset" : 106272 + "byteOffset" : 64384 } ], "buffers" : [ { - "byteLength" : 129888, - "uri" : "Monkey.bin" + "byteLength" : 88000, + "uri" : "data:application/octet-stream;base64,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NgA3AC0AMwA3AC0ANwAgADgAOQA6ADgAOgA7ADkAPAA9ADkAPQA6ADoAPQAwADoAMAAuADsAOgAuADsALgAoACAANwA+ACAAPgAhADcANgA/ADcAPwA+AD4APwBAAD4AQABBACEAPgBBACEAQQAiAEIAQwBEAEIARABFAEMAOAA7AEMAOwBEAEQAOwAoAEQAKAAlAEUARAAlAEUAJQAkADYARgBHADYARwA/AEYASABJAEYASQBHAEcASQBKAEcASgBLAD8ARwBLAD8ASwBAAEwATQBOAEwATgBPAE0AUABRAE0AUQBOAE4AUQA4AE4AOABDAE8ATgBDAE8AQwBCADQAUgBTADQAUwA1AFIAVABVAFIAVQBTAFMAVQBIAFMASABGADUAUwBGADUARgA2AFAAVgBXAFAAVwBRAFYAWABZAFYAWQBXAFcAWQA8AFcAPAA5AFEAVwA5AFEAOQA4AFQAWgBbAFQAWwBVAFoAXABdAFoAXQBbAFsAXQBeAFsAXgBfAFUAWwBfAFUAXwBIAGAAYQBiAGAAYgBjAGEAZABlAGEAZQBiAGIAZQBYAGIAWABWAGMAYgBWAGMAVgBQAEgAXwBmAEgAZgBJAF8AXgBnAF8AZwBmAGYAZwBoAGYAaABpAEkAZgBpAEkAaQBKAGoAawBsAGoAbABtAGsAYABjAGsAYwBsAGwAYwBQAGwAUABNAG0AbABNAG0ATQBMAF4AbgBvAF4AbwBnAG4AcABxAG4AcQBvAG8AcQByAG8AcgBzAGcAbwBzAGcAcwBoAHQAdQB2AHQAdgB3AHUAeAB5AHUAeQB2AHYAeQBgAHYAYABrAHcAdgBrAHcAawBqAFwAegB7AFwAewBdAHoAfAB9AHoAfQB7AHsAfQBwAHsAcABuAF0AewBuAF0AbgBeAHgAfgB/AHgAfwB5AH4AgACBAH4AgQB/AH8AgQBkAH8AZABhAHkAfwBhAHkAYQBgAHwAggCDAHwAgwB9AIIAhACFAIIAhQCDAIMAhQCGAIMAhgCHAH0AgwCHAH0AhwBwAIgAiQCKAIgAigCLAIkAjACNAIkAjQCKAIoAjQCAAIoAgAB+AIsAigB+AIsAfgB4AHAAhwCOAHAAjgBxAIcAhgCPAIcAjwCOAI4AjwCQAI4AkACRAHEAjgCRAHEAkQByAJIAkwCUAJIAlACVAJMAiACLAJMAiwCUAJQAiwB4AJQAeAB1AJUAlAB1AJUAdQB0AIYAlgCXAIYAlwCPAJYACAATAJYAEwCXAJcAEwAXAJcAFwCYAI8AlwCYAI8AmACQAB0AHACZAB0AmQCaABwAEQCbABwAmwCZAJkAmwCIAJkAiACTAJoAmQCTAJoAkwCSAIQAnACdAIQAnQCFAJwAAAADAJwAAwCdAJ0AAwAIAJ0ACACWAIUAnQCWAIUAlgCGABEAEACeABEAngCbABAADwCfABAAnwCeAJ4AnwCMAJ4AjACJAJsAngCJAJsAiQCIAAAAnACgAAAAoAChAJwAhACiAJwAogCgAKAAogCjAKAAowCkAKEAoACkAKEApAClAKYApwCoAKYAqACpAKcAjACfAKcAnwCoAKgAnwAPAKgADwCqAKkAqACqAKkAqgCrAIQAggCsAIQArACiAIIAfACtAIIArQCsAKwArQCuAKwArgCvAKIArACvAKIArwCjALAAsQCyALAAsgCzALEAgACNALEAjQCyALIAjQCMALIAjACnALMAsgCnALMApwCmAHwAegC0AHwAtACtAHoAXAC1AHoAtQC0ALQAtQC2ALQAtgC3AK0AtAC3AK0AtwCuALgAuQC6ALgAugC7ALkAZACBALkAgQC6ALoAgQCAALoAgACxALsAugCxALsAsQCwAFwAWgC8AFwAvAC1AFoAVAC9AFoAvQC8ALwAvQC+ALwAvgC/ALUAvAC/ALUAvwC2AMAAwQDCAMAAwgDDAMEAWABlAMEAZQDCAMIAZQBkAMIAZAC5AMMAwgC5AMMAuQC4AFQAUgDEAFQAxAC9AFIANADFAFIAxQDEAMQAxQDGAMQAxgDHAL0AxADHAL0AxwC+AMgAyQDKAMgAygDLAMkAPABZAMkAWQDKAMoAWQBYAMoAWADBAMsAygDBAMsAwQDAADQAMgDMADQAzADFADIALADNADIAzQDMAMwAzQDOAMwAzgDPAMUAzADPAMUAzwDGANAA0QDSANAA0gDTANEAMAA9ANEAPQDSANIAPQA8ANIAPADJANMA0gDJANMAyQDIACwAKgDUACwA1ADNACoABADVACoA1QDUANQA1QDWANQA1gDXAM0A1ADXAM0A1wDOANgA2QDaANgA2gDbANkADQAxANkAMQDaANoAMQAwANoAMADRANsA2gDRANsA0QDQAAQAAQDcAAQA3ADVAAEAAAChAAEAoQDcANwAoQClANwApQDdANUA3ADdANUA3QDWAKsAqgDeAKsA3gDfAKoADwAOAKoADgDeAN4ADgANAN4ADQDZAN8A3gDZAN8A2QDYAOAA4QDiAOAA4gDjANYA3QDiANYA4gDhAKUA4wDiAKUA4gDdAKsA3wDkAKsA5ADlANgA5gDkANgA5ADfAOcA5QDkAOcA5ADmAM4A1wDoAM4A6ADpANYA4QDoANYA6ADXAOAA6QDoAOAA6ADhAOcA5gDqAOcA6gDrANgA2wDqANgA6gDmANAA6wDqANAA6gDbAOAA7ADtAOAA7QDpAMYAzwDtAMYA7QDsAM4A6QDtAM4A7QDPANAA0wDuANAA7gDrAMgA7wDuAMgA7gDTAOcA6wDuAOcA7gDvAOAA8ADxAOAA8QDsAL4AxwDxAL4A8QDwAMYA7ADxAMYA8QDHAMgAywDyAMgA8gDvAMAA8wDyAMAA8gDLAOcA7wDyAOcA8gDzAOAA9AD1AOAA9QDwALYAvwD1ALYA9QD0AL4A8AD1AL4A9QC/AMAAwwD2AMAA9gDzALgA9wD2ALgA9gDDAOcA8wD2AOcA9gD3AOAA+AD5AOAA+QD0AK4AtwD5AK4A+QD4ALYA9AD5ALYA+QC3ALgAuwD6ALgA+gD3ALAA+wD6ALAA+gC7AOcA9wD6AOcA+gD7AOAA/AD9AOAA/QD4AKMArwD9AKMA/QD8AK4A+AD9AK4A/QCvALAAswD+ALAA/gD7AKYA/wD+AKYA/gCzAOcA+wD+AOcA/gD/AOAA4wAAAeAAAAH8AKUApAAAAaUAAAHjAKMA/AAAAaMAAAGkAKYAqQABAaYAAQH/AKsA5QABAasAAQGpAOcA/wABAecAAQHlAAIBAwEEAQIBBAEFAQMBBgEHAQMBBwEEAQQBBwEIAQQBCAEJAQUBBAEJAQUBCQEKAQgBCwEMAQgBDAEJAQsBDQEOAQsBDgEMAQwBDgEPAQwBDwEQAQkBDAEQAQkBEAEKAREBEgETAREBEwEUARIBFQEWARIBFgETARMBFgEGARMBBgEDARQBEwEDARQBAwECAQ0BFwEYAQ0BGAEOARcBGQEaARcBGgEYARgBGgEbARgBGwEcAQ4BGAEcAQ4BHAEPAR0BHgEfAR0BHwEgAR4BIQEiAR4BIgEfAR8BIgEVAR8BFQESASABHwESASABEgERARkBIwEkARkBJAEaASMBJQEmASMBJgEkASQBJgEnASQBJwEoARoBJAEoARoBKAEbASkBKgErASkBKwEsASoBLQEuASoBLgErASsBLgEhASsBIQEeASwBKwEeASwBHgEdASUBLwEwASUBMAEmAS8BMQEyAS8BMgEwATABMgEzATABMwE0ASYBMAE0ASYBNAEnATUBNgE3ATUBNwE4ATYBOQE6ATYBOgE3ATcBOgEtATcBLQEqATgBNwEqATgBKgEpATEBOwE8ATEBPAEyATsBPQE+ATsBPgE8ATwBPgE/ATwBPwFAATIBPAFAATIBQAEzAUEBQgFDAUEBQwFEAUIBRQFGAUIBRgFDAUMBRgFHAUMBRwFIAUQBQwFIAUQBSAFJAUoBSwFMAUoBTAFNAUsBTgFPAUsBTwFMAUwBTwFQAUwBUAFRAU0BTAFRAU0BUQFSAUUBUwFUAUUBVAFGAVMBVQFWAVMBVgFUAVQBVgFXAVQBVwFYAUYBVAFYAUYBWAFHAVkBWgFbAVkBWwFcAVoBXQFeAVoBXgFbAVsBXgFOAVsBTgFLAVwBWwFLAVwBSwFKAVUBXwFgAVUBYAFWAV8BYQFiAV8BYgFgAWABYgFjAWABYwFkAVYBYAFkAVYBZAFXAWUBZgFnAWUBZwFoAWYBaQFqAWYBagFnAWcBagFdAWcBXQFaAWgBZwFaAWgBWgFZAWEBawFsAWEBbAFiAWsBbQFuAWsBbgFsAWwBbgFvAWwBbwFwAWIBbAFwAWIBcAFjAXEBcgFzAXEBcwF0AXIBdQF2AXIBdgFzAXMBdgFpAXMBaQFmAXQBcwFmAXQBZgFlAW0BdwF4AW0BeAFuAXcBeQF6AXcBegF4AXgBegF7AXgBewF8AW4BeAF8AW4BfAFvAX0BfgF/AX0BfwGAAX4BgQGCAX4BggF/AX8BggF1AX8BdQFyAYABfwFyAYABcgFxAXkBgwGEAXkBhAF6AYMBhQGGAYMBhgGEAYQBhgGHAYQBhwGIAXoBhAGIAXoBiAF7AYkBigGLAYkBiwGMAYoBjQGOAYoBjgGLAYsBjgGBAYsBgQF+AYwBiwF+AYwBfgF9AYUBjwGQAYUBkAGGAY8BkQGSAY8BkgGQAZABkgGTAZABkwGUAYYBkAGUAYYBlAGHAZUBlgGXAZUBlwGYAZYBmQGaAZYBmgGXAZcBmgGNAZcBjQGKAZgBlwGKAZgBigGJAZEBmwGcAZEBnAGSAZsBnQGeAZsBngGcAZwBngGfAZwBnwGgAZIBnAGgAZIBoAGTAaEBogGjAaEBowGkAaIBpQGmAaIBpgGjAaMBpgGZAaMBmQGWAaQBowGWAaQBlgGVAZ0BpwGoAZ0BqAGeAacBqQGqAacBqgGoAagBqgGrAagBqwGsAZ4BqAGsAZ4BrAGfAa0BrgGvAa0BrwGwAa4BsQGyAa4BsgGvAa8BsgGlAa8BpQGiAbABrwGiAbABogGhAakBswG0AakBtAGqAbMBtQG2AbMBtgG0AbQBtgG3AbQBtwG4AaoBtAG4AaoBuAGrAbcBtgG5AbcBuQG6AbYBtQG7AbYBuwG5AbkBuwGxAbkBsQGuAboBuQGuAboBrgGtAbwBvQG+AbwBvgG/Ab0BwAHBAb0BwQG+Ab4BwQGfAb4BnwGsAb8BvgGsAb8BrAGrAaEBwgHDAaEBwwGwAcIBxAHFAcIBxQHDAcMBxQHGAcMBxgHHAbABwwHHAbABxwGtAcAByAHJAcAByQHBAcgBygHLAcgBywHJAckBywGTAckBkwGgAcEByQGgAcEBoAGfAZUBzAHNAZUBzQGkAcwBzgHPAcwBzwHNAc0BzwHEAc0BxAHCAaQBzQHCAaQBwgGhAdAB0QHSAdAB0gHTAdEBhwGUAdEBlAHSAdIBlAGTAdIBkwHLAdMB0gHLAdMBywHKAZUBmAHUAZUB1AHMAZgBiQHVAZgB1QHUAdQB1QHWAdQB1gHXAcwB1AHXAcwB1wHOAdgB2QHaAdgB2gHbAdkBewGIAdkBiAHaAdoBiAGHAdoBhwHRAdsB2gHRAdsB0QHQAYkBjAHcAYkB3AHVAYwBfQHdAYwB3QHcAdwB3QHeAdwB3gHfAdUB3AHfAdUB3wHWAeAB4QHiAeAB4gHjAeEBbwF8AeEBfAHiAeIBfAF7AeIBewHZAeMB4gHZAeMB2QHYAX0BgAHkAX0B5AHdAYABcQHlAYAB5QHkAeQB5QHmAeQB5gHnAd0B5AHnAd0B5wHeAegB6QHqAegB6gHrAekBYwFwAekBcAHqAeoBcAFvAeoBbwHhAesB6gHhAesB4QHgAXEBdAHsAXEB7AHlAXQBZQHtAXQB7QHsAewB7QHuAewB7gHvAeUB7AHvAeUB7wHmAfAB8QHyAfAB8gHzAfEBVwFkAfEBZAHyAfIBZAFjAfIBYwHpAfMB8gHpAfMB6QHoAWUBaAH0AWUB9AHtAWgBWQH1AWgB9QH0AfQB9QH2AfQB9gH3Ae0B9AH3Ae0B9wHuAfgB+QH6AfgB+gH7AfkBRwFYAfkBWAH6AfoBWAFXAfoBVwHxAfsB+gHxAfsB8QHwAVkBXAH8AVkB/AH1AVwBSgH9AVwB/QH8AfwB/QH+AfwB/gH/AfUB/AH/AfUB/wH2AQACAQICAgACAgIDAgECSQFIAQECSAECAgICSAFHAQICRwH5AQMCAgL5AQMC+QH4AUoBTQEEAkoBBAL9AU0BUgEFAk0BBQIEAgQCBQIGAgQCBgIHAv0BBAIHAv0BBwL+AQACCAIJAgACCQIBAggCCgILAggCCwIJAgkCCwIMAgkCDAINAgECCQINAgECDQJJAQwCDgIPAgwCDwIQAg4CEQISAg4CEgIPAg8CEgIGAg8CBgIFAhACDwIFAhACBQJSAbwBvwETArwBEwIUAr8BqwG4Ab8BuAETAhMCuAG3ARMCtwEVAhQCEwIVAhQCFQIWArcBugEXArcBFwIYAroBrQHHAboBxwEXAhcCxwHGARcCxgEZAhgCFwIZAhgCGQIaAhYCFQIbAhYCGwIcAhUCtwEdAhUCHQIbAhsCHQIeAhsCHgIfAhwCGwIfAhwCHwIgAh4CHQIhAh4CIQIiAh0CtwEYAh0CGAIhAiECGAIaAiECGgIjAiICIQIjAiICIwIkAgwCCwIlAgwCJQImAgsCCgInAgsCJwIlAiUCJwIgAiUCIAIfAiYCJQIfAiYCHwIeAiQCKAIpAiQCKQIiAigCEQIOAigCDgIpAikCDgIMAikCDAImAiICKQImAiICJgIeAioCKwIsAioCLAItAisCLgIvAisCLwIsAiwCLwIIASwCCAEHAS0CLAIHAS0CBwEGAQgBLwIwAggBMAILAS8CLgIxAi8CMQIwAjACMQIyAjACMgIzAgsBMAIzAgsBMwINATQCNQI2AjQCNgI3AjUCKgItAjUCLQI2AjYCLQIGATYCBgEWATcCNgIWATcCFgEVAQ0BMwI4Ag0BOAIXATMCMgI5AjMCOQI4AjgCOQI6AjgCOgI7AhcBOAI7AhcBOwIZATwCPQI+AjwCPgI/Aj0CNAI3Aj0CNwI+Aj4CNwIVAT4CFQEiAT8CPgIiAT8CIgEhARkBOwJAAhkBQAIjATsCOgJBAjsCQQJAAkACQQJCAkACQgJDAiMBQAJDAiMBQwIlATkBRAJFAjkBRQI6AUQCRgJHAkQCRwJFAkUCRwJIAkUCSAJJAjoBRQJJAjoBSQItAUoCSwJMAkoCTAJNAksCTgJPAksCTwJMAkwCTwI9AUwCPQE7AU0CTAI7AU0COwExATwCPwJQAjwCUAJRAj8CIQEuAT8CLgFQAlACLgEtAVACLQFJAlECUAJJAlECSQJIAjEBLwFSAjEBUgJNAi8BJQFDAi8BQwJSAlICQwJCAlICQgJTAk0CUgJTAk0CUwJKAlQCVQJWAlQCVgJXAlUCWAJZAlUCWQJWAlYCWQJaAlYCWgJbAlcCVgJbAlcCWwJGAloCWQJcAloCXAJdAlkCWAJeAlkCXgJcAlwCXgJfAlwCXwJgAl0CXAJgAl0CYAJOAkgCRwJhAkgCYQJiAkcCRgJbAkcCWwJhAmECWwJaAmECWgJjAmICYQJjAmICYwJkAloCXQJlAloCZQJjAl0CTgJLAl0CSwJlAmUCSwJKAmUCSgJmAmMCZQJmAmMCZgJkAmcCaAJpAmcCaQJqAmgCPAJRAmgCUQJpAmkCUQJIAmkCSAJiAmoCaQJiAmoCYgJkAkoCUwJrAkoCawJmAlMCQgJsAlMCbAJrAmsCbAJnAmsCZwJqAmYCawJqAmYCagJkAm0CbgJvAm0CbwJwAm4CcQJyAm4CcgJvAm8CcgJzAm8CcwJ0AnACbwJ0AnACdAJUAnUCdgJ3AnUCdwJ4AnYCeQJ6AnYCegJ3AncCegJ7AncCewJ8AngCdwJ8AngCfAJfAn0CfgJ/An0CfwKAAn4CgQKCAn4CggJ/An8CggJxAn8CcQJuAoACfwJuAoACbgJtAnkCgwKEAnkChAJ6AoMChQKGAoMChgKEAoQChgKHAoQChwKIAnoChAKIAnoCiAJ7AokCigKLAokCiwKMAooCjQKOAooCjgKLAosCjgKBAosCgQJ+AowCiwJ+AowCfgJ9AoUCjwKQAoUCkAKGAo8CkQKSAo8CkgKQApACkgKJApACiQKTAoYCkAKTAoYCkwKHApQClQKWApQClgKXApUCmAKZApUCmQKWApYCmQKNApYCjQKKApcClgKKApcCigKJApECmgKbApECmwKSApoCmAKVApoClQKbApsClQKUApsClAKXApICmwKXApIClwKJAlgCVQKcAlgCnAKdAlUCVAJ0AlUCdAKcApwCdAJzApwCcwKeAp0CnAKeAp0CngKfAnUCeAKgAnUCoAKhAngCXwJeAngCXgKgAqACXgJYAqACWAKdAqECoAKdAqECnQKfAp8CngKiAp8CogKjAp4CcwKkAp4CpAKiAqICpAKlAqICpQKmAqMCogKmAqMCpgKnAqgCqQKqAqgCqgKrAqkCdQKhAqkCoQKqAqoCoQKfAqoCnwKjAqsCqgKjAqsCowKnApgCrAKtApgCrQKZAqwCrgKvAqwCrwKtAq0CrwKwAq0CsAKxApkCrQKxApkCsQKNArICswK0ArICtAK1ArMCrgKsArMCrAK0ArQCrAKYArQCmAKaArUCtAKaArUCmgKRAo0CsQK2Ao0CtgKOArECsAK3ArECtwK2ArYCtwK4ArYCuAK5Ao4CtgK5Ao4CuQKBAroCuwK8AroCvAK9ArsCsgK1ArsCtQK8ArwCtQKRArwCkQKPAr0CvAKPAr0CjwKFAoECuQK+AoECvgKCArkCuAK/ArkCvwK+Ar4CvwLAAr4CwALBAoICvgLBAoICwQJxAsICwwLEAsICxALFAsMCugK9AsMCvQLEAsQCvQKFAsQChQKDAsUCxAKDAsUCgwJ5AnECwQLGAnECxgJyAsECwALHAsECxwLGAsYCxwKlAsYCpQKkAnICxgKkAnICpAJzAqgCyALJAqgCyQKpAsgCwgLFAsgCxQLJAskCxQJ5AskCeQJ2AqkCyQJ2AqkCdgJ1Aq4CygLLAq4CywKvAsoCwAK/AsoCvwLLAssCvwK4AssCuAK3Aq8CywK3Aq8CtwKwAroCwwLMAroCzAK7AsMCwgLNAsMCzQLMAswCzQKuAswCrgKzArsCzAKzArsCswKyAq4CzgLPAq4CzwLKAs4CpwKmAs4CpgLPAs8CpgKlAs8CpQLHAsoCzwLHAsoCxwLAAqgCqwLQAqgC0ALIAqsCpwLOAqsCzgLQAtACzgKuAtACrgLNAsgC0ALNAsgCzQLCAokCjALRAokC0QLSAowCfQLTAowC0wLRAtEC0wJJAdECSQENAtIC0QINAtICDQIMAlIB1ALVAlIB1QIQAtQChwKTAtQCkwLVAtUCkwKJAtUCiQLSAhAC1QLSAhAC0gIMAn0CgALWAn0C1gLTAoACbQLXAoAC1wLWAtYC1wLYAtYC2ALZAtMC1gLZAtMC2QJJAdoC2wLcAtoC3ALdAtsCewKIAtsCiALcAtwCiAKHAtwChwLUAt0C3ALUAt0C1AJSAW0CcALeAm0C3gLXAnACVALfAnAC3wLeAt4C3wLgAt4C4ALhAtcC3gLhAtcC4QLYAuIC4wLkAuIC5ALlAuMCXwJ8AuMCfALkAuQCfAJ7AuQCewLbAuUC5ALbAuUC2wLaAlQCVwLmAlQC5gLfAlcCRgJEAlcCRALmAuYCRAI5AeYCOQHnAt8C5gLnAt8C5wLgAj0BTwLoAj0B6ALpAk8CTgJgAk8CYALoAugCYAJfAugCXwLjAukC6ALjAukC4wLiAjUB6gLrAjUB6wI2AeoC7ALtAuoC7QLrAusC7QLgAusC4ALnAjYB6wLnAjYB5wI5AeIC7gLvAuIC7wLpAu4C8ALxAu4C8QLvAu8C8QI/Ae8CPwE+AekC7wI+AekCPgE9AewC8gLzAuwC8wLtAvIC9AL1AvIC9QLzAvMC9QLYAvMC2ALhAu0C8wLhAu0C4QLgAtoC9gL3AtoC9wLlAvYC+AL5AvYC+QL3AvcC+QLwAvcC8ALuAuUC9wLuAuUC7gLiAkEBRAH6AkEB+gL7AkQBSQHZAkQB2QL6AvoC2QLYAvoC2AL1AvsC+gL1AvsC9QL0AtoC3QL8AtoC/AL2At0CUgFRAd0CUQH8AvwCUQFQAfwCUAH9AvYC/AL9AvYC/QL4AjwCaAL+AjwC/gL/AmgCZwIAA2gCAAP+Av4CAAMBA/4CAQMCA/8C/gICA/8CAgMDAwEDAAMEAwEDBAMFAwADZwJsAgADbAIEAwQDbAJCAgQDQgIGAwUDBAMGAwUDBgMHAzQCPQIIAzQCCAMJAz0CPAL/Aj0C/wIIAwgD/wIDAwgDAwMKAwkDCAMKAwkDCgMLAwcDBgMMAwcDDAMNAwYDQgJBAgYDQQIMAwwDQQI6AgwDOgIOAw0DDAMOAw0DDgMPAyoCNQIQAyoCEAMRAzUCNAIJAzUCCQMQAxADCQMLAxADCwMSAxEDEAMSAxEDEgMTAw8DDgMUAw8DFAMVAw4DOgI5Ag4DOQIUAxQDOQIyAhQDMgIWAxUDFAMWAxUDFgMXAy4CKwIYAy4CGAMZAysCKgIRAysCEQMYAxgDEQMTAxgDEwMaAxkDGAMaAxkDGgMbAxcDFgMcAxcDHAMdAxYDMgIxAhYDMQIcAxwDMQIuAhwDLgIZAx0DHAMZAx0DGQMbAxsDGgMeAxsDHgMfAxoDEwMgAxoDIAMeAx4DIAMhAx4DIQMiAx8DHgMiAx8DIgMjAyQDJQMmAyQDJgMnAyUDFwMdAyUDHQMmAyYDHQMbAyYDGwMfAycDJgMfAycDHwMjAxMDEgMoAxMDKAMgAxIDCwMpAxIDKQMoAygDKQMqAygDKgMrAyADKAMrAyADKwMhAywDLQMuAywDLgMvAy0DDwMVAy0DFQMuAy4DFQMXAy4DFwMlAy8DLgMlAy8DJQMkAwsDCgMwAwsDMAMpAwoDAwMxAwoDMQMwAzADMQMyAzADMgMzAykDMAMzAykDMwMqAzQDNQM2AzQDNgM3AzUDBwMNAzUDDQM2AzYDDQMPAzYDDwMtAzcDNgMtAzcDLQMsAwMDAgM4AwMDOAMxAwIDAQM5AwIDOQM4AzgDOQM6AzgDOgM7AzEDOAM7AzEDOwMyAzoDOQM8AzoDPAM9AzkDAQMFAzkDBQM8AzwDBQMHAzwDBwM1Az0DPAM1Az0DNQM0AzoDPgM/AzoDPwM7Az4DIwMiAz4DIgM/Az8DIgMhAz8DIQNAAzsDPwNAAzsDQAMyAyQDJwNBAyQDQQNCAycDIwM+AycDPgNBA0EDPgM6A0EDOgM9A0IDQQM9A0IDPQM0AzIDQANDAzIDQwMzAyEDKwNDAyEDQwNAAyoDMwNDAyoDQwMrAywDLwNEAywDRAM3AyQDQgNEAyQDRAMvAzQDNwNEAzQDRANCAyACJwJFAyACRQNGAycCCgJHAycCRwNFA0UDRwNIA0UDSANJA0YDRQNJA0YDSQNKA0sDTANNA0sDTQNOA0wDEQIoAkwDKAJNA00DKAIkAk0DJAJPA04DTQNPA04DTwNQAxYCHAJRAxYCUQNSAxwCIAJGAxwCRgNRA1EDRgNKA1EDSgNTA1IDUQNTA1IDUwNUA1ADTwNVA1ADVQNWA08DJAIjAk8DIwJVA1UDIwIaAlUDGgJXA1YDVQNXA1YDVwNYA7wBFAJZA7wBWQNaAxQCFgJSAxQCUgNZA1kDUgNUA1kDVANbA1oDWQNbA1oDWwNcA1gDVwNdA1gDXQNeA1cDGgIZAlcDGQJdA10DGQLGAV0DxgFfA14DXQNfA14DXwNgAwoCCAJhAwoCYQNHAwgCAAJiAwgCYgNhA2EDYgNjA2EDYwNkA0cDYQNkA0cDZANIA2UDZgNnA2UDZwNoA2YDBgISAmYDEgJnA2cDEgIRAmcDEQJMA2gDZwNMA2gDTANLAwACAwJpAwACaQNiAwMC+AFqAwMCagNpA2kDagNrA2kDawNsA2IDaQNsA2IDbANjA20DbgNvA20DbwNwA24D/gEHAm4DBwJvA28DBwIGAm8DBgJmA3ADbwNmA3ADZgNlA/gB+wFxA/gBcQNqA/sB8AFyA/sBcgNxA3EDcgNzA3EDcwN0A2oDcQN0A2oDdANrA3UDdgN3A3UDdwN4A3YD9gH/AXYD/wF3A3cD/wH+AXcD/gFuA3gDdwNuA3gDbgNtA/AB8wF5A/ABeQNyA/MB6AF6A/MBegN5A3kDegN7A3kDewN8A3IDeQN8A3IDfANzA30DfgN/A30DfwOAA34D7gH3AX4D9wF/A38D9wH2AX8D9gF2A4ADfwN2A4ADdgN1A+gB6wGBA+gBgQN6A+sB4AGCA+sBggOBA4EDggODA4EDgwOEA3oDgQOEA3oDhAN7A4UDhgOHA4UDhwOIA4YD5gHvAYYD7wGHA4cD7wHuAYcD7gF+A4gDhwN+A4gDfgN9A+AB4wGJA+ABiQOCA+MB2AGKA+MBigOJA4kDigOLA4kDiwOMA4IDiQOMA4IDjAODA40DjgOPA40DjwOQA44D3gHnAY4D5wGPA48D5wHmAY8D5gGGA5ADjwOGA5ADhgOFA9gB2wGRA9gBkQOKA9sB0AGSA9sBkgORA5EDkgOTA5EDkwOUA4oDkQOUA4oDlAOLA5UDlgOXA5UDlwOYA5YD1gHfAZYD3wGXA5cD3wHeAZcD3gGOA5gDlwOOA5gDjgONA9AB0wGZA9ABmQOSA9MBygGaA9MBmgOZA5kDmgObA5kDmwOcA5IDmQOcA5IDnAOTA50DngOfA50DnwOgA54DzgHXAZ4D1wGfA58D1wHWAZ8D1gGWA6ADnwOWA6ADlgOVA8oByAGhA8oBoQOaA8gBwAGiA8gBogOhA6EDogOjA6EDowOkA5oDoQOkA5oDpAObA6UDpgOnA6UDpwOoA6YDxAHPAaYDzwGnA6cDzwHOAacDzgGeA6gDpwOeA6gDngOdA8ABvQGpA8ABqQOiA70BvAFaA70BWgOpA6kDWgNcA6kDXAOqA6IDqQOqA6IDqgOjA2ADXwOrA2ADqwOsA18DxgHFAV8DxQGrA6sDxQHEAasDxAGmA6wDqwOmA6wDpgOlA6MDqgOtA6MDrQOuA6oDXAOvA6oDrwOtA60DrwOwA60DsAOxA64DrQOxA64DsQOyA7MDtAO1A7MDtQO2A7QDYAOsA7QDrAO1A7UDrAOlA7UDpQO3A7YDtQO3A7YDtwO4A5sDpAO5A5sDuQO6A6QDowOuA6QDrgO5A7kDrgOyA7kDsgO7A7oDuQO7A7oDuwO8A7gDtwO9A7gDvQO+A7cDpQOoA7cDqAO9A70DqAOdA70DnQO/A74DvQO/A74DvwPAA5MDnAPBA5MDwQPCA5wDmwO6A5wDugPBA8EDugO8A8EDvAPDA8IDwQPDA8IDwwPEA8ADvwPFA8ADxQPGA78DnQOgA78DoAPFA8UDoAOVA8UDlQPHA8YDxQPHA8YDxwPIA4sDlAPJA4sDyQPKA5QDkwPCA5QDwgPJA8kDwgPEA8kDxAPLA8oDyQPLA8oDywPMA8gDxwPNA8gDzQPOA8cDlQOYA8cDmAPNA80DmAONA80DjQPPA84DzQPPA84DzwPQA4MDjAPRA4MD0QPSA4wDiwPKA4wDygPRA9EDygPMA9EDzAPTA9ID0QPTA9ID0wPUA9ADzwPVA9AD1QPWA88DjQOQA88DkAPVA9UDkAOFA9UDhQPXA9YD1QPXA9YD1wPYA3sDhAPZA3sD2QPaA4QDgwPSA4QD0gPZA9kD0gPUA9kD1APbA9oD2QPbA9oD2wPcA9gD1wPdA9gD3QPeA9cDhQOIA9cDiAPdA90DiAN9A90DfQPfA94D3QPfA94D3wPgA3MDfAPhA3MD4QPiA3wDewPaA3wD2gPhA+ED2gPcA+ED3APjA+ID4QPjA+ID4wPkA+AD3wPlA+AD5QPmA98DfQOAA98DgAPlA+UDgAN1A+UDdQPnA+YD5QPnA+YD5wPoA2sDdAPpA2sD6QPqA3QDcwPiA3QD4gPpA+kD4gPkA+kD5APrA+oD6QPrA+oD6wPsA+gD5wPtA+gD7QPuA+cDdQN4A+cDeAPtA+0DeANtA+0DbQPvA+4D7QPvA+4D7wPwA2MDbAPxA2MD8QPyA2wDawPqA2wD6gPxA/ED6gPsA/ED7APzA/ID8QPzA/ID8wP0A/AD7wP1A/AD9QP2A+8DbQNwA+8DcAP1A/UDcANlA/UDZQP3A/YD9QP3A/YD9wP4A0gDZAP5A0gD+QP6A2QDYwPyA2QD8gP5A/kD8gP0A/kD9AP7A/oD+QP7A/oD+wP8A/gD9wP9A/gD/QP+A/cDZQNoA/cDaAP9A/0DaANLA/0DSwP/A/4D/QP/A/4D/wMABFwDWwMBBFwDAQSvA1sDVAMCBFsDAgQBBAEEAgQDBAEEAwQEBK8DAQQEBK8DBASwAwUEBgQHBAUEBwQIBAYEWANeAwYEXgMHBAcEXgNgAwcEYAO0AwgEBwS0AwgEtAOzA1QDUwMJBFQDCQQCBFMDSgMKBFMDCgQJBAkECgQLBAkECwQMBAIECQQMBAIEDAQDBA0EDgQPBA0EDwQQBA4EUANWAw4EVgMPBA8EVgNYAw8EWAMGBBAEDwQGBBAEBgQFBEoDSQMRBEoDEQQKBEkDSAP6A0kD+gMRBBEE+gP8AxEE/AMSBAoEEQQSBAoEEgQLBAAE/wMTBAAEEwQUBP8DSwNOA/8DTgMTBBMETgNQAxMEUAMOBBQEEwQOBBQEDgQNBLUBswEVBLUBFQQWBLMBqQEXBLMBFwQVBBUEFwQYBBUEGAQZBBYEFQQZBBYEGQQaBBsEHAQdBBsEHQQeBBwEsQG7ARwEuwEdBB0EuwG1AR0EtQEWBB4EHQQWBB4EFgQaBKkBpwEfBKkBHwQXBKcBnQEgBKcBIAQfBB8EIAQhBB8EIQQiBBcEHwQiBBcEIgQYBCMEJAQlBCMEJQQmBCQEpQGyASQEsgElBCUEsgGxASUEsQEcBCYEJQQcBCYEHAQbBJ0BmwEnBJ0BJwQgBJsBkQEoBJsBKAQnBCcEKAQpBCcEKQQqBCAEJwQqBCAEKgQhBCsELAQtBCsELQQuBCwEmQGmASwEpgEtBC0EpgGlAS0EpQEkBC4ELQQkBC4EJAQjBJEBjwEvBJEBLwQoBI8BhQEwBI8BMAQvBC8EMAQxBC8EMQQyBCgELwQyBCgEMgQpBDMENAQ1BDMENQQ2BDQEjQGaATQEmgE1BDUEmgGZATUEmQEsBDYENQQsBDYELAQrBIUBgwE3BIUBNwQwBIMBeQE4BIMBOAQ3BDcEOAQ5BDcEOQQ6BDAENwQ6BDAEOgQxBDsEPAQ9BDsEPQQ+BDwEgQGOATwEjgE9BD0EjgGNAT0EjQE0BD4EPQQ0BD4ENAQzBHkBdwE/BHkBPwQ4BHcBbQFABHcBQAQ/BD8EQARBBD8EQQRCBDgEPwRCBDgEQgQ5BEMERARFBEMERQRGBEQEdQGCAUQEggFFBEUEggGBAUUEgQE8BEYERQQ8BEYEPAQ7BG0BawFHBG0BRwRABGsBYQFIBGsBSARHBEcESARJBEcESQRKBEAERwRKBEAESgRBBEsETARNBEsETQROBEwEaQF2AUwEdgFNBE0EdgF1AU0EdQFEBE4ETQREBE4ERARDBGEBXwFPBGEBTwRIBF8BVQFQBF8BUARPBE8EUARRBE8EUQRSBEgETwRSBEgEUgRJBFMEVARVBFMEVQRWBFQEXQFqAVQEagFVBFUEagFpAVUEaQFMBFYEVQRMBFYETARLBFUBUwFXBFUBVwRQBFMBRQFYBFMBWARXBFcEWARZBFcEWQRaBFAEVwRaBFAEWgRRBFsEXARdBFsEXQReBFwETgFeAVwEXgFdBF0EXgFdAV0EXQFUBF4EXQRUBF4EVARTBF8EYARhBF8EYQRiBGAEYwRkBGAEZARhBGEEZARlBGEEZQRmBGIEYQRmBGIEZgRnBGgEaQRqBGgEagRrBGkEYwRgBGkEYARqBGoEYARfBGoEXwRsBGsEagRsBGsEbARtBG4EbwRwBG4EcARxBG8EXwRiBG8EYgRwBHAEYgRnBHAEZwRyBHEEcARyBHEEcgRzBG0EbAR0BG0EdAR1BGwEXwRvBGwEbwR0BHQEbwRuBHQEbgR2BHUEdAR2BHUEdgR3BHgEeQR6BHgEegR7BHkEbgRxBHkEcQR6BHoEcQRzBHoEcwR8BHsEegR8BHsEfAR9BHcEdgR+BHcEfgR/BHYEbgR5BHYEeQR+BH4EeQR4BH4EeASABH8EfgSABH8EgASBBAIBBQGCBAIBggSDBAUBCgGEBAUBhASCBIIEhAR4BIIEeAR7BIMEggR7BIMEewR9BHgEhASFBHgEhQSABIQECgEQAYQEEAGFBIUEEAEPAYUEDwGGBIAEhQSGBIAEhgSBBBEBFAGHBBEBhwSIBBQBAgGDBBQBgwSHBIcEgwR9BIcEfQSJBIgEhwSJBIgEiQSKBIEEhgSLBIEEiwSMBIYEDwEcAYYEHAGLBIsEHAEbAYsEGwGNBIwEiwSNBIwEjQSOBB0BIAGPBB0BjwSQBCABEQGIBCABiASPBI8EiASKBI8EigSRBJAEjwSRBJAEkQSSBI4EjQSTBI4EkwSUBI0EGwEoAY0EKAGTBJMEKAEnAZMEJwGVBJQEkwSVBJQElQSWBCkBLAGXBCkBlwSYBCwBHQGQBCwBkASXBJcEkASSBJcEkgSZBJgElwSZBJgEmQSaBJYElQSbBJYEmwScBJUEJwE0AZUENAGbBJsENAEzAZsEMwGdBJwEmwSdBJwEnQSeBJIEnwSgBJIEoASZBJ8EcwRyBJ8EcgSgBKAEcgRnBKAEZwShBJkEoAShBJkEoQSaBG0EdQSiBG0EogSjBHUEdwSkBHUEpASiBKIEpASWBKIElgScBKMEogScBKMEnASeBJIEkQSlBJIEpQSfBJEEigSJBJEEiQSlBKUEiQR9BKUEfQR8BJ8EpQR8BJ8EfARzBIEEjASmBIEEpgR/BIwEjgSUBIwElASmBKYElASWBKYElgSkBH8EpgSkBH8EpAR3BKcEqASpBKcEqQSqBKgEmgShBKgEoQSpBKkEoQRnBKkEZwRmBKoEqQRmBKoEZgRlBG0EowSrBG0EqwRrBKMEngSsBKMErASrBKsErAStBKsErQSuBGsEqwSuBGsErgRoBDUBOAGvBDUBrwSwBDgBKQGYBDgBmASvBK8EmASaBK8EmgSoBLAErwSoBLAEqASnBJ4EnQSxBJ4EsQSsBJ0EMwFAAZ0EQAGxBLEEQAE/AbEEPwGyBKwEsQSyBKwEsgStBOwCswS0BOwCtATyArMEtQS2BLMEtgS0BLQEtgS3BLQEtwS4BPICtAS4BPICuAT0ArkEugS7BLkEuwS8BLoEvQS+BLoEvgS7BLsEvgTwArsE8AL5ArwEuwT5ArwE+QL4AjUBsAS/BDUBvwTqArAEpwTABLAEwAS/BL8EwAS1BL8EtQSzBOoCvwSzBOoCswTsAr0EwQTCBL0EwgS+BMEErQSyBMEEsgTCBMIEsgQ/AcIEPwHxAr4EwgTxAr4E8QLwAkEB+wLDBEEBwwTEBPsC9AK4BPsCuATDBMMEuAS3BMMEtwTFBMQEwwTFBMQExQTGBLkEvATHBLkExwTIBLwE+AL9ArwE/QLHBMcE/QJQAccEUAHJBMgExwTJBMgEyQTKBEEBxATLBEEBywRCAcQExgTMBMQEzATLBMsEzARZBMsEWQRYBEIBywRYBEIBWARFAVsEzQTOBFsEzgRcBM0EygTJBM0EyQTOBM4EyQRQAc4EUAFPAVwEzgRPAVwETwFOAc8E0ATRBM8E0QTSBNAE0wTUBNAE1ATRBNEE1ATVBNEE1QTWBNIE0QTWBNIE1gTXBNgE2QTaBNgE2gTbBNkE3ATdBNkE3QTaBNoE3QTPBNoEzwTSBNsE2gTSBNsE0gTXBNcE1gTeBNcE3gTfBNYE1QTgBNYE4ATeBN4E4AThBN4E4QTiBN8E3gTiBN8E4gTjBOQE5QTmBOQE5gTnBOUE2ATbBOUE2wTmBOYE2wTXBOYE1wTfBOcE5gTfBOcE3wTjBOME4gToBOME6ATpBOIE4QTqBOIE6gToBOgE6gTrBOgE6wTsBOkE6ATsBOkE7ATtBO4E7wTwBO4E8ATxBO8E5ATnBO8E5wTwBPAE5wTjBPAE4wTpBPEE8ATpBPEE6QTtBO0E7ATyBO0E8gTzBOwE6wT0BOwE9ATyBPIE9ARlBPIEZQRkBPME8gRkBPMEZARjBGgE9QT2BGgE9gRpBPUE7gTxBPUE8QT2BPYE8QTtBPYE7QTzBGkE9gTzBGkE8wRjBKcEqgT3BKcE9wTABKoEZQT0BKoE9AT3BPcE9ATrBPcE6wT4BMAE9wT4BMAE+AS1BO4E9QT5BO4E+QT6BPUEaASuBPUErgT5BPkErgStBPkErQTBBPoE+QTBBPoEwQS9BEkEUgT7BEkE+wT8BFIEUQT9BFIE/QT7BPsE/QT+BPsE/gT/BPwE+wT/BPwE/wQABQEFAgUDBQEFAwUEBQIFUwRWBAIFVgQDBQMFVgRLBAMFSwQFBQQFAwUFBQQFBQUGBQcFCAUJBQcFCQUKBQgFCwUMBQgFDAUJBQkFDAXTBAkF0wTQBAoFCQXQBAoF0ATPBNwEDQUOBdwEDgXdBA0FDwUQBQ0FEAUOBQ4FEAUHBQ4FBwUKBd0EDgUKBd0ECgXPBBEFEgUTBREFEwUUBRIFFQUWBRIFFgUTBRMFFgUXBRMFFwUYBRQFEwUYBRQFGAUZBRoFGwUcBRoFHAUdBRsFHgUfBRsFHwUcBRwFHwURBRwFEQUUBR0FHAUUBR0FFAUZBRkFGAUgBRkFIAUhBRgFFwUiBRgFIgUgBSAFIgUjBSAFIwUkBSEFIAUkBSEFJAUlBSYFJwUoBSYFKAUpBScFGgUdBScFHQUoBSgFHQUZBSgFGQUhBSkFKAUhBSkFIQUlBSUFJAUqBSUFKgUrBSQFIwUsBSQFLAUqBSoFLAULBSoFCwUIBSsFKgUIBSsFCAUHBQ8FLQUuBQ8FLgUQBS0FJgUpBS0FKQUuBS4FKQUlBS4FJQUrBRAFLgUrBRAFKwUHBTkEQgQvBTkELwUwBUIEQQQxBUIEMQUvBS8FMQUyBS8FMgUzBTAFLwUzBTAFMwU0BTUFNgU3BTUFNwU4BTYFQwRGBDYFRgQ3BTcFRgQ7BDcFOwQ5BTgFNwU5BTgFOQU6BTQFMwU7BTQFOwU8BTMFMgU9BTMFPQU7BTsFPQU+BTsFPgU/BTwFOwU/BTwFPwVABUEFQgVDBUEFQwVEBUIFNQU4BUIFOAVDBUMFOAU6BUMFOgVFBUQFQwVFBUQFRQVGBUAFPwVHBUAFRwVIBT8FPgVJBT8FSQVHBUcFSQVKBUcFSgVLBUgFRwVLBUgFSwVMBU0FTgVPBU0FTwVQBU4FQQVEBU4FRAVPBU8FRAVGBU8FRgVRBVAFTwVRBVAFUQVSBUwFSwVTBUwFUwVUBUsFSgVVBUsFVQVTBVMFVQVWBVMFVgVXBVQFUwVXBVQFVwVYBVkFWgVbBVkFWwVcBVoFTQVQBVoFUAVbBVsFUAVSBVsFUgVdBVwFWwVdBVwFXQVeBV8FYAVhBV8FYQViBWAFYwVkBWAFZAVhBWEFZAVYBWEFWAVXBWIFYQVXBWIFVwVWBV4FZQVmBV4FZgVcBWUFZwVoBWUFaAVmBWYFaAVpBWYFaQVqBVwFZgVqBVwFagVZBQsFLAVrBQsFawVsBSwFIwVtBSwFbQVrBWsFbQVYBWsFWAVkBWwFawVkBWwFZAVjBV4FbgVvBV4FbwVlBW4FJgUtBW4FLQVvBW8FLQUPBW8FDwVwBWUFbwVwBWUFcAVnBSMFIgVxBSMFcQVtBSIFFwVyBSIFcgVxBXEFcgVMBXEFTAVUBW0FcQVUBW0FVAVYBVIFcwV0BVIFdAVdBXMFGgUnBXMFJwV0BXQFJwUmBXQFJgVuBV0FdAVuBV0FbgVeBRcFFgV1BRcFdQVyBRYFFQV2BRYFdgV1BXUFdgVABXUFQAVIBXIFdQVIBXIFSAVMBUYFdwV4BUYFeAVRBXcFHgUbBXcFGwV4BXgFGwUaBXgFGgVzBVEFeAVzBVEFcwVSBRUFeQV6BRUFegV2BXkFewV8BXkFfAV6BXoFfAU0BXoFNAU8BXYFegU8BXYFPAVABToFfQV+BToFfgVFBX0FfwWABX0FgAV+BX4FgAUeBX4FHgV3BUUFfgV3BUUFdwVGBTEEOgSBBTEEgQWCBToEOQQwBToEMAWBBYEFMAU0BYEFNAV8BYIFgQV8BYIFfAV7BToFOQWDBToFgwV9BTkFOwQ+BDkFPgSDBYMFPgQzBIMFMwSEBX0FgwWEBX0FhAV/BYUFhgWHBYUFhwWIBYYFewV5BYYFeQWHBYcFeQUVBYcFFQUSBYgFhwUSBYgFEgURBR4FgAWJBR4FiQUfBYAFfwWKBYAFigWJBYkFigWFBYkFhQWIBR8FiQWIBR8FiAURBRgEIgSLBRgEiwWMBSIEIQQqBCIEKgSLBYsFKgQpBIsFKQQyBIwFiwUyBIwFMgQxBCsELgSNBSsEjQU2BC4EIwQmBC4EJgSNBY0FJgQbBI0FGwSOBTYEjQWOBTYEjgUzBBgEjAWPBRgEjwWQBYwFMQSCBYwFggWPBY8FggV7BY8FewWGBZAFjwWGBZAFhgWFBX8FhAWRBX8FkQWKBYQFMwSOBYQFjgWRBZEFjgUbBJEFGwSSBYoFkQWSBYoFkgWFBRoEGQSTBRoEkwWUBRgEkAWTBRgEkwUZBIUFlAWTBYUFkwWQBYUFkgWVBYUFlQWUBRsEHgSVBRsElQWSBRoElAWVBRoElQUeBEEESgSWBUEElgUxBUoESQT8BEoE/ASWBZYF/AQABZYFAAWXBTEFlgWXBTEFlwUyBQYFBQWYBQYFmAWZBQUFSwROBAUFTgSYBZgFTgRDBJgFQwQ2BZkFmAU2BZkFNgU1BQAFmgWbBQAFmwWXBZoFnAWdBZoFnQWbBZsFnQU+BZsFPgU9BZcFmwU9BZcFPQUyBUEFngWfBUEFnwVCBZ4FoAWhBZ4FoQWfBZ8FoQUGBZ8FBgWZBUIFnwWZBUIFmQU1BZwFogWjBZwFowWdBaIFpAWlBaIFpQWjBaMFpQVKBaMFSgVJBZ0FowVJBZ0FSQU+BU0FpgWnBU0FpwVOBaYFqAWpBaYFqQWnBacFqQWgBacFoAWeBU4FpwWeBU4FngVBBV8FYgWqBV8FqgWrBWIFVgVVBWIFVQWqBaoFVQVKBaoFSgWlBasFqgWlBasFpQWkBU0FWgWsBU0FrAWmBVoFWQVqBVoFagWsBawFagVpBawFaQWtBaYFrAWtBaYFrQWoBesE6gSuBesErgWvBeoE4QSwBeoEsAWuBa4FsAWxBa4FsQWyBa8FrgWyBa8FsgWzBbQFtQW2BbQFtgW3BbUF5ATvBLUF7wS2BbYF7wTuBLYF7gS4BbcFtgW4BbcFuAW5Bf4EugW7Bf4EuwW8BboFswWyBboFsgW7BbsFsgWxBbsFsQW9BbwFuwW9BbwFvQW+BbQFtwW/BbQFvwXABbcFuQXBBbcFwQW/Bb8FwQUBBb8FAQXCBcAFvwXCBcAFwgXDBVEEWgTEBVEExAX9BFoEWQTFBVoExQXEBcQFxQWzBcQFswW6Bf0ExAW6Bf0EugX+BLkFxgXHBbkFxwXBBcYFWwReBMYFXgTHBccFXgRTBMcFUwQCBcEFxwUCBcEFAgUBBVkEyAXJBVkEyQXFBcgFtwS2BMgFtgTJBckFtgS1BMkFtQTKBcUFyQXKBcUFygWzBb0EugTLBb0EywXMBboEuQTNBboEzQXLBcsFzQVbBMsFWwTGBcwFywXGBcwFxgW5BbUE+ATOBbUEzgXKBesErwXOBesEzgX4BLMFygXOBbMFzgWvBbkFuAXPBbkFzwXMBe4E+gTPBe4EzwW4Bb0EzAXPBb0EzwX6BFkEzATQBVkE0AXIBcYExQTQBcYE0AXMBLcEyAXQBbcE0AXFBLkEyATRBbkE0QXNBcoEzQTRBcoE0QXIBFsEzQXRBVsE0QXNBF8F0gXTBV8F0wVgBdIF1AXVBdIF1QXTBdMF1QXWBdMF1gXXBWAF0wXXBWAF1wVjBdgF2QXaBdgF2gXbBdkF3AXdBdkF3QXaBdoF3QVpBdoFaQVoBdsF2gVoBdsFaAVnBQsFbAXeBQsF3gUMBWwFYwXXBWwF1wXeBd4F1wXWBd4F1gXfBQwF3gXfBQwF3wXTBNgF2wXgBdgF4AXhBdsFZwVwBdsFcAXgBeAFcAUPBeAFDwUNBeEF4AUNBeEFDQXcBL4FvQXiBb4F4gXjBb0FsQXkBb0F5AXiBeIF5AXWBeIF1gXVBeMF4gXVBeMF1QXUBdgF5QXmBdgF5gXZBeUFtAXABeUFwAXmBeYFwAXDBeYFwwXnBdkF5gXnBdkF5wXcBeEE4AToBeEE6AWwBeAE1QTpBeAE6QXoBegF6QXWBegF1gXkBbAF6AXkBbAF5AWxBdgF6gXrBdgF6wXlBeoF2ATlBOoF5QTrBesF5QTkBOsF5AS1BeUF6wW1BeUFtQW0BdME3wXsBdME7AXUBNYF6QXsBdYF7AXfBdUE1ATsBdUE7AXpBdgE6gXtBdgE7QXZBNgF4QXtBdgF7QXqBdwE2QTtBdwE7QXhBe4F7wXwBe4F8AXxBe8F8gXzBe8F8wXwBfAF8wX0BfAF9AX1BfEF8AX1BfEF9QX2BfcF+AX5BfcF+QX6BfgF+wX8BfgF/AX5BfkF/AX9BfkF/QX+BfoF+QX+BfoF/gX/Be4F8QUABu4FAAYBBvEF9gUCBvEFAgYABgAGAgYDBgAGAwYEBgEGAAYEBgEGBAYFBgYGBwYIBgYGCAYJBgcG/wX+BQcG/gUIBggG/gX9BQgG/QUKBgkGCAYKBgkGCgYLBgUGBAYMBgUGDAYNBgQGAwYOBgQGDgYMBgwGDgYPBgwGDwYQBg0GDAYQBg0GEAYRBhIGEwYUBhIGFAYVBhMGBgYJBhMGCQYUBhQGCQYLBhQGCwYWBhUGFAYWBhUGFgYXBhEGEAYYBhEGGAYZBhAGDwYaBhAGGgYYBhgGGgYbBhgGGwYcBhkGGAYcBhkGHAYdBh4GHwYgBh4GIAYhBh8GEgYVBh8GFQYgBiAGFQYXBiAGFwYiBiEGIAYiBiEGIgYjBh0GHAYkBh0GJAYlBhwGGwYmBhwGJgYkBiQGJgYnBiQGJwYoBiUGJAYoBiUGKAYpBioGKwYsBioGLAYtBisGHgYhBisGIQYsBiwGIQYjBiwGIwYuBi0GLAYuBi0GLgYvBikGKAYwBikGMAYxBigGJwYyBigGMgYwBjAGMgYzBjAGMwY0BjEGMAY0BjEGNAY1BjYGNwY4BjYGOAY5BjcGKgYtBjcGLQY4BjgGLQYvBjgGLwY6BjkGOAY6BjkGOgY7BicGPAY9BicGPQYyBjwGPgY/BjwGPwY9Bj0GPwZABj0GQAZBBjIGPQZBBjIGQQYzBkIGQwZEBkIGRAZFBkMGRgZHBkMGRwZEBkQGRwYqBkQGKgY3BkUGRAY3BkUGNwY2BhsGSAZJBhsGSQYmBkgGSgZLBkgGSwZJBkkGSwY+BkkGPgY8BiYGSQY8BiYGPAYnBkYGTAZNBkYGTQZHBkwGTgZPBkwGTwZNBk0GTwYeBk0GHgYrBkcGTQYrBkcGKwYqBg8GUAZRBg8GUQYaBlAGUgZTBlAGUwZRBlEGUwZKBlEGSgZIBhoGUQZIBhoGSAYbBk4GVAZVBk4GVQZPBlQGVgZXBlQGVwZVBlUGVwYSBlUGEgYfBk8GVQYfBk8GHwYeBgMGWAZZBgMGWQYOBlgGWgZbBlgGWwZZBlkGWwZSBlkGUgZQBg4GWQZQBg4GUAYPBlYGXAZdBlYGXQZXBlwGXgZfBlwGXwZdBl0GXwYGBl0GBgYTBlcGXQYTBlcGEwYSBvYFYAZhBvYFYQYCBmAGYgZjBmAGYwZhBmEGYwZaBmEGWgZYBgIGYQZYBgIGWAYDBl4GZAZlBl4GZQZfBmQGZgZnBmQGZwZlBmUGZwb/BWUG/wUHBl8GZQYHBl8GBwYGBvYF9QVoBvYFaAZgBvUF9AVpBvUFaQZoBmgGaQZqBmgGagZrBmAGaAZrBmAGawZiBmwGbQZuBmwGbgZvBm0G9wX6BW0G+gVuBm4G+gX/BW4G/wVnBm8GbgZnBm8GZwZmBv4EvAVwBv4EcAZxBrwFvgVyBrwFcgZwBnAGcgZzBnAGcwZ0BnEGcAZ0BnEGdAZ1BnYGdwZ4BnYGeAZ5BncGwwXCBXcGwgV4BngGwgUBBXgGAQV6BnkGeAZ6BnkGegZ7Br4FfAZ9Br4FfQZyBnwGNQY0BnwGNAZ9Bn0GNAYzBn0GMwZ+BnIGfQZ+BnIGfgZzBjYGOQZ/BjYGfwaABjkGOwaBBjkGgQZ/Bn8GgQbDBX8GwwV3BoAGfwZ3BoAGdwZ2BgAF/wSCBgAFggaaBf8E/gRxBv8EcQaCBoIGcQZ1BoIGdQaDBpoFggaDBpoFgwacBXsGegaEBnsGhAaFBnoGAQUEBXoGBAWEBoQGBAUGBYQGBgWhBYUGhAahBYUGoQWgBaQFhgaHBqQFhwaIBoYGiQaKBoYGigaHBocGigb0BYcG9AXzBYgGhwbzBYgG8wXyBfcFiwaMBvcFjAb4BYsGjQaOBosGjgaMBowGjgaoBYwGqAWPBvgFjAaPBvgFjwb7BTMGQQaQBjMGkAZ+BkEGQAaRBkEGkQaQBpAGkQaSBpAGkgaTBn4GkAaTBn4GkwZzBpQGlQaWBpQGlgaXBpUGQgZFBpUGRQaWBpYGRQY2BpYGNgaABpcGlgaABpcGgAZ2BpgGmQaaBpgGmgabBpkGnAadBpkGnQaaBpoGnQZzBpoGcwaTBpsGmgaTBpsGkwaSBnYGngafBnYGnwaXBp4GoAahBp4GoQafBp8GoQaiBp8GogajBpcGnwajBpcGowaUBqQGpQamBqQGpganBqUGnAaZBqUGmQamBqYGmQaYBqYGmAaoBqcGpgaoBqcGqAapBqIGoQaqBqIGqgarBqEGoAasBqEGrAaqBqoGrAatBqoGrQauBqsGqgauBqsGrgavBrAGsQayBrAGsgazBrEGtAa1BrEGtQayBrIGtQacBrIGnAalBrMGsgalBrMGpQakBqAGtga3BqAGtwasBrYGuAa5BrYGuQa3BrcGuQa6BrcGuga7BqwGtwa7BqwGuwatBokGvAa9BokGvQa+BrwGtAaxBrwGsQa9Br0GsQawBr0GsAa/Br4GvQa/Br4GvwbABroGuQbBBroGwQbCBrkGuAbDBrkGwwbBBsEGwwaNBsEGjQbEBsIGwQbEBsIGxAbFBvQFigbGBvQFxgZpBooGiQa+BooGvgbGBsYGvgbABsYGwAbHBmkGxgbHBmkGxwZqBsUGxAbIBsUGyAbJBsQGjQaLBsQGiwbIBsgGiwb3BcgG9wVtBskGyAZtBskGbQZsBpwFygbLBpwFywaiBcoGtAa8BsoGvAbLBssGvAaJBssGiQaGBqIFywaGBqIFhgakBY0GwwbMBo0GzAaOBsMGuAbNBsMGzQbMBswGzQagBcwGoAWpBY4GzAapBY4GqQWoBZwFgwbOBpwFzgbKBoMGdQbPBoMGzwbOBs4GzwacBs4GnAa1BsoGzga1BsoGtQa0BqAG0AbRBqAG0Qa2BtAGewaFBtAGhQbRBtEGhQagBdEGoAXNBrYG0QbNBrYGzQa4BnUGdAbSBnUG0gbPBnMGnQbSBnMG0gZ0BpwGzwbSBpwG0gadBqAGngbTBqAG0wbQBnYGeQbTBnYG0waeBnsG0AbTBnsG0wZ5BmoGxwbUBmoG1AbVBscGwAbWBscG1gbUBtQG1gbXBtQG1wbYBtUG1AbYBtUG2AbZBtoG2wbcBtoG3AbdBtsGxQbJBtsGyQbcBtwGyQZsBtwGbAbeBt0G3AbeBt0G3gbfBsAGvwbgBsAG4AbWBr8GsAbhBr8G4QbgBuAG4QbiBuAG4gbjBtYG4AbjBtYG4wbXBuQG5QbmBuQG5gbnBuUGugbCBuUGwgbmBuYGwgbFBuYGxQbbBucG5gbbBucG2wbaBrAGswboBrAG6AbhBrMGpAbpBrMG6QboBugG6QbqBugG6gbrBuEG6AbrBuEG6wbiBuwG7QbuBuwG7gbvBu0GrQa7Bu0GuwbuBu4Guwa6Bu4GugblBu8G7gblBu8G5QbkBqQGpwbwBqQG8AbpBqcGqQbxBqcG8QbwBvAG8QbyBvAG8gbzBukG8AbzBukG8wbqBvQG9Qb2BvQG9gb3BvUGrwauBvUGrgb2BvYGrgatBvYGrQbtBvcG9gbtBvcG7QbsBqkGqAb4BqkG+AbxBqgGmAb5BqgG+Qb4BvgG+Qb6BvgG+gb7BvEG+Ab7BvEG+wbyBvwG/Qb+BvwG/gb/Bv0GogarBv0Gqwb+Bv4GqwavBv4Grwb1Bv8G/gb1Bv8G9Qb0BpgGmwYAB5gGAAf5BpsGkgYBB5sGAQcABwAHAQcCBwAHAgcDB/kGAAcDB/kGAwf6BgQHBQcGBwQHBgcHBwUHlAajBgUHowYGBwYHowaiBgYHogb9BgcHBgf9BgcH/Qb8BpIGkQYIB5IGCAcBB5EGQAYJB5EGCQcIBwgHCQcKBwgHCgcLBwEHCAcLBwEHCwcCBwwHDQcOBwwHDgcPBw0HQgaVBg0HlQYOBw4HlQaUBg4HlAYFBw8HDgcFBw8HBQcEB2IGawYQB2IGEAcRB2sGagbVBmsG1QYQBxAH1QbZBhAH2QYSBxEHEAcSBxEHEgcTB98G3gYUB98GFAcVB94GbAZvBt4GbwYUBxQHbwZmBhQHZgYWBxUHFAcWBxUHFgcXB1oGYwYYB1oGGAcZB2MGYgYRB2MGEQcYBxgHEQcTBxgHEwcaBxkHGAcaBxkHGgcbBxcHFgccBxcHHAcdBxYHZgZkBhYHZAYcBxwHZAZeBhwHXgYeBx0HHAceBx0HHgcfB1IGWwYgB1IGIAchB1sGWgYZB1sGGQcgByAHGQcbByAHGwciByEHIAciByEHIgcjBx8HHgckBx8HJAclBx4HXgZcBh4HXAYkByQHXAZWBiQHVgYmByUHJAcmByUHJgcnB0oGUwYoB0oGKAcpB1MGUgYhB1MGIQcoBygHIQcjBygHIwcqBykHKAcqBykHKgcrBycHJgcsBycHLActByYHVgZUBiYHVAYsBywHVAZOBiwHTgYuBy0HLAcuBy0HLgcvBz4GSwYwBz4GMAcxB0sGSgYpB0sGKQcwBzAHKQcrBzAHKwcyBzEHMAcyBzEHMgczBy8HLgc0By8HNAc1By4HTgZMBi4HTAY0BzQHTAZGBjQHRgY2BzUHNAc2BzUHNgc3B0AGPwY4B0AGOAcJBz8GPgYxBz8GMQc4BzgHMQczBzgHMwc5BwkHOAc5BwkHOQcKBzcHNgc6BzcHOgc7BzYHRgZDBjYHQwY6BzoHQwZCBjoHQgYNBzsHOgcNBzsHDQcMB/IG+wY8B/IGPAc9B/sG+gY+B/sGPgc8BzwHPgc/BzwHPwdABz0HPAdABz0HQAdBB0IHQwdEB0IHRAdFB0MH/Ab/BkMH/wZEB0QH/wb0BkQH9AZGB0UHRAdGB0UHRgdHB0EHQAdIB0EHSAdJB0AHPwdKB0AHSgdIB0gHSgdLB0gHSwdMB0kHSAdMB0kHTAdNB04HTwdQB04HUAdRB08HQgdFB08HRQdQB1AHRQdHB1AHRwdSB1EHUAdSB1EHUgdTB00HTAdUB00HVAdVB0wHSwdWB0wHVgdUB1QHVgdXB1QHVwdYB1UHVAdYB1UHWAdZB1oHWwdcB1oHXAddB1sHTgdRB1sHUQdcB1wHUQdTB1wHUwdeB10HXAdeB10HXgdfB1kHWAdgB1kHYAdhB1gHVwdiB1gHYgdgB2AHYgdjB2AHYwdkB2EHYAdkB2EHZAdlB2YHZwdoB2YHaAdpB2cHWgddB2cHXQdoB2gHXQdfB2gHXwdqB2kHaAdqB2kHagdrBxMHEgdsBxMHbAdtBxIH2QZuBxIHbgdsB2wHbgdZB2wHWQdhB20HbAdhB20HYQdlB18HbwdwB18HcAdqB28H3wYVB28HFQdwB3AHFQcXB3AHFwdxB2oHcAdxB2oHcQdrB9cGcgdzB9cGcwfYBnIHTQdVB3IHVQdzB3MHVQdZB3MHWQduB9gGcwduB9gGbgfZBl8HXgd0B18HdAdvB14HUwd1B14HdQd0B3QHdQfaBnQH2gbdBm8HdAfdBm8H3QbfBtcG4wZ2B9cGdgdyB+MG4gZ3B+MGdwd2B3YHdwdBB3YHQQdJB3IHdgdJB3IHSQdNB0cHeAd5B0cHeQdSB3gH5AbnBngH5wZ5B3kH5wbaBnkH2gZ1B1IHeQd1B1IHdQdTB/IGPQd6B/IGegfzBj0HQQd3Bz0Hdwd6B3oHdwfiBnoH4gbrBvMGegfrBvMG6wbqBuQGeAd7B+QGewfvBngHRwdGB3gHRgd7B3sHRgf0BnsH9Ab3Bu8Gewf3Bu8G9wbsBgIHCwd8BwIHfAcDBwsHCgd9BwsHfQd8B3wHfQc/B3wHPwc+BwMHfAc+BwMHPgf6BkIHfgd/B0IHfwdDB34HDAcPB34HDwd/B38HDwcEB38HBAcHB0MHfwcHB0MHBwf8BjMHgAeBBzMHgQc5B4AHSwdKB4AHSgeBB4EHSgc/B4EHPwd9BzkHgQd9BzkHfQcKB0IHTweCB0IHggd+B08HTgeDB08HgweCB4IHgwc3B4IHNwc7B34Hggc7B34HOwcMBysHhAeFBysHhQcyB4QHVwdWB4QHVgeFB4UHVgdLB4UHSweABzIHhQeABzIHgAczB04HWweGB04HhgeDB1sHWgeHB1sHhweGB4YHhwcvB4YHLwc1B4MHhgc1B4MHNQc3ByMHiAeJByMHiQcqB4gHYwdiB4gHYgeJB4kHYgdXB4kHVweEByoHiQeEByoHhAcrB1oHZweKB1oHigeHB2cHZgeLB2cHiweKB4oHiwcnB4oHJwctB4cHigctB4cHLQcvBxsHjAeNBxsHjQciB4wHZQdkB4wHZAeNB40HZAdjB40HYweIByIHjQeIByIHiAcjB2YHaQeOB2YHjgeLB2kHawePB2kHjweOB44HjwcfB44HHwclB4sHjgclB4sHJQcnBxMHbQeQBxMHkAcaB2UHjAeQB2UHkAdtBxsHGgeQBxsHkAeMBx8HjweRBx8HkQcdB2sHcQeRB2sHkQePBxcHHQeRBxcHkQdxBykGMQaSBykGkgeTBzEGNQaUBzEGlAeSB5IHlAeVB5IHlQeWB5MHkgeWB5MHlgeXB5gHmQeaB5gHmgebB5kHOwY6BpkHOgaaB5oHOgYvBpoHLwacB5sHmgecB5sHnAedBx0GJQaeBx0GngefByUGKQaTByUGkweeB54HkweXB54HlwegB58HngegB58HoAehB50HnAeiB50HogejB5wHLwYuBpwHLgaiB6IHLgYjBqIHIwakB6MHogekB6MHpAelBxEGGQamBxEGpgenBxkGHQafBxkGnwemB6YHnwehB6YHoQeoB6cHpgeoB6cHqAepB6UHpAeqB6UHqgerB6QHIwYiBqQHIgaqB6oHIgYXBqoHFwasB6sHqgesB6sHrAetBwUGDQauBwUGrgevBw0GEQanBw0GpweuB64HpwepB64HqQewB68HrgewB68HsAexB60HrAeyB60HsgezB6wHFwYWBqwHFgayB7IHFgYLBrIHCwa0B7MHsge0B7MHtAe1B+4FAQa2B+4Ftge3BwEGBQavBwEGrwe2B7YHrwexB7YHsQe4B7cHtge4B7cHuAe5B7UHtAe6B7UHuge7B7QHCwYKBrQHCga6B7oHCgb9BboH/QW8B7sHuge8B7sHvAe9B/IF7wW+B/IFvge/B+8F7gW3B+8Ftwe+B74Htwe5B74HuQfAB78HvgfAB78HwAfBB70HvAfCB70HwgfDB7wH/QX8BbwH/AXCB8IH/AX7BcIH+wXEB8MHwgfEB8MHxAfFB7kHxgfHB7kHxwfAB8YHlweWB8YHlgfHB8cHlgeVB8cHlQfIB8AHxwfIB8AHyAfBB5gHmwfJB5gHyQfKB5sHnQfLB5sHywfJB8kHywe9B8kHvQfDB8oHyQfDB8oHwwfFB7kHuAfMB7kHzAfGB7gHsQfNB7gHzQfMB8wHzQehB8wHoQegB8YHzAegB8YHoAeXB6UHzgfPB6UHzwejB84HtQe7B84HuwfPB88Huwe9B88HvQfLB6MHzwfLB6MHywedB7EHsAfQB7EH0AfNB6kHqAfQB6kH0AewB6EHzQfQB6EH0AeoB6UHqwfRB6UH0QfOB60HswfRB60H0QerB7UHzgfRB7UH0QezB18FqwXSB18F0gfTB6sFpAWIBqsFiAbSB9IHiAbyBdIH8gW/B9MH0ge/B9MHvwfBB/sFjwbUB/sF1AfEB48GqAWtBY8GrQXUB9QHrQVpBdQHaQXVB8QH1AfVB8QH1QfFB18F0wfWB18F1gfSBdMHwQfIB9MHyAfWB9YHyAeVB9YHlQfXB9IF1gfXB9IF1wfUBZgHygfYB5gH2AfZB8oHxQfVB8oH1QfYB9gH1QdpBdgHaQXdBdkH2AfdBdkH3QXcBb4F4wXaB74F2gd8BuMF1AXXB+MF1wfaB9oH1weVB9oHlQeUB3wG2geUB3wGlAc1BpgH2QfbB5gH2weZB9kH3AXnBdkH5wXbB9sH5wXDBdsHwwWBBpkH2weBBpkHgQY7Bg==" } ] } diff --git a/assets/scenes/load_scene_example.scn b/assets/scenes/load_scene_example.scn deleted file mode 100644 index a68262197272b..0000000000000 --- a/assets/scenes/load_scene_example.scn +++ /dev/null @@ -1,32 +0,0 @@ -[ - ( - entity: 0, - components: [ - { - "type": "ComponentB", - "map": { - "value": "hello", - }, - }, - { - "type": "ComponentA", - "map": { - "x": 1.0, - "y": 2.0, - }, - }, - ], - ), - ( - entity: 1, - components: [ - { - "type": "ComponentA", - "map": { - "x": 3.0, - "y": 4.0, - }, - }, - ], - ), -] \ No newline at end of file diff --git a/assets/scenes/load_scene_example.scn.ron b/assets/scenes/load_scene_example.scn.ron new file mode 100644 index 0000000000000..981c85d724d5e --- /dev/null +++ b/assets/scenes/load_scene_example.scn.ron @@ -0,0 +1,64 @@ +[ + ( + entity: 0, + components: [ + { + "type": "bevy_transform::components::transform::Transform", + "struct": { + "translation": { + "type": "glam::f32::vec3::Vec3", + "value": (0.0, 0.0, 0.0), + }, + "rotation": { + "type": "glam::f32::scalar::quat::Quat", + "value": (0.0, 0.0, 0.0, 1.0), + }, + "scale": { + "type": "glam::f32::vec3::Vec3", + "value": (1.0, 1.0, 1.0), + }, + }, + }, + { + "type": "scene::ComponentB", + "struct": { + "value": { + "type": "alloc::string::String", + "value": "hello", + }, + }, + }, + { + "type": "scene::ComponentA", + "struct": { + "x": { + "type": "f32", + "value": 1.0, + }, + "y": { + "type": "f32", + "value": 2.0, + }, + }, + }, + ], + ), + ( + entity: 1, + components: [ + { + "type": "scene::ComponentA", + "struct": { + "x": { + "type": "f32", + "value": 3.0, + }, + "y": { + "type": "f32", + "value": 4.0, + }, + }, + }, + ], + ), +] diff --git a/assets/shaders/animate_shader.wgsl b/assets/shaders/animate_shader.wgsl new file mode 100644 index 0000000000000..947c4a5420aff --- /dev/null +++ b/assets/shaders/animate_shader.wgsl @@ -0,0 +1,73 @@ +#import bevy_pbr::mesh_types +#import bevy_pbr::mesh_view_bindings + +@group(1) @binding(0) +var mesh: Mesh; + +// NOTE: Bindings must come before functions that use them! +#import bevy_pbr::mesh_functions + +struct Vertex { + @location(0) position: vec3, + @location(1) normal: vec3, + @location(2) uv: vec2, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) uv: vec2, +}; + +@vertex +fn vertex(vertex: Vertex) -> VertexOutput { + var out: VertexOutput; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); + out.uv = vertex.uv; + return out; +} + + +struct Time { + time_since_startup: f32, +}; +@group(2) @binding(0) +var time: Time; + + +fn oklab_to_linear_srgb(c: vec3) -> vec3 { + let L = c.x; + let a = c.y; + let b = c.z; + + let l_ = L + 0.3963377774 * a + 0.2158037573 * b; + let m_ = L - 0.1055613458 * a - 0.0638541728 * b; + let s_ = L - 0.0894841775 * a - 1.2914855480 * b; + + let l = l_*l_*l_; + let m = m_*m_*m_; + let s = s_*s_*s_; + + return vec3( + 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s, + -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s, + -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s, + ); +} + +@fragment +fn fragment(in: VertexOutput) -> @location(0) vec4 { + let speed = 2.0; + let t_1 = sin(time.time_since_startup * speed) * 0.5 + 0.5; + let t_2 = cos(time.time_since_startup * speed); + + let distance_to_center = distance(in.uv, vec2(0.5)) * 1.4; + + // blending is done in a perceptual color space: https://bottosson.github.io/posts/oklab/ + let red = vec3(0.627955, 0.224863, 0.125846); + let green = vec3(0.86644, -0.233887, 0.179498); + let blue = vec3(0.701674, 0.274566, -0.169156); + let white = vec3(1.0, 0.0, 0.0); + let mixed = mix(mix(red, blue, t_1), mix(green, white, t_2), distance_to_center); + + return vec4(oklab_to_linear_srgb(mixed), 1.0); +} diff --git a/assets/shaders/array_texture.wgsl b/assets/shaders/array_texture.wgsl new file mode 100644 index 0000000000000..91f4564e7fe27 --- /dev/null +++ b/assets/shaders/array_texture.wgsl @@ -0,0 +1,55 @@ +#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_bindings + +#import bevy_pbr::pbr_types +#import bevy_pbr::utils +#import bevy_pbr::clustered_forward +#import bevy_pbr::lighting +#import bevy_pbr::shadows +#import bevy_pbr::pbr_functions + +@group(1) @binding(0) +var my_array_texture: texture_2d_array; +@group(1) @binding(1) +var my_array_texture_sampler: sampler; + +struct FragmentInput { + @builtin(front_facing) is_front: bool, + @builtin(position) frag_coord: vec4, + #import bevy_pbr::mesh_vertex_output +}; + +@fragment +fn fragment(in: FragmentInput) -> @location(0) vec4 { + let layer = i32(in.world_position.x) & 0x3; + + // Prepare a 'processed' StandardMaterial by sampling all textures to resolve + // the material members + var pbr_input: PbrInput = pbr_input_new(); + + pbr_input.material.base_color = textureSample(my_array_texture, my_array_texture_sampler, in.uv, layer); +#ifdef VERTEX_COLORS + pbr_input.material.base_color = pbr_input.material.base_color * in.color; +#endif + + pbr_input.frag_coord = in.frag_coord; + pbr_input.world_position = in.world_position; + pbr_input.world_normal = in.world_normal; + + pbr_input.is_orthographic = view.projection[3].w == 1.0; + + pbr_input.N = prepare_normal( + pbr_input.material.flags, + in.world_normal, +#ifdef VERTEX_TANGENTS +#ifdef STANDARDMATERIAL_NORMAL_MAP + in.world_tangent, +#endif +#endif + in.uv, + in.is_front, + ); + pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); + + return tone_mapping(pbr(pbr_input)); +} diff --git a/assets/shaders/cubemap_unlit.wgsl b/assets/shaders/cubemap_unlit.wgsl new file mode 100644 index 0000000000000..6837384dea3ac --- /dev/null +++ b/assets/shaders/cubemap_unlit.wgsl @@ -0,0 +1,24 @@ +#import bevy_pbr::mesh_view_bindings + +#ifdef CUBEMAP_ARRAY +@group(1) @binding(0) +var base_color_texture: texture_cube_array; +#else +@group(1) @binding(0) +var base_color_texture: texture_cube; +#endif + +@group(1) @binding(1) +var base_color_sampler: sampler; + +@fragment +fn fragment( + #import bevy_pbr::mesh_vertex_output +) -> @location(0) vec4 { + let fragment_position_view_lh = world_position.xyz * vec3(1.0, 1.0, -1.0); + return textureSample( + base_color_texture, + base_color_sampler, + fragment_position_view_lh + ); +} diff --git a/assets/shaders/custom_material.frag b/assets/shaders/custom_material.frag new file mode 100644 index 0000000000000..bf46d1e5334fb --- /dev/null +++ b/assets/shaders/custom_material.frag @@ -0,0 +1,16 @@ +#version 450 +layout(location = 0) in vec2 v_Uv; + +layout(location = 0) out vec4 o_Target; + +layout(set = 1, binding = 0) uniform CustomMaterial { + vec4 Color; +}; + +layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture; +layout(set = 1, binding = 2) uniform sampler CustomMaterial_sampler; + + +void main() { + o_Target = Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv); +} diff --git a/assets/shaders/custom_material.vert b/assets/shaders/custom_material.vert new file mode 100644 index 0000000000000..59af5305ba7b2 --- /dev/null +++ b/assets/shaders/custom_material.vert @@ -0,0 +1,28 @@ +#version 450 + +layout(location = 0) in vec3 Vertex_Position; +layout(location = 1) in vec3 Vertex_Normal; +layout(location = 2) in vec2 Vertex_Uv; + +layout(location = 0) out vec2 v_Uv; + +layout(set = 0, binding = 0) uniform CameraViewProj { + mat4 ViewProj; + mat4 View; + mat4 InverseView; + mat4 Projection; + vec3 WorldPosition; + float width; + float height; +}; + +layout(set = 2, binding = 0) uniform Mesh { + mat4 Model; + mat4 InverseTransposeModel; + uint flags; +}; + +void main() { + v_Uv = Vertex_Uv; + gl_Position = ViewProj * Model * vec4(Vertex_Position, 1.0); +} diff --git a/assets/shaders/custom_material.wgsl b/assets/shaders/custom_material.wgsl new file mode 100644 index 0000000000000..65dd816b0e29e --- /dev/null +++ b/assets/shaders/custom_material.wgsl @@ -0,0 +1,17 @@ +struct CustomMaterial { + color: vec3, +}; + +@group(1) @binding(0) +var material: CustomMaterial; +@group(1) @binding(1) +var base_color_texture: texture_2d; +@group(1) @binding(2) +var base_color_sampler: sampler; + +@fragment +fn fragment( + #import bevy_pbr::mesh_vertex_output +) -> @location(0) vec4 { + return vec4(material.color, 1.0) * textureSample(base_color_texture, base_color_sampler, uv); +} diff --git a/assets/shaders/custom_material_chromatic_aberration.wgsl b/assets/shaders/custom_material_chromatic_aberration.wgsl new file mode 100644 index 0000000000000..e8ccdcfb62513 --- /dev/null +++ b/assets/shaders/custom_material_chromatic_aberration.wgsl @@ -0,0 +1,27 @@ +#import bevy_pbr::mesh_view_bindings + +@group(1) @binding(0) +var texture: texture_2d; + +@group(1) @binding(1) +var our_sampler: sampler; + +@fragment +fn fragment( + @builtin(position) position: vec4, + #import bevy_sprite::mesh2d_vertex_output +) -> @location(0) vec4 { + // Get screen position with coordinates from 0 to 1 + let uv = position.xy / vec2(view.width, view.height); + let offset_strength = 0.02; + + // Sample each color channel with an arbitrary shift + var output_color = vec4( + textureSample(texture, our_sampler, uv + vec2(offset_strength, -offset_strength)).r, + textureSample(texture, our_sampler, uv + vec2(-offset_strength, 0.0)).g, + textureSample(texture, our_sampler, uv + vec2(0.0, offset_strength)).b, + 1.0 + ); + + return output_color; +} diff --git a/assets/shaders/custom_material_screenspace_texture.wgsl b/assets/shaders/custom_material_screenspace_texture.wgsl new file mode 100644 index 0000000000000..aad35920c093e --- /dev/null +++ b/assets/shaders/custom_material_screenspace_texture.wgsl @@ -0,0 +1,16 @@ +#import bevy_pbr::mesh_view_bindings + +@group(1) @binding(0) +var texture: texture_2d; +@group(1) @binding(1) +var texture_sampler: sampler; + +@fragment +fn fragment( + @builtin(position) position: vec4, + #import bevy_pbr::mesh_vertex_output +) -> @location(0) vec4 { + let uv = position.xy / vec2(view.width, view.height); + let color = textureSample(texture, texture_sampler, uv); + return color; +} diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl new file mode 100644 index 0000000000000..dd7cfc150308d --- /dev/null +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -0,0 +1,38 @@ +#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_bindings + +struct CustomMaterial { + color: vec4, +}; +@group(1) @binding(0) +var material: CustomMaterial; + +// NOTE: Bindings must come before functions that use them! +#import bevy_pbr::mesh_functions + +struct Vertex { + @location(0) position: vec3, + @location(1) blend_color: vec4, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) blend_color: vec4, +}; + +@vertex +fn vertex(vertex: Vertex) -> VertexOutput { + var out: VertexOutput; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); + out.blend_color = vertex.blend_color; + return out; +} + +struct FragmentInput { + @location(0) blend_color: vec4, +}; + +@fragment +fn fragment(input: FragmentInput) -> @location(0) vec4 { + return material.color * input.blend_color; +} diff --git a/assets/shaders/game_of_life.wgsl b/assets/shaders/game_of_life.wgsl new file mode 100644 index 0000000000000..8858b20751e3c --- /dev/null +++ b/assets/shaders/game_of_life.wgsl @@ -0,0 +1,66 @@ +@group(0) @binding(0) +var texture: texture_storage_2d; + +fn hash(value: u32) -> u32 { + var state = value; + state = state ^ 2747636419u; + state = state * 2654435769u; + state = state ^ state >> 16u; + state = state * 2654435769u; + state = state ^ state >> 16u; + state = state * 2654435769u; + return state; +} + +fn randomFloat(value: u32) -> f32 { + return f32(hash(value)) / 4294967295.0; +} + +@compute @workgroup_size(8, 8, 1) +fn init(@builtin(global_invocation_id) invocation_id: vec3, @builtin(num_workgroups) num_workgroups: vec3) { + let location = vec2(i32(invocation_id.x), i32(invocation_id.y)); + + let randomNumber = randomFloat(invocation_id.y * num_workgroups.x + invocation_id.x); + let alive = randomNumber > 0.9; + let color = vec4(f32(alive)); + + textureStore(texture, location, color); +} + +fn is_alive(location: vec2, offset_x: i32, offset_y: i32) -> i32 { + let value: vec4 = textureLoad(texture, location + vec2(offset_x, offset_y)); + return i32(value.x); +} + +fn count_alive(location: vec2) -> i32 { + return is_alive(location, -1, -1) + + is_alive(location, -1, 0) + + is_alive(location, -1, 1) + + is_alive(location, 0, -1) + + is_alive(location, 0, 1) + + is_alive(location, 1, -1) + + is_alive(location, 1, 0) + + is_alive(location, 1, 1); +} + +@compute @workgroup_size(8, 8, 1) +fn update(@builtin(global_invocation_id) invocation_id: vec3) { + let location = vec2(i32(invocation_id.x), i32(invocation_id.y)); + + let n_alive = count_alive(location); + + var alive: bool; + if (n_alive == 3) { + alive = true; + } else if (n_alive == 2) { + let currently_alive = is_alive(location, 0, 0); + alive = bool(currently_alive); + } else { + alive = false; + } + let color = vec4(f32(alive)); + + storageBarrier(); + + textureStore(texture, location, color); +} \ No newline at end of file diff --git a/assets/shaders/instancing.wgsl b/assets/shaders/instancing.wgsl new file mode 100644 index 0000000000000..7cb00b039a84a --- /dev/null +++ b/assets/shaders/instancing.wgsl @@ -0,0 +1,36 @@ +#import bevy_pbr::mesh_types +#import bevy_pbr::mesh_view_bindings + +@group(1) @binding(0) +var mesh: Mesh; + +// NOTE: Bindings must come before functions that use them! +#import bevy_pbr::mesh_functions + +struct Vertex { + @location(0) position: vec3, + @location(1) normal: vec3, + @location(2) uv: vec2, + + @location(3) i_pos_scale: vec4, + @location(4) i_color: vec4, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4, + @location(0) color: vec4, +}; + +@vertex +fn vertex(vertex: Vertex) -> VertexOutput { + let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz; + var out: VertexOutput; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(position, 1.0)); + out.color = vertex.i_color; + return out; +} + +@fragment +fn fragment(in: VertexOutput) -> @location(0) vec4 { + return in.color; +} diff --git a/assets/shaders/line_material.wgsl b/assets/shaders/line_material.wgsl new file mode 100644 index 0000000000000..e47ffe6e16acb --- /dev/null +++ b/assets/shaders/line_material.wgsl @@ -0,0 +1,13 @@ +struct LineMaterial { + color: vec4, +}; + +@group(1) @binding(0) +var material: LineMaterial; + +@fragment +fn fragment( + #import bevy_pbr::mesh_vertex_output +) -> @location(0) vec4 { + return material.color; +} diff --git a/assets/shaders/shader_defs.wgsl b/assets/shaders/shader_defs.wgsl new file mode 100644 index 0000000000000..0efa91231a622 --- /dev/null +++ b/assets/shaders/shader_defs.wgsl @@ -0,0 +1,17 @@ +struct CustomMaterial { + color: vec4, +}; + +@group(1) @binding(0) +var material: CustomMaterial; + +@fragment +fn fragment( + #import bevy_pbr::mesh_vertex_output +) -> @location(0) vec4 { +#ifdef IS_RED + return vec4(1.0, 0.0, 0.0, 1.0); +#else + return material.color; +#endif +} diff --git a/assets/sounds/Windless Slopes.mp3 b/assets/sounds/Windless Slopes.mp3 deleted file mode 100644 index 8b8b76d7ab7e1..0000000000000 Binary files a/assets/sounds/Windless Slopes.mp3 and /dev/null differ diff --git a/assets/sounds/Windless Slopes.ogg b/assets/sounds/Windless Slopes.ogg new file mode 100644 index 0000000000000..87910863670e3 Binary files /dev/null and b/assets/sounds/Windless Slopes.ogg differ diff --git a/assets/sounds/breakout_collision.ogg b/assets/sounds/breakout_collision.ogg new file mode 100644 index 0000000000000..0211d70cfb845 Binary files /dev/null and b/assets/sounds/breakout_collision.ogg differ diff --git a/assets/textures/Game Icons/exitRight.png b/assets/textures/Game Icons/exitRight.png new file mode 100644 index 0000000000000..de78ab6f0f025 Binary files /dev/null and b/assets/textures/Game Icons/exitRight.png differ diff --git a/assets/textures/Game Icons/right.png b/assets/textures/Game Icons/right.png new file mode 100644 index 0000000000000..3f2480f9a5465 Binary files /dev/null and b/assets/textures/Game Icons/right.png differ diff --git a/assets/textures/Game Icons/wrench.png b/assets/textures/Game Icons/wrench.png new file mode 100644 index 0000000000000..440e7edc41af9 Binary files /dev/null and b/assets/textures/Game Icons/wrench.png differ diff --git a/assets/textures/Ryfjallet_cubemap.png b/assets/textures/Ryfjallet_cubemap.png new file mode 100644 index 0000000000000..777987b75552a Binary files /dev/null and b/assets/textures/Ryfjallet_cubemap.png differ diff --git a/assets/textures/Ryfjallet_cubemap_astc4x4.ktx2 b/assets/textures/Ryfjallet_cubemap_astc4x4.ktx2 new file mode 100644 index 0000000000000..78696cadca7ee Binary files /dev/null and b/assets/textures/Ryfjallet_cubemap_astc4x4.ktx2 differ diff --git a/assets/textures/Ryfjallet_cubemap_bc7.ktx2 b/assets/textures/Ryfjallet_cubemap_bc7.ktx2 new file mode 100644 index 0000000000000..17e67c7c9ff8b Binary files /dev/null and b/assets/textures/Ryfjallet_cubemap_bc7.ktx2 differ diff --git a/assets/textures/Ryfjallet_cubemap_etc2.ktx2 b/assets/textures/Ryfjallet_cubemap_etc2.ktx2 new file mode 100644 index 0000000000000..22a389cfd90e5 Binary files /dev/null and b/assets/textures/Ryfjallet_cubemap_etc2.ktx2 differ diff --git a/assets/textures/Ryfjallet_cubemap_readme.txt b/assets/textures/Ryfjallet_cubemap_readme.txt new file mode 100644 index 0000000000000..81bed0d91d8f3 --- /dev/null +++ b/assets/textures/Ryfjallet_cubemap_readme.txt @@ -0,0 +1,21 @@ +Modifications +============= + +The original work, as attributed below, has been modified as follows using the ImageMagick tool: + +mogrify -resize 256x256 -format png *.jpg +convert posx.png negx.png posy.png negy.png posz.png negz.png -gravity center -append cubemap.png + +Author +====== + +This is the work of Emil Persson, aka Humus. +http://www.humus.name + + + +License +======= + +This work is licensed under a Creative Commons Attribution 3.0 Unported License. +http://creativecommons.org/licenses/by/3.0/ diff --git a/assets/textures/array_texture.png b/assets/textures/array_texture.png new file mode 100644 index 0000000000000..ab2c144e11247 Binary files /dev/null and b/assets/textures/array_texture.png differ diff --git a/assets/textures/simplespace/License.txt b/assets/textures/simplespace/License.txt new file mode 100644 index 0000000000000..bdba93fb4f136 --- /dev/null +++ b/assets/textures/simplespace/License.txt @@ -0,0 +1,22 @@ + + + Simple Space + + Created/distributed by Kenney (www.kenney.nl) + Creation date: 03-03-2021 + + ------------------------------ + + License: (Creative Commons Zero, CC0) + http://creativecommons.org/publicdomain/zero/1.0/ + + This content is free to use in personal, educational and commercial projects. + Support us by crediting Kenney or www.kenney.nl (this is not mandatory) + + ------------------------------ + + Donate: http://support.kenney.nl + Patreon: http://patreon.com/kenney/ + + Follow on Twitter for updates: + http://twitter.com/KenneyNL \ No newline at end of file diff --git a/assets/textures/simplespace/enemy_A.png b/assets/textures/simplespace/enemy_A.png new file mode 100644 index 0000000000000..7db03b81a3320 Binary files /dev/null and b/assets/textures/simplespace/enemy_A.png differ diff --git a/assets/textures/simplespace/enemy_B.png b/assets/textures/simplespace/enemy_B.png new file mode 100644 index 0000000000000..fbc35f06bf6a6 Binary files /dev/null and b/assets/textures/simplespace/enemy_B.png differ diff --git a/assets/textures/simplespace/ship_C.png b/assets/textures/simplespace/ship_C.png new file mode 100644 index 0000000000000..4f5e493701a1a Binary files /dev/null and b/assets/textures/simplespace/ship_C.png differ diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 5e318eadf3047..165fbce1cb25e 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -1,12 +1,41 @@ [package] name = "benches" version = "0.1.0" -authors = ["Carter Anderson "] -edition = "2018" +edition = "2021" +description = "Benchmarks for Bevy engine" +publish = false +license = "MIT OR Apache-2.0" [dev-dependencies] -criterion = "0.3" -bevy = { path = "../" } +glam = "0.21" +rand = "0.8" +rand_chacha = "0.3" +criterion = { version = "0.3", features = ["html_reports"] } +bevy_app = { path = "../crates/bevy_app" } +bevy_ecs = { path = "../crates/bevy_ecs" } +bevy_reflect = { path = "../crates/bevy_reflect" } +bevy_tasks = { path = "../crates/bevy_tasks" } +bevy_utils = { path = "../crates/bevy_utils" } + +[[bench]] +name = "ecs" +path = "benches/bevy_ecs/benches.rs" +harness = false + +[[bench]] +name = "reflect_list" +path = "benches/bevy_reflect/list.rs" +harness = false + +[[bench]] +name = "reflect_map" +path = "benches/bevy_reflect/map.rs" +harness = false + +[[bench]] +name = "reflect_struct" +path = "benches/bevy_reflect/struct.rs" +harness = false [[bench]] name = "iter" diff --git a/benches/benches/bevy_ecs/benches.rs b/benches/benches/bevy_ecs/benches.rs new file mode 100644 index 0000000000000..4e0c165655bb3 --- /dev/null +++ b/benches/benches/bevy_ecs/benches.rs @@ -0,0 +1,13 @@ +use criterion::criterion_main; + +mod components; +mod iteration; +mod scheduling; +mod world; + +criterion_main!( + iteration::iterations_benches, + components::components_benches, + scheduling::scheduling_benches, + world::world_benches, +); diff --git a/benches/benches/bevy_ecs/components/add_remove.rs b/benches/benches/bevy_ecs/components/add_remove.rs new file mode 100644 index 0000000000000..0c5bc56b45b8e --- /dev/null +++ b/benches/benches/bevy_ecs/components/add_remove.rs @@ -0,0 +1,30 @@ +use bevy::prelude::*; + +#[derive(Component)] +struct A(f32); +#[derive(Component)] +struct B(f32); + +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + + let entities = world + .spawn_batch((0..10000).map(|_| (A(0.0),))) + .collect::>(); + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0.insert_one(*entity, B(0.0)).unwrap(); + } + + for entity in &self.1 { + self.0.remove_one::(*entity).unwrap(); + } + } +} diff --git a/benches/benches/bevy_ecs/components/add_remove_big_sparse_set.rs b/benches/benches/bevy_ecs/components/add_remove_big_sparse_set.rs new file mode 100644 index 0000000000000..db4f83d99edfb --- /dev/null +++ b/benches/benches/bevy_ecs/components/add_remove_big_sparse_set.rs @@ -0,0 +1,55 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct A(Mat4); +#[derive(Component, Copy, Clone)] +struct B(Mat4); + +#[derive(Component, Copy, Clone)] +struct C(Mat4); +#[derive(Component, Copy, Clone)] +struct D(Mat4); + +#[derive(Component, Copy, Clone)] +struct E(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct F(Mat4); +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + let mut entities = Vec::with_capacity(10_000); + for _ in 0..10_000 { + entities.push( + world + .spawn() + .insert_bundle(( + A(Mat4::from_scale(Vec3::ONE)), + B(Mat4::from_scale(Vec3::ONE)), + C(Mat4::from_scale(Vec3::ONE)), + D(Mat4::from_scale(Vec3::ONE)), + E(Mat4::from_scale(Vec3::ONE)), + )) + .id(), + ); + } + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0 + .entity_mut(*entity) + .insert(F(Mat4::from_scale(Vec3::ONE))); + } + + for entity in &self.1 { + self.0.entity_mut(*entity).remove::(); + } + } +} diff --git a/benches/benches/bevy_ecs/components/add_remove_big_table.rs b/benches/benches/bevy_ecs/components/add_remove_big_table.rs new file mode 100644 index 0000000000000..0d2ba46c73a6a --- /dev/null +++ b/benches/benches/bevy_ecs/components/add_remove_big_table.rs @@ -0,0 +1,54 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct A(Mat4); +#[derive(Component, Copy, Clone)] +struct B(Mat4); + +#[derive(Component, Copy, Clone)] +struct C(Mat4); +#[derive(Component, Copy, Clone)] +struct D(Mat4); + +#[derive(Component, Copy, Clone)] +struct E(Mat4); + +#[derive(Component, Copy, Clone)] +struct F(Mat4); +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + let mut entities = Vec::with_capacity(10_000); + for _ in 0..10_000 { + entities.push( + world + .spawn() + .insert_bundle(( + A(Mat4::from_scale(Vec3::ONE)), + B(Mat4::from_scale(Vec3::ONE)), + C(Mat4::from_scale(Vec3::ONE)), + D(Mat4::from_scale(Vec3::ONE)), + E(Mat4::from_scale(Vec3::ONE)), + )) + .id(), + ); + } + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0 + .entity_mut(*entity) + .insert(F(Mat4::from_scale(Vec3::ONE))); + } + + for entity in &self.1 { + self.0.entity_mut(*entity).remove::(); + } + } +} diff --git a/benches/benches/bevy_ecs/components/add_remove_sparse_set.rs b/benches/benches/bevy_ecs/components/add_remove_sparse_set.rs new file mode 100644 index 0000000000000..6cb90f641c788 --- /dev/null +++ b/benches/benches/bevy_ecs/components/add_remove_sparse_set.rs @@ -0,0 +1,31 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(f32); +#[derive(Component)] +#[component(storage = "SparseSet")] +struct B(f32); + +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + let mut entities = Vec::with_capacity(10_000); + for _ in 0..10_000 { + entities.push(world.spawn().insert(A(0.0)).id()); + } + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0.entity_mut(*entity).insert(B(0.0)); + } + + for entity in &self.1 { + self.0.entity_mut(*entity).remove::(); + } + } +} diff --git a/benches/benches/bevy_ecs/components/add_remove_table.rs b/benches/benches/bevy_ecs/components/add_remove_table.rs new file mode 100644 index 0000000000000..67043a5bbd8c2 --- /dev/null +++ b/benches/benches/bevy_ecs/components/add_remove_table.rs @@ -0,0 +1,30 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(f32); +#[derive(Component)] +struct B(f32); + +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + let mut entities = Vec::with_capacity(10_000); + for _ in 0..10_000 { + entities.push(world.spawn().insert(A(0.0)).id()); + } + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0.entity_mut(*entity).insert(B(0.0)); + } + + for entity in &self.1 { + self.0.entity_mut(*entity).remove::(); + } + } +} diff --git a/benches/benches/bevy_ecs/components/archetype_updates.rs b/benches/benches/bevy_ecs/components/archetype_updates.rs new file mode 100644 index 0000000000000..26adacd816a6e --- /dev/null +++ b/benches/benches/bevy_ecs/components/archetype_updates.rs @@ -0,0 +1,116 @@ +use bevy_ecs::{ + component::Component, + schedule::{Stage, SystemStage}, + world::World, +}; +use criterion::{BenchmarkId, Criterion}; + +#[derive(Component)] +struct A(f32); + +fn setup(system_count: usize) -> (World, SystemStage) { + let mut world = World::new(); + fn empty() {} + let mut stage = SystemStage::parallel(); + for _ in 0..system_count { + stage.add_system(empty); + } + stage.run(&mut world); + (world, stage) +} + +/// create `count` entities with distinct archetypes +fn add_archetypes(world: &mut World, count: u16) { + for i in 0..count { + let mut e = world.spawn(); + if i & 1 << 0 != 0 { + e.insert(A::<0>(1.0)); + } + if i & 1 << 1 != 0 { + e.insert(A::<1>(1.0)); + } + if i & 1 << 2 != 0 { + e.insert(A::<2>(1.0)); + } + if i & 1 << 3 != 0 { + e.insert(A::<3>(1.0)); + } + if i & 1 << 4 != 0 { + e.insert(A::<4>(1.0)); + } + if i & 1 << 5 != 0 { + e.insert(A::<5>(1.0)); + } + if i & 1 << 6 != 0 { + e.insert(A::<6>(1.0)); + } + if i & 1 << 7 != 0 { + e.insert(A::<7>(1.0)); + } + if i & 1 << 8 != 0 { + e.insert(A::<8>(1.0)); + } + if i & 1 << 9 != 0 { + e.insert(A::<9>(1.0)); + } + if i & 1 << 10 != 0 { + e.insert(A::<10>(1.0)); + } + if i & 1 << 11 != 0 { + e.insert(A::<11>(1.0)); + } + if i & 1 << 12 != 0 { + e.insert(A::<12>(1.0)); + } + if i & 1 << 13 != 0 { + e.insert(A::<13>(1.0)); + } + if i & 1 << 14 != 0 { + e.insert(A::<14>(1.0)); + } + if i & 1 << 15 != 0 { + e.insert(A::<15>(1.0)); + } + } +} + +pub fn no_archetypes(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("no_archetypes"); + for i in 0..=5 { + let system_count = i * 20; + let (mut world, mut stage) = setup(system_count); + group.bench_with_input( + BenchmarkId::new("system_count", system_count), + &system_count, + |bencher, &_system_count| { + bencher.iter(|| { + stage.run(&mut world); + }); + }, + ); + } +} + +pub fn added_archetypes(criterion: &mut Criterion) { + const SYSTEM_COUNT: usize = 100; + let mut group = criterion.benchmark_group("added_archetypes"); + for archetype_count in [100, 200, 500, 1000, 2000, 5000, 10000] { + group.bench_with_input( + BenchmarkId::new("archetype_count", archetype_count), + &archetype_count, + |bencher, &archetype_count| { + bencher.iter_batched( + || { + let (mut world, stage) = setup(SYSTEM_COUNT); + add_archetypes(&mut world, archetype_count); + (world, stage) + }, + |(mut world, mut stage)| { + stage.run(&mut world); + }, + criterion::BatchSize::LargeInput, + ) + }, + ); + } +} diff --git a/benches/benches/bevy_ecs/components/insert_simple.rs b/benches/benches/bevy_ecs/components/insert_simple.rs new file mode 100644 index 0000000000000..17ebc24593e3c --- /dev/null +++ b/benches/benches/bevy_ecs/components/insert_simple.rs @@ -0,0 +1,34 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark; + +impl Benchmark { + pub fn new() -> Self { + Self + } + + pub fn run(&mut self) { + let mut world = World::new(); + world.spawn_batch((0..10_000).map(|_| { + ( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + ) + })); + } +} diff --git a/benches/benches/bevy_ecs/components/insert_simple_unbatched.rs b/benches/benches/bevy_ecs/components/insert_simple_unbatched.rs new file mode 100644 index 0000000000000..daca82a18c895 --- /dev/null +++ b/benches/benches/bevy_ecs/components/insert_simple_unbatched.rs @@ -0,0 +1,34 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark; + +impl Benchmark { + pub fn new() -> Self { + Self + } + + pub fn run(&mut self) { + let mut world = World::new(); + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + } +} diff --git a/benches/benches/bevy_ecs/components/mod.rs b/benches/benches/bevy_ecs/components/mod.rs new file mode 100644 index 0000000000000..419f938c91b23 --- /dev/null +++ b/benches/benches/bevy_ecs/components/mod.rs @@ -0,0 +1,65 @@ +use criterion::*; + +mod add_remove_big_sparse_set; +mod add_remove_big_table; +mod add_remove_sparse_set; +mod add_remove_table; +mod archetype_updates; +mod insert_simple; +mod insert_simple_unbatched; + +use archetype_updates::*; + +criterion_group!( + components_benches, + add_remove, + add_remove_big, + insert_simple, + no_archetypes, + added_archetypes, +); + +fn add_remove(c: &mut Criterion) { + let mut group = c.benchmark_group("add_remove"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("table", |b| { + let mut bench = add_remove_table::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("sparse_set", |b| { + let mut bench = add_remove_sparse_set::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn add_remove_big(c: &mut Criterion) { + let mut group = c.benchmark_group("add_remove_big"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("table", |b| { + let mut bench = add_remove_big_table::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("sparse_set", |b| { + let mut bench = add_remove_big_sparse_set::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn insert_simple(c: &mut Criterion) { + let mut group = c.benchmark_group("insert_simple"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = insert_simple::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("unbatched", |b| { + let mut bench = insert_simple_unbatched::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} diff --git a/benches/benches/bevy_ecs/iteration/heavy_compute.rs b/benches/benches/bevy_ecs/iteration/heavy_compute.rs new file mode 100644 index 0000000000000..440d1bcb22f6f --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/heavy_compute.rs @@ -0,0 +1,53 @@ +use bevy_ecs::prelude::*; +use bevy_tasks::{ComputeTaskPool, TaskPool}; +use criterion::Criterion; +use glam::*; + +pub fn heavy_compute(c: &mut Criterion) { + #[derive(Component, Copy, Clone)] + struct Position(Vec3); + + #[derive(Component, Copy, Clone)] + struct Rotation(Vec3); + + #[derive(Component, Copy, Clone)] + struct Velocity(Vec3); + + #[derive(Component, Copy, Clone)] + struct Transform(Mat4); + + let mut group = c.benchmark_group("heavy_compute"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + ComputeTaskPool::init(TaskPool::default); + + let mut world = World::default(); + + world.spawn_batch((0..1000).map(|_| { + ( + Transform(Mat4::from_axis_angle(Vec3::X, 1.2)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + ) + })); + + fn sys(mut query: Query<(&mut Position, &mut Transform)>) { + query.par_for_each_mut(128, |(mut pos, mut mat)| { + for _ in 0..100 { + mat.0 = mat.0.inverse(); + } + + pos.0 = mat.0.transform_vector3(pos.0); + }); + } + + let mut system = IntoSystem::into_system(sys); + system.initialize(&mut world); + system.update_archetype_component_access(&world); + + b.iter(move || system.run((), &mut world)); + }); + group.finish(); +} diff --git a/benches/benches/bevy_ecs/iteration/iter_frag.rs b/benches/benches/bevy_ecs/iteration/iter_frag.rs new file mode 100644 index 0000000000000..dcc675f6551fa --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_frag.rs @@ -0,0 +1,35 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..20 { + $world.spawn().insert_bundle(($variants(0.0), Data(1.0))); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<&'w mut Data>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); + + let query = world.query::<&mut Data>(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut data in self.1.iter_mut(&mut self.0) { + data.0 *= 2.0; + } + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_frag_foreach.rs b/benches/benches/bevy_ecs/iteration/iter_frag_foreach.rs new file mode 100644 index 0000000000000..16cade377f333 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_frag_foreach.rs @@ -0,0 +1,35 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..20 { + $world.spawn().insert_bundle(($variants(0.0), Data(1.0))); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<&'w mut Data>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); + + let query = world.query::<&mut Data>(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut data| { + data.0 *= 2.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_frag_foreach_sparse.rs b/benches/benches/bevy_ecs/iteration/iter_frag_foreach_sparse.rs new file mode 100644 index 0000000000000..4cec9fe20c739 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_frag_foreach_sparse.rs @@ -0,0 +1,46 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..5 { + $world.spawn().insert($variants(0.0)); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<&'w mut Data>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + for _ in 0..5 { + world.spawn().insert(Data(1.0)); + } + + create_entities!(world; C00, C01, C02, C03, C04, C05, C06, C07, C08, C09); + create_entities!(world; C10, C11, C12, C13, C14, C15, C16, C17, C18, C19); + create_entities!(world; C20, C21, C22, C23, C24, C25, C26, C27, C28, C29); + create_entities!(world; C30, C31, C32, C33, C34, C35, C36, C37, C38, C39); + create_entities!(world; C40, C41, C42, C43, C44, C45, C46, C47, C48, C49); + create_entities!(world; C50, C51, C52, C53, C54, C55, C56, C57, C58, C59); + create_entities!(world; C60, C61, C62, C63, C64, C65, C66, C67, C68, C69); + create_entities!(world; C70, C71, C72, C73, C74, C75, C76, C77, C78, C79); + create_entities!(world; C80, C81, C82, C83, C84, C85, C86, C87, C88, C89); + create_entities!(world; C90, C91, C92, C93, C94, C95, C96, C97, C98, C99); + let query = world.query::<&mut Data>(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut data| { + data.0 *= 2.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_frag_foreach_wide.rs b/benches/benches/bevy_ecs/iteration/iter_frag_foreach_wide.rs new file mode 100644 index 0000000000000..e955982cdd4a4 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_frag_foreach_wide.rs @@ -0,0 +1,73 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..20 { + $world.spawn().insert_bundle(( + $variants(0.0), + Data::<0>(1.0), + Data::<1>(1.0), + Data::<2>(1.0), + Data::<3>(1.0), + Data::<4>(1.0), + Data::<5>(1.0), + Data::<6>(1.0), + Data::<7>(1.0), + Data::<8>(1.0), + Data::<9>(1.0), + Data::<10>(1.0), + )); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>( + World, + QueryState<( + &'w mut Data<0>, + &'w mut Data<1>, + &'w mut Data<2>, + &'w mut Data<3>, + &'w mut Data<4>, + &'w mut Data<5>, + &'w mut Data<6>, + &'w mut Data<7>, + &'w mut Data<8>, + &'w mut Data<9>, + &'w mut Data<10>, + )>, +); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut data| { + data.0 .0 *= 2.0; + data.1 .0 *= 2.0; + data.2 .0 *= 2.0; + data.3 .0 *= 2.0; + data.4 .0 *= 2.0; + data.5 .0 *= 2.0; + data.6 .0 *= 2.0; + data.7 .0 *= 2.0; + data.8 .0 *= 2.0; + data.9 .0 *= 2.0; + data.10 .0 *= 2.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_frag_foreach_wide_sparse.rs b/benches/benches/bevy_ecs/iteration/iter_frag_foreach_wide_sparse.rs new file mode 100644 index 0000000000000..f7d2a066f9fc7 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_frag_foreach_wide_sparse.rs @@ -0,0 +1,83 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..5 { + $world.spawn().insert($variants(0.0)); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>( + World, + QueryState<( + &'w mut Data<0>, + &'w mut Data<1>, + &'w mut Data<2>, + &'w mut Data<3>, + &'w mut Data<4>, + &'w mut Data<5>, + &'w mut Data<6>, + &'w mut Data<7>, + &'w mut Data<8>, + &'w mut Data<9>, + &'w mut Data<10>, + )>, +); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + for _ in 0..5 { + world.spawn().insert_bundle(( + Data::<0>(1.0), + Data::<1>(1.0), + Data::<2>(1.0), + Data::<3>(1.0), + Data::<4>(1.0), + Data::<5>(1.0), + Data::<6>(1.0), + Data::<7>(1.0), + Data::<8>(1.0), + Data::<9>(1.0), + Data::<10>(1.0), + )); + } + + create_entities!(world; C00, C01, C02, C03, C04, C05, C06, C07, C08, C09); + create_entities!(world; C10, C11, C12, C13, C14, C15, C16, C17, C18, C19); + create_entities!(world; C20, C21, C22, C23, C24, C25, C26, C27, C28, C29); + create_entities!(world; C30, C31, C32, C33, C34, C35, C36, C37, C38, C39); + create_entities!(world; C40, C41, C42, C43, C44, C45, C46, C47, C48, C49); + create_entities!(world; C50, C51, C52, C53, C54, C55, C56, C57, C58, C59); + create_entities!(world; C60, C61, C62, C63, C64, C65, C66, C67, C68, C69); + create_entities!(world; C70, C71, C72, C73, C74, C75, C76, C77, C78, C79); + create_entities!(world; C80, C81, C82, C83, C84, C85, C86, C87, C88, C89); + create_entities!(world; C90, C91, C92, C93, C94, C95, C96, C97, C98, C99); + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut data| { + data.0 .0 *= 2.0; + data.1 .0 *= 2.0; + data.2 .0 *= 2.0; + data.3 .0 *= 2.0; + data.4 .0 *= 2.0; + data.5 .0 *= 2.0; + data.6 .0 *= 2.0; + data.7 .0 *= 2.0; + data.8 .0 *= 2.0; + data.9 .0 *= 2.0; + data.10 .0 *= 2.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_frag_sparse.rs b/benches/benches/bevy_ecs/iteration/iter_frag_sparse.rs new file mode 100644 index 0000000000000..0778b65429b61 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_frag_sparse.rs @@ -0,0 +1,46 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..5 { + $world.spawn().insert($variants(0.0)); + } + )* + }; +} +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<&'w mut Data>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + for _ in 0..5 { + world.spawn().insert(Data(1.0)); + } + + create_entities!(world; C00, C01, C02, C03, C04, C05, C06, C07, C08, C09); + create_entities!(world; C10, C11, C12, C13, C14, C15, C16, C17, C18, C19); + create_entities!(world; C20, C21, C22, C23, C24, C25, C26, C27, C28, C29); + create_entities!(world; C30, C31, C32, C33, C34, C35, C36, C37, C38, C39); + create_entities!(world; C40, C41, C42, C43, C44, C45, C46, C47, C48, C49); + create_entities!(world; C50, C51, C52, C53, C54, C55, C56, C57, C58, C59); + create_entities!(world; C60, C61, C62, C63, C64, C65, C66, C67, C68, C69); + create_entities!(world; C70, C71, C72, C73, C74, C75, C76, C77, C78, C79); + create_entities!(world; C80, C81, C82, C83, C84, C85, C86, C87, C88, C89); + create_entities!(world; C90, C91, C92, C93, C94, C95, C96, C97, C98, C99); + let query = world.query::<&mut Data>(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut data in self.1.iter_mut(&mut self.0) { + data.0 *= 2.0; + } + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_frag_wide.rs b/benches/benches/bevy_ecs/iteration/iter_frag_wide.rs new file mode 100644 index 0000000000000..024a7bfbdf450 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_frag_wide.rs @@ -0,0 +1,73 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..20 { + $world.spawn().insert_bundle(( + $variants(0.0), + Data::<0>(1.0), + Data::<1>(1.0), + Data::<2>(1.0), + Data::<3>(1.0), + Data::<4>(1.0), + Data::<5>(1.0), + Data::<6>(1.0), + Data::<7>(1.0), + Data::<8>(1.0), + Data::<9>(1.0), + Data::<10>(1.0), + )); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>( + World, + QueryState<( + &'w mut Data<0>, + &'w mut Data<1>, + &'w mut Data<2>, + &'w mut Data<3>, + &'w mut Data<4>, + &'w mut Data<5>, + &'w mut Data<6>, + &'w mut Data<7>, + &'w mut Data<8>, + &'w mut Data<9>, + &'w mut Data<10>, + )>, +); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut data in self.1.iter_mut(&mut self.0) { + data.0 .0 *= 2.0; + data.1 .0 *= 2.0; + data.2 .0 *= 2.0; + data.3 .0 *= 2.0; + data.4 .0 *= 2.0; + data.5 .0 *= 2.0; + data.6 .0 *= 2.0; + data.7 .0 *= 2.0; + data.8 .0 *= 2.0; + data.9 .0 *= 2.0; + data.10 .0 *= 2.0; + } + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_frag_wide_sparse.rs b/benches/benches/bevy_ecs/iteration/iter_frag_wide_sparse.rs new file mode 100644 index 0000000000000..c3393a240e338 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_frag_wide_sparse.rs @@ -0,0 +1,83 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..5 { + $world.spawn().insert($variants(0.0)); + } + )* + }; +} +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>( + World, + QueryState<( + &'w mut Data<0>, + &'w mut Data<1>, + &'w mut Data<2>, + &'w mut Data<3>, + &'w mut Data<4>, + &'w mut Data<5>, + &'w mut Data<6>, + &'w mut Data<7>, + &'w mut Data<8>, + &'w mut Data<9>, + &'w mut Data<10>, + )>, +); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + for _ in 0..5 { + world.spawn().insert_bundle(( + Data::<0>(1.0), + Data::<1>(1.0), + Data::<2>(1.0), + Data::<3>(1.0), + Data::<4>(1.0), + Data::<5>(1.0), + Data::<6>(1.0), + Data::<7>(1.0), + Data::<8>(1.0), + Data::<9>(1.0), + Data::<10>(1.0), + )); + } + + create_entities!(world; C00, C01, C02, C03, C04, C05, C06, C07, C08, C09); + create_entities!(world; C10, C11, C12, C13, C14, C15, C16, C17, C18, C19); + create_entities!(world; C20, C21, C22, C23, C24, C25, C26, C27, C28, C29); + create_entities!(world; C30, C31, C32, C33, C34, C35, C36, C37, C38, C39); + create_entities!(world; C40, C41, C42, C43, C44, C45, C46, C47, C48, C49); + create_entities!(world; C50, C51, C52, C53, C54, C55, C56, C57, C58, C59); + create_entities!(world; C60, C61, C62, C63, C64, C65, C66, C67, C68, C69); + create_entities!(world; C70, C71, C72, C73, C74, C75, C76, C77, C78, C79); + create_entities!(world; C80, C81, C82, C83, C84, C85, C86, C87, C88, C89); + create_entities!(world; C90, C91, C92, C93, C94, C95, C96, C97, C98, C99); + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut data in self.1.iter_mut(&mut self.0) { + data.0 .0 *= 2.0; + data.1 .0 *= 2.0; + data.2 .0 *= 2.0; + data.3 .0 *= 2.0; + data.4 .0 *= 2.0; + data.5 .0 *= 2.0; + data.6 .0 *= 2.0; + data.7 .0 *= 2.0; + data.8 .0 *= 2.0; + data.9 .0 *= 2.0; + data.10 .0 *= 2.0; + } + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple.rs b/benches/benches/bevy_ecs/iteration/iter_simple.rs new file mode 100644 index 0000000000000..5d99e19dba1f0 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple.rs @@ -0,0 +1,41 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + let query = world.query::<(&Velocity, &mut Position)>(); + Self(world, query) + } + + pub fn run(&mut self) { + for (velocity, mut position) in self.1.iter_mut(&mut self.0) { + position.0 += velocity.0; + } + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple_foreach.rs b/benches/benches/bevy_ecs/iteration/iter_simple_foreach.rs new file mode 100644 index 0000000000000..ae5b5c8757676 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple_foreach.rs @@ -0,0 +1,42 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + let query = world.query::<(&Velocity, &mut Position)>(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1 + .for_each_mut(&mut self.0, |(velocity, mut position)| { + position.0 += velocity.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple_foreach_sparse_set.rs b/benches/benches/bevy_ecs/iteration/iter_simple_foreach_sparse_set.rs new file mode 100644 index 0000000000000..98e7ea3f74fa7 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple_foreach_sparse_set.rs @@ -0,0 +1,44 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + let query = world.query::<(&Velocity, &mut Position)>(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1 + .for_each_mut(&mut self.0, |(velocity, mut position)| { + position.0 += velocity.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple_foreach_wide.rs b/benches/benches/bevy_ecs/iteration/iter_simple_foreach_wide.rs new file mode 100644 index 0000000000000..a5a0cea163b89 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple_foreach_wide.rs @@ -0,0 +1,66 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark<'w>( + World, + QueryState<( + &'w Velocity<0>, + &'w mut Position<0>, + &'w Velocity<1>, + &'w mut Position<1>, + &'w Velocity<2>, + &'w mut Position<2>, + &'w Velocity<3>, + &'w mut Position<3>, + &'w Velocity<4>, + &'w mut Position<4>, + )>, +); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Rotation(Vec3::X), + Position::<0>(Vec3::X), + Velocity::<0>(Vec3::X), + Position::<1>(Vec3::X), + Velocity::<1>(Vec3::X), + Position::<2>(Vec3::X), + Velocity::<2>(Vec3::X), + Position::<3>(Vec3::X), + Velocity::<3>(Vec3::X), + Position::<4>(Vec3::X), + Velocity::<4>(Vec3::X), + )); + } + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut item| { + item.1 .0 += item.0 .0; + item.3 .0 += item.2 .0; + item.5 .0 += item.4 .0; + item.7 .0 += item.6 .0; + }); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple_foreach_wide_sparse_set.rs b/benches/benches/bevy_ecs/iteration/iter_simple_foreach_wide_sparse_set.rs new file mode 100644 index 0000000000000..d357393c310b6 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple_foreach_wide_sparse_set.rs @@ -0,0 +1,68 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Velocity(Vec3); + +pub struct Benchmark<'w>( + World, + QueryState<( + &'w Velocity<0>, + &'w mut Position<0>, + &'w Velocity<1>, + &'w mut Position<1>, + &'w Velocity<2>, + &'w mut Position<2>, + &'w Velocity<3>, + &'w mut Position<3>, + &'w Velocity<4>, + &'w mut Position<4>, + )>, +); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Rotation(Vec3::X), + Position::<0>(Vec3::X), + Velocity::<0>(Vec3::X), + Position::<1>(Vec3::X), + Velocity::<1>(Vec3::X), + Position::<2>(Vec3::X), + Velocity::<2>(Vec3::X), + Position::<3>(Vec3::X), + Velocity::<3>(Vec3::X), + Position::<4>(Vec3::X), + Velocity::<4>(Vec3::X), + )); + } + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut item| { + item.1 .0 += item.0 .0; + item.3 .0 += item.2 .0; + item.5 .0 += item.4 .0; + item.7 .0 += item.6 .0; + }); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple_sparse_set.rs b/benches/benches/bevy_ecs/iteration/iter_simple_sparse_set.rs new file mode 100644 index 0000000000000..676d4c4c4eff9 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple_sparse_set.rs @@ -0,0 +1,43 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + let query = world.query::<(&Velocity, &mut Position)>(); + Self(world, query) + } + + pub fn run(&mut self) { + for (velocity, mut position) in self.1.iter_mut(&mut self.0) { + position.0 += velocity.0; + } + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple_system.rs b/benches/benches/bevy_ecs/iteration/iter_simple_system.rs new file mode 100644 index 0000000000000..f120b0015f169 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple_system.rs @@ -0,0 +1,47 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark(World, Box>); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + fn query_system(mut query: Query<(&Velocity, &mut Position)>) { + for (velocity, mut position) in &mut query { + position.0 += velocity.0; + } + } + + let mut system = IntoSystem::into_system(query_system); + system.initialize(&mut world); + system.update_archetype_component_access(&world); + Self(world, Box::new(system)) + } + + pub fn run(&mut self) { + self.1.run((), &mut self.0); + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple_wide.rs b/benches/benches/bevy_ecs/iteration/iter_simple_wide.rs new file mode 100644 index 0000000000000..a182531404f5f --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple_wide.rs @@ -0,0 +1,66 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark<'w>( + World, + QueryState<( + &'w Velocity<0>, + &'w mut Position<0>, + &'w Velocity<1>, + &'w mut Position<1>, + &'w Velocity<2>, + &'w mut Position<2>, + &'w Velocity<3>, + &'w mut Position<3>, + &'w Velocity<4>, + &'w mut Position<4>, + )>, +); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Rotation(Vec3::X), + Position::<0>(Vec3::X), + Velocity::<0>(Vec3::X), + Position::<1>(Vec3::X), + Velocity::<1>(Vec3::X), + Position::<2>(Vec3::X), + Velocity::<2>(Vec3::X), + Position::<3>(Vec3::X), + Velocity::<3>(Vec3::X), + Position::<4>(Vec3::X), + Velocity::<4>(Vec3::X), + )); + } + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut item in self.1.iter_mut(&mut self.0) { + item.1 .0 += item.0 .0; + item.3 .0 += item.2 .0; + item.5 .0 += item.4 .0; + item.7 .0 += item.6 .0; + } + } +} diff --git a/benches/benches/bevy_ecs/iteration/iter_simple_wide_sparse_set.rs b/benches/benches/bevy_ecs/iteration/iter_simple_wide_sparse_set.rs new file mode 100644 index 0000000000000..88de1f225fd94 --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/iter_simple_wide_sparse_set.rs @@ -0,0 +1,68 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Velocity(Vec3); + +pub struct Benchmark<'w>( + World, + QueryState<( + &'w Velocity<0>, + &'w mut Position<0>, + &'w Velocity<1>, + &'w mut Position<1>, + &'w Velocity<2>, + &'w mut Position<2>, + &'w Velocity<3>, + &'w mut Position<3>, + &'w Velocity<4>, + &'w mut Position<4>, + )>, +); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Rotation(Vec3::X), + Position::<0>(Vec3::X), + Velocity::<0>(Vec3::X), + Position::<1>(Vec3::X), + Velocity::<1>(Vec3::X), + Position::<2>(Vec3::X), + Velocity::<2>(Vec3::X), + Position::<3>(Vec3::X), + Velocity::<3>(Vec3::X), + Position::<4>(Vec3::X), + Velocity::<4>(Vec3::X), + )); + } + + let query = world.query(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut item in self.1.iter_mut(&mut self.0) { + item.1 .0 += item.0 .0; + item.3 .0 += item.2 .0; + item.5 .0 += item.4 .0; + item.7 .0 += item.6 .0; + } + } +} diff --git a/benches/benches/bevy_ecs/iteration/mod.rs b/benches/benches/bevy_ecs/iteration/mod.rs new file mode 100644 index 0000000000000..e3ed6a6afeabe --- /dev/null +++ b/benches/benches/bevy_ecs/iteration/mod.rs @@ -0,0 +1,119 @@ +use criterion::*; + +mod heavy_compute; +mod iter_frag; +mod iter_frag_foreach; +mod iter_frag_foreach_sparse; +mod iter_frag_foreach_wide; +mod iter_frag_foreach_wide_sparse; +mod iter_frag_sparse; +mod iter_frag_wide; +mod iter_frag_wide_sparse; +mod iter_simple; +mod iter_simple_foreach; +mod iter_simple_foreach_sparse_set; +mod iter_simple_foreach_wide; +mod iter_simple_foreach_wide_sparse_set; +mod iter_simple_sparse_set; +mod iter_simple_system; +mod iter_simple_wide; +mod iter_simple_wide_sparse_set; + +use heavy_compute::*; + +criterion_group!( + iterations_benches, + iter_frag, + iter_frag_sparse, + iter_simple, + heavy_compute, +); + +fn iter_simple(c: &mut Criterion) { + let mut group = c.benchmark_group("iter_simple"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = iter_simple::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("wide", |b| { + let mut bench = iter_simple_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("system", |b| { + let mut bench = iter_simple_system::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("sparse_set", |b| { + let mut bench = iter_simple_sparse_set::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("wide_sparse_set", |b| { + let mut bench = iter_simple_wide_sparse_set::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach", |b| { + let mut bench = iter_simple_foreach::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach_wide", |b| { + let mut bench = iter_simple_foreach_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach_sparse_set", |b| { + let mut bench = iter_simple_foreach_sparse_set::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach_wide_sparse_set", |b| { + let mut bench = iter_simple_foreach_wide_sparse_set::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn iter_frag(c: &mut Criterion) { + let mut group = c.benchmark_group("iter_fragmented"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = iter_frag::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("wide", |b| { + let mut bench = iter_frag_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach", |b| { + let mut bench = iter_frag_foreach::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach_wide", |b| { + let mut bench = iter_frag_foreach_wide::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn iter_frag_sparse(c: &mut Criterion) { + let mut group = c.benchmark_group("iter_fragmented_sparse"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = iter_frag_sparse::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("wide", |b| { + let mut bench = iter_frag_wide_sparse::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach", |b| { + let mut bench = iter_frag_foreach_sparse::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach_wide", |b| { + let mut bench = iter_frag_foreach_wide_sparse::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} diff --git a/benches/benches/bevy_ecs/scheduling/mod.rs b/benches/benches/bevy_ecs/scheduling/mod.rs new file mode 100644 index 0000000000000..21ccc56c04f63 --- /dev/null +++ b/benches/benches/bevy_ecs/scheduling/mod.rs @@ -0,0 +1,24 @@ +use criterion::criterion_group; + +mod run_criteria; +mod schedule; +mod stages; + +use run_criteria::*; +use schedule::*; +use stages::*; + +criterion_group!( + scheduling_benches, + run_criteria_yes, + run_criteria_no, + run_criteria_yes_with_labels, + run_criteria_no_with_labels, + run_criteria_yes_with_query, + run_criteria_yes_with_resource, + empty_systems, + busy_systems, + contrived, + schedule, + build_schedule, +); diff --git a/benches/benches/bevy_ecs/scheduling/run_criteria.rs b/benches/benches/bevy_ecs/scheduling/run_criteria.rs new file mode 100644 index 0000000000000..11a0b1efd56e8 --- /dev/null +++ b/benches/benches/bevy_ecs/scheduling/run_criteria.rs @@ -0,0 +1,204 @@ +use bevy_ecs::{ + component::Component, + prelude::{ParallelSystemDescriptorCoercion, Res, Resource, RunCriteriaDescriptorCoercion}, + schedule::{ShouldRun, Stage, SystemStage}, + system::Query, + world::World, +}; +use criterion::Criterion; + +fn run_stage(stage: &mut SystemStage, world: &mut World) { + stage.run(world); +} + +pub fn run_criteria_yes(criterion: &mut Criterion) { + let mut world = World::new(); + let mut group = criterion.benchmark_group("run_criteria/yes"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + fn empty() {} + fn always_yes() -> ShouldRun { + ShouldRun::Yes + } + for amount in 0..21 { + let mut stage = SystemStage::parallel(); + stage.add_system(empty.with_run_criteria(always_yes)); + for _ in 0..amount { + // TODO: should change this to use a label or have another bench that uses a label instead + stage + .add_system(empty.with_run_criteria(always_yes)) + .add_system(empty.with_run_criteria(always_yes)) + .add_system(empty.with_run_criteria(always_yes)) + .add_system(empty.with_run_criteria(always_yes)) + .add_system(empty.with_run_criteria(always_yes)); + } + // run once to initialize systems + run_stage(&mut stage, &mut world); + group.bench_function(&format!("{:03}_systems", 5 * amount + 1), |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }); + } + group.finish(); +} + +pub fn run_criteria_no(criterion: &mut Criterion) { + let mut world = World::new(); + let mut group = criterion.benchmark_group("run_criteria/no"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + fn empty() {} + fn always_no() -> ShouldRun { + ShouldRun::No + } + for amount in 0..21 { + let mut stage = SystemStage::parallel(); + stage.add_system(empty.with_run_criteria(always_no)); + for _ in 0..amount { + stage + .add_system(empty.with_run_criteria(always_no)) + .add_system(empty.with_run_criteria(always_no)) + .add_system(empty.with_run_criteria(always_no)) + .add_system(empty.with_run_criteria(always_no)) + .add_system(empty.with_run_criteria(always_no)); + } + // run once to initialize systems + run_stage(&mut stage, &mut world); + group.bench_function(&format!("{:03}_systems", 5 * amount + 1), |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }); + } + group.finish(); +} + +pub fn run_criteria_yes_with_labels(criterion: &mut Criterion) { + let mut world = World::new(); + let mut group = criterion.benchmark_group("run_criteria/yes_with_labels"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + fn empty() {} + fn always_yes() -> ShouldRun { + ShouldRun::Yes + } + for amount in 0..21 { + let mut stage = SystemStage::parallel(); + stage.add_system(empty.with_run_criteria(always_yes.label("always yes"))); + for _ in 0..amount { + stage + .add_system(empty.with_run_criteria("always yes")) + .add_system(empty.with_run_criteria("always yes")) + .add_system(empty.with_run_criteria("always yes")) + .add_system(empty.with_run_criteria("always yes")) + .add_system(empty.with_run_criteria("always yes")); + } + // run once to initialize systems + run_stage(&mut stage, &mut world); + group.bench_function(&format!("{:03}_systems", 5 * amount + 1), |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }); + } + group.finish(); +} + +pub fn run_criteria_no_with_labels(criterion: &mut Criterion) { + let mut world = World::new(); + let mut group = criterion.benchmark_group("run_criteria/no_with_labels"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + fn empty() {} + fn always_no() -> ShouldRun { + ShouldRun::No + } + for amount in 0..21 { + let mut stage = SystemStage::parallel(); + stage.add_system(empty.with_run_criteria(always_no.label("always no"))); + for _ in 0..amount { + stage + .add_system(empty.with_run_criteria("always no")) + .add_system(empty.with_run_criteria("always no")) + .add_system(empty.with_run_criteria("always no")) + .add_system(empty.with_run_criteria("always no")) + .add_system(empty.with_run_criteria("always no")); + } + // run once to initialize systems + run_stage(&mut stage, &mut world); + group.bench_function(&format!("{:03}_systems", 5 * amount + 1), |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }); + } + group.finish(); +} + +#[derive(Component, Resource)] +struct TestBool(pub bool); + +pub fn run_criteria_yes_with_query(criterion: &mut Criterion) { + let mut world = World::new(); + world.spawn().insert(TestBool(true)); + let mut group = criterion.benchmark_group("run_criteria/yes_using_query"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + fn empty() {} + fn yes_with_query(query: Query<&TestBool>) -> ShouldRun { + query.single().0.into() + } + for amount in 0..21 { + let mut stage = SystemStage::parallel(); + stage.add_system(empty.with_run_criteria(yes_with_query)); + for _ in 0..amount { + stage + .add_system(empty.with_run_criteria(yes_with_query)) + .add_system(empty.with_run_criteria(yes_with_query)) + .add_system(empty.with_run_criteria(yes_with_query)) + .add_system(empty.with_run_criteria(yes_with_query)) + .add_system(empty.with_run_criteria(yes_with_query)); + } + // run once to initialize systems + run_stage(&mut stage, &mut world); + group.bench_function(&format!("{:03}_systems", 5 * amount + 1), |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }); + } + group.finish(); +} + +pub fn run_criteria_yes_with_resource(criterion: &mut Criterion) { + let mut world = World::new(); + world.insert_resource(TestBool(true)); + let mut group = criterion.benchmark_group("run_criteria/yes_using_resource"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + fn empty() {} + fn yes_with_resource(res: Res) -> ShouldRun { + res.0.into() + } + for amount in 0..21 { + let mut stage = SystemStage::parallel(); + stage.add_system(empty.with_run_criteria(yes_with_resource)); + for _ in 0..amount { + stage + .add_system(empty.with_run_criteria(yes_with_resource)) + .add_system(empty.with_run_criteria(yes_with_resource)) + .add_system(empty.with_run_criteria(yes_with_resource)) + .add_system(empty.with_run_criteria(yes_with_resource)) + .add_system(empty.with_run_criteria(yes_with_resource)); + } + // run once to initialize systems + run_stage(&mut stage, &mut world); + group.bench_function(&format!("{:03}_systems", 5 * amount + 1), |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }); + } + group.finish(); +} diff --git a/benches/benches/bevy_ecs/scheduling/schedule.rs b/benches/benches/bevy_ecs/scheduling/schedule.rs new file mode 100644 index 0000000000000..49de37079b73b --- /dev/null +++ b/benches/benches/bevy_ecs/scheduling/schedule.rs @@ -0,0 +1,131 @@ +use bevy_app::App; +use bevy_ecs::prelude::*; +use criterion::Criterion; + +pub fn schedule(c: &mut Criterion) { + #[derive(Component)] + struct A(f32); + #[derive(Component)] + struct B(f32); + #[derive(Component)] + struct C(f32); + #[derive(Component)] + struct D(f32); + #[derive(Component)] + struct E(f32); + + fn ab(mut query: Query<(&mut A, &mut B)>) { + query.for_each_mut(|(mut a, mut b)| { + std::mem::swap(&mut a.0, &mut b.0); + }); + } + + fn cd(mut query: Query<(&mut C, &mut D)>) { + query.for_each_mut(|(mut c, mut d)| { + std::mem::swap(&mut c.0, &mut d.0); + }); + } + + fn ce(mut query: Query<(&mut C, &mut E)>) { + query.for_each_mut(|(mut c, mut e)| { + std::mem::swap(&mut c.0, &mut e.0); + }); + } + + let mut group = c.benchmark_group("schedule"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut world = World::default(); + + world.spawn_batch((0..10000).map(|_| (A(0.0), B(0.0)))); + + world.spawn_batch((0..10000).map(|_| (A(0.0), B(0.0), C(0.0)))); + + world.spawn_batch((0..10000).map(|_| (A(0.0), B(0.0), C(0.0), D(0.0)))); + + world.spawn_batch((0..10000).map(|_| (A(0.0), B(0.0), C(0.0), E(0.0)))); + + let mut stage = SystemStage::parallel(); + stage.add_system(ab); + stage.add_system(cd); + stage.add_system(ce); + stage.run(&mut world); + + b.iter(move || stage.run(&mut world)); + }); + group.finish(); +} + +pub fn build_schedule(criterion: &mut Criterion) { + // empty system + fn empty_system() {} + + // Use multiple different kinds of label to ensure that dynamic dispatch + // doesn't somehow get optimized away. + #[derive(Debug, Clone, Copy)] + struct NumLabel(usize); + #[derive(Debug, Clone, Copy, SystemLabel)] + struct DummyLabel; + + impl SystemLabel for NumLabel { + fn as_str(&self) -> &'static str { + let s = self.0.to_string(); + Box::leak(s.into_boxed_str()) + } + } + + let mut group = criterion.benchmark_group("build_schedule"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(15)); + + // Method: generate a set of `graph_size` systems which have a One True Ordering. + // Add system to the stage with full constraints. Hopefully this should be maximimally + // difficult for bevy to figure out. + // Also, we are performing the `as_label` operation outside of the loop since that + // requires an allocation and a leak. This is not something that would be necessary in a + // real scenario, just a contrivance for the benchmark. + let labels: Vec<_> = (0..1000).map(|i| NumLabel(i).as_label()).collect(); + + // Benchmark graphs of different sizes. + for graph_size in [100, 500, 1000] { + // Basic benchmark without constraints. + group.bench_function(format!("{graph_size}_schedule_noconstraints"), |bencher| { + bencher.iter(|| { + let mut app = App::new(); + for _ in 0..graph_size { + app.add_system(empty_system); + } + app.update(); + }); + }); + + // Benchmark with constraints. + group.bench_function(format!("{graph_size}_schedule"), |bencher| { + bencher.iter(|| { + let mut app = App::new(); + app.add_system(empty_system.label(DummyLabel)); + + // Build a fully-connected dependency graph describing the One True Ordering. + // Not particularly realistic but this can be refined later. + for i in 0..graph_size { + let mut sys = empty_system.label(labels[i]).before(DummyLabel); + for a in 0..i { + sys = sys.after(labels[a]); + } + for b in i + 1..graph_size { + sys = sys.before(labels[b]); + } + app.add_system(sys); + } + // Run the app for a single frame. + // This is necessary since dependency resolution does not occur until the game runs. + // FIXME: Running the game clutters up the benchmarks, so ideally we'd be + // able to benchmark the dependency resolution directly. + app.update(); + }); + }); + } + + group.finish(); +} diff --git a/benches/benches/bevy_ecs/scheduling/stages.rs b/benches/benches/bevy_ecs/scheduling/stages.rs new file mode 100644 index 0000000000000..0f258f4f99f38 --- /dev/null +++ b/benches/benches/bevy_ecs/scheduling/stages.rs @@ -0,0 +1,162 @@ +use bevy_ecs::{ + component::Component, + schedule::{Stage, SystemStage}, + system::Query, + world::World, +}; +use criterion::Criterion; + +fn run_stage(stage: &mut SystemStage, world: &mut World) { + stage.run(world); +} + +#[derive(Component)] +struct A(f32); +#[derive(Component)] +struct B(f32); +#[derive(Component)] +struct C(f32); +#[derive(Component)] +struct D(f32); +#[derive(Component)] +struct E(f32); + +const ENTITY_BUNCH: usize = 5000; + +pub fn empty_systems(criterion: &mut Criterion) { + let mut world = World::new(); + let mut group = criterion.benchmark_group("empty_systems"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + fn empty() {} + for amount in 0..5 { + let mut stage = SystemStage::parallel(); + for _ in 0..amount { + stage.add_system(empty); + } + run_stage(&mut stage, &mut world); + group.bench_function(&format!("{:03}_systems", amount), |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }); + } + for amount in 1..21 { + let mut stage = SystemStage::parallel(); + for _ in 0..amount { + stage + .add_system(empty) + .add_system(empty) + .add_system(empty) + .add_system(empty) + .add_system(empty); + } + run_stage(&mut stage, &mut world); + group.bench_function(&format!("{:03}_systems", 5 * amount), |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }); + } + group.finish() +} + +pub fn busy_systems(criterion: &mut Criterion) { + fn ab(mut q: Query<(&mut A, &mut B)>) { + q.for_each_mut(|(mut a, mut b)| { + std::mem::swap(&mut a.0, &mut b.0); + }); + } + fn cd(mut q: Query<(&mut C, &mut D)>) { + q.for_each_mut(|(mut c, mut d)| { + std::mem::swap(&mut c.0, &mut d.0); + }); + } + fn ce(mut q: Query<(&mut C, &mut E)>) { + q.for_each_mut(|(mut c, mut e)| { + std::mem::swap(&mut c.0, &mut e.0); + }); + } + let mut world = World::new(); + let mut group = criterion.benchmark_group("busy_systems"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + for entity_bunches in 1..6 { + world.spawn_batch((0..4 * ENTITY_BUNCH).map(|_| (A(0.0), B(0.0)))); + world.spawn_batch((0..4 * ENTITY_BUNCH).map(|_| (A(0.0), B(0.0), C(0.0)))); + world.spawn_batch((0..ENTITY_BUNCH).map(|_| (A(0.0), B(0.0), C(0.0), D(0.0)))); + world.spawn_batch((0..ENTITY_BUNCH).map(|_| (A(0.0), B(0.0), C(0.0), E(0.0)))); + for system_amount in 0..5 { + let mut stage = SystemStage::parallel(); + stage.add_system(ab).add_system(cd).add_system(ce); + for _ in 0..system_amount { + stage.add_system(ab).add_system(cd).add_system(ce); + } + run_stage(&mut stage, &mut world); + group.bench_function( + &format!( + "{:02}x_entities_{:02}_systems", + entity_bunches, + 3 * system_amount + 3 + ), + |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }, + ); + } + } + group.finish() +} + +pub fn contrived(criterion: &mut Criterion) { + fn s_0(mut q_0: Query<(&mut A, &mut B)>) { + q_0.for_each_mut(|(mut c_0, mut c_1)| { + std::mem::swap(&mut c_0.0, &mut c_1.0); + }); + } + fn s_1(mut q_0: Query<(&mut A, &mut C)>, mut q_1: Query<(&mut B, &mut D)>) { + q_0.for_each_mut(|(mut c_0, mut c_1)| { + std::mem::swap(&mut c_0.0, &mut c_1.0); + }); + q_1.for_each_mut(|(mut c_0, mut c_1)| { + std::mem::swap(&mut c_0.0, &mut c_1.0); + }); + } + fn s_2(mut q_0: Query<(&mut C, &mut D)>) { + q_0.for_each_mut(|(mut c_0, mut c_1)| { + std::mem::swap(&mut c_0.0, &mut c_1.0); + }); + } + let mut world = World::new(); + let mut group = criterion.benchmark_group("contrived"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(3)); + for entity_bunches in 1..6 { + world.spawn_batch((0..ENTITY_BUNCH).map(|_| (A(0.0), B(0.0), C(0.0), D(0.0)))); + world.spawn_batch((0..ENTITY_BUNCH).map(|_| (A(0.0), B(0.0)))); + world.spawn_batch((0..ENTITY_BUNCH).map(|_| (C(0.0), D(0.0)))); + for system_amount in 0..5 { + let mut stage = SystemStage::parallel(); + stage.add_system(s_0).add_system(s_1).add_system(s_2); + for _ in 0..system_amount { + stage.add_system(s_0).add_system(s_1).add_system(s_2); + } + run_stage(&mut stage, &mut world); + group.bench_function( + &format!( + "{:02}x_entities_{:02}_systems", + entity_bunches, + 3 * system_amount + 3 + ), + |bencher| { + bencher.iter(|| { + run_stage(&mut stage, &mut world); + }); + }, + ); + } + } + group.finish() +} diff --git a/benches/benches/bevy_ecs/world/commands.rs b/benches/benches/bevy_ecs/world/commands.rs new file mode 100644 index 0000000000000..7688ce1d8759d --- /dev/null +++ b/benches/benches/bevy_ecs/world/commands.rs @@ -0,0 +1,263 @@ +use bevy_ecs::{ + component::Component, + entity::Entity, + system::{Command, CommandQueue, Commands}, + world::World, +}; +use criterion::{black_box, Criterion}; + +#[derive(Component)] +struct A; +#[derive(Component)] +struct B; +#[derive(Component)] +struct C; + +pub fn empty_commands(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("empty_commands"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + group.bench_function("0_entities", |bencher| { + let mut world = World::default(); + let mut command_queue = CommandQueue::default(); + + bencher.iter(|| { + command_queue.apply(&mut world); + }); + }); + + group.finish(); +} + +pub fn spawn_commands(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("spawn_commands"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in (1..5).map(|i| i * 2 * 1000) { + group.bench_function(format!("{}_entities", entity_count), |bencher| { + let mut world = World::default(); + let mut command_queue = CommandQueue::default(); + + bencher.iter(|| { + let mut commands = Commands::new(&mut command_queue, &world); + for i in 0..entity_count { + let mut entity = commands.spawn(); + + if black_box(i % 2 == 0) { + entity.insert(A); + } + + if black_box(i % 3 == 0) { + entity.insert(B); + } + + if black_box(i % 4 == 0) { + entity.insert(C); + } + + if black_box(i % 5 == 0) { + entity.despawn(); + } + } + drop(commands); + command_queue.apply(&mut world); + }); + }); + } + + group.finish(); +} + +#[derive(Default, Component)] +struct Matrix([[f32; 4]; 4]); + +#[derive(Default, Component)] +struct Vec3([f32; 3]); + +pub fn insert_commands(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("insert_commands"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + let entity_count = 10_000; + group.bench_function(format!("insert"), |bencher| { + let mut world = World::default(); + let mut command_queue = CommandQueue::default(); + let mut entities = Vec::new(); + for _ in 0..entity_count { + entities.push(world.spawn().id()); + } + + bencher.iter(|| { + let mut commands = Commands::new(&mut command_queue, &world); + for entity in &entities { + commands + .entity(*entity) + .insert_bundle((Matrix::default(), Vec3::default())); + } + drop(commands); + command_queue.apply(&mut world); + }); + }); + group.bench_function(format!("insert_batch"), |bencher| { + let mut world = World::default(); + let mut command_queue = CommandQueue::default(); + let mut entities = Vec::new(); + for _ in 0..entity_count { + entities.push(world.spawn().id()); + } + + bencher.iter(|| { + let mut commands = Commands::new(&mut command_queue, &world); + let mut values = Vec::with_capacity(entity_count); + for entity in &entities { + values.push((*entity, (Matrix::default(), Vec3::default()))); + } + commands.insert_or_spawn_batch(values); + drop(commands); + command_queue.apply(&mut world); + }); + }); + + group.finish(); +} + +struct FakeCommandA; +struct FakeCommandB(u64); + +impl Command for FakeCommandA { + fn write(self, world: &mut World) { + black_box(self); + black_box(world); + } +} + +impl Command for FakeCommandB { + fn write(self, world: &mut World) { + black_box(self); + black_box(world); + } +} + +pub fn fake_commands(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("fake_commands"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for command_count in (1..5).map(|i| i * 2 * 1000) { + group.bench_function(format!("{}_commands", command_count), |bencher| { + let mut world = World::default(); + let mut command_queue = CommandQueue::default(); + + bencher.iter(|| { + let mut commands = Commands::new(&mut command_queue, &world); + for i in 0..command_count { + if black_box(i % 2 == 0) { + commands.add(FakeCommandA); + } else { + commands.add(FakeCommandB(0)); + } + } + drop(commands); + command_queue.apply(&mut world); + }); + }); + } + + group.finish(); +} + +#[derive(Default)] +struct SizedCommand(T); + +impl Command for SizedCommand { + fn write(self, world: &mut World) { + black_box(self); + black_box(world); + } +} + +struct LargeStruct([u64; 64]); + +impl Default for LargeStruct { + fn default() -> Self { + Self([0; 64]) + } +} + +pub fn sized_commands_impl(criterion: &mut Criterion) { + let mut group = + criterion.benchmark_group(format!("sized_commands_{}_bytes", std::mem::size_of::())); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for command_count in (1..5).map(|i| i * 2 * 1000) { + group.bench_function(format!("{}_commands", command_count), |bencher| { + let mut world = World::default(); + let mut command_queue = CommandQueue::default(); + + bencher.iter(|| { + let mut commands = Commands::new(&mut command_queue, &world); + for _ in 0..command_count { + commands.add(T::default()); + } + drop(commands); + command_queue.apply(&mut world); + }); + }); + } + + group.finish(); +} + +pub fn zero_sized_commands(criterion: &mut Criterion) { + sized_commands_impl::>(criterion); +} + +pub fn medium_sized_commands(criterion: &mut Criterion) { + sized_commands_impl::>(criterion); +} + +pub fn large_sized_commands(criterion: &mut Criterion) { + sized_commands_impl::>(criterion); +} + +pub fn get_or_spawn(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("get_or_spawn"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + group.bench_function("individual", |bencher| { + let mut world = World::default(); + let mut command_queue = CommandQueue::default(); + + bencher.iter(|| { + let mut commands = Commands::new(&mut command_queue, &world); + for i in 0..10_000 { + commands + .get_or_spawn(Entity::from_raw(i)) + .insert_bundle((Matrix::default(), Vec3::default())); + } + command_queue.apply(&mut world); + }); + }); + + group.bench_function("batched", |bencher| { + let mut world = World::default(); + let mut command_queue = CommandQueue::default(); + + bencher.iter(|| { + let mut commands = Commands::new(&mut command_queue, &world); + let mut values = Vec::with_capacity(10_000); + for i in 0..10_000 { + values.push((Entity::from_raw(i), (Matrix::default(), Vec3::default()))); + } + commands.insert_or_spawn_batch(values); + command_queue.apply(&mut world); + }); + }); + + group.finish(); +} diff --git a/benches/benches/bevy_ecs/world/mod.rs b/benches/benches/bevy_ecs/world/mod.rs new file mode 100644 index 0000000000000..dae5274dc8c25 --- /dev/null +++ b/benches/benches/bevy_ecs/world/mod.rs @@ -0,0 +1,27 @@ +use criterion::criterion_group; + +mod commands; +mod world_get; + +use commands::*; +use world_get::*; + +criterion_group!( + world_benches, + empty_commands, + spawn_commands, + insert_commands, + fake_commands, + zero_sized_commands, + medium_sized_commands, + large_sized_commands, + get_or_spawn, + world_entity, + world_get, + world_query_get, + world_query_iter, + world_query_for_each, + query_get_component_simple, + query_get_component, + query_get, +); diff --git a/benches/benches/bevy_ecs/world/world_get.rs b/benches/benches/bevy_ecs/world/world_get.rs new file mode 100644 index 0000000000000..511864fa27418 --- /dev/null +++ b/benches/benches/bevy_ecs/world/world_get.rs @@ -0,0 +1,404 @@ +use bevy_ecs::{ + bundle::Bundle, + component::Component, + entity::Entity, + prelude::*, + system::{Query, SystemState}, + world::World, +}; +use criterion::{black_box, Criterion}; +use rand::{prelude::SliceRandom, SeedableRng}; +use rand_chacha::ChaCha8Rng; + +#[derive(Component, Default)] +#[component(storage = "Table")] +struct Table(f32); +#[derive(Component, Default)] +#[component(storage = "SparseSet")] +struct Sparse(f32); +#[derive(Component, Default)] +#[component(storage = "Table")] +struct WideTable(f32); +#[derive(Component, Default)] +#[component(storage = "SparseSet")] +struct WideSparse(f32); + +const RANGE: std::ops::Range = 5..6; + +fn deterministic_rand() -> ChaCha8Rng { + ChaCha8Rng::seed_from_u64(42) +} + +fn setup(entity_count: u32) -> World { + let mut world = World::default(); + world.spawn_batch((0..entity_count).map(|_| (T::default(),))); + black_box(world) +} + +fn setup_wide(entity_count: u32) -> World { + let mut world = World::default(); + world.spawn_batch((0..entity_count).map(|_| T::default())); + black_box(world) +} + +pub fn world_entity(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_entity"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + group.bench_function(format!("{}_entities", entity_count), |bencher| { + let world = setup::(entity_count); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + black_box(world.entity(entity)); + } + }); + }); + } + + group.finish(); +} + +pub fn world_get(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_get"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let world = setup::
(entity_count); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + assert!(world.get::
(entity).is_some()); + } + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let world = setup::(entity_count); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + assert!(world.get::(entity).is_some()); + } + }); + }); + } + + group.finish(); +} + +pub fn world_query_get(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_query_get"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let mut world = setup::
(entity_count); + let mut query = world.query::<&Table>(); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }); + group.bench_function(format!("{}_entities_table_wide", entity_count), |bencher| { + let mut world = setup_wide::<( + WideTable<0>, + WideTable<1>, + WideTable<2>, + WideTable<3>, + WideTable<4>, + WideTable<5>, + )>(entity_count); + let mut query = world.query::<( + &WideTable<0>, + &WideTable<1>, + &WideTable<2>, + &WideTable<3>, + &WideTable<4>, + &WideTable<5>, + )>(); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let mut world = setup::(entity_count); + let mut query = world.query::<&Sparse>(); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }); + group.bench_function( + format!("{}_entities_sparse_wide", entity_count), + |bencher| { + let mut world = setup_wide::<( + WideSparse<0>, + WideSparse<1>, + WideSparse<2>, + WideSparse<3>, + WideSparse<4>, + WideSparse<5>, + )>(entity_count); + let mut query = world.query::<( + &WideSparse<0>, + &WideSparse<1>, + &WideSparse<2>, + &WideSparse<3>, + &WideSparse<4>, + &WideSparse<5>, + )>(); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::from_raw(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }, + ); + } + + group.finish(); +} + +pub fn world_query_iter(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_query_iter"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let mut world = setup::
(entity_count); + let mut query = world.query::<&Table>(); + + bencher.iter(|| { + let mut count = 0; + for comp in query.iter(&world) { + black_box(comp); + count += 1; + black_box(count); + } + assert_eq!(black_box(count), entity_count); + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let mut world = setup::(entity_count); + let mut query = world.query::<&Sparse>(); + + bencher.iter(|| { + let mut count = 0; + for comp in query.iter(&world) { + black_box(comp); + count += 1; + black_box(count); + } + assert_eq!(black_box(count), entity_count); + }); + }); + } + + group.finish(); +} + +pub fn world_query_for_each(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_query_for_each"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let mut world = setup::
(entity_count); + let mut query = world.query::<&Table>(); + + bencher.iter(|| { + let mut count = 0; + query.for_each(&world, |comp| { + black_box(comp); + count += 1; + black_box(count); + }); + assert_eq!(black_box(count), entity_count); + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let mut world = setup::(entity_count); + let mut query = world.query::<&Sparse>(); + + bencher.iter(|| { + let mut count = 0; + query.for_each(&world, |comp| { + black_box(comp); + count += 1; + black_box(count); + }); + assert_eq!(black_box(count), entity_count); + }); + }); + } + + group.finish(); +} + +pub fn query_get_component_simple(criterion: &mut Criterion) { + #[derive(Component)] + struct A(f32); + + let mut group = criterion.benchmark_group("query_get_component_simple"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + group.bench_function("unchecked", |bencher| { + let mut world = World::new(); + + let entity = world.spawn().insert(A(0.0)).id(); + let mut query = world.query::<&mut A>(); + + bencher.iter(|| { + for _x in 0..100000 { + let mut a = unsafe { query.get_unchecked(&mut world, entity).unwrap() }; + a.0 += 1.0; + } + }); + }); + group.bench_function("system", |bencher| { + let mut world = World::new(); + + let entity = world.spawn().insert(A(0.0)).id(); + fn query_system(In(entity): In, mut query: Query<&mut A>) { + for _ in 0..100_000 { + let mut a = query.get_mut(entity).unwrap(); + a.0 += 1.0; + } + } + + let mut system = IntoSystem::into_system(query_system); + system.initialize(&mut world); + system.update_archetype_component_access(&world); + + bencher.iter(|| system.run(entity, &mut world)); + }); + + group.finish(); +} + +pub fn query_get_component(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("query_get_component"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let mut world = World::default(); + let mut entities: Vec<_> = world + .spawn_batch((0..entity_count).map(|_| (Table::default(),))) + .collect(); + entities.shuffle(&mut deterministic_rand()); + let mut query = SystemState::>::new(&mut world); + let query = query.get(&world); + + bencher.iter(|| { + let mut count = 0; + for comp in entities + .iter() + .flat_map(|&e| query.get_component::
(e)) + { + black_box(comp); + count += 1; + black_box(count); + } + assert_eq!(black_box(count), entity_count); + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let mut world = World::default(); + let mut entities: Vec<_> = world + .spawn_batch((0..entity_count).map(|_| (Sparse::default(),))) + .collect(); + entities.shuffle(&mut deterministic_rand()); + let mut query = SystemState::>::new(&mut world); + let query = query.get(&world); + + bencher.iter(|| { + let mut count = 0; + for comp in entities + .iter() + .flat_map(|&e| query.get_component::(e)) + { + black_box(comp); + count += 1; + black_box(count); + } + assert_eq!(black_box(count), entity_count); + }); + }); + } + + group.finish(); +} + +pub fn query_get(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("query_get"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + group.bench_function(format!("{}_entities_table", entity_count), |bencher| { + let mut world = World::default(); + let mut entities: Vec<_> = world + .spawn_batch((0..entity_count).map(|_| (Table::default(),))) + .collect(); + entities.shuffle(&mut deterministic_rand()); + let mut query = SystemState::>::new(&mut world); + let query = query.get(&world); + + bencher.iter(|| { + let mut count = 0; + for comp in entities.iter().flat_map(|&e| query.get(e)) { + black_box(comp); + count += 1; + black_box(count); + } + assert_eq!(black_box(count), entity_count); + }); + }); + group.bench_function(format!("{}_entities_sparse", entity_count), |bencher| { + let mut world = World::default(); + let mut entities: Vec<_> = world + .spawn_batch((0..entity_count).map(|_| (Sparse::default(),))) + .collect(); + entities.shuffle(&mut deterministic_rand()); + let mut query = SystemState::>::new(&mut world); + let query = query.get(&world); + + bencher.iter(|| { + let mut count = 0; + for comp in entities.iter().flat_map(|&e| query.get(e)) { + black_box(comp); + count += 1; + black_box(count); + } + assert_eq!(black_box(count), entity_count); + }); + }); + } + + group.finish(); +} diff --git a/benches/benches/bevy_reflect/list.rs b/benches/benches/bevy_reflect/list.rs new file mode 100644 index 0000000000000..3c48dd22e4556 --- /dev/null +++ b/benches/benches/bevy_reflect/list.rs @@ -0,0 +1,155 @@ +use std::{iter, time::Duration}; + +use bevy_reflect::{DynamicList, List}; +use criterion::{ + black_box, criterion_group, criterion_main, measurement::Measurement, BatchSize, + BenchmarkGroup, BenchmarkId, Criterion, Throughput, +}; + +criterion_group!( + benches, + concrete_list_apply, + concrete_list_clone_dynamic, + dynamic_list_apply, + dynamic_list_push +); +criterion_main!(benches); + +const WARM_UP_TIME: Duration = Duration::from_millis(500); +const MEASUREMENT_TIME: Duration = Duration::from_secs(4); + +// log10 scaling +const SIZES: [usize; 5] = [100_usize, 316, 1000, 3162, 10000]; + +fn list_apply( + group: &mut BenchmarkGroup, + bench_name: &str, + f_base: F1, + f_patch: F3, +) where + M: Measurement, + LBase: List, + LPatch: List, + F1: Fn(usize) -> F2, + F2: Fn() -> LBase, + F3: Fn(usize) -> LPatch, +{ + for size in SIZES { + group.throughput(Throughput::Elements(size as u64)); + + group.bench_with_input( + BenchmarkId::new(bench_name, size), + &size, + |bencher, &size| { + let f_base = f_base(size); + let patch = f_patch(size); + bencher.iter_batched( + || f_base(), + |mut base| base.apply(black_box(&patch)), + BatchSize::SmallInput, + ); + }, + ); + } +} + +fn concrete_list_apply(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("concrete_list_apply"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + let empty_base = |_: usize| || Vec::::new(); + let full_base = |size: usize| move || iter::repeat(0).take(size).collect::>(); + let patch = |size: usize| iter::repeat(1).take(size).collect::>(); + + list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch); + + list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| { + patch(size).clone_dynamic() + }); + + list_apply(&mut group, "same_len_concrete_patch", full_base, patch); + + list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| { + patch(size).clone_dynamic() + }); + + group.finish(); +} + +fn concrete_list_clone_dynamic(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("concrete_list_clone_dynamic"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + for size in SIZES { + group.throughput(Throughput::Elements(size as u64)); + + group.bench_with_input( + BenchmarkId::from_parameter(size), + &size, + |bencher, &size| { + let v = iter::repeat(0).take(size).collect::>(); + + bencher.iter(|| black_box(&v).clone_dynamic()); + }, + ); + } + + group.finish(); +} + +fn dynamic_list_push(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("dynamic_list_push"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + for size in SIZES { + group.throughput(Throughput::Elements(size as u64)); + + group.bench_with_input( + BenchmarkId::from_parameter(size), + &size, + |bencher, &size| { + let src = iter::repeat(()).take(size).collect::>(); + let dst = DynamicList::default(); + + bencher.iter_batched( + || (src.clone(), dst.clone_dynamic()), + |(src, mut dst)| { + for item in src { + dst.push(item); + } + }, + BatchSize::SmallInput, + ); + }, + ); + } + + group.finish(); +} + +fn dynamic_list_apply(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("dynamic_list_apply"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + let empty_base = |_: usize| || Vec::::new().clone_dynamic(); + let full_base = |size: usize| move || iter::repeat(0).take(size).collect::>(); + let patch = |size: usize| iter::repeat(1).take(size).collect::>(); + + list_apply(&mut group, "empty_base_concrete_patch", empty_base, patch); + + list_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| { + patch(size).clone_dynamic() + }); + + list_apply(&mut group, "same_len_concrete_patch", full_base, patch); + + list_apply(&mut group, "same_len_dynamic_patch", full_base, |size| { + patch(size).clone_dynamic() + }); + + group.finish(); +} diff --git a/benches/benches/bevy_reflect/map.rs b/benches/benches/bevy_reflect/map.rs new file mode 100644 index 0000000000000..b5c8a95eeec3c --- /dev/null +++ b/benches/benches/bevy_reflect/map.rs @@ -0,0 +1,303 @@ +use std::{fmt::Write, iter, time::Duration}; + +use bevy_reflect::{DynamicMap, Map}; +use bevy_utils::HashMap; +use criterion::{ + black_box, criterion_group, criterion_main, measurement::Measurement, BatchSize, + BenchmarkGroup, BenchmarkId, Criterion, Throughput, +}; + +criterion_group!( + benches, + concrete_map_apply, + dynamic_map_apply, + dynamic_map_get, + dynamic_map_insert +); +criterion_main!(benches); + +const WARM_UP_TIME: Duration = Duration::from_millis(500); +const MEASUREMENT_TIME: Duration = Duration::from_secs(4); +const SIZES: [usize; 5] = [100, 316, 1000, 3162, 10000]; + +/// Generic benchmark for applying one `Map` to another. +/// +/// `f_base` is a function which takes an input size and produces a generator +/// for new base maps. `f_patch` is a function which produces a map to be +/// applied to the base map. +fn map_apply( + group: &mut BenchmarkGroup, + bench_name: &str, + f_base: F1, + f_patch: F3, +) where + M: Measurement, + MBase: Map, + MPatch: Map, + F1: Fn(usize) -> F2, + F2: Fn() -> MBase, + F3: Fn(usize) -> MPatch, +{ + for size in SIZES { + group.throughput(Throughput::Elements(size as u64)); + group.bench_with_input( + BenchmarkId::new(bench_name, size), + &size, + |bencher, &size| { + let f_base = f_base(size); + bencher.iter_batched( + || (f_base(), f_patch(size)), + |(mut base, patch)| base.apply(black_box(&patch)), + BatchSize::SmallInput, + ); + }, + ); + } +} + +fn concrete_map_apply(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("concrete_map_apply"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + let empty_base = |_: usize| || HashMap::::default(); + + let key_range_base = |size: usize| { + move || { + (0..size as u64) + .zip(iter::repeat(0)) + .collect::>() + } + }; + + let key_range_patch = |size: usize| { + (0..size as u64) + .zip(iter::repeat(1)) + .collect::>() + }; + + let disjoint_patch = |size: usize| { + (size as u64..2 * size as u64) + .zip(iter::repeat(1)) + .collect::>() + }; + + map_apply( + &mut group, + "empty_base_concrete_patch", + empty_base, + key_range_patch, + ); + + map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| { + key_range_patch(size).clone_dynamic() + }); + + map_apply( + &mut group, + "same_keys_concrete_patch", + key_range_base, + key_range_patch, + ); + + map_apply( + &mut group, + "same_keys_dynamic_patch", + key_range_base, + |size| key_range_patch(size).clone_dynamic(), + ); + + map_apply( + &mut group, + "disjoint_keys_concrete_patch", + key_range_base, + disjoint_patch, + ); + + map_apply( + &mut group, + "disjoint_keys_dynamic_patch", + key_range_base, + |size| disjoint_patch(size).clone_dynamic(), + ); +} + +fn u64_to_n_byte_key(k: u64, n: usize) -> String { + let mut key = String::with_capacity(n); + write!(&mut key, "{}", k).unwrap(); + + // Pad key to n bytes. + key.extend(iter::repeat('\0').take(n - key.len())); + key +} + +fn dynamic_map_apply(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("dynamic_map_apply"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + let empty_base = |_: usize| || DynamicMap::default(); + + let key_range_base = |size: usize| { + move || { + (0..size as u64) + .zip(iter::repeat(0)) + .collect::>() + .clone_dynamic() + } + }; + + let key_range_patch = |size: usize| { + (0..size as u64) + .zip(iter::repeat(1)) + .collect::>() + }; + + let disjoint_patch = |size: usize| { + (size as u64..2 * size as u64) + .zip(iter::repeat(1)) + .collect::>() + }; + + map_apply( + &mut group, + "empty_base_concrete_patch", + empty_base, + key_range_patch, + ); + + map_apply(&mut group, "empty_base_dynamic_patch", empty_base, |size| { + key_range_patch(size).clone_dynamic() + }); + + map_apply( + &mut group, + "same_keys_concrete_patch", + key_range_base, + key_range_patch, + ); + + map_apply( + &mut group, + "same_keys_dynamic_patch", + key_range_base, + |size| key_range_patch(size).clone_dynamic(), + ); + + map_apply( + &mut group, + "disjoint_keys_concrete_patch", + key_range_base, + disjoint_patch, + ); + + map_apply( + &mut group, + "disjoint_keys_dynamic_patch", + key_range_base, + |size| disjoint_patch(size).clone_dynamic(), + ); +} + +fn dynamic_map_get(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("dynamic_map_get"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + for size in SIZES { + group.throughput(Throughput::Elements(size as u64)); + group.bench_with_input( + BenchmarkId::new("u64_keys", size), + &size, + |bencher, &size| { + let mut map = DynamicMap::default(); + for i in 0..size as u64 { + map.insert(i, i); + } + + bencher.iter(|| { + for i in 0..size as u64 { + let key = black_box(i); + black_box(assert!(map.get(&key).is_some())); + } + }); + }, + ); + } + + for size in SIZES { + group.throughput(Throughput::Elements(size as u64)); + group.bench_with_input( + BenchmarkId::new("64_byte_keys", size), + &size, + |bencher, &size| { + let mut map = DynamicMap::default(); + let mut keys = Vec::with_capacity(size); + for i in 0..size as u64 { + let key = u64_to_n_byte_key(i, 64); + map.insert(key.clone(), i); + keys.push(key); + } + + bencher.iter(|| { + for i in 0..size { + let key = black_box(&keys[i]); + assert!(map.get(key).is_some()); + } + }); + }, + ); + } +} + +fn dynamic_map_insert(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("dynamic_map_insert"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + for size in SIZES { + group.throughput(Throughput::Elements(size as u64)); + group.bench_with_input( + BenchmarkId::new("u64_keys", size), + &size, + |bencher, &size| { + bencher.iter_batched( + || DynamicMap::default(), + |mut map| { + for i in 0..size as u64 { + let key = black_box(i); + black_box(map.insert(key, i)); + } + }, + BatchSize::SmallInput, + ); + }, + ); + } + + for size in SIZES { + group.throughput(Throughput::Elements(size as u64)); + group.bench_with_input( + BenchmarkId::new("64_byte_keys", size), + &size, + |bencher, &size| { + let mut keys = Vec::with_capacity(size); + for i in 0..size { + let key = u64_to_n_byte_key(i as u64, 64); + keys.push(key); + } + + bencher.iter_batched( + || (DynamicMap::default(), keys.clone()), + |(mut map, keys)| { + for (i, key) in keys.into_iter().enumerate() { + let key = black_box(key); + map.insert(key, i); + } + }, + BatchSize::SmallInput, + ); + }, + ); + } +} diff --git a/benches/benches/bevy_reflect/struct.rs b/benches/benches/bevy_reflect/struct.rs new file mode 100644 index 0000000000000..613bd13d19b4c --- /dev/null +++ b/benches/benches/bevy_reflect/struct.rs @@ -0,0 +1,485 @@ +use std::time::Duration; + +use bevy_reflect::{DynamicStruct, GetField, Reflect, Struct}; +use criterion::{ + black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput, +}; + +criterion_group!( + benches, + concrete_struct_apply, + concrete_struct_field, + dynamic_struct_apply, + dynamic_struct_get_field, + dynamic_struct_insert, +); +criterion_main!(benches); + +const WARM_UP_TIME: Duration = Duration::from_millis(500); +const MEASUREMENT_TIME: Duration = Duration::from_secs(4); +const SIZES: [usize; 4] = [16, 32, 64, 128]; + +fn concrete_struct_field(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("concrete_struct_field"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + let structs: [Box; 4] = [ + Box::new(Struct16::default()), + Box::new(Struct32::default()), + Box::new(Struct64::default()), + Box::new(Struct128::default()), + ]; + + for s in structs { + let field_count = s.field_len(); + + group.bench_with_input( + BenchmarkId::from_parameter(field_count), + &s, + |bencher, s| { + let field_names = (0..field_count) + .map(|i| format!("field_{}", i)) + .collect::>(); + + bencher.iter(|| { + for name in &field_names { + s.field(black_box(name)); + } + }); + }, + ); + } +} + +fn concrete_struct_apply(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("concrete_struct_apply"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + // Use functions that produce trait objects of varying concrete types as the + // input to the benchmark. + let inputs: &[fn() -> (Box, Box)] = &[ + || (Box::new(Struct16::default()), Box::new(Struct16::default())), + || (Box::new(Struct32::default()), Box::new(Struct32::default())), + || (Box::new(Struct64::default()), Box::new(Struct64::default())), + || { + ( + Box::new(Struct128::default()), + Box::new(Struct128::default()), + ) + }, + ]; + + for input in inputs { + let field_count = input().0.field_len(); + group.throughput(Throughput::Elements(field_count as u64)); + + group.bench_with_input( + BenchmarkId::new("apply_concrete", field_count), + input, + |bencher, input| { + bencher.iter_batched( + input, + |(mut obj, patch)| obj.apply(black_box(patch.as_ref())), + BatchSize::SmallInput, + ); + }, + ); + } + + for input in inputs { + let field_count = input().0.field_len(); + group.throughput(Throughput::Elements(field_count as u64)); + + group.bench_with_input( + BenchmarkId::new("apply_dynamic", field_count), + input, + |bencher, input| { + bencher.iter_batched( + || { + let (obj, _) = input(); + let patch = obj.clone_dynamic(); + (obj, patch) + }, + |(mut obj, patch)| obj.apply(black_box(&patch)), + BatchSize::SmallInput, + ); + }, + ); + } +} + +fn dynamic_struct_apply(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("dynamic_struct_apply"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + let patches: &[(fn() -> Box, usize)] = &[ + (|| Box::new(Struct16::default()), 16), + (|| Box::new(Struct32::default()), 32), + (|| Box::new(Struct64::default()), 64), + (|| Box::new(Struct128::default()), 128), + ]; + + for (patch, field_count) in patches { + let field_count = *field_count; + group.throughput(Throughput::Elements(field_count as u64)); + + let mut base = DynamicStruct::default(); + for i in 0..field_count { + let field_name = format!("field_{}", i); + base.insert(&field_name, 1u32); + } + + group.bench_with_input( + BenchmarkId::new("apply_concrete", field_count), + &patch, + |bencher, patch| { + bencher.iter_batched( + || (base.clone_dynamic(), patch()), + |(mut base, patch)| base.apply(black_box(&*patch)), + BatchSize::SmallInput, + ); + }, + ); + } + + for field_count in SIZES { + group.throughput(Throughput::Elements(field_count as u64)); + + group.bench_with_input( + BenchmarkId::new("apply_dynamic", field_count), + &field_count, + |bencher, &field_count| { + let mut base = DynamicStruct::default(); + let mut patch = DynamicStruct::default(); + for i in 0..field_count { + let field_name = format!("field_{}", i); + base.insert(&field_name, 0u32); + patch.insert(&field_name, 1u32); + } + + bencher.iter_batched( + || base.clone_dynamic(), + |mut base| base.apply(black_box(&patch)), + BatchSize::SmallInput, + ); + }, + ); + } +} + +fn dynamic_struct_insert(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("dynamic_struct_insert"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + for field_count in SIZES { + group.throughput(Throughput::Elements(field_count as u64)); + group.bench_with_input( + BenchmarkId::from_parameter(field_count), + &field_count, + |bencher, field_count| { + let mut s = DynamicStruct::default(); + for i in 0..*field_count { + let field_name = format!("field_{}", i); + s.insert(&field_name, ()); + } + + let field = format!("field_{}", field_count); + bencher.iter_batched( + || s.clone_dynamic(), + |mut s| { + black_box(s.insert(black_box(&field), ())); + }, + BatchSize::SmallInput, + ); + }, + ); + } + + group.finish(); +} + +fn dynamic_struct_get_field(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("dynamic_struct_get"); + group.warm_up_time(WARM_UP_TIME); + group.measurement_time(MEASUREMENT_TIME); + + for field_count in SIZES { + group.throughput(Throughput::Elements(field_count as u64)); + group.bench_with_input( + BenchmarkId::from_parameter(field_count), + &field_count, + |bencher, field_count| { + let mut s = DynamicStruct::default(); + for i in 0..*field_count { + let field_name = format!("field_{}", i); + s.insert(&field_name, ()); + } + + let field = black_box("field_63"); + bencher.iter(|| { + black_box(s.get_field::<()>(field)); + }); + }, + ); + } +} + +#[derive(Clone, Default, Reflect)] +struct Struct16 { + field_0: u32, + field_1: u32, + field_2: u32, + field_3: u32, + field_4: u32, + field_5: u32, + field_6: u32, + field_7: u32, + field_8: u32, + field_9: u32, + field_10: u32, + field_11: u32, + field_12: u32, + field_13: u32, + field_14: u32, + field_15: u32, +} + +#[derive(Clone, Default, Reflect)] +struct Struct32 { + field_0: u32, + field_1: u32, + field_2: u32, + field_3: u32, + field_4: u32, + field_5: u32, + field_6: u32, + field_7: u32, + field_8: u32, + field_9: u32, + field_10: u32, + field_11: u32, + field_12: u32, + field_13: u32, + field_14: u32, + field_15: u32, + field_16: u32, + field_17: u32, + field_18: u32, + field_19: u32, + field_20: u32, + field_21: u32, + field_22: u32, + field_23: u32, + field_24: u32, + field_25: u32, + field_26: u32, + field_27: u32, + field_28: u32, + field_29: u32, + field_30: u32, + field_31: u32, +} + +#[derive(Clone, Default, Reflect)] +struct Struct64 { + field_0: u32, + field_1: u32, + field_2: u32, + field_3: u32, + field_4: u32, + field_5: u32, + field_6: u32, + field_7: u32, + field_8: u32, + field_9: u32, + field_10: u32, + field_11: u32, + field_12: u32, + field_13: u32, + field_14: u32, + field_15: u32, + field_16: u32, + field_17: u32, + field_18: u32, + field_19: u32, + field_20: u32, + field_21: u32, + field_22: u32, + field_23: u32, + field_24: u32, + field_25: u32, + field_26: u32, + field_27: u32, + field_28: u32, + field_29: u32, + field_30: u32, + field_31: u32, + field_32: u32, + field_33: u32, + field_34: u32, + field_35: u32, + field_36: u32, + field_37: u32, + field_38: u32, + field_39: u32, + field_40: u32, + field_41: u32, + field_42: u32, + field_43: u32, + field_44: u32, + field_45: u32, + field_46: u32, + field_47: u32, + field_48: u32, + field_49: u32, + field_50: u32, + field_51: u32, + field_52: u32, + field_53: u32, + field_54: u32, + field_55: u32, + field_56: u32, + field_57: u32, + field_58: u32, + field_59: u32, + field_60: u32, + field_61: u32, + field_62: u32, + field_63: u32, +} + +#[derive(Clone, Default, Reflect)] +struct Struct128 { + field_0: u32, + field_1: u32, + field_2: u32, + field_3: u32, + field_4: u32, + field_5: u32, + field_6: u32, + field_7: u32, + field_8: u32, + field_9: u32, + field_10: u32, + field_11: u32, + field_12: u32, + field_13: u32, + field_14: u32, + field_15: u32, + field_16: u32, + field_17: u32, + field_18: u32, + field_19: u32, + field_20: u32, + field_21: u32, + field_22: u32, + field_23: u32, + field_24: u32, + field_25: u32, + field_26: u32, + field_27: u32, + field_28: u32, + field_29: u32, + field_30: u32, + field_31: u32, + field_32: u32, + field_33: u32, + field_34: u32, + field_35: u32, + field_36: u32, + field_37: u32, + field_38: u32, + field_39: u32, + field_40: u32, + field_41: u32, + field_42: u32, + field_43: u32, + field_44: u32, + field_45: u32, + field_46: u32, + field_47: u32, + field_48: u32, + field_49: u32, + field_50: u32, + field_51: u32, + field_52: u32, + field_53: u32, + field_54: u32, + field_55: u32, + field_56: u32, + field_57: u32, + field_58: u32, + field_59: u32, + field_60: u32, + field_61: u32, + field_62: u32, + field_63: u32, + field_64: u32, + field_65: u32, + field_66: u32, + field_67: u32, + field_68: u32, + field_69: u32, + field_70: u32, + field_71: u32, + field_72: u32, + field_73: u32, + field_74: u32, + field_75: u32, + field_76: u32, + field_77: u32, + field_78: u32, + field_79: u32, + field_80: u32, + field_81: u32, + field_82: u32, + field_83: u32, + field_84: u32, + field_85: u32, + field_86: u32, + field_87: u32, + field_88: u32, + field_89: u32, + field_90: u32, + field_91: u32, + field_92: u32, + field_93: u32, + field_94: u32, + field_95: u32, + field_96: u32, + field_97: u32, + field_98: u32, + field_99: u32, + field_100: u32, + field_101: u32, + field_102: u32, + field_103: u32, + field_104: u32, + field_105: u32, + field_106: u32, + field_107: u32, + field_108: u32, + field_109: u32, + field_110: u32, + field_111: u32, + field_112: u32, + field_113: u32, + field_114: u32, + field_115: u32, + field_116: u32, + field_117: u32, + field_118: u32, + field_119: u32, + field_120: u32, + field_121: u32, + field_122: u32, + field_123: u32, + field_124: u32, + field_125: u32, + field_126: u32, + field_127: u32, +} diff --git a/benches/benches/bevy_tasks/iter.rs b/benches/benches/bevy_tasks/iter.rs index 2c58adfc2708b..74b043f9a234e 100644 --- a/benches/benches/bevy_tasks/iter.rs +++ b/benches/benches/bevy_tasks/iter.rs @@ -1,4 +1,4 @@ -use bevy::tasks::{ParallelIterator, TaskPoolBuilder}; +use bevy_tasks::{ParallelIterator, TaskPoolBuilder}; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; struct ParChunks<'a, T>(std::slice::Chunks<'a, T>); @@ -6,8 +6,6 @@ impl<'a, T> ParallelIterator> for ParChunks<'a, T> where T: 'a + Send + Sync, { - type Item = &'a T; - fn next_batch(&mut self) -> Option> { self.0.next().map(|s| s.iter()) } @@ -18,15 +16,13 @@ impl<'a, T> ParallelIterator> for ParChunksMut<'a, T> where T: 'a + Send + Sync, { - type Item = &'a mut T; - fn next_batch(&mut self) -> Option> { self.0.next().map(|s| s.iter_mut()) } } fn bench_overhead(c: &mut Criterion) { - fn noop(_: &mut usize) {}; + fn noop(_: &mut usize) {} let mut v = (0..10000).collect::>(); c.bench_function("overhead_iter", |b| { diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000000000..2ebc97cf45585 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +doc-valid-idents = ["sRGB", "NaN", "iOS", "glTF", "GitHub", "WebGPU"] diff --git a/crates/bevy_animation/Cargo.toml b/crates/bevy_animation/Cargo.toml new file mode 100644 index 0000000000000..1cf3668b094a4 --- /dev/null +++ b/crates/bevy_animation/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "bevy_animation" +version = "0.9.0-dev" +edition = "2021" +description = "Provides animation functionality for Bevy Engine" +homepage = "https://bevyengine.org" +repository = "https://github.com/bevyengine/bevy" +license = "MIT OR Apache-2.0" +keywords = ["bevy"] + +[dependencies] +# bevy +bevy_app = { path = "../bevy_app", version = "0.9.0-dev" } +bevy_asset = { path = "../bevy_asset", version = "0.9.0-dev" } +bevy_core = { path = "../bevy_core", version = "0.9.0-dev" } +bevy_math = { path = "../bevy_math", version = "0.9.0-dev" } +bevy_reflect = { path = "../bevy_reflect", version = "0.9.0-dev", features = ["bevy"] } +bevy_time = { path = "../bevy_time", version = "0.9.0-dev" } +bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" } +bevy_ecs = { path = "../bevy_ecs", version = "0.9.0-dev" } +bevy_transform = { path = "../bevy_transform", version = "0.9.0-dev" } +bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.9.0-dev" } diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs new file mode 100644 index 0000000000000..ee70d801ef952 --- /dev/null +++ b/crates/bevy_animation/src/lib.rs @@ -0,0 +1,301 @@ +//! Animation for the game engine Bevy + +#![warn(missing_docs)] + +use std::ops::Deref; + +use bevy_app::{App, CoreStage, Plugin}; +use bevy_asset::{AddAsset, Assets, Handle}; +use bevy_core::Name; +use bevy_ecs::{ + change_detection::DetectChanges, + entity::Entity, + prelude::Component, + reflect::ReflectComponent, + schedule::ParallelSystemDescriptorCoercion, + system::{Query, Res}, +}; +use bevy_hierarchy::Children; +use bevy_math::{Quat, Vec3}; +use bevy_reflect::{Reflect, TypeUuid}; +use bevy_time::Time; +use bevy_transform::{prelude::Transform, TransformSystem}; +use bevy_utils::{tracing::warn, HashMap}; + +#[allow(missing_docs)] +pub mod prelude { + #[doc(hidden)] + pub use crate::{ + AnimationClip, AnimationPlayer, AnimationPlugin, EntityPath, Keyframes, VariableCurve, + }; +} + +/// List of keyframes for one of the attribute of a [`Transform`]. +#[derive(Clone, Debug)] +pub enum Keyframes { + /// Keyframes for rotation. + Rotation(Vec), + /// Keyframes for translation. + Translation(Vec), + /// Keyframes for scale. + Scale(Vec), +} + +/// Describes how an attribute of a [`Transform`] should be animated. +/// +/// `keyframe_timestamps` and `keyframes` should have the same length. +#[derive(Clone, Debug)] +pub struct VariableCurve { + /// Timestamp for each of the keyframes. + pub keyframe_timestamps: Vec, + /// List of the keyframes. + pub keyframes: Keyframes, +} + +/// Path to an entity, with [`Name`]s. Each entity in a path must have a name. +#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)] +pub struct EntityPath { + /// Parts of the path + pub parts: Vec, +} + +/// A list of [`VariableCurve`], and the [`EntityPath`] to which they apply. +#[derive(Clone, TypeUuid, Debug, Default)] +#[uuid = "d81b7179-0448-4eb0-89fe-c067222725bf"] +pub struct AnimationClip { + curves: HashMap>, + duration: f32, +} + +impl AnimationClip { + #[inline] + /// Hashmap of the [`VariableCurve`]s per [`EntityPath`]. + pub fn curves(&self) -> &HashMap> { + &self.curves + } + + /// Duration of the clip, represented in seconds + #[inline] + pub fn duration(&self) -> f32 { + self.duration + } + + /// Add a [`VariableCurve`] to an [`EntityPath`]. + pub fn add_curve_to_path(&mut self, path: EntityPath, curve: VariableCurve) { + // Update the duration of the animation by this curve duration if it's longer + self.duration = self + .duration + .max(*curve.keyframe_timestamps.last().unwrap_or(&0.0)); + self.curves.entry(path).or_default().push(curve); + } +} + +/// Animation controls +#[derive(Component, Reflect)] +#[reflect(Component)] +pub struct AnimationPlayer { + paused: bool, + repeat: bool, + speed: f32, + elapsed: f32, + animation_clip: Handle, +} + +impl Default for AnimationPlayer { + fn default() -> Self { + Self { + paused: false, + repeat: false, + speed: 1.0, + elapsed: 0.0, + animation_clip: Default::default(), + } + } +} + +impl AnimationPlayer { + /// Start playing an animation, resetting state of the player + pub fn play(&mut self, handle: Handle) -> &mut Self { + *self = Self { + animation_clip: handle, + ..Default::default() + }; + self + } + + /// Set the animation to repeat + pub fn repeat(&mut self) -> &mut Self { + self.repeat = true; + self + } + + /// Stop the animation from repeating + pub fn stop_repeating(&mut self) -> &mut Self { + self.repeat = false; + self + } + + /// Pause the animation + pub fn pause(&mut self) { + self.paused = true; + } + + /// Unpause the animation + pub fn resume(&mut self) { + self.paused = false; + } + + /// Is the animation paused + pub fn is_paused(&self) -> bool { + self.paused + } + + /// Speed of the animation playback + pub fn speed(&self) -> f32 { + self.speed + } + + /// Set the speed of the animation playback + pub fn set_speed(&mut self, speed: f32) -> &mut Self { + self.speed = speed; + self + } + + /// Time elapsed playing the animation + pub fn elapsed(&self) -> f32 { + self.elapsed + } + + /// Seek to a specific time in the animation + pub fn set_elapsed(&mut self, elapsed: f32) -> &mut Self { + self.elapsed = elapsed; + self + } +} + +/// System that will play all animations, using any entity with a [`AnimationPlayer`] +/// and a [`Handle`] as an animation root +pub fn animation_player( + time: Res