diff --git a/.cargo/config_fast_builds b/.cargo/config_fast_builds index 4d1ff9c1e3b38..cfbcd697c8f03 100644 --- a/.cargo/config_fast_builds +++ b/.cargo/config_fast_builds @@ -10,7 +10,7 @@ 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=/opt/homebrew/bin/zld", "-Zshare-generics=y"] +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"] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7ce131577cf2d..432363bdcc764 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -10,23 +10,46 @@ assignees: '' The release number or commit hash of the version you're using. -## Operating system & version +## \[Optional\] Relevant system information -Ex: Windows 10, Ubuntu 18.04, iOS 14. +If you cannot get Bevy to build or run on your machine, please include: -## What you did +- 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. -The steps you took to uncover this bug. Please list full reproduction steps if -feasible. +```ignore +`AdapterInfo { name: "NVIDIA GeForce RTX 2070", vendor: 4318, device: 7938, device_type: DiscreteGpu, backend: Vulkan }` +``` -## What you expected to happen +You should also consider testing the examples of our upstream dependencies to help isolate any setup-specific issue: -What you think should've happened if everything was working properly. +- [`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 -## What actually happened +Describe how you arrived at the problem. If you can, consider providing a code snippet or link. -The actual result of the actions you described. +## What went wrong + +If it's not clear, break this out into: + +- what were you expecting? +- what actually happened? ## Additional information -Any additional information you would like to add such as screenshots, logs, etc. +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/bors.toml b/.github/bors.toml index 9931de1ec54c6..d1735c98b9603 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -7,14 +7,16 @@ status = [ "build-wasm (nightly, ubuntu-latest)", "build-android", "markdownlint", - "check-markdown-links", "run-examples", + "run-examples-on-wasm", "check-doc", "check-missing-examples-in-docs", "check-unused-dependencies", "ci", "miri", "check-compiles", + "build-and-install-on-iOS", + "run-examples-on-windows", ] use_squash_merge = true diff --git a/.github/linters/markdown-link-check.json b/.github/linters/markdown-link-check.json deleted file mode 100644 index 9e2a95efc3ff6..0000000000000 --- a/.github/linters/markdown-link-check.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "ignorePatterns": [ - { - "pattern": "^https?://github\\.com/" - }, - { - "pattern": "^https?://docs\\.github\\.com/" - }, - { - "pattern": "^https?://reddit\\.com/" - } - ], - "replacementPatterns": [], - "httpHeaders": [ - { - "urls": ["https://crates.io"], - "headers": { - "Accept": "text/html" - } - } - ], - "timeout": "20s", - "retryOn429": true, - "retryCount": 5, - "fallbackRetryDelay": "30s", - "aliveStatusCodes": [200, 206] -} 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 2f7da5fa767ab..25b6b8766a67f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,18 +76,20 @@ jobs: 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') }} + # TODO: re-enable cache once nightly is unpinned + # - 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: nightly + # TODO: check again with nightly once https://github.com/rust-lang/miri/issues/2223 is fixed + toolchain: nightly-2022-06-08 components: miri override: true - name: Install alsa and udev @@ -135,6 +137,7 @@ jobs: toolchain: [stable, nightly] os: [ubuntu-latest] runs-on: ${{ matrix.os }} + needs: build steps: - uses: actions/checkout@v3 - uses: actions/cache@v3 @@ -157,31 +160,6 @@ jobs: command: check args: --target wasm32-unknown-unknown --no-default-features --features bevy_winit,x11,hdr,bevy_gltf - 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: Uninstall android-31 - run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --uninstall "platforms;android-31" - - 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: cargo apk build --example android - markdownlint: runs-on: ubuntu-latest needs: check-missing-examples-in-docs @@ -200,57 +178,9 @@ jobs: # Not needed here as only one Linter is used. #GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - check-markdown-links: - runs-on: ubuntu-latest - needs: markdownlint - if: always() - steps: - - uses: actions/checkout@v3 - - name: check dead links - continue-on-error: true - id: run1 - uses: gaurav-nelson/github-action-markdown-link-check@9710f0fec812ce0a3b98bef4c9d842fc1f39d976 - with: - use-quiet-mode: 'yes' - use-verbose-mode: 'yes' - config-file: '.github/linters/markdown-link-check.json' - - name: Sleep for 30 seconds - if: steps.run1.outcome=='failure' - run: sleep 30s - shell: bash - - name: check dead links (retry) - continue-on-error: true - id: run2 - if: steps.run1.outcome=='failure' - uses: gaurav-nelson/github-action-markdown-link-check@9710f0fec812ce0a3b98bef4c9d842fc1f39d976 - with: - use-quiet-mode: 'yes' - use-verbose-mode: 'yes' - config-file: '.github/linters/markdown-link-check.json' - - name: Sleep for 30 seconds - if: steps.run2.outcome=='failure' - run: sleep 30s - shell: bash - - name: check dead links (retry 2) - continue-on-error: true - id: run3 - if: steps.run2.outcome=='failure' - uses: gaurav-nelson/github-action-markdown-link-check@9710f0fec812ce0a3b98bef4c9d842fc1f39d976 - with: - use-quiet-mode: 'yes' - use-verbose-mode: 'yes' - config-file: '.github/linters/markdown-link-check.json' - - name: set the status - if: always() - run: | - if ${{ steps.run1.outcome=='success' || steps.run2.outcome=='success' || steps.run3.outcome=='success' }}; then - echo success - else - exit 1 - fi - run-examples: runs-on: ubuntu-latest + timeout-minutes: 30 steps: - name: Install Bevy dependencies run: | @@ -278,13 +208,13 @@ jobs: toolchain: stable - name: Build bevy run: | - cargo build --no-default-features --features "bevy_dynamic_plugin,bevy_gilrs,bevy_gltf,bevy_winit,render,png,hdr,x11,bevy_ci_testing,trace,trace_chrome,bevy_audio,vorbis" + cargo build --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 --no-default-features --features "bevy_dynamic_plugin,bevy_gilrs,bevy_gltf,bevy_winit,render,png,hdr,x11,bevy_ci_testing,trace,trace_chrome,bevy_audio,vorbis" + 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 diff --git a/.github/workflows/ios.yml b/.github/workflows/ios.yml deleted file mode 100644 index 7cc954acf9c80..0000000000000 --- a/.github/workflows/ios.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: iOS cron CI - -on: - schedule: - - cron: "0 0 * * *" - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - 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 }}-cargo-check-test-${{ 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 diff --git a/.github/workflows/post-release.yml b/.github/workflows/post-release.yml new file mode 100644 index 0000000000000..4cc3ca9bde68d --- /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@v3 + 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..d4702b33b5297 --- /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@v3 + 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..ec060da4afb1e --- /dev/null +++ b/.github/workflows/validation-jobs.yml @@ -0,0 +1,157 @@ +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: Uninstall android-31 + run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --uninstall "platforms;android-31" + + - 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: cargo apk build --example android + + run-examples-on-windows: + runs-on: windows-latest + timeout-minutes: 30 + 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 + run: | + cargo build --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 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/CONTRIBUTING.md b/CONTRIBUTING.md index 20541abdb77d7..e359130848da3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,9 +74,9 @@ He makes the final decision on both design and code changes within Bevy in order 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 have: +[Bevy Org members](https://github.com/orgs/bevyengine/people) are contributors who: -1. Have actively engaged with Bevy development +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! @@ -198,9 +198,9 @@ Check out our [plugin guidelines](https://github.com/bevyengine/bevy/blob/main/d ### Fixing bugs -Bugs in Bevy (or the associated website / book) are filed on the issue tracker using the [`bug`](https://github.com/bevyengine/bevy/issues?q=is%3Aissue+is%3Aopen+label%3Abug) label. +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 [`E-Good-First-Issue`](https://github.com/bevyengine/bevy/issues?q=is%3Aopen+is%3Aissue+label%3AE-Good-First-Issue) label, and feel free to ask questions on that issue's thread in question or on Discord. +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. diff --git a/Cargo.toml b/Cargo.toml index 0c8a9a5d0441d..ab114c9d000e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.8.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/**/*"] +exclude = ["assets/", "tools/", ".github/", "crates/", "examples/wasm/assets/"] homepage = "https://bevyengine.org" keywords = ["game", "engine", "gamedev", "graphics", "bevy"] license = "MIT OR Apache-2.0" @@ -13,7 +13,7 @@ repository = "https://github.com/bevyengine/bevy" [workspace] exclude = ["benches", "crates/bevy_ecs_compile_fail_tests"] -members = ["crates/*", "examples/ios", "tools/ci", "tools/spancmp", "errors"] +members = ["crates/*", "examples/ios", "tools/ci", "tools/spancmp", "tools/build-wasm-example", "errors"] [features] default = [ @@ -143,6 +143,10 @@ path = "examples/2d/mesh2d.rs" name = "mesh2d_manual" path = "examples/2d/mesh2d_manual.rs" +[[example]] +name = "mesh2d_vertex_color_texture" +path = "examples/2d/mesh2d_vertex_color_texture.rs" + [[example]] name = "shapes" path = "examples/2d/shapes.rs" @@ -167,11 +171,19 @@ path = "examples/2d/text2d.rs" name = "texture_atlas" path = "examples/2d/texture_atlas.rs" +[[example]] +name = "transparency_2d" +path = "examples/2d/transparency_2d.rs" + # 3D Rendering [[example]] name = "3d_scene" path = "examples/3d/3d_scene.rs" +[[example]] +name = "3d_shapes" +path = "examples/3d/shapes.rs" + [[example]] name = "lighting" path = "examples/3d/lighting.rs" @@ -208,10 +220,6 @@ path = "examples/3d/render_to_texture.rs" name = "shadow_biases" path = "examples/3d/shadow_biases.rs" -[[example]] -name = "3d_shapes" -path = "examples/3d/shapes.rs" - [[example]] name = "shadow_caster_receiver" path = "examples/3d/shadow_caster_receiver.rs" @@ -220,10 +228,18 @@ path = "examples/3d/shadow_caster_receiver.rs" name = "spherical_area_lights" path = "examples/3d/spherical_area_lights.rs" +[[example]] +name = "split_screen" +path = "examples/3d/split_screen.rs" + [[example]] name = "texture" path = "examples/3d/texture.rs" +[[example]] +name = "transparency_3d" +path = "examples/3d/transparency_3d.rs" + [[example]] name = "two_passes" path = "examples/3d/two_passes.rs" @@ -508,6 +524,10 @@ path = "examples/scene/scene.rs" name = "custom_vertex_attribute" path = "examples/shader/custom_vertex_attribute.rs" +[[example]] +name = "post_processing" +path = "examples/shader/post_processing.rs" + [[example]] name = "shader_defs" path = "examples/shader/shader_defs.rs" @@ -606,6 +626,10 @@ path = "examples/ui/text.rs" name = "text_debug" path = "examples/ui/text_debug.rs" +[[example]] +name = "transparency_ui" +path = "examples/ui/transparency_ui.rs" + [[example]] name = "ui" path = "examples/ui/ui.rs" diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 9035c3b21aa77..0000000000000 --- a/LICENSE +++ /dev/null @@ -1,6 +0,0 @@ -Bevy is dual-licensed under either - -* MIT License (docs/LICENSE-MIT or http://opensource.org/licenses/MIT) -* Apache License, Version 2.0 (docs/LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - -at your option. diff --git a/docs/LICENSE-APACHE b/LICENSE-APACHE similarity index 100% rename from docs/LICENSE-APACHE rename to LICENSE-APACHE diff --git a/docs/LICENSE-MIT b/LICENSE-MIT similarity index 100% rename from docs/LICENSE-MIT rename to LICENSE-MIT diff --git a/README.md b/README.md index a5ca4d8180570..41c2f9f0ae8da 100644 --- a/README.md +++ b/README.md @@ -100,8 +100,8 @@ Additionally, we would like to thank the [Amethyst](https://github.com/amethyst/ Bevy is free and open source! All code in this repository is dual-licensed under either: -* MIT License ([LICENSE-MIT](docs/LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) -* Apache License, Version 2.0 ([LICENSE-APACHE](docs/LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) +* 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. diff --git a/assets/shaders/animate_shader.wgsl b/assets/shaders/animate_shader.wgsl index fdc60da00b539..d71b30bfafe7c 100644 --- a/assets/shaders/animate_shader.wgsl +++ b/assets/shaders/animate_shader.wgsl @@ -1,9 +1,12 @@ -#import bevy_pbr::mesh_view_bind_group -#import bevy_pbr::mesh_struct +#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; @@ -17,10 +20,8 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { - let world_position = mesh.model * vec4(vertex.position, 1.0); - var out: VertexOutput; - out.clip_position = view.view_proj * world_position; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); out.uv = vertex.uv; return out; } diff --git a/assets/shaders/custom_material_chromatic_aberration.wgsl b/assets/shaders/custom_material_chromatic_aberration.wgsl new file mode 100644 index 0000000000000..811cfb8810abc --- /dev/null +++ b/assets/shaders/custom_material_chromatic_aberration.wgsl @@ -0,0 +1,25 @@ +#import bevy_pbr::mesh_view_bindings + +[[group(1), binding(0)]] +var texture: texture_2d; + +[[group(1), binding(1)]] +var our_sampler: sampler; + + +[[stage(fragment)]] +fn fragment([[builtin(position)]] position: vec4) -> [[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 index a1fa6998f20d2..2228b79083af9 100644 --- a/assets/shaders/custom_material_screenspace_texture.wgsl +++ b/assets/shaders/custom_material_screenspace_texture.wgsl @@ -1,4 +1,4 @@ -#import bevy_pbr::mesh_view_bind_group +#import bevy_pbr::mesh_view_bindings [[group(1), binding(0)]] var texture: texture_2d; diff --git a/assets/shaders/custom_vertex_attribute.wgsl b/assets/shaders/custom_vertex_attribute.wgsl index 3e42b03d710ae..2def33a098602 100644 --- a/assets/shaders/custom_vertex_attribute.wgsl +++ b/assets/shaders/custom_vertex_attribute.wgsl @@ -1,10 +1,5 @@ -#import bevy_pbr::mesh_view_bind_group -#import bevy_pbr::mesh_struct - -struct Vertex { - [[location(0)]] position: vec3; - [[location(1)]] blend_color: vec4; -}; +#import bevy_pbr::mesh_view_bindings +#import bevy_pbr::mesh_bindings struct CustomMaterial { color: vec4; @@ -12,8 +7,13 @@ struct CustomMaterial { [[group(1), binding(0)]] var material: CustomMaterial; -[[group(2), 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)]] blend_color: vec4; +}; struct VertexOutput { [[builtin(position)]] clip_position: vec4; @@ -22,10 +22,8 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { - let world_position = mesh.model * vec4(vertex.position, 1.0); - var out: VertexOutput; - out.clip_position = view.view_proj * world_position; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); out.blend_color = vertex.blend_color; return out; } diff --git a/assets/shaders/instancing.wgsl b/assets/shaders/instancing.wgsl index 262df7224b887..c83a853b07842 100644 --- a/assets/shaders/instancing.wgsl +++ b/assets/shaders/instancing.wgsl @@ -1,9 +1,12 @@ -#import bevy_pbr::mesh_view_bind_group -#import bevy_pbr::mesh_struct +#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; @@ -21,10 +24,8 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz; - let world_position = mesh.model * vec4(position, 1.0); - var out: VertexOutput; - out.clip_position = view.view_proj * world_position; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(position, 1.0)); out.color = vertex.i_color; return out; } diff --git a/assets/shaders/shader_defs.wgsl b/assets/shaders/shader_defs.wgsl index 0d1c93d37e5ea..a26e988ed951a 100644 --- a/assets/shaders/shader_defs.wgsl +++ b/assets/shaders/shader_defs.wgsl @@ -1,9 +1,12 @@ -#import bevy_pbr::mesh_view_bind_group -#import bevy_pbr::mesh_struct +#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; @@ -16,17 +19,15 @@ struct VertexOutput { [[stage(vertex)]] fn vertex(vertex: Vertex) -> VertexOutput { - let world_position = mesh.model * vec4(vertex.position, 1.0); - var out: VertexOutput; - out.clip_position = view.view_proj * world_position; + out.clip_position = mesh_position_local_to_clip(mesh.model, vec4(vertex.position, 1.0)); return out; } [[stage(fragment)]] fn fragment() -> [[location(0)]] vec4 { var color = vec4(0.0, 0.0, 1.0, 1.0); -# ifdef IS_RED +# ifdef IS_RED color = vec4(1.0, 0.0, 0.0, 1.0); # endif return color; diff --git a/benches/Cargo.toml b/benches/Cargo.toml index eeb69a2413e65..6ba2ad92809fe 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -11,6 +11,7 @@ glam = "0.20" 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" } @@ -46,6 +47,11 @@ name = "world_get" path = "benches/bevy_ecs/world_get.rs" harness = false +[[bench]] +name = "schedule" +path = "benches/bevy_ecs/schedule.rs" +harness = false + [[bench]] name = "reflect_list" path = "benches/bevy_reflect/list.rs" diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs b/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs index 4ddae1781ea1b..169f32f06500e 100644 --- a/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs +++ b/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs @@ -1,5 +1,5 @@ use bevy_ecs::prelude::*; -use bevy_tasks::TaskPool; +use bevy_tasks::{ComputeTaskPool, TaskPool}; use glam::*; #[derive(Component, Copy, Clone)] @@ -18,6 +18,8 @@ pub struct Benchmark(World, Box>); impl Benchmark { pub fn new() -> Self { + ComputeTaskPool::init(TaskPool::default); + let mut world = World::default(); world.spawn_batch((0..1000).map(|_| { @@ -29,8 +31,8 @@ impl Benchmark { ) })); - fn sys(task_pool: Res, mut query: Query<(&mut Position, &mut Transform)>) { - query.par_for_each_mut(&task_pool, 128, |(mut pos, mut mat)| { + 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(); } @@ -39,7 +41,6 @@ impl Benchmark { }); } - world.insert_resource(TaskPool::default()); let mut system = IntoSystem::into_system(sys); system.initialize(&mut world); system.update_archetype_component_access(&world); diff --git a/benches/benches/bevy_ecs/schedule.rs b/benches/benches/bevy_ecs/schedule.rs new file mode 100644 index 0000000000000..0b8916e98b1e8 --- /dev/null +++ b/benches/benches/bevy_ecs/schedule.rs @@ -0,0 +1,69 @@ +use bevy_app::App; +use bevy_ecs::prelude::*; +use criterion::{criterion_group, criterion_main, Criterion}; + +criterion_group!(benches, build_schedule); +criterion_main!(benches); + +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, PartialEq, Eq, Hash, SystemLabel)] + struct NumLabel(usize); + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SystemLabel)] + struct DummyLabel; + + 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. + let labels: Vec<_> = (0..1000).map(NumLabel).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/crates/bevy_animation/Cargo.toml b/crates/bevy_animation/Cargo.toml index fdb9bd164ce6b..2ef00a7c53c6c 100644 --- a/crates/bevy_animation/Cargo.toml +++ b/crates/bevy_animation/Cargo.toml @@ -15,6 +15,7 @@ bevy_asset = { path = "../bevy_asset", version = "0.8.0-dev" } bevy_core = { path = "../bevy_core", version = "0.8.0-dev" } bevy_math = { path = "../bevy_math", version = "0.8.0-dev" } bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", features = ["bevy"] } +bevy_time = { path = "../bevy_time", version = "0.8.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" } bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" } bevy_transform = { path = "../bevy_transform", version = "0.8.0-dev" } diff --git a/crates/bevy_animation/src/lib.rs b/crates/bevy_animation/src/lib.rs index c894207dcd71e..7882ad4d2f274 100644 --- a/crates/bevy_animation/src/lib.rs +++ b/crates/bevy_animation/src/lib.rs @@ -6,7 +6,7 @@ use std::ops::Deref; use bevy_app::{App, CoreStage, Plugin}; use bevy_asset::{AddAsset, Assets, Handle}; -use bevy_core::{Name, Time}; +use bevy_core::Name; use bevy_ecs::{ change_detection::DetectChanges, entity::Entity, @@ -18,6 +18,7 @@ use bevy_ecs::{ use bevy_hierarchy::{Children, HierarchySystem}; 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}; @@ -229,7 +230,7 @@ pub fn animation_player( match &curve.keyframes { Keyframes::Rotation(keyframes) => transform.rotation = keyframes[0], Keyframes::Translation(keyframes) => { - transform.translation = keyframes[0] + transform.translation = keyframes[0]; } Keyframes::Scale(keyframes) => transform.scale = keyframes[0], } diff --git a/crates/bevy_app/Cargo.toml b/crates/bevy_app/Cargo.toml index 602dd466e798c..2dd25147e42f9 100644 --- a/crates/bevy_app/Cargo.toml +++ b/crates/bevy_app/Cargo.toml @@ -12,13 +12,15 @@ keywords = ["bevy"] trace = [] bevy_ci_testing = ["serde", "ron"] default = ["bevy_reflect"] +bevy_reflect = ["dep:bevy_reflect", "bevy_ecs/bevy_reflect"] [dependencies] # bevy bevy_derive = { path = "../bevy_derive", version = "0.8.0-dev" } -bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" } +bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev", default-features = false } bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", optional = true } bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" } +bevy_tasks = { path = "../bevy_tasks", version = "0.8.0-dev" } # other serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/crates/bevy_app/src/ci_testing.rs b/crates/bevy_app/src/ci_testing.rs index b319f04a91750..66206d91095c3 100644 --- a/crates/bevy_app/src/ci_testing.rs +++ b/crates/bevy_app/src/ci_testing.rs @@ -1,6 +1,8 @@ use crate::{app::AppExit, App}; use serde::Deserialize; +use bevy_utils::tracing::info; + /// A configuration struct for automated CI testing. /// /// It gets used when the `bevy_ci_testing` feature is enabled to automatically @@ -20,18 +22,29 @@ fn ci_testing_exit_after( if let Some(exit_after) = ci_testing_config.exit_after { if *current_frame > exit_after { app_exit_events.send(AppExit); + info!("Exiting after {} frames. Test successful!", exit_after); } } *current_frame += 1; } pub(crate) fn setup_app(app: &mut App) -> &mut App { - let filename = - std::env::var("CI_TESTING_CONFIG").unwrap_or_else(|_| "ci_testing_config.ron".to_string()); - let config: CiTestingConfig = ron::from_str( - &std::fs::read_to_string(filename).expect("error reading CI testing configuration file"), - ) - .expect("error deserializing CI testing configuration file"); + #[cfg(not(target_arch = "wasm32"))] + let config: CiTestingConfig = { + let filename = std::env::var("CI_TESTING_CONFIG") + .unwrap_or_else(|_| "ci_testing_config.ron".to_string()); + ron::from_str( + &std::fs::read_to_string(filename) + .expect("error reading CI testing configuration file"), + ) + .expect("error deserializing CI testing configuration file") + }; + #[cfg(target_arch = "wasm32")] + let config: CiTestingConfig = { + let config = include_str!("../../../ci_testing_config.ron"); + ron::from_str(config).expect("error deserializing CI testing configuration file") + }; + app.insert_resource(config) .add_system(ci_testing_exit_after); diff --git a/crates/bevy_app/src/plugin_group.rs b/crates/bevy_app/src/plugin_group.rs index 75d450032aa7b..d707634fd8503 100644 --- a/crates/bevy_app/src/plugin_group.rs +++ b/crates/bevy_app/src/plugin_group.rs @@ -128,7 +128,7 @@ impl PluginGroupBuilder { /// Consumes the [`PluginGroupBuilder`] and [builds](Plugin::build) the contained [`Plugin`]s /// in the order specified. pub fn finish(self, app: &mut App) { - for ty in self.order.iter() { + for ty in &self.order { if let Some(entry) = self.plugins.get(ty) { if entry.enabled { debug!("added plugin: {}", entry.plugin.name()); @@ -173,7 +173,7 @@ mod tests { std::any::TypeId::of::(), std::any::TypeId::of::(), ] - ) + ); } #[test] @@ -190,7 +190,7 @@ mod tests { std::any::TypeId::of::(), std::any::TypeId::of::(), ] - ) + ); } #[test] @@ -207,7 +207,7 @@ mod tests { std::any::TypeId::of::(), std::any::TypeId::of::(), ] - ) + ); } #[test] @@ -225,7 +225,7 @@ mod tests { std::any::TypeId::of::(), std::any::TypeId::of::(), ] - ) + ); } #[test] @@ -243,7 +243,7 @@ mod tests { std::any::TypeId::of::(), std::any::TypeId::of::(), ] - ) + ); } #[test] @@ -261,6 +261,6 @@ mod tests { std::any::TypeId::of::(), std::any::TypeId::of::(), ] - ) + ); } } diff --git a/crates/bevy_asset/Cargo.toml b/crates/bevy_asset/Cargo.toml index 550a5b0330e33..5ccf6ac63f5c9 100644 --- a/crates/bevy_asset/Cargo.toml +++ b/crates/bevy_asset/Cargo.toml @@ -40,7 +40,7 @@ wasm-bindgen-futures = "0.4" js-sys = "0.3" [target.'cfg(target_os = "android")'.dependencies] -ndk-glue = { version = "0.6" } +ndk-glue = { version = "0.5" } [dev-dependencies] futures-lite = "1.4.0" diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index 04d1f3bda07d4..bc7defc361c52 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -7,7 +7,7 @@ use crate::{ use anyhow::Result; use bevy_ecs::system::{Res, ResMut}; use bevy_log::warn; -use bevy_tasks::TaskPool; +use bevy_tasks::IoTaskPool; use bevy_utils::{Entry, HashMap, Uuid}; use crossbeam_channel::TryRecvError; use parking_lot::{Mutex, RwLock}; @@ -56,7 +56,6 @@ pub struct AssetServerInternal { loaders: RwLock>>, extension_to_loader_index: RwLock>, handle_to_path: Arc>>>, - task_pool: TaskPool, } /// Loads assets from the filesystem on background threads @@ -66,11 +65,11 @@ pub struct AssetServer { } impl AssetServer { - pub fn new(source_io: T, task_pool: TaskPool) -> Self { - Self::with_boxed_io(Box::new(source_io), task_pool) + pub fn new(source_io: T) -> Self { + Self::with_boxed_io(Box::new(source_io)) } - pub fn with_boxed_io(asset_io: Box, task_pool: TaskPool) -> Self { + pub fn with_boxed_io(asset_io: Box) -> Self { AssetServer { server: Arc::new(AssetServerInternal { loaders: Default::default(), @@ -79,7 +78,6 @@ impl AssetServer { asset_ref_counter: Default::default(), handle_to_path: Default::default(), asset_lifecycles: Default::default(), - task_pool, asset_io, }), } @@ -315,7 +313,6 @@ impl AssetServer { &self.server.asset_ref_counter.channel, self.asset_io(), version, - &self.server.task_pool, ); if let Err(err) = asset_loader @@ -377,8 +374,7 @@ impl AssetServer { pub(crate) fn load_untracked(&self, asset_path: AssetPath<'_>, force: bool) -> HandleId { let server = self.clone(); let owned_path = asset_path.to_owned(); - self.server - .task_pool + IoTaskPool::get() .spawn(async move { if let Err(err) = server.load_async(owned_path, force).await { warn!("{}", err); @@ -620,8 +616,8 @@ mod test { fn setup(asset_path: impl AsRef) -> AssetServer { use crate::FileAssetIo; - - AssetServer::new(FileAssetIo::new(asset_path, false), Default::default()) + IoTaskPool::init(Default::default); + AssetServer::new(FileAssetIo::new(asset_path, false)) } #[test] @@ -781,8 +777,11 @@ mod test { asset_server.get_handle_untyped(id) } - fn get_asset(id: impl Into, world: &World) -> Option<&PngAsset> { - world.resource::>().get(id.into()) + fn get_asset<'world>( + id: &Handle, + world: &'world World, + ) -> Option<&'world PngAsset> { + world.resource::>().get(id) } fn get_load_state(id: impl Into, world: &World) -> LoadState { @@ -800,7 +799,7 @@ mod test { ); // load the asset - let handle = load_asset(path.clone(), &app.world); + let handle = load_asset(path.clone(), &app.world).typed(); let weak_handle = handle.clone_weak(); // asset is loading @@ -826,7 +825,7 @@ mod test { assert!(get_asset(&weak_handle, &app.world).is_none()); // finally, reload the asset - let handle = load_asset(path.clone(), &app.world); + let handle = load_asset(path.clone(), &app.world).typed(); assert_eq!(LoadState::Loading, get_load_state(&handle, &app.world)); app.update(); assert_eq!(LoadState::Loaded, get_load_state(&handle, &app.world)); diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 0a111ffbd8867..23ec2cf3dee66 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -129,12 +129,12 @@ impl Assets { /// /// This is the main method for accessing asset data from an [Assets] collection. If you need /// mutable access to the asset, use [`get_mut`](Assets::get_mut). - pub fn get>(&self, handle: H) -> Option<&T> { + pub fn get(&self, handle: &Handle) -> Option<&T> { self.assets.get(&handle.into()) } /// Checks if an asset exists for the given handle - pub fn contains>(&self, handle: H) -> bool { + pub fn contains(&self, handle: &Handle) -> bool { self.assets.contains_key(&handle.into()) } @@ -142,7 +142,7 @@ impl Assets { /// /// This is the main method for mutably accessing asset data from an [Assets] collection. If you /// do not need mutable access to the asset, you may also use [get](Assets::get). - pub fn get_mut>(&mut self, handle: H) -> Option<&mut T> { + pub fn get_mut(&mut self, handle: &Handle) -> Option<&mut T> { let id: HandleId = handle.into(); self.events.send(AssetEvent::Modified { handle: Handle::weak(id), @@ -398,6 +398,6 @@ mod tests { let handle = assets_before.add(MyAsset); app.add_asset::(); // Ensure this doesn't overwrite the Asset let assets_after = app.world.resource_mut::>(); - assert!(assets_after.get(handle).is_some()); + assert!(assets_after.get(&handle).is_some()); } } diff --git a/crates/bevy_asset/src/debug_asset_server.rs b/crates/bevy_asset/src/debug_asset_server.rs index d4970dabcc071..b7e3c71c85c89 100644 --- a/crates/bevy_asset/src/debug_asset_server.rs +++ b/crates/bevy_asset/src/debug_asset_server.rs @@ -58,14 +58,14 @@ impl Default for HandleMap { impl Plugin for DebugAssetServerPlugin { fn build(&self, app: &mut bevy_app::App) { + IoTaskPool::init(|| { + TaskPoolBuilder::default() + .num_threads(2) + .thread_name("Debug Asset Server IO Task Pool".to_string()) + .build() + }); let mut debug_asset_app = App::new(); debug_asset_app - .insert_resource(IoTaskPool( - TaskPoolBuilder::default() - .num_threads(2) - .thread_name("Debug Asset Server IO Task Pool".to_string()) - .build(), - )) .insert_resource(AssetServerSettings { asset_folder: "crates".to_string(), watch_for_changes: true, @@ -93,8 +93,7 @@ pub(crate) fn sync_debug_assets( let (changed_shaders, handle_map, debug_assets) = state.get_mut(world); for changed in changed_shaders.iter_current_update_events() { let debug_handle = match changed { - AssetEvent::Created { handle } => handle, - AssetEvent::Modified { handle } => handle, + AssetEvent::Created { handle } | AssetEvent::Modified { handle } => handle, AssetEvent::Removed { .. } => continue, }; if let Some(handle) = handle_map.handles.get(debug_handle) { diff --git a/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs b/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs index 8224edd3893da..d5f64c8a455e8 100644 --- a/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs +++ b/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs @@ -48,6 +48,6 @@ impl AssetCountDiagnosticsPlugin { } pub fn diagnostic_system(mut diagnostics: ResMut, assets: Res>) { - diagnostics.add_measurement(Self::diagnostic_id(), assets.len() as f64); + diagnostics.add_measurement(Self::diagnostic_id(), || assets.len() as f64); } } diff --git a/crates/bevy_asset/src/handle.rs b/crates/bevy_asset/src/handle.rs index 06fdee6907a37..e929371e2fdbc 100644 --- a/crates/bevy_asset/src/handle.rs +++ b/crates/bevy_asset/src/handle.rs @@ -10,7 +10,7 @@ use crate::{ Asset, Assets, }; use bevy_ecs::{component::Component, reflect::ReflectComponent}; -use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize}; +use bevy_reflect::{FromReflect, Reflect, ReflectDeserialize, ReflectSerialize}; use bevy_utils::Uuid; use crossbeam_channel::{Receiver, Sender}; use serde::{Deserialize, Serialize}; @@ -169,7 +169,7 @@ impl Handle { /// Makes this handle Strong if it wasn't already. /// /// This method requires the corresponding [Assets](crate::Assets) collection - pub fn make_strong(&mut self, assets: &mut Assets) { + pub fn make_strong(&mut self, assets: &Assets) { if self.is_strong() { return; } @@ -341,6 +341,14 @@ impl HandleUntyped { matches!(self.handle_type, HandleType::Strong(_)) } + /// Create a weak typed [`Handle`] from this handle. + /// + /// If this handle is strong and dropped, there is no guarantee that the asset + /// will still be available (if only the returned handle is kept) + pub fn typed_weak(&self) -> Handle { + self.clone_weak().typed() + } + /// Convert this handle into a typed [Handle]. /// /// The new handle will maintain the Strong or Weak status of the current handle. diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 870f100d10306..b5ba1a1854d02 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -30,7 +30,6 @@ pub use path::*; use bevy_app::{prelude::Plugin, App}; use bevy_ecs::schedule::{StageLabel, SystemStage}; -use bevy_tasks::IoTaskPool; /// The names of asset stages in an App Schedule #[derive(Debug, Hash, PartialEq, Eq, Clone, StageLabel)] @@ -82,12 +81,8 @@ pub fn create_platform_default_asset_io(app: &mut App) -> Box { impl Plugin for AssetPlugin { fn build(&self, app: &mut App) { if !app.world.contains_resource::() { - let task_pool = app.world.resource::().0.clone(); - let source = create_platform_default_asset_io(app); - - let asset_server = AssetServer::with_boxed_io(source, task_pool); - + let asset_server = AssetServer::with_boxed_io(source); app.insert_resource(asset_server); } diff --git a/crates/bevy_asset/src/loader.rs b/crates/bevy_asset/src/loader.rs index 5a5de9b8c11eb..5d6b87d8388ba 100644 --- a/crates/bevy_asset/src/loader.rs +++ b/crates/bevy_asset/src/loader.rs @@ -5,7 +5,6 @@ use crate::{ use anyhow::Result; use bevy_ecs::system::{Res, ResMut}; use bevy_reflect::{TypeUuid, TypeUuidDynamic}; -use bevy_tasks::TaskPool; use bevy_utils::{BoxedFuture, HashMap}; use crossbeam_channel::{Receiver, Sender}; use downcast_rs::{impl_downcast, Downcast}; @@ -84,7 +83,6 @@ pub struct LoadContext<'a> { pub(crate) labeled_assets: HashMap, BoxedLoadedAsset>, pub(crate) path: &'a Path, pub(crate) version: usize, - pub(crate) task_pool: &'a TaskPool, } impl<'a> LoadContext<'a> { @@ -93,7 +91,6 @@ impl<'a> LoadContext<'a> { ref_change_channel: &'a RefChangeChannel, asset_io: &'a dyn AssetIo, version: usize, - task_pool: &'a TaskPool, ) -> Self { Self { ref_change_channel, @@ -101,7 +98,6 @@ impl<'a> LoadContext<'a> { labeled_assets: Default::default(), version, path, - task_pool, } } @@ -144,10 +140,6 @@ impl<'a> LoadContext<'a> { asset_metas } - pub fn task_pool(&self) -> &TaskPool { - self.task_pool - } - pub fn asset_io(&self) -> &dyn AssetIo { self.asset_io } diff --git a/crates/bevy_asset/src/path.rs b/crates/bevy_asset/src/path.rs index df99eaee19da1..cb39b3f5ca127 100644 --- a/crates/bevy_asset/src/path.rs +++ b/crates/bevy_asset/src/path.rs @@ -1,4 +1,4 @@ -use bevy_reflect::{Reflect, ReflectDeserialize}; +use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize}; use bevy_utils::AHasher; use serde::{Deserialize, Serialize}; use std::{ diff --git a/crates/bevy_audio/src/audio_output.rs b/crates/bevy_audio/src/audio_output.rs index f7010f2c7d8f3..e378a2c43f4c3 100644 --- a/crates/bevy_audio/src/audio_output.rs +++ b/crates/bevy_audio/src/audio_output.rs @@ -43,17 +43,15 @@ where Source: Asset + Decodable, { fn play_source(&self, audio_source: &Source, repeat: bool) -> Option { - if let Some(stream_handle) = &self.stream_handle { + self.stream_handle.as_ref().map(|stream_handle| { let sink = Sink::try_new(stream_handle).unwrap(); if repeat { sink.append(audio_source.decoder().repeat_infinite()); } else { sink.append(audio_source.decoder()); } - Some(sink) - } else { - None - } + sink + }) } fn try_play_queued( diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 44b2be465b9cf..7c7d193564f87 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -3,25 +3,19 @@ mod name; mod task_pool_options; -mod time; pub use bytemuck::{bytes_of, cast_slice, Pod, Zeroable}; pub use name::*; pub use task_pool_options::*; -pub use time::*; pub mod prelude { //! The Bevy Core Prelude. #[doc(hidden)] - pub use crate::{DefaultTaskPoolOptions, Name, Time, Timer}; + pub use crate::{DefaultTaskPoolOptions, Name}; } use bevy_app::prelude::*; -use bevy_ecs::{ - entity::Entity, - schedule::{ExclusiveSystemDescriptorCoercion, SystemLabel}, - system::IntoExclusiveSystem, -}; +use bevy_ecs::entity::Entity; use bevy_utils::HashSet; use std::ops::Range; @@ -29,14 +23,6 @@ use std::ops::Range; #[derive(Default)] pub struct CorePlugin; -/// A `SystemLabel` enum for ordering systems relative to core Bevy systems. -#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemLabel)] -pub enum CoreSystem { - /// Updates the elapsed time. Any system that interacts with [Time] component should run after - /// this. - Time, -} - impl Plugin for CorePlugin { fn build(&self, app: &mut App) { // Setup the default bevy task pools @@ -44,22 +30,9 @@ impl Plugin for CorePlugin { .get_resource::() .cloned() .unwrap_or_default() - .create_default_pools(&mut app.world); + .create_default_pools(); - app.init_resource::