Compare commits

..
Author SHA1 Message Date
Kevin Granade c5e64564a1 Use the backend test cleanup helper more extensively
BUGS=[408310509]
2025-08-15 12:44:25 -07:00
155 changed files with 1681 additions and 3530 deletions
@@ -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
+2 -3
View File
@@ -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:
+2 -6
View File
@@ -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
-2
View File
@@ -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
+2 -7
View File
@@ -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_
+1
View File
@@ -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()
+2 -2
View File
@@ -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)
+6 -6
View File
@@ -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
-5
View File
@@ -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(
@@ -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 -1
View File
@@ -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.
-60
View File
@@ -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."
-3
View File
@@ -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
+6 -1
View File
@@ -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
}
+1 -2
View File
@@ -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
View File
@@ -221,8 +221,6 @@ export is an html with the output captured in a <code>&lt;pre&gt;</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
View File
@@ -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', '~&gt; 1.64.0'
<pre><code class="language-shell">pod 'Filament', '~&gt; 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-&gt;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-&gt;beginFrame(swapChain)) {
@@ -408,7 +408,7 @@ if (renderer-&gt;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
View File
@@ -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', '~&gt; 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 &amp; 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-&gt;createSwapChain(nativeWindow);
Renderer* renderer = engine-&gt;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-&gt;createCamera(EntityManager::get().create());
View* view = engine-&gt;createView();
Scene* scene = engine-&gt;createScene();
view-&gt;setCamera(camera);
view-&gt;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-&gt;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-&gt;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-&gt;beginFrame(swapChain)) {
// for each View
renderer-&gt;render(view);
renderer-&gt;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>
+2 -2
View File
@@ -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
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -5
View File
@@ -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
+1 -6
View File
@@ -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"
}
},
+24 -31
View File
@@ -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
+2 -2
View File
@@ -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.
-11
View File
@@ -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>
+1 -8
View File
@@ -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()
+1 -1
View File
@@ -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.
+1 -4
View File
@@ -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;
+5 -7
View File
@@ -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);
}
});
+2 -13
View File
@@ -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("
+1 -13
View File
@@ -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>";
+3 -51
View File
@@ -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
+36 -194
View File
@@ -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);
+81 -23
View File
@@ -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
+7 -40
View File
@@ -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;
-2
View File
@@ -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
-44
View File
@@ -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
+34 -45
View File
@@ -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
+43 -38
View File
@@ -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
+85 -78
View File
@@ -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);
+10 -8
View File
@@ -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();
}
}
}
+37 -49
View File
@@ -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
+15 -16
View File
@@ -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
+3 -8
View File
@@ -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
+5 -7
View File
@@ -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
+22 -26
View File
@@ -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
+6 -13
View File
@@ -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);
}
}
+31 -43
View File
@@ -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
+3 -53
View File
@@ -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.
*
+1 -14
View File
@@ -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%
};
-24
View File
@@ -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 {
+8 -27
View File
@@ -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
+2 -8
View File
@@ -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;
-40
View File
@@ -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();
}
-37
View File
@@ -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) {
-10
View File
@@ -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);
+2 -18
View File
@@ -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;
}
+2 -5
View File
@@ -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, &currentPipeline, sizeof(PipelineState)) != 0)) {
currentPipeline = pipeline;
-8
View File
@@ -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);
}
+1 -1
View File
@@ -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;
+32 -96
View File
@@ -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];
}
}
}
+13 -47
View File
@@ -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),
+1 -13
View File
@@ -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{};
+1 -3
View File
@@ -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)) {
+2 -8
View File
@@ -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";
-8
View File
@@ -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;
+1 -2
View File
@@ -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 {
+1 -1
View File
@@ -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;
-16
View File
@@ -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 -95
View File
@@ -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);
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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