Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c5e64564a1 |
@@ -1,23 +0,0 @@
|
||||
name: 'Get and cache glTF Assets'
|
||||
description: 'Downloads and caches glTF assets by calling the get-gltf-sample-assets.sh script.'
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: ./.github/actions/dep-versions
|
||||
- name: Hash models file
|
||||
id: hash-models
|
||||
shell: bash
|
||||
run: echo "hash=$(cat test/renderdiff/tests/gltf_models.txt | md5sum | sed 's/ -//g')" >> $GITHUB_OUTPUT
|
||||
- name: Cache glTF assets
|
||||
id: cache-gltf
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: gltf
|
||||
key: gltf-assets-${{ env.GITHUB_GLTF_SAMPLE_ASSETS_COMMIT }}-${{ steps.hash-models.outputs.hash }}
|
||||
- name: Download assets via script if cache not found
|
||||
if: steps.cache-gltf.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Cache miss for commit ${{ env.GITHUB_GLTF_SAMPLE_ASSETS_COMMIT }}. Running download script..."
|
||||
xargs bash build/common/get-gltf-sample-assets.sh < test/renderdiff/tests/gltf_models.txt
|
||||
@@ -1,6 +1,5 @@
|
||||
name: 'Get and cache Mesa'
|
||||
description: 'Get and cache Mesa'
|
||||
|
||||
name: 'Get Mesa'
|
||||
description: 'Caches and installs Mesa'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
|
||||
@@ -8,8 +8,8 @@ runs:
|
||||
uses: actions/cache@v3
|
||||
id: cache-vulkan-sdk
|
||||
with:
|
||||
path: ${{ runner.homedir }}/VulkanSDK
|
||||
key: vulkansdk-${{ env.GITHUB_VULKANSDK_VERSION }}-2-${{ runner.os }}
|
||||
path: vulkansdk
|
||||
key: vulkansdk-${{ env.GITHUB_VULKANSDK_VERSION }}-${{ runner.os }}
|
||||
- name: Download Vulkan SDK
|
||||
if: steps.cache-vulkan-sdk.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
@@ -21,8 +21,4 @@ runs:
|
||||
run: |
|
||||
source ${{ github.workspace }}/build/common/get-vulkan-sdk.sh
|
||||
unpack_vulkan_installer
|
||||
pushd .
|
||||
cd ~/VulkanSDK/${GITHUB_VULKANSDK_VERSION}
|
||||
sudo ./install_vulkan.py
|
||||
popd
|
||||
shell: bash
|
||||
@@ -19,7 +19,5 @@ runs:
|
||||
- name: Install Mac Prerequisites
|
||||
shell: bash
|
||||
run: |
|
||||
# Install brew prereqs
|
||||
brew install coreutils
|
||||
# Install ninja
|
||||
source ./build/common/get-ninja.sh
|
||||
|
||||
@@ -118,22 +118,17 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
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
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- uses: ./.github/actions/get-mesa
|
||||
- name: Prerequisites
|
||||
run: |
|
||||
pip install tifffile numpy
|
||||
# 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: Render and compare
|
||||
id: render_compare
|
||||
run: |
|
||||
ls ./gltf/Models
|
||||
TEST_DIR=test/renderdiff
|
||||
source ${TEST_DIR}/src/preamble.sh
|
||||
start_
|
||||
|
||||
@@ -544,6 +544,7 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
message(AUTHOR_WARNING "WebGPU is in development stage and broken at this stage. Its support is very limited.")
|
||||
add_definitions(-DFILAMENT_SUPPORTS_WEBGPU)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -8,5 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- Rename `sampler` parameter `unfilterable` to `filterable` [⚠️ **New Material Version**]
|
||||
- materials: new mutable specialization constants feature. See the [materials documentation](https://google.github.io/filament/Materials.html) for details. [⚠️ **New Material Version**]
|
||||
- Update CMake minimum version to 3.22.1
|
||||
- material: Add a material parameter to control shadow far attenuation (b/436680157)
|
||||
|
||||
@@ -18,7 +18,7 @@ Filament release archives contains host-side tools that are required to generate
|
||||
Make sure you always use tools from the same release as the runtime library. This is particularly
|
||||
important for `matc` (material compiler).
|
||||
|
||||
If you'd rather build Filament yourself, please refer to our [build manual](/BUILDING.md).
|
||||
If you'd rather build Filament yourself, please refer to our [build manual](BUILDING.md).
|
||||
|
||||
### Android
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.64.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.63.1'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.64.0'
|
||||
pod 'Filament', '~> 1.63.1'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
@@ -222,7 +222,7 @@ MaterialInstance* materialInstance = material->createInstance();
|
||||
```
|
||||
|
||||
To learn more about materials and `matc`, please refer to the
|
||||
[materials documentation](https://google.github.io/filament/Materials.html).
|
||||
[materials documentation](./docs/Materials.md.html).
|
||||
|
||||
To render, simply pass the `View` to the `Renderer`:
|
||||
|
||||
@@ -240,7 +240,7 @@ in the `samples/` directory. These samples are all based on `libs/filamentapp/`
|
||||
code that creates a native window with SDL2 and initializes the Filament engine, renderer and views.
|
||||
|
||||
For more information on how to prepare environment maps for image-based lighting please refer to
|
||||
[BUILDING.md](/BUILDING.md#running-the-native-samples).
|
||||
[BUILDING.md](https://github.com/google/filament/blob/main/BUILDING.md#running-the-native-samples).
|
||||
|
||||
### Android
|
||||
|
||||
@@ -272,7 +272,7 @@ To get started you can use the textures and environment maps found respectively
|
||||
refer to their respective `URL.txt` files to know more about the original authors.
|
||||
|
||||
Environments must be pre-processed using
|
||||
[`cmgen`](/BUILDING.md#running-the-native-samples) or
|
||||
[`cmgen`](https://github.com/google/filament/blob/main/BUILDING.md#running-the-native-samples) or
|
||||
using the `libiblprefilter` library.
|
||||
|
||||
## How to make contributions
|
||||
|
||||
@@ -7,11 +7,6 @@ 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
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.64.1
|
||||
|
||||
- Update CMake minimum version to 3.22.1
|
||||
- material: Add a material parameter to control shadow far attenuation (b/436680157)
|
||||
|
||||
## v1.64.0
|
||||
|
||||
- engine: add a `linearFog` material parameter. [⚠️ **New Material Version**]
|
||||
|
||||
@@ -60,14 +60,6 @@ static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void setConstant(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
instance->setConstant(name, v);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetParameterBool(JNIEnv *env, jclass,
|
||||
@@ -272,13 +264,6 @@ Java_com_google_android_filament_MaterialInstance_nSetParameterTexture(
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetConstantBool(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_, jboolean x) {
|
||||
setConstant(env, nativeMaterialInstance, name_, bool(x));
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetScissor(
|
||||
|
||||
-13
@@ -402,16 +402,6 @@ public class MaterialInstance {
|
||||
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a bool constant.
|
||||
*
|
||||
* @param name the name of the material constant
|
||||
* @param x the value of the material constant
|
||||
*/
|
||||
public void setConstant(@NonNull String name, boolean x) {
|
||||
nSetConstantBool(getNativeObject(), name, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
@@ -931,9 +921,6 @@ public class MaterialInstance {
|
||||
@NonNull String name, int element, @NonNull @Size(min = 1) float[] v,
|
||||
@IntRange(from = 0) int offset, @IntRange(from = 1) int count);
|
||||
|
||||
private static native void nSetConstantBool(long nativeMaterialInstance,
|
||||
@NonNull String name, boolean x);
|
||||
|
||||
private static native void nSetParameterTexture(long nativeMaterialInstance,
|
||||
@NonNull String name, long nativeTexture, long sampler);
|
||||
|
||||
|
||||
@@ -1949,18 +1949,6 @@ public class View {
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public float gtaoThicknessHeuristic = 0.004f;
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public boolean gtaoUseVisibilityBitmasks = false;
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public float gtaoConstThickness = 0.5f;
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public boolean gtaoLinearThickness = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.64.0
|
||||
VERSION_NAME=1.63.1
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
# Copyright (C) 2025 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#!/usr/bin/bash
|
||||
set -e
|
||||
|
||||
GLTF_SAMPLE_ASSETS_COMMIT=${GITHUB_GLTF_SAMPLE_ASSETS_COMMIT:-d441dfdb87413ff412c620849a649d61789a470f}
|
||||
COMMIT_HASH="${GLTF_SAMPLE_ASSETS_COMMIT}"
|
||||
REPO_URL="https://github.com/KhronosGroup/glTF-Sample-Assets.git"
|
||||
TARGET_DIR="gltf"
|
||||
|
||||
# The default directories to check out if none are specified
|
||||
DEFAULT_SPARSE_PATHS=(
|
||||
"Models/Box/"
|
||||
"Models/Triangle/"
|
||||
"Models/AnimatedCube/"
|
||||
)
|
||||
|
||||
# Check if command-line arguments are provided
|
||||
if [ "$#" -gt 0 ]; then
|
||||
# If arguments are provided, use them as the paths
|
||||
SPARSE_PATHS=()
|
||||
for model_name in "$@"; do
|
||||
SPARSE_PATHS+=("Models/${model_name}/")
|
||||
done
|
||||
echo "Downloading specified models: $@"
|
||||
else
|
||||
# Otherwise, use the default list
|
||||
SPARSE_PATHS=("${DEFAULT_SPARSE_PATHS[@]}")
|
||||
echo "No models specified, downloading default set."
|
||||
fi
|
||||
|
||||
echo "Removing old directory..."
|
||||
rm -rf "${TARGET_DIR}"
|
||||
|
||||
# Clone the repository using a "treeless" clone, which is highly efficient.
|
||||
# --filter=tree:0: Clones only the repository structure without file content (no historical directory listings), making the initial clone very small.
|
||||
# --no-checkout: Prevents automatically checking out the main branch. We will check out a specific commit later.
|
||||
# --sparse: Initializes the repository for sparse checkout, allowing us to fetch only specific directories.
|
||||
git clone --filter=tree:0 --no-checkout --sparse "${REPO_URL}" "${TARGET_DIR}"
|
||||
|
||||
cd "${TARGET_DIR}"
|
||||
|
||||
git sparse-checkout set "${SPARSE_PATHS[@]}"
|
||||
|
||||
echo "Checking out commit ${COMMIT_HASH}..."
|
||||
git checkout "${COMMIT_HASH}"
|
||||
|
||||
echo "Successfully checked out the specified models into the '${TARGET_DIR}' directory."
|
||||
@@ -137,9 +137,6 @@ if [[ "$OS_NAME" == "Darwin" ]]; then
|
||||
|
||||
# This is necessary to be able to build vk (lavapipe) on macOS. Doesn't seem like a real dependency.
|
||||
sed -I '' "s/error('Vulkan drivers require dri3 for X11 support')//g" meson.build
|
||||
# This is to properly link lib-xcb-present on the mac build (though we won't be drawing to any
|
||||
# real hardware surface).
|
||||
sed -I '' "s/dep_xcb_present = null_dep/dep_xcb_present = dependency('xcb-present')/g" meson.build
|
||||
fi
|
||||
|
||||
# -Dosmesa=true => builds OSMesa, which is an offscreen GL context
|
||||
|
||||
@@ -109,8 +109,13 @@ function install_mac() {
|
||||
unzip -t vulkan_sdk.zip
|
||||
fi
|
||||
echo "recognized zip layout 'vulkan_sdk.zip' ${InstallVulkan}.app/Contents" >&2
|
||||
${InstallVulkan}.app/Contents/MacOS/${InstallVulkan} --accept-licenses --default-answer --confirm-command install
|
||||
local sdk_temp=${VULKAN_SDK_DIR}.tmp
|
||||
sudo ${InstallVulkan}.app/Contents/MacOS/${InstallVulkan} --root "$sdk_temp" --accept-licenses --default-answer --confirm-command install
|
||||
du -hs $sdk_temp
|
||||
test -d $sdk_temp/macOS || { echo "unrecognized dmg folder layout: $sdk_temp" ; ls -l $sdk_temp ; }
|
||||
cp -r $sdk_temp/macOS/* $VULKAN_SDK_DIR/
|
||||
if [[ -d ${InstallVulkan}.app/Contents ]] ; then
|
||||
sudo rm -rf "$sdk_temp"
|
||||
rm -rf ${InstallVulkan}.app
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -5,5 +5,4 @@ GITHUB_MESA_VERSION=24.2.1
|
||||
GITHUB_LLVM_VERSION=16
|
||||
GITHUB_NDK_VERSION=27.0.11718014
|
||||
GITHUB_EMSDK_VERSION=3.1.60
|
||||
GITHUB_VULKANSDK_VERSION=1.4.321.0
|
||||
GITHUB_GLTF_SAMPLE_ASSETS_COMMIT=d441dfdb87413ff412c620849a649d61789a470f
|
||||
GITHUB_VULKANSDK_VERSION=1.4.321.0
|
||||
+2
-4
@@ -221,8 +221,6 @@ export is an html with the output captured in a <code><pre></code> tag).</
|
||||
<li>Replace css styling in the exported output as needed (so they don't interfere with the book's css.</li>
|
||||
<li>Replace resource urls to refer to locations relative to the mdbook structure.</li>
|
||||
</ul>
|
||||
<p>Any <code>markdeep</code> doc can be placed in <code>docs_src/src_markdeep/</code> and they will be parsed to html and included
|
||||
in the book as above.</p>
|
||||
<h3 id="readmes"><a class="header" href="#readmes">READMEs</a></h3>
|
||||
<p>Filament depends on a number of libraries, which reside in the directory <code>libs</code>. These individual
|
||||
libaries often have README.md in their root to describe itself. We collect these descriptions into our
|
||||
@@ -231,7 +229,7 @@ located in <code>tools</code>. Some of tools also have README.md as description.
|
||||
<p>The process for copying and processing these READMEs is outlined in <a href="#introductory-doc">Introductory docs</a>.</p>
|
||||
<h3 id="other-technical-notes"><a class="header" href="#other-technical-notes">Other technical notes</a></h3>
|
||||
<p>These are technical documents that do not fit into a library, tool, or directory of the
|
||||
Filament source tree. We collect them into the <code>docs_src/src_mdbook/src/notes</code> directory. No additional
|
||||
Filament source tree. We collect them into the <code>docs_src/src/notes</code> directory. No additional
|
||||
processing is needed for these documents.</p>
|
||||
<h3 id="raw-source-files"><a class="header" href="#raw-source-files">Raw source files</a></h3>
|
||||
<p>These are files that are not part of the <code>mdbook</code> generation, but should be included output in <code>/docs</code>
|
||||
@@ -244,7 +242,7 @@ add a link in <code>SUMMARY.md</code>, and perform the steps outlined in
|
||||
<a href="#how-to-create">how-to create section</a>.</p>
|
||||
<p>For example, if you are adding a general technical note, then you would</p>
|
||||
<ul>
|
||||
<li>Place the document (file with extension <code>.md</code>) in <code>docs_src/src_mdbook/src/notes</code></li>
|
||||
<li>Place the document (file with extension <code>.md</code>) in <code>docs_src/src/notes</code></li>
|
||||
<li>Add a link in <a href="https://github.com/google/filament/blob/main/docs_src/src_mdbook/src/SUMMARY.md"><code>docs_src/src_mdbook/src/SUMMARY.md</code></a></li>
|
||||
<li>Run the commands in the <a href="#how-to-generate">Generate</a> section</li>
|
||||
</ul>
|
||||
|
||||
+9
-9
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.64.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.63.1'
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||
@@ -196,18 +196,18 @@ dependencies {
|
||||
</div>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.64.0'
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.63.1'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
<li><a href="../main/filament.html">Filament</a>, an in-depth explanation of
|
||||
<li><a href="https://google.github.io/filament/Filament.html">Filament</a>, an in-depth explanation of
|
||||
real-time physically based rendering, the graphics capabilities and implementation of Filament.
|
||||
This document explains the math and reasoning behind most of our decisions. This document is a
|
||||
good introduction to PBR for graphics programmers.</li>
|
||||
<li><a href="../main/materials.html">Materials</a>, the full reference
|
||||
<li><a href="https://google.github.io/filament/Materials.html">Materials</a>, the full reference
|
||||
documentation for our material system. This document explains our different material models, how
|
||||
to use the material compiler <code>matc</code> and how to write custom materials.</li>
|
||||
<li><a href="../notes/material_properties.html">Material Properties</a>, a reference
|
||||
<li><a href="https://google.github.io/filament/notes/material_properties.html">Material Properties</a>, a reference
|
||||
sheet for the standard material model.</li>
|
||||
</ul>
|
||||
<h2 id="examples"><a class="header" href="#examples">Examples</a></h2>
|
||||
@@ -395,7 +395,7 @@ by <code>matc</code>:</p>
|
||||
MaterialInstance* materialInstance = material->createInstance();
|
||||
</code></pre>
|
||||
<p>To learn more about materials and <code>matc</code>, please refer to the
|
||||
<a href="../main/materials.html">materials documentation</a>.</p>
|
||||
<a href="./docs/Materials.html">materials documentation</a>.</p>
|
||||
<p>To render, simply pass the <code>View</code> to the <code>Renderer</code>:</p>
|
||||
<pre><code class="language-c++">// beginFrame() returns false if we need to skip a frame
|
||||
if (renderer->beginFrame(swapChain)) {
|
||||
@@ -408,7 +408,7 @@ if (renderer->beginFrame(swapChain)) {
|
||||
in the <code>samples/</code> directory. These samples are all based on <code>libs/filamentapp/</code> which contains the
|
||||
code that creates a native window with SDL2 and initializes the Filament engine, renderer and views.</p>
|
||||
<p>For more information on how to prepare environment maps for image-based lighting please refer to
|
||||
<a href="building.html#running-the-native-samples">BUILDING.md</a>.</p>
|
||||
<a href="https://github.com/google/filament/blob/main/BUILDING.md#running-the-native-samples">BUILDING.md</a>.</p>
|
||||
<h3 id="android-1"><a class="header" href="#android-1">Android</a></h3>
|
||||
<p>See <code>android/samples</code> for examples of how to use Filament on Android.</p>
|
||||
<p>You must always first initialize Filament by calling <code>Filament.init()</code>.</p>
|
||||
@@ -430,7 +430,7 @@ OpenGL ES.</p>
|
||||
<code>third_party/textures</code> and <code>third_party/environments</code>. These assets are under CC0 license. Please
|
||||
refer to their respective <code>URL.txt</code> files to know more about the original authors.</p>
|
||||
<p>Environments must be pre-processed using
|
||||
<a href="building.html#running-the-native-samples"><code>cmgen</code></a> or
|
||||
<a href="https://github.com/google/filament/blob/main/BUILDING.md#running-the-native-samples"><code>cmgen</code></a> or
|
||||
using the <code>libiblprefilter</code> library.</p>
|
||||
<h2 id="how-to-make-contributions"><a class="header" href="#how-to-make-contributions">How to make contributions</a></h2>
|
||||
<p>Please read and follow the steps in <a href="contributing.html">CONTRIBUTING.md</a>. Make sure you are
|
||||
@@ -513,7 +513,7 @@ and tools.</p>
|
||||
<li><code>web</code>: JavaScript bindings, documentation, and samples</li>
|
||||
</ul>
|
||||
<h2 id="license"><a class="header" href="#license">License</a></h2>
|
||||
<p>Please see <a href="https://github.com/google/filament/blob/main/LICENSE">LICENSE</a>.</p>
|
||||
<p>Please see <a href="/LICENSE">LICENSE</a>.</p>
|
||||
<h2 id="disclaimer"><a class="header" href="#disclaimer">Disclaimer</a></h2>
|
||||
<p>This is not an officially supported Google product.</p>
|
||||
|
||||
|
||||
+563
-10
@@ -1,11 +1,564 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Redirecting...</title>
|
||||
<meta http-equiv="refresh" content="0; url=./dup/intro.html">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Introduction - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<link rel="stylesheet" href="css/variables.css">
|
||||
<link rel="stylesheet" href="css/general.css">
|
||||
<link rel="stylesheet" href="css/chrome.css">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="highlight.css">
|
||||
<link rel="stylesheet" href="tomorrow-night.css">
|
||||
<link rel="stylesheet" href="ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div style="display:flex;align-items:center;justify-content:center">
|
||||
<img class="flogo" src="images/filament_logo_small.png"></img>
|
||||
</div>
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
|
||||
<!--
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
-->
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Filament</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="filament"><a class="header" href="#filament">Filament</a></h1>
|
||||
<p><a href="https://github.com/google/filament/actions?query=workflow%3AAndroid"><img src="https://github.com/google/filament/workflows/Android/badge.svg" alt="Android Build Status" /></a>
|
||||
<a href="https://github.com/google/filament/actions?query=workflow%3AiOS"><img src="https://github.com/google/filament/workflows/iOS/badge.svg" alt="iOS Build Status" /></a>
|
||||
<a href="https://github.com/google/filament/actions?query=workflow%3ALinux"><img src="https://github.com/google/filament/workflows/Linux/badge.svg" alt="Linux Build Status" /></a>
|
||||
<a href="https://github.com/google/filament/actions?query=workflow%3AmacOS"><img src="https://github.com/google/filament/workflows/macOS/badge.svg" alt="macOS Build Status" /></a>
|
||||
<a href="https://github.com/google/filament/actions?query=workflow%3AWindows"><img src="https://github.com/google/filament/workflows/Windows/badge.svg" alt="Windows Build Status" /></a>
|
||||
<a href="https://github.com/google/filament/actions?query=workflow%3AWeb"><img src="https://github.com/google/filament/workflows/Web/badge.svg" alt="Web Build Status" /></a></p>
|
||||
<p>Filament is a real-time physically based rendering engine for Android, iOS, Linux, macOS, Windows,
|
||||
and WebGL. It is designed to be as small as possible and as efficient as possible on Android.</p>
|
||||
<h2 id="download"><a class="header" href="#download">Download</a></h2>
|
||||
<p><a href="https://github.com/google/filament/releases">Download Filament releases</a> to access stable builds.
|
||||
Filament release archives contains host-side tools that are required to generate assets.</p>
|
||||
<p>Make sure you always use tools from the same release as the runtime library. This is particularly
|
||||
important for <code>matc</code> (material compiler).</p>
|
||||
<p>If you'd rather build Filament yourself, please refer to our <a href="building.html">build manual</a>.</p>
|
||||
<h3 id="android"><a class="header" href="#android">Android</a></h3>
|
||||
<p>Android projects can simply declare Filament libraries as Maven dependencies:</p>
|
||||
<pre><code class="language-gradle">repositories {
|
||||
// ...
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.63.1'
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||
<div class="table-wrapper"><table><thead><tr><th>Artifact</th><th>Description</th></tr></thead><tbody>
|
||||
<tr><td><a href="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-android"><img src="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-android/badge.svg?subject=filament-android" alt="filament-android" /></a></td><td>The Filament rendering engine itself.</td></tr>
|
||||
<tr><td><a href="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-android-debug"><img src="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-android-debug/badge.svg?subject=filament-android-debug" alt="filament-android-debug" /></a></td><td>Debug version of <code>filament-android</code>.</td></tr>
|
||||
<tr><td><a href="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/gltfio-android"><img src="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/gltfio-android/badge.svg?subject=gltfio-android" alt="gltfio-android" /></a></td><td>A glTF 2.0 loader for Filament, depends on <code>filament-android</code>.</td></tr>
|
||||
<tr><td><a href="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-utils-android"><img src="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filament-utils-android/badge.svg?subject=filament-utils-android" alt="filament-utils-android" /></a></td><td>KTX loading, Kotlin math, and camera utilities, depends on <code>gltfio-android</code>.</td></tr>
|
||||
<tr><td><a href="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android"><img src="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android/badge.svg?subject=filamat-android" alt="filamat-android" /></a></td><td>A runtime material builder/compiler. This library is large but contains a full shader compiler/validator/optimizer and supports both OpenGL and Vulkan.</td></tr>
|
||||
<tr><td><a href="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android-lite"><img src="https://maven-badges.herokuapp.com/maven-central/com.google.android.filament/filamat-android-lite/badge.svg?subject=filamat-android-lite" alt="filamat-android-lite" /></a></td><td>A much smaller alternative to <code>filamat-android</code> that can only generate OpenGL shaders. It does not provide validation or optimizations.</td></tr>
|
||||
</tbody></table>
|
||||
</div>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.63.1'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
<li><a href="https://google.github.io/filament/Filament.html">Filament</a>, an in-depth explanation of
|
||||
real-time physically based rendering, the graphics capabilities and implementation of Filament.
|
||||
This document explains the math and reasoning behind most of our decisions. This document is a
|
||||
good introduction to PBR for graphics programmers.</li>
|
||||
<li><a href="https://google.github.io/filament/Materials.html">Materials</a>, the full reference
|
||||
documentation for our material system. This document explains our different material models, how
|
||||
to use the material compiler <code>matc</code> and how to write custom materials.</li>
|
||||
<li><a href="https://google.github.io/filament/notes/material_properties.html">Material Properties</a>, a reference
|
||||
sheet for the standard material model.</li>
|
||||
</ul>
|
||||
<h2 id="examples"><a class="header" href="#examples">Examples</a></h2>
|
||||
<p><img src="../images/samples/example_bistro1.jpg" alt="Night scene" />
|
||||
<img src="../images/samples/example_bistro2.jpg" alt="Night scene" />
|
||||
<img src="../images/samples/example_materials1.jpg" alt="Materials" />
|
||||
<img src="../images/samples/example_materials2.jpg" alt="Materials" />
|
||||
<img src="../images/samples/example_helmet.jpg" alt="Helmet" />
|
||||
<img src="../images/samples/example_ssr.jpg" alt="Screen-space refraction" /></p>
|
||||
<h2 id="features"><a class="header" href="#features">Features</a></h2>
|
||||
<h3 id="apis"><a class="header" href="#apis">APIs</a></h3>
|
||||
<ul>
|
||||
<li>Native C++ API for Android, iOS, Linux, macOS and Windows</li>
|
||||
<li>Java/JNI API for Android</li>
|
||||
<li>JavaScript API</li>
|
||||
</ul>
|
||||
<h3 id="backends"><a class="header" href="#backends">Backends</a></h3>
|
||||
<ul>
|
||||
<li>OpenGL 4.1+ for Linux, macOS and Windows</li>
|
||||
<li>OpenGL ES 3.0+ for Android and iOS</li>
|
||||
<li>Metal for macOS and iOS</li>
|
||||
<li>Vulkan 1.0 for Android, Linux, macOS, and Windows</li>
|
||||
<li>WebGL 2.0 for all platforms</li>
|
||||
</ul>
|
||||
<h3 id="rendering"><a class="header" href="#rendering">Rendering</a></h3>
|
||||
<ul>
|
||||
<li>Clustered forward renderer</li>
|
||||
<li>Cook-Torrance microfacet specular BRDF</li>
|
||||
<li>Lambertian diffuse BRDF</li>
|
||||
<li>Custom lighting/surface shading</li>
|
||||
<li>HDR/linear lighting</li>
|
||||
<li>Metallic workflow</li>
|
||||
<li>Clear coat</li>
|
||||
<li>Anisotropic lighting</li>
|
||||
<li>Approximated translucent (subsurface) materials</li>
|
||||
<li>Cloth/fabric/sheen shading</li>
|
||||
<li>Normal mapping & ambient occlusion mapping</li>
|
||||
<li>Image-based lighting</li>
|
||||
<li>Physically-based camera (shutter speed, sensitivity and aperture)</li>
|
||||
<li>Physical light units</li>
|
||||
<li>Point lights, spot lights, and directional light</li>
|
||||
<li>Specular anti-aliasing</li>
|
||||
<li>Point, spot, and directional light shadows</li>
|
||||
<li>Cascaded shadows</li>
|
||||
<li>EVSM, PCSS, DPCF, or PCF shadows</li>
|
||||
<li>Transparent shadows</li>
|
||||
<li>Contact shadows</li>
|
||||
<li>Screen-space ambient occlusion</li>
|
||||
<li>Screen-space reflections</li>
|
||||
<li>Screen-space refraction</li>
|
||||
<li>Global fog</li>
|
||||
<li>Dynamic resolution (with support for AMD FidelityFX FSR)</li>
|
||||
</ul>
|
||||
<h3 id="post-processing"><a class="header" href="#post-processing">Post processing</a></h3>
|
||||
<ul>
|
||||
<li>HDR bloom</li>
|
||||
<li>Depth of field bokeh</li>
|
||||
<li>Multiple tone mappers: generic (customizable), ACES, filmic, etc.</li>
|
||||
<li>Color and tone management: luminance scaling, gamut mapping</li>
|
||||
<li>Color grading: exposure, night adaptation, white balance, channel mixer,
|
||||
shadows/mid-tones/highlights, ASC CDL, contrast, saturation, etc.</li>
|
||||
<li>TAA, FXAA, MSAA</li>
|
||||
<li>Screen-space lens flares</li>
|
||||
</ul>
|
||||
<h3 id="gltf-20"><a class="header" href="#gltf-20">glTF 2.0</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Encodings</p>
|
||||
<ul>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Embeded</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Binary</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Primitive Types</p>
|
||||
<ul>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Points</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Lines</li>
|
||||
<li><input disabled="" type="checkbox"/>
|
||||
Line Loop</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Line Strip</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Triangles</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Triangle Strip</li>
|
||||
<li><input disabled="" type="checkbox"/>
|
||||
Triangle Fan</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Animation</p>
|
||||
<ul>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Transform animation</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Linear interpolation</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Morph animation
|
||||
<ul>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Sparse accessor</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Skin animation</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
Joint animation</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Extensions</p>
|
||||
<ul>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_draco_mesh_compression</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_lights_punctual</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_clearcoat</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_emissive_strength</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_ior</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_pbrSpecularGlossiness</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_sheen</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_transmission</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_unlit</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_variants</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_volume</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_materials_specular</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_mesh_quantization</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_texture_basisu</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
KHR_texture_transform</li>
|
||||
<li><input disabled="" type="checkbox" checked=""/>
|
||||
EXT_meshopt_compression</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="rendering-with-filament"><a class="header" href="#rendering-with-filament">Rendering with Filament</a></h2>
|
||||
<h3 id="native-linux-macos-and-windows"><a class="header" href="#native-linux-macos-and-windows">Native Linux, macOS and Windows</a></h3>
|
||||
<p>You must create an <code>Engine</code>, a <code>Renderer</code> and a <code>SwapChain</code>. The <code>SwapChain</code> is created from a
|
||||
native window pointer (an <code>NSView</code> on macOS or a <code>HWND</code> on Windows for instance):</p>
|
||||
<pre><code class="language-c++">Engine* engine = Engine::create();
|
||||
SwapChain* swapChain = engine->createSwapChain(nativeWindow);
|
||||
Renderer* renderer = engine->createRenderer();
|
||||
</code></pre>
|
||||
<p>To render a frame you must then create a <code>View</code>, a <code>Scene</code> and a <code>Camera</code>:</p>
|
||||
<pre><code class="language-c++">Camera* camera = engine->createCamera(EntityManager::get().create());
|
||||
View* view = engine->createView();
|
||||
Scene* scene = engine->createScene();
|
||||
|
||||
view->setCamera(camera);
|
||||
view->setScene(scene);
|
||||
</code></pre>
|
||||
<p>Renderables are added to the scene:</p>
|
||||
<pre><code class="language-c++">Entity renderable = EntityManager::get().create();
|
||||
// build a quad
|
||||
RenderableManager::Builder(1)
|
||||
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
|
||||
.material(0, materialInstance)
|
||||
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vertexBuffer, indexBuffer, 0, 6)
|
||||
.culling(false)
|
||||
.build(*engine, renderable);
|
||||
scene->addEntity(renderable);
|
||||
</code></pre>
|
||||
<p>The material instance is obtained from a material, itself loaded from a binary blob generated
|
||||
by <code>matc</code>:</p>
|
||||
<pre><code class="language-c++">Material* material = Material::Builder()
|
||||
.package((void*) BAKED_MATERIAL_PACKAGE, sizeof(BAKED_MATERIAL_PACKAGE))
|
||||
.build(*engine);
|
||||
MaterialInstance* materialInstance = material->createInstance();
|
||||
</code></pre>
|
||||
<p>To learn more about materials and <code>matc</code>, please refer to the
|
||||
<a href="./docs/Materials.html">materials documentation</a>.</p>
|
||||
<p>To render, simply pass the <code>View</code> to the <code>Renderer</code>:</p>
|
||||
<pre><code class="language-c++">// beginFrame() returns false if we need to skip a frame
|
||||
if (renderer->beginFrame(swapChain)) {
|
||||
// for each View
|
||||
renderer->render(view);
|
||||
renderer->endFrame();
|
||||
}
|
||||
</code></pre>
|
||||
<p>For complete examples of Linux, macOS and Windows Filament applications, look at the source files
|
||||
in the <code>samples/</code> directory. These samples are all based on <code>libs/filamentapp/</code> which contains the
|
||||
code that creates a native window with SDL2 and initializes the Filament engine, renderer and views.</p>
|
||||
<p>For more information on how to prepare environment maps for image-based lighting please refer to
|
||||
<a href="https://github.com/google/filament/blob/main/BUILDING.md#running-the-native-samples">BUILDING.md</a>.</p>
|
||||
<h3 id="android-1"><a class="header" href="#android-1">Android</a></h3>
|
||||
<p>See <code>android/samples</code> for examples of how to use Filament on Android.</p>
|
||||
<p>You must always first initialize Filament by calling <code>Filament.init()</code>.</p>
|
||||
<p>Rendering with Filament on Android is similar to rendering from native code (the APIs are largely
|
||||
the same across languages). You can render into a <code>Surface</code> by passing a <code>Surface</code> to the
|
||||
<code>createSwapChain</code> method. This allows you to render to a <code>SurfaceTexture</code>, a <code>TextureView</code> or
|
||||
a <code>SurfaceView</code>. To make things easier we provide an Android specific API called <code>UiHelper</code> in the
|
||||
package <code>com.google.android.filament.android</code>. All you need to do is set a render callback on the
|
||||
helper and attach your <code>SurfaceView</code> or <code>TextureView</code> to it. You are still responsible for
|
||||
creating the swap chain in the <code>onNativeWindowChanged()</code> callback.</p>
|
||||
<h3 id="ios-1"><a class="header" href="#ios-1">iOS</a></h3>
|
||||
<p>Filament is supported on iOS 11.0 and above. See <code>ios/samples</code> for examples of using Filament on
|
||||
iOS.</p>
|
||||
<p>Filament on iOS is largely the same as native rendering with C++. A <code>CAEAGLLayer</code> or <code>CAMetalLayer</code>
|
||||
is passed to the <code>createSwapChain</code> method. Filament for iOS supports both Metal (preferred) and
|
||||
OpenGL ES.</p>
|
||||
<h2 id="assets"><a class="header" href="#assets">Assets</a></h2>
|
||||
<p>To get started you can use the textures and environment maps found respectively in
|
||||
<code>third_party/textures</code> and <code>third_party/environments</code>. These assets are under CC0 license. Please
|
||||
refer to their respective <code>URL.txt</code> files to know more about the original authors.</p>
|
||||
<p>Environments must be pre-processed using
|
||||
<a href="https://github.com/google/filament/blob/main/BUILDING.md#running-the-native-samples"><code>cmgen</code></a> or
|
||||
using the <code>libiblprefilter</code> library.</p>
|
||||
<h2 id="how-to-make-contributions"><a class="header" href="#how-to-make-contributions">How to make contributions</a></h2>
|
||||
<p>Please read and follow the steps in <a href="contributing.html">CONTRIBUTING.md</a>. Make sure you are
|
||||
familiar with the <a href="code_style.html">code style</a>.</p>
|
||||
<h2 id="directory-structure"><a class="header" href="#directory-structure">Directory structure</a></h2>
|
||||
<p>This repository not only contains the core Filament engine, but also its supporting libraries
|
||||
and tools.</p>
|
||||
<ul>
|
||||
<li><code>android</code>: Android libraries and projects
|
||||
<ul>
|
||||
<li><code>filamat-android</code>: Filament material generation library (AAR) for Android</li>
|
||||
<li><code>filament-android</code>: Filament library (AAR) for Android</li>
|
||||
<li><code>filament-utils-android</code>: Extra utilities (KTX loader, math types, etc.)</li>
|
||||
<li><code>gltfio-android</code>: Filament glTF loading library (AAR) for Android</li>
|
||||
<li><code>samples</code>: Android-specific Filament samples</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code>art</code>: Source for various artworks (logos, PDF manuals, etc.)</li>
|
||||
<li><code>assets</code>: 3D assets to use with sample applications</li>
|
||||
<li><code>build</code>: CMake build scripts</li>
|
||||
<li><code>docs</code>: Documentation
|
||||
<ul>
|
||||
<li><code>math</code>: Mathematica notebooks used to explore BRDFs, equations, etc.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code>filament</code>: Filament rendering engine (minimal dependencies)
|
||||
<ul>
|
||||
<li><code>backend</code>: Rendering backends/drivers (Vulkan, Metal, OpenGL/ES)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code>ide</code>: Configuration files for IDEs (CLion, etc.)</li>
|
||||
<li><code>ios</code>: Sample projects for iOS</li>
|
||||
<li><code>libs</code>: Libraries
|
||||
<ul>
|
||||
<li><code>bluegl</code>: OpenGL bindings for macOS, Linux and Windows</li>
|
||||
<li><code>bluevk</code>: Vulkan bindings for macOS, Linux, Windows and Android</li>
|
||||
<li><code>camutils</code>: Camera manipulation utilities</li>
|
||||
<li><code>filabridge</code>: Library shared by the Filament engine and host tools</li>
|
||||
<li><code>filaflat</code>: Serialization/deserialization library used for materials</li>
|
||||
<li><code>filagui</code>: Helper library for <a href="https://github.com/ocornut/imgui">Dear ImGui</a></li>
|
||||
<li><code>filamat</code>: Material generation library</li>
|
||||
<li><code>filamentapp</code>: SDL2 skeleton to build sample apps</li>
|
||||
<li><code>filameshio</code>: Tiny filamesh parsing library (see also <code>tools/filamesh</code>)</li>
|
||||
<li><code>geometry</code>: Mesh-related utilities</li>
|
||||
<li><code>gltfio</code>: Loader for glTF 2.0</li>
|
||||
<li><code>ibl</code>: IBL generation tools</li>
|
||||
<li><code>image</code>: Image filtering and simple transforms</li>
|
||||
<li><code>imageio</code>: Image file reading / writing, only intended for internal use</li>
|
||||
<li><code>matdbg</code>: DebugServer for inspecting shaders at run-time (debug builds only)</li>
|
||||
<li><code>math</code>: Math library</li>
|
||||
<li><code>mathio</code>: Math types support for output streams</li>
|
||||
<li><code>utils</code>: Utility library (threads, memory, data structures, etc.)</li>
|
||||
<li><code>viewer</code>: glTF viewer library (requires gltfio)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code>samples</code>: Sample desktop applications</li>
|
||||
<li><code>shaders</code>: Shaders used by <code>filamat</code> and <code>matc</code></li>
|
||||
<li><code>third_party</code>: External libraries and assets
|
||||
<ul>
|
||||
<li><code>environments</code>: Environment maps under CC0 license that can be used with <code>cmgen</code></li>
|
||||
<li><code>models</code>: Models under permissive licenses</li>
|
||||
<li><code>textures</code>: Textures under CC0 license</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code>tools</code>: Host tools
|
||||
<ul>
|
||||
<li><code>cmgen</code>: Image-based lighting asset generator</li>
|
||||
<li><code>filamesh</code>: Mesh converter</li>
|
||||
<li><code>glslminifier</code>: Minifies GLSL source code</li>
|
||||
<li><code>matc</code>: Material compiler</li>
|
||||
<li><code>filament-matp</code>: Material parser</li>
|
||||
<li><code>matinfo</code> Displays information about materials compiled with <code>matc</code></li>
|
||||
<li><code>mipgen</code> Generates a series of miplevels from a source image</li>
|
||||
<li><code>normal-blending</code>: Tool to blend normal maps</li>
|
||||
<li><code>resgen</code> Aggregates binary blobs into embeddable resources</li>
|
||||
<li><code>roughness-prefilter</code>: Pre-filters a roughness map from a normal map to reduce aliasing</li>
|
||||
<li><code>specular-color</code>: Computes the specular color of conductors based on spectral data</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><code>web</code>: JavaScript bindings, documentation, and samples</li>
|
||||
</ul>
|
||||
<h2 id="license"><a class="header" href="#license">License</a></h2>
|
||||
<p>Please see <a href="/LICENSE">LICENSE</a>.</p>
|
||||
<h2 id="disclaimer"><a class="header" href="#disclaimer">Disclaimer</a></h2>
|
||||
<p>This is not an officially supported Google product.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
|
||||
<a rel="next prefetch" href="dup/building.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
|
||||
<a rel="next prefetch" href="dup/building.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="elasticlunr.min.js"></script>
|
||||
<script src="mark.min.js"></script>
|
||||
<script src="searcher.js"></script>
|
||||
|
||||
<script src="clipboard.min.js"></script>
|
||||
<script src="highlight.js"></script>
|
||||
<script src="book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -160,8 +160,8 @@
|
||||
<main>
|
||||
<h1 id="core-concepts"><a class="header" href="#core-concepts">Core Concepts</a></h1>
|
||||
<ul>
|
||||
<li><a href="filament.html">Filament</a> - High-level designs; Filament's PBR/math assumptions; implementation details.</li>
|
||||
<li><a href="materials.html">Materials</a> - A guide to Filament's material definition.</li>
|
||||
<li><a href="main/filament.html">Filament</a> - High-level designs; Filament's PBR/math assumptions; implementation details.</li>
|
||||
<li><a href="main/materials.html">Materials</a> - A guide to Filament's material definition.</li>
|
||||
</ul>
|
||||
|
||||
</main>
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2
-5
@@ -65,9 +65,6 @@ We describe step 1 in detail for the sake of record:
|
||||
- Replace css styling in the exported output as needed (so they don't interfere with the book's css.
|
||||
- Replace resource urls to refer to locations relative to the mdbook structure.
|
||||
|
||||
Any `markdeep` doc can be placed in `docs_src/src_markdeep/` and they will be parsed to html and included
|
||||
in the book as above.
|
||||
|
||||
### READMEs
|
||||
Filament depends on a number of libraries, which reside in the directory `libs`. These individual
|
||||
libaries often have README.md in their root to describe itself. We collect these descriptions into our
|
||||
@@ -78,7 +75,7 @@ The process for copying and processing these READMEs is outlined in [Introductor
|
||||
|
||||
### Other technical notes
|
||||
These are technical documents that do not fit into a library, tool, or directory of the
|
||||
Filament source tree. We collect them into the `docs_src/src_mdbook/src/notes` directory. No additional
|
||||
Filament source tree. We collect them into the `docs_src/src/notes` directory. No additional
|
||||
processing is needed for these documents.
|
||||
|
||||
### Raw source files
|
||||
@@ -93,7 +90,7 @@ add a link in `SUMMARY.md`, and perform the steps outlined in
|
||||
[how-to create section](#how-to-create).
|
||||
|
||||
For example, if you are adding a general technical note, then you would
|
||||
- Place the document (file with extension `.md`) in `docs_src/src_mdbook/src/notes`
|
||||
- Place the document (file with extension `.md`) in `docs_src/src/notes`
|
||||
- Add a link in [`docs_src/src_mdbook/src/SUMMARY.md`]
|
||||
- Run the commands in the [Generate](#how-to-generate) section
|
||||
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
"README.md": {
|
||||
"dest": "dup/intro.md",
|
||||
"link_transforms": {
|
||||
"/BUILDING.md#running-the-native-samples": "building.md#running-the-native-samples",
|
||||
"/BUILDING.md": "building.md",
|
||||
"BUILDING.md": "building.md",
|
||||
"/CONTRIBUTING.md": "contributing.md",
|
||||
"/CODE_STYLE.md": "code_style.md",
|
||||
"/LICENSE": "https://github.com/google/filament/blob/main/LICENSE",
|
||||
"https://google.github.io/filament/Filament.html": "../main/filament.md",
|
||||
"https://google.github.io/filament/Materials.html": "../main/materials.md",
|
||||
"https://google.github.io/filament/notes/material_properties.html": "../notes/material_properties.md",
|
||||
"docs/images/samples": "../images/samples"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -212,7 +212,7 @@ when using textures.
|
||||
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
|
||||
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
|
||||
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
|
||||
(reflected light takes on the color of the surfaced as defined by `baseColor`).
|
||||
(reflected light takes on the color of the surfaced as defined by `baseColor`).
|
||||
|
||||
The effect of `metallic` is shown in figure [metallicProperty] (click on the image to see a
|
||||
larger version).
|
||||
@@ -248,7 +248,7 @@ The effect of `roughness` on metallic surfaces is shown in figure [roughnessCond
|
||||
When refraction through an object is enabled (using a `refractonType` of `thin` or `solid`), the
|
||||
`roughness` property will also affect the refractions, as shown in figure
|
||||
[roughnessRefractionProperty] (click on the image to see a larger version).
|
||||
|
||||
|
||||
![Figure [roughnessRefractionProperty]: Refractive sphere with `roughness` varying from 0.0
|
||||
(left) to 1.0 (right)](images/materials/refraction_roughness.png)
|
||||
|
||||
@@ -307,7 +307,7 @@ The sheen color controls the color appearance and strength of an optional sheen
|
||||
base layer described by the properties above. The sheen layer always sits below the clear coat layer
|
||||
if such a layer is present.
|
||||
|
||||
The sheen layer can be used to represent cloth and fabric materials. Please refer to
|
||||
The sheen layer can be used to represent cloth and fabric materials. Please refer to
|
||||
section [Cloth model] for more information about cloth and fabric materials.
|
||||
|
||||
The effect of `sheenColor` is shown in figure [materialSheenColor]
|
||||
@@ -520,14 +520,14 @@ light to bend further away from the initial path.
|
||||
|
||||
Table [commonMatIOR] describes acceptable refractive indices for various types of materials.
|
||||
|
||||
Material | IOR
|
||||
Material | IOR
|
||||
--------------------------:|:-----------------
|
||||
Air | 1.0
|
||||
Water | 1.33
|
||||
Common liquids | 1.33 to 1.5
|
||||
Common gemstones | 1.58 to 2.33
|
||||
Plastics, glass | 1.5 to 1.58
|
||||
Other dielectric materials | 1.33 to 1.58
|
||||
Water | 1.33
|
||||
Common liquids | 1.33 to 1.5
|
||||
Common gemstones | 1.58 to 2.33
|
||||
Plastics, glass | 1.5 to 1.58
|
||||
Other dielectric materials | 1.33 to 1.58
|
||||
[Table [commonMatIOR]: Index of refraction of common materials]
|
||||
|
||||
The appearance of a refractive material will greatly depend on the `refractionType` and
|
||||
@@ -1096,37 +1096,30 @@ Value
|
||||
`bool` or `number`, depending on the `type` of the constant. The type must be one of the types
|
||||
described in table [materialConstantsTypes].
|
||||
|
||||
Constants may also be specified as mutable by setting the `mutable` property to `true`. Only
|
||||
`bool` spec constants may be specified as mutable.
|
||||
|
||||
| Type | Description | Default | May be mutable? |
|
||||
|:------|:------------------------------|:--------|-----------------|
|
||||
| int | A signed, 32 bit GLSL int | | no |
|
||||
| float | A single-precision GLSL float | 0.0 | no |
|
||||
| bool | A GLSL bool | false | yes |
|
||||
Type | Description | Default
|
||||
:----------------------|:-----------------------------------------|:------------------
|
||||
int | A signed, 32 bit GLSL int | 0
|
||||
float | A single-precision GLSL float | 0.0
|
||||
bool | A GLSL bool | false
|
||||
[Table [materialConstantsTypes]: Material constants types]
|
||||
|
||||
Description
|
||||
: Lists the constant parameters accepted by your material. These constants can be set, or
|
||||
"specialized", at runtime when loading a material package. Multiple materials can be loaded from
|
||||
the same material package with differing constant parameter specializations. If a constant
|
||||
parameter is specialized as mutable, it may be changed at any time on a per-instance basis via
|
||||
`MaterialInstance::setConstant(name, value)`. Otherwise, once a material is loaded from a
|
||||
material package, its constant parameters cannot be changed.
|
||||
|
||||
Compared to regular parameters, constant parameters allow the compiler to generate more
|
||||
efficient code. Access constant parameters from the shader by prefixing the name with
|
||||
`materialConstant_`. For example, a constant parameter named `myConstant` is accessed in the
|
||||
shader as `materialConstant_myConstant`. If a constant parameter is not set at runtime, the
|
||||
default is used.
|
||||
the same material package with differing constant parameter specializations. Once a material is
|
||||
loaded from a material package, its constant parameters cannot be changed. Compared to regular
|
||||
parameters, constant parameters allow the compiler to generate more efficient code. Access
|
||||
constant parameters from the shader by prefixing the name with `materialConstant_`. For example,
|
||||
a constant parameter named `myConstant` is accessed in the shader as
|
||||
`materialConstant_myConstant`. If a constant parameter is not set at runtime, the default is
|
||||
used.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
constants : [
|
||||
{
|
||||
name : overrideAlpha,
|
||||
type : bool,
|
||||
mutable : true,
|
||||
type : bool
|
||||
},
|
||||
{
|
||||
name : customAlpha,
|
||||
@@ -2646,7 +2639,7 @@ standard skybox material. It produces a list of 2 parameters, named `showSun` an
|
||||
respectively a boolean and a cubemap texture.
|
||||
|
||||
```text
|
||||
$ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
$ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
{
|
||||
"parameters": [
|
||||
{
|
||||
@@ -2662,7 +2655,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### --variant-filter
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Core Concepts
|
||||
|
||||
|
||||
- [Filament](filament.md) - High-level designs; Filament's PBR/math assumptions; implementation details.
|
||||
- [Materials](materials.md) - A guide to Filament's material definition.
|
||||
- [Filament](main/filament.md) - High-level designs; Filament's PBR/math assumptions; implementation details.
|
||||
- [Materials](main/materials.md) - A guide to Filament's material definition.
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
# Performance Analysis
|
||||
|
||||
## Android
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Download and install Android GPU Inspector (AGI). See https://developer.android.com/agi.
|
||||
|
||||
### Android with the Vulkan Backend
|
||||
|
||||
---
|
||||
|
||||
#### Profiling
|
||||
|
||||
1. Build a release build of Filament with the applicable backend(s) enabled
|
||||
_(+ any special flags for enabling sys strace. Nothing special is needed for Vulkan or WebGPU aside from
|
||||
building a release build with no flags)_ _(debug builds for this are useless)_
|
||||
|
||||
```shell
|
||||
# the following command assumes you are in the root filament directory
|
||||
# and ANDROID_HOME is exported (and possibly also CC and CXX on linux as needed)
|
||||
#
|
||||
# NOTE: to build with WebGPU support you need to explicitly include the -W flag
|
||||
# (it doesn't get compiled in by default), e.g.:
|
||||
# ./build.sh -W -p android,desktop -i release
|
||||
#
|
||||
# Note that you can speed this up a bit (and reduce disk space usage) by limiting the target to just
|
||||
# the ABI you plan on testing with the -q flag, e.g. -q arm64-v8a. If you do this, you
|
||||
# will need to update the android/gradle.properties file to specify the ABI(s) you are targeting
|
||||
# with the com.google.android.filament.abis property.
|
||||
# Thus, a build command that would target BOTH Vulkan AND WebGPU AND only target the ARM64 ABI would look something
|
||||
# like:
|
||||
# ./build.sh -W -q arm64-v8a -p android,desktop -i release
|
||||
./build.sh -p android,desktop -i release
|
||||
```
|
||||
1. Connect your Android device to your computer via a USB cord with USB debugging enabled and configure
|
||||
the system property to default to the desired backend, e.g.
|
||||
_(to determine how these numbers map to the backends, see the `enum class Backend`
|
||||
definition in [filament/filament/backend/include/backend/DriverEnums.h](../../../../filament/backend/include/backend/DriverEnums.h))_:
|
||||
```shell
|
||||
# to set the backend to Vulkan:
|
||||
adb shell setprop debug.filament.backend 2
|
||||
# to set the backend to WebGPU:
|
||||
adb shell setprop debug.filament.backend 4
|
||||
# to view the current property:
|
||||
adb shell getprop debug.filament.backend
|
||||
```
|
||||
1. Build and run a sample, e.g. sample-gltf-viewer, on your Android device
|
||||
_([Android Studio](https://developer.android.com/studio) recommended)_.
|
||||
1. Run [AGI](https://developer.android.com/agi) and follow instructions to profile the app/system
|
||||
with trace capture(s). See https://developer.android.com/agi/start for more details to
|
||||
get started.
|
||||
- We typically only run "Capture System Profiler trace" _(not necessarily "Capture Frame Profiler trace")_
|
||||
- When configuring the trace:
|
||||
- For both the WebGPU and Vulkan backends configure the profiler for the Vulkan API
|
||||
_(since WebGPU should be using Vulkan under the hood as well)_
|
||||
- Running for ~1 seconds should suffice
|
||||
- Hit the "Configure" button in Trace objects, select "Switch to advanced mode" and add:
|
||||
```
|
||||
data_sources {
|
||||
config {
|
||||
name: "track_event"
|
||||
track_event_config {
|
||||
disabled_categories: "*"
|
||||
enabled_categories: "filament/filament"
|
||||
enabled_categories: "filament/jobsystem"
|
||||
enabled_categories: "filament/gltfio"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
- One you open the trace, zoom into a series of frames to get a sense of generally how long they typically take
|
||||
_(use `W`, `S`, `A` and `D` keys and mouse wheel for navigation)_ and find a representative one.
|
||||
We are most interested in the performance of the
|
||||
`FEngine::loop` thread, how long it takes, overlap in activities/processes/commands, reduction in queue submissions,
|
||||
etc. Similarly, we can view GPU timeline as it relates to that. We want to see overlapping
|
||||
shader invocations and non-interrupted fragment shader runs.
|
||||
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Redirecting...</title>
|
||||
<meta http-equiv="refresh" content="0; url=./dup/intro.html">
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
@@ -258,8 +258,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
include/backend/platforms/WebGPUPlatform.h
|
||||
src/webgpu/platform/WebGPUPlatform.cpp
|
||||
src/webgpu/SpdMipmapGenerator/SpdMipmapGenerator.cpp
|
||||
src/webgpu/utils/AsyncTaskCounter.cpp
|
||||
src/webgpu/utils/AsyncTaskCounter.h
|
||||
src/webgpu/utils/StringPlaceholderTemplateProcessor.cpp
|
||||
src/webgpu/utils/StringPlaceholderTemplateProcessor.h
|
||||
src/webgpu/WebGPUBlitter.cpp
|
||||
@@ -414,7 +412,6 @@ endif()
|
||||
|
||||
if (APPLE AND NOT IOS)
|
||||
target_link_libraries(${TARGET} PRIVATE "-framework Cocoa")
|
||||
target_link_libraries(${TARGET} PRIVATE "-framework QuartzCore")
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TARGET} PUBLIC math)
|
||||
@@ -530,7 +527,7 @@ target_link_libraries(${TARGET} PRIVATE
|
||||
if (LINUX AND FILAMENT_SUPPORTS_WEBGPU)
|
||||
# Unclear why this needs to be explicit while other linux libs do not, but it is necessary for
|
||||
# WebGPU to build on linux.
|
||||
target_link_libraries(${TARGET} PRIVATE -lxcb -lX11)
|
||||
target_link_libraries(${TARGET} PRIVATE -lxcb)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -579,9 +576,6 @@ if (APPLE OR LINUX)
|
||||
test/test_CommandBufferQueue.cpp
|
||||
test/test_Template.cpp
|
||||
)
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
list(APPEND BACKEND_TEST_SRC test/test_WebGPUAsyncTaskCounter.cpp)
|
||||
endif()
|
||||
if (APPLE)
|
||||
# Metal-specific tests
|
||||
list(APPEND BACKEND_TEST_SRC
|
||||
@@ -599,7 +593,6 @@ if (APPLE OR LINUX)
|
||||
spirv-cross-glsl)
|
||||
# Create input/output directories for test result images.
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/images/actual_images)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/images/diff_images)
|
||||
file(COPY test/expected_images DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/images)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
* Set to `true` to forcibly disable amortized shader compilation in the backend.
|
||||
* Currently only honored by the GL backend.
|
||||
*/
|
||||
bool disableAmortizedShaderCompile = true;
|
||||
bool disableAmortizedShaderCompile = false;
|
||||
|
||||
/**
|
||||
* Disable backend handles use-after-free checks.
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -67,7 +66,6 @@ public:
|
||||
using DescriptorBindingsInfo = utils::FixedCapacityVector<Descriptor>;
|
||||
using DescriptorSetInfo = std::array<DescriptorBindingsInfo, MAX_DESCRIPTOR_SET_COUNT>;
|
||||
using SpecializationConstantsInfo = utils::FixedCapacityVector<SpecializationConstant>;
|
||||
using MutableSpecConstantsInfo = utils::bitset8;
|
||||
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
|
||||
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
|
||||
|
||||
@@ -105,8 +103,7 @@ public:
|
||||
Program& descriptorBindings(backend::descriptor_set_t set,
|
||||
DescriptorBindingsInfo descriptorBindings) noexcept;
|
||||
|
||||
Program& specializationConstants(SpecializationConstantsInfo specConstants,
|
||||
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept;
|
||||
Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept;
|
||||
|
||||
struct PushConstant {
|
||||
utils::CString name;
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace filament::backend {
|
||||
* A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU
|
||||
* driver (backend).
|
||||
*/
|
||||
class WebGPUPlatform : public Platform {
|
||||
class WebGPUPlatform final : public Platform {
|
||||
public:
|
||||
WebGPUPlatform();
|
||||
~WebGPUPlatform() override = default;
|
||||
@@ -52,14 +52,6 @@ public:
|
||||
// either returns a valid device or panics
|
||||
[[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter);
|
||||
|
||||
struct Configuration {
|
||||
wgpu::BackendType forceBackendType = wgpu::BackendType::Undefined;
|
||||
};
|
||||
|
||||
[[nodiscard]] virtual Configuration getConfiguration() const noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
protected:
|
||||
[[nodiscard]] Driver* createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
|
||||
@@ -97,13 +97,11 @@ void CommandStream::execute(void* buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
Driver& UTILS_RESTRICT driver = mDriver;
|
||||
CommandBase* UTILS_RESTRICT base = static_cast<CommandBase*>(buffer);
|
||||
mDriver.execute([&driver, base] {
|
||||
auto& d = driver;
|
||||
auto p = base;
|
||||
while (UTILS_LIKELY(p)) {
|
||||
p = p->execute(d);
|
||||
mDriver.execute([this, buffer]() {
|
||||
Driver& UTILS_RESTRICT driver = mDriver;
|
||||
CommandBase* UTILS_RESTRICT base = static_cast<CommandBase*>(buffer);
|
||||
while (UTILS_LIKELY(base)) {
|
||||
base = base->execute(driver);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -82,19 +82,8 @@ Program& Program::attributes(AttributesInfo attributes) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants,
|
||||
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept {
|
||||
// String the two lists together.
|
||||
mSpecializationConstants = SpecializationConstantsInfo(
|
||||
specConstants.size() + mutableSpecConstants.size());
|
||||
std::uninitialized_move(specConstants.begin(), specConstants.end(),
|
||||
mSpecializationConstants.begin());
|
||||
for (uint32_t i = 0; i < mutableSpecConstants.size(); i++) {
|
||||
mSpecializationConstants[i + specConstants.size()] = SpecializationConstant {
|
||||
.id = i + firstMutableId,
|
||||
.value = mutableSpecConstants[i],
|
||||
};
|
||||
}
|
||||
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants) noexcept {
|
||||
mSpecializationConstants = std::move(specConstants);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,10 +118,6 @@ MetalSwapChain::MetalSwapChain(
|
||||
layerDrawableMutex(std::make_shared<std::mutex>()),
|
||||
type(SwapChainType::CAMETALLAYER) {
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION([nativeWindow isKindOfClass:[CAMetalLayer class]])
|
||||
<< "nativeWindow pointer of class "
|
||||
<< [NSStringFromClass([nativeWindow class]) UTF8String] << " is not a CAMetalLayer";
|
||||
|
||||
if (!(flags & SwapChain::CONFIG_TRANSPARENT) && !nativeWindow.opaque) {
|
||||
LOG(WARNING) << "Warning: Filament SwapChain has no CONFIG_TRANSPARENT flag, but the "
|
||||
"CAMetaLayer("
|
||||
|
||||
@@ -452,7 +452,7 @@ void VulkanDriver::updateDescriptorSetTexture(
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
|
||||
if (UTILS_UNLIKELY(mExternalImageManager.isExternallySampledTexture(texture))) {
|
||||
if (mExternalImageManager.isExternallySampledTexture(texture)) {
|
||||
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
|
||||
mAppState.hasBoundExternalImages = true;
|
||||
} else {
|
||||
@@ -1939,18 +1939,6 @@ void VulkanDriver::bindDescriptorSet(
|
||||
if (dsh) {
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
mDescriptorSetCache.bind(setIndex, set, std::move(offsets));
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
auto const& bindInDrawBundle = mPipelineState.bindInDraw.second;
|
||||
// The set index being bound has already been bound or will be bound. If it's already
|
||||
// been bound and this set has external samplers, we do the doBindindraw block in
|
||||
// draw2() again. Because this set might potentially cause a new pipelineLayout
|
||||
// (therefore pipeline) to be bound.
|
||||
if (bindInDrawBundle.descriptorSetMask[setIndex] &&
|
||||
mExternalImageManager.hasExternalSampler(set)) {
|
||||
mPipelineState.bindInDraw.first = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mDescriptorSetCache.unbind(setIndex);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ fvkutils::DescriptorSetMask VulkanExternalImageManager::prepareBindSets(LayoutAr
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::hasExternalSampler(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set) const {
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
|
||||
auto itr = std::find_if(mSetBindings.begin(), mSetBindings.end(),
|
||||
[&](SetBindingInfo const& info) { return info.set == set; });
|
||||
return itr != mSetBindings.end();
|
||||
@@ -191,11 +191,9 @@ void VulkanExternalImageManager::updateSetAndLayout(
|
||||
VkDescriptorSet const srcSet = oldSet != VK_NULL_HANDLE ? oldSet : set->getVkSet();
|
||||
copySet(mPlatform->getDevice(), srcSet, newSet, copyBindings);
|
||||
|
||||
set->setExternalSamplerVkSet(newSet,
|
||||
[&descriptorSetCache = mDescriptorSetCache, layoutCount = layout->count, newLayout,
|
||||
newSet](VulkanDescriptorSet*) {
|
||||
descriptorSetCache->manualRecycle(layoutCount, newLayout, newSet);
|
||||
});
|
||||
set->setExternalSamplerVkSet(newSet, [&](VulkanDescriptorSet*) {
|
||||
mDescriptorSetCache->manualRecycle(layout->count, newLayout, newSet);
|
||||
});
|
||||
if (oldLayout != newLayout) {
|
||||
layout->setExternalSamplerVkLayout(newLayout);
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ public:
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
bool hasExternalSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set) const;
|
||||
|
||||
private:
|
||||
bool hasExternalSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
||||
|
||||
void updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
|
||||
@@ -46,13 +46,17 @@ fvkmemory::resource_ptr<VulkanTimerQuery> VulkanQueryManager::getNextQuery(
|
||||
return {};
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
std::pair<uint32_t, uint32_t> queryIndices;
|
||||
size_t const firstUnused = unused.firstSetBit();
|
||||
{
|
||||
unused.forEachSetBit([&](size_t index) {
|
||||
if (found) {
|
||||
return;
|
||||
}
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
mUsed.set(firstUnused);
|
||||
queryIndices = { firstUnused * 2, firstUnused * 2 + 1 };
|
||||
}
|
||||
mUsed.set(index);
|
||||
found = true;
|
||||
queryIndices = std::make_pair(index * 2, index * 2 + 1);
|
||||
});
|
||||
return fvkmemory::resource_ptr<VulkanTimerQuery>::construct(resourceManager, queryIndices.first,
|
||||
queryIndices.second);
|
||||
}
|
||||
|
||||
@@ -677,11 +677,11 @@ wgpu::BindGroupLayout WebGPUBlitter::createTextureBindGroupLayout(const SamplerM
|
||||
.texture = {
|
||||
.sampleType = depthSource
|
||||
? wgpu::TextureSampleType::Depth
|
||||
: (multisampledSource
|
||||
? wgpu::TextureSampleType::UnfilterableFloat
|
||||
: wgpu::TextureSampleType::Float), // only F32 scalar sample
|
||||
// type supported for now
|
||||
// (aside from depth)
|
||||
: wgpu::TextureSampleType::Float, // only F32 scalar sample
|
||||
// type supported for now
|
||||
// (aside from depth)
|
||||
// see assumptions listed
|
||||
// in blit function
|
||||
.viewDimension = sourceDimension,
|
||||
.multisampled = multisampledSource,
|
||||
},
|
||||
@@ -758,7 +758,7 @@ wgpu::ShaderModule WebGPUBlitter::createShaderModule(
|
||||
}
|
||||
} else {
|
||||
if (multisampledSource) {
|
||||
textureType = "texture_multisampled_2d<f32>";
|
||||
textureType = "texture_multisampled_2d";
|
||||
} else {
|
||||
if (sourceDimension == wgpu::TextureViewDimension::e3D) {
|
||||
textureType = "texture_3d<f32>";
|
||||
|
||||
@@ -49,16 +49,8 @@ constexpr size_t FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS = 4;
|
||||
// textures, samplers, and buffers,...), which would otherwise spam the logs.
|
||||
#define FWGPU_DEBUG_BIND_GROUPS 0x00000020
|
||||
|
||||
// Enables Android systrace
|
||||
#define FWGPU_DEBUG_SYSTRACE 0x00000040
|
||||
|
||||
// Enable a minimal set of traces to assess the performance of the backend.
|
||||
// All other debug features must be disabled.
|
||||
#define FWGPU_DEBUG_PROFILING 0x00000080
|
||||
|
||||
// Useful default combinations
|
||||
#define FWGPU_DEBUG_EVERYTHING (0xFFFFFFFF & ~FWGPU_DEBUG_PROFILING)
|
||||
#define FWGPU_DEBUG_PERFORMANCE FWGPU_DEBUG_SYSTRACE
|
||||
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF
|
||||
|
||||
#if defined(FILAMENT_BACKEND_DEBUG_FLAG)
|
||||
#define FWGPU_DEBUG_FORWARDED_FLAG (FILAMENT_BACKEND_DEBUG_FLAG & FWGPU_DEBUG_EVERYTHING)
|
||||
@@ -67,53 +59,13 @@ constexpr size_t FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS = 4;
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FWGPU_DEBUG_FLAGS (FWGPU_DEBUG_PERFORMANCE | FWGPU_DEBUG_FORWARDED_FLAG)
|
||||
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_FORWARDED_FLAG
|
||||
#else
|
||||
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_SYSTRACE
|
||||
#endif
|
||||
|
||||
// Override the debug flags if we are forcing profiling mode
|
||||
#if defined(FILAMENT_FORCE_PROFILING_MODE)
|
||||
#undef FWGPU_DEBUG_FLAGS
|
||||
#define FWGPU_DEBUG_FLAGS (FWGPU_DEBUG_PROFILING)
|
||||
#define FWGPU_DEBUG_FLAGS 0
|
||||
#endif
|
||||
|
||||
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_PROFILING) && FWGPU_DEBUG_FLAGS != FWGPU_DEBUG_PROFILING
|
||||
#error PROFILING is exclusive; all other debug features must be disabled.
|
||||
#endif
|
||||
|
||||
#if FWGPU_DEBUG_FLAGS == FWGPU_DEBUG_PROFILING
|
||||
|
||||
#ifndef NDEBUG
|
||||
#error PROFILING is meaningless in DEBUG mode.
|
||||
#endif
|
||||
|
||||
#define FWGPU_SYSTRACE_CONTEXT()
|
||||
#define FWGPU_SYSTRACE_START(marker)
|
||||
#define FWGPU_SYSTRACE_END()
|
||||
#define FWGPU_SYSTRACE_SCOPE()
|
||||
#define FWGPU_PROFILE_MARKER(marker) PROFILE_SCOPE(marker)
|
||||
|
||||
#elif FWGPU_ENABLED(FWGPU_DEBUG_SYSTRACE)
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#define FWGPU_SYSTRACE_CONTEXT() FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FWGPU_SYSTRACE_START(marker) FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, marker)
|
||||
#define FWGPU_SYSTRACE_END() FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FWGPU_SYSTRACE_SCOPE() FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FWGPU_PROFILE_MARKER(marker) FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
|
||||
#else
|
||||
#define FWGPU_SYSTRACE_CONTEXT()
|
||||
#define FWGPU_SYSTRACE_START(marker)
|
||||
#define FWGPU_SYSTRACE_END()
|
||||
#define FWGPU_SYSTRACE_SCOPE()
|
||||
#define FWGPU_PROFILE_MARKER(marker)
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
|
||||
#define FWGPU_LOGI LOG(INFO)
|
||||
#define FWGPU_LOGD FWGPU_LOGI
|
||||
|
||||
@@ -32,12 +32,10 @@
|
||||
#include "WebGPUTextureHelpers.h"
|
||||
#include "WebGPUVertexBuffer.h"
|
||||
#include "WebGPUVertexBufferInfo.h"
|
||||
#include "webgpu/utils/AsyncTaskCounter.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DriverBase.h"
|
||||
#include "SystraceProfile.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
@@ -149,7 +147,6 @@ void WebGPUDriver::tick(int) {
|
||||
|
||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FWGPU_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||
}
|
||||
|
||||
void WebGPUDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
|
||||
@@ -166,7 +163,6 @@ void WebGPUDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
|
||||
FWGPU_PROFILE_MARKER(PROFILE_NAME_ENDFRAME);
|
||||
mPipelineLayoutCache.onFrameEnd();
|
||||
mPipelineCache.onFrameEnd();
|
||||
|
||||
@@ -180,7 +176,6 @@ void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
|
||||
// A new command encoder is created fresh after it.
|
||||
// If a command encoder is not in flight (nullptr), then this is a noop.
|
||||
void WebGPUDriver::flush(int /* dummy */) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
if (mCommandEncoder == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -204,9 +199,10 @@ void WebGPUDriver::flush(int /* dummy */) {
|
||||
// Submits the currently recorded commands and waits for them to complete on the GPU.
|
||||
// This is a synchronous operation and should be used sparingly.
|
||||
void WebGPUDriver::finish(int /* dummy */) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
mDevice.Tick();
|
||||
mAdapter.GetInstance().ProcessEvents();
|
||||
if (mCommandEncoder == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
flush();
|
||||
// Wait for all previously submitted work to finish.
|
||||
std::mutex syncPoint;
|
||||
@@ -225,47 +221,36 @@ void WebGPUDriver::finish(int /* dummy */) {
|
||||
});
|
||||
std::unique_lock<std::mutex> lock(syncPoint);
|
||||
syncCondition.wait(lock, [&done] { return done; });
|
||||
mReadPixelMapsCounter.waitForAllToFinish();
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
if (!rph) {
|
||||
return;
|
||||
if (rph) {
|
||||
destructHandle<WebGPURenderPrimitive>(rph);
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPURenderPrimitive>(rph);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vertexBufferInfoHandle) {
|
||||
if (!vertexBufferInfoHandle) {
|
||||
return;
|
||||
if (vertexBufferInfoHandle) {
|
||||
destructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vertexBufferHandle) {
|
||||
if (!vertexBufferHandle) {
|
||||
return;
|
||||
if (vertexBufferHandle) {
|
||||
destructHandle<WebGPUVertexBuffer>(vertexBufferHandle);
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPUVertexBuffer>(vertexBufferHandle);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle) {
|
||||
if (!indexBufferHandle) {
|
||||
return;
|
||||
if (indexBufferHandle) {
|
||||
destructHandle<WebGPUIndexBuffer>(indexBufferHandle);
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPUIndexBuffer>(indexBufferHandle);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> bufferObjectHandle) {
|
||||
if (!bufferObjectHandle) {
|
||||
return;
|
||||
if (bufferObjectHandle) {
|
||||
destructHandle<WebGPUBufferObject>(bufferObjectHandle);
|
||||
}
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
destructHandle<WebGPUBufferObject>(bufferObjectHandle);
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyTexture(Handle<HwTexture> textureHandle) {
|
||||
@@ -434,10 +419,9 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
// associated with a handle that was previously allocated by an S-method.
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
const uint64_t flags) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
mNativeWindow = nativeWindow;
|
||||
assert_invariant(!mSwapChain);
|
||||
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
|
||||
|
||||
wgpu::Extent2D extent = mPlatform.getSurfaceExtent(mNativeWindow);
|
||||
@@ -472,14 +456,12 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
|
||||
|
||||
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
|
||||
const uint8_t bufferCount, const uint8_t attributeCount, const AttributeArray attributes) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle, bufferCount, attributeCount,
|
||||
attributes, mDeviceLimits);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle,
|
||||
const uint32_t vertexCount, Handle<HwVertexBufferInfo> vertexBufferInfoHandle) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto vertexBufferInfo = handleCast<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
|
||||
constructHandle<WebGPUVertexBuffer>(vertexBufferHandle, vertexCount,
|
||||
vertexBufferInfo->bufferCount, vertexBufferInfoHandle);
|
||||
@@ -487,14 +469,12 @@ void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle
|
||||
|
||||
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
const ElementType elementType, const uint32_t indexCount, const BufferUsage usage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
|
||||
constructHandle<WebGPUIndexBuffer>(indexBufferHandle, mDevice, elementSize, indexCount);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle,
|
||||
const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUBufferObject>(bufferObjectHandle, mDevice, bindingType, byteCount);
|
||||
}
|
||||
|
||||
@@ -502,7 +482,6 @@ void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const Sampler
|
||||
const uint8_t levels, const TextureFormat format, const uint8_t samples,
|
||||
const uint32_t width, const uint32_t height, const uint32_t depth,
|
||||
const TextureUsage usage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUTexture>(textureHandle, target, levels, format, samples, width, height,
|
||||
depth, usage, mDevice);
|
||||
}
|
||||
@@ -518,47 +497,13 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
|
||||
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle r,
|
||||
const backend::TextureSwizzle g, const backend::TextureSwizzle b,
|
||||
const backend::TextureSwizzle a) {
|
||||
|
||||
if (!isTextureSwizzleSupported()) {
|
||||
FWGPU_LOGW << "WebGPUDriver::createTextureViewSwizzleR called while texture swizzling is "
|
||||
"not supported by the device/driver";
|
||||
}
|
||||
|
||||
auto sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
|
||||
assert_invariant(sourceTexture);
|
||||
|
||||
wgpu::TextureComponentSwizzle swizzle
|
||||
{
|
||||
.r = toWGPUComponentSwizzle(r),
|
||||
.g = toWGPUComponentSwizzle(g),
|
||||
.b = toWGPUComponentSwizzle(b),
|
||||
.a = toWGPUComponentSwizzle(a),
|
||||
};
|
||||
|
||||
wgpu::TextureComponentSwizzleDescriptor swizzleDesc {};
|
||||
swizzleDesc.swizzle = swizzle;
|
||||
|
||||
const wgpu::TextureViewDescriptor viewDesc {
|
||||
.nextInChain = &swizzleDesc,
|
||||
.label = "swizzled_texture_view",
|
||||
.format = sourceTexture->getTexture().GetFormat(),
|
||||
.dimension = sourceTexture->getViewDimension(),
|
||||
.baseMipLevel = 0,
|
||||
.mipLevelCount = sourceTexture->getTexture().GetMipLevelCount(),
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = sourceTexture->getTexture().GetDepthOrArrayLayers(),
|
||||
};
|
||||
|
||||
wgpu::TextureView swizzledView{ sourceTexture->getTexture().CreateView(&viewDesc) };
|
||||
FILAMENT_CHECK_POSTCONDITION(swizzledView) << "Failed to create swizzled Texture view";
|
||||
constructHandle<WebGPUTexture>(textureHandle, sourceTexture, swizzledView);
|
||||
PANIC_POSTCONDITION("Swizzle WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
|
||||
const backend::SamplerType target, const backend::TextureFormat format,
|
||||
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
@@ -566,7 +511,6 @@ void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> textureHandle,
|
||||
const backend::SamplerType target, const backend::TextureFormat format,
|
||||
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
|
||||
void* externalImage) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
@@ -586,7 +530,6 @@ void WebGPUDriver::importTextureR(Handle<HwTexture> textureHandle, const intptr_
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimitiveHandle,
|
||||
Handle<HwVertexBuffer> vertexBufferHandle, Handle<HwIndexBuffer> indexBufferHandle,
|
||||
const PrimitiveType primitiveType) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
assert_invariant(mDevice);
|
||||
const auto renderPrimitive = constructHandle<WebGPURenderPrimitive>(renderPrimitiveHandle);
|
||||
const auto vertexBuffer = handleCast<WebGPUVertexBuffer>(vertexBufferHandle);
|
||||
@@ -597,7 +540,6 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimit
|
||||
}
|
||||
|
||||
void WebGPUDriver::createProgramR(Handle<HwProgram> programHandle, Program&& program) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUProgram>(programHandle, mDevice, program);
|
||||
}
|
||||
|
||||
@@ -615,7 +557,6 @@ void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle
|
||||
const TargetBufferFlags targetFlags, const uint32_t width, const uint32_t height,
|
||||
const uint8_t samples, const uint8_t layerCount, const MRT color,
|
||||
const TargetBufferInfo depth, const TargetBufferInfo stencil) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPURenderTarget>(
|
||||
renderTargetHandle, width, height, samples, layerCount, color, depth, stencil,
|
||||
targetFlags,
|
||||
@@ -637,13 +578,11 @@ void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
||||
void WebGPUDriver::createDescriptorSetLayoutR(
|
||||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle,
|
||||
backend::DescriptorSetLayout&& info) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle, std::move(info), mDevice);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> descriptorSetHandle,
|
||||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
auto layout = handleCast<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle);
|
||||
constructHandle<WebGPUDescriptorSet>(descriptorSetHandle, layout->getLayout(),
|
||||
layout->getBindGroupEntries());
|
||||
@@ -703,7 +642,7 @@ bool WebGPUDriver::isTextureFormatSupported(const TextureFormat format) {
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureSwizzleSupported() {
|
||||
return mDevice.HasFeature(wgpu::FeatureName::TextureComponentSwizzle);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureFormatMipmappable(const TextureFormat format) {
|
||||
@@ -985,7 +924,7 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
|
||||
<< "Failed to create staging input texture for blit?";
|
||||
const auto copyInfo{ wgpu::TexelCopyTextureInfo{
|
||||
.texture = stagingTexture,
|
||||
.mipLevel = 0,
|
||||
.mipLevel = level,
|
||||
.origin = { .x = 0, .y = 0, .z = 0 },
|
||||
.aspect = texture->getAspect(),
|
||||
} };
|
||||
@@ -1040,6 +979,7 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
|
||||
mQueue.Submit(1, &blitCommand);
|
||||
mCommandEncoder = nullptr;
|
||||
}
|
||||
stagingTexture.Destroy();
|
||||
} else {
|
||||
// Direct copy without a blit.
|
||||
const auto copyInfo { wgpu::TexelCopyTextureInfo{
|
||||
@@ -1159,7 +1099,6 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
// resolve targets if necessary.
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
RenderPassParams const& params) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
if (!mCommandEncoder) {
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor{ .label = "frame_command_encoder" };
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
@@ -1313,7 +1252,6 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
}
|
||||
|
||||
void WebGPUDriver::endRenderPass(int /* dummy */) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
mRenderPassEncoder.End();
|
||||
mRenderPassEncoder = nullptr;
|
||||
}
|
||||
@@ -1322,12 +1260,10 @@ void WebGPUDriver::nextSubpass(int) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSwapChain,
|
||||
Handle<HwSwapChain> readSwapChain) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
|
||||
"WebGPU driver does not support distinct draw/read swap chains.");
|
||||
auto swapChain = handleCast<WebGPUSwapChain>(drawSwapChain);
|
||||
auto swapChain = handleCast<WebGPUSwapChain>(drawSch);
|
||||
mSwapChain = swapChain;
|
||||
assert_invariant(mSwapChain);
|
||||
|
||||
@@ -1340,16 +1276,13 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSwapChain,
|
||||
assert_invariant(mTextureView);
|
||||
|
||||
if (!mCommandEncoder) {
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
|
||||
.label = "frame_command_encoder"
|
||||
};
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = { .label = "frame_command_encoder" };
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
}
|
||||
assert_invariant(mCommandEncoder);
|
||||
}
|
||||
|
||||
void WebGPUDriver::commit(Handle<HwSwapChain> swapChainHandle) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
||||
if (UTILS_VERY_UNLIKELY(!mCommandEncoder)) {
|
||||
// nothing to submit
|
||||
if (UTILS_LIKELY(mSwapChain)) {
|
||||
@@ -1400,20 +1333,18 @@ void WebGPUDriver::insertEventMarker(char const* string) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::pushGroupMarker(char const* string) {
|
||||
FWGPU_SYSTRACE_CONTEXT();
|
||||
FWGPU_SYSTRACE_START(string);
|
||||
}
|
||||
|
||||
void WebGPUDriver::popGroupMarker(int /* dummy */) {
|
||||
FWGPU_SYSTRACE_CONTEXT();
|
||||
FWGPU_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void WebGPUDriver::startCapture(int /* dummy */) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::stopCapture(int /* dummy */) {
|
||||
void WebGPUDriver::popGroupMarker(int) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::startCapture(int) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::stopCapture(int) {
|
||||
//todo
|
||||
}
|
||||
|
||||
@@ -1539,7 +1470,7 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, c
|
||||
.size = bufferSize,
|
||||
};
|
||||
|
||||
wgpu::Buffer stagingBuffer{ mDevice.CreateBuffer(&bufferDesc) };
|
||||
const wgpu::Buffer stagingBuffer{ mDevice.CreateBuffer(&bufferDesc) };
|
||||
assert_invariant(stagingBuffer);
|
||||
|
||||
// WebGPU's texture coordinates for copies are top-left, but Filament's y-coordinate is
|
||||
@@ -1590,7 +1521,6 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, c
|
||||
.driver = this,
|
||||
});
|
||||
|
||||
mReadPixelMapsCounter.startTask();
|
||||
userData->buffer.MapAsync(
|
||||
wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowSpontaneous,
|
||||
[](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
|
||||
@@ -1614,7 +1544,6 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, c
|
||||
FWGPU_LOGE << "Failed to map staging buffer for readPixels: " << message;
|
||||
}
|
||||
data->driver->scheduleDestroy(std::move(data->pixelBufferDescriptor));
|
||||
data->driver->mReadPixelMapsCounter.finishTask();
|
||||
},
|
||||
userData.release());
|
||||
}
|
||||
@@ -1629,93 +1558,12 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> destinationRenderTargetHandle, const Viewport destinationViewport,
|
||||
Handle<HwRenderTarget> sourceRenderTargetHandle, const Viewport sourceViewport,
|
||||
const SamplerMagFilter filter) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
auto const sourceTarget{ handleCast<WebGPURenderTarget>(sourceRenderTargetHandle) };
|
||||
auto const destinationTarget{ handleCast<WebGPURenderTarget>(destinationRenderTargetHandle) };
|
||||
assert_invariant(sourceTarget && destinationTarget);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(buffers == TargetBufferFlags::COLOR0)
|
||||
<< "blitDEPRECATED only supports COLOR0";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(sourceViewport.left >= 0 && sourceViewport.bottom >= 0 &&
|
||||
destinationViewport.left >= 0 && destinationViewport.bottom >= 0)
|
||||
<< "Source and destination viewports must be positive.";
|
||||
|
||||
// We always blit from/to the COLOR0 attachment.
|
||||
auto sourceAttachment {sourceTarget->getColorAttachmentInfos()[0]};
|
||||
Handle<HwTexture> sourceTextureHandle{ sourceAttachment.handle };
|
||||
auto destinationAttachment {destinationTarget->getColorAttachmentInfos()[0]};
|
||||
Handle<HwTexture> destinationTextureHandle{
|
||||
destinationAttachment.handle
|
||||
};
|
||||
|
||||
if (UTILS_UNLIKELY(!sourceTextureHandle || !destinationTextureHandle)) {
|
||||
FWGPU_LOGE << "blitDEPRECATED could not find a valid color attachment to blit.";
|
||||
return;
|
||||
}
|
||||
|
||||
// WebGPU's texture coordinates are top-left, while Filament's are bottom-left.
|
||||
// We need to flip the y-coordinate for both source and destination.
|
||||
auto const sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
|
||||
auto const destinationTexture{ handleCast<WebGPUTexture>(destinationTextureHandle) };
|
||||
|
||||
const uint32_t sourceTextureHeight{ sourceTexture->height };
|
||||
const uint32_t flippedSourceY{ sourceTextureHeight - sourceViewport.bottom -
|
||||
sourceViewport.height };
|
||||
|
||||
const uint32_t destinationTextureHeight{ destinationTexture->height };
|
||||
const uint32_t flippedDestinationY{ destinationTextureHeight - destinationViewport.bottom -
|
||||
destinationViewport.height };
|
||||
|
||||
const wgpu::Origin2D sourceOrigin{ static_cast<uint32_t>(sourceViewport.left), flippedSourceY };
|
||||
const wgpu::Origin2D destinationOrigin{ static_cast<uint32_t>(destinationViewport.left),
|
||||
flippedDestinationY };
|
||||
|
||||
const wgpu::Extent2D sourceSize{ static_cast<uint32_t>(sourceViewport.width),
|
||||
static_cast<uint32_t>(sourceViewport.height) };
|
||||
const wgpu::Extent2D destinationSize{ static_cast<uint32_t>(destinationViewport.width),
|
||||
static_cast<uint32_t>(destinationViewport.height) };
|
||||
|
||||
bool reusedCommandEncoder{ true };
|
||||
if (mCommandEncoder) {
|
||||
flush();
|
||||
} else {
|
||||
reusedCommandEncoder = false;
|
||||
const wgpu::CommandEncoderDescriptor desc{ .label = "blit_deprecated_command" };
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&desc);
|
||||
}
|
||||
|
||||
const WebGPUBlitter::BlitArgs blitArgs{
|
||||
.source = { .texture = sourceTexture->getTexture(),
|
||||
.aspect = sourceTexture->getAspect(),
|
||||
.origin = sourceOrigin,
|
||||
.extent = sourceSize,
|
||||
.mipLevel = sourceAttachment.level,
|
||||
.layerOrDepth = sourceAttachment.layer,
|
||||
},
|
||||
.destination = { .texture = destinationTexture->getTexture(),
|
||||
.aspect = destinationTexture->getAspect(),
|
||||
.origin = destinationOrigin,
|
||||
.extent = destinationSize,
|
||||
.mipLevel = destinationAttachment.level,
|
||||
.layerOrDepth = destinationAttachment.layer,
|
||||
},
|
||||
.filter = filter,
|
||||
};
|
||||
mBlitter.blit(mQueue, mCommandEncoder, blitArgs);
|
||||
|
||||
if (!reusedCommandEncoder) {
|
||||
const wgpu::CommandBufferDescriptor desc{ .label = "blit_deprecated_command_buffer" };
|
||||
const wgpu::CommandBuffer blitCommand{ mCommandEncoder.Finish(&desc) };
|
||||
mQueue.Submit(1, &blitCommand);
|
||||
mCommandEncoder = nullptr;
|
||||
}
|
||||
PANIC_PRECONDITION("WebGPUDriver::blitDEPRECATED not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::resolve(Handle<HwTexture> destinationTextureHandle, const uint8_t sourceLevel,
|
||||
const uint8_t sourceLayer, Handle<HwTexture> sourceTextureHandle,
|
||||
const uint8_t destinationLevel, const uint8_t destinationLayer) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
if (!mCommandEncoder) {
|
||||
const wgpu::CommandEncoderDescriptor encoderDescriptor{
|
||||
.label = "command_created_with_resolve",
|
||||
@@ -1753,7 +1601,6 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
|
||||
const uint8_t sourceLayer, const math::uint2 destinationOrigin,
|
||||
Handle<HwTexture> sourceTextureHandle, const uint8_t destinationLevel,
|
||||
const uint8_t destinationLayer, const math::uint2 sourceOrigin, const math::uint2 size) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
bool reusedCommandEncoder{ true };
|
||||
if (mCommandEncoder) {
|
||||
// make sure command elements (draws, etc.) prior to this blit are processed before the blit
|
||||
@@ -1812,7 +1659,6 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
assert_invariant(mRenderPassEncoder);
|
||||
const auto program{ handleCast<WebGPUProgram>(pipelineState.program) };
|
||||
assert_invariant(program);
|
||||
@@ -1901,7 +1747,6 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> renderPrimitiveHandle) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto renderPrimitive = handleCast<WebGPURenderPrimitive>(renderPrimitiveHandle);
|
||||
const auto vertexBufferInfo = handleCast<WebGPUVertexBufferInfo>(
|
||||
renderPrimitive->vertexBuffer->getVertexBufferInfoHandle());
|
||||
@@ -1919,7 +1764,6 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> renderPrimitive
|
||||
|
||||
void WebGPUDriver::draw2(const uint32_t indexOffset, const uint32_t indexCount,
|
||||
const uint32_t instanceCount) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
// Bind groups are deferred until the actual draw call.
|
||||
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
|
||||
auto& binding = mCurrentDescriptorSets[i];
|
||||
@@ -1966,7 +1810,6 @@ void WebGPUDriver::resetState(int) {
|
||||
void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> descriptorSetHandle,
|
||||
const backend::descriptor_binding_t binding, Handle<HwBufferObject> bufferObjectHandle,
|
||||
const uint32_t offset, const uint32_t size) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto bindGroup = handleCast<WebGPUDescriptorSet>(descriptorSetHandle);
|
||||
const auto buffer = handleCast<WebGPUBufferObject>(bufferObjectHandle);
|
||||
if (!bindGroup->getIsLocked()) {
|
||||
@@ -1987,7 +1830,6 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> descriptorS
|
||||
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> descriptorSetHandle,
|
||||
const backend::descriptor_binding_t binding, Handle<HwTexture> textureHandle,
|
||||
const SamplerParams params) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
auto bindGroup = handleCast<WebGPUDescriptorSet>(descriptorSetHandle);
|
||||
auto texture = handleCast<WebGPUTexture>(textureHandle);
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "webgpu/WebGPUPipelineCache.h"
|
||||
#include "webgpu/WebGPUPipelineLayoutCache.h"
|
||||
#include "webgpu/WebGPURenderPassMipmapGenerator.h"
|
||||
#include "webgpu/utils/AsyncTaskCounter.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
@@ -91,7 +90,6 @@ private:
|
||||
spd::MipmapGenerator mSpdComputePassMipmapGenerator;
|
||||
WebGPUMsaaTextureResolver mMsaaTextureResolver{};
|
||||
WebGPUBlitter mBlitter;
|
||||
webgpuutils::AsyncTaskCounter mReadPixelMapsCounter{};
|
||||
|
||||
struct DescriptorSetBindingInfo{
|
||||
wgpu::BindGroup bindGroup;
|
||||
|
||||
@@ -372,7 +372,6 @@ bool WebGPUPipelineCache::RenderPipelineKeyEqual::operator()(RenderPipelineKey c
|
||||
}
|
||||
|
||||
void WebGPUPipelineCache::removeExpiredPipelines() {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
using Iterator = decltype(mRenderPipelines)::const_iterator;
|
||||
for (Iterator iterator{ mRenderPipelines.begin() }; iterator != mRenderPipelines.end();) {
|
||||
RenderPipelineCacheEntry const& entry{ iterator.value() };
|
||||
|
||||
@@ -89,7 +89,6 @@ bool WebGPUPipelineLayoutCache::PipelineLayoutKeyEqual::operator()(PipelineLayou
|
||||
}
|
||||
|
||||
void WebGPUPipelineLayoutCache::removeExpiredPipelineLayouts() {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
using Iterator = decltype(mPipelineLayouts)::const_iterator;
|
||||
for (Iterator iterator{ mPipelineLayouts.begin() }; iterator != mPipelineLayouts.end();) {
|
||||
PipelineLayoutCacheEntry const& entry{ iterator.value() };
|
||||
|
||||
@@ -100,6 +100,9 @@ namespace {
|
||||
sampleCountPerAttachment = texture->samples;
|
||||
firstAttachment = false;
|
||||
}
|
||||
FILAMENT_CHECK_PRECONDITION(texture->samples == sampleCountPerAttachment)
|
||||
<< target.name << " attachment texture has " << +texture->samples
|
||||
<< " samples, but the other attachment(s) have " << +sampleCountPerAttachment << ".";
|
||||
if (renderTargetSampleCount > 1 && sampleCountPerAttachment == 1) {
|
||||
texture->createMsaaSidecarTextureIfNotAlreadyCreated(renderTargetSampleCount,
|
||||
device);
|
||||
|
||||
@@ -107,6 +107,84 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fUsage Filament's requested texture usage
|
||||
* @param samples How many samples to use for MSAA
|
||||
* @param needsComputeStorageSupport if we need to use this texture as storage binding in something
|
||||
* like a compute shader
|
||||
* @param needsRenderAttachmentSupport if we need to use this texture as a render pass attachment
|
||||
* in something like a render pass blit (e.g. mipmap generation)
|
||||
* @return The appropriate texture usage flags for the underlying texture
|
||||
*/
|
||||
[[nodiscard]] wgpu::TextureUsage fToWGPUTextureUsage(TextureUsage const& fUsage,
|
||||
const uint8_t samples, const bool needsComputeStorageSupport,
|
||||
const bool needsRenderAttachmentSupport) {
|
||||
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
|
||||
|
||||
// if needsComputeStorageSupport we need to read and write to the texture in a shader and thus
|
||||
// require CopySrc, CopyDst, TextureBinding, & StorageBinding
|
||||
|
||||
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
|
||||
// TODO Validate assumptions, revisit if issues.
|
||||
if (needsComputeStorageSupport || any(TextureUsage::BLIT_SRC & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopySrc;
|
||||
}
|
||||
if (needsComputeStorageSupport ||
|
||||
any((TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE) & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopyDst;
|
||||
}
|
||||
if (needsComputeStorageSupport || any(TextureUsage::SAMPLEABLE & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::TextureBinding;
|
||||
}
|
||||
if (needsComputeStorageSupport) {
|
||||
retUsage |= wgpu::TextureUsage::StorageBinding;
|
||||
}
|
||||
if (needsRenderAttachmentSupport) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
if (any(TextureUsage::BLIT_SRC & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::TextureBinding;
|
||||
}
|
||||
if (any(TextureUsage::BLIT_DST & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
if (any(TextureUsage::GEN_MIPMAPPABLE & fUsage)) {
|
||||
retUsage |= (wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding);
|
||||
}
|
||||
// WGPU Render attachment covers either color or stencil situation dependant
|
||||
// NOTE: Depth attachment isn't used this way in Vulkan but logically maps to WGPU docs. If
|
||||
// issues, investigate here.
|
||||
if (any((TextureUsage::COLOR_ATTACHMENT | TextureUsage::STENCIL_ATTACHMENT |
|
||||
TextureUsage::DEPTH_ATTACHMENT) &
|
||||
fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
|
||||
// This is from Vulkan logic- if there are any issues try disabling this first, allows perf
|
||||
// benefit though
|
||||
const bool useTransientAttachment =
|
||||
// Usage consists of attachment flags only.
|
||||
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(fUsage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
if (useTransientAttachment) {
|
||||
retUsage |= wgpu::TextureUsage::TransientAttachment;
|
||||
}
|
||||
// NOTE: Unused wgpu flags:
|
||||
// StorageAttachment
|
||||
|
||||
// NOTE: Unused Filament flags:
|
||||
// SUBPASS_INPUT VK goes to input attachment which we don't support right now
|
||||
// PROTECTED
|
||||
return retUsage;
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::TextureAspect fToWGPUTextureViewAspect(TextureUsage const& fUsage,
|
||||
TextureFormat const& fFormat) {
|
||||
|
||||
@@ -208,11 +286,9 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
|
||||
mAspect{ fToWGPUTextureViewAspect(usage, format) },
|
||||
mWebGPUUsage{ fToWGPUTextureUsage(usage, samples,
|
||||
mMipmapGenerationStrategy == MipmapGenerationStrategy::SPD_COMPUTE_PASS,
|
||||
mMipmapGenerationStrategy == MipmapGenerationStrategy::RENDER_PASS,
|
||||
device.HasFeature(wgpu::FeatureName::TransientAttachments)) },
|
||||
mViewUsage{ fToWGPUTextureUsage(usage, samples, false, false,
|
||||
device.HasFeature(wgpu::FeatureName::TransientAttachments)) },
|
||||
mDimension{ toWebGPUTextureViewDimension(samplerType) },
|
||||
mMipmapGenerationStrategy == MipmapGenerationStrategy::RENDER_PASS) },
|
||||
mViewUsage{ fToWGPUTextureUsage(usage, samples, false, false) },
|
||||
mDimension{toWebGPUTextureViewDimension(samplerType)},
|
||||
mBlockWidth{ filament::backend::getBlockWidth(format) },
|
||||
mBlockHeight{ filament::backend::getBlockHeight(format) },
|
||||
mDefaultMipLevel{ 0 },
|
||||
@@ -283,24 +359,6 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
|
||||
toWebGPUTextureViewDimension(src->target)) },
|
||||
mMsaaSidecarTexture{ src->mMsaaSidecarTexture } {}
|
||||
|
||||
WebGPUTexture::WebGPUTexture(const WebGPUTexture* src, const wgpu::TextureView view) noexcept
|
||||
: HwTexture{ src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage},
|
||||
mViewFormat{ src->mViewFormat },
|
||||
mMipmapGenerationStrategy{ src->mMipmapGenerationStrategy },
|
||||
mWebGPUFormat{ src->mWebGPUFormat },
|
||||
mAspect{ src->mAspect },
|
||||
mWebGPUUsage{ src->mWebGPUUsage },
|
||||
mViewUsage{ src->mViewUsage },
|
||||
mBlockWidth{ src->mBlockWidth },
|
||||
mBlockHeight{ src->mBlockHeight },
|
||||
mArrayLayerCount{ src->mArrayLayerCount },
|
||||
mTexture{ src->mTexture },
|
||||
mDefaultMipLevel{ src->mDefaultMipLevel },
|
||||
mDefaultBaseArrayLayer{ 0 },
|
||||
mDefaultTextureView{ view },
|
||||
mMsaaSidecarTexture{src->mMsaaSidecarTexture}{}
|
||||
|
||||
wgpu::Texture const& WebGPUTexture::getMsaaSidecarTexture(const uint8_t sampleCount) const {
|
||||
if (mMsaaSidecarTexture == nullptr) {
|
||||
return mMsaaSidecarTexture; // nullptr (no such sidecar)
|
||||
|
||||
@@ -34,26 +34,11 @@ public:
|
||||
NONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a Filament texture and a texture view
|
||||
*/
|
||||
WebGPUTexture(SamplerType, uint8_t levels, TextureFormat, uint8_t samples, uint32_t width,
|
||||
uint32_t height, uint32_t depth, TextureUsage, wgpu::Device const&) noexcept;
|
||||
|
||||
/**
|
||||
* Creates a "Filament Texture View", where the underlying texture is the same as the source,
|
||||
* but the view elements differ
|
||||
*/
|
||||
WebGPUTexture(WebGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
|
||||
/**
|
||||
* @param textureView texture view to use for the respective texture. e.g. it can be a
|
||||
* swizzled Texture view for that filament texture
|
||||
*
|
||||
* Associates the underlying texture with the given texture view
|
||||
*/
|
||||
WebGPUTexture(WebGPUTexture const* source, wgpu::TextureView textureView) noexcept;
|
||||
|
||||
[[nodiscard]] wgpu::TextureAspect getAspect() const { return mAspect; }
|
||||
|
||||
[[nodiscard]] size_t getBlockWidth() const { return mBlockWidth; }
|
||||
|
||||
@@ -198,21 +198,81 @@ namespace filament::backend {
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT: return wgpu::TextureFormat::BC6HRGBFloat;
|
||||
case TextureFormat::RGBA_BPTC_UNORM: return wgpu::TextureFormat::BC7RGBAUnorm;
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM: return wgpu::TextureFormat::BC7RGBAUnormSrgb;
|
||||
case TextureFormat::RGB9_E5: return wgpu::TextureFormat::RGB9E5Ufloat;
|
||||
case TextureFormat::RGB8: return wgpu::TextureFormat::RGBA8Unorm;
|
||||
case TextureFormat::SRGB8: return wgpu::TextureFormat::RGBA8UnormSrgb;
|
||||
case TextureFormat::RGB8_SNORM: return wgpu::TextureFormat::RGBA8Snorm;
|
||||
case TextureFormat::RGB8UI: return wgpu::TextureFormat::RGBA8Uint;
|
||||
case TextureFormat::RGB8I: return wgpu::TextureFormat::RGBA8Sint;
|
||||
case TextureFormat::RGB565:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and discard the alpha and lower precision.
|
||||
FWGPU_LOGW << "Requested Filament texture format RGB565 but getting "
|
||||
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case TextureFormat::RGB9_E5: return wgpu::TextureFormat::RGB9E5Ufloat;
|
||||
case TextureFormat::RGB5_A1:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and handle the packing/unpacking in shaders.
|
||||
FWGPU_LOGW << "Requested Filament texture format RGB5_A1 but getting "
|
||||
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case TextureFormat::RGBA4:
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm
|
||||
// and handle the packing/unpacking in shaders.
|
||||
FWGPU_LOGW << "Requested Filament texture format RGBA4 but getting "
|
||||
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case TextureFormat::RGB8:
|
||||
FWGPU_LOGW << "Requested Filament texture format RGB8 but getting "
|
||||
"wgpu::TextureFormat::RGBA8Unorm (no direct sRGB equivalent in wgpu "
|
||||
"without alpha)";
|
||||
return wgpu::TextureFormat::RGBA8Unorm;
|
||||
case TextureFormat::SRGB8:
|
||||
FWGPU_LOGW << "Requested Filament texture format SRGB8 but getting "
|
||||
"wgpu::TextureFormat::RGBA8UnormSrgb (no direct sRGB equivalent in wgpu "
|
||||
"without alpha)";
|
||||
return wgpu::TextureFormat::RGBA8UnormSrgb;
|
||||
case TextureFormat::RGB8_SNORM:
|
||||
FWGPU_LOGW
|
||||
<< "Requested Filament texture format RGB8_SNORM but getting "
|
||||
"wgpu::TextureFormat::RGBA8Snorm (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA8Snorm;
|
||||
case TextureFormat::RGB8UI:
|
||||
FWGPU_LOGW << "Requested Filament texture format RGB8UI but getting "
|
||||
"wgpu::TextureFormat::RGBA8Uint (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA8Uint;
|
||||
case TextureFormat::RGB8I:
|
||||
FWGPU_LOGW << "Requested Filament texture format RGB8I but getting "
|
||||
"wgpu::TextureFormat::RGBA8Sint (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA8Sint;
|
||||
case TextureFormat::R11F_G11F_B10F: return wgpu::TextureFormat::RG11B10Ufloat;
|
||||
case TextureFormat::UNUSED: return wgpu::TextureFormat::Undefined;
|
||||
case TextureFormat::RGB10_A2: return wgpu::TextureFormat::RGB10A2Unorm;
|
||||
case TextureFormat::RGB16F: return wgpu::TextureFormat::RGBA16Float;
|
||||
case TextureFormat::RGB16UI: return wgpu::TextureFormat::RGBA16Uint;
|
||||
case TextureFormat::RGB16I: return wgpu::TextureFormat::RGBA16Sint;
|
||||
case TextureFormat::RGB32F: return wgpu::TextureFormat::RGBA32Float;
|
||||
case TextureFormat::RGB32UI: return wgpu::TextureFormat::RGBA32Uint;
|
||||
case TextureFormat::RGB32I: return wgpu::TextureFormat::RGBA32Sint;
|
||||
case TextureFormat::RGB16F:
|
||||
FWGPU_LOGW
|
||||
<< "Requested Filament texture format RGB16F but getting "
|
||||
"wgpu::TextureFormat::RGBA16Float (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA16Float;
|
||||
case TextureFormat::RGB16UI:
|
||||
FWGPU_LOGW
|
||||
<< "Requested Filament texture format RGB16UI but getting "
|
||||
"wgpu::TextureFormat::RGBA16Uint (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA16Uint;
|
||||
case TextureFormat::RGB16I:
|
||||
FWGPU_LOGW
|
||||
<< "Requested Filament texture format RGB16I but getting "
|
||||
"wgpu::TextureFormat::RGBA16Sint (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA16Sint;
|
||||
case TextureFormat::RGB32F:
|
||||
FWGPU_LOGW
|
||||
<< "Requested Filament texture format RGB32F but getting "
|
||||
"wgpu::TextureFormat::RGBA32Float (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA32Float;
|
||||
case TextureFormat::RGB32UI:
|
||||
FWGPU_LOGW
|
||||
<< "Requested Filament texture format RGB32UI but getting "
|
||||
"wgpu::TextureFormat::RGBA32Uint (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA32Uint;
|
||||
case TextureFormat::RGB32I:
|
||||
FWGPU_LOGW
|
||||
<< "Requested Filament texture format RGB32I but getting "
|
||||
"wgpu::TextureFormat::RGBA32Sint (no direct mapping in wgpu without alpha)";
|
||||
return wgpu::TextureFormat::RGBA32Sint;
|
||||
case TextureFormat::DXT1_RGB: return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case TextureFormat::DXT1_RGBA: return wgpu::TextureFormat::BC1RGBAUnorm;
|
||||
case TextureFormat::DXT3_RGBA: return wgpu::TextureFormat::BC2RGBAUnorm;
|
||||
@@ -221,20 +281,6 @@ namespace filament::backend {
|
||||
case TextureFormat::DXT1_SRGBA: return wgpu::TextureFormat::BC1RGBAUnormSrgb;
|
||||
case TextureFormat::DXT3_SRGBA: return wgpu::TextureFormat::BC2RGBAUnormSrgb;
|
||||
case TextureFormat::DXT5_SRGBA: return wgpu::TextureFormat::BC3RGBAUnormSrgb;
|
||||
// No direct mapping in wgpu. Could potentially map to RGBA8Unorm and handle the
|
||||
// packing/unpacking in shaders.
|
||||
case TextureFormat::RGB565:
|
||||
FWGPU_LOGW << "Requested Filament texture format RGB565 but getting "
|
||||
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case TextureFormat::RGB5_A1:
|
||||
FWGPU_LOGW << "Requested Filament texture format RGB5_A1 but getting "
|
||||
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
case TextureFormat::RGBA4:
|
||||
FWGPU_LOGW << "Requested Filament texture format RGBA4 but getting "
|
||||
"wgpu::TextureFormat::Undefined (no direct mapping in wgpu)";
|
||||
return wgpu::TextureFormat::Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,20 +341,6 @@ namespace filament::backend {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::ComponentSwizzle toWGPUComponentSwizzle(const backend::TextureSwizzle swizzle) {
|
||||
switch (swizzle) {
|
||||
case backend::TextureSwizzle::CHANNEL_0: return wgpu::ComponentSwizzle::R;
|
||||
case backend::TextureSwizzle::CHANNEL_1: return wgpu::ComponentSwizzle::G;
|
||||
case backend::TextureSwizzle::CHANNEL_2: return wgpu::ComponentSwizzle::B;
|
||||
case backend::TextureSwizzle::CHANNEL_3: return wgpu::ComponentSwizzle::A;
|
||||
case backend::TextureSwizzle::SUBSTITUTE_ZERO: return wgpu::ComponentSwizzle::Zero;
|
||||
case backend::TextureSwizzle::SUBSTITUTE_ONE: return wgpu::ComponentSwizzle::One;
|
||||
default:
|
||||
FWGPU_LOGW << "invalid swizzle component returning wgpu::ComponentSwizzle::Undefined";
|
||||
return wgpu::ComponentSwizzle::Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if https://www.w3.org/TR/webgpu/#copy-compatible which states:
|
||||
* Two GPUTextureFormats format1 and format2 are copy-compatible if:
|
||||
@@ -518,93 +550,6 @@ namespace filament::backend {
|
||||
return source != destination && pixelDataType != PixelDataType::COMPRESSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fUsage Filament's requested texture usage
|
||||
* @param samples How many samples to use for MSAA
|
||||
* @param needsComputeStorageSupport if we need to use this texture as storage binding in something
|
||||
* like a compute shader
|
||||
* @param needsRenderAttachmentSupport if we need to use this texture as a render pass attachment
|
||||
* in something like a render pass blit (e.g. mipmap generation)
|
||||
* @param deviceSupportsTransientAttachments if the device itself supports Render Attachments
|
||||
* @return The appropriate texture usage flags for the underlying texture
|
||||
*/
|
||||
[[nodiscard]] constexpr wgpu::TextureUsage fToWGPUTextureUsage(TextureUsage const& fUsage,
|
||||
const uint8_t samples, const bool needsComputeStorageSupport,
|
||||
const bool needsRenderAttachmentSupport, const bool deviceSupportsTransientAttachments) {
|
||||
wgpu::TextureUsage retUsage{ wgpu::TextureUsage::None };
|
||||
|
||||
if (any(TextureUsage::BLIT_SRC & fUsage)) {
|
||||
retUsage |= (wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::TextureBinding);
|
||||
}
|
||||
if (any(TextureUsage::BLIT_DST & fUsage)) {
|
||||
retUsage |= (wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::RenderAttachment);
|
||||
}
|
||||
if (any(TextureUsage::UPLOADABLE & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::CopyDst;
|
||||
}
|
||||
if (any(TextureUsage::GEN_MIPMAPPABLE & fUsage)) {
|
||||
retUsage |= (wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::TextureBinding);
|
||||
}
|
||||
if (any(TextureUsage::SAMPLEABLE & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::TextureBinding;
|
||||
}
|
||||
// if needsComputeStorageSupport we need to read and write to the texture in a shader and thus
|
||||
// require CopySrc, CopyDst, TextureBinding, & StorageBinding
|
||||
if (needsComputeStorageSupport) {
|
||||
retUsage |= (wgpu::TextureUsage::StorageBinding |
|
||||
wgpu::TextureUsage::CopySrc |
|
||||
wgpu::TextureUsage::CopyDst |
|
||||
wgpu::TextureUsage::TextureBinding);
|
||||
}
|
||||
|
||||
wgpu::TextureUsage transientAttachmentNeeded{ wgpu::TextureUsage::None };
|
||||
const bool useTransientAttachment {
|
||||
deviceSupportsTransientAttachments &&
|
||||
// Usage consists of attachment flags only.
|
||||
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(fUsage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1)};
|
||||
if (useTransientAttachment) {
|
||||
transientAttachmentNeeded |= wgpu::TextureUsage::TransientAttachment;
|
||||
}
|
||||
|
||||
// A texture that is a blit destination or render attachment will often need to be
|
||||
// a copy source for subsequent operations (e.g., mipmap generation, readbacks).
|
||||
// However, we dont need to add the CopySrc IF its a transientAttachment
|
||||
if (any((TextureUsage::BLIT_DST | TextureUsage::COLOR_ATTACHMENT |
|
||||
TextureUsage::DEPTH_ATTACHMENT) &
|
||||
fUsage)) {
|
||||
if (!useTransientAttachment) {
|
||||
retUsage |= wgpu::TextureUsage::CopySrc;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsRenderAttachmentSupport) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
// WGPU Render attachment covers either color or stencil situation dependant
|
||||
if (any((TextureUsage::COLOR_ATTACHMENT | TextureUsage::STENCIL_ATTACHMENT |
|
||||
TextureUsage::DEPTH_ATTACHMENT) &
|
||||
fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
retUsage |= transientAttachmentNeeded;
|
||||
}
|
||||
|
||||
// NOTE: Unused wgpu flags:
|
||||
// StorageAttachment
|
||||
|
||||
// NOTE: Unused Filament flags:
|
||||
// SUBPASS_INPUT: VK goes to input attachment which we don't support right now
|
||||
// PROTECTED
|
||||
return retUsage;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUTEXTUREHELPERS_H
|
||||
|
||||
@@ -67,11 +67,9 @@ constexpr std::array REQUIRED_FEATURES = {
|
||||
};
|
||||
|
||||
constexpr std::array OPTIONAL_FEATURES = {
|
||||
wgpu::FeatureName::CoreFeaturesAndLimits,
|
||||
wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8,
|
||||
wgpu::FeatureName::TextureComponentSwizzle,
|
||||
};
|
||||
wgpu::FeatureName::CoreFeaturesAndLimits };
|
||||
|
||||
enum class LimitToValidate : uint8_t {
|
||||
begin = 0,// needs to be first for iterating through all possible values in the enum
|
||||
@@ -507,7 +505,6 @@ struct AdapterDetailsHash final {
|
||||
|
||||
// selects one preferred adapter or panics if none can be found
|
||||
wgpu::Adapter selectPreferredAdapter(
|
||||
WebGPUPlatform::Configuration const& configuration,
|
||||
std::unordered_set<AdapterDetails, AdapterDetailsHash> const& compatibleAdapters) {
|
||||
// for each unique adapter...
|
||||
AdapterDetails const* selectedAdapter = nullptr;
|
||||
@@ -515,10 +512,6 @@ wgpu::Adapter selectPreferredAdapter(
|
||||
|
||||
// choose the most desirable adapter that meets the minimum requirements...
|
||||
for (AdapterDetails const& details: compatibleAdapters) {
|
||||
if (configuration.forceBackendType != wgpu::BackendType::Undefined &&
|
||||
configuration.forceBackendType != details.info.backendType) {
|
||||
continue;
|
||||
}
|
||||
if (!adapterMeetsMinimumRequirements(details)) {
|
||||
continue;
|
||||
}
|
||||
@@ -594,7 +587,7 @@ wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||
}
|
||||
const std::unordered_set<AdapterDetails, AdapterDetailsHash> compatibleAdapters =
|
||||
requestCompatibleAdapters(mInstance, requests);
|
||||
return selectPreferredAdapter(getConfiguration(), compatibleAdapters);
|
||||
return selectPreferredAdapter(compatibleAdapters);
|
||||
}
|
||||
|
||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
|
||||
@@ -38,12 +38,7 @@ std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
constexpr std::array backendTypes = {
|
||||
wgpu::BackendType::Metal,
|
||||
// To enable software rasterization on MacOS, we need to ensure Vulkan adapters are
|
||||
// available.
|
||||
wgpu::BackendType::Vulkan,
|
||||
};
|
||||
constexpr std::array backendTypes = { wgpu::BackendType::Metal };
|
||||
constexpr std::array forceFallbackAdapters = { false, true };
|
||||
constexpr size_t totalCombinations =
|
||||
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
|
||||
|
||||
@@ -41,21 +41,36 @@
|
||||
uint32_t height;
|
||||
} wl;
|
||||
}// namespace
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) && defined(FILAMENT_SUPPORTS_XLIB)
|
||||
// TODO: we should allow for headless on Linux explicitly. Right now headless path doesn't work
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
// TODO: we should allow for headless on Linux explicitly. Right now this is the headless path
|
||||
// (with no FILAMENT_SUPPORTS_XCB or FILAMENT_SUPPORTS_XLIB).
|
||||
#include <dlfcn.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
static constexpr const char* LIBRARY_X11 { "libX11.so.6" };
|
||||
|
||||
namespace {
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#include <xcb/xcb.h>
|
||||
namespace {
|
||||
typedef xcb_connection_t* (*XCB_CONNECT)(const char* displayname, int* screenp);
|
||||
}// namespace
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
#include <X11/Xlib.h>
|
||||
namespace {
|
||||
typedef Display* (*X11_OPEN_DISPLAY)(const char*);
|
||||
struct XEnv final {
|
||||
X11_OPEN_DISPLAY openDisplay;
|
||||
Display* display { nullptr };
|
||||
void* library { nullptr };
|
||||
} g_x11;
|
||||
} // namespace
|
||||
}// namespace
|
||||
#endif
|
||||
static constexpr const char* LIBRARY_X11 = "libX11.so.6";
|
||||
namespace {
|
||||
struct XEnv {
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
XCB_CONNECT xcbConnect;
|
||||
xcb_connection_t* connection;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
X11_OPEN_DISPLAY openDisplay;
|
||||
Display* display;
|
||||
#endif
|
||||
void* library = nullptr;
|
||||
} g_x11;
|
||||
}// namespace
|
||||
#else
|
||||
#error Not a supported Linux or FeeBSD + WebGPU platform
|
||||
#endif
|
||||
@@ -101,26 +116,59 @@ wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
surfaceExtent.height = ptrval->height;
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window size for Linux Wayland-backed surface.";
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) && defined (FILAMENT_SUPPORTS_XLIB)
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||
int screen = 0;
|
||||
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
#endif
|
||||
}
|
||||
if (g_x11.openDisplay == nullptr) {
|
||||
g_x11.openDisplay = reinterpret_cast<X11_OPEN_DISPLAY>(dlsym(g_x11.library, "XOpenDisplay"));
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
}
|
||||
Window window { reinterpret_cast<Window const>(nativeWindow) };
|
||||
XWindowAttributes windowAttributes;
|
||||
Status ok { XGetWindowAttributes(g_x11.display, window, &windowAttributes) };
|
||||
FILAMENT_CHECK_PRECONDITION(ok != 0) << " XGetWindowAttributes failed";
|
||||
surfaceExtent.width = static_cast<uint32_t>(windowAttributes.width);
|
||||
surfaceExtent.height = static_cast<uint32_t>(windowAttributes.height);
|
||||
|
||||
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||
bool useXcb = false;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
useXcb = (SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||
#else
|
||||
useXcb = true;
|
||||
#endif
|
||||
if (useXcb) {
|
||||
const xcb_setup_t* setup = xcb_get_setup(g_x11.connection);
|
||||
xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
|
||||
xcb_screen_t* screen = screen_iter.data;
|
||||
surfaceExtent.width = static_cast<uint32_t>(screen->width_in_pixels);
|
||||
surfaceExtent.height = static_cast<uint32_t>(screen->height_in_pixels);
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||
"XCB-backed surface.";
|
||||
}
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (!useXcb) {
|
||||
int screenNumber = DefaultScreen(g_x11.display);
|
||||
Screen* screen = ScreenOfDisplay(g_x11.display, screenNumber);
|
||||
surfaceExtent.width = static_cast<uint32_t>(WidthOfScreen(screen));
|
||||
surfaceExtent.height = static_cast<uint32_t>(HeightOfScreen(screen));
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||
"XLib-backed surface.";
|
||||
}
|
||||
#endif
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Cannot get window surface size for X11 surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for XLIB?)";
|
||||
"(not built with support for XCB or XLIB?)";
|
||||
#elif defined(__linux__)
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Cannot get window surface size for Linux (or FreeBSD) OS "
|
||||
"(not built with support for Wayland or X11?)";
|
||||
#else
|
||||
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||
@@ -128,40 +176,80 @@ wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
return surfaceExtent;
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||
wgpu::SurfaceSourceWaylandSurface surfaceSourceWayland{};
|
||||
surfaceSourceWayland.display = ptrval->display;
|
||||
surfaceSourceWayland.surface = ptrval->surface;
|
||||
wgpu::SurfaceDescriptor const surfaceDescriptor {
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceWayland,
|
||||
.label = "linux_wayland_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux Wayland-backed surface.";
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11) && defined(FILAMENT_SUPPORTS_XLIB)
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||
int screen = 0;
|
||||
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
#endif
|
||||
}
|
||||
if (g_x11.openDisplay == nullptr) {
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
}
|
||||
wgpu::SurfaceSourceXlibWindow surfaceSourceXlib{};
|
||||
surfaceSourceXlib.display = g_x11.display;
|
||||
surfaceSourceXlib.window = reinterpret_cast<Window const>(nativeWindow);
|
||||
wgpu::SurfaceDescriptor const surfaceDescriptor {
|
||||
.nextInChain = &surfaceSourceXlib,
|
||||
.label = "linux_xlib_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||
bool useXcb = false;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
useXcb = (flags & SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||
#else
|
||||
useXcb = true;
|
||||
#endif
|
||||
if (useXcb) {
|
||||
wgpu::SurfaceSourceXCBWindow surfaceSourceXcb{};
|
||||
surfaceSourceXcb.connection = g_x11.connection;
|
||||
|
||||
// TODO: this looks really wrong, please fix!!
|
||||
surfaceSourceXcb.window = *((uint32_t*) nativeWindow);
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceXcb,
|
||||
.label = "linux_xcb_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XCB-backed surface.";
|
||||
}
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (!useXcb) {
|
||||
wgpu::SurfaceSourceXlibWindow surfaceSourceXlib{};
|
||||
surfaceSourceXlib.display = g_x11.display;
|
||||
surfaceSourceXlib.window = reinterpret_cast<uint64_t>(nativeWindow);
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceXlib,
|
||||
.label = "linux_xlib_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XLib-backed surface.";
|
||||
}
|
||||
#endif
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XLib-backed surface.";
|
||||
<< "Cannot create WebGPU X11 surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for XCB or XLIB?)";
|
||||
#elif defined(__linux__)
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Cannot create WebGPU surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for Wayland or X11?)";
|
||||
#else
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "AsyncTaskCounter.h"
|
||||
|
||||
#include "utils/debug.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace filament::backend::webgpuutils {
|
||||
|
||||
void AsyncTaskCounter::startTask() {
|
||||
std::lock_guard<std::mutex> lock{ mMutex };
|
||||
++mTasksInProgress;
|
||||
}
|
||||
|
||||
void AsyncTaskCounter::finishTask() {
|
||||
std::lock_guard<std::mutex> lock{ mMutex };
|
||||
--mTasksInProgress;
|
||||
assert_invariant(mTasksInProgress >= 0);
|
||||
if (mTasksInProgress == 0) {
|
||||
mFinishedCondition.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void AsyncTaskCounter::waitForAllToFinish() {
|
||||
std::unique_lock<std::mutex> lock{ mMutex };
|
||||
mFinishedCondition.wait(lock, [this] { return mTasksInProgress == 0; });
|
||||
}
|
||||
|
||||
} // namespace filament::backend::webgpuutils
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_UTIL_ASYNCWORKCOUNTER_H
|
||||
#define TNT_FILAMENT_BACKEND_UTIL_ASYNCWORKCOUNTER_H
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace filament::backend::webgpuutils {
|
||||
|
||||
/**
|
||||
* Counts the number of asynchronous tasks of a given type.
|
||||
* This can be used to wait for such a count to decrement to 0 (wait for work to complete) in a
|
||||
* thead safe manner.
|
||||
*/
|
||||
class AsyncTaskCounter final {
|
||||
public:
|
||||
AsyncTaskCounter() = default;
|
||||
AsyncTaskCounter(AsyncTaskCounter const&) = delete;
|
||||
AsyncTaskCounter(AsyncTaskCounter const&&) = delete;
|
||||
AsyncTaskCounter& operator=(AsyncTaskCounter const&) = delete;
|
||||
AsyncTaskCounter& operator=(AsyncTaskCounter const&&) = delete;
|
||||
~AsyncTaskCounter() = default;
|
||||
|
||||
/**
|
||||
* Asynchronously (thread-safe) increase the work counter by one
|
||||
*/
|
||||
void startTask();
|
||||
|
||||
/**
|
||||
* Asynchronously (thread-safe) decrease the work counter by one
|
||||
*/
|
||||
void finishTask();
|
||||
|
||||
/**
|
||||
* Wait for all tasks to finish (for the work counter to reach 0) (thread-safe)
|
||||
*/
|
||||
void waitForAllToFinish();
|
||||
|
||||
private:
|
||||
std::mutex mMutex;
|
||||
std::condition_variable mFinishedCondition;
|
||||
int mTasksInProgress{ 0 };
|
||||
};
|
||||
|
||||
} // namespace filament::backend::webgpuutils
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_UTIL_ASYNCWORKCOUNTER_H
|
||||
@@ -85,18 +85,6 @@ std::filesystem::path ScreenshotParams::expectedFilePath() const {
|
||||
return expectedDirectoryPath().append(expectedFileName());
|
||||
}
|
||||
|
||||
std::filesystem::path ScreenshotParams::diffFilePath() const {
|
||||
return diffDirectoryPath().append(diffFileName());
|
||||
}
|
||||
|
||||
std::filesystem::path ScreenshotParams::diffDirectoryPath() {
|
||||
return BackendTest::binaryDirectory().append("images/diff_images");
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::diffFileName() const {
|
||||
return absl::StrFormat("%s_diff.png", mFileName);
|
||||
}
|
||||
|
||||
const std::string ScreenshotParams::filePrefix() const {
|
||||
// TODO(b/422804941): If there are platform specific goldens, when on those platforms append a
|
||||
// unique platform identifying string to this.
|
||||
@@ -140,6 +128,8 @@ void ImageExpectation::compareImage() const {
|
||||
if (bytesFilled) {
|
||||
#ifndef FILAMENT_IOS
|
||||
LoadedPng loadedImage(mParams.expectedFilePath());
|
||||
// Bytewise compare.
|
||||
EXPECT_EQ(loadedImage.bytes().size(), mResult.bytes().size());
|
||||
if (loadedImage.bytes().size() != mResult.bytes().size()) {
|
||||
// Something is wrong with the size of the expected result, which usually means the file
|
||||
// is missing. Fail the test and early return so later steps can assume the image is
|
||||
@@ -147,41 +137,18 @@ void ImageExpectation::compareImage() const {
|
||||
BackendTest::markImageAsFailure(mParams.filePrefix());
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the vector to full saturation on all channels, bad pixels will have
|
||||
// color channels but not alpha set to 0 to produce highly contrasting black pixels.
|
||||
std::vector<unsigned char> imageDiff( loadedImage.bytes().size(), 255 );
|
||||
int pixelDeviations = 0;
|
||||
for (int i = 0; i < mResult.bytes().size(); i += 4) {
|
||||
// In order to handle color channels propoerly for the output, stride 4 bytes at a time.
|
||||
// A failure of any byte in a pixel counts as a failure of the whole pixel.
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
if( std::abs( mResult.bytes()[i+j] - loadedImage.bytes()[i+j] ) >
|
||||
mParams.pixelMatchThreshold() ) {
|
||||
pixelDeviations++;
|
||||
imageDiff[i] = 0;
|
||||
imageDiff[i+1] = 0;
|
||||
imageDiff[i+2] = 0;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < mResult.bytes().size(); ++i) {
|
||||
if (std::abs( mResult.bytes()[i] - loadedImage.bytes()[i] ) >
|
||||
mParams.pixelMatchThreshold()) {
|
||||
pixelDeviations++;
|
||||
}
|
||||
}
|
||||
EXPECT_LE(pixelDeviations, mParams.allowedPixelDeviations());
|
||||
if (pixelDeviations > mParams.allowedPixelDeviations()) {
|
||||
BackendTest::markImageAsFailure(mParams.filePrefix());
|
||||
image::LinearImage image;
|
||||
image = image::toLinearWithAlpha<uint8_t>(
|
||||
mParams.width(), mParams.height(),
|
||||
mParams.width() * 4, (uint8_t *)imageDiff.data(),
|
||||
[](uint8_t value) -> float { return value; },
|
||||
[](filament::math::float4 rgba) -> filament::math::float4 { return rgba; });
|
||||
std::string filePath = mParams.diffFilePath();
|
||||
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
|
||||
// To avoid going from linear -> sRGB -> linear save the PNG as linear.
|
||||
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG_LINEAR, image, "",
|
||||
filePath);
|
||||
}
|
||||
EXPECT_LE(pixelDeviations, mParams.allowedPixelDeviations());
|
||||
// TODO: Add better debug output, such as generating a diff image.
|
||||
#else
|
||||
// For builds that can't load PNGs (currently iOS only) use the expected hash.
|
||||
uint32_t actualHash = mResult.hash();
|
||||
|
||||
@@ -58,9 +58,6 @@ public:
|
||||
static std::filesystem::path expectedDirectoryPath();
|
||||
std::string expectedFileName() const;
|
||||
std::filesystem::path expectedFilePath() const;
|
||||
static std::filesystem::path diffDirectoryPath();
|
||||
std::string diffFileName() const;
|
||||
std::filesystem::path diffFilePath() const;
|
||||
const std::string filePrefix() const;
|
||||
int allowedPixelDeviations() const;
|
||||
int pixelMatchThreshold() const;
|
||||
|
||||
@@ -19,8 +19,6 @@ code. When cmake is run it will copy this directory to the build output creating
|
||||
The unit tests will then write their resulting images into `images/actual_images` in order to make
|
||||
the tests easier to debug.
|
||||
|
||||
If an image comparison test fails, it writes a diff image to `images/diff_images` where each pixel of the diff is white (255,255,255) if the comparison for the corresponding pixel succeeds and black (0,0,0) of the comparison for the corresponding pixel fails.
|
||||
|
||||
### Python utility for updating golden images and comparing results
|
||||
|
||||
Inside the unit test source code directory there is a python script called
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_BACKEND_TEST_WORKAROUNDS_H
|
||||
#define TNT_BACKEND_TEST_WORKAROUNDS_H
|
||||
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
// consider, if needed, adding (as Vulkan also has transient attachments): || defined(FILAMENT_SUPPORTS_VULKAN)
|
||||
// TODO: support TextureUsage::READ_PIXELS (or something like that)
|
||||
// and search-and-replace TEXTURE_USAGE_READ_PIXELS with
|
||||
// "| TextureUsage::READ_PIXELS" when it is available.
|
||||
// Until then, this serves as a workaround for WebGPU testing, such that
|
||||
// it can easily be swapped out for the "right" thing when that is available.
|
||||
// The problem this is trying to address is that the WebGPU backend will
|
||||
// assign a texture with wgpu::TextureUsage::TransientAttachment if the Filament
|
||||
// usage flags don't indicate the pixels are needed CPU-side. If transient attachment is
|
||||
// assigned to the texture, the texture will not be available later for readPixels, which
|
||||
// is often needed for the unit tests. If we suggest that
|
||||
// the texture needs to support being a blit source, then TransientAttachment will
|
||||
// not get assigned. However, this is not quite right in the test, because a test
|
||||
// may not really want to do a blit with the texture, just read the pixels from it later for
|
||||
// image comparison (renderdiff).
|
||||
// Ideally, the test could express this with a TextureUsage::READ_PIXELS flag, but that
|
||||
// does not yet exist. So, a hack would be to explicitly set the BLIT_SRC flag instead in
|
||||
// its place.
|
||||
#define TEXTURE_USAGE_READ_PIXELS | TextureUsage::BLIT_SRC
|
||||
#else
|
||||
#define TEXTURE_USAGE_READ_PIXELS
|
||||
#endif
|
||||
|
||||
#endif // TNT_BACKEND_TEST_WORKAROUNDS_H
|
||||
@@ -144,9 +144,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorMagnify) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
@@ -161,18 +159,17 @@ TEST_F(BlitTest, ColorMagnify) {
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> const srcTexture = mCleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT |
|
||||
TextureUsage::BLIT_SRC));
|
||||
Handle<HwTexture> const srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> const dstTexture = mCleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::BLIT_DST));
|
||||
Handle<HwTexture> const dstTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
@@ -208,9 +205,7 @@ TEST_F(BlitTest, ColorMagnify) {
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorMinify) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -225,18 +220,17 @@ TEST_F(BlitTest, ColorMinify) {
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> const srcTexture = mCleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT |
|
||||
TextureUsage::BLIT_SRC));
|
||||
Handle<HwTexture> const srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> const dstTexture = mCleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::BLIT_DST));
|
||||
Handle<HwTexture> const dstTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
@@ -264,7 +258,6 @@ TEST_F(BlitTest, ColorMinify) {
|
||||
}
|
||||
|
||||
TEST_F(BlitTest, ColorResolve) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Nothing is drawn, see b/417229577");
|
||||
auto& api = getDriverApi();
|
||||
@@ -288,12 +281,13 @@ TEST_F(BlitTest, ColorResolve) {
|
||||
// Create 4-sample texture.
|
||||
Handle<HwTexture> const srcColorTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight,
|
||||
1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::BLIT_SRC | TextureUsage::UPLOADABLE));
|
||||
1,
|
||||
TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create 1-sample texture.
|
||||
Handle<HwTexture> const dstColorTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::BLIT_DST));
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a 4-sample render target with the 4-sample texture.
|
||||
Handle<HwRenderTarget> const srcRenderTarget = mCleanup.add(api.createRenderTarget(
|
||||
@@ -346,7 +340,6 @@ TEST_F(BlitTest, ColorResolve) {
|
||||
|
||||
TEST_F(BlitTest, Blit2DTextureArray) {
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
api.startCapture(0);
|
||||
mCleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
@@ -368,18 +361,18 @@ TEST_F(BlitTest, Blit2DTextureArray) {
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(SamplerType::SAMPLER_2D_ARRAY,
|
||||
kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT |
|
||||
TextureUsage::BLIT_SRC));
|
||||
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth,
|
||||
kSrcTexHeight, kSrcTexDepth,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0),
|
||||
flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::BLIT_DST));
|
||||
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create two RenderTargets.
|
||||
const int level = 0;
|
||||
@@ -416,7 +409,6 @@ TEST_F(BlitTest, Blit2DTextureArray) {
|
||||
|
||||
TEST_F(BlitTest, BlitRegion) {
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -433,18 +425,17 @@ TEST_F(BlitTest, BlitRegion) {
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT |
|
||||
TextureUsage::BLIT_SRC));
|
||||
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
|
||||
kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::BLIT_DST));
|
||||
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Blit one-quarter of src level 1 to dst level 0.
|
||||
Viewport srcRect = {
|
||||
@@ -492,9 +483,7 @@ TEST_F(BlitTest, BlitRegion) {
|
||||
|
||||
TEST_F(BlitTest, BlitRegionToSwapChain) {
|
||||
FAIL_IF(Backend::VULKAN, "Crashes due to not finding color attachment, see b/417481493");
|
||||
SKIP_IF(Backend::WEBGPU, "WebGPU Crashes due to not finding color attachment");
|
||||
auto& api = getDriverApi();
|
||||
mCleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -547,13 +536,13 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
srcRect, SamplerMagFilter::LINEAR);
|
||||
|
||||
api.commit(swapChain);
|
||||
}
|
||||
api.commit(swapChain);
|
||||
|
||||
// TODO: for some reason, this test has been disabled. It needs to be tested on all
|
||||
// machines.
|
||||
// EXPECT_IMAGE(dstRenderTarget,
|
||||
// ScreenshotParams(kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain", 0x0));
|
||||
// TODO: for some reason, this test has been disabled. It needs to be tested on all
|
||||
// machines.
|
||||
// EXPECT_IMAGE(dstRenderTarget,
|
||||
// ScreenshotParams(kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain", 0x0));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "SharedShaders.h"
|
||||
#include "Skip.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
#include "Workarounds.h"
|
||||
|
||||
namespace test {
|
||||
|
||||
@@ -92,6 +91,7 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
|
||||
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
||||
// uniform definition.
|
||||
@@ -102,29 +102,42 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
});
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
|
||||
// Draw 10 triangles, updating the vertex buffer / index buffer each time.
|
||||
size_t triangleIndex = 0;
|
||||
for (float i = -1.0f; i < 1.0f; i += 0.2f) {
|
||||
const float low = i, high = i + 0.2;
|
||||
const filament::math::float2 v[3]{{ low, low },
|
||||
{ high, low },
|
||||
{ low, high }};
|
||||
triangle.updateVertices(v);
|
||||
// Draw 10 triangles, updating the vertex buffer / index buffer each time.
|
||||
size_t triangleIndex = 0;
|
||||
for (float i = -1.0f; i < 1.0f; i += 0.2f) {
|
||||
const float low = i, high = i + 0.2;
|
||||
const filament::math::float2 v[3]{{ low, low },
|
||||
{ high, low },
|
||||
{ low, high }};
|
||||
triangle.updateVertices(v);
|
||||
|
||||
if (updateIndices) {
|
||||
if (triangleIndex % 2 == 0) {
|
||||
// Upload each index separately, to test offsets.
|
||||
const TrianglePrimitive::index_type i[3]{ 0, 1, 2 };
|
||||
triangle.updateIndices(i + 0, 1, 0);
|
||||
triangle.updateIndices(i + 1, 1, 1);
|
||||
triangle.updateIndices(i + 2, 1, 2);
|
||||
} else {
|
||||
// This effectively hides this triangle.
|
||||
const TrianglePrimitive::index_type i[3]{ 0, 0, 0 };
|
||||
triangle.updateIndices(i);
|
||||
if (updateIndices) {
|
||||
if (triangleIndex % 2 == 0) {
|
||||
// Upload each index separately, to test offsets.
|
||||
const TrianglePrimitive::index_type i[3]{ 0, 1, 2 };
|
||||
triangle.updateIndices(i + 0, 1, 0);
|
||||
triangle.updateIndices(i + 1, 1, 1);
|
||||
triangle.updateIndices(i + 2, 1, 2);
|
||||
} else {
|
||||
// This effectively hides this triangle.
|
||||
const TrianglePrimitive::index_type i[3]{ 0, 0, 0 };
|
||||
triangle.updateIndices(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (triangleIndex > 0) {
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||
}
|
||||
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
triangleIndex++;
|
||||
}
|
||||
|
||||
if (triangleIndex > 0) {
|
||||
@@ -142,20 +155,14 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
||||
|
||||
triangleIndex++;
|
||||
}
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
}
|
||||
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
// This test renders two triangles in two separate draw calls. Between the draw calls, a uniform
|
||||
// buffer object is partially updated.
|
||||
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"All values including alpha are written as 0, see b/417254943");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
@@ -178,9 +185,9 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
|
||||
|
||||
// Create a render target.
|
||||
auto colorTexture = cleanup.add(
|
||||
api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1, screenWidth(),
|
||||
screenHeight(), 1, TextureUsage::COLOR_ATTACHMENT TEXTURE_USAGE_READ_PIXELS));
|
||||
auto colorTexture =
|
||||
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
|
||||
screenWidth(), screenHeight(), 1, TextureUsage::COLOR_ATTACHMENT));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR0, screenWidth(),
|
||||
screenHeight(), 1, 0, { { colorTexture } }, {}, {}));
|
||||
|
||||
@@ -240,15 +247,13 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||
"BufferObjectUpdateWithOffset", 2320747245));
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// This ensures all driver commands have finished before exiting the test.
|
||||
api.finish();
|
||||
|
||||
executeCommands();
|
||||
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -29,58 +29,61 @@ TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
SKIP_IF(Backend::VULKAN, "Frame callbacks are unsupported in Vulkan, see b/417254479");
|
||||
SKIP_IF(Backend::WEBGPU, "Frame callbacks are unsupported in WebGPU");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a SwapChain.
|
||||
// In order for the frameScheduledCallback to be called, this must be a real SwapChain (not
|
||||
// headless) so we obtain a drawable.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
|
||||
|
||||
int callbackCountA = 0;
|
||||
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
|
||||
callable();
|
||||
callbackCountA++;
|
||||
}, 0);
|
||||
|
||||
// Render the first frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
api.endRenderPass(0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Render the next frame. The same callback should be called.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
api.endRenderPass(0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Now switch out the callback.
|
||||
int callbackCountB = 0;
|
||||
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountB](PresentCallable callable) {
|
||||
callable();
|
||||
callbackCountB++;
|
||||
}, 0);
|
||||
{
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
cleanup.addPostCall([&]() { getDriver().purge(); });
|
||||
|
||||
// Render one final frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
api.endRenderPass(0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
// Create a SwapChain.
|
||||
// In order for the frameScheduledCallback to be called, this must be a real SwapChain (not
|
||||
// headless) so we obtain a drawable.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
|
||||
api.finish();
|
||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
|
||||
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
|
||||
callable();
|
||||
callbackCountA++;
|
||||
}, 0);
|
||||
|
||||
// Render the first frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
api.endRenderPass(0);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Render the next frame. The same callback should be called.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
api.endRenderPass(0);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Now switch out the callback.
|
||||
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountB](PresentCallable callable) {
|
||||
callable();
|
||||
callbackCountB++;
|
||||
}, 0);
|
||||
|
||||
// Render one final frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
api.endRenderPass(0);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
api.finish();
|
||||
}
|
||||
EXPECT_EQ(callbackCountA, 2);
|
||||
EXPECT_EQ(callbackCountB, 1);
|
||||
}
|
||||
@@ -90,43 +93,47 @@ TEST_F(BackendTest, FrameCompletedCallback) {
|
||||
SKIP_IF(Backend::VULKAN, "Frame callbacks are unsupported in Vulkan, see b/417254479");
|
||||
SKIP_IF(Backend::WEBGPU, "Frame callbacks are unsupported in WebGPU");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a SwapChain.
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
|
||||
int callbackCountA = 0;
|
||||
api.setFrameCompletedCallback(swapChain, nullptr,
|
||||
[&callbackCountA]() { callbackCountA++; });
|
||||
|
||||
// Render the first frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Render the next frame. The same callback should be called.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Now switch out the callback.
|
||||
int callbackCountB = 0;
|
||||
api.setFrameCompletedCallback(swapChain, nullptr,
|
||||
[&callbackCountB]() { callbackCountB++; });
|
||||
{
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
cleanup.addPostCall([&]() { getDriver().purge(); });
|
||||
|
||||
// Render one final frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
// Create a SwapChain.
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
|
||||
api.finish();
|
||||
api.setFrameCompletedCallback(swapChain, nullptr,
|
||||
[&callbackCountA]() { callbackCountA++; });
|
||||
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
// Render the first frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Render the next frame. The same callback should be called.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Now switch out the callback.
|
||||
api.setFrameCompletedCallback(swapChain, nullptr,
|
||||
[&callbackCountB]() { callbackCountB++; });
|
||||
|
||||
// Render one final frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
api.finish();
|
||||
}
|
||||
|
||||
EXPECT_EQ(callbackCountA, 2);
|
||||
EXPECT_EQ(callbackCountB, 1);
|
||||
|
||||
@@ -104,6 +104,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL),
|
||||
"OpenGL image is upside down due to readPixels failing for texture with uploaded image "
|
||||
"data");
|
||||
FAIL_IF(Backend::WEBGPU, "BUG");
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
@@ -126,7 +127,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
TrianglePrimitive const triangle(getDriverApi());
|
||||
|
||||
// Create a texture.
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
Handle<HwTexture> const texture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage));
|
||||
|
||||
@@ -157,7 +158,10 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
|
||||
api.update3DImage(texture, 0, 0, 0, 0, kTexWidth, kTexHeight, 1, std::move(pb));
|
||||
|
||||
for (int frame = 0; frame < kNumFrames; frame++) {
|
||||
for (int frameCount = 0; frameCount < kNumFrames; frameCount++) {
|
||||
Cleanup frameCleanup(api);
|
||||
frameCleanup.addPostCall([&]() { executeCommands(); });
|
||||
frameCleanup.addPostCall([&]() { getDriver().purge(); });
|
||||
|
||||
// Prep for rendering.
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
@@ -190,9 +194,9 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
};
|
||||
shader.bindUniform<MaterialParams>(api, ubuffer, uniformBinding);
|
||||
shader.uploadUniform(api, ubuffer, uniformBinding, MaterialParams{
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
.fbWidth = float(params.viewport.width),
|
||||
.fbHeight = float(params.viewport.height),
|
||||
.sourceLevel = float(sourceLevel),
|
||||
});
|
||||
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
@@ -245,7 +249,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
//
|
||||
// NOTE: Calling glReadPixels on any miplevel other than the base level
|
||||
// seems to be un-reliable on some GPU's.
|
||||
if (frame == kNumFrames - 1) {
|
||||
if (frameCount == kNumFrames - 1) {
|
||||
EXPECT_IMAGE(renderTargets[0],
|
||||
ScreenshotParams(kTexWidth, kTexHeight, "FeedbackLoops", 4192780705));
|
||||
}
|
||||
@@ -254,8 +258,6 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,6 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImage2D) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
FAIL_IF(Backend::VULKAN, "Multiple test cases crash, see b/417481434");
|
||||
|
||||
// All of these test cases should result in the same rendered image, and thus the same hash.
|
||||
@@ -315,15 +314,19 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
||||
|
||||
api.startCapture();
|
||||
|
||||
Cleanup cleanup(api);
|
||||
cleanup.addPostCall([&]() { api.finish(); });
|
||||
cleanup.addPostCall([&]() { api.stopCapture(); });
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
for (const auto& t : testCases) {
|
||||
Cleanup cleanup(api);
|
||||
Cleanup caseCleanup(api);
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
auto swapChain = caseCleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
|
||||
auto defaultRenderTarget = caseCleanup.add(api.createDefaultRenderTarget(0));
|
||||
|
||||
// Create a program.
|
||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||
@@ -387,8 +390,6 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
}
|
||||
|
||||
api.stopCapture();
|
||||
}
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
@@ -399,6 +400,7 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
cleanup.addPostCall([&]() { api.stopCapture(); });
|
||||
|
||||
PixelDataFormat const pixelFormat = PixelDataFormat::RGBA;
|
||||
PixelDataType const pixelType = PixelDataType::UBYTE;
|
||||
@@ -446,37 +448,36 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
|
||||
api.update3DImage(texture, 0, 0, 0, 0, kTexSize, kTexSize, 1, std::move(descriptor));
|
||||
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
|
||||
// Update samplers.
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
// Update samplers.
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
|
||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||
|
||||
RenderPassParams params = getClearColorRenderPass();
|
||||
params.viewport.width = kTexSize;
|
||||
params.viewport.height = kTexSize;
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(state);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = mTriangle.getVertexBufferInfo();
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(mTriangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
RenderPassParams params = getClearColorRenderPass();
|
||||
params.viewport.width = kTexSize;
|
||||
params.viewport.height = kTexSize;
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(state);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = mTriangle.getVertexBufferInfo();
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(mTriangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(kTexSize, kTexSize, "UpdateImageSRGB", 3300305265));
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(kTexSize, kTexSize, "UpdateImageSRGB", 3300305265));
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture();
|
||||
api.commit(swapChain);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
@@ -487,6 +488,7 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
cleanup.addPostCall([&]() { api.stopCapture(); });
|
||||
|
||||
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
|
||||
PixelDataType pixelType = PixelDataType::HALF;
|
||||
@@ -519,17 +521,7 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, kTexSize);
|
||||
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, kTexSize, kTexSize, 1, std::move(descriptor));
|
||||
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
|
||||
});
|
||||
|
||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||
|
||||
RenderFrame outerFrame(api);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
RenderPassParams params = getClearColorRenderPass();
|
||||
@@ -550,9 +542,6 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
ScreenshotParams(kTexSize, kTexSize, "UpdateImageMipLevel", 1875922935));
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture();
|
||||
}
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImage3D) {
|
||||
@@ -565,12 +554,13 @@ TEST_F(LoadImageTest, UpdateImage3D) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
cleanup.addPostCall([&]() { api.stopCapture(); });
|
||||
|
||||
PixelDataFormat pixelFormat = PixelDataFormat::RGBA;
|
||||
PixelDataType pixelType = PixelDataType::FLOAT;
|
||||
TextureFormat textureFormat = TextureFormat::RGBA16F;
|
||||
SamplerType samplerType = SamplerType::SAMPLER_2D_ARRAY;
|
||||
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT;
|
||||
TextureUsage usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
@@ -635,8 +625,6 @@ TEST_F(LoadImageTest, UpdateImage3D) {
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(kTexSize, kTexSize, "UpdateImage3D", 1875922935));
|
||||
}
|
||||
|
||||
api.stopCapture();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -108,28 +108,27 @@ TEST_F(BackendTest, MRT) {
|
||||
RenderPassParams params = getClearColorRenderPass();
|
||||
params.viewport = getFullViewport();
|
||||
|
||||
Cleanup captureCleanup(api);
|
||||
api.startCapture(0);
|
||||
captureCleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
|
||||
// Draw a triangle.
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
// Draw a triangle.
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
}
|
||||
}
|
||||
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -56,8 +56,9 @@ using namespace filament::backend;
|
||||
|
||||
TEST_F(BackendTest, TextureViewLod) {
|
||||
auto& api = getDriverApi();
|
||||
api.startCapture(0);
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
@@ -123,7 +124,7 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
|
||||
api.beginFrame(0, 0, 0);
|
||||
RenderFrame frame(api);
|
||||
|
||||
// We set the base mip to 1, and the max mip to 3
|
||||
// Level 0: 128x128 (red)
|
||||
@@ -211,15 +212,9 @@ TEST_F(BackendTest, TextureViewLod) {
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
}
|
||||
|
||||
api.finish();
|
||||
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -59,10 +59,11 @@ using namespace filament::backend;
|
||||
* successfully.
|
||||
*/
|
||||
TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
DriverApi& api = getDriverApi();
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
DriverApi& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
@@ -86,9 +87,10 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
params.viewport = getFullViewport();
|
||||
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
RenderFrame frame(api);
|
||||
|
||||
// Render a triangle.
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
@@ -101,12 +103,7 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
}
|
||||
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -111,12 +111,13 @@ TEST_F(BackendTest, PushConstants) {
|
||||
|
||||
auto& api = getDriverApi();
|
||||
|
||||
api.startCapture(0);
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
// Create a SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
@@ -141,7 +142,7 @@ TEST_F(BackendTest, PushConstants) {
|
||||
ps.rasterState.depthWrite = false;
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.bindPipeline(ps);
|
||||
@@ -183,10 +184,7 @@ TEST_F(BackendTest, PushConstants) {
|
||||
"pushConstants", 3575588741));
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
}
|
||||
|
||||
api.stopCapture(0);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -80,7 +80,6 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Two cases fail, see b/417255941 and b/417255943");
|
||||
// These test scenarios use a known hash of the result pixel buffer to decide pass / fail,
|
||||
@@ -289,7 +288,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
params.viewport.height = t.getRenderTargetSize();
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
RenderFrame frame (api);
|
||||
|
||||
// Render a white triangle over blue.
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
@@ -351,11 +350,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
}
|
||||
|
||||
// This ensures all driver commands have finished before exiting the test.
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
@@ -364,6 +359,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
|
||||
DriverApi& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
// Create a platform-specific SwapChain and make it current.
|
||||
auto swapChain = cleanup.add(
|
||||
@@ -417,28 +413,29 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
}
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
|
||||
// Render some content, just so we don't read back uninitialized data.
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
// Render some content, just so we don't read back uninitialized data.
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
||||
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
|
||||
[](void* buffer, size_t size, void* user) {
|
||||
ReadPixelsTest* test = (ReadPixelsTest*)user;
|
||||
test->readPixelsFinished = true;
|
||||
}, this);
|
||||
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
||||
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
|
||||
[](void* buffer, size_t size, void* user) {
|
||||
ReadPixelsTest* test = (ReadPixelsTest*) user;
|
||||
test->readPixelsFinished = true;
|
||||
}, this);
|
||||
|
||||
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize,
|
||||
std::move(descriptor));
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize,
|
||||
std::move(descriptor));
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
@@ -449,7 +446,6 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
||||
free(buffer);
|
||||
|
||||
api.finish();
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -46,9 +46,8 @@ Shader createShader(DriverApi& api, Cleanup& cleanup, Backend backend) {
|
||||
|
||||
// Rendering an external image without setting any data should not crash.
|
||||
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
SKIP_IF(Backend::METAL, "External images aren't supported in Metal");
|
||||
SKIP_IF(Backend::VULKAN, "External images aren't supported in Vulkan");
|
||||
SKIP_IF(Backend::WEBGPU, "External images aren't supported in WebGPU");
|
||||
SKIP_IF(Backend::METAL, "External images aren't supported in metal");
|
||||
SKIP_IF(Backend::VULKAN, "External images aren't supported in vulkan");
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
@@ -82,37 +81,36 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
||||
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
|
||||
api.startCapture(0);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
Cleanup cleanupCapture(api);
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {});
|
||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {});
|
||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||
|
||||
// Render a triangle.
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
// Render a triangle.
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
}
|
||||
}
|
||||
api.finish();
|
||||
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, RenderExternalImage) {
|
||||
SKIP_IF(Backend::METAL, "External images aren't supported in Metal");
|
||||
SKIP_IF(Backend::VULKAN, "External images aren't supported in Vulkan");
|
||||
SKIP_IF(Backend::WEBGPU, "External images aren't supported in WebGPU");
|
||||
SKIP_IF(Backend::METAL, "External images aren't supported in metal");
|
||||
SKIP_IF(Backend::VULKAN, "External images aren't supported in vulkan");
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
@@ -182,32 +180,32 @@ TEST_F(BackendTest, RenderExternalImage) {
|
||||
RenderPassParams params = getClearColorRenderPass();
|
||||
params.viewport = getFullViewport();
|
||||
|
||||
api.startCapture(0);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
Cleanup cleanupCapture(api);
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {});
|
||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {});
|
||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||
|
||||
// Render a triangle.
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(screenWidth(), screenHeight(), "RenderExternalImage", 1206264951));
|
||||
|
||||
api.stopCapture(0);
|
||||
api.finish();
|
||||
flushAndWait();
|
||||
// Render a triangle.
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.flush();
|
||||
api.commit(swapChain);
|
||||
}
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(screenWidth(), screenHeight(), "RenderExternalImage", 1206264951));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -31,7 +31,6 @@ using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Affected area in wrong corner, see b/417229118");
|
||||
auto& api = getDriverApi();
|
||||
@@ -44,8 +43,9 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
constexpr int kSrcRtWidth = 384;
|
||||
constexpr int kSrcRtHeight = 384;
|
||||
|
||||
api.startCapture(0);
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
// color texture (mip level 1) 512x512 depth texture (mip level 0) 512x512
|
||||
// +----------------------------------------+ +------------------------------------------+
|
||||
@@ -122,7 +122,7 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
shader.addProgramToPipelineState(ps);
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.scissor(scissor);
|
||||
@@ -137,9 +137,6 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
ScreenshotParams(kSrcTexWidth >> 1, kSrcTexHeight >> 1, "scissor", 15842520));
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,12 +144,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
||||
TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
api.startCapture(0);
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// The test is executed within this block scope to force destructors to run before
|
||||
// executeCommands().
|
||||
{
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture(0);
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
@@ -202,7 +198,7 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
shader.addProgramToPipelineState(ps);
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.scissor(scissor);
|
||||
@@ -229,9 +225,6 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
||||
ScreenshotParams(512, 512, "ScissorViewportEdgeCases", 2199186852));
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ public:
|
||||
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
|
||||
|
||||
api.makeCurrent(mSwapChain, mSwapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
ps.primitiveType = PrimitiveType::TRIANGLES;
|
||||
@@ -117,7 +117,6 @@ public:
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(mSwapChain);
|
||||
api.endFrame(0);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
@@ -126,7 +125,6 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Stencil not supported, see b/417230776");
|
||||
auto& api = getDriverApi();
|
||||
@@ -147,13 +145,9 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
||||
|
||||
EXPECT_IMAGE(renderTarget,
|
||||
ScreenshotParams(screenWidth(), screenHeight(), "StencilBuffer", 0x3B1AEF0F));
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Stencil not supported, see b/417230776");
|
||||
auto& api = getDriverApi();
|
||||
@@ -174,13 +168,9 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
||||
|
||||
EXPECT_IMAGE(renderTarget,
|
||||
ScreenshotParams(screenWidth(), screenHeight(), "DepthAndStencilBuffer", 0x3B1AEF0F));
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Stencil not supported, see b/417230776");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL), "Stencil isn't applied");
|
||||
@@ -207,6 +197,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
{ depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
||||
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
// We'll be using a triangle as geometry.
|
||||
TrianglePrimitive smallTriangle(api);
|
||||
@@ -236,44 +227,41 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
||||
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
|
||||
|
||||
api.makeCurrent(mSwapChain, mSwapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.beginRenderPass(renderTarget0, params);
|
||||
ps.primitiveType = PrimitiveType::TRIANGLES;
|
||||
ps.vertexBufferInfo = smallTriangle.getVertexBufferInfo();
|
||||
api.bindPipeline(ps);
|
||||
api.bindRenderPrimitive(smallTriangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
api.beginRenderPass(renderTarget0, params);
|
||||
ps.primitiveType = PrimitiveType::TRIANGLES;
|
||||
ps.vertexBufferInfo = smallTriangle.getVertexBufferInfo();
|
||||
api.bindPipeline(ps);
|
||||
api.bindRenderPrimitive(smallTriangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
// Step 2: Render a larger triangle with the stencil test enabled.
|
||||
params.flags.clear = TargetBufferFlags::COLOR0;
|
||||
params.flags.discardStart = TargetBufferFlags::COLOR0;
|
||||
params.flags.discardEnd = TargetBufferFlags::STENCIL;
|
||||
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.stencilState.stencilWrite = false;
|
||||
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::KEEP;
|
||||
ps.stencilState.front.stencilFunc = StencilState::StencilFunction::E;
|
||||
ps.stencilState.front.ref = 0u;
|
||||
// Step 2: Render a larger triangle with the stencil test enabled.
|
||||
params.flags.clear = TargetBufferFlags::COLOR0;
|
||||
params.flags.discardStart = TargetBufferFlags::COLOR0;
|
||||
params.flags.discardEnd = TargetBufferFlags::STENCIL;
|
||||
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
ps.rasterState.colorWrite = true;
|
||||
ps.stencilState.stencilWrite = false;
|
||||
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::KEEP;
|
||||
ps.stencilState.front.stencilFunc = StencilState::StencilFunction::E;
|
||||
ps.stencilState.front.ref = 0u;
|
||||
|
||||
api.beginRenderPass(renderTarget1, params);
|
||||
ps.primitiveType = PrimitiveType::TRIANGLES;
|
||||
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(ps);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(mSwapChain);
|
||||
api.stopCapture(0);
|
||||
api.endFrame(0);
|
||||
api.beginRenderPass(renderTarget1, params);
|
||||
ps.primitiveType = PrimitiveType::TRIANGLES;
|
||||
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||
api.bindPipeline(ps);
|
||||
api.bindRenderPrimitive(triangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(mSwapChain);
|
||||
api.stopCapture(0);
|
||||
}
|
||||
EXPECT_IMAGE(renderTarget1, ScreenshotParams(screenWidth(), screenHeight(),
|
||||
"StencilBufferAutoResolve", 3353562179));
|
||||
|
||||
flushAndWait();
|
||||
getDriver().purge();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "webgpu/utils/AsyncTaskCounter.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
namespace test {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int TOLERANCE_MILLISECONDS{ 5 };
|
||||
constexpr int SMALLER_JOB_MILLISECONDS{ 10 };
|
||||
static_assert(SMALLER_JOB_MILLISECONDS > TOLERANCE_MILLISECONDS);
|
||||
constexpr int LARGER_JOB_MILLISECONDS{ 20 };
|
||||
static_assert(LARGER_JOB_MILLISECONDS > (SMALLER_JOB_MILLISECONDS + TOLERANCE_MILLISECONDS));
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(AsyncTaskCounter, noWorkJobs) {
|
||||
filament::backend::webgpuutils::AsyncTaskCounter counter{};
|
||||
const auto waitFuture{ std::async(std::launch::async,
|
||||
[&counter] { counter.waitForAllToFinish(); }) };
|
||||
const auto status{ waitFuture.wait_for(std::chrono::milliseconds(TOLERANCE_MILLISECONDS)) };
|
||||
ASSERT_NE(status, std::future_status::timeout)
|
||||
<< "Timed out waiting for no work to finish. This should have returned immediately. "
|
||||
"Tolerance/timeout: "
|
||||
<< TOLERANCE_MILLISECONDS << " milliseconds.";
|
||||
}
|
||||
|
||||
TEST(AsyncTaskCounter, oneWorkJob) {
|
||||
filament::backend::webgpuutils::AsyncTaskCounter counter{};
|
||||
const auto task{ [&counter]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(SMALLER_JOB_MILLISECONDS));
|
||||
counter.finishTask();
|
||||
} };
|
||||
const auto startTime{ std::chrono::steady_clock::now() };
|
||||
counter.startTask();
|
||||
std::thread t{ task };
|
||||
const auto waitFuture{ std::async(std::launch::async,
|
||||
[&counter] { counter.waitForAllToFinish(); }) };
|
||||
const auto expectedDuration{ SMALLER_JOB_MILLISECONDS };
|
||||
const auto status{ waitFuture.wait_for(
|
||||
std::chrono::milliseconds(expectedDuration + TOLERANCE_MILLISECONDS)) };
|
||||
const auto endTime{ std::chrono::steady_clock::now() };
|
||||
const auto duration{ std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
endTime - startTime) };
|
||||
ASSERT_NE(status, std::future_status::timeout)
|
||||
<< "Timed out waiting for one work item to finish. Expected job duration: "
|
||||
<< expectedDuration
|
||||
<< " milliseconds. Timeout: " << (expectedDuration + TOLERANCE_MILLISECONDS)
|
||||
<< " milliseconds.";
|
||||
ASSERT_GT(duration, std::chrono::milliseconds(expectedDuration - TOLERANCE_MILLISECONDS))
|
||||
<< "The one job should have taken at least its expected duration of "
|
||||
<< expectedDuration << " milliseconds (" << TOLERANCE_MILLISECONDS
|
||||
<< " milliseconds tolerance), but took " << duration << " milliseconds";
|
||||
t.join();
|
||||
}
|
||||
|
||||
TEST(AsyncTaskCounter, multipleWorkJobs) {
|
||||
filament::backend::webgpuutils::AsyncTaskCounter counter{};
|
||||
const auto smallerTasks{ [&counter]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(SMALLER_JOB_MILLISECONDS));
|
||||
// validates the counter incrementing and decrementing before the work is done.
|
||||
counter.startTask();
|
||||
counter.finishTask();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(SMALLER_JOB_MILLISECONDS));
|
||||
counter.finishTask();
|
||||
} };
|
||||
const auto largerTask{ [&counter]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(LARGER_JOB_MILLISECONDS));
|
||||
counter.finishTask();
|
||||
} };
|
||||
const auto startTime{ std::chrono::steady_clock::now() };
|
||||
counter.startTask();
|
||||
std::thread t1{ smallerTasks };
|
||||
counter.startTask();
|
||||
std::thread t2{ largerTask };
|
||||
const auto expectedDuration{ std::max(SMALLER_JOB_MILLISECONDS * 2, LARGER_JOB_MILLISECONDS) };
|
||||
const auto waitFuture{ std::async(std::launch::async,
|
||||
[&counter] { counter.waitForAllToFinish(); }) };
|
||||
const auto status{ waitFuture.wait_for(
|
||||
std::chrono::milliseconds(expectedDuration + TOLERANCE_MILLISECONDS)) };
|
||||
const auto endTime{ std::chrono::steady_clock::now() };
|
||||
const auto duration{ std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
endTime - startTime) };
|
||||
ASSERT_NE(status, std::future_status::timeout)
|
||||
<< "Timed out waiting for 3 work items to finish. Expected job duration: "
|
||||
<< expectedDuration
|
||||
<< " milliseconds. Timeout: " << (expectedDuration + TOLERANCE_MILLISECONDS)
|
||||
<< " milliseconds.";
|
||||
ASSERT_GT(duration, std::chrono::milliseconds(expectedDuration - TOLERANCE_MILLISECONDS))
|
||||
<< "The jobs should have taken at least their expected duration of " << expectedDuration
|
||||
<< " milliseconds (" << TOLERANCE_MILLISECONDS << " milliseconds tolerance), but took "
|
||||
<< duration << " milliseconds";
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
|
||||
TEST(AsyncTaskCounter, counterReuse) {
|
||||
filament::backend::webgpuutils::AsyncTaskCounter counter{};
|
||||
const auto batchOfTasks{ [&counter]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(SMALLER_JOB_MILLISECONDS));
|
||||
counter.startTask();
|
||||
counter.finishTask();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(SMALLER_JOB_MILLISECONDS));
|
||||
counter.finishTask();
|
||||
} };
|
||||
// first batch of tasks...
|
||||
const auto firstStartTime{ std::chrono::steady_clock::now() };
|
||||
counter.startTask();
|
||||
std::thread firstBatch{ batchOfTasks };
|
||||
const auto firstWaitFuture{ std::async(std::launch::async,
|
||||
[&counter] { counter.waitForAllToFinish(); }) };
|
||||
const auto expectedDuration{ SMALLER_JOB_MILLISECONDS * 2 };
|
||||
const auto firstStatus{ firstWaitFuture.wait_for(
|
||||
std::chrono::milliseconds(expectedDuration + TOLERANCE_MILLISECONDS)) };
|
||||
const auto firstEndTime{ std::chrono::steady_clock::now() };
|
||||
const auto firstDuration{ std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
firstEndTime - firstStartTime) };
|
||||
ASSERT_NE(firstStatus, std::future_status::timeout)
|
||||
<< "Timed out waiting for first batch to finish. Expected job duration: "
|
||||
<< expectedDuration
|
||||
<< " milliseconds. Timeout: " << (expectedDuration + TOLERANCE_MILLISECONDS)
|
||||
<< " milliseconds.";
|
||||
ASSERT_GT(firstDuration, std::chrono::milliseconds(expectedDuration - TOLERANCE_MILLISECONDS))
|
||||
<< "The first batch of tasks should have taken at least its expected duration of "
|
||||
<< expectedDuration << " milliseconds (" << TOLERANCE_MILLISECONDS
|
||||
<< " milliseconds tolerance), but took " << firstDuration << " milliseconds";
|
||||
firstBatch.join();
|
||||
// second batch of tasks...
|
||||
const auto secondStartTime{ std::chrono::steady_clock::now() };
|
||||
counter.startTask();
|
||||
std::thread secondBatch{ batchOfTasks };
|
||||
const auto secondWaitFuture{ std::async(std::launch::async,
|
||||
[&counter] { counter.waitForAllToFinish(); }) };
|
||||
const auto secondStatus{ secondWaitFuture.wait_for(
|
||||
std::chrono::milliseconds(expectedDuration + TOLERANCE_MILLISECONDS)) };
|
||||
const auto secondEndTime{ std::chrono::steady_clock::now() };
|
||||
const auto secondDuration{ std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
secondEndTime - secondStartTime) };
|
||||
ASSERT_NE(secondStatus, std::future_status::timeout)
|
||||
<< "Timed out waiting for second batch to finish. Expected job duration: "
|
||||
<< expectedDuration
|
||||
<< " milliseconds. Timeout: " << (expectedDuration + TOLERANCE_MILLISECONDS)
|
||||
<< " milliseconds.";
|
||||
ASSERT_GT(secondDuration, std::chrono::milliseconds(expectedDuration - TOLERANCE_MILLISECONDS))
|
||||
<< "The second batch of tasks should have taken at least its expected duration of "
|
||||
<< expectedDuration << " milliseconds (" << TOLERANCE_MILLISECONDS
|
||||
<< " milliseconds tolerance), but took " << secondDuration << " milliseconds";
|
||||
secondBatch.join();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -87,9 +87,6 @@ public:
|
||||
std::is_same_v<math::mat3f, T>
|
||||
>;
|
||||
|
||||
template<typename T>
|
||||
using is_supported_constant_parameter_t = std::enable_if_t<std::is_same_v<bool, T>>;
|
||||
|
||||
/**
|
||||
* Creates a new MaterialInstance using another MaterialInstance as a template for initialization.
|
||||
* The new MaterialInstance is an instance of the same Material of the template instance and
|
||||
@@ -241,13 +238,13 @@ public:
|
||||
|
||||
/**
|
||||
* Gets the value of a parameter by name.
|
||||
*
|
||||
*
|
||||
* Note: Only supports non-texture parameters such as numeric and math types.
|
||||
*
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*
|
||||
*
|
||||
* @see Material::hasParameter
|
||||
*/
|
||||
template<typename T>
|
||||
@@ -265,53 +262,6 @@ public:
|
||||
return getParameter<T>(name, strlen(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a mutable constant parameter by name.
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @param value Value of the parameter to set.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*/
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(const char* UTILS_NONNULL name, size_t nameLength, T const& value);
|
||||
|
||||
/** inline helper to provide the name as a null-terminated string literal */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(StringLiteral const name, T const& value) {
|
||||
setConstant<T>(name.data, name.size, value);
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(const char* UTILS_NONNULL name, T const& value) {
|
||||
setConstant<T>(name, strlen(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a mutable constant parameter by name.
|
||||
*
|
||||
* @param name Name of the parameter as defined by Material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @throws utils::PreConditionPanic if name doesn't exist or no-op if exceptions are disabled.
|
||||
*
|
||||
* @see Material::hasConstant
|
||||
*/
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(const char* UTILS_NONNULL name, size_t nameLength) const;
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(StringLiteral const name) const {
|
||||
return getConstant<T>(name.data, name.size);
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(const char* UTILS_NONNULL name) const {
|
||||
return getConstant<T>(name, strlen(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
|
||||
@@ -437,20 +437,7 @@ struct AmbientOcclusionOptions {
|
||||
struct Gtao {
|
||||
uint8_t sampleSliceCount = 4; //!< # of slices. Higher value makes less noise.
|
||||
uint8_t sampleStepsPerSlice = 3; //!< # of steps the radius is divided into for integration. Higher value makes less bias.
|
||||
float thicknessHeuristic = 0.004f; //!< thickness heuristic, should be closed to 0. No effect when useVisibilityBitmasks sets to true.
|
||||
|
||||
/**
|
||||
* Enables or disables visibility bitmasks mode. Notes that bent normal doesn't work under this mode.
|
||||
* Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation.
|
||||
*/
|
||||
bool useVisibilityBitmasks = false;
|
||||
float constThickness = 0.5f; //!< constant thickness value of objects on the screen in world space. Only take effect when useVisibilityBitmasks is set to true.
|
||||
|
||||
/**
|
||||
* Increase thickness with distance to maintain detail on distant surfaces.
|
||||
* Caution: Changing this option at runtime is very expensive as it may trigger a shader re-compilation.
|
||||
*/
|
||||
bool linearThickness = false;
|
||||
float thicknessHeuristic = 0.004f; //!< thickness heuristic, should be closed to 0
|
||||
};
|
||||
Gtao gtao; // %codegen_skip_javascript% %codegen_java_flatten%
|
||||
};
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -758,29 +757,6 @@ public:
|
||||
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
|
||||
utils::FixedCapacityVector<Camera const*> getDirectionalShadowCameras() const noexcept;
|
||||
|
||||
//! debugging: enable or disable froxel visualisation for this view.
|
||||
void setFroxelVizEnabled(bool enabled) noexcept;
|
||||
|
||||
//! debugging: returns information about the froxel configuration
|
||||
struct FroxelConfigurationInfo {
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
uint8_t depth;
|
||||
uint32_t viewportWidth;
|
||||
uint32_t viewportHeight;
|
||||
math::uint2 froxelDimension;
|
||||
float zLightFar;
|
||||
float linearizer;
|
||||
math::mat4f p;
|
||||
math::float4 clipTransform;
|
||||
};
|
||||
|
||||
struct FroxelConfigurationInfoWithAge {
|
||||
FroxelConfigurationInfo info;
|
||||
uint32_t age;
|
||||
};
|
||||
|
||||
FroxelConfigurationInfoWithAge getFroxelConfigurationInfo() const noexcept;
|
||||
|
||||
/** Result of a picking query */
|
||||
struct PickingQueryResult {
|
||||
|
||||
@@ -124,20 +124,6 @@ size_t Froxelizer::getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexc
|
||||
return std::min(FROXEL_BUFFER_MAX_ENTRY_COUNT * sizeof(FroxelEntry), targetSize);
|
||||
}
|
||||
|
||||
View::FroxelConfigurationInfo Froxelizer::getFroxelConfigurationInfo() const noexcept {
|
||||
return { uint8_t(getFroxelCountX()),
|
||||
uint8_t(getFroxelCountY()),
|
||||
uint8_t(getFroxelCountZ()),
|
||||
mViewport.width,
|
||||
mViewport.height,
|
||||
mFroxelDimension,
|
||||
mZLightFar,
|
||||
mLinearizer[0],
|
||||
mProjection,
|
||||
mClipTransform
|
||||
};
|
||||
}
|
||||
|
||||
Froxelizer::Froxelizer(FEngine& engine)
|
||||
: mArena("froxel", PER_FROXELDATA_ARENA_SIZE),
|
||||
mZLightNear(FROXEL_FIRST_SLICE_DEPTH),
|
||||
@@ -212,14 +198,10 @@ void Froxelizer::setProjection(const mat4f& projection,
|
||||
bool Froxelizer::prepare(
|
||||
FEngine::DriverApi& driverApi, RootArenaScope& rootArenaScope,
|
||||
filament::Viewport const& viewport,
|
||||
const mat4f& projection, float const projectionNear, float const projectionFar,
|
||||
float4 const& clipTransform) noexcept {
|
||||
const mat4f& projection, float const projectionNear, float const projectionFar) noexcept {
|
||||
setViewport(viewport);
|
||||
setProjection(projection, projectionNear, projectionFar);
|
||||
|
||||
// Only for debugging
|
||||
mClipTransform = clipTransform;
|
||||
|
||||
bool uniformsNeedUpdating = false;
|
||||
if (UTILS_UNLIKELY(mDirtyFlags)) {
|
||||
uniformsNeedUpdating = update();
|
||||
@@ -379,10 +361,8 @@ bool Froxelizer::update() noexcept {
|
||||
getFroxelBufferEntryCount(), viewport);
|
||||
|
||||
mFroxelDimension = froxelDimension;
|
||||
// note: because froxelDimension is a power-of-two and viewport is an integer, mClipFroxel
|
||||
// is an exact value (which is not true for 1/mClipToFroxelX, btw)
|
||||
mClipToFroxelX = float(viewport.width) / float(2 * froxelDimension.x);
|
||||
mClipToFroxelY = float(viewport.height) / float(2 * froxelDimension.y);
|
||||
mClipToFroxelX = (0.5f * float(viewport.width)) / float(froxelDimension.x);
|
||||
mClipToFroxelY = (0.5f * float(viewport.height)) / float(froxelDimension.y);
|
||||
|
||||
uniformsNeedUpdating = true;
|
||||
|
||||
@@ -428,7 +408,8 @@ bool Froxelizer::update() noexcept {
|
||||
}
|
||||
|
||||
// for the inverse-transformation (view-space z to z-slice)
|
||||
mLinearizer = { linearizer, 1.0f / linearizer };
|
||||
mLinearizer = 1.0f / linearizer;
|
||||
mZLightFar = zLightFar;
|
||||
|
||||
mParamsZ[0] = 0; // updated when camera changes
|
||||
mParamsZ[1] = 0; // updated when camera changes
|
||||
@@ -485,7 +466,7 @@ bool Froxelizer::update() noexcept {
|
||||
// ==> i = log2(z_screen * (far/near)) * (-1/linearizer) + zcount
|
||||
mParamsZ[0] = mZLightFar / Pw;
|
||||
mParamsZ[1] = 0.0f;
|
||||
mParamsZ[2] = -mLinearizer[1];
|
||||
mParamsZ[2] = -mLinearizer;
|
||||
} else {
|
||||
// orthographic projection
|
||||
// z_view = (1 - z_screen) * (near - far) - near
|
||||
@@ -495,7 +476,7 @@ bool Froxelizer::update() noexcept {
|
||||
// Pw = far / (far - near)
|
||||
mParamsZ[0] = -1.0f / (Pz * mZLightFar); // -(far-near) / mZLightFar
|
||||
mParamsZ[1] = Pw / (Pz * mZLightFar); // far / mZLightFar
|
||||
mParamsZ[2] = mLinearizer[1];
|
||||
mParamsZ[2] = mLinearizer;
|
||||
}
|
||||
uniformsNeedUpdating = true;
|
||||
}
|
||||
@@ -526,7 +507,7 @@ size_t Froxelizer::findSliceZ(float const z) const noexcept {
|
||||
|
||||
// This whole function is now branch-less.
|
||||
|
||||
int s = int( fast::log2(-z / mZLightFar) * mLinearizer[1] + float(mFroxelCountZ) );
|
||||
int s = int( fast::log2(-z / mZLightFar) * mLinearizer + float(mFroxelCountZ) );
|
||||
|
||||
// there are cases where z can be negative here, e.g.:
|
||||
// - the light is visible, but its center is behind the camera
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "details/Scene.h"
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include <filament/View.h>
|
||||
#include <filament/Viewport.h>
|
||||
|
||||
#include <backend/Handle.h>
|
||||
@@ -32,7 +31,6 @@
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
namespace filament {
|
||||
@@ -114,8 +112,7 @@ public:
|
||||
* return true if updateUniforms() needs to be called
|
||||
*/
|
||||
bool prepare(backend::DriverApi& driverApi, RootArenaScope& rootArenaScope, Viewport const& viewport,
|
||||
const math::mat4f& projection, float projectionNear, float projectionFar,
|
||||
math::float4 const& clipTransform) noexcept;
|
||||
const math::mat4f& projection, float projectionNear, float projectionFar) noexcept;
|
||||
|
||||
Froxel getFroxelAt(size_t x, size_t y, size_t z) const noexcept;
|
||||
size_t getFroxelCountX() const noexcept { return mFroxelCountX; }
|
||||
@@ -164,8 +161,6 @@ public:
|
||||
|
||||
static size_t getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexcept;
|
||||
|
||||
View::FroxelConfigurationInfo getFroxelConfigurationInfo() const noexcept;
|
||||
|
||||
private:
|
||||
size_t getFroxelBufferEntryCount() const noexcept {
|
||||
return mFroxelBufferEntryCount;
|
||||
@@ -265,10 +260,9 @@ private:
|
||||
uint16_t mFroxelCountZ = 0;
|
||||
uint32_t mFroxelCount = 0;
|
||||
math::uint2 mFroxelDimension = {};
|
||||
math::float4 mClipTransform = { 1, 1, 0, 0 };
|
||||
|
||||
math::mat4f mProjection;
|
||||
math::float2 mLinearizer{};
|
||||
float mLinearizer = 0.0f;
|
||||
float mClipToFroxelX = 0.0f;
|
||||
float mClipToFroxelY = 0.0f;
|
||||
backend::BufferObjectHandle mRecordsBuffer;
|
||||
|
||||
@@ -233,46 +233,6 @@ template UTILS_PUBLIC mat3f MaterialInstance::getParameter<mat3f> (const ch
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<>
|
||||
inline void FMaterialInstance::setConstantImpl<bool>(std::string_view const name, bool const& value) {
|
||||
std::optional<uint32_t> id = mMaterial->getMutableConstantId(name);
|
||||
FILAMENT_CHECK_PRECONDITION(id.has_value()) << "No mutable constant with name " << name;
|
||||
mConstants.set(*id, value);
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
void MaterialInstance::setConstant(const char* name, size_t nameLength, T const& value) {
|
||||
downcast(this)->setConstantImpl({ name, nameLength }, value);
|
||||
}
|
||||
|
||||
// Explicit template instantiation of our supported types.
|
||||
//
|
||||
// Mutable spec constants will probably only ever allow bools, but it's nice to keep the API
|
||||
// forwards-compatible.
|
||||
template UTILS_PUBLIC void MaterialInstance::setConstant<bool>(const char* name, size_t nameLength, bool const& v);
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<>
|
||||
inline bool FMaterialInstance::getConstantImpl<bool>(std::string_view const name) const {
|
||||
std::optional<uint32_t> id = mMaterial->getMutableConstantId(name);
|
||||
FILAMENT_CHECK_PRECONDITION(id.has_value()) << "No mutable constant with name " << name;
|
||||
return mConstants[*id];
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
T MaterialInstance::getConstant(const char* name, size_t nameLength) const {
|
||||
return downcast(this)->getConstantImpl<T>({ name, nameLength });
|
||||
}
|
||||
|
||||
// Explicit template instantiation of our supported types.
|
||||
//
|
||||
// Mutable spec constants will probably only ever allow bools, but it's nice to keep the API
|
||||
// forwards-compatible.
|
||||
template UTILS_PUBLIC bool MaterialInstance::getConstant<bool>(const char* name, size_t nameLength) const;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
Material const* MaterialInstance::getMaterial() const noexcept {
|
||||
return downcast(this)->getMaterial();
|
||||
}
|
||||
|
||||
@@ -229,11 +229,6 @@ bool MaterialParser::getConstants(FixedCapacityVector<MaterialConstant>* contain
|
||||
return get<ChunkMaterialConstants>(container);
|
||||
}
|
||||
|
||||
bool MaterialParser::getMutableConstants(
|
||||
FixedCapacityVector<MaterialMutableConstant>* container) const noexcept {
|
||||
return get<ChunkMaterialMutableConstants>(container);
|
||||
}
|
||||
|
||||
bool MaterialParser::getPushConstants(CString* structVarName,
|
||||
FixedCapacityVector<MaterialPushConstant>* value) const noexcept {
|
||||
auto [start, end] = mImpl.mChunkContainer.getChunkRange(MaterialPushConstants);
|
||||
@@ -766,38 +761,6 @@ bool ChunkMaterialConstants::unflatten(Unflattener& unflattener,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChunkMaterialMutableConstants::unflatten(Unflattener& unflattener,
|
||||
FixedCapacityVector<MaterialMutableConstant>* materialConstants) {
|
||||
assert_invariant(materialConstants);
|
||||
|
||||
// Read number of constants.
|
||||
uint64_t numConstants = 0;
|
||||
if (!unflattener.read(&numConstants)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
materialConstants->reserve(numConstants);
|
||||
materialConstants->resize(numConstants);
|
||||
|
||||
for (uint64_t i = 0; i < numConstants; i++) {
|
||||
CString constantName;
|
||||
bool defaultValue;
|
||||
|
||||
if (!unflattener.read(&constantName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!unflattener.read(&defaultValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(*materialConstants)[i].name = std::move(constantName);
|
||||
(*materialConstants)[i].defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChunkMaterialPushConstants::unflatten(Unflattener& unflattener,
|
||||
CString* structVarName,
|
||||
FixedCapacityVector<MaterialPushConstant>* materialPushConstants) {
|
||||
|
||||
@@ -49,7 +49,6 @@ class BufferInterfaceBlock;
|
||||
class SamplerInterfaceBlock;
|
||||
struct SubpassInfo;
|
||||
struct MaterialConstant;
|
||||
struct MaterialMutableConstant;
|
||||
struct MaterialPushConstant;
|
||||
|
||||
class MaterialParser {
|
||||
@@ -80,8 +79,6 @@ public:
|
||||
bool getShaderModels(uint32_t* value) const noexcept;
|
||||
bool getMaterialProperties(uint64_t* value) const noexcept;
|
||||
bool getConstants(utils::FixedCapacityVector<MaterialConstant>* value) const noexcept;
|
||||
bool getMutableConstants(utils::FixedCapacityVector<MaterialMutableConstant>* value)
|
||||
const noexcept;
|
||||
bool getPushConstants(utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* value) const noexcept;
|
||||
|
||||
@@ -241,13 +238,6 @@ struct ChunkMaterialConstants {
|
||||
static filamat::ChunkType const tag = filamat::MaterialConstants;
|
||||
};
|
||||
|
||||
struct ChunkMaterialMutableConstants {
|
||||
static bool unflatten(filaflat::Unflattener& unflattener,
|
||||
utils::FixedCapacityVector<MaterialMutableConstant>* materialConstants);
|
||||
using Container = utils::FixedCapacityVector<MaterialMutableConstant>;
|
||||
static filamat::ChunkType const tag = filamat::MaterialMutableConstants;
|
||||
};
|
||||
|
||||
struct ChunkMaterialPushConstants {
|
||||
static bool unflatten(filaflat::Unflattener& unflattener, utils::CString* structVarName,
|
||||
utils::FixedCapacityVector<MaterialPushConstant>* materialPushConstants);
|
||||
|
||||
@@ -199,8 +199,6 @@ FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine,
|
||||
loadMaterial(engine);
|
||||
}
|
||||
mMaterial->prepareProgram(Variant{ Variant::type_t(variant) },
|
||||
// TODO(exv): allow post process materials to use mutable spec constants?
|
||||
Program::MutableSpecConstantsInfo(0),
|
||||
CompilerPriorityQueue::CRITICAL);
|
||||
return mMaterial;
|
||||
}
|
||||
@@ -467,10 +465,7 @@ UTILS_NOINLINE
|
||||
PipelineState PostProcessManager::getPipelineState(
|
||||
FMaterial const* const ma, PostProcessVariant variant) const noexcept {
|
||||
return {
|
||||
.program = ma->getProgram(Variant{ Variant::type_t(variant) },
|
||||
// TODO(exv): allow post process materials to use mutable spec constants?
|
||||
Program::MutableSpecConstantsInfo(0)
|
||||
),
|
||||
.program = ma->getProgram(Variant{ Variant::type_t(variant) }),
|
||||
.vertexBufferInfo = mFullScreenQuadVbih,
|
||||
.pipelineLayout = {
|
||||
.setLayout = {
|
||||
@@ -998,17 +993,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
#endif
|
||||
auto& material = getPostProcessMaterial(materialName);
|
||||
|
||||
FMaterial* ma = material.getMaterial(mEngine);
|
||||
bool dirty = false;
|
||||
setConstantParameter(ma, "useVisibilityBitmasks", options.gtao.useVisibilityBitmasks, dirty);
|
||||
setConstantParameter(ma, "linearThickness", options.gtao.linearThickness, dirty);
|
||||
if (dirty) {
|
||||
ma->invalidate();
|
||||
// TODO: call Material::compile(), we can't do that now because it works only
|
||||
// with surface materials
|
||||
}
|
||||
|
||||
ma = material.getMaterial(mEngine);
|
||||
FMaterial const * const ma = material.getMaterial(mEngine);
|
||||
FMaterialInstance* const mi = getMaterialInstance(ma);
|
||||
|
||||
// Set AO type specific material parameters
|
||||
@@ -1043,7 +1028,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
mi->setParameter("radius", options.radius);
|
||||
mi->setParameter("intensity", options.intensity);
|
||||
mi->setParameter("thicknessHeuristic", options.gtao.thicknessHeuristic);
|
||||
mi->setParameter("constThickness", options.gtao.constThickness);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -243,9 +243,7 @@ void RenderPass::appendCommands(FEngine const& engine,
|
||||
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
|
||||
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
|
||||
auto ma = first->info.mi->getMaterial();
|
||||
ma->prepareProgram(first->info.materialVariant,
|
||||
first->info.mi->getMutableSpecConstants(),
|
||||
CompilerPriorityQueue::CRITICAL);
|
||||
ma->prepareProgram(first->info.materialVariant, CompilerPriorityQueue::CRITICAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1098,8 +1096,7 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
}
|
||||
|
||||
assert_invariant(ma);
|
||||
pipeline.program = ma->getProgram(info.materialVariant,
|
||||
info.mi->getMutableSpecConstants());
|
||||
pipeline.program = ma->getProgram(info.materialVariant);
|
||||
|
||||
if (UTILS_UNLIKELY(memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) {
|
||||
currentPipeline = pipeline;
|
||||
|
||||
@@ -77,14 +77,6 @@ utils::FixedCapacityVector<Camera const*> View::getDirectionalShadowCameras() co
|
||||
return downcast(this)->getDirectionalShadowCameras();
|
||||
}
|
||||
|
||||
void View::setFroxelVizEnabled(bool const enabled) noexcept {
|
||||
downcast(this)->setFroxelVizEnabled(enabled);
|
||||
}
|
||||
|
||||
View::FroxelConfigurationInfoWithAge View::getFroxelConfigurationInfo() const noexcept {
|
||||
return downcast(this)->getFroxelConfigurationInfo();
|
||||
}
|
||||
|
||||
void View::setShadowingEnabled(bool const enabled) noexcept {
|
||||
downcast(this)->setShadowingEnabled(enabled);
|
||||
}
|
||||
|
||||
@@ -748,7 +748,7 @@ public:
|
||||
bool enable_staging_buffer_bypass = false;
|
||||
} vulkan;
|
||||
bool disable_parallel_shader_compile = false;
|
||||
bool disable_amortized_shader_compile = true;
|
||||
bool disable_amortized_shader_compile = false;
|
||||
bool disable_handle_use_after_free_check = false;
|
||||
bool disable_heap_handle_tags = true; // FIXME: this should be false
|
||||
} backend;
|
||||
|
||||
@@ -364,24 +364,8 @@ FMaterial::FMaterial(FEngine& engine, const Builder& builder,
|
||||
|
||||
processBlendingMode(parser);
|
||||
processSpecializationConstants(engine, builder, parser);
|
||||
processMutableSpecializationConstants(engine, builder, parser);
|
||||
processPushConstants(engine, parser);
|
||||
processDescriptorSets(engine, parser);
|
||||
|
||||
switch (mMaterialDomain) {
|
||||
case filament::MaterialDomain::SURFACE:
|
||||
mVariantBits = VARIANT_BITS;
|
||||
break;
|
||||
case filament::MaterialDomain::POST_PROCESS:
|
||||
mVariantBits = POST_PROCESS_VARIANT_BITS;
|
||||
break;
|
||||
case filament::MaterialDomain::COMPUTE:
|
||||
mVariantBits = 0;
|
||||
break;
|
||||
}
|
||||
mCachedPrograms = FixedCapacityVector<backend::Handle<backend::HwProgram>>(
|
||||
1 << (mVariantBits + mMaterialMutableConstants.size()));
|
||||
|
||||
precacheDepthVariants(engine);
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
@@ -482,17 +466,13 @@ void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
UserVariantFilterMask const variantFilter =
|
||||
~variantSpec & UserVariantFilterMask(UserVariantFilterBit::ALL);
|
||||
|
||||
int mutableConstantVariations = 1 << mMaterialMutableConstants.size();
|
||||
|
||||
if (UTILS_LIKELY(mEngine.getDriverApi().isParallelShaderCompileSupported())) {
|
||||
auto const& variants = isVariantLit() ?
|
||||
VariantUtils::getLitVariants() : VariantUtils::getUnlitVariants();
|
||||
for (auto const variant: variants) {
|
||||
if (!variantFilter || variant == Variant::filterUserVariant(variant, variantFilter)) {
|
||||
if (hasVariant(variant)) {
|
||||
for (int spec = 0; spec < mutableConstantVariations; spec++) {
|
||||
prepareProgram(variant, Program::MutableSpecConstantsInfo(spec), priority);
|
||||
}
|
||||
prepareProgram(variant, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -573,15 +553,14 @@ bool FMaterial::hasVariant(Variant const variant) const noexcept {
|
||||
}
|
||||
|
||||
void FMaterial::prepareProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
assert_invariant(mEngine.hasFeatureLevel(mFeatureLevel));
|
||||
switch (getMaterialDomain()) {
|
||||
case MaterialDomain::SURFACE:
|
||||
getSurfaceProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
getSurfaceProgramSlow(variant, priorityQueue);
|
||||
break;
|
||||
case MaterialDomain::POST_PROCESS:
|
||||
getPostProcessProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
getPostProcessProgramSlow(variant, priorityQueue);
|
||||
break;
|
||||
case MaterialDomain::COMPUTE:
|
||||
// TODO: implement MaterialDomain::COMPUTE
|
||||
@@ -590,7 +569,6 @@ void FMaterial::prepareProgramSlow(Variant const variant,
|
||||
}
|
||||
|
||||
void FMaterial::getSurfaceProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
// filterVariant() has already been applied in generateCommands(), shouldn't be needed here
|
||||
// if we're unlit, we don't have any bits that correspond to lit materials
|
||||
@@ -601,25 +579,25 @@ void FMaterial::getSurfaceProgramSlow(Variant const variant,
|
||||
Variant const vertexVariant = Variant::filterVariantVertex(variant);
|
||||
Variant const fragmentVariant = Variant::filterVariantFragment(variant);
|
||||
|
||||
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant, mutableSpecConstants) };
|
||||
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant) };
|
||||
pb.priorityQueue(priorityQueue);
|
||||
pb.multiview(
|
||||
mEngine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW &&
|
||||
Variant::isStereoVariant(variant));
|
||||
createAndCacheProgram(std::move(pb), variant, mutableSpecConstants);
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
}
|
||||
|
||||
void FMaterial::getPostProcessProgramSlow(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
Program pb{ getProgramWithVariants(variant, variant, variant, mutableSpecConstants) };
|
||||
Program pb{ getProgramWithVariants(variant, variant, variant) };
|
||||
pb.priorityQueue(priorityQueue);
|
||||
createAndCacheProgram(std::move(pb), variant, mutableSpecConstants);
|
||||
createAndCacheProgram(std::move(pb), variant);
|
||||
}
|
||||
|
||||
Program FMaterial::getProgramWithVariants(Variant variant,
|
||||
Variant vertexVariant, Variant fragmentVariant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const {
|
||||
Program FMaterial::getProgramWithVariants(
|
||||
Variant variant,
|
||||
Variant vertexVariant,
|
||||
Variant fragmentVariant) const {
|
||||
FEngine const& engine = mEngine;
|
||||
const ShaderModel sm = engine.getShaderModel();
|
||||
const bool isNoop = engine.getBackend() == Backend::NOOP;
|
||||
@@ -680,34 +658,29 @@ Program FMaterial::getProgramWithVariants(Variant variant,
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_RENDERABLE]);
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_MATERIAL,
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_MATERIAL]);
|
||||
program.specializationConstants(mSpecializationConstants,
|
||||
CONFIG_MAX_RESERVED_SPEC_CONSTANTS + mMaterialConstants.size(), mutableSpecConstants);
|
||||
program.specializationConstants(mSpecializationConstants);
|
||||
|
||||
program.pushConstants(ShaderStage::VERTEX, mPushConstants[uint8_t(ShaderStage::VERTEX)]);
|
||||
program.pushConstants(ShaderStage::FRAGMENT, mPushConstants[uint8_t(ShaderStage::FRAGMENT)]);
|
||||
|
||||
program.cacheId(
|
||||
hash::combine(size_t(mCacheId),
|
||||
hash::combine(variant.key, mutableSpecConstants.getValue())));
|
||||
program.cacheId(hash::combine(size_t(mCacheId), variant.key));
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant const variant,
|
||||
Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const noexcept {
|
||||
FEngine const& engine = mEngine;
|
||||
DriverApi& driverApi = mEngine.getDriverApi();
|
||||
|
||||
bool const isShared = isSharedVariant(variant);
|
||||
size_t const cacheIndex = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
|
||||
// Check if the default material has this program cached
|
||||
if (isShared) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
if (pDefaultMaterial) {
|
||||
auto const program = pDefaultMaterial->mCachedPrograms[cacheIndex];
|
||||
auto const program = pDefaultMaterial->mCachedPrograms[variant.key];
|
||||
if (program) {
|
||||
mCachedPrograms[cacheIndex] = program;
|
||||
mCachedPrograms[variant.key] = program;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -716,17 +689,17 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant const variant,
|
||||
auto const program = driverApi.createProgram(std::move(p));
|
||||
driverApi.setDebugTag(program.getId(), mName);
|
||||
assert_invariant(program);
|
||||
mCachedPrograms[cacheIndex] = program;
|
||||
mCachedPrograms[variant.key] = program;
|
||||
|
||||
// If the default material doesn't already have this program cached, and all caching conditions
|
||||
// are met (Surface Domain and no custom depth shader), cache it now.
|
||||
// New Materials will inherit these program automatically.
|
||||
if (isShared) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[cacheIndex]) {
|
||||
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[variant.key]) {
|
||||
// set the tag to the default material name
|
||||
driverApi.setDebugTag(program.getId(), mName);
|
||||
pDefaultMaterial->mCachedPrograms[cacheIndex] = program;
|
||||
pDefaultMaterial->mCachedPrograms[variant.key] = program;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -774,14 +747,6 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
|
||||
return count;
|
||||
}
|
||||
|
||||
std::optional<uint32_t> FMaterial::getMutableConstantId(std::string_view name) const noexcept {
|
||||
auto const pos = mMutableSpecializationConstantsNameToIndex.find(name);
|
||||
if (pos != mMutableSpecializationConstantsNameToIndex.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
|
||||
// Swaps in an edited version of the original package that was used to create the material. The
|
||||
@@ -840,9 +805,7 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
|
||||
|
||||
#endif // FILAMENT_ENABLE_MATDBG
|
||||
|
||||
[[nodiscard]] Handle<HwProgram> FMaterial::getProgramWithMATDBG(Variant const variant,
|
||||
Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
size_t cacheIndex = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
[[nodiscard]] Handle<HwProgram> FMaterial::getProgramWithMATDBG(Variant const variant) const noexcept {
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
assert_invariant((size_t)variant.key < VARIANT_COUNT);
|
||||
std::unique_lock lock(mActiveProgramsLock);
|
||||
@@ -857,13 +820,13 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
|
||||
lock.unlock();
|
||||
if (isSharedVariant(variant)) {
|
||||
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
|
||||
if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[cacheIndex]) {
|
||||
return pDefaultMaterial->getProgram(variant, mutableSpecConstants);
|
||||
if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[variant.key]) {
|
||||
return pDefaultMaterial->getProgram(variant);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
assert_invariant(mCachedPrograms[cacheIndex]);
|
||||
return mCachedPrograms[cacheIndex];
|
||||
assert_invariant(mCachedPrograms[variant.key]);
|
||||
return mCachedPrograms[variant.key];
|
||||
}
|
||||
|
||||
void FMaterial::destroyPrograms(FEngine& engine,
|
||||
@@ -876,7 +839,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
case MaterialDomain::SURFACE: {
|
||||
if (mIsDefaultMaterial || mHasCustomDepthShader) {
|
||||
// default material, or we have custom depth shaders, we destroy all variants
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variants, and this generates traffic in the command queue.
|
||||
@@ -894,7 +857,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
UTILS_UNUSED_IN_RELEASE
|
||||
auto const UTILS_NULLABLE pDefaultMaterial = engine.getDefaultMaterial();
|
||||
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
@@ -906,7 +869,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
// During Engine::shutdown, auto-cleanup destroys the
|
||||
// default material first
|
||||
assert_invariant(!pDefaultMaterial ||
|
||||
pDefaultMaterial->mCachedPrograms[k & (VARIANT_COUNT - 1)]);
|
||||
pDefaultMaterial->mCachedPrograms[k]);
|
||||
// we don't own this variant, skip, but clear the entry.
|
||||
cachedPrograms[k].clear();
|
||||
continue;
|
||||
@@ -920,7 +883,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
break;
|
||||
}
|
||||
case MaterialDomain::POST_PROCESS: {
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
@@ -932,13 +895,8 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
break;
|
||||
}
|
||||
case MaterialDomain::COMPUTE: {
|
||||
for (size_t k = 0, n = cachedPrograms.size(); k < n; ++k) {
|
||||
// Only destroy if the handle is valid. Not strictly needed, but we have a lot
|
||||
// of variant, and this generates traffic in the command queue.
|
||||
if (cachedPrograms[k]) {
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[k]));
|
||||
}
|
||||
}
|
||||
// Compute programs don't have variants
|
||||
driverApi.destroyProgram(std::move(cachedPrograms[0]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1173,19 +1131,6 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processMutableSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* const parser) {
|
||||
// Older materials won't have a mutable constants chunk, but that's okay.
|
||||
parser->getMutableConstants(&mMaterialMutableConstants);
|
||||
for (size_t i = 0, c = mMaterialMutableConstants.size(); i < c; i++) {
|
||||
auto& item = mMaterialMutableConstants[i];
|
||||
// the key can be a string_view because mMaterialMutableConstants owns the CString
|
||||
std::string_view const key{ item.name.data(), item.name.size() };
|
||||
mMutableSpecializationConstantsNameToIndex[key] = i;
|
||||
mDefaultMutableSpecializationConstants.set(i, item.defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processPushConstants(FEngine&, MaterialParser const* parser) {
|
||||
FixedCapacityVector<Program::PushConstant>& vertexConstants =
|
||||
mPushConstants[uint8_t(ShaderStage::VERTEX)];
|
||||
@@ -1241,9 +1186,7 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
}
|
||||
assert_invariant(Variant::isValidDepthVariant(variant));
|
||||
if (hasVariant(variant)) {
|
||||
// The default material shouldn't ever have mutable spec constants.
|
||||
prepareProgram(variant, Program::MutableSpecConstantsInfo(0),
|
||||
CompilerPriorityQueue::HIGH);
|
||||
prepareProgram(variant, CompilerPriorityQueue::HIGH);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -1256,16 +1199,9 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
assert_invariant(pDefaultMaterial);
|
||||
auto const allDepthVariants = VariantUtils::getDepthVariants();
|
||||
int mutableConstantVariations = 1 << mMaterialMutableConstants.size();
|
||||
for (auto const variant: allDepthVariants) {
|
||||
assert_invariant(Variant::isValidDepthVariant(variant));
|
||||
size_t defaultCacheIndex = pDefaultMaterial->getCachedProgramIndex(variant,
|
||||
Program::MutableSpecConstantsInfo(0));
|
||||
for (int spec = 0; spec < mutableConstantVariations; spec++) {
|
||||
size_t cacheIndex = getCachedProgramIndex(variant,
|
||||
Program::MutableSpecConstantsInfo(spec));
|
||||
mCachedPrograms[cacheIndex] = pDefaultMaterial->mCachedPrograms[defaultCacheIndex];
|
||||
}
|
||||
mCachedPrograms[variant.key] = pDefaultMaterial->mCachedPrograms[variant.key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,9 +132,8 @@ public:
|
||||
|
||||
FEngine& getEngine() const noexcept { return mEngine; }
|
||||
|
||||
bool isCached(Variant const variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept {
|
||||
return bool(mCachedPrograms[getCachedProgramIndex(variant, mutableSpecConstants)]);
|
||||
bool isCached(Variant const variant) const noexcept {
|
||||
return bool(mCachedPrograms[variant.key]);
|
||||
}
|
||||
|
||||
void invalidate(Variant::type_t variantMask = 0, Variant::type_t variantValue = 0) noexcept;
|
||||
@@ -143,26 +142,22 @@ public:
|
||||
// Must be called outside of backend render pass.
|
||||
// Must be called before getProgram() below.
|
||||
void prepareProgram(Variant const variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
// prepareProgram() is called for each RenderPrimitive in the scene, so it must be efficient.
|
||||
if (UTILS_UNLIKELY(!isCached(variant, mutableSpecConstants))) {
|
||||
prepareProgramSlow(variant, mutableSpecConstants, priorityQueue);
|
||||
if (UTILS_UNLIKELY(!isCached(variant))) {
|
||||
prepareProgramSlow(variant, priorityQueue);
|
||||
}
|
||||
}
|
||||
|
||||
// getProgram returns the backend program for the material's given variant and set of mutable
|
||||
// spec constants.
|
||||
// getProgram returns the backend program for the material's given variant.
|
||||
// Must be called after prepareProgram().
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgram(Variant const variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
|
||||
backend::Handle<backend::HwProgram> getProgram(Variant const variant) const noexcept {
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
return getProgramWithMATDBG(variant, mutableSpecConstants);
|
||||
return getProgramWithMATDBG(variant);
|
||||
#endif
|
||||
size_t index = getCachedProgramIndex(variant, mutableSpecConstants);
|
||||
assert_invariant(mCachedPrograms[index]);
|
||||
return mCachedPrograms[index];
|
||||
assert_invariant(mCachedPrograms[variant.key]);
|
||||
return mCachedPrograms[variant.key];
|
||||
}
|
||||
|
||||
// MaterialInstance::use() binds descriptor sets before drawing. For shared variants,
|
||||
@@ -183,8 +178,7 @@ public:
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept;
|
||||
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant) const noexcept;
|
||||
|
||||
bool isVariantLit() const noexcept { return mIsVariantLit; }
|
||||
|
||||
@@ -241,12 +235,6 @@ public:
|
||||
}
|
||||
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
|
||||
|
||||
uint32_t getMutableConstantCount() const noexcept { return mMaterialMutableConstants.size(); }
|
||||
std::optional<uint32_t> getMutableConstantId(std::string_view name) const noexcept;
|
||||
backend::Program::MutableSpecConstantsInfo getDefaultMutableConstants() const noexcept {
|
||||
return mDefaultMutableSpecializationConstants;
|
||||
}
|
||||
|
||||
uint32_t generateMaterialInstanceId() const noexcept { return mMaterialInstanceId++; }
|
||||
|
||||
void destroyPrograms(FEngine& engine,
|
||||
@@ -300,48 +288,34 @@ public:
|
||||
private:
|
||||
bool hasVariant(Variant variant) const noexcept;
|
||||
void prepareProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
void getSurfaceProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
void getPostProcessProgramSlow(Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
backend::Program getProgramWithVariants(Variant variant,
|
||||
Variant vertexVariant, Variant fragmentVariant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const;
|
||||
Variant vertexVariant, Variant fragmentVariant) const;
|
||||
|
||||
void processBlendingMode(MaterialParser const* parser);
|
||||
|
||||
void processSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* parser);
|
||||
|
||||
void processMutableSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* parser);
|
||||
|
||||
void processPushConstants(FEngine& engine, MaterialParser const* parser);
|
||||
|
||||
void precacheDepthVariants(FEngine& engine);
|
||||
|
||||
void processDescriptorSets(FEngine& engine, MaterialParser const* parser);
|
||||
|
||||
void createAndCacheProgram(backend::Program&& p, Variant variant,
|
||||
backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept;
|
||||
void createAndCacheProgram(backend::Program&& p, Variant variant) const noexcept;
|
||||
|
||||
inline bool isSharedVariant(Variant const variant) const {
|
||||
return (mMaterialDomain == MaterialDomain::SURFACE) && !mIsDefaultMaterial &&
|
||||
!mHasCustomDepthShader && Variant::isValidDepthVariant(variant);
|
||||
}
|
||||
|
||||
inline size_t getCachedProgramIndex(const Variant variant,
|
||||
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept {
|
||||
return static_cast<size_t>(variant.key) |
|
||||
(static_cast<size_t>(mutableSpecConstants.getValue()) << mVariantBits);
|
||||
}
|
||||
|
||||
// try to order by frequency of use
|
||||
mutable utils::FixedCapacityVector<backend::Handle<backend::HwProgram>> mCachedPrograms;
|
||||
mutable std::array<backend::Handle<backend::HwProgram>, VARIANT_COUNT> mCachedPrograms;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayout;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayoutVsm;
|
||||
DescriptorSetLayout mDescriptorSetLayout;
|
||||
@@ -377,7 +351,6 @@ private:
|
||||
bool mHasCustomDepthShader = false;
|
||||
bool mIsDefaultMaterial = false;
|
||||
bool mSpecularAntiAliasing = false;
|
||||
uint8_t mVariantBits;
|
||||
|
||||
// reserve some space to construct the default material instance
|
||||
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
|
||||
@@ -402,13 +375,6 @@ private:
|
||||
// current specialization constants for the HwProgram
|
||||
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mSpecializationConstants;
|
||||
|
||||
// Mutable constants defined by this Material.
|
||||
utils::FixedCapacityVector<MaterialMutableConstant> mMaterialMutableConstants;
|
||||
// A map from the Constant name to the mMutableMaterialConstant index
|
||||
std::unordered_map<std::string_view, uint32_t> mMutableSpecializationConstantsNameToIndex;
|
||||
// Default values for the mutable constants.
|
||||
backend::Program::MutableSpecConstantsInfo mDefaultMutableSpecializationConstants;
|
||||
|
||||
// current push constants for the HwProgram
|
||||
std::array<utils::FixedCapacityVector<backend::Program::PushConstant>,
|
||||
backend::Program::SHADER_TYPE_COUNT>
|
||||
|
||||
@@ -63,7 +63,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
const char* name) noexcept
|
||||
: mMaterial(material),
|
||||
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
|
||||
mConstants(material->getDefaultMutableConstants()),
|
||||
mCulling(CullingMode::BACK),
|
||||
mShadowCulling(CullingMode::BACK),
|
||||
mDepthFunc(RasterState::DepthFunc::LE),
|
||||
@@ -129,7 +128,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
mTextureParameters(other->mTextureParameters),
|
||||
mDescriptorSet(other->mDescriptorSet.duplicate(
|
||||
"MaterialInstance", mMaterial->getDescriptorSetLayout())),
|
||||
mConstants(other->mConstants),
|
||||
mPolygonOffset(other->mPolygonOffset),
|
||||
mStencilState(other->mStencilState),
|
||||
mMaskThreshold(other->mMaskThreshold),
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/bitset.h>
|
||||
@@ -69,7 +68,7 @@ public:
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
void commitStreamUniformAssociations(FEngine::DriverApi& driver);
|
||||
|
||||
|
||||
void commit(FEngine& engine) const;
|
||||
|
||||
void commit(FEngine::DriverApi& driver) const;
|
||||
@@ -245,10 +244,6 @@ public:
|
||||
|
||||
using MaterialInstance::setParameter;
|
||||
|
||||
const backend::Program::MutableSpecConstantsInfo getMutableSpecConstants() const noexcept {
|
||||
return mConstants;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class FMaterial;
|
||||
friend class MaterialInstance;
|
||||
@@ -271,12 +266,6 @@ private:
|
||||
template<typename T>
|
||||
T getParameterImpl(std::string_view name) const;
|
||||
|
||||
template<typename T>
|
||||
void setConstantImpl(std::string_view name, T const& value);
|
||||
|
||||
template<typename T>
|
||||
T getConstantImpl(std::string_view name) const;
|
||||
|
||||
// keep these grouped, they're accessed together in the render-loop
|
||||
FMaterial const* mMaterial = nullptr;
|
||||
|
||||
@@ -290,7 +279,6 @@ private:
|
||||
mutable DescriptorSet mDescriptorSet;
|
||||
UniformBuffer mUniforms;
|
||||
bool mHasStreamUniformAssociations = false;
|
||||
backend::Program::MutableSpecConstantsInfo mConstants;
|
||||
|
||||
backend::PolygonOffset mPolygonOffset{};
|
||||
backend::StencilState mStencilState{};
|
||||
|
||||
@@ -653,9 +653,7 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept {
|
||||
FILAMENT_CHECK_PRECONDITION(formatMipmappable)
|
||||
<< "Texture format " << (unsigned)mFormat << " is not mipmappable.";
|
||||
|
||||
auto const& featureFlags = downcast(engine).features.engine.debug;
|
||||
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(any(mUsage & TextureUsage::GEN_MIPMAPPABLE),
|
||||
featureFlags.assert_texture_can_generate_mipmap)
|
||||
FILAMENT_CHECK_PRECONDITION(any(mUsage & TextureUsage::GEN_MIPMAPPABLE))
|
||||
<< "Texture usage does not have GEN_MIPMAPPABLE set";
|
||||
|
||||
if (mLevelCount < 2 || (mWidth == 1 && mHeight == 1)) {
|
||||
|
||||
@@ -659,12 +659,10 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
|
||||
if (hasDynamicLighting()) {
|
||||
auto& froxelizer = mFroxelizer;
|
||||
if (froxelizer.prepare(driver, rootArenaScope, viewport,
|
||||
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf,
|
||||
cameraInfo.clipTransform)) {
|
||||
cameraInfo.projection, cameraInfo.zn, cameraInfo.zf)) {
|
||||
// TODO: might be more consistent to do this in prepareLighting(), but it's not
|
||||
// strictly necessary
|
||||
getColorPassDescriptorSet().prepareDynamicLights(mFroxelizer, mFroxelVizEnabled);
|
||||
mFroxelConfigurationAge++;
|
||||
getColorPassDescriptorSet().prepareDynamicLights(mFroxelizer);
|
||||
}
|
||||
// We need to pass viewMatrix by value here because it extends the scope of this
|
||||
// function.
|
||||
@@ -1436,10 +1434,6 @@ void FView::setStereoscopicOptions(const StereoscopicOptions& options) noexcept
|
||||
mStereoscopicOptions = options;
|
||||
}
|
||||
|
||||
View::FroxelConfigurationInfoWithAge FView::getFroxelConfigurationInfo() const noexcept {
|
||||
return { mFroxelizer.getFroxelConfigurationInfo(), mFroxelConfigurationAge };
|
||||
}
|
||||
|
||||
void FView::setMaterialGlobal(uint32_t const index, float4 const& value) {
|
||||
FILAMENT_CHECK_PRECONDITION(index < 4)
|
||||
<< "material global variable index (" << +index << ") out of range";
|
||||
|
||||
@@ -240,12 +240,6 @@ public:
|
||||
return mShadowMapManager->getDirectionalShadowCameras();
|
||||
}
|
||||
|
||||
void setFroxelVizEnabled(bool const enabled) noexcept {
|
||||
mFroxelVizEnabled = enabled;
|
||||
}
|
||||
|
||||
FroxelConfigurationInfoWithAge getFroxelConfigurationInfo() const noexcept;
|
||||
|
||||
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
|
||||
assert_invariant(!renderTarget || !mMultiSampleAntiAliasingOptions.enabled ||
|
||||
!renderTarget->hasSampleableDepth());
|
||||
@@ -558,8 +552,6 @@ private:
|
||||
|
||||
mutable Froxelizer mFroxelizer;
|
||||
utils::JobSystem::Job* mFroxelizerSync = nullptr;
|
||||
bool mFroxelVizEnabled = false;
|
||||
uint32_t mFroxelConfigurationAge = 0;
|
||||
|
||||
Viewport mViewport;
|
||||
bool mCulling = true;
|
||||
|
||||
@@ -372,13 +372,12 @@ void ColorPassDescriptorSet::prepareAmbientLight(FEngine const& engine, FIndirec
|
||||
});
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareDynamicLights(Froxelizer& froxelizer, bool const enableFroxelViz) noexcept {
|
||||
void ColorPassDescriptorSet::prepareDynamicLights(Froxelizer& froxelizer) noexcept {
|
||||
auto& s = mUniforms.edit();
|
||||
froxelizer.updateUniforms(s);
|
||||
float const f = froxelizer.getLightFar();
|
||||
// TODO: make the falloff rate a parameter
|
||||
s.lightFarAttenuationParams = 0.5f * float2{ 10.0f, 10.0f / (f * f) };
|
||||
s.enableFroxelViz = enableFroxelViz;
|
||||
}
|
||||
|
||||
void ColorPassDescriptorSet::prepareShadowMapping(BufferObjectHandle shadowUniforms) noexcept {
|
||||
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
void prepareAmbientLight(FEngine const& engine,
|
||||
FIndirectLight const& ibl, float intensity, float exposure) noexcept;
|
||||
|
||||
void prepareDynamicLights(Froxelizer& froxelizer, bool enableFroxelViz) noexcept;
|
||||
void prepareDynamicLights(Froxelizer& froxelizer) noexcept;
|
||||
|
||||
void prepareShadowVSM(TextureHandle texture,
|
||||
VsmShadowOptions const& options) noexcept;
|
||||
|
||||
@@ -65,10 +65,6 @@ material {
|
||||
type : float,
|
||||
name : thicknessHeuristic
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : constThickness
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : ssctShadowDistance
|
||||
@@ -102,18 +98,6 @@ material {
|
||||
name : ssctSampleCount
|
||||
}
|
||||
],
|
||||
constants : [
|
||||
{
|
||||
type : bool,
|
||||
name : useVisibilityBitmasks,
|
||||
default : false
|
||||
},
|
||||
{
|
||||
type : bool,
|
||||
name : linearThickness,
|
||||
default : false
|
||||
}
|
||||
],
|
||||
variables : [
|
||||
vertex
|
||||
],
|
||||
|
||||
@@ -65,10 +65,6 @@ material {
|
||||
type : float,
|
||||
name : thicknessHeuristic
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : constThickness
|
||||
},
|
||||
{
|
||||
type : float,
|
||||
name : ssctShadowDistance
|
||||
@@ -102,18 +98,6 @@ material {
|
||||
name : ssctSampleCount
|
||||
}
|
||||
],
|
||||
constants : [
|
||||
{
|
||||
type : bool,
|
||||
name : useVisibilityBitmasks,
|
||||
default : false
|
||||
},
|
||||
{
|
||||
type : bool,
|
||||
name : linearThickness,
|
||||
default : false
|
||||
}
|
||||
],
|
||||
outputs : [
|
||||
{
|
||||
name : aoData,
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "../utils/geometry.fs"
|
||||
|
||||
#define rsqrt inversesqrt
|
||||
#define SECTOR_COUNT 32u
|
||||
|
||||
#ifndef COMPUTE_BENT_NORMAL
|
||||
#error COMPUTE_BENT_NORMAL must be set
|
||||
@@ -59,67 +58,6 @@ float spatialOffsetsNoise(float2 uv) {
|
||||
return 0.25 * float((position.y - position.x) & 3);
|
||||
}
|
||||
|
||||
// If the new sample value is greater then the current one, update the value with some fallOff.
|
||||
// Otherwise, apply thicknessHeuristic.
|
||||
float updateHorizon(float sampleHorizonCos, float currentHorizonCos, float fallOff) {
|
||||
return sampleHorizonCos > currentHorizonCos
|
||||
? mix(sampleHorizonCos, currentHorizonCos, fallOff)
|
||||
: mix(currentHorizonCos, sampleHorizonCos, materialParams.thicknessHeuristic);
|
||||
}
|
||||
|
||||
float calculateHorizonCos(highp vec3 sampleDelta, highp vec3 viewDir, float horizonCos) {
|
||||
highp float sqSampleDist = dot(sampleDelta, sampleDelta);
|
||||
float invSampleDist = rsqrt(sqSampleDist);
|
||||
|
||||
// Use the view space radius to calculate the fallOff
|
||||
float fallOff = saturate(sqSampleDist * materialParams.invRadiusSquared * 2.0);
|
||||
|
||||
// sample horizon cos
|
||||
float shc = dot(sampleDelta, viewDir) * invSampleDist;
|
||||
|
||||
return updateHorizon(shc, horizonCos, fallOff);
|
||||
}
|
||||
|
||||
// https://cdrinmatane.github.io/posts/ssaovb-code/
|
||||
// https://github.com/cdrinmatane/SSRT3/blob/main/HDRP/Shaders/Resources/SSRTCS.compute
|
||||
uint updateSectors(float minHorizon, float maxHorizon, uint globalOccludedBitfield) {
|
||||
uint startHorizonInt = uint(minHorizon * float(SECTOR_COUNT));
|
||||
uint angleHorizonInt = uint(ceil(saturate(maxHorizon-minHorizon) * float(SECTOR_COUNT)));
|
||||
uint angleHorizonBitfield = angleHorizonInt > 0u ? (0xFFFFFFFFu >> (SECTOR_COUNT-angleHorizonInt)) : 0u;
|
||||
uint currentOccludedBitfield = angleHorizonBitfield << startHorizonInt;
|
||||
return globalOccludedBitfield | currentOccludedBitfield;
|
||||
}
|
||||
|
||||
// https://cdrinmatane.github.io/posts/ssaovb-code/
|
||||
// https://github.com/cdrinmatane/SSRT3/blob/main/HDRP/Shaders/Resources/SSRTCS.compute
|
||||
// The visibility bitmask method replaces the traditional two horizon angles with a bitmask
|
||||
// for each slice. This bitmask flags whether each sector is occluded or not,
|
||||
// which enables surfaces to be modeled with constant thickness, overcoming the limitation
|
||||
// of treating them as a simple height field.
|
||||
uint calculateVisibilityMask(highp vec3 deltaPos, highp vec3 viewDir, float samplingDirection,
|
||||
uint globalOccludedBitfield, float n, highp vec3 origin) {
|
||||
vec2 frontBackHorizon;
|
||||
float linearThicknessMultiplier = materialConstants_linearThickness
|
||||
? saturate(origin.z * materialParams.invFarPlane) * 100.0
|
||||
: 1.0;
|
||||
vec3 deltaPosBackface = deltaPos - viewDir * materialParams.constThickness * linearThicknessMultiplier;
|
||||
|
||||
// Project sample onto the unit circle and compute the angle relative to V
|
||||
frontBackHorizon.x = dot(normalize(deltaPos), viewDir);
|
||||
frontBackHorizon.y = dot(normalize(deltaPosBackface), viewDir);
|
||||
|
||||
frontBackHorizon.x = acosFast(frontBackHorizon.x);
|
||||
frontBackHorizon.y = acosFast(frontBackHorizon.y);
|
||||
|
||||
// Shift sample from V to normal, clamp in [0..1]
|
||||
frontBackHorizon = clamp(((samplingDirection * -frontBackHorizon) + n + HALF_PI) / PI, 0.0, 1.0);
|
||||
|
||||
// Sampling direction inverts min/max angles
|
||||
frontBackHorizon = samplingDirection >= 0.0 ? frontBackHorizon.yx : frontBackHorizon.xy;
|
||||
|
||||
return updateSectors(frontBackHorizon.x, frontBackHorizon.y, globalOccludedBitfield);
|
||||
}
|
||||
|
||||
void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal,
|
||||
highp vec2 uv, highp vec3 origin, vec3 normal) {
|
||||
vec2 uvSamplePos = uv;
|
||||
@@ -146,26 +84,20 @@ void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal,
|
||||
vec3 direction = vec3(cosPhi, sinPhi, 0.0);
|
||||
// Project direction onto the plane orthogonal to viewDir.
|
||||
vec3 orthoDirection = normalize(direction - (dot(direction, viewDir)*viewDir));
|
||||
// `axis` is orthogonal to direction and viewDir (basically the normal of the slice plane)
|
||||
// axis is orthogonal to direction and viewDir (basically the normal of the slice plane)
|
||||
// Used to define projectedNormal
|
||||
vec3 axis = cross(orthoDirection, viewDir);
|
||||
// `projNormal` is the normal projected onto the slice plane
|
||||
// Project the normal onto the slice plane
|
||||
vec3 projNormal = normal - axis * dot(normal, axis);
|
||||
|
||||
float signNorm = sign(dot(orthoDirection, projNormal));
|
||||
float projNormalLength = length(projNormal);
|
||||
float cosNorm = saturate(dot(projNormal, viewDir) / projNormalLength);
|
||||
|
||||
// `n` is the signed angle between projNormal and viewDir
|
||||
float n = signNorm * acosFast(cosNorm);
|
||||
|
||||
// These are only used in non-bitmask mode
|
||||
float horizonCos0 = -1.0;
|
||||
float horizonCos1 = -1.0;
|
||||
|
||||
// This is only used in bitmask mode
|
||||
uint globalOccludedBitfield = 0u;
|
||||
|
||||
for (float j = 0.0; j < materialParams.stepsPerSlice; j += 1.0) {
|
||||
// At least move 1 pixel forward in the screen-space
|
||||
vec2 sampleOffset = max((j + initialRayStep)*stepRadius, 1.0 + j) * omega;
|
||||
@@ -184,36 +116,33 @@ void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal,
|
||||
|
||||
highp vec3 sampleDelta0 = (samplePos0 - origin);
|
||||
highp vec3 sampleDelta1 = (samplePos1 - origin);
|
||||
highp vec2 sqSampleDist = vec2(dot(sampleDelta0, sampleDelta0), dot(sampleDelta1, sampleDelta1));
|
||||
vec2 invSampleDist = rsqrt(sqSampleDist);
|
||||
|
||||
if (materialConstants_useVisibilityBitmasks) {
|
||||
globalOccludedBitfield = calculateVisibilityMask(sampleDelta0, viewDir, 1.0, globalOccludedBitfield, n, origin);
|
||||
globalOccludedBitfield = calculateVisibilityMask(sampleDelta1, viewDir, -1.0, globalOccludedBitfield, n, origin);
|
||||
}
|
||||
else {
|
||||
horizonCos0 = calculateHorizonCos(sampleDelta0, viewDir, horizonCos0);
|
||||
horizonCos1 = calculateHorizonCos(sampleDelta1, viewDir, horizonCos1);
|
||||
}
|
||||
// Use the view space radius to calculate the fallOff
|
||||
vec2 fallOff = saturate(sqSampleDist.xy * materialParams.invRadiusSquared * 2.0);
|
||||
|
||||
// sample horizon cos
|
||||
float shc0 = dot(sampleDelta0, viewDir) * invSampleDist.x;
|
||||
float shc1 = dot(sampleDelta1, viewDir) * invSampleDist.y;
|
||||
|
||||
// If the new sample value is greater then the current one, update the value with some fallOff.
|
||||
// Otherwise, apply thicknessHeuristic.
|
||||
horizonCos0 = shc0 > horizonCos0 ? mix(shc0, horizonCos0, fallOff.x) : mix(horizonCos0, shc0, materialParams.thicknessHeuristic);
|
||||
horizonCos1 = shc1 > horizonCos1 ? mix(shc1, horizonCos1, fallOff.y) : mix(horizonCos1, shc1, materialParams.thicknessHeuristic);
|
||||
}
|
||||
|
||||
if (materialConstants_useVisibilityBitmasks) {
|
||||
// Calculate the portion of the occluded sector
|
||||
visibility += 1.0 - float(bitCount(globalOccludedBitfield)) / float(SECTOR_COUNT);
|
||||
float h0 = -acosFast(horizonCos1);
|
||||
float h1 = acosFast(horizonCos0);
|
||||
h0 = n + clamp(h0 - n, -HALF_PI, HALF_PI);
|
||||
h1 = n + clamp(h1 - n, -HALF_PI, HALF_PI);
|
||||
|
||||
// Note: bent normal calculation is not supported in visibility bitmasks mode
|
||||
}
|
||||
else {
|
||||
float h0 = -acosFast(horizonCos1);
|
||||
float h1 = acosFast(horizonCos0);
|
||||
h0 = n + clamp(h0 - n, -HALF_PI, HALF_PI);
|
||||
h1 = n + clamp(h1 - n, -HALF_PI, HALF_PI);
|
||||
#if COMPUTE_BENT_NORMAL
|
||||
float angle = 0.5 * (h0 + h1);
|
||||
bentNormal += viewDir * cos(angle) - orthoDirection * sin(angle);
|
||||
#endif
|
||||
|
||||
#if COMPUTE_BENT_NORMAL
|
||||
float angle = 0.5 * (h0 + h1);
|
||||
bentNormal += viewDir * cos(angle) - orthoDirection * sin(angle);
|
||||
#endif
|
||||
|
||||
visibility += projNormalLength * (integrateArcCosWeight(h0, n) + integrateArcCosWeight(h1, n));
|
||||
}
|
||||
visibility += projNormalLength * (integrateArcCosWeight(h0, n) + integrateArcCosWeight(h1, n));
|
||||
}
|
||||
|
||||
obscurance = 1.0 - saturate(visibility * materialParams.sliceCount.y);
|
||||
|
||||
@@ -672,7 +672,7 @@ TEST(FilamentTest, FroxelData) {
|
||||
|
||||
Froxelizer froxelData(*engine);
|
||||
froxelData.setOptions(5, 100);
|
||||
froxelData.prepare(engine->getDriverApi(), scope, vp, p, 0.1, 100, {1,1,0,0});
|
||||
froxelData.prepare(engine->getDriverApi(), scope, vp, p, 0.1, 100);
|
||||
|
||||
Froxel f = froxelData.getFroxelAt(0,0,0);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.64.0"
|
||||
spec.version = "1.63.1"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
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.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.64.0/filament-v1.64.0-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.63.1/filament-v1.63.1-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -54,12 +54,10 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
MaterialDescriptorSetLayoutInfo = charTo64bitNum("MAT_DSLI"),
|
||||
MaterialProperties = charTo64bitNum("MAT_PROP"),
|
||||
MaterialConstants = charTo64bitNum("MAT_CONS"),
|
||||
MaterialMutableConstants = charTo64bitNum("MAT_MCNS"),
|
||||
MaterialPushConstants = charTo64bitNum("MAT_PCON"),
|
||||
|
||||
MaterialName = charTo64bitNum("MAT_NAME"),
|
||||
MaterialVersion = charTo64bitNum("MAT_VERS"),
|
||||
MaterialCompilationParameters = charTo64bitNum("MAT_CPRM"),
|
||||
MaterialCacheId = charTo64bitNum("MAT_UUID"),
|
||||
MaterialFeatureLevel = charTo64bitNum("MAT_FEAT"),
|
||||
MaterialShading = charTo64bitNum("MAT_SHAD"),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user