Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4fa86fb01 | ||
|
|
7da2a08df6 | ||
|
|
82246d934d | ||
|
|
c60969ef67 | ||
|
|
ce37f216bc | ||
|
|
81c71fbbb9 | ||
|
|
07a7c6003a | ||
|
|
804a74c205 | ||
|
|
dde49a410a | ||
|
|
770ce7f8ec | ||
|
|
11714d3adc | ||
|
|
6aac9071b3 | ||
|
|
da9173e9dc | ||
|
|
cd64d50408 | ||
|
|
a3145cb96f | ||
|
|
cdfb92e14a | ||
|
|
55c16e6e7a | ||
|
|
65e3c3bfb9 |
@@ -0,0 +1,26 @@
|
|||||||
|
name: 'Renderdiff Generate'
|
||||||
|
description: 'Sets up prerequisites and runs the generate script for renderdiff'
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- uses: ./.github/actions/mac-prereq
|
||||||
|
- uses: ./.github/actions/get-gltf-assets
|
||||||
|
- uses: ./.github/actions/get-mesa
|
||||||
|
- uses: ./.github/actions/get-vulkan-sdk
|
||||||
|
- name: Prerequisites
|
||||||
|
run: |
|
||||||
|
# Must have at least clang-16 for a webgpu/dawn build.
|
||||||
|
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
||||||
|
shell: bash
|
||||||
|
- name: Generate images
|
||||||
|
run: |
|
||||||
|
TEST_DIR=test/renderdiff
|
||||||
|
source ${TEST_DIR}/src/preamble.sh
|
||||||
|
set -eux
|
||||||
|
bash ${TEST_DIR}/generate.sh
|
||||||
|
set +eux
|
||||||
|
shell: bash
|
||||||
|
- name: Build diffimg tool
|
||||||
|
run: |
|
||||||
|
./build.sh release diffimg
|
||||||
|
shell: bash
|
||||||
@@ -10,16 +10,27 @@ jobs:
|
|||||||
# a branch on filament-assets.
|
# a branch on filament-assets.
|
||||||
update-renderdiff-goldens:
|
update-renderdiff-goldens:
|
||||||
name: update-renderdiff-goldens
|
name: update-renderdiff-goldens
|
||||||
runs-on: 'ubuntu-24.04-4core'
|
runs-on: 'macos-14-xlarge'
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: ./.github/actions/linux-prereq
|
|
||||||
- id: get_commit_msg
|
- id: get_commit_msg
|
||||||
uses: ./.github/actions/get-commit-msg
|
uses: ./.github/actions/get-commit-msg
|
||||||
- name: Build diffimg
|
- name: Check if accepting new goldens
|
||||||
run: ./build.sh release diffimg
|
id: check_accept
|
||||||
|
env:
|
||||||
|
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||||
|
run: |
|
||||||
|
if echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py --mode=accept_new_goldens; then
|
||||||
|
echo "accept=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "accept=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
shell: bash
|
||||||
|
- name: Renderdiff Generate for new goldens
|
||||||
|
if: steps.check_accept.outputs.accept == 'true'
|
||||||
|
uses: ./.github/actions/renderdiff-generate
|
||||||
- name: Run update script
|
- name: Run update script
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||||
@@ -27,10 +38,24 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
|
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
|
||||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||||
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
|
||||||
git config --global user.email "filament.bot@gmail.com"
|
git config --global user.email "filament.bot@gmail.com"
|
||||||
git config --global user.name "Filament Bot"
|
git config --global user.name "Filament Bot"
|
||||||
git config --global credential.helper cache
|
git config --global credential.helper cache
|
||||||
|
|
||||||
|
if [[ "${{ steps.check_accept.outputs.accept }}" == "true" ]]; then
|
||||||
|
SHORT_HASH="${COMMIT_HASH:0:8}"
|
||||||
|
GOLDEN_BRANCH="accept-goldens-${SHORT_HASH}"
|
||||||
|
echo "Generating new goldens for branch ${GOLDEN_BRANCH}"
|
||||||
|
python3 test/renderdiff/src/update_golden.py \
|
||||||
|
--branch=${GOLDEN_BRANCH} \
|
||||||
|
--source=$(pwd)/out/renderdiff/renders \
|
||||||
|
--commit-msg="Auto-update goldens from ${COMMIT_HASH}" \
|
||||||
|
--push-to-remote \
|
||||||
|
--golden-repo-token=${GH_TOKEN}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
||||||
echo "branch==${GOLDEN_BRANCH}"
|
echo "branch==${GOLDEN_BRANCH}"
|
||||||
echo "hash==${COMMIT_HASH}"
|
echo "hash==${COMMIT_HASH}"
|
||||||
python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \
|
python3 test/renderdiff/src/update_golden.py --branch=${GOLDEN_BRANCH} \
|
||||||
|
|||||||
@@ -126,18 +126,24 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: ./.github/actions/mac-prereq
|
|
||||||
- uses: ./.github/actions/get-gltf-assets
|
|
||||||
- uses: ./.github/actions/get-mesa
|
|
||||||
- uses: ./.github/actions/get-vulkan-sdk
|
|
||||||
- id: get_commit_msg
|
- id: get_commit_msg
|
||||||
uses: ./.github/actions/get-commit-msg
|
uses: ./.github/actions/get-commit-msg
|
||||||
- name: Prerequisites
|
- name: Check if accepting new goldens
|
||||||
|
id: check_accept
|
||||||
|
env:
|
||||||
|
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||||
run: |
|
run: |
|
||||||
# Must have at least clang-16 for a webgpu/dawn build.
|
if echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py --mode=accept_new_goldens; then
|
||||||
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
|
echo "accept=true" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "accept=false" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
|
- name: Renderdiff Generate
|
||||||
|
if: steps.check_accept.outputs.accept != 'true'
|
||||||
|
uses: ./.github/actions/renderdiff-generate
|
||||||
- name: Render and compare
|
- name: Render and compare
|
||||||
|
if: steps.check_accept.outputs.accept != 'true'
|
||||||
id: render_compare
|
id: render_compare
|
||||||
env:
|
env:
|
||||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||||
@@ -146,9 +152,6 @@ jobs:
|
|||||||
source ${TEST_DIR}/src/preamble.sh
|
source ${TEST_DIR}/src/preamble.sh
|
||||||
set -eux
|
set -eux
|
||||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
|
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
|
||||||
bash ${TEST_DIR}/generate.sh
|
|
||||||
# Build diffimg tool
|
|
||||||
./build.sh release diffimg
|
|
||||||
|
|
||||||
python3 ${TEST_DIR}/src/golden_manager.py \
|
python3 ${TEST_DIR}/src/golden_manager.py \
|
||||||
--branch=${GOLDEN_BRANCH} \
|
--branch=${GOLDEN_BRANCH} \
|
||||||
@@ -172,10 +175,12 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
shell: bash
|
shell: bash
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: steps.check_accept.outputs.accept != 'true'
|
||||||
with:
|
with:
|
||||||
name: presubmit-renderdiff-result
|
name: presubmit-renderdiff-result
|
||||||
path: ./out/renderdiff
|
path: ./out/renderdiff
|
||||||
- name: Compare result
|
- name: Compare result
|
||||||
|
if: steps.check_accept.outputs.accept != 'true'
|
||||||
run: |
|
run: |
|
||||||
ERROR_STR="${{ steps.render_compare.outputs.err }}"
|
ERROR_STR="${{ steps.render_compare.outputs.err }}"
|
||||||
if [ -n "${ERROR_STR}" ]; then
|
if [ -n "${ERROR_STR}" ]; then
|
||||||
|
|||||||
@@ -8,3 +8,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
|||||||
## Release notes for next branch cut
|
## Release notes for next branch cut
|
||||||
|
|
||||||
- engine: fix crash when using variance shadow maps
|
- engine: fix crash when using variance shadow maps
|
||||||
|
- materials: better shadow normal-bias calculations [⚠️ **New Material Version**]
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.69.3'
|
implementation 'com.google.android.filament:filament-android:1.69.4'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
|||||||
iOS projects can use CocoaPods to install the latest release:
|
iOS projects can use CocoaPods to install the latest release:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pod 'Filament', '~> 1.69.3'
|
pod 'Filament', '~> 1.69.4'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ A new header is inserted each time a *tag* is created.
|
|||||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||||
|
|
||||||
|
## v1.69.5
|
||||||
|
|
||||||
|
- engine: fix crash when using variance shadow maps
|
||||||
|
|
||||||
## v1.69.4
|
## v1.69.4
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
GROUP=com.google.android.filament
|
GROUP=com.google.android.filament
|
||||||
VERSION_NAME=1.69.3
|
VERSION_NAME=1.69.4
|
||||||
|
|
||||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -235,9 +235,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
},
|
},
|
||||||
@@ -429,9 +429,9 @@
|
|||||||
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="
|
"integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
"integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.69.3'
|
implementation 'com.google.android.filament:filament-android:1.69.4'
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||||
@@ -195,7 +195,7 @@ dependencies {
|
|||||||
</div>
|
</div>
|
||||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||||
<pre><code class="language-shell">pod 'Filament', '~> 1.69.3'
|
<pre><code class="language-shell">pod 'Filament', '~> 1.69.4'
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ branch called <code>my-pr-branch</code>, to <code>filament</code>. This PR requi
|
|||||||
it in the following fashion</p>
|
it in the following fashion</p>
|
||||||
<h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3>
|
<h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3>
|
||||||
<ul>
|
<ul>
|
||||||
|
<li>Make sure you've completed the steps in 'Setting up python'</li>
|
||||||
<li>Run interactive mode in the <code>update_golden.py</code> script.
|
<li>Run interactive mode in the <code>update_golden.py</code> script.
|
||||||
<pre><code>python3 test/renderdiff/src/update_golden.py
|
<pre><code>python3 test/renderdiff/src/update_golden.py
|
||||||
</code></pre>
|
</code></pre>
|
||||||
@@ -263,6 +264,27 @@ branch of the golden repo (i.e. <code>my-pr-branch-golden</code>).</li>
|
|||||||
<li>If the PR is merged, then there is another workflow that will merge <code>my-pr-branch-golden</code>
|
<li>If the PR is merged, then there is another workflow that will merge <code>my-pr-branch-golden</code>
|
||||||
to the <code>main</code> branch of the golden repo.</li>
|
to the <code>main</code> branch of the golden repo.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<h3 id="automated-update-via-commit-message"><a class="header" href="#automated-update-via-commit-message">Automated update via commit message</a></h3>
|
||||||
|
<p>Alternatively, if you are confident in your changes and want the CI to handle the update
|
||||||
|
for you, you can use the following tag in your commit message:</p>
|
||||||
|
<ul>
|
||||||
|
<li>In the commit message of your working branch on <code>filament</code>, add the following line:
|
||||||
|
<pre><code>RDIFF_ACCEPT_NEW_GOLDENS
|
||||||
|
</code></pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p>This has the following effects:</p>
|
||||||
|
<ul>
|
||||||
|
<li>The presubmit test <code>test-renderdiff</code> will be bypassed (it will not perform rendering or
|
||||||
|
comparison).</li>
|
||||||
|
<li>When the PR is merged, the postsubmit CI will automatically:
|
||||||
|
<ol>
|
||||||
|
<li>Build Filament and generate the new images.</li>
|
||||||
|
<li>Upload them to a temporary branch in <code>filament-assets</code>.</li>
|
||||||
|
<li>Merge that branch into <code>main</code>.</li>
|
||||||
|
</ol>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<h2 id="viewing-test-results"><a class="header" href="#viewing-test-results">Viewing test results</a></h2>
|
<h2 id="viewing-test-results"><a class="header" href="#viewing-test-results">Viewing test results</a></h2>
|
||||||
<p>We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
|
<p>We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
|
||||||
be used by pointing your browser to a localhost port. If you input the viewer with a PR or a
|
be used by pointing your browser to a localhost port. If you input the viewer with a PR or a
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -27,8 +27,6 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -66,7 +64,7 @@ public:
|
|||||||
|
|
||||||
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This
|
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This
|
||||||
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
|
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
|
||||||
void flush(std::function<void(void*, void*)> const& debugPrintHistogram = nullptr);
|
void flush();
|
||||||
|
|
||||||
// returns from waitForCommands() immediately.
|
// returns from waitForCommands() immediately.
|
||||||
void requestExit();
|
void requestExit();
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ namespace filament::backend {
|
|||||||
|
|
||||||
class CommandBase {
|
class CommandBase {
|
||||||
static constexpr size_t FILAMENT_OBJECT_ALIGNMENT = alignof(std::max_align_t);
|
static constexpr size_t FILAMENT_OBJECT_ALIGNMENT = alignof(std::max_align_t);
|
||||||
friend class CommandStream;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using Execute = Dispatcher::Execute;
|
using Execute = Dispatcher::Execute;
|
||||||
@@ -169,8 +168,8 @@ struct CommandType<void (Driver::*)(ARGS...)> {
|
|||||||
|
|
||||||
class CustomCommand : public CommandBase {
|
class CustomCommand : public CommandBase {
|
||||||
std::function<void()> mCommand;
|
std::function<void()> mCommand;
|
||||||
public:
|
|
||||||
static void execute(Driver&, CommandBase* base, intptr_t* next);
|
static void execute(Driver&, CommandBase* base, intptr_t* next);
|
||||||
|
public:
|
||||||
CustomCommand(CustomCommand&& rhs) = default;
|
CustomCommand(CustomCommand&& rhs) = default;
|
||||||
|
|
||||||
explicit CustomCommand(std::function<void()> cmd)
|
explicit CustomCommand(std::function<void()> cmd)
|
||||||
@@ -180,12 +179,11 @@ public:
|
|||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
class NoopCommand : public CommandBase {
|
class NoopCommand : public CommandBase {
|
||||||
public:
|
|
||||||
intptr_t mNext;
|
intptr_t mNext;
|
||||||
static void execute(Driver&, CommandBase* self, intptr_t* next) noexcept {
|
static void execute(Driver&, CommandBase* self, intptr_t* next) noexcept {
|
||||||
*next = static_cast<NoopCommand*>(self)->mNext;
|
*next = static_cast<NoopCommand*>(self)->mNext;
|
||||||
}
|
}
|
||||||
|
public:
|
||||||
constexpr explicit NoopCommand(void* next) noexcept
|
constexpr explicit NoopCommand(void* next) noexcept
|
||||||
: CommandBase(execute), mNext(intptr_t((char *)next - (char *)this)) { }
|
: CommandBase(execute), mNext(intptr_t((char *)next - (char *)this)) { }
|
||||||
};
|
};
|
||||||
@@ -221,36 +219,6 @@ public:
|
|||||||
|
|
||||||
CircularBuffer const& getCircularBuffer() const noexcept { return mCurrentBuffer; }
|
CircularBuffer const& getCircularBuffer() const noexcept { return mCurrentBuffer; }
|
||||||
|
|
||||||
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
|
|
||||||
using Execute = Dispatcher::Execute;
|
|
||||||
struct CommandInfo {
|
|
||||||
size_t size;
|
|
||||||
const char* name;
|
|
||||||
int index;
|
|
||||||
};
|
|
||||||
std::unordered_map<Execute, CommandInfo> mCommands;
|
|
||||||
|
|
||||||
void initializeLookup() {
|
|
||||||
int currentIndex = 0;
|
|
||||||
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params)
|
|
||||||
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
|
||||||
mCommands[mDispatcher.methodName##_] = { CommandBase::align(sizeof(COMMAND_TYPE(methodName))), \
|
|
||||||
#methodName, currentIndex++ };
|
|
||||||
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
|
|
||||||
mCommands[mDispatcher.methodName##_] = { \
|
|
||||||
CommandBase::align(sizeof(COMMAND_TYPE(methodName##R))), #methodName, currentIndex++ \
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "private/backend/DriverAPI.inc"
|
|
||||||
|
|
||||||
mCommands[CustomCommand::execute] = { CommandBase::align(sizeof(CustomCommand)),
|
|
||||||
"CustomCommand", currentIndex++ };
|
|
||||||
|
|
||||||
// NoopCommands have variable size. We will handle them specially using their mNext pointer.
|
|
||||||
mCommands[NoopCommand::execute] = { 0, "NoopCommand", currentIndex++ };
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
||||||
inline void methodName(paramsDecl) noexcept { \
|
inline void methodName(paramsDecl) noexcept { \
|
||||||
@@ -295,13 +263,6 @@ public:
|
|||||||
|
|
||||||
void execute(void* buffer);
|
void execute(void* buffer);
|
||||||
|
|
||||||
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
|
|
||||||
void debugIterateCommands(void* head, void* tail,
|
|
||||||
std::function<void(CommandInfo const& info)> const& callback);
|
|
||||||
|
|
||||||
void debugPrintHistogram(void* head, void* tail);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* queueCommand() allows to queue a lambda function as a command.
|
* queueCommand() allows to queue a lambda function as a command.
|
||||||
* This is much less efficient than using the Driver* API.
|
* This is much less efficient than using the Driver* API.
|
||||||
|
|||||||
@@ -48,11 +48,6 @@
|
|||||||
|
|
||||||
#define FILAMENT_DEBUG_COMMANDS FILAMENT_DEBUG_COMMANDS_NONE
|
#define FILAMENT_DEBUG_COMMANDS FILAMENT_DEBUG_COMMANDS_NONE
|
||||||
|
|
||||||
// Upon command stream overflow, print a histogram of commands
|
|
||||||
#ifndef FILAMENT_DEBUG_COMMANDS_HISTOGRAM
|
|
||||||
#define FILAMENT_DEBUG_COMMANDS_HISTOGRAM 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
class BufferDescriptor;
|
class BufferDescriptor;
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ bool CommandBufferQueue::isExitRequested() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CommandBufferQueue::flush(std::function<void(void*, void*)> const& debugPrintHistogram) {
|
void CommandBufferQueue::flush() {
|
||||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||||
|
|
||||||
CircularBuffer& circularBuffer = mCircularBuffer;
|
CircularBuffer& circularBuffer = mCircularBuffer;
|
||||||
@@ -106,13 +106,6 @@ void CommandBufferQueue::flush(std::function<void(void*, void*)> const& debugPri
|
|||||||
std::unique_lock lock(mLock);
|
std::unique_lock lock(mLock);
|
||||||
|
|
||||||
// circular buffer is too small, we corrupted the stream
|
// circular buffer is too small, we corrupted the stream
|
||||||
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
|
|
||||||
if (UTILS_VERY_UNLIKELY(used > mFreeSpace)) {
|
|
||||||
if (debugPrintHistogram) {
|
|
||||||
debugPrintHistogram(begin, end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
||||||
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
|
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
|
||||||
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
|
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
|
||||||
|
|||||||
@@ -30,11 +30,6 @@
|
|||||||
#include <utils/ostream.h>
|
#include <utils/ostream.h>
|
||||||
#include <utils/sstream.h>
|
#include <utils/sstream.h>
|
||||||
|
|
||||||
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
|
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -88,10 +83,6 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept
|
|||||||
__system_property_get("debug.filament.perfcounters", property);
|
__system_property_get("debug.filament.perfcounters", property);
|
||||||
mUsePerformanceCounter = bool(atoi(property));
|
mUsePerformanceCounter = bool(atoi(property));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
|
|
||||||
initializeLookup();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandStream::execute(void* buffer) {
|
void CommandStream::execute(void* buffer) {
|
||||||
@@ -135,71 +126,6 @@ void CommandStream::execute(void* buffer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
|
|
||||||
void CommandStream::debugIterateCommands(void* head, void* tail,
|
|
||||||
std::function<void(CommandInfo const& info)> const& callback) {
|
|
||||||
CommandBase* UTILS_RESTRICT base = static_cast<CommandBase*>(head);
|
|
||||||
auto p = base;
|
|
||||||
while (UTILS_LIKELY(p)) {
|
|
||||||
if (p >= tail) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Execute e = p->mExecute;
|
|
||||||
|
|
||||||
if (e == NoopCommand::execute) {
|
|
||||||
NoopCommand* noop = static_cast<NoopCommand*>(p);
|
|
||||||
size_t size = noop->mNext;
|
|
||||||
int noopIndex = mCommands[NoopCommand::execute].index;
|
|
||||||
callback({ size, "NoopCommand", noopIndex });
|
|
||||||
p = reinterpret_cast<CommandBase*>(reinterpret_cast<char*>(p) + size);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto it = mCommands.find(e); it != mCommands.end()) {
|
|
||||||
size_t size = it->second.size;
|
|
||||||
callback(it->second);
|
|
||||||
p = reinterpret_cast<CommandBase*>(reinterpret_cast<char*>(p) + size);
|
|
||||||
} else {
|
|
||||||
LOG(ERROR) << "Cannot find command in lookup table";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommandStream::debugPrintHistogram(void* head, void* tail) {
|
|
||||||
std::unordered_map<std::string_view, int> histogram;
|
|
||||||
std::unordered_map<int, int> index_histogram;
|
|
||||||
debugIterateCommands(head, tail, [&](CommandInfo const& info) {
|
|
||||||
histogram[std::string_view(info.name)]++;
|
|
||||||
index_histogram[info.index]++;
|
|
||||||
});
|
|
||||||
|
|
||||||
std::vector<std::pair<std::string_view, int>> sorted_histogram(histogram.begin(),
|
|
||||||
histogram.end());
|
|
||||||
std::sort(sorted_histogram.begin(), sorted_histogram.end(),
|
|
||||||
[](auto const& a, auto const& b) { return a.second > b.second; });
|
|
||||||
|
|
||||||
LOG(INFO) << "Command stream histogram:";
|
|
||||||
for (auto const& [name, count]: sorted_histogram) {
|
|
||||||
LOG(INFO) << name << ": " << count;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<int, int>> sorted_index_histogram(index_histogram.begin(),
|
|
||||||
index_histogram.end());
|
|
||||||
std::sort(sorted_index_histogram.begin(), sorted_index_histogram.end(),
|
|
||||||
[](auto const& a, auto const& b) { return a.second > b.second; });
|
|
||||||
|
|
||||||
std::string short_histogram = "";
|
|
||||||
for (size_t i = 0, n = sorted_index_histogram.size(); i < n; ++i) {
|
|
||||||
short_histogram += std::to_string(sorted_index_histogram[i].first) + ":" +
|
|
||||||
std::to_string(sorted_index_histogram[i].second);
|
|
||||||
short_histogram += (i < n - 1) ? ";" : ".";
|
|
||||||
}
|
|
||||||
LOG(INFO) << "CS hist: " << short_histogram;
|
|
||||||
LOG(INFO) << "";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void CommandStream::queueCommand(std::function<void()> command) {
|
void CommandStream::queueCommand(std::function<void()> command) {
|
||||||
new(allocateCommand(CustomCommand::align(sizeof(CustomCommand)))) CustomCommand(std::move(command));
|
new(allocateCommand(CustomCommand::align(sizeof(CustomCommand)))) CustomCommand(std::move(command));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public:
|
|||||||
GLenum getIndicesType() const noexcept {
|
GLenum getIndicesType() const noexcept {
|
||||||
return indicesType;
|
return indicesType;
|
||||||
}
|
}
|
||||||
} gl;
|
};
|
||||||
|
|
||||||
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
|
static bool queryOpenGLVersion(GLint* major, GLint* minor) noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ VK_DEFINE_HANDLE(VmaPool)
|
|||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
struct VulkanCommandBuffer;
|
struct VulkanCommandBuffer;
|
||||||
|
struct VulkanRenderPass;
|
||||||
struct VulkanRenderTarget;
|
struct VulkanRenderTarget;
|
||||||
struct VulkanSwapChain;
|
struct VulkanSwapChain;
|
||||||
struct VulkanTexture;
|
struct VulkanTexture;
|
||||||
@@ -58,11 +59,11 @@ struct VulkanAttachment {
|
|||||||
VkImageSubresourceRange getSubresourceRange() const;
|
VkImageSubresourceRange getSubresourceRange() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanRenderPass {
|
struct VulkanRenderPassContext {
|
||||||
// Between the begin and end command render pass we cache the command buffer
|
// Between the begin and end command render pass we cache the command buffer
|
||||||
VulkanCommandBuffer* commandBuffer;
|
VulkanCommandBuffer* commandBuffer;
|
||||||
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget;
|
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget;
|
||||||
VkRenderPass renderPass;
|
fvkmemory::resource_ptr<VulkanRenderPass> renderPass;
|
||||||
RenderPassParams params;
|
RenderPassParams params;
|
||||||
int currentSubpass;
|
int currentSubpass;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1975,17 +1975,19 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
|||||||
rpkey.initialDepthLayout = currentDepthLayout;
|
rpkey.initialDepthLayout = currentDepthLayout;
|
||||||
rpkey.subpassMask = uint8_t(params.subpassMask);
|
rpkey.subpassMask = uint8_t(params.subpassMask);
|
||||||
|
|
||||||
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
|
fvkmemory::resource_ptr<VulkanRenderPass> renderPass =
|
||||||
|
mFramebufferCache.getRenderPass(rpkey, &mResourceManager);
|
||||||
mPipelineCache.bindRenderPass(renderPass, 0);
|
mPipelineCache.bindRenderPass(renderPass, 0);
|
||||||
|
|
||||||
// Create the VkFramebuffer or fetch it from cache.
|
// Create the VkFramebuffer or fetch it from cache.
|
||||||
VulkanFboCache::FboKey fbkey = rt->getFboKey();
|
VulkanFboCache::FboKey fbkey = rt->getFboKey();
|
||||||
fbkey.renderPass = renderPass;
|
fbkey.renderPass = renderPass->getVkRenderPass();
|
||||||
fbkey.layers = 1;
|
fbkey.layers = 1;
|
||||||
|
|
||||||
rt->emitBarriersBeginRenderPass(*commandBuffer);
|
rt->emitBarriersBeginRenderPass(*commandBuffer);
|
||||||
|
|
||||||
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
|
fvkmemory::resource_ptr<VulkanFramebuffer> vkfb =
|
||||||
|
mFramebufferCache.getFramebuffer(fbkey, &mResourceManager);
|
||||||
|
|
||||||
// Assign a label to the framebuffer for debugging purposes.
|
// Assign a label to the framebuffer for debugging purposes.
|
||||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
|
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
|
||||||
@@ -1998,12 +2000,14 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
|||||||
|
|
||||||
// The current command buffer now has references to the render target and its attachments.
|
// The current command buffer now has references to the render target and its attachments.
|
||||||
commandBuffer->acquire(rt);
|
commandBuffer->acquire(rt);
|
||||||
|
commandBuffer->acquire(renderPass);
|
||||||
|
commandBuffer->acquire(vkfb);
|
||||||
|
|
||||||
// Populate the structures required for vkCmdBeginRenderPass.
|
// Populate the structures required for vkCmdBeginRenderPass.
|
||||||
VkRenderPassBeginInfo renderPassInfo {
|
VkRenderPassBeginInfo renderPassInfo {
|
||||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||||
.renderPass = renderPass,
|
.renderPass = renderPass->getVkRenderPass(),
|
||||||
.framebuffer = vkfb,
|
.framebuffer = vkfb->getVkFramebuffer(),
|
||||||
|
|
||||||
// The renderArea field constrains the LoadOp, but scissoring does not.
|
// The renderArea field constrains the LoadOp, but scissoring does not.
|
||||||
// Therefore, we do not set the scissor rect here, we only need it in draw().
|
// Therefore, we do not set the scissor rect here, we only need it in draw().
|
||||||
@@ -2058,7 +2062,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
|||||||
mCurrentRenderPass = {
|
mCurrentRenderPass = {
|
||||||
.commandBuffer = commandBuffer,
|
.commandBuffer = commandBuffer,
|
||||||
.renderTarget = rt,
|
.renderTarget = rt,
|
||||||
.renderPass = renderPassInfo.renderPass,
|
.renderPass = renderPass,
|
||||||
.params = params,
|
.params = params,
|
||||||
.currentSubpass = 0,
|
.currentSubpass = 0,
|
||||||
};
|
};
|
||||||
@@ -2078,8 +2082,7 @@ void VulkanDriver::endRenderPass(int) {
|
|||||||
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
|
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
|
||||||
|
|
||||||
mCurrentRenderPass.renderTarget = {};
|
mCurrentRenderPass.renderTarget = {};
|
||||||
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
mCurrentRenderPass.renderPass = {};
|
||||||
|
|
||||||
mCurrentRenderPass.commandBuffer = nullptr;
|
mCurrentRenderPass.commandBuffer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2244,7 +2247,7 @@ void VulkanDriver::resolve(
|
|||||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||||
FVK_SYSTRACE_SCOPE();
|
FVK_SYSTRACE_SCOPE();
|
||||||
|
|
||||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass)
|
||||||
<< "resolve() cannot be invoked inside a render pass.";
|
<< "resolve() cannot be invoked inside a render pass.";
|
||||||
|
|
||||||
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
|
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
|
||||||
@@ -2287,7 +2290,7 @@ void VulkanDriver::blit(
|
|||||||
math::uint2 size) {
|
math::uint2 size) {
|
||||||
FVK_SYSTRACE_SCOPE();
|
FVK_SYSTRACE_SCOPE();
|
||||||
|
|
||||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass)
|
||||||
<< "blit() cannot be invoked inside a render pass.";
|
<< "blit() cannot be invoked inside a render pass.";
|
||||||
|
|
||||||
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
|
auto srcTexture = resource_ptr<VulkanTexture>::cast(&mResourceManager, src);
|
||||||
@@ -2329,7 +2332,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
|||||||
|
|
||||||
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
|
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
|
||||||
|
|
||||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
FILAMENT_CHECK_PRECONDITION(!mCurrentRenderPass.renderPass)
|
||||||
<< "blitDEPRECATED() cannot be invoked inside a render pass.";
|
<< "blitDEPRECATED() cannot be invoked inside a render pass.";
|
||||||
|
|
||||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ private:
|
|||||||
|
|
||||||
resource_ptr<VulkanSwapChain> mCurrentSwapChain;
|
resource_ptr<VulkanSwapChain> mCurrentSwapChain;
|
||||||
resource_ptr<VulkanRenderTarget> mDefaultRenderTarget;
|
resource_ptr<VulkanRenderTarget> mDefaultRenderTarget;
|
||||||
VulkanRenderPass mCurrentRenderPass = {};
|
VulkanRenderPassContext mCurrentRenderPass = {};
|
||||||
VmaAllocator mAllocator = VK_NULL_HANDLE;
|
VmaAllocator mAllocator = VK_NULL_HANDLE;
|
||||||
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
|
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "VulkanFboCache.h"
|
#include "VulkanFboCache.h"
|
||||||
|
|
||||||
#include "VulkanConstants.h"
|
#include "VulkanConstants.h"
|
||||||
|
#include "VulkanHandles.h"
|
||||||
#include "vulkan/utils/Image.h"
|
#include "vulkan/utils/Image.h"
|
||||||
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
@@ -69,9 +70,10 @@ VulkanFboCache::~VulkanFboCache() {
|
|||||||
<< "Please explicitly call terminate() while the VkDevice is still alive.";
|
<< "Please explicitly call terminate() while the VkDevice is still alive.";
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
|
fvkmemory::resource_ptr<VulkanFramebuffer> VulkanFboCache::getFramebuffer(
|
||||||
|
FboKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
|
||||||
FboMap::iterator iter = mFramebufferCache.find(config);
|
FboMap::iterator iter = mFramebufferCache.find(config);
|
||||||
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
|
if (UTILS_LIKELY(iter != mFramebufferCache.end())) {
|
||||||
iter.value().timestamp = mCurrentTime;
|
iter.value().timestamp = mCurrentTime;
|
||||||
return iter->second.handle;
|
return iter->second.handle;
|
||||||
}
|
}
|
||||||
@@ -117,13 +119,16 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
|
|||||||
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
|
VkResult error = vkCreateFramebuffer(mDevice, &info, VKALLOC, &framebuffer);
|
||||||
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
|
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create framebuffer."
|
||||||
<< " error=" << static_cast<int32_t>(error);
|
<< " error=" << static_cast<int32_t>(error);
|
||||||
mFramebufferCache[config] = {framebuffer, mCurrentTime};
|
fvkmemory::resource_ptr<VulkanFramebuffer> fbh =
|
||||||
return framebuffer;
|
fvkmemory::resource_ptr<VulkanFramebuffer>::construct(resManager, mDevice, framebuffer);
|
||||||
|
mFramebufferCache[config] = {fbh, mCurrentTime};
|
||||||
|
return fbh;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept {
|
fvkmemory::resource_ptr<VulkanRenderPass> VulkanFboCache::getRenderPass(
|
||||||
|
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept {
|
||||||
auto iter = mRenderPassCache.find(config);
|
auto iter = mRenderPassCache.find(config);
|
||||||
if (UTILS_LIKELY(iter != mRenderPassCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
|
if (UTILS_LIKELY(iter != mRenderPassCache.end())) {
|
||||||
iter.value().timestamp = mCurrentTime;
|
iter.value().timestamp = mCurrentTime;
|
||||||
return iter->second.handle;
|
return iter->second.handle;
|
||||||
}
|
}
|
||||||
@@ -326,7 +331,9 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
|
|||||||
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
|
VkResult error = vkCreateRenderPass(mDevice, &renderPassInfo, VKALLOC, &renderPass);
|
||||||
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
|
FILAMENT_CHECK_POSTCONDITION(error == VK_SUCCESS) << "Unable to create render pass."
|
||||||
<< " error=" << error;
|
<< " error=" << error;
|
||||||
mRenderPassCache[config] = {renderPass, mCurrentTime};
|
fvkmemory::resource_ptr<VulkanRenderPass> rph =
|
||||||
|
fvkmemory::resource_ptr<VulkanRenderPass>::construct(resManager, mDevice, renderPass);
|
||||||
|
mRenderPassCache[config] = {rph, mCurrentTime};
|
||||||
|
|
||||||
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
|
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
|
||||||
FVK_LOGD << "Created render pass " << renderPass << " with ";
|
FVK_LOGD << "Created render pass " << renderPass << " with ";
|
||||||
@@ -343,13 +350,12 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
|
|||||||
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount;
|
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return renderPass;
|
return rph;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanFboCache::resetFramebuffers() noexcept {
|
void VulkanFboCache::resetFramebuffers() noexcept {
|
||||||
for (const auto& pair: mFramebufferCache) {
|
for (const auto& pair: mFramebufferCache) {
|
||||||
mRenderPassRefCount[pair.first.renderPass]--;
|
mRenderPassRefCount[pair.first.renderPass]--;
|
||||||
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
|
|
||||||
}
|
}
|
||||||
mFramebufferCache.clear();
|
mFramebufferCache.clear();
|
||||||
}
|
}
|
||||||
@@ -357,9 +363,6 @@ void VulkanFboCache::resetFramebuffers() noexcept {
|
|||||||
void VulkanFboCache::terminate() noexcept {
|
void VulkanFboCache::terminate() noexcept {
|
||||||
resetFramebuffers();
|
resetFramebuffers();
|
||||||
|
|
||||||
for (const auto& pair: mRenderPassCache) {
|
|
||||||
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
|
|
||||||
}
|
|
||||||
mRenderPassRefCount.clear();
|
mRenderPassRefCount.clear();
|
||||||
mRenderPassCache.clear();
|
mRenderPassCache.clear();
|
||||||
}
|
}
|
||||||
@@ -380,8 +383,6 @@ void VulkanFboCache::gc() noexcept {
|
|||||||
const FboVal fbo = iter->second;
|
const FboVal fbo = iter->second;
|
||||||
if (fbo.timestamp < evictTime && fbo.handle) {
|
if (fbo.timestamp < evictTime && fbo.handle) {
|
||||||
mRenderPassRefCount[iter->first.renderPass]--;
|
mRenderPassRefCount[iter->first.renderPass]--;
|
||||||
vkDestroyFramebuffer(mDevice, fbo.handle, VKALLOC);
|
|
||||||
iter.value().handle = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
// erase(iterator) returns the iterator to the next element.
|
// erase(iterator) returns the iterator to the next element.
|
||||||
iter = mFramebufferCache.erase(iter);
|
iter = mFramebufferCache.erase(iter);
|
||||||
@@ -391,11 +392,8 @@ void VulkanFboCache::gc() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (RenderPassMap::iterator iter = mRenderPassCache.begin(); iter != mRenderPassCache.end(); ) {
|
for (RenderPassMap::iterator iter = mRenderPassCache.begin(); iter != mRenderPassCache.end(); ) {
|
||||||
const VkRenderPass handle = iter->second.handle;
|
const VkRenderPass handle = iter->second.handle->getVkRenderPass();
|
||||||
if (iter->second.timestamp < evictTime && handle && mRenderPassRefCount[handle] == 0) {
|
if (iter->second.timestamp < evictTime && handle && mRenderPassRefCount[handle] == 0) {
|
||||||
vkDestroyRenderPass(mDevice, handle, VKALLOC);
|
|
||||||
iter.value().handle = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
// erase(iterator) returns the iterator to the next element.
|
// erase(iterator) returns the iterator to the next element.
|
||||||
iter = mRenderPassCache.erase(iter);
|
iter = mRenderPassCache.erase(iter);
|
||||||
mRenderPassRefCount.erase(handle);
|
mRenderPassRefCount.erase(handle);
|
||||||
|
|||||||
@@ -18,6 +18,9 @@
|
|||||||
#define TNT_FILAMENT_BACKEND_VULKANFBOCACHE_H
|
#define TNT_FILAMENT_BACKEND_VULKANFBOCACHE_H
|
||||||
|
|
||||||
#include "VulkanContext.h"
|
#include "VulkanContext.h"
|
||||||
|
#include "vulkan/memory/Resource.h"
|
||||||
|
#include "vulkan/memory/ResourceManager.h"
|
||||||
|
#include "vulkan/memory/ResourcePointer.h"
|
||||||
|
|
||||||
#include <utils/Hash.h>
|
#include <utils/Hash.h>
|
||||||
|
|
||||||
@@ -27,6 +30,9 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
struct VulkanFramebuffer;
|
||||||
|
struct VulkanRenderPass;
|
||||||
|
|
||||||
// Simple manager for VkFramebuffer and VkRenderPass objects.
|
// Simple manager for VkFramebuffer and VkRenderPass objects.
|
||||||
//
|
//
|
||||||
// Note that a VkFramebuffer is just a binding between a render pass and a set of image views. So,
|
// Note that a VkFramebuffer is just a binding between a render pass and a set of image views. So,
|
||||||
@@ -57,7 +63,7 @@ public:
|
|||||||
uint8_t padding[2];
|
uint8_t padding[2];
|
||||||
};
|
};
|
||||||
struct RenderPassVal {
|
struct RenderPassVal {
|
||||||
VkRenderPass handle;
|
fvkmemory::resource_ptr<VulkanRenderPass> handle;
|
||||||
uint32_t timestamp;
|
uint32_t timestamp;
|
||||||
};
|
};
|
||||||
static_assert(0 == MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT % 8);
|
static_assert(0 == MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT % 8);
|
||||||
@@ -83,7 +89,7 @@ public:
|
|||||||
VkImageView depth; // 8 bytes
|
VkImageView depth; // 8 bytes
|
||||||
};
|
};
|
||||||
struct FboVal {
|
struct FboVal {
|
||||||
VkFramebuffer handle;
|
fvkmemory::resource_ptr<VulkanFramebuffer> handle;
|
||||||
uint32_t timestamp;
|
uint32_t timestamp;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(VkRenderPass) == 8, "VkRenderPass has unexpected size.");
|
static_assert(sizeof(VkRenderPass) == 8, "VkRenderPass has unexpected size.");
|
||||||
@@ -98,10 +104,12 @@ public:
|
|||||||
~VulkanFboCache();
|
~VulkanFboCache();
|
||||||
|
|
||||||
// Retrieves or creates a VkFramebuffer handle.
|
// Retrieves or creates a VkFramebuffer handle.
|
||||||
VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
|
fvkmemory::resource_ptr<VulkanFramebuffer> getFramebuffer(
|
||||||
|
FboKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
|
||||||
|
|
||||||
// Retrieves or creates a VkRenderPass handle.
|
// Retrieves or creates a VkRenderPass handle.
|
||||||
VkRenderPass getRenderPass(RenderPassKey const& config) noexcept;
|
fvkmemory::resource_ptr<VulkanRenderPass> getRenderPass(
|
||||||
|
RenderPassKey const& config, fvkmemory::ResourceManager* resManager) noexcept;
|
||||||
|
|
||||||
// Evicts old unused Vulkan objects. Call this once per frame.
|
// Evicts old unused Vulkan objects. Call this once per frame.
|
||||||
void gc() noexcept;
|
void gc() noexcept;
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ void VulkanRenderTarget::transformViewportToPlatform(VkViewport* bounds) const {
|
|||||||
flipVertically(bounds, getExtent().height);
|
flipVertically(bounds, getExtent().height);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) const {
|
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPassContext& pass) const {
|
||||||
if (!mOffscreen) {
|
if (!mOffscreen) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -715,4 +715,18 @@ VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
|
|||||||
vertexBuffer(vb),
|
vertexBuffer(vb),
|
||||||
indexBuffer(ib) {}
|
indexBuffer(ib) {}
|
||||||
|
|
||||||
|
VulkanFramebuffer::VulkanFramebuffer(VkDevice device, VkFramebuffer framebuffer)
|
||||||
|
: mDevice(device), mFramebuffer(framebuffer) {}
|
||||||
|
|
||||||
|
VulkanFramebuffer::~VulkanFramebuffer() {
|
||||||
|
vkDestroyFramebuffer(mDevice, mFramebuffer, VKALLOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
VulkanRenderPass::VulkanRenderPass(VkDevice device, VkRenderPass renderPass)
|
||||||
|
: mDevice(device), mRenderPass(renderPass) {}
|
||||||
|
|
||||||
|
VulkanRenderPass::~VulkanRenderPass() {
|
||||||
|
vkDestroyRenderPass(mDevice, mRenderPass, VKALLOC);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
|
|||||||
return mInfo->fbkey.samples;
|
return mInfo->fbkey.samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t getColorTargetCount(VulkanRenderPass const& pass) const;
|
uint8_t getColorTargetCount(VulkanRenderPassContext const& pass) const;
|
||||||
|
|
||||||
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
|
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
|
||||||
|
|
||||||
@@ -585,6 +585,32 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
|
|||||||
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
|
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct VulkanFramebuffer : public fvkmemory::Resource {
|
||||||
|
VulkanFramebuffer(VkDevice device, VkFramebuffer framebuffer);
|
||||||
|
~VulkanFramebuffer();
|
||||||
|
|
||||||
|
inline VkFramebuffer getVkFramebuffer() const noexcept {
|
||||||
|
return mFramebuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
VkDevice mDevice;
|
||||||
|
VkFramebuffer mFramebuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanRenderPass : public fvkmemory::Resource {
|
||||||
|
VulkanRenderPass(VkDevice device, VkRenderPass renderPass);
|
||||||
|
~VulkanRenderPass();
|
||||||
|
|
||||||
|
inline VkRenderPass getVkRenderPass() const noexcept {
|
||||||
|
return mRenderPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
VkDevice mDevice;
|
||||||
|
VkRenderPass mRenderPass;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|
||||||
#endif // TNT_FILAMENT_BACKEND_VULKANHANDLES_H
|
#endif // TNT_FILAMENT_BACKEND_VULKANHANDLES_H
|
||||||
|
|||||||
@@ -459,8 +459,10 @@ void VulkanPipelineCache::bindStencilState(StencilState const& stencilState) noe
|
|||||||
mPipelineRequirements.stencilState = stencilState;
|
mPipelineRequirements.stencilState = stencilState;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
|
void VulkanPipelineCache::bindRenderPass(
|
||||||
mPipelineRequirements.renderPass = renderPass;
|
fvkmemory::resource_ptr<VulkanRenderPass> renderPass,
|
||||||
|
int subpassIndex) noexcept {
|
||||||
|
mPipelineRequirements.renderPass = renderPass->getVkRenderPass();
|
||||||
mPipelineRequirements.subpassIndex = subpassIndex;
|
mPipelineRequirements.subpassIndex = subpassIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ public:
|
|||||||
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
|
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
|
||||||
void bindRasterState(RasterState const& rasterState) noexcept;
|
void bindRasterState(RasterState const& rasterState) noexcept;
|
||||||
void bindStencilState(StencilState const& stencilState) noexcept;
|
void bindStencilState(StencilState const& stencilState) noexcept;
|
||||||
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
|
void bindRenderPass(fvkmemory::resource_ptr<VulkanRenderPass> renderPass,
|
||||||
|
int subpassIndex) noexcept;
|
||||||
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
|
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
|
||||||
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
|
void bindVertexArray(VkVertexInputAttributeDescription const* attribDesc,
|
||||||
VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
|
VkVertexInputBindingDescription const* bufferDesc, uint8_t count);
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ template ResourceType getTypeEnum<VulkanSync>() noexcept;
|
|||||||
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
|
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
|
||||||
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
|
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
|
||||||
template ResourceType getTypeEnum<VulkanStream>() noexcept;
|
template ResourceType getTypeEnum<VulkanStream>() noexcept;
|
||||||
|
template ResourceType getTypeEnum<VulkanFramebuffer>() noexcept;
|
||||||
|
template ResourceType getTypeEnum<VulkanRenderPass>() noexcept;
|
||||||
|
|
||||||
template<typename D>
|
template<typename D>
|
||||||
ResourceType getTypeEnum() noexcept {
|
ResourceType getTypeEnum() noexcept {
|
||||||
@@ -108,6 +110,12 @@ ResourceType getTypeEnum() noexcept {
|
|||||||
if constexpr (std::is_same_v<D, VulkanStream>) {
|
if constexpr (std::is_same_v<D, VulkanStream>) {
|
||||||
return ResourceType::STREAM;
|
return ResourceType::STREAM;
|
||||||
}
|
}
|
||||||
|
if constexpr (std::is_same_v<D, VulkanFramebuffer>) {
|
||||||
|
return ResourceType::FRAMEBUFFER;
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<D, VulkanRenderPass>) {
|
||||||
|
return ResourceType::RENDER_PASS;
|
||||||
|
}
|
||||||
return ResourceType::UNDEFINED_TYPE;
|
return ResourceType::UNDEFINED_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +163,10 @@ std::string_view getTypeStr(ResourceType type) {
|
|||||||
return "Semaphore";
|
return "Semaphore";
|
||||||
case ResourceType::STREAM:
|
case ResourceType::STREAM:
|
||||||
return "VulkanStream";
|
return "VulkanStream";
|
||||||
|
case ResourceType::FRAMEBUFFER:
|
||||||
|
return "Framebuffer";
|
||||||
|
case ResourceType::RENDER_PASS:
|
||||||
|
return "RenderPass";
|
||||||
case ResourceType::UNDEFINED_TYPE:
|
case ResourceType::UNDEFINED_TYPE:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,9 @@ enum class ResourceType : uint8_t {
|
|||||||
MEMORY_MAPPED_BUFFER = 18,
|
MEMORY_MAPPED_BUFFER = 18,
|
||||||
SEMAPHORE = 19,
|
SEMAPHORE = 19,
|
||||||
STREAM = 20,
|
STREAM = 20,
|
||||||
UNDEFINED_TYPE = 21, // Must be the last enum because we use it for iterating over the enums.
|
FRAMEBUFFER = 21,
|
||||||
|
RENDER_PASS = 22,
|
||||||
|
UNDEFINED_TYPE = 23, // Must be the last enum because we use it for iterating over the enums.
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename D>
|
template<typename D>
|
||||||
|
|||||||
@@ -126,6 +126,12 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
|
|||||||
case ResourceType::STREAM:
|
case ResourceType::STREAM:
|
||||||
destruct<VulkanStream>(Handle<VulkanStream>(id));
|
destruct<VulkanStream>(Handle<VulkanStream>(id));
|
||||||
break;
|
break;
|
||||||
|
case ResourceType::FRAMEBUFFER:
|
||||||
|
destruct<VulkanFramebuffer>(Handle<VulkanFramebuffer>(id));
|
||||||
|
break;
|
||||||
|
case ResourceType::RENDER_PASS:
|
||||||
|
destruct<VulkanRenderPass>(Handle<VulkanRenderPass>(id));
|
||||||
|
break;
|
||||||
case ResourceType::UNDEFINED_TYPE:
|
case ResourceType::UNDEFINED_TYPE:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "Shader.h"
|
#include "Shader.h"
|
||||||
#include "SharedShaders.h"
|
#include "SharedShaders.h"
|
||||||
|
#include "Skip.h"
|
||||||
#include "TrianglePrimitive.h"
|
#include "TrianglePrimitive.h"
|
||||||
|
|
||||||
#include <backend/PixelBufferDescriptor.h>
|
#include <backend/PixelBufferDescriptor.h>
|
||||||
@@ -31,6 +32,9 @@ using namespace filament::backend;
|
|||||||
using namespace filament::math;
|
using namespace filament::math;
|
||||||
|
|
||||||
TEST_F(BackendTest, AutoresolveDifferingSampleCounts) {
|
TEST_F(BackendTest, AutoresolveDifferingSampleCounts) {
|
||||||
|
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::OPENGL), "see b/486954356");
|
||||||
|
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::VULKAN), "see b/486954356");
|
||||||
|
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
constexpr int kRenderTargetSize = 512;
|
constexpr int kRenderTargetSize = 512;
|
||||||
|
|
||||||
|
|||||||
@@ -271,8 +271,8 @@ std::unique_ptr<MaterialDefinition> MaterialDefinition::create(FEngine& engine,
|
|||||||
|
|
||||||
void MaterialDefinition::terminate(FEngine& engine) {
|
void MaterialDefinition::terminate(FEngine& engine) {
|
||||||
DriverApi& driver = engine.getDriverApi();
|
DriverApi& driver = engine.getDriverApi();
|
||||||
perViewDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
perViewDescriptorSetLayoutPcf.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||||
perViewDescriptorSetLayoutVsm.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
perViewDescriptorSetLayoutS2d.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||||
descriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
descriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,23 +607,23 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
|||||||
refractionMode == RefractionMode::SCREEN_SPACE;
|
refractionMode == RefractionMode::SCREEN_SPACE;
|
||||||
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||||
|
|
||||||
this->perViewDescriptorSetLayoutDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
this->perViewDescriptorSetLayoutPcfDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||||
materialDomain, isLit, isSSR, hasFog, false);
|
materialDomain, isLit, isSSR, hasFog, false);
|
||||||
|
|
||||||
this->perViewDescriptorSetLayoutVsmDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
this->perViewDescriptorSetLayoutS2dDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||||
materialDomain, isLit, isSSR, hasFog, true);
|
materialDomain, isLit, isSSR, hasFog, true);
|
||||||
|
|
||||||
// set the labels
|
// set the labels
|
||||||
this->descriptorSetLayoutDescription.label = CString{ name }.append("_perMat");
|
this->descriptorSetLayoutDescription.label = CString{ name }.append("_perMat");
|
||||||
this->perViewDescriptorSetLayoutDescription.label = CString{ name }.append("_perView");
|
this->perViewDescriptorSetLayoutPcfDescription.label = CString{ name }.append("_perView");
|
||||||
this->perViewDescriptorSetLayoutVsmDescription.label = CString{ name }.append("_perViewVsm");
|
this->perViewDescriptorSetLayoutS2dDescription.label = CString{ name }.append("_perViewVsm");
|
||||||
|
|
||||||
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
|
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
|
||||||
for (auto&& [bindingPoint, dsl] : {
|
for (auto&& [bindingPoint, dsl] : {
|
||||||
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
|
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||||
descriptor_sets::getPerRenderableLayout() },
|
descriptor_sets::getPerRenderableLayout() },
|
||||||
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
|
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
|
||||||
this->perViewDescriptorSetLayoutDescription }}) {
|
this->perViewDescriptorSetLayoutPcfDescription }}) {
|
||||||
Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint];
|
Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint];
|
||||||
descriptors.reserve(dsl.descriptors.size());
|
descriptors.reserve(dsl.descriptors.size());
|
||||||
for (auto const& entry: dsl.descriptors) {
|
for (auto const& entry: dsl.descriptors) {
|
||||||
@@ -636,17 +636,17 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
|||||||
descriptorSetLayoutFactory, driver,
|
descriptorSetLayoutFactory, driver,
|
||||||
this->descriptorSetLayoutDescription };
|
this->descriptorSetLayoutDescription };
|
||||||
|
|
||||||
this->perViewDescriptorSetLayout = {
|
this->perViewDescriptorSetLayoutPcf = {
|
||||||
descriptorSetLayoutFactory, driver,
|
descriptorSetLayoutFactory, driver,
|
||||||
this->perViewDescriptorSetLayoutDescription };
|
this->perViewDescriptorSetLayoutPcfDescription };
|
||||||
|
|
||||||
this->perViewDescriptorSetLayoutVsm = {
|
this->perViewDescriptorSetLayoutS2d = {
|
||||||
descriptorSetLayoutFactory, driver,
|
descriptorSetLayoutFactory, driver,
|
||||||
this->perViewDescriptorSetLayoutVsmDescription };
|
this->perViewDescriptorSetLayoutS2dDescription };
|
||||||
}
|
}
|
||||||
|
|
||||||
backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetLayoutDescription(
|
backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetLayoutDescription(
|
||||||
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
|
Variant const variant, bool const useS2dDescriptorSetLayout) const noexcept {
|
||||||
if (materialDomain == MaterialDomain::SURFACE) {
|
if (materialDomain == MaterialDomain::SURFACE) {
|
||||||
if (Variant::isValidDepthVariant(variant)) {
|
if (Variant::isValidDepthVariant(variant)) {
|
||||||
// Use the layout description used to create the per view depth variant layout.
|
// Use the layout description used to create the per view depth variant layout.
|
||||||
@@ -657,10 +657,10 @@ backend::DescriptorSetLayout const& MaterialDefinition::getPerViewDescriptorSetL
|
|||||||
return descriptor_sets::getSsrVariantLayout();
|
return descriptor_sets::getSsrVariantLayout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (useVsmDescriptorSetLayout) {
|
if (useS2dDescriptorSetLayout) {
|
||||||
return perViewDescriptorSetLayoutVsmDescription;
|
return perViewDescriptorSetLayoutS2dDescription;
|
||||||
}
|
}
|
||||||
return perViewDescriptorSetLayoutDescription;
|
return perViewDescriptorSetLayoutPcfDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwProgram> MaterialDefinition::compileProgram(
|
Handle<HwProgram> MaterialDefinition::compileProgram(
|
||||||
@@ -689,7 +689,7 @@ Handle<HwProgram> MaterialDefinition::compileProgram(
|
|||||||
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_VIEW,
|
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_VIEW,
|
||||||
getPerViewDescriptorSetLayoutDescription(
|
getPerViewDescriptorSetLayoutDescription(
|
||||||
specialization.variant,
|
specialization.variant,
|
||||||
Variant::isVSMVariant(specialization.variant)));
|
Variant::isShadowSampler2DVariant(specialization.variant)));
|
||||||
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_RENDERABLE,
|
pb.descriptorLayout(+DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||||
descriptor_sets::getPerRenderableLayout());
|
descriptor_sets::getPerRenderableLayout());
|
||||||
pb.descriptorLayout(
|
pb.descriptorLayout(
|
||||||
|
|||||||
@@ -94,17 +94,17 @@ struct MaterialDefinition {
|
|||||||
backend::ShaderModel const sm, bool isStereoSupported) const noexcept;
|
backend::ShaderModel const sm, bool isStereoSupported) const noexcept;
|
||||||
|
|
||||||
backend::DescriptorSetLayout const& getPerViewDescriptorSetLayoutDescription(
|
backend::DescriptorSetLayout const& getPerViewDescriptorSetLayoutDescription(
|
||||||
Variant const variant, bool useVsmDescriptorSetLayout) const noexcept;
|
Variant const variant, bool useS2dDescriptorSetLayout) const noexcept;
|
||||||
|
|
||||||
// Keep track of the definitions of the descriptor set layouts, as these
|
// Keep track of the definitions of the descriptor set layouts, as these
|
||||||
// may be used by some backends in parallel compilation of programs.
|
// may be used by some backends in parallel compilation of programs.
|
||||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutDescription;
|
backend::DescriptorSetLayout perViewDescriptorSetLayoutPcfDescription;
|
||||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutVsmDescription;
|
backend::DescriptorSetLayout perViewDescriptorSetLayoutS2dDescription;
|
||||||
backend::DescriptorSetLayout descriptorSetLayoutDescription;
|
backend::DescriptorSetLayout descriptorSetLayoutDescription;
|
||||||
|
|
||||||
// try to order by frequency of use
|
// try to order by frequency of use
|
||||||
filament::DescriptorSetLayout perViewDescriptorSetLayout;
|
filament::DescriptorSetLayout perViewDescriptorSetLayoutPcf;
|
||||||
filament::DescriptorSetLayout perViewDescriptorSetLayoutVsm;
|
filament::DescriptorSetLayout perViewDescriptorSetLayoutS2d;
|
||||||
filament::DescriptorSetLayout descriptorSetLayout;
|
filament::DescriptorSetLayout descriptorSetLayout;
|
||||||
backend::Program::DescriptorSetInfo programDescriptorBindings;
|
backend::Program::DescriptorSetInfo programDescriptorBindings;
|
||||||
|
|
||||||
|
|||||||
@@ -802,7 +802,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
|||||||
|
|
||||||
// use our special SSR variant, it can only be applied to object that have
|
// use our special SSR variant, it can only be applied to object that have
|
||||||
// the SCREEN_SPACE ReflectionMode.
|
// the SCREEN_SPACE ReflectionMode.
|
||||||
passBuilder.variant(Variant{ Variant::SPECIAL_SSR });
|
passBuilder.variant(Variant{ Variant::SPECIAL_SSR_VARIANT });
|
||||||
|
|
||||||
// generate all our drawing commands, except blended objects.
|
// generate all our drawing commands, except blended objects.
|
||||||
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
|
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
|
||||||
|
|||||||
@@ -568,7 +568,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
|||||||
if constexpr (isDepthPass) {
|
if constexpr (isDepthPass) {
|
||||||
cmd.info.materialVariant = variant;
|
cmd.info.materialVariant = variant;
|
||||||
cmd.info.rasterState = {};
|
cmd.info.rasterState = {};
|
||||||
cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isVSMVariant(variant);
|
cmd.info.rasterState.colorWrite = Variant::isPickingVariant(variant) || Variant::isDepthMomentsVariant(variant);
|
||||||
cmd.info.rasterState.depthWrite = true;
|
cmd.info.rasterState.depthWrite = true;
|
||||||
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
|
cmd.info.rasterState.depthFunc = RasterState::DepthFunc::GE;
|
||||||
cmd.info.rasterState.alphaToCoverage = false;
|
cmd.info.rasterState.alphaToCoverage = false;
|
||||||
@@ -616,7 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
|||||||
bool const hasSkinningOrMorphing = hasSkinning || hasMorphing;
|
bool const hasSkinningOrMorphing = hasSkinning || hasMorphing;
|
||||||
|
|
||||||
// if we are already an SSR variant, the SRE bit is already set
|
// if we are already an SSR variant, the SRE bit is already set
|
||||||
static_assert(Variant::SPECIAL_SSR & Variant::SRE);
|
static_assert(Variant::SPECIAL_SSR_VARIANT & Variant::SRE);
|
||||||
Variant renderableVariant{ variant };
|
Variant renderableVariant{ variant };
|
||||||
|
|
||||||
// we can't have SSR and shadowing together by construction
|
// we can't have SSR and shadowing together by construction
|
||||||
|
|||||||
+58
-75
@@ -213,12 +213,14 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
|||||||
* Final shadow map transform
|
* Final shadow map transform
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Final shadow transform (focused warped light-space)
|
// Final shadow transform (focused warped light-space), world space to clip space
|
||||||
const mat4f S = F * (W * LMpMv);
|
const mat4f S = F * (W * LMpMv);
|
||||||
|
|
||||||
// Computes St the transform to use in the shader to access the shadow map texture
|
// Computes St the transform to use in the shader to access the shadow map texture
|
||||||
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
|
// i.e. it transforms a world-space vertex to a texture coordinate in the shadowmap
|
||||||
const auto [Mt, Mn] = getTextureCoordsMapping(shadowMapInfo, getViewport());
|
const auto [Mt, Mn] = getTextureCoordsMapping(shadowMapInfo, getViewport());
|
||||||
|
|
||||||
|
// world space to texture atlas space
|
||||||
const mat4f St = highPrecisionMultiply(Mt, S);
|
const mat4f St = highPrecisionMultiply(Mt, S);
|
||||||
|
|
||||||
ShaderParameters shaderParameters;
|
ShaderParameters shaderParameters;
|
||||||
@@ -226,14 +228,12 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
|
|||||||
|
|
||||||
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
|
// note: in texelSizeWorldSpace() below, we can use Mb * Mt * F * W because
|
||||||
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
|
// L * Mp * Mv is a rigid transform for directional lights, and doesn't matter.
|
||||||
// if Wp[3][1] is 0, then LISPSM was cancelled.
|
// if Wp[3][1] is 0, then LiSPSM was canceled.
|
||||||
if (useLispsm && Wp[3][1] != 0.0f) {
|
if (useLispsm && Wp[3][1] != 0.0f) {
|
||||||
shaderParameters.texelSizeAtOneMeterWs =
|
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S, shadowMapInfo.shadowDimension);
|
||||||
texelSizeWorldSpace(Wp, mat4f(Mt * F), shadowMapInfo.shadowDimension);
|
|
||||||
} else {
|
} else {
|
||||||
// We know we're using an ortho projection
|
// We know we're using an ortho projection
|
||||||
shaderParameters.texelSizeAtOneMeterWs =
|
shaderParameters.texelSizeAtOneMeterWs = texelSizeWorldSpace(S.upperLeft(), shadowMapInfo.shadowDimension);
|
||||||
texelSizeWorldSpace(St.upperLeft(), shadowMapInfo.shadowDimension);
|
|
||||||
}
|
}
|
||||||
if (!shadowMapInfo.vsm) {
|
if (!shadowMapInfo.vsm) {
|
||||||
shaderParameters.lightSpace = St;
|
shaderParameters.lightSpace = St;
|
||||||
@@ -1117,84 +1117,67 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p,
|
|||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture,
|
float2 ShadowMap::texelSizeWorldSpace(const mat3f& clipFromWorld, uint16_t shadowDimension) noexcept {
|
||||||
uint16_t shadowDimension) noexcept {
|
|
||||||
// The Jacobian of the transformation from texture-to-world is the matrix itself for
|
// The Jacobian of the transformation from texture-to-world is the matrix itself for
|
||||||
// orthographic projections. We just need to inverse worldToShadowTexture,
|
// orthographic projections. We just need to inverse shadowMapFromWorld,
|
||||||
// which is guaranteed to be orthographic.
|
// which is guaranteed to be orthographic.
|
||||||
// The two first columns give us how a texel maps in world-space.
|
// The two first columns give us how a texel maps in world-space.
|
||||||
const float ures = 1.0f / float(shadowDimension);
|
float const oneTexel = 2.0f / float(shadowDimension);
|
||||||
const float vres = 1.0f / float(shadowDimension);
|
mat3f const worldFromClip(inverse(clipFromWorld));
|
||||||
const mat3f shadowTextureToWorld(inverse(worldToShadowTexture));
|
float3 const Jx = worldFromClip[0];
|
||||||
const float3 Jx = shadowTextureToWorld[0];
|
float3 const Jy = worldFromClip[1];
|
||||||
const float3 Jy = shadowTextureToWorld[1];
|
float2 const s = float2{ length(Jx), length(Jy) } * oneTexel;
|
||||||
const float s = std::max(length(Jx) * ures, length(Jy) * vres);
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF,
|
/**
|
||||||
uint16_t shadowDimension) noexcept {
|
* Calculates the Jacobian matrix J = ∂(u,v,d)/∂(x,y,z) for a 4x4 perspective projection.
|
||||||
// Here we compute the Jacobian of inverse(MbMtF * Wp).
|
*
|
||||||
// The expression below has been computed with Mathematica. However, it's not very hard,
|
* Given a view-space point P = [x, y, z, 1]^T and a projection matrix M,
|
||||||
// albeit error-prone, to do it by hand because MbMtF is a linear transform.
|
* the projected homogeneous coordinates are [X, Y, Z, W]^T = M * P.
|
||||||
// So we really only need to calculate the Jacobian of inverse(Wp) at inverse(MbMtF).
|
* The resulting NDC coordinates are u = X/W, v = Y/W, and depth d = Z/W.
|
||||||
//
|
*
|
||||||
// Because we're only interested in the length of the columns of the Jacobian, we can use
|
* To find the Jacobian on the CPU, we apply the quotient rule to each component:
|
||||||
// Mb * Mt * F * Wp instead of the full expression Mb * Mt * F * Wp * Wv * L * Mp * Mv,
|
* ∂(X/W) / ∂xi = ( (∂X/∂xi) * W - X * (∂W/∂xi) ) / W^2
|
||||||
// because Wv * L * Mp * Mv is a rigid transform, which doesn't affect the length of
|
*
|
||||||
// the Jacobian's column vectors.
|
* In matrix form, this can be expressed as:
|
||||||
|
* J = (1/W) * [ M_sub - (1/W) * (T ⊗ w_grad) ]
|
||||||
|
*
|
||||||
|
* Where:
|
||||||
|
* - W: The homogeneous w-component after projection (usually -z for standard mats).
|
||||||
|
* - M_sub: The top-left 3x3 submatrix of M.
|
||||||
|
* - T: The column vector [X, Y, Z]^T (pre-perspective divide).
|
||||||
|
* - w_grad: The row vector [m30, m31, m32] (the first three elements of M's last row).
|
||||||
|
* - ⊗: The outer product, resulting in a 3x3 matrix.
|
||||||
|
*
|
||||||
|
* This Jacobian describes the local "stretch" of the projection. For LiSPSM or
|
||||||
|
* shadow mapping, the inverse Jacobian J^-1 provides the world-space footprint
|
||||||
|
* of a shadow texel, which is essential for calculating an accurate,
|
||||||
|
* non-constant depth bias to eliminate shadow acne.
|
||||||
|
*
|
||||||
|
* @param M The 4x4 projection matrix.
|
||||||
|
* @param p The 3D point in view-space where the Jacobian is evaluated.
|
||||||
|
* @return A 3x3 matrix representing the partial derivatives of (u,v,d) w.r.t (x,y,z).
|
||||||
|
*/
|
||||||
|
static mat3f jacobian(mat4f const& M, float3 const& p) noexcept {
|
||||||
|
float4 const T = M * p;
|
||||||
|
mat3f const M_sub = M.upperLeft();
|
||||||
|
float3 const w_grad = { M[0].w, M[1].w, M[2].w };
|
||||||
|
mat3f const t_cross_w{
|
||||||
|
w_grad.x * T.xyz,
|
||||||
|
w_grad.y * T.xyz,
|
||||||
|
w_grad.z * T.xyz
|
||||||
|
};
|
||||||
|
return (M_sub - t_cross_w / T.w) / T.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
float2 ShadowMap::texelSizeWorldSpace(mat4f const& S, uint16_t const shadowDimension) noexcept {
|
||||||
// The Jacobian is not constant, so we evaluate it in the center of the shadow-map texture.
|
// The Jacobian is not constant, so we evaluate it in the center of the shadow-map texture.
|
||||||
// It might be better to do this computation in the vertex shader.
|
// It might be better to do this computation in the vertex shader.
|
||||||
float3 const p = { 0.5f, 0.5f, 0.0f };
|
float3 const p = { 0.0f, 0.0f, 0.0f }; // clip-space
|
||||||
|
float const oneTexel = 2.0f / float(shadowDimension);
|
||||||
const float ures = 1.0f / float(shadowDimension);
|
mat3f const J = jacobian(inverse(S), p);
|
||||||
const float vres = 1.0f / float(shadowDimension);
|
float2 const s = float2{ length(J[0]), length(J[1]) } * oneTexel;
|
||||||
const float dres = 1.0f / 65536.0f;
|
|
||||||
|
|
||||||
constexpr bool JACOBIAN_ESTIMATE = false;
|
|
||||||
if constexpr (JACOBIAN_ESTIMATE) {
|
|
||||||
// This estimates the Jacobian -- this is a lot heavier. This is mostly for reference
|
|
||||||
// and testing.
|
|
||||||
const mat4f Si(inverse(MbMtF * Wp));
|
|
||||||
const float3 p0 = mat4f::project(Si, p);
|
|
||||||
const float3 p1 = mat4f::project(Si, p + float3{ 1, 0, 0 } * ures);
|
|
||||||
const float3 p2 = mat4f::project(Si, p + float3{ 0, 1, 0 } * vres);
|
|
||||||
const float3 p3 = mat4f::project(Si, p + float3{ 0, 0, 1 } * dres);
|
|
||||||
const float3 Jx = p1 - p0;
|
|
||||||
const float3 Jy = p2 - p0;
|
|
||||||
const float3 UTILS_UNUSED Jz = p3 - p0;
|
|
||||||
const float s = std::max(length(Jx), length(Jy));
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float n = Wp[0][0];
|
|
||||||
const float A = Wp[1][1];
|
|
||||||
const float B = Wp[3][1];
|
|
||||||
const float sx = MbMtF[0][0];
|
|
||||||
const float sy = MbMtF[1][1];
|
|
||||||
const float sz = MbMtF[2][2];
|
|
||||||
const float ox = MbMtF[3][0];
|
|
||||||
const float oy = MbMtF[3][1];
|
|
||||||
const float oz = MbMtF[3][2];
|
|
||||||
|
|
||||||
const float X = p.x - ox;
|
|
||||||
const float Y = p.y - oy;
|
|
||||||
const float Z = p.z - oz;
|
|
||||||
|
|
||||||
const float dz = A * sy - Y;
|
|
||||||
const float nsxsz = n * sx * sz;
|
|
||||||
const float j = -(B * sy) / (nsxsz * dz * dz);
|
|
||||||
const mat3f J(mat3f::row_major_init{
|
|
||||||
j * dz * sz, j * X * sz, 0.0f,
|
|
||||||
0.0f, j * nsxsz, 0.0f,
|
|
||||||
0.0f, j * Z * sx, j * dz * sx
|
|
||||||
});
|
|
||||||
|
|
||||||
float3 const Jx = J[0] * ures;
|
|
||||||
float3 const Jy = J[1] * vres;
|
|
||||||
UTILS_UNUSED float3 const Jz = J[2] * dres;
|
|
||||||
const float s = std::max(length(Jx), length(Jy));
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ public:
|
|||||||
math::mat4f lightSpace{};
|
math::mat4f lightSpace{};
|
||||||
math::float4 lightFromWorldZ{};
|
math::float4 lightFromWorldZ{};
|
||||||
math::float4 scissorNormalized{};
|
math::float4 scissorNormalized{};
|
||||||
float texelSizeAtOneMeterWs{};
|
math::float2 texelSizeAtOneMeterWs{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Call once per frame if the light, scene (or visible layers) or camera changes.
|
// Call once per frame if the light, scene (or visible layers) or camera changes.
|
||||||
@@ -319,11 +319,8 @@ private:
|
|||||||
|
|
||||||
math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept;
|
math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept;
|
||||||
|
|
||||||
static float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture,
|
static math::float2 texelSizeWorldSpace(const math::mat3f& clipFromWorld, uint16_t shadowDimension) noexcept;
|
||||||
uint16_t shadowDimension) noexcept;
|
static math::float2 texelSizeWorldSpace(const math::mat4f& clipFromWorld, uint16_t shadowDimension) noexcept;
|
||||||
|
|
||||||
static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF,
|
|
||||||
uint16_t shadowDimension) noexcept;
|
|
||||||
|
|
||||||
static constexpr Segment sBoxSegments[12] = {
|
static constexpr Segment sBoxSegments[12] = {
|
||||||
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
|
{ 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 },
|
||||||
|
|||||||
@@ -742,17 +742,16 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng
|
|||||||
|
|
||||||
// Texel size is constant for directional light (although that's not true when LISPSM
|
// Texel size is constant for directional light (although that's not true when LISPSM
|
||||||
// is used, but in that case we're pretending it is).
|
// is used, but in that case we're pretending it is).
|
||||||
const float wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
|
float2 const wsTexelSize = shaderParameters.texelSizeAtOneMeterWs;
|
||||||
|
|
||||||
auto& s = mShadowUb.edit();
|
auto& s = mShadowUb.edit();
|
||||||
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
s.shadows[shadowIndex].layer = shadowMap.getLayer();
|
||||||
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
s.shadows[shadowIndex].lightFromWorldMatrix = shaderParameters.lightSpace;
|
||||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSize;
|
s.shadows[shadowIndex].normalBias = wsTexelSize * normalBias;
|
||||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSize;
|
|
||||||
s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
|
s.shadows[shadowIndex].elvsm = options.vsm.elvsm;
|
||||||
s.shadows[shadowIndex].bulbRadiusLs =
|
s.shadows[shadowIndex].bulbRadiusLs =
|
||||||
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / wsTexelSize;
|
mSoftShadowOptions.penumbraScale * options.shadowBulbRadius / length(wsTexelSize);
|
||||||
|
|
||||||
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
shadowTechnique |= ShadowTechnique::SHADOW_MAP;
|
||||||
cascadeHasVisibleShadows |= 0x1u << i;
|
cascadeHasVisibleShadows |= 0x1u << i;
|
||||||
@@ -833,7 +832,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
|
|||||||
// and if we need to generate it, update all the UBO data
|
// and if we need to generate it, update all the UBO data
|
||||||
if (shadowMap.hasVisibleShadows()) {
|
if (shadowMap.hasVisibleShadows()) {
|
||||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||||
// note: normalBias is set to zero for VSM
|
// note: normalBias is set to zero for VSM
|
||||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||||
|
|
||||||
@@ -845,12 +844,11 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
|
|||||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
|
||||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||||
s.shadows[shadowIndex].bulbRadiusLs =
|
s.shadows[shadowIndex].bulbRadiusLs =
|
||||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||||
/ wsTexelSizeAtOneMeter;
|
/ length(wsTexelSizeAtOneMeter);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -926,7 +924,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
|||||||
// and if we need to generate it, update all the UBO data
|
// and if we need to generate it, update all the UBO data
|
||||||
if (shadowMap.hasVisibleShadows()) {
|
if (shadowMap.hasVisibleShadows()) {
|
||||||
const size_t shadowIndex = shadowMap.getShadowIndex();
|
const size_t shadowIndex = shadowMap.getShadowIndex();
|
||||||
const float wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
const float2 wsTexelSizeAtOneMeter = shaderParameters.texelSizeAtOneMeterWs;
|
||||||
// note: normalBias is set to zero for VSM
|
// note: normalBias is set to zero for VSM
|
||||||
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
const float normalBias = shadowMapInfo.vsm ? 0.0f : options->normalBias;
|
||||||
|
|
||||||
@@ -938,12 +936,11 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
|
|||||||
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
s.shadows[shadowIndex].scissorNormalized = shaderParameters.scissorNormalized;
|
||||||
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
s.shadows[shadowIndex].normalBias = normalBias * wsTexelSizeAtOneMeter;
|
||||||
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
s.shadows[shadowIndex].lightFromWorldZ = shaderParameters.lightFromWorldZ;
|
||||||
s.shadows[shadowIndex].texelSizeAtOneMeter = wsTexelSizeAtOneMeter;
|
|
||||||
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
s.shadows[shadowIndex].nearOverFarMinusNear = float(n / (f - n));
|
||||||
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
s.shadows[shadowIndex].elvsm = options->vsm.elvsm;
|
||||||
s.shadows[shadowIndex].bulbRadiusLs =
|
s.shadows[shadowIndex].bulbRadiusLs =
|
||||||
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
mSoftShadowOptions.penumbraScale * options->shadowBulbRadius
|
||||||
/ wsTexelSizeAtOneMeter;
|
/ length(wsTexelSizeAtOneMeter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -798,6 +798,12 @@ void FEngine::submitFrame() {
|
|||||||
void FEngine::flush() {
|
void FEngine::flush() {
|
||||||
// flush the command buffer
|
// flush the command buffer
|
||||||
flushCommandBuffer(mCommandBufferQueue);
|
flushCommandBuffer(mCommandBufferQueue);
|
||||||
|
|
||||||
|
// In single-threaded mode, we have to call execute() to drain the command
|
||||||
|
// buffer to really free up space
|
||||||
|
if constexpr (!UTILS_HAS_THREADING) {
|
||||||
|
execute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FEngine::flushAndWait() {
|
void FEngine::flushAndWait() {
|
||||||
@@ -920,12 +926,7 @@ int FEngine::loop() {
|
|||||||
|
|
||||||
void FEngine::flushCommandBuffer(CommandBufferQueue& commandBufferQueue) const {
|
void FEngine::flushCommandBuffer(CommandBufferQueue& commandBufferQueue) const {
|
||||||
getDriver().purge();
|
getDriver().purge();
|
||||||
commandBufferQueue.flush([this](void* begin, void* end) {
|
commandBufferQueue.flush();
|
||||||
UTILS_UNUSED FEngine* engine = const_cast<FEngine*>(this);
|
|
||||||
#if FILAMENT_DEBUG_COMMANDS_HISTOGRAM
|
|
||||||
engine->getDriverApi().debugPrintHistogram(begin, end);
|
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
|
const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
|
||||||
|
|||||||
@@ -257,9 +257,9 @@ filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
|
|||||||
}
|
}
|
||||||
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
|
// mDefinition.perViewDescriptorSetLayout{Vsm} is already resolved for MaterialDomain
|
||||||
if (useVsmDescriptorSetLayout) {
|
if (useVsmDescriptorSetLayout) {
|
||||||
return mDefinition.perViewDescriptorSetLayoutVsm;
|
return mDefinition.perViewDescriptorSetLayoutS2d;
|
||||||
}
|
}
|
||||||
return mDefinition.perViewDescriptorSetLayout;
|
return mDefinition.perViewDescriptorSetLayoutPcf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FMaterial::compile(CompilerPriorityQueue const priority,
|
void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ public:
|
|||||||
// This is mostly intended to be used for post-process materials; but it's also useful for
|
// This is mostly intended to be used for post-process materials; but it's also useful for
|
||||||
// Surface material that behave like post-process material (i.e. that don't really have variants or for
|
// Surface material that behave like post-process material (i.e. that don't really have variants or for
|
||||||
// which VSM is nonsensical, like for unlit materials)
|
// which VSM is nonsensical, like for unlit materials)
|
||||||
return mDefinition.perViewDescriptorSetLayout;
|
return mDefinition.perViewDescriptorSetLayoutPcf;
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorSetLayout const& getPerViewDescriptorSetLayout(
|
DescriptorSetLayout const& getPerViewDescriptorSetLayout(
|
||||||
@@ -378,11 +378,11 @@ private:
|
|||||||
Variant variant = {}) const noexcept;
|
Variant variant = {}) const noexcept;
|
||||||
|
|
||||||
bool isSharedVariant(Variant const variant) const {
|
bool isSharedVariant(Variant const variant) const {
|
||||||
// HACK: The default material "should" have VSM | DEP, but then we'd have to compile it as a
|
// HACK: The default material "should" have MNT | DEP, but then we'd have to compile it as a
|
||||||
// lit material, which would increase binary size. Perhaps we could specially compile it
|
// lit material, which would increase binary size. Perhaps we could specially compile it
|
||||||
// with this variant, but with the shader program cache in active development, the days of
|
// with this variant, but with the shader program cache in active development, the days of
|
||||||
// the default material are numbered anyway.
|
// the default material are numbered anyway.
|
||||||
constexpr Variant::type_t vsmAndDep = Variant::VSM | Variant::DEP;
|
constexpr Variant::type_t vsmAndDep = Variant::MNT | Variant::DEP;
|
||||||
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
|
return mDefinition.materialDomain == MaterialDomain::SURFACE && !mIsDefaultMaterial &&
|
||||||
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
|
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant) &&
|
||||||
(variant.key & vsmAndDep) != vsmAndDep;
|
(variant.key & vsmAndDep) != vsmAndDep;
|
||||||
|
|||||||
@@ -969,10 +969,7 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
|
|||||||
variant.setDirectionalLighting(view.hasDirectionalLighting());
|
variant.setDirectionalLighting(view.hasDirectionalLighting());
|
||||||
variant.setDynamicLighting(view.hasDynamicLighting());
|
variant.setDynamicLighting(view.hasDynamicLighting());
|
||||||
variant.setFog(view.hasFog());
|
variant.setFog(view.hasFog());
|
||||||
// The VSM bit has a different meaning for STANDARD_VARIANT (as opposed to DEPTH_VARIANT),
|
variant.setShadowSampler2D(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
|
||||||
// In the STANDARD_VARIANT case, we are *using* the shadow-map, and the VSM only decides which
|
|
||||||
// type of sampler is used (samplerShadow or sampler2D).
|
|
||||||
variant.setVsm(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
|
|
||||||
variant.setStereo(view.hasStereo());
|
variant.setStereo(view.hasStereo());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -981,10 +978,7 @@ void FRenderer::renderJob(DriverApi& driver, RootArenaScope& rootArenaScope, FVi
|
|||||||
|
|
||||||
if (view.needsShadowMap()) {
|
if (view.needsShadowMap()) {
|
||||||
Variant shadowVariant(Variant::DEPTH_VARIANT);
|
Variant shadowVariant(Variant::DEPTH_VARIANT);
|
||||||
// The VSM bit has a different meaning for DEPTH_VARIANT (as opposed to STANDARD_VARIANT),
|
shadowVariant.setDepthMoments(view.getShadowType() == ShadowType::VSM);
|
||||||
// In the DEPTH_VARIANT case, we are *generating* the shadow-map, and some computations
|
|
||||||
// are handled differently. In addition, the color buffer is used.
|
|
||||||
shadowVariant.setVsm(view.getShadowType() == ShadowType::VSM);
|
|
||||||
|
|
||||||
auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime,
|
auto shadows = view.renderShadowMaps(engine, fg, cameraInfo, mShaderUserTime,
|
||||||
RenderPassBuilder{ commandArena }
|
RenderPassBuilder{ commandArena }
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ FView::FView(FEngine& engine)
|
|||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
// This can fail if another view has already registered this data source
|
// This can fail if another view has already registered this data source
|
||||||
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
|
mDebugState->owner = debugRegistry.registerDataSource("d.view.frame_info",
|
||||||
[weak = std::weak_ptr(mDebugState)]() -> DebugRegistry::DataSource {
|
[weak = std::weak_ptr<DebugState>(mDebugState)]() -> DebugRegistry::DataSource {
|
||||||
// the View could have been destroyed by the time we do this
|
// the View could have been destroyed by the time we do this
|
||||||
auto const state = weak.lock();
|
auto const state = weak.lock();
|
||||||
if (!state) {
|
if (!state) {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
Pod::Spec.new do |spec|
|
Pod::Spec.new do |spec|
|
||||||
spec.name = "Filament"
|
spec.name = "Filament"
|
||||||
spec.version = "1.69.3"
|
spec.version = "1.69.4"
|
||||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||||
spec.homepage = "https://google.github.io/filament"
|
spec.homepage = "https://google.github.io/filament"
|
||||||
spec.authors = "Google LLC."
|
spec.authors = "Google LLC."
|
||||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||||
spec.platform = :ios, "11.0"
|
spec.platform = :ios, "11.0"
|
||||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.3/filament-v1.69.3-ios.tgz" }
|
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.69.4/filament-v1.69.4-ios.tgz" }
|
||||||
|
|
||||||
spec.libraries = 'c++'
|
spec.libraries = 'c++'
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ backend::DescriptorSetLayout const& getPerRenderableLayout() noexcept;
|
|||||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
|
backend::DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||||
MaterialDomain domain,
|
MaterialDomain domain,
|
||||||
bool isLit, bool isSSR, bool hasFog,
|
bool isLit, bool isSSR, bool hasFog,
|
||||||
bool isVSM) noexcept;
|
bool isShadowSampler2D) noexcept;
|
||||||
|
|
||||||
backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
backend::DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
||||||
Variant variant,
|
Variant variant,
|
||||||
|
|||||||
@@ -299,10 +299,9 @@ struct ShadowUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
|
|||||||
math::mat4f lightFromWorldMatrix; // 64
|
math::mat4f lightFromWorldMatrix; // 64
|
||||||
math::float4 lightFromWorldZ; // 16
|
math::float4 lightFromWorldZ; // 16
|
||||||
math::float4 scissorNormalized; // 16
|
math::float4 scissorNormalized; // 16
|
||||||
float texelSizeAtOneMeter; // 4
|
|
||||||
float bulbRadiusLs; // 4
|
float bulbRadiusLs; // 4
|
||||||
float nearOverFarMinusNear; // 4
|
float nearOverFarMinusNear; // 4
|
||||||
float normalBias; // 4
|
math::float2 normalBias; // 4
|
||||||
bool elvsm; // 4
|
bool elvsm; // 4
|
||||||
uint32_t layer; // 4
|
uint32_t layer; // 4
|
||||||
uint32_t reserved1; // 4
|
uint32_t reserved1; // 4
|
||||||
|
|||||||
@@ -48,20 +48,21 @@ struct Variant {
|
|||||||
// SRE: Shadow Receiver
|
// SRE: Shadow Receiver
|
||||||
// SKN: Skinning
|
// SKN: Skinning
|
||||||
// DEP: Depth only
|
// DEP: Depth only
|
||||||
// FOG: Fog
|
// FOG: Fog (standard)
|
||||||
// PCK: Picking (depth variant only)
|
// PCK: Picking (depth)
|
||||||
// VSM: Variance shadow maps (depth) / sampler type (standard)
|
// MNT: Output depth moments (depth)
|
||||||
|
// S2D: Sampler type for shadows (0: samplerShadowArray, 1: sampler2DArray) (standard)
|
||||||
// STE: Instanced stereo rendering
|
// STE: Instanced stereo rendering
|
||||||
//
|
//
|
||||||
// X: either 1 or 0
|
// X: either 1 or 0
|
||||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
// Variant | STE | VSM | FOG | DEP | SKN | SRE | DYN | DIR | 256
|
// Variant | STE | S2D | FOG | DEP | SKN | SRE | DYN | DIR | 256
|
||||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
// PCK
|
// MNT PCK
|
||||||
//
|
//
|
||||||
// Standard variants:
|
// Standard variants:
|
||||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
// | STE | VSM | FOG | 0 | SKN | SRE | DYN | DIR | 128 - 44 = 84
|
// | STE | S2D | FOG | 0 | SKN | SRE | DYN | DIR | 128 - 44 = 84
|
||||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
// Vertex shader X 0 0 0 X X X X
|
// Vertex shader X 0 0 0 X X X X
|
||||||
// Fragment shader 0 X X 0 0 X X X
|
// Fragment shader 0 X X 0 0 X X X
|
||||||
@@ -72,7 +73,7 @@ struct Variant {
|
|||||||
//
|
//
|
||||||
// Depth variants:
|
// Depth variants:
|
||||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
// | STE | VSM | PCK | 1 | SKN | 0 | 0 | 0 | 16 - 4 = 12
|
// | STE | MNT | PCK | 1 | SKN | 0 | 0 | 0 | 16 - 4 = 12
|
||||||
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
// +-----+-----+-----+-----+-----+-----+-----+-----+
|
||||||
// Vertex depth X X 0 1 X 0 0 0
|
// Vertex depth X X 0 1 X 0 0 0
|
||||||
// Fragment depth 0 0 X 1 0 0 0 0
|
// Fragment depth 0 0 X 1 0 0 0 0
|
||||||
@@ -96,13 +97,15 @@ struct Variant {
|
|||||||
static constexpr type_t DEP = 0x10; // depth only variants
|
static constexpr type_t DEP = 0x10; // depth only variants
|
||||||
static constexpr type_t FOG = 0x20; // fog (standard)
|
static constexpr type_t FOG = 0x20; // fog (standard)
|
||||||
static constexpr type_t PCK = 0x20; // picking (depth)
|
static constexpr type_t PCK = 0x20; // picking (depth)
|
||||||
static constexpr type_t VSM = 0x40; // variance shadow maps / sampler type
|
static constexpr type_t S2D = 0x40; // sampler type
|
||||||
|
static constexpr type_t MNT = 0x40; // variance shadow maps
|
||||||
static constexpr type_t STE = 0x80; // instanced stereo
|
static constexpr type_t STE = 0x80; // instanced stereo
|
||||||
|
|
||||||
static constexpr type_t NO_VARIANT = 0u;
|
static constexpr type_t NO_VARIANT = 0u;
|
||||||
|
|
||||||
// special variants (variants that use the reserved space)
|
// special variants (variants that use the reserved space)
|
||||||
static constexpr type_t SPECIAL_SSR = VSM | SRE; // screen-space reflections variant
|
static constexpr type_t SPECIAL_SSR_VARIANT= S2D | SRE ;
|
||||||
|
static constexpr type_t SPECIAL_SSR_MASK = STE | S2D | DEP | SRE | DYN | DIR;
|
||||||
|
|
||||||
static constexpr type_t STANDARD_MASK = DEP;
|
static constexpr type_t STANDARD_MASK = DEP;
|
||||||
static constexpr type_t STANDARD_VARIANT = 0u;
|
static constexpr type_t STANDARD_VARIANT = 0u;
|
||||||
@@ -127,29 +130,30 @@ struct Variant {
|
|||||||
void setSkinning(bool v) noexcept { set(v, SKN); }
|
void setSkinning(bool v) noexcept { set(v, SKN); }
|
||||||
void setFog(bool v) noexcept { set(v, FOG); }
|
void setFog(bool v) noexcept { set(v, FOG); }
|
||||||
void setPicking(bool v) noexcept { set(v, PCK); }
|
void setPicking(bool v) noexcept { set(v, PCK); }
|
||||||
void setVsm(bool v) noexcept { set(v, VSM); }
|
void setShadowSampler2D(bool v) noexcept { set(v, S2D); }
|
||||||
|
void setDepthMoments(bool v) noexcept { set(v, MNT); }
|
||||||
void setStereo(bool v) noexcept { set(v, STE); }
|
void setStereo(bool v) noexcept { set(v, STE); }
|
||||||
|
|
||||||
static constexpr bool isValidDepthVariant(Variant variant) noexcept {
|
static constexpr bool isValidDepthVariant(Variant variant) noexcept {
|
||||||
// Can't have VSM and PICKING together with DEPTH variants
|
// Can't have VSM and PICKING together with DEPTH variants
|
||||||
constexpr type_t RESERVED_MASK = VSM | PCK | DEP | SRE | DYN | DIR;
|
constexpr type_t RESERVED_MASK = MNT | PCK | DEP | SRE | DYN | DIR;
|
||||||
constexpr type_t RESERVED_VALUE = VSM | PCK | DEP;
|
constexpr type_t RESERVED_VALUE = MNT | PCK | DEP;
|
||||||
return ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) &&
|
return ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) &&
|
||||||
((variant.key & RESERVED_MASK) != RESERVED_VALUE);
|
((variant.key & RESERVED_MASK) != RESERVED_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr bool isValidStandardVariant(Variant variant) noexcept {
|
static constexpr bool isValidStandardVariant(Variant variant) noexcept {
|
||||||
// can't have shadow receiver if we don't have any lighting
|
// can't have shadow receiver if we don't have any lighting
|
||||||
constexpr type_t RESERVED0_MASK = VSM | FOG | SRE | DYN | DIR;
|
constexpr type_t RESERVED0_MASK = S2D | FOG | SRE | DYN | DIR;
|
||||||
constexpr type_t RESERVED0_VALUE = VSM | FOG | SRE;
|
constexpr type_t RESERVED0_VALUE = S2D | FOG | SRE;
|
||||||
|
|
||||||
// can't have shadow receiver if we don't have any lighting
|
// can't have shadow receiver if we don't have any lighting
|
||||||
constexpr type_t RESERVED1_MASK = VSM | SRE | DYN | DIR;
|
constexpr type_t RESERVED1_MASK = S2D | SRE | DYN | DIR;
|
||||||
constexpr type_t RESERVED1_VALUE = SRE;
|
constexpr type_t RESERVED1_VALUE = SRE;
|
||||||
|
|
||||||
// can't have VSM without shadow receiver
|
// can't have VSM without shadow receiver
|
||||||
constexpr type_t RESERVED2_MASK = VSM | SRE;
|
constexpr type_t RESERVED2_MASK = S2D | SRE;
|
||||||
constexpr type_t RESERVED2_VALUE = VSM;
|
constexpr type_t RESERVED2_VALUE = S2D;
|
||||||
|
|
||||||
return ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) &&
|
return ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) &&
|
||||||
((variant.key & RESERVED0_MASK) != RESERVED0_VALUE) &&
|
((variant.key & RESERVED0_MASK) != RESERVED0_VALUE) &&
|
||||||
@@ -174,11 +178,15 @@ struct Variant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static constexpr bool isSSRVariant(Variant variant) noexcept {
|
static constexpr bool isSSRVariant(Variant variant) noexcept {
|
||||||
return (variant.key & (STE | VSM | DEP | SRE | DYN | DIR)) == (VSM | SRE);
|
return (variant.key & SPECIAL_SSR_MASK) == SPECIAL_SSR_VARIANT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr bool isVSMVariant(Variant variant) noexcept {
|
static constexpr bool isShadowSampler2DVariant(Variant variant) noexcept {
|
||||||
return !isSSRVariant(variant) && ((variant.key & VSM) == VSM);
|
return !isSSRVariant(variant) && ((variant.key & (S2D | DEP)) == S2D);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool isDepthMomentsVariant(Variant variant) noexcept {
|
||||||
|
return !isSSRVariant(variant) && ((variant.key & (MNT | DEP)) == (MNT | DEP));
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr bool isShadowReceiverVariant(Variant variant) noexcept {
|
static constexpr bool isShadowReceiverVariant(Variant variant) noexcept {
|
||||||
@@ -202,13 +210,13 @@ struct Variant {
|
|||||||
// vertex shader.
|
// vertex shader.
|
||||||
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
|
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
|
||||||
if (isSSRVariant(variant)) {
|
if (isSSRVariant(variant)) {
|
||||||
variant.key &= ~(VSM | SRE);
|
variant.key &= ~SPECIAL_SSR_VARIANT;
|
||||||
}
|
}
|
||||||
return variant & (STE | SKN | SRE | DYN | DIR);
|
return variant & (STE | SKN | SRE | DYN | DIR);
|
||||||
}
|
}
|
||||||
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
|
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
|
||||||
// Only VSM, skinning, and stereo affect the vertex shader's DEPTH variant
|
// Only MNT, skinning, and stereo affect the vertex shader's DEPTH variant
|
||||||
return variant & (STE | VSM | SKN | DEP);
|
return variant & (STE | MNT | SKN | DEP);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -217,11 +225,11 @@ struct Variant {
|
|||||||
// filter out fragment variants that are not needed. For e.g. skinning doesn't
|
// filter out fragment variants that are not needed. For e.g. skinning doesn't
|
||||||
// affect the fragment shader.
|
// affect the fragment shader.
|
||||||
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
|
if ((variant.key & STANDARD_MASK) == STANDARD_VARIANT) {
|
||||||
return variant & (VSM | FOG | SRE | DYN | DIR);
|
return variant & (S2D | FOG | SRE | DYN | DIR);
|
||||||
}
|
}
|
||||||
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
|
if ((variant.key & DEPTH_MASK) == DEPTH_VARIANT) {
|
||||||
// Only VSM & PICKING affects the fragment shader's DEPTH variant
|
// Only VSM & PICKING affects the fragment shader's DEPTH variant
|
||||||
return variant & (VSM | PCK | DEP);
|
return variant & (MNT | PCK | DEP);
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -230,8 +238,8 @@ struct Variant {
|
|||||||
// special case for depth variant
|
// special case for depth variant
|
||||||
if (isValidDepthVariant(variant)) {
|
if (isValidDepthVariant(variant)) {
|
||||||
if (!isLit) {
|
if (!isLit) {
|
||||||
// if we're unlit, we never need the VSM variant
|
// if we're unlit, we never need the MNT variant
|
||||||
return variant & ~VSM;
|
return variant & ~MNT;
|
||||||
}
|
}
|
||||||
return variant;
|
return variant;
|
||||||
}
|
}
|
||||||
@@ -242,9 +250,9 @@ struct Variant {
|
|||||||
// when the shading mode is unlit, remove all the lighting variants
|
// when the shading mode is unlit, remove all the lighting variants
|
||||||
return variant & UNLIT_MASK;
|
return variant & UNLIT_MASK;
|
||||||
}
|
}
|
||||||
// if shadow receiver is disabled, turn off VSM
|
// if shadow receiver is disabled, we pick the shadow sampler
|
||||||
if (!(variant.key & SRE)) {
|
if (!(variant.key & SRE)) {
|
||||||
return variant & ~VSM;
|
return variant & ~S2D;
|
||||||
}
|
}
|
||||||
return variant;
|
return variant;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ utils::CString getDescriptorName(DescriptorSetBindingPoints const set,
|
|||||||
DescriptorSetLayout getPerViewDescriptorSetLayout(
|
DescriptorSetLayout getPerViewDescriptorSetLayout(
|
||||||
MaterialDomain const domain,
|
MaterialDomain const domain,
|
||||||
bool const isLit, bool const isSSR, bool const hasFog,
|
bool const isLit, bool const isSSR, bool const hasFog,
|
||||||
bool const isVSM) noexcept {
|
bool const isShadowSampler2D) noexcept {
|
||||||
|
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
case MaterialDomain::SURFACE: {
|
case MaterialDomain::SURFACE: {
|
||||||
@@ -255,7 +255,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayout(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// change the SHADOW_MAP descriptor type for VSM
|
// change the SHADOW_MAP descriptor type for VSM
|
||||||
if (isVSM) {
|
if (isShadowSampler2D) {
|
||||||
auto const pos = std::find_if(layout.descriptors.begin(), layout.descriptors.end(),
|
auto const pos = std::find_if(layout.descriptors.begin(), layout.descriptors.end(),
|
||||||
[](auto const& v) {
|
[](auto const& v) {
|
||||||
return v.binding == PerViewBindingPoints::SHADOW_MAP;
|
return v.binding == PerViewBindingPoints::SHADOW_MAP;
|
||||||
@@ -285,8 +285,7 @@ DescriptorSetLayout getPerViewDescriptorSetLayoutWithVariant(
|
|||||||
return ssrVariantDescriptorSetLayout;
|
return ssrVariantDescriptorSetLayout;
|
||||||
}
|
}
|
||||||
// We need to filter out all the descriptors not included in the "resolved" layout below
|
// We need to filter out all the descriptors not included in the "resolved" layout below
|
||||||
return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog,
|
return getPerViewDescriptorSetLayout(domain, isLit, isSSR, hasFog, Variant::isShadowSampler2DVariant(variant));
|
||||||
Variant::isVSMVariant(variant));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorType getDescriptorType(SamplerType const type, SamplerFormat const format) {
|
DescriptorType getDescriptorType(SamplerType const type, SamplerFormat const format) {
|
||||||
|
|||||||
@@ -32,41 +32,46 @@ namespace filament {
|
|||||||
Variant Variant::filterUserVariant(
|
Variant Variant::filterUserVariant(
|
||||||
Variant variant, UserVariantFilterMask filterMask) noexcept {
|
Variant variant, UserVariantFilterMask filterMask) noexcept {
|
||||||
// these are easy to filter by just removing the corresponding bit
|
// these are easy to filter by just removing the corresponding bit
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::DIRECTIONAL_LIGHTING) {
|
if (filterMask & uint32_t(UserVariantFilterBit::DIRECTIONAL_LIGHTING)) {
|
||||||
variant.key &= ~DIR;
|
variant.key &= ~DIR;
|
||||||
}
|
}
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::DYNAMIC_LIGHTING) {
|
|
||||||
|
if (filterMask & uint32_t(UserVariantFilterBit::DYNAMIC_LIGHTING)) {
|
||||||
variant.key &= ~DYN;
|
variant.key &= ~DYN;
|
||||||
}
|
}
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::SKINNING) {
|
|
||||||
|
if (filterMask & uint32_t(UserVariantFilterBit::SKINNING)) {
|
||||||
variant.key &= ~SKN;
|
variant.key &= ~SKN;
|
||||||
}
|
}
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::STE) {
|
|
||||||
|
if (filterMask & uint32_t(UserVariantFilterBit::STE)) {
|
||||||
variant.key &= ~(filterMask & STE);
|
variant.key &= ~(filterMask & STE);
|
||||||
}
|
}
|
||||||
if (!isValidDepthVariant(variant)) {
|
|
||||||
// we can't remove FOG from depth variants, this would, in fact, remove picking
|
if (isValidDepthVariant(variant)) {
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::FOG) {
|
// depth variants can have their MNT bit filtered
|
||||||
variant.key &= ~FOG;
|
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
|
||||||
|
variant.key &= ~MNT;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// depth variants can have their VSM bit filtered
|
// we can't remove FOG from depth variants, this would, in fact, remove picking
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
|
if (filterMask & uint32_t(UserVariantFilterBit::FOG)) {
|
||||||
variant.key &= ~VSM;
|
variant.key &= ~FOG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSSRVariant(variant)) {
|
if (!isSSRVariant(variant)) {
|
||||||
// SSR variant needs to be handled separately
|
// SSR variant needs to be handled separately
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::SHADOW_RECEIVER) {
|
if (filterMask & uint32_t(UserVariantFilterBit::SHADOW_RECEIVER)) {
|
||||||
variant.key &= ~SRE;
|
variant.key &= ~SRE;
|
||||||
}
|
}
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::VSM) {
|
if (filterMask & uint32_t(UserVariantFilterBit::VSM)) {
|
||||||
variant.key &= ~VSM;
|
variant.key &= ~S2D;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// see if we need to filter out the SSR variants
|
// see if we need to filter out the SSR variants
|
||||||
if (filterMask & (uint32_t)UserVariantFilterBit::SSR) {
|
if (filterMask & uint32_t(UserVariantFilterBit::SSR)) {
|
||||||
variant.key &= ~SPECIAL_SSR;
|
variant.key &= ~SPECIAL_SSR_VARIANT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return variant;
|
return variant;
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(io::sstream& out,
|
|||||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING",
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_SHADOWING",
|
||||||
litVariants && filament::Variant::isShadowReceiverVariant(variant));
|
litVariants && filament::Variant::isShadowReceiverVariant(variant));
|
||||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
|
||||||
filament::Variant::isVSMVariant(variant));
|
filament::Variant::isShadowSampler2DVariant(variant) ||
|
||||||
|
filament::Variant::isDepthMomentsVariant(variant));
|
||||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
|
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
|
||||||
hasStereo(variant, featureLevel));
|
hasStereo(variant, featureLevel));
|
||||||
CodeGenerator::generateDefine(out, "VARIANT_DEPTH",
|
CodeGenerator::generateDefine(out, "VARIANT_DEPTH",
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
|
|||||||
// reason we name them "unused*" to ensure we're not using them by mistake (type/format don't
|
// reason we name them "unused*" to ensure we're not using them by mistake (type/format don't
|
||||||
// matter).
|
// matter).
|
||||||
|
|
||||||
static SamplerInterfaceBlock const sibPcf{ SamplerInterfaceBlock::Builder()
|
static SamplerInterfaceBlock const sibShadowSamplerPcf{ SamplerInterfaceBlock::Builder()
|
||||||
.name("sampler0")
|
.name("sampler0")
|
||||||
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
|
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
|
||||||
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::SHADOW, Precision::MEDIUM, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
|
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::SHADOW, Precision::MEDIUM, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
|
||||||
@@ -62,7 +62,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
|
|||||||
)
|
)
|
||||||
.build() };
|
.build() };
|
||||||
|
|
||||||
static SamplerInterfaceBlock const sibVsm{ SamplerInterfaceBlock::Builder()
|
static SamplerInterfaceBlock const sibShadowSampler2D{ SamplerInterfaceBlock::Builder()
|
||||||
.name("sampler0")
|
.name("sampler0")
|
||||||
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
|
.stageFlags(backend::ShaderStageFlags::FRAGMENT)
|
||||||
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
|
.add( {{ "shadowMap", +PerViewBindingPoints::SHADOW_MAP, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
|
||||||
@@ -85,10 +85,10 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(Variant variant) noexce
|
|||||||
|
|
||||||
if (Variant::isSSRVariant(variant)) {
|
if (Variant::isSSRVariant(variant)) {
|
||||||
return sibSsr;
|
return sibSsr;
|
||||||
} else if (Variant::isVSMVariant(variant)) {
|
} else if (Variant::isShadowSampler2DVariant(variant)) {
|
||||||
return sibVsm;
|
return sibShadowSampler2D;
|
||||||
} else {
|
} else {
|
||||||
return sibPcf;
|
return sibShadowSamplerPcf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,11 +60,13 @@
|
|||||||
#define GLTFIO_WARN(msg) slog.w << msg << io::endl
|
#define GLTFIO_WARN(msg) slog.w << msg << io::endl
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef GLTFIO_USE_FILESYSTEM
|
||||||
#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(FILAMENT_IOS)
|
#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(FILAMENT_IOS)
|
||||||
#define GLTFIO_USE_FILESYSTEM 0
|
#define GLTFIO_USE_FILESYSTEM 0
|
||||||
#else
|
#else
|
||||||
#define GLTFIO_USE_FILESYSTEM 1
|
#define GLTFIO_USE_FILESYSTEM 1
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
class NameComponentManager;
|
class NameComponentManager;
|
||||||
|
|||||||
@@ -48,10 +48,11 @@ std::string formatVariantString(Variant variant, MaterialDomain domain) noexcept
|
|||||||
if (variant.key & Variant::DEP) variantString += "DEP|";
|
if (variant.key & Variant::DEP) variantString += "DEP|";
|
||||||
if (variant.key & Variant::DEP) {
|
if (variant.key & Variant::DEP) {
|
||||||
if (variant.key & Variant::PCK) variantString += "PCK|";
|
if (variant.key & Variant::PCK) variantString += "PCK|";
|
||||||
|
if (variant.key & Variant::MNT) variantString += "MNT|";
|
||||||
} else {
|
} else {
|
||||||
if (variant.key & Variant::FOG) variantString += "FOG|";
|
if (variant.key & Variant::FOG) variantString += "FOG|";
|
||||||
|
if (variant.key & Variant::S2D) variantString += "S2D|";
|
||||||
}
|
}
|
||||||
if (variant.key & Variant::VSM) variantString += "VSM|";
|
|
||||||
if (variant.key & Variant::STE) variantString += "STE|";
|
if (variant.key & Variant::STE) variantString += "STE|";
|
||||||
variantString = variantString.substr(0, variantString.length() - 1);
|
variantString = variantString.substr(0, variantString.length() - 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ highp vec4 getSpotLightSpacePosition(int index, highp vec3 dir, highp float zLig
|
|||||||
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
|
highp mat4 lightFromWorldMatrix = shadowUniforms.shadows[index].lightFromWorldMatrix;
|
||||||
|
|
||||||
// for spotlights, the bias depends on z
|
// for spotlights, the bias depends on z
|
||||||
float bias = shadowUniforms.shadows[index].normalBias * zLight;
|
highp vec2 bias = shadowUniforms.shadows[index].normalBias * zLight;
|
||||||
|
|
||||||
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
|
return computeLightSpacePosition(getWorldPosition(), getWorldGeometricNormalVector(),
|
||||||
dir, bias, lightFromWorldMatrix);
|
dir, bias, lightFromWorldMatrix);
|
||||||
|
|||||||
@@ -12,12 +12,51 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
|
highp vec4 computeLightSpacePosition(highp vec3 p, const highp vec3 n,
|
||||||
const highp vec3 dir, const float b, highp_mat4 lightFromWorldMatrix) {
|
const highp vec3 dir, const highp vec2 b, highp_mat4 lightFromWorldMatrix) {
|
||||||
|
|
||||||
#if !defined(VARIANT_HAS_VSM)
|
#if !defined(VARIANT_HAS_VSM)
|
||||||
highp float cosTheta = saturate(dot(n, dir));
|
// --------------------------------------------------------------------------------------
|
||||||
highp float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
|
// Anisotropic Normal Bias for Shadow Mapping
|
||||||
p += n * (sinTheta * b);
|
// --------------------------------------------------------------------------------------
|
||||||
|
// To prevent shadow acne, we must push the geometry along its normal to clear the
|
||||||
|
// quantization steps of the shadow map's discrete depth grid. The exact physical depth
|
||||||
|
// error we must clear is proportional to the shadow texel's world-space dimensions.
|
||||||
|
//
|
||||||
|
// This implementation computes the exact geometric projection of the rectangular
|
||||||
|
// shadow map texel onto the surface normal.
|
||||||
|
//
|
||||||
|
// 1. Coordinate Space Transition:
|
||||||
|
// We project the world-space normal onto the light's X and Y basis vectors (L_right,
|
||||||
|
// L_up). This gives us the lateral components of the normal in Light Space (n_Lx, n_Ly).
|
||||||
|
//
|
||||||
|
// 2. The Implicit sin(theta) Slope Scale:
|
||||||
|
// Because the normal is a unit vector, the magnitude of its lateral components in
|
||||||
|
// light space inherently equals sin(theta), where theta is the angle of incidence.
|
||||||
|
// This perfectly and automatically scales the bias from 0.0 (top-down, flat surface)
|
||||||
|
// to maximum (grazing angle).
|
||||||
|
//
|
||||||
|
// 3. Exact Anisotropic Footprint (The L1 Norm):
|
||||||
|
// Shadow texels are rarely perfectly square due to Cascaded Shadow Maps (CSM) or
|
||||||
|
// Light Space Perspective Shadow Maps (LiSPSM). Jx and Jy are the physical world-space
|
||||||
|
// dimensions of the texel.
|
||||||
|
// By evaluating `abs(n_Lx * Jx) + abs(n_Ly * Jy)`, we compute the exact scalar
|
||||||
|
// projection of the rectangular texel footprint.
|
||||||
|
// - It is superior to `max(Jx, Jy)` which assumes a massive square and causes Peter Panning.
|
||||||
|
// - It is superior to `length()` which assumes an ellipse and under-biases the corners.
|
||||||
|
// --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Extract the first row (Light's Right vector in World Space)
|
||||||
|
highp vec3 L_right = vec3(lightFromWorldMatrix[0][0], lightFromWorldMatrix[1][0], lightFromWorldMatrix[2][0]);
|
||||||
|
|
||||||
|
// Extract the second row (Light's Up vector in World Space)
|
||||||
|
highp vec3 L_up = vec3(lightFromWorldMatrix[0][1], lightFromWorldMatrix[1][1], lightFromWorldMatrix[2][1]);
|
||||||
|
|
||||||
|
// Project the world normal onto the shadow map's 2D grid
|
||||||
|
highp float n_Lx = dot(n, L_right);
|
||||||
|
highp float n_Ly = dot(n, L_up);
|
||||||
|
|
||||||
|
// Apply the anisotropic normal bias
|
||||||
|
p += n * (abs(n_Lx * b.x) + abs(n_Ly * b.y));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return mulMat4x4Float3(lightFromWorldMatrix, p);
|
return mulMat4x4Float3(lightFromWorldMatrix, p);
|
||||||
|
|||||||
@@ -5,10 +5,9 @@ struct ShadowData {
|
|||||||
highp mat4 lightFromWorldMatrix;
|
highp mat4 lightFromWorldMatrix;
|
||||||
highp vec4 lightFromWorldZ;
|
highp vec4 lightFromWorldZ;
|
||||||
highp vec4 scissorNormalized;
|
highp vec4 scissorNormalized;
|
||||||
mediump float texelSizeAtOneMeter;
|
|
||||||
mediump float bulbRadiusLs;
|
mediump float bulbRadiusLs;
|
||||||
mediump float nearOverFarMinusNear;
|
mediump float nearOverFarMinusNear;
|
||||||
mediump float normalBias;
|
highp vec2 normalBias;
|
||||||
bool elvsm;
|
bool elvsm;
|
||||||
mediump uint layer;
|
mediump uint layer;
|
||||||
mediump uint reserved1;
|
mediump uint reserved1;
|
||||||
|
|||||||
@@ -76,10 +76,11 @@ into **branch** of `filament-assets`. This branch is paired with a PR or commit
|
|||||||
|
|
||||||
As an example, imagine I am working on a PR, and I've uploaded my change, which is in a
|
As an example, imagine I am working on a PR, and I've uploaded my change, which is in a
|
||||||
branch called `my-pr-branch`, to `filament`. This PR requires updating the golden. We would do
|
branch called `my-pr-branch`, to `filament`. This PR requires updating the golden. We would do
|
||||||
it in the following fashion
|
it in the following fashion on a macOS machine:
|
||||||
|
|
||||||
### Using a script to update the golden repo
|
### Using a script to update the golden repo
|
||||||
|
|
||||||
|
- Make sure you've completed the steps in 'Setting up python'
|
||||||
- Run interactive mode in the `update_golden.py` script.
|
- Run interactive mode in the `update_golden.py` script.
|
||||||
```
|
```
|
||||||
python3 test/renderdiff/src/update_golden.py
|
python3 test/renderdiff/src/update_golden.py
|
||||||
@@ -114,6 +115,24 @@ Doing the above has multiple effects:
|
|||||||
- If the PR is merged, then there is another workflow that will merge `my-pr-branch-golden`
|
- If the PR is merged, then there is another workflow that will merge `my-pr-branch-golden`
|
||||||
to the `main` branch of the golden repo.
|
to the `main` branch of the golden repo.
|
||||||
|
|
||||||
|
### Automated update via commit message
|
||||||
|
|
||||||
|
Alternatively, if you are confident in your changes and want the CI to handle the update
|
||||||
|
for you, you can use the following tag in your commit message:
|
||||||
|
|
||||||
|
- In the commit message of your working branch on `filament`, add the following line:
|
||||||
|
```
|
||||||
|
RDIFF_ACCEPT_NEW_GOLDENS
|
||||||
|
```
|
||||||
|
|
||||||
|
This has the following effects:
|
||||||
|
- The presubmit test `test-renderdiff` will be bypassed (it will not perform rendering or
|
||||||
|
comparison).
|
||||||
|
- When the PR is merged, the postsubmit CI will automatically:
|
||||||
|
1. Build Filament and generate the new images.
|
||||||
|
2. Upload them to a temporary branch in `filament-assets`.
|
||||||
|
3. Merge that branch into `main`.
|
||||||
|
|
||||||
## Viewing test results
|
## Viewing test results
|
||||||
|
|
||||||
We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
|
We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import re
|
|||||||
from utils import execute, ArgParseImpl
|
from utils import execute, ArgParseImpl
|
||||||
|
|
||||||
RDIFF_UPDATE_GOLDEN_STR = 'RDIFF_BRANCH'
|
RDIFF_UPDATE_GOLDEN_STR = 'RDIFF_BRANCH'
|
||||||
|
RDIFF_ACCEPT_NEW_GOLDENS_STR = 'RDIFF_ACCEPT_NEW_GOLDENS'
|
||||||
|
|
||||||
def _parse_commit(commit_str):
|
def _parse_commit(commit_str):
|
||||||
lines = commit_str.split('\n')
|
lines = commit_str.split('\n')
|
||||||
@@ -42,9 +43,11 @@ def _parse_commit(commit_str):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
RE_STR = rf"{RDIFF_UPDATE_GOLDEN_STR}(?:\s)?\=(?:\s)?([a-zA-Z0-9\s\-\/]+)"
|
RE_STR = rf"{RDIFF_UPDATE_GOLDEN_STR}(?:\s)?\=(?:\s)?([a-zA-Z0-9\s\-\/]+)"
|
||||||
|
RE_ACCEPT = rf"^{RDIFF_ACCEPT_NEW_GOLDENS_STR}$"
|
||||||
|
|
||||||
parser = ArgParseImpl()
|
parser = ArgParseImpl()
|
||||||
parser.add_argument('--file', help='A file containing the commit message')
|
parser.add_argument('--file', help='A file containing the commit message')
|
||||||
|
parser.add_argument('--mode', choices=['branch', 'accept_new_goldens'], default='branch', help='Mode of operation')
|
||||||
args, _ = parser.parse_known_args(sys.argv[1:])
|
args, _ = parser.parse_known_args(sys.argv[1:])
|
||||||
|
|
||||||
if not args.file:
|
if not args.file:
|
||||||
@@ -53,14 +56,21 @@ if __name__ == "__main__":
|
|||||||
with open(args.file, 'r') as f:
|
with open(args.file, 'r') as f:
|
||||||
msg = f.read()
|
msg = f.read()
|
||||||
|
|
||||||
to_update = []
|
|
||||||
commit, title, description = _parse_commit(msg)
|
commit, title, description = _parse_commit(msg)
|
||||||
|
|
||||||
|
if args.mode == 'accept_new_goldens':
|
||||||
|
for line in description:
|
||||||
|
if re.match(RE_ACCEPT, line):
|
||||||
|
sys.exit(0)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# args.mode == 'branch'
|
||||||
for line in description:
|
for line in description:
|
||||||
m = re.match(RE_STR, line)
|
m = re.match(RE_STR, line)
|
||||||
if not m:
|
if not m:
|
||||||
continue
|
continue
|
||||||
print(m.group(1))
|
print(m.group(1))
|
||||||
exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Always default to the main branch
|
# Always default to the main branch
|
||||||
print('main')
|
print('main')
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
from utils import execute, ArgParseImpl, mkdir_p
|
from utils import execute, ArgParseImpl, mkdir_p
|
||||||
|
|
||||||
@@ -99,10 +100,11 @@ class GoldenManager:
|
|||||||
code, old_commit = execute(f'git log --format=%B -n 1', cwd=assets_dir)
|
code, old_commit = execute(f'git log --format=%B -n 1', cwd=assets_dir)
|
||||||
if tag and len(tag) > 0:
|
if tag and len(tag) > 0:
|
||||||
old_commit += f'\nFILAMENT={tag}'
|
old_commit += f'\nFILAMENT={tag}'
|
||||||
COMMIT_FILE = '/tmp/golden_commit.txt'
|
with tempfile.NamedTemporaryFile('w', delete=False) as f:
|
||||||
with open(COMMIT_FILE, 'w') as f:
|
|
||||||
f.write(old_commit)
|
f.write(old_commit)
|
||||||
self._git_exec(f'commit --amend -F {COMMIT_FILE}')
|
commit_file = f.name
|
||||||
|
self._git_exec(f'commit --amend -F {commit_file}')
|
||||||
|
os.remove(commit_file)
|
||||||
|
|
||||||
# Do the actual merge
|
# Do the actual merge
|
||||||
self._git_exec(f'checkout main')
|
self._git_exec(f'checkout main')
|
||||||
@@ -139,11 +141,11 @@ class GoldenManager:
|
|||||||
os.path.join(rdiff_dir, f))
|
os.path.join(rdiff_dir, f))
|
||||||
self._git_exec(f'add {os.path.join(GOLDENS_DIR, f)}')
|
self._git_exec(f'add {os.path.join(GOLDENS_DIR, f)}')
|
||||||
|
|
||||||
TMP_GOLDEN_COMMIT_FILE = '/tmp/golden_commit.txt'
|
with tempfile.NamedTemporaryFile('w', delete=False) as f:
|
||||||
|
|
||||||
with open(TMP_GOLDEN_COMMIT_FILE, 'w') as f:
|
|
||||||
f.write(commit_msg)
|
f.write(commit_msg)
|
||||||
self._git_exec(f'commit -a -F {TMP_GOLDEN_COMMIT_FILE}')
|
tmp_golden_commit_file = f.name
|
||||||
|
self._git_exec(f'commit -a -F {tmp_golden_commit_file}')
|
||||||
|
os.remove(tmp_golden_commit_file)
|
||||||
if push_to_remote and \
|
if push_to_remote and \
|
||||||
(self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH):
|
(self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH):
|
||||||
self._git_exec(f'push -f origin {branch}')
|
self._git_exec(f'push -f origin {branch}')
|
||||||
|
|||||||
@@ -64,19 +64,19 @@ def _do_update(golden_manager, config):
|
|||||||
|
|
||||||
def _same_image_diffimg(diffimg_path, img1, img2):
|
def _same_image_diffimg(diffimg_path, img1, img2):
|
||||||
cmd = [diffimg_path, img1, img2]
|
cmd = [diffimg_path, img1, img2]
|
||||||
try:
|
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise RuntimeError(f"diffimg failed with return code {result.returncode}:\n{result.stderr}")
|
||||||
|
|
||||||
output = result.stdout.strip()
|
output = result.stdout.strip()
|
||||||
if not output:
|
if not output:
|
||||||
return False
|
raise RuntimeError("diffimg produced no output")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res_json = json.loads(output)
|
res_json = json.loads(output)
|
||||||
return res_json.get('passed', False)
|
return res_json.get('passed', False)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError as e:
|
||||||
return False
|
raise RuntimeError(f"Failed to parse diffimg output: {e}\nOutput: {output}")
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
|
def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
|
||||||
ret_delete = []
|
ret_delete = []
|
||||||
@@ -93,13 +93,13 @@ def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
|
|||||||
# However, update_golden typically only updates/adds based on the new render set.
|
# However, update_golden typically only updates/adds based on the new render set.
|
||||||
# But strict sync might imply deleting missing ones.
|
# But strict sync might imply deleting missing ones.
|
||||||
# The original logic was: delete = list(base - new).
|
# The original logic was: delete = list(base - new).
|
||||||
delete = list(base - new)
|
delete_files = list(base_files - new_files)
|
||||||
|
|
||||||
# Files in new but not in base are definitely updates (additions)
|
# Files in new but not in base are definitely updates (additions)
|
||||||
update = list(new - base)
|
update_files = list(new_files - base_files)
|
||||||
|
|
||||||
# Files in both need comparison
|
# Files in both need comparison
|
||||||
for fpath in base.intersection(new_files):
|
for fpath in base_files.intersection(new_files):
|
||||||
base_fpath = os.path.join(golden_dir, fpath)
|
base_fpath = os.path.join(golden_dir, fpath)
|
||||||
new_fpath = os.path.join(update_dir, fpath)
|
new_fpath = os.path.join(update_dir, fpath)
|
||||||
|
|
||||||
@@ -110,10 +110,10 @@ def _get_deletes_updates(update_dir, golden_dir, diffimg_path):
|
|||||||
is_different = _file_as_str(new_fpath) != _file_as_str(base_fpath)
|
is_different = _file_as_str(new_fpath) != _file_as_str(base_fpath)
|
||||||
|
|
||||||
if is_different:
|
if is_different:
|
||||||
update.append(fpath)
|
ret_update.append(fpath)
|
||||||
|
|
||||||
ret_update += update
|
ret_update += update_files
|
||||||
ret_delete += delete
|
ret_delete += delete_files
|
||||||
|
|
||||||
return ret_delete, ret_update
|
return ret_delete, ret_update
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ if __name__ == "__main__":
|
|||||||
parser = ArgParseImpl()
|
parser = ArgParseImpl()
|
||||||
parser.add_argument('--branch', help='Branch of the golden repo to write to')
|
parser.add_argument('--branch', help='Branch of the golden repo to write to')
|
||||||
parser.add_argument('--golden-repo-token', help='Access token for the golden repo')
|
parser.add_argument('--golden-repo-token', help='Access token for the golden repo')
|
||||||
parser.add_argument('--push-to-remote', action="store_true", help='Access token for the golden repo')
|
parser.add_argument('--push-to-remote', action="store_true", help='Push the golden repo changes to remote')
|
||||||
parser.add_argument('--diffimg', help='Path to the diffimg tool',
|
parser.add_argument('--diffimg', help='Path to the diffimg tool',
|
||||||
default='./out/cmake-release/tools/diffimg/diffimg')
|
default='./out/cmake-release/tools/diffimg/diffimg')
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import shutil
|
|||||||
import argparse
|
import argparse
|
||||||
import sys
|
import sys
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import shlex
|
||||||
|
|
||||||
def execute(cmd,
|
def execute(cmd,
|
||||||
cwd=None,
|
cwd=None,
|
||||||
@@ -25,7 +26,7 @@ def execute(cmd,
|
|||||||
stdin=None,
|
stdin=None,
|
||||||
env=None,
|
env=None,
|
||||||
raise_errors=False):
|
raise_errors=False):
|
||||||
in_env = os.environ
|
in_env = os.environ.copy()
|
||||||
in_env.update(env if env else {})
|
in_env.update(env if env else {})
|
||||||
home = os.environ['HOME']
|
home = os.environ['HOME']
|
||||||
if f'{home}/bin' not in in_env['PATH']:
|
if f'{home}/bin' not in in_env['PATH']:
|
||||||
@@ -45,11 +46,11 @@ def execute(cmd,
|
|||||||
'universal_newlines': True
|
'universal_newlines': True
|
||||||
}
|
}
|
||||||
if capture_output:
|
if capture_output:
|
||||||
process = subprocess.Popen(cmd.split(' '), **kwargs)
|
process = subprocess.Popen(shlex.split(cmd), **kwargs)
|
||||||
output, err_output = process.communicate()
|
output, err_output = process.communicate()
|
||||||
return_code = process.returncode
|
return_code = process.returncode
|
||||||
else:
|
else:
|
||||||
return_code = subprocess.call(cmd.split(' '), **kwargs)
|
return_code = subprocess.call(shlex.split(cmd), **kwargs)
|
||||||
|
|
||||||
if return_code:
|
if return_code:
|
||||||
# Error
|
# Error
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "filament",
|
"name": "filament",
|
||||||
"version": "1.69.3",
|
"version": "1.69.4",
|
||||||
"description": "Real-time physically based rendering engine",
|
"description": "Real-time physically based rendering engine",
|
||||||
"main": "filament.js",
|
"main": "filament.js",
|
||||||
"module": "filament.js",
|
"module": "filament.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user