Compare commits

..
Author SHA1 Message Date
Eliza Velasquez 84cecd334b materials: fix mutable spec constants nvidia bug
It turns out that I didn't understand how `FixedCapacityVector` works.
2025-09-03 18:13:09 -07:00
Eliza Velasquez bb6ebd3aea materials: reintroduce mutable spec constants
This change apparently breaks nvidia, still working on it.
2025-08-27 13:27:39 -07:00
85 changed files with 10866 additions and 1325 deletions
+1 -1
View File
@@ -633,7 +633,7 @@ endif()
# With WebGPU, Tint does not support ClipDistance which is used in Stereo. Mentioned in comment
# https://github.com/google/dawn/blob/855d17b08abdf02f9142bf5a8f14d0ea088810a4/src/tint/lang/spirv/reader/ast_parser/function.cc#L4434
if (FILAMENT_SUPPORTS_WEBGPU)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=stereo)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=skinning,stereo)
endif()
# Disable ESSL 1.0 code generation.
+2 -1
View File
@@ -8,4 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- materials: added `getEyeFromViewMatrix()` for vertex shader [⚠️ **Recompile Materials**]
- 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**]
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.65.0'
implementation 'com.google.android.filament:filament-android:1.64.0'
}
```
@@ -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.65.0'
pod 'Filament', '~> 1.64.0'
```
## Documentation
-4
View File
@@ -7,10 +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.65.0
lighting: the intermediate froxel record buffer is now dynamically sized [⚠️ **New Material Version**]
## v1.64.1
- Update CMake minimum version to 3.22.1
@@ -146,7 +146,7 @@ abstract class MaterialCompiler extends TaskWithBinary {
.gradleProperty("com.google.android.filament.include-webgpu")
.forUseAtConfigurationTime().present
if (include_webgpu) {
matcArgs += ['-a', 'webgpu', '--variant-filter=stereo']
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo']
}
def mat_no_opt = providers
@@ -60,6 +60,14 @@ 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,
@@ -264,6 +272,13 @@ 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,6 +402,16 @@ 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.
*
@@ -921,6 +931,9 @@ 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);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.65.0
VERSION_NAME=1.64.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+2 -2
View File
@@ -250,7 +250,7 @@ execution profile data.</p>
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/performance_analysis.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../notes/framegraph.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
@@ -264,7 +264,7 @@ execution profile data.</p>
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/performance_analysis.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../notes/framegraph.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
+2 -2
View File
@@ -331,7 +331,7 @@ nodes</li>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../notes/performance_analysis.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../notes/coverage.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
@@ -345,7 +345,7 @@ nodes</li>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../notes/performance_analysis.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../notes/coverage.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
-352
View File
@@ -1,352 +0,0 @@
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Performance analysis - 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="performance-analysis"><a class="header" href="#performance-analysis">Performance Analysis</a></h1>
<h2 id="android"><a class="header" href="#android">Android</a></h2>
<h3 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h3>
<ul>
<li>Download and install Android GPU Inspector (AGI). See https://developer.android.com/agi.</li>
</ul>
<hr />
<h4 id="profiling"><a class="header" href="#profiling">Profiling</a></h4>
<ol>
<li>
<p>Before profiling the application or analyzing the performance in a consistent way, ideally
the <strong>GPU frequency on the target hardware should get locked</strong>. In order to do this, you need to do the following:</p>
<ol>
<li>Ensure your device is OEM unlocked. If your phone is carrier-locked, you may need
to wait a period, such as 60 or 90 days after activation to be eligible for unlocking.
Some phones don't support this at all. You will need to enable developer options
(e.g. Settings &gt; About Phone and tap the Build number 7 times). You need to go to
Settings &gt; System &gt; Developer options and toggle on "OEM unlocking".</li>
<li>Next, you need to unlock the phone. You will need to install Android SDK Platform
Tools on you computer, enable "USB debugging" in your phone's Settings &gt; System &gt; Developer options.</li>
<li>Connect the phone to the computer via a USB cable and run from the command line:
<pre><code class="language-shell">adb reboot bootloader
# once the phone is in bootloader mode, run the following to begin the unlocking
# process:
fastboot flashing unlock
</code></pre>
A warning will appear on your phone's screen. Use the volume buttons to navigate and
the power button to select the "Unlock the bootloader" option. The phone will perform
a factory data reset and reboot with an unlocked bootloader.</li>
<li>You would need to flash an image to the phone with root permissions, such as a *-userdebug or *-eng
build. One way to do this is with the <a href="https://flash.android.com/">Android Flash Tool</a>. Connect the
tool to your device and find a build to flash ending in <code>-userdebug</code> or <code>-eng</code>. Once you have that
selected run <code>Install build</code>.</li>
<li>Shell into your device as root and configure the gpu frequency to be locked, e.g.:
<pre><code class="language-shell">adb shell
su
# navigate to the system GPU directory. this varies on different phones. One phone might have
# it at /sys/class/kgsl/kgsl-3d0 and another might be in something similar, maybe with "mali" instead
# of "kgsl". At the time of writing this for the device at hand it was /sys/devices/platform/1f000000.mali
cd /sys/devices/platform/1f000000.mali
# get the current available GPU governors and frequencies.
# note that some systems may have these at gpu_available_governors and gpu_available_frequencies, but
# the system at the time of writing this had available_governors and available_frequences
cat available_governors
cat available_frequencies
# depending on the governors, you may want to set it prioritize performance over other things like
# battery. Some systems allow you to do this with something like (although, for the device used at the
# time of writing this did not have an equivalent option):
echo performance &gt; gpu_governor
# finally, lock your frequency in, usually to something high like 897 MHz. Some systems may have you
# pipe the value to gpu_min_freq and gpu_max_freq, but the system used at the time of writing this had:
echo 940000 &gt; hint_min_freq
echo 940000 &gt; hint_max_freq
# you can typically verify the GPU is running at that frequency consistently by running something like
# the following a few times over time, which should show the frequency you want to lock the device to:
cat cur_freq
</code></pre>
</li>
<li>You may need to re-apply the <code>hint_min_freq</code> just before starting the profiling trace and check before and after
that the frequency remained at the value expected. Some systems may adjust the frequency on you, but you
may want to ensure the frequency remains the same through the analysis.</li>
<li>The GPU frequency settings should be undone after restarting the device, but after you have done your app profiling,
you can revert the state of the device, such as the OS build image, back to the way you had it
initially, as needed.</li>
</ol>
</li>
<li>
<p>Build a release build of Filament with the applicable backend(s) enabled
<em>(+ any special flags for enabling sys strace. Nothing special is needed for Vulkan or WebGPU aside from
building a release build with no flags)</em> <em>(debug builds for this are useless)</em></p>
<pre><code class="language-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
</code></pre>
</li>
<li>
<p>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.
<em>(to determine how these numbers map to the backends, see the <code>enum class Backend</code>
definition in <a href="../../../../filament/backend/include/backend/DriverEnums.h">filament/filament/backend/include/backend/DriverEnums.h</a>)</em>:</p>
<pre><code class="language-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
</code></pre>
</li>
<li>
<p>Build and run a sample, e.g. sample-gltf-viewer, on your Android device
<em>(<a href="https://developer.android.com/studio">Android Studio</a> recommended)</em>.</p>
</li>
<li>
<p>Run <a href="https://developer.android.com/agi">AGI</a> 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.</p>
<ul>
<li>We typically only run "Capture System Profiler trace" <em>(not necessarily "Capture Frame Profiler trace")</em></li>
<li>When configuring the trace:
<ul>
<li>For both the WebGPU and Vulkan backends configure the profiler for the Vulkan API
<em>(since WebGPU should be using Vulkan under the hood as well)</em></li>
<li>Running for ~1 seconds should suffice</li>
<li>Hit the "Configure" button in Trace objects, select "Switch to advanced mode" and add:
<pre><code>data_sources {
config {
name: "track_event"
track_event_config {
disabled_categories: "*"
enabled_categories: "filament/filament"
enabled_categories: "filament/jobsystem"
enabled_categories: "filament/gltfio"
}
}
}
</code></pre>
</li>
</ul>
</li>
<li>One you open the trace, zoom into a series of frames to get a sense of generally how long they typically take
<em>(use <code>W</code>, <code>S</code>, <code>A</code> and <code>D</code> keys and mouse wheel for navigation)</em> and find a representative one.
We are most interested in the performance of the
<code>FEngine::loop</code> 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.</li>
</ul>
</li>
</ol>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../notes/coverage.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/framegraph.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="prev" href="../notes/coverage.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/framegraph.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>
+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
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+31 -24
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,30 +1096,37 @@ Value
`bool` or `number`, depending on the `type` of the constant. The type must be one of the types
described in table [materialConstantsTypes].
Type | Description | Default
:----------------------|:-----------------------------------------|:------------------
int | A signed, 32 bit GLSL int | 0
float | A single-precision GLSL float | 0.0
bool | A GLSL bool | false
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 |
[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. 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. 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.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
constants : [
{
name : overrideAlpha,
type : bool
type : bool,
mutable : true,
},
{
name : customAlpha,
@@ -2639,7 +2646,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": [
{
@@ -2655,7 +2662,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
}
]
}
```
```
### --variant-filter
-1
View File
@@ -24,7 +24,6 @@
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
- [Using Instruments on macOS](./notes/instruments.md)
- [Code coverage analysis](./notes/coverage.md)
- [Performance analysis](./notes/performance_analysis.md)
- [Framegraph](./notes/framegraph.md)
- [Libraries](./notes/libs.md)
- [bluegl](./dup/bluegl.md)
@@ -6,64 +6,12 @@
- Download and install Android GPU Inspector (AGI). See https://developer.android.com/agi.
### Android with the Vulkan Backend
---
#### Profiling
1. Before profiling the application or analyzing the performance in a consistent way, ideally
the **GPU frequency on the target hardware should get locked**. In order to do this, you need to do the following:
1. Ensure your device is OEM unlocked. If your phone is carrier-locked, you may need
to wait a period, such as 60 or 90 days after activation to be eligible for unlocking.
Some phones don't support this at all. You will need to enable developer options
(e.g. Settings > About Phone and tap the Build number 7 times). You need to go to
Settings > System > Developer options and toggle on "OEM unlocking".
1. Next, you need to unlock the phone. You will need to install Android SDK Platform
Tools on you computer, enable "USB debugging" in your phone's Settings > System > Developer options.
1. Connect the phone to the computer via a USB cable and run from the command line:
```shell
adb reboot bootloader
# once the phone is in bootloader mode, run the following to begin the unlocking
# process:
fastboot flashing unlock
```
A warning will appear on your phone's screen. Use the volume buttons to navigate and
the power button to select the "Unlock the bootloader" option. The phone will perform
a factory data reset and reboot with an unlocked bootloader.
1. You would need to flash an image to the phone with root permissions, such as a *-userdebug or *-eng
build. One way to do this is with the [Android Flash Tool](https://flash.android.com/). Connect the
tool to your device and find a build to flash ending in `-userdebug` or `-eng`. Once you have that
selected run `Install build`.
1. Shell into your device as root and configure the gpu frequency to be locked, e.g.:
```shell
adb shell
su
# navigate to the system GPU directory. this varies on different phones. One phone might have
# it at /sys/class/kgsl/kgsl-3d0 and another might be in something similar, maybe with "mali" instead
# of "kgsl". At the time of writing this for the device at hand it was /sys/devices/platform/1f000000.mali
cd /sys/devices/platform/1f000000.mali
# get the current available GPU governors and frequencies.
# note that some systems may have these at gpu_available_governors and gpu_available_frequencies, but
# the system at the time of writing this had available_governors and available_frequences
cat available_governors
cat available_frequencies
# depending on the governors, you may want to set it prioritize performance over other things like
# battery. Some systems allow you to do this with something like (although, for the device used at the
# time of writing this did not have an equivalent option):
echo performance > gpu_governor
# finally, lock your frequency in, usually to something high like 897 MHz. Some systems may have you
# pipe the value to gpu_min_freq and gpu_max_freq, but the system used at the time of writing this had:
echo 940000 > hint_min_freq
echo 940000 > hint_max_freq
# you can typically verify the GPU is running at that frequency consistently by running something like
# the following a few times over time, which should show the frequency you want to lock the device to:
cat cur_freq
```
1. You may need to re-apply the `hint_min_freq` just before starting the profiling trace and check before and after
that the frequency remained at the value expected. Some systems may adjust the frequency on you, but you
may want to ensure the frequency remains the same through the analysis.
1. The GPU frequency settings should be undone after restarting the device, but after you have done your app profiling,
you can revert the state of the device, such as the OS build image, back to the way you had it
initially, as needed.
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)_
+1 -10
View File
@@ -580,9 +580,7 @@ if (APPLE OR LINUX)
test/test_Template.cpp
)
if (FILAMENT_SUPPORTS_WEBGPU)
list(APPEND BACKEND_TEST_SRC
test/test_WebGPUAsyncTaskCounter.cpp
test/test_WebGPUComposeSwizzle.cpp)
list(APPEND BACKEND_TEST_SRC test/test_WebGPUAsyncTaskCounter.cpp)
endif()
if (APPLE)
# Metal-specific tests
@@ -631,10 +629,6 @@ if (APPLE AND NOT IOS)
target_link_libraries(backend_test PRIVATE image imageio)
list(APPEND BACKEND_TEST_DEPS image)
endif()
if (FILAMENT_SUPPORTS_WEBGPU)
target_link_libraries(backend_test PRIVATE webgpu_dawn dawncpp_headers)
list(APPEND BACKEND_TEST_DEPS webgpu_dawn dawncpp_headers)
endif()
set(BACKEND_TEST_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libbackendtest_combined.a")
combine_static_libs(backend_test "${BACKEND_TEST_COMBINED_OUTPUT}" "${BACKEND_TEST_DEPS}")
@@ -666,9 +660,6 @@ if (LINUX)
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
if (FILAMENT_SUPPORTS_WEBGPU)
target_link_libraries(backend_test_linux PRIVATE webgpu_dawn dawncpp_headers)
endif()
endif()
# ==================================================================================================
@@ -629,18 +629,6 @@ enum class PrimitiveType : uint8_t {
TRIANGLE_STRIP = 5 //!< triangle strip
};
[[nodiscard]] constexpr bool isStripPrimitiveType(const PrimitiveType type) {
switch (type) {
case PrimitiveType::POINTS:
case PrimitiveType::LINES:
case PrimitiveType::TRIANGLES:
return false;
case PrimitiveType::LINE_STRIP:
case PrimitiveType::TRIANGLE_STRIP:
return true;
}
}
/**
* Supported uniform types
*/
+4 -1
View File
@@ -20,6 +20,7 @@
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/bitset.h>
#include <backend/DriverEnums.h>
@@ -66,6 +67,7 @@ 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>;
@@ -103,7 +105,8 @@ public:
Program& descriptorBindings(backend::descriptor_set_t set,
DescriptorBindingsInfo descriptorBindings) noexcept;
Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept;
Program& specializationConstants(SpecializationConstantsInfo specConstants,
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept;
struct PushConstant {
utils::CString name;
@@ -19,16 +19,6 @@
#include <backend/Platform.h>
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_X11)
// Resolve the conflicts between webgpu_cpp.h and X11 defines
#undef Always
#undef Success
#undef None
#undef True
#undef False
#undef Status
#undef Bool
#endif
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
+13 -2
View File
@@ -82,8 +82,19 @@ Program& Program::attributes(AttributesInfo attributes) noexcept {
return *this;
}
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants) noexcept {
mSpecializationConstants = std::move(specConstants);
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],
};
}
return *this;
}
+1 -1
View File
@@ -1948,7 +1948,7 @@ void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
if (UTILS_UNLIKELY(t->hwStream)) {
detachStream(t);
}
if (UTILS_UNLIKELY(t->externalTexture)) {
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
mPlatform.destroyExternalImageTexture(t->externalTexture);
} else {
glDeleteTextures(1, &t->gl.id);
@@ -211,9 +211,7 @@ VkSemaphore VulkanCommandBuffer::submit() {
FVK_LOGI << "Submitting cmdbuffer=" << mBuffer
<< " wait=(";
for (size_t s = 0, count = mWaitSemaphores.size(); s < count; ++s) {
FVK_LOGI << "\tsemaphore=" << mWaitSemaphores[s] << "|stage="
<< mWaitSemaphoreStages[s]
<< (s < mWaitSemaphores.size() - 1 ? "\n" : "");
FVK_LOGI << mWaitSemaphores[s] << " ";
}
FVK_LOGI << ") "
<< " signal=" << mSubmission
@@ -436,13 +434,10 @@ bool VulkanCommands::flush() {
}
if (lastSubmit != VK_NULL_HANDLE) {
// Note that the stage we're waiting on is the fragment shader stage. This assumes
// that the subsequent command buffer will only depend on
// 1) fragment output of the previous command buffer
// 2) reading/writing of buffers (i.e. UBO) of the previous command buffer
// Restricting the wait stages will allow for vertex work to proceed (more overlapping
// that the subsequent command buffer will only dependent on the fragment output of the
// previous buffer, allowing for vertex work to proceed (more overlapping
// vertex/fragment work).
pool->waitFor(lastSubmit,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
pool->waitFor(lastSubmit, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
lastSubmit = VK_NULL_HANDLE;
}
dependency = pool->flush();
+1 -6
View File
@@ -308,10 +308,7 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
// Creates a special "default" render target (i.e. associated with the swap chain)
explicit VulkanRenderTarget();
VulkanRenderTarget(VulkanRenderTarget&& target)
: HwRenderTarget(0, 0),
mOffscreen(false),
mProtected(false) {
VulkanRenderTarget(VulkanRenderTarget&& target) {
swap(std::move(target));
}
@@ -368,8 +365,6 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
private:
void swap(VulkanRenderTarget&& target) {
std::swap(width, target.width);
std::swap(height, target.height);
std::swap(mOffscreen, target.mOffscreen);
std::swap(mProtected, target.mProtected);
std::swap(mInfo, target.mInfo);
@@ -106,7 +106,7 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
return { vals[0], vals[1], vals[2], vals[3] };
};
// We make sure all identities are mapped into respective channels so that actual channel
// We make sure all all identities are mapped into respective channels so that actual channel
// mapping will be passed onto the output.
VkComponentMapping const prevExplicit = identityToChannel(prev);
VkComponentMapping const nextExplicit = identityToChannel(next);
File diff suppressed because it is too large Load Diff
+20 -14
View File
@@ -207,17 +207,7 @@ void WebGPUDriver::finish(int /* dummy */) {
FWGPU_SYSTRACE_SCOPE();
mDevice.Tick();
mAdapter.GetInstance().ProcessEvents();
mReadPixelMapsCounter.waitForAllToFinish();
flush();
// Some applications/samples might call finish in the very beginning of an application before
// creating any resources/tasks to flush out the system on any pending work from previous runs.
// Currently, gltf_viewer sample on android exhibit this behavior.
// Bail out early to safeguard against those cases. Otherwise, OnSubmittedWorkDone will
// hang on work that was never submitted.
if (mCommandEncoder == nullptr) {
return;
}
// Wait for all previously submitted work to finish.
std::mutex syncPoint;
std::condition_variable syncCondition;
@@ -235,6 +225,7 @@ 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) {
@@ -536,16 +527,31 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
auto sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
assert_invariant(sourceTexture);
wgpu::TextureComponentSwizzle const nextSwizzle{
wgpu::TextureComponentSwizzle swizzle
{
.r = toWGPUComponentSwizzle(r),
.g = toWGPUComponentSwizzle(g),
.b = toWGPUComponentSwizzle(b),
.a = toWGPUComponentSwizzle(a),
};
// The WebGPUTexture constructor for swizzled views will handle composing the swizzle and
// creating the new texture view.
constructHandle<WebGPUTexture>(textureHandle, sourceTexture, nextSwizzle);
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);
}
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
@@ -293,11 +293,10 @@ wgpu::RenderPipeline WebGPUPipelineCache::createRenderPipeline(
},
.primitive = {
.topology = toWebGPU(request.primitiveType),
// TODO This won't always be the case for the primitives bound. But this particular field
// in the pipeline struct is only meant for restarting the strip, which we don't need so far.
.stripIndexFormat =
isStripPrimitiveType(request.primitiveType)? wgpu::IndexFormat::Uint16 :
wgpu::IndexFormat::Undefined,
// TODO should we assume some constant format here or is there a way to get
// this from PipelineState somehow or elsewhere?
// Perhaps, cache/assert format from index buffers as they are requested?
.stripIndexFormat = wgpu::IndexFormat::Undefined,
.frontFace = request.rasterState.inverseFrontFaces ? wgpu::FrontFace::CW : wgpu::FrontFace::CCW,
.cullMode = toWebGPU(request.rasterState.culling),
// TODO no depth clamp in WebGPU supported directly. unclippedDepth is close, so we are
@@ -313,10 +312,12 @@ wgpu::RenderPipeline WebGPUPipelineCache::createRenderPipeline(
},
.fragment = nullptr // will add below if fragment module is included
};
FILAMENT_CHECK_POSTCONDITION(!isStripPrimitiveType(request.primitiveType) ||
pipelineDescriptor.primitive.stripIndexFormat != wgpu::IndexFormat::Undefined)
<< "If the topology is a strip format, e.g. TriangleStrip, the stripIndexFormat cannot "
"be Undefined.";
// TODO:
if (pipelineDescriptor.primitive.topology == wgpu::PrimitiveTopology::LineStrip ||
pipelineDescriptor.primitive.topology == wgpu::PrimitiveTopology::TriangleStrip) {
PANIC_POSTCONDITION("stripIndexFormat must be set for strip topologies. "
"This needs to be plumbed through from the RenderPrimitive.");
}
wgpu::FragmentState fragmentState = {};
const wgpu::BlendState blendState {
@@ -161,7 +161,7 @@ private:
// render targets... //
uint8_t multisampleCount{ 0 }; // 1 : 313
uint8_t colorFormatCount{ 0 }; // 1 : 314
uint8_t padding[5]{ 0 }; // 5 : 315
uint8_t padding[5]{ 0 }; // 5 : 319
TargetBufferFlags targetRenderFlags{ TargetBufferFlags::NONE }; // 4 : 320
wgpu::TextureFormat depthStencilFormat { wgpu::TextureFormat::Undefined }; // 4 : 324
wgpu::TextureFormat colorFormats[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]{ //
+18 -44
View File
@@ -216,9 +216,7 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
mBlockWidth{ filament::backend::getBlockWidth(format) },
mBlockHeight{ filament::backend::getBlockHeight(format) },
mDefaultMipLevel{ 0 },
mDefaultBaseArrayLayer{ 0 },
// an undefined mSwizzle means that is not a swizzle view
mSwizzle{} {
mDefaultBaseArrayLayer{ 0 } {
assert_invariant(
samples == 1 ||
samples == 4 &&
@@ -281,36 +279,11 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
mTexture{ src->mTexture },
mDefaultMipLevel{ baseLevel },
mDefaultBaseArrayLayer{ src->mArrayLayerCount },
mMsaaSidecarTexture{ src->mMsaaSidecarTexture },
mSwizzle{ src->mSwizzle } {
const bool swizzleUndefined{ (src->mSwizzle.r == wgpu::ComponentSwizzle::Undefined) &&
(src->mSwizzle.g == wgpu::ComponentSwizzle::Undefined) &&
(src->mSwizzle.b == wgpu::ComponentSwizzle::Undefined) &&
(src->mSwizzle.a == wgpu::ComponentSwizzle::Undefined) };
if (swizzleUndefined) {
mDefaultTextureView = makeTextureView(mDefaultMipLevel, levelCount, 0,
mDefaultBaseArrayLayer, src->getViewDimension());
} else {
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
swizzleDesc.swizzle = mSwizzle;
const wgpu::TextureViewDescriptor viewDesc{
.nextInChain = &swizzleDesc,
.label = "swizzled_texture_view",
.format = mTexture.GetFormat(),
.dimension = src->getViewDimension(),
.baseMipLevel = mDefaultMipLevel,
.mipLevelCount = levelCount,
.baseArrayLayer = 0,
.arrayLayerCount = mDefaultBaseArrayLayer,
};
mDefaultTextureView = mTexture.CreateView(&viewDesc);
FILAMENT_CHECK_POSTCONDITION(mDefaultTextureView)
<< "Failed to create swizzled Texture view";
}
}
mDefaultTextureView{ makeTextureView(mDefaultMipLevel, levelCount, 0, mDefaultBaseArrayLayer,
toWebGPUTextureViewDimension(src->target)) },
mMsaaSidecarTexture{ src->mMsaaSidecarTexture } {}
WebGPUTexture::WebGPUTexture(const WebGPUTexture* src,
const wgpu::TextureComponentSwizzle nextSwizzle) noexcept
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 },
@@ -325,19 +298,20 @@ WebGPUTexture::WebGPUTexture(const WebGPUTexture* src,
mTexture{ src->mTexture },
mDefaultMipLevel{ src->mDefaultMipLevel },
mDefaultBaseArrayLayer{ 0 },
mMsaaSidecarTexture{src->mMsaaSidecarTexture},
mSwizzle{ composeSwizzle(src->getSwizzle(), nextSwizzle) } {
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
swizzleDesc.swizzle = mSwizzle;
mDefaultTextureView{ view },
mMsaaSidecarTexture{src->mMsaaSidecarTexture}{}
const wgpu::TextureViewDescriptor viewDesc{
.nextInChain = &swizzleDesc,
.label = "swizzled_texture_view",
.format = mTexture.GetFormat(),
.dimension = src->getViewDimension(),
};
mDefaultTextureView = mTexture.CreateView(&viewDesc);
FILAMENT_CHECK_POSTCONDITION(mDefaultTextureView) << "Failed to create swizzled Texture view";
wgpu::Texture const& WebGPUTexture::getMsaaSidecarTexture(const uint8_t sampleCount) const {
if (mMsaaSidecarTexture == nullptr) {
return mMsaaSidecarTexture; // nullptr (no such sidecar)
}
FILAMENT_CHECK_PRECONDITION(sampleCount == mMsaaSidecarTexture.GetSampleCount())
<< "The MSAA sidecar texture has a different sample count ("
<< mMsaaSidecarTexture.GetSampleCount() << ") than requested (" << +sampleCount
<< "). Note that this restriction was written when WebGPU only supported msaa "
"textures with 4 samples. If that has changed, this implementation should be "
"updated (e.g. map of sidecar textures by sampleCount or something).";
return mMsaaSidecarTexture;
}
bool WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(const wgpu::TextureFormat format) {
+13 -8
View File
@@ -47,11 +47,12 @@ public:
WebGPUTexture(WebGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
/**
* @param nextSwizzle the swizzle to apply to the source texture.
* Creates a new texture view by composing the nextSwizzle with the source which itself it
* could be a swizzled view
* @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::TextureComponentSwizzle nextSwizzle) noexcept;
WebGPUTexture(WebGPUTexture const* source, wgpu::TextureView textureView) noexcept;
[[nodiscard]] wgpu::TextureAspect getAspect() const { return mAspect; }
@@ -61,6 +62,8 @@ public:
[[nodiscard]] wgpu::Texture const& getTexture() const { return mTexture; }
[[nodiscard]] wgpu::Texture const& getMsaaSidecarTexture(uint8_t sampleCount) const;
[[nodiscard]] wgpu::TextureView const& getDefaultTextureView() const {
return mDefaultTextureView;
}
@@ -71,8 +74,6 @@ public:
[[nodiscard]] wgpu::TextureViewDimension getViewDimension() const { return mDimension; }
[[nodiscard]] wgpu::TextureFormat getViewFormat() const { return mViewFormat; }
[[nodiscard]] wgpu::TextureComponentSwizzle getSwizzle() const { return mSwizzle; }
[[nodiscard]] uint32_t getArrayLayerCount() const { return mArrayLayerCount; }
[[nodiscard]] MipmapGenerationStrategy getMipmapGenerationStrategy() const {
@@ -101,6 +102,12 @@ public:
wgpu::TextureView makeMsaaSidecarTextureViewIfTextureSidecarExists(uint8_t samples,
uint8_t mipLevel, uint32_t arrayLayer) const;
/**
* @return nullptr if a MSAA sidecar texture is not appliable, otherwise a view to one
*/
[[nodiscard]] wgpu::TextureView makeMsaaSidecarTextureView(wgpu::Texture const&,
uint8_t mipLevel, uint32_t arrayLayer) const;
/**
* @param format a required texture format (can be a view to a different underlying texture
* format)
@@ -137,8 +144,6 @@ private:
// by sampleCount or something like that.
// For now that complexity and cost is not warranted due to WebGPU's restrictions.
wgpu::Texture mMsaaSidecarTexture = nullptr;
// an undefined mSwizzle means that is not a swizzle view
wgpu::TextureComponentSwizzle mSwizzle{};
[[nodiscard]] wgpu::TextureView makeTextureView(const uint8_t& baseLevel,
const uint8_t& levelCount, const uint32_t& baseArrayLayer,
@@ -605,85 +605,6 @@ namespace filament::backend {
return retUsage;
}
/**
* "Updates" the previous swizzle parameters with the next/new parameters, essentially stacking
* swizzle parameters. Essentially this combines the parameters such that the result is
* the same mapping of channels you would get if you first applied the "previous" mapping
* followed by the "next" mapping.
*/
[[nodiscard]] constexpr wgpu::TextureComponentSwizzle composeSwizzle(
wgpu::TextureComponentSwizzle const& previous, wgpu::TextureComponentSwizzle const& next) {
// start with the "next" values...
wgpu::ComponentSwizzle result[] { next.r, next.g, next.b, next.a };
// track the current channel as we go through values to know what to assign in the undefined
// case...
wgpu::ComponentSwizzle channel{wgpu::ComponentSwizzle::R};
for (auto& out: result) {
switch (out) {
case wgpu::ComponentSwizzle::R:
if (previous.r != wgpu::ComponentSwizzle::Undefined) {
out = previous.r;
}
break;
case wgpu::ComponentSwizzle::G:
if (previous.g != wgpu::ComponentSwizzle::Undefined) {
out = previous.g;
}
break;
case wgpu::ComponentSwizzle::B:
if (previous.b != wgpu::ComponentSwizzle::Undefined) {
out = previous.b;
}
break;
case wgpu::ComponentSwizzle::A:
if (previous.a != wgpu::ComponentSwizzle::Undefined) {
out = previous.a;
}
break;
// Do not modify the (next) value for explicit One or Zero...
case wgpu::ComponentSwizzle::Zero:
case wgpu::ComponentSwizzle::One:
break;
case wgpu::ComponentSwizzle::Undefined:
// fallback to the previous value if the next value is undefined...
switch (channel) {
case wgpu::ComponentSwizzle::R:
out = previous.r;
break;
case wgpu::ComponentSwizzle::G:
out = previous.g;
break;
case wgpu::ComponentSwizzle::B:
out = previous.b;
break;
case wgpu::ComponentSwizzle::A:
out = previous.a;
break;
default:
// should not be possible...
out = wgpu::ComponentSwizzle::Undefined;
break;
}
break;
}
switch (channel) {
case wgpu::ComponentSwizzle::R:
channel = wgpu::ComponentSwizzle::G;
break;
case wgpu::ComponentSwizzle::G:
channel = wgpu::ComponentSwizzle::B;
break;
case wgpu::ComponentSwizzle::B:
channel = wgpu::ComponentSwizzle::A;
break;
default:
channel = wgpu::ComponentSwizzle::Undefined;
break;
}
}
return { .r = result[0], .g = result[1], .b = result[2], .a = result[3] };
}
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_WEBGPUTEXTUREHELPERS_H
@@ -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 "webgpu/WebGPUTextureHelpers.h"
#include <gtest/gtest.h>
#include <webgpu/webgpu_cpp.h>
namespace test {
TEST(composeSwizzle, allUndefined) {
const wgpu::TextureComponentSwizzle undefinedPrevious{
.r = wgpu::ComponentSwizzle::Undefined,
.g = wgpu::ComponentSwizzle::Undefined,
.b = wgpu::ComponentSwizzle::Undefined,
.a = wgpu::ComponentSwizzle::Undefined,
};
const wgpu::TextureComponentSwizzle undefinedNext{
.r = wgpu::ComponentSwizzle::Undefined,
.g = wgpu::ComponentSwizzle::Undefined,
.b = wgpu::ComponentSwizzle::Undefined,
.a = wgpu::ComponentSwizzle::Undefined,
};
const wgpu::TextureComponentSwizzle result{ filament::backend::composeSwizzle(undefinedPrevious,
undefinedNext) };
EXPECT_EQ(result.r, wgpu::ComponentSwizzle::Undefined);
EXPECT_EQ(result.g, wgpu::ComponentSwizzle::Undefined);
EXPECT_EQ(result.b, wgpu::ComponentSwizzle::Undefined);
EXPECT_EQ(result.a, wgpu::ComponentSwizzle::Undefined);
}
TEST(composeSwizzle, previousUndefined) {
const wgpu::TextureComponentSwizzle undefinedPrevious{
.r = wgpu::ComponentSwizzle::Undefined,
.g = wgpu::ComponentSwizzle::Undefined,
.b = wgpu::ComponentSwizzle::Undefined,
.a = wgpu::ComponentSwizzle::Undefined,
};
const wgpu::TextureComponentSwizzle definedNext{
.r = wgpu::ComponentSwizzle::G,
.g = wgpu::ComponentSwizzle::R,
.b = wgpu::ComponentSwizzle::A,
.a = wgpu::ComponentSwizzle::B,
};
const wgpu::TextureComponentSwizzle result{ filament::backend::composeSwizzle(undefinedPrevious,
definedNext) };
EXPECT_EQ(result.r, wgpu::ComponentSwizzle::G);
EXPECT_EQ(result.g, wgpu::ComponentSwizzle::R);
EXPECT_EQ(result.b, wgpu::ComponentSwizzle::A);
EXPECT_EQ(result.a, wgpu::ComponentSwizzle::B);
}
TEST(composeSwizzle, nextUndefined) {
const wgpu::TextureComponentSwizzle definedPrevious{
.r = wgpu::ComponentSwizzle::G,
.g = wgpu::ComponentSwizzle::R,
.b = wgpu::ComponentSwizzle::A,
.a = wgpu::ComponentSwizzle::B,
};
const wgpu::TextureComponentSwizzle undefinedNext{
.r = wgpu::ComponentSwizzle::Undefined,
.g = wgpu::ComponentSwizzle::Undefined,
.b = wgpu::ComponentSwizzle::Undefined,
.a = wgpu::ComponentSwizzle::Undefined,
};
const wgpu::TextureComponentSwizzle result{ filament::backend::composeSwizzle(definedPrevious,
undefinedNext) };
EXPECT_EQ(result.r, wgpu::ComponentSwizzle::G);
EXPECT_EQ(result.g, wgpu::ComponentSwizzle::R);
EXPECT_EQ(result.b, wgpu::ComponentSwizzle::A);
EXPECT_EQ(result.a, wgpu::ComponentSwizzle::B);
}
TEST(composeSwizzle, bothDefinedNoSwizzling) {
const wgpu::TextureComponentSwizzle previousOneToOneMapping{
.r = wgpu::ComponentSwizzle::R,
.g = wgpu::ComponentSwizzle::G,
.b = wgpu::ComponentSwizzle::B,
.a = wgpu::ComponentSwizzle::A,
};
const wgpu::TextureComponentSwizzle nextOneToOneMapping{
.r = wgpu::ComponentSwizzle::R,
.g = wgpu::ComponentSwizzle::G,
.b = wgpu::ComponentSwizzle::B,
.a = wgpu::ComponentSwizzle::A,
};
const wgpu::TextureComponentSwizzle result{ filament::backend::composeSwizzle(
previousOneToOneMapping, nextOneToOneMapping) };
EXPECT_EQ(result.r, wgpu::ComponentSwizzle::R);
EXPECT_EQ(result.g, wgpu::ComponentSwizzle::G);
EXPECT_EQ(result.b, wgpu::ComponentSwizzle::B);
EXPECT_EQ(result.a, wgpu::ComponentSwizzle::A);
}
TEST(composeSwizzle, bothDefined) {
const wgpu::TextureComponentSwizzle previous{
.r = wgpu::ComponentSwizzle::G,
.g = wgpu::ComponentSwizzle::R,
.b = wgpu::ComponentSwizzle::A,
.a = wgpu::ComponentSwizzle::B,
};
const wgpu::TextureComponentSwizzle next{
.r = wgpu::ComponentSwizzle::A,
.g = wgpu::ComponentSwizzle::B,
.b = wgpu::ComponentSwizzle::G,
.a = wgpu::ComponentSwizzle::R,
};
const wgpu::TextureComponentSwizzle result{ filament::backend::composeSwizzle(previous,
next) };
EXPECT_EQ(result.r, wgpu::ComponentSwizzle::B);
EXPECT_EQ(result.g, wgpu::ComponentSwizzle::A);
EXPECT_EQ(result.b, wgpu::ComponentSwizzle::R);
EXPECT_EQ(result.a, wgpu::ComponentSwizzle::G);
}
TEST(composeSwizzle, bothPartiallyDefined) {
const wgpu::TextureComponentSwizzle partiallyDefinedPrevious{
.r = wgpu::ComponentSwizzle::Undefined,
.g = wgpu::ComponentSwizzle::B,
.b = wgpu::ComponentSwizzle::G,
.a = wgpu::ComponentSwizzle::Undefined,
};
const wgpu::TextureComponentSwizzle partiallyDefinedNext{
.r = wgpu::ComponentSwizzle::A,
.g = wgpu::ComponentSwizzle::G,
.b = wgpu::ComponentSwizzle::Undefined,
.a = wgpu::ComponentSwizzle::R,
};
const wgpu::TextureComponentSwizzle result{ filament::backend::composeSwizzle(
partiallyDefinedPrevious, partiallyDefinedNext) };
EXPECT_EQ(result.r, wgpu::ComponentSwizzle::A);
EXPECT_EQ(result.g, wgpu::ComponentSwizzle::B);
EXPECT_EQ(result.b, wgpu::ComponentSwizzle::G);
EXPECT_EQ(result.a, wgpu::ComponentSwizzle::R);
}
TEST(composeSwizzle, onesAndZeros) {
const wgpu::TextureComponentSwizzle previousWithOnesAndZeros{
.r = wgpu::ComponentSwizzle::Zero,
.g = wgpu::ComponentSwizzle::R,
.b = wgpu::ComponentSwizzle::One,
.a = wgpu::ComponentSwizzle::B,
};
const wgpu::TextureComponentSwizzle nextWithOnesAndZeros{
.r = wgpu::ComponentSwizzle::One,
.g = wgpu::ComponentSwizzle::Zero,
.b = wgpu::ComponentSwizzle::Undefined,
.a = wgpu::ComponentSwizzle::R,
};
const wgpu::TextureComponentSwizzle result{ filament::backend::composeSwizzle(
previousWithOnesAndZeros, nextWithOnesAndZeros) };
EXPECT_EQ(result.r, wgpu::ComponentSwizzle::One);
EXPECT_EQ(result.g, wgpu::ComponentSwizzle::Zero);
EXPECT_EQ(result.b, wgpu::ComponentSwizzle::One);
EXPECT_EQ(result.a, wgpu::ComponentSwizzle::Zero);
}
} // namespace test
+53 -3
View File
@@ -87,6 +87,9 @@ 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
@@ -238,13 +241,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>
@@ -262,6 +265,53 @@ 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.
*
+6 -7
View File
@@ -543,13 +543,12 @@ public:
* visible -- in this case, using a larger value can improve performance.
* e.g. when standing and looking straight, several meters of the ground
* isn't visible and if lights are expected to shine there, there is no
* point using a short zLightNear. This value is clamped between
* the camera near and far plane. (Default 5m).
* point using a short zLightNear. (Default 5m).
*
* @param zLightFar Distance from the camera after which lights are not expected to be visible.
* Similarly to zLightNear, setting this value properly can improve
* performance. This value is clamped between the camera near and far plane.
* (Default 100m).
* performance. (Default 100m).
*
*
* Together zLightNear and zLightFar must be chosen so that the visible influence of lights
* is spread between these two values.
@@ -764,9 +763,9 @@ public:
//! debugging: returns information about the froxel configuration
struct FroxelConfigurationInfo {
uint16_t width;
uint16_t height;
uint16_t depth;
uint8_t width;
uint8_t height;
uint8_t depth;
uint32_t viewportWidth;
uint32_t viewportHeight;
math::uint2 froxelDimension;
+2 -4
View File
@@ -18,10 +18,8 @@
#define TNT_FILAMENT_BUFFERPOOLALLOCATOR_H
#include <utils/Allocator.h>
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <memory>
#include <utility>
#include <stdint.h>
@@ -112,7 +110,7 @@ void* BufferPoolAllocator<POOL_SIZE, ALIGNMENT, AllocatorPolicy, LockingPolicy>:
if (UTILS_UNLIKELY(size > mSize)) {
clearInternal(); // free all buffers
// round to 4K allocations to help cutting down on calling malloc.
size_t const roundedSize = ((size + sizeof(Header)) + (ALLOCATION_ROUNDING - 1)) & ~(ALLOCATION_ROUNDING - 1);
size_t roundedSize = ((size + sizeof(Header)) + (ALLOCATION_ROUNDING - 1)) & ~(ALLOCATION_ROUNDING - 1);
mSize = roundedSize - sizeof(Header); // record the new buffer size
assert_invariant(mSize >= size);
}
@@ -121,7 +119,7 @@ void* BufferPoolAllocator<POOL_SIZE, ALIGNMENT, AllocatorPolicy, LockingPolicy>:
// larger than the requested size).
if (UTILS_UNLIKELY(mEntries.empty())) {
++mOutstandingBuffers;
Header* p = static_cast<Header*>(mAllocator.alloc(mSize + sizeof(Header), ALIGNMENT));
Header* p = (Header*)mAllocator.alloc(mSize + sizeof(Header), ALIGNMENT);
p->size = mSize;
return p + 1;
}
+91 -132
View File
@@ -19,29 +19,25 @@
#include "Allocators.h"
#include "Intersections.h"
#include "components/LightManager.h"
#include "details/Engine.h"
#include "details/Scene.h"
#include <private/filament/EngineEnums.h>
#include <private/utils/Tracing.h>
#include <private/backend/DriverApi.h>
#include <filament/Box.h>
#include <filament/View.h>
#include <filament/Viewport.h>
#include <backend/DriverEnums.h>
#include <utils/architecture.h>
#include <private/utils/Tracing.h>
#include <utils/BinaryTreeArray.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/JobSystem.h>
#include <utils/Logger.h>
#include <utils/ostream.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <math/fast.h>
#include <math/mat3.h>
@@ -52,9 +48,9 @@
#include <math/vec4.h>
#include <algorithm>
#include <utils/architecture.h>
#include <array>
#include <cmath>
#include <functional>
#include <limits>
#include <type_traits>
#include <utility>
@@ -70,33 +66,24 @@ namespace filament {
using namespace backend;
// The number of froxel buffer entries is only limited by the maximum UBO size (see
// getFroxelBufferByteCount()), each entry consumes 4 bytes, so with a 16KB UBO, we get
// 4096 froxels.
// Increasing this value too much adds pressure on the record buffer, which is also limited
// to min(16K[ubo], 64K[uint16]) entries. In practice not all froxels are used.
constexpr size_t FROXEL_BUFFER_MAX_ENTRY_COUNT = 8192;
// Froxel buffer UBO is an array of uvec4. Make sure that the buffer is properly aligned.
static_assert(FROXEL_BUFFER_MAX_ENTRY_COUNT % 4 == 0u);
// TODO: these should come from a configuration object on View or Camera
static constexpr size_t FROXEL_SLICE_COUNT = 16;
static constexpr float FROXEL_FIRST_SLICE_DEPTH = 5;
static constexpr float FROXEL_LAST_SLICE_DISTANCE = 100;
// This is overridden by setOptions()
static constexpr float FROXEL_FIRST_SLICE_DEPTH_DEFAULT = 5; // 5m
static constexpr float FROXEL_LAST_SLICE_DISTANCE_DEFAULT = 100; // 100m
// The record buffer is limited by both the UBO size and our use of 16-bits indices.
constexpr size_t RECORD_BUFFER_ENTRY_COUNT = CONFIG_MINSPEC_UBO_SIZE; // 16 KiB UBO minspec
// Buffer needed for Froxelizer internal data structures (~256 KiB)
constexpr size_t PER_FROXELDATA_ARENA_SIZE = sizeof(float4) *
(FROXEL_BUFFER_MAX_ENTRY_COUNT +
FROXEL_BUFFER_MAX_ENTRY_COUNT + 3 +
FROXEL_SLICE_COUNT / 4 + 1);
(FROXEL_BUFFER_MAX_ENTRY_COUNT +
FROXEL_BUFFER_MAX_ENTRY_COUNT + 3 +
FROXEL_SLICE_COUNT / 4 + 1);
// Number of lights processed by one group (e.g. 32)
// number of lights processed by one group (e.g. 32)
static constexpr size_t LIGHT_PER_GROUP = sizeof(Froxelizer::LightGroupType) * 8;
// Number of groups (i.e. jobs) to use for froxelization (e.g. 8)
// number of groups (i.e. jobs) to use for froxelization (e.g. 8)
static constexpr size_t GROUP_COUNT =
(CONFIG_MAX_LIGHT_COUNT + LIGHT_PER_GROUP - 1) / LIGHT_PER_GROUP;
@@ -104,6 +91,14 @@ static constexpr size_t GROUP_COUNT =
static_assert(CONFIG_MAX_LIGHT_INDEX <= std::numeric_limits<Froxelizer::RecordBufferType>::max(),
"can't have more than 256 lights");
// Record buffer cannot be larger than 65K entries because froxels use uint16_t to store indices
// to it.
static_assert(RECORD_BUFFER_ENTRY_COUNT <= 65536,
"RecordBuffer cannot be larger than 65536 entries");
static_assert(RECORD_BUFFER_ENTRY_COUNT <= CONFIG_MINSPEC_UBO_SIZE,
"RecordBuffer cannot be larger than the UBO minspec (16KiB)");
struct Froxelizer::FroxelThreadData :
public std::array<LightGroupType, FROXEL_BUFFER_MAX_ENTRY_COUNT> {
};
@@ -129,25 +124,25 @@ size_t Froxelizer::getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexc
return std::min(FROXEL_BUFFER_MAX_ENTRY_COUNT * sizeof(FroxelEntry), targetSize);
}
size_t Froxelizer::getFroxelRecordBufferByteCount(FEngine::DriverApi& driverApi) noexcept {
// Make sure that targetSize is 16-byte aligned so that it'll fit properly into an array of
// uvec4. The maximum size is 64K entries, because we're using 16 bits indices.
size_t const targetSize = (driverApi.getMaxUniformBufferSize() / 16) * 16;
return std::min(size_t(std::numeric_limits<uint16_t>::max()), targetSize);
}
View::FroxelConfigurationInfo Froxelizer::getFroxelConfigurationInfo() const noexcept {
// this must return the configuration during the last update()
return mFroxelConfigurationInfo;
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_DEFAULT),
mZLightFar(FROXEL_LAST_SLICE_DISTANCE_DEFAULT),
mUserZLightNear(FROXEL_FIRST_SLICE_DEPTH_DEFAULT),
mUserZLightFar(FROXEL_LAST_SLICE_DISTANCE_DEFAULT) {
: mArena("froxel", PER_FROXELDATA_ARENA_SIZE),
mZLightNear(FROXEL_FIRST_SLICE_DEPTH),
mZLightFar(FROXEL_LAST_SLICE_DISTANCE)
{
static_assert(std::is_same_v<RecordBufferType, uint8_t>,
"Record Buffer must use bytes");
@@ -157,17 +152,10 @@ Froxelizer::Froxelizer(FEngine& engine)
return;
}
size_t const froxelBufferByteCount = getFroxelBufferByteCount(driverApi);
size_t const froxelBufferByteCount = getFroxelBufferByteCount(engine.getDriverApi());
mFroxelBufferEntryCount = froxelBufferByteCount / sizeof(FroxelEntry);
mFroxelBufferEntryCount &= ~0xF; // make sure it's a multiple of 16 (helps vectorizing)
assert_invariant(mFroxelBufferEntryCount >= 16); // that's also needed elsewhere
size_t const froxelRecordBufferByteCount = getFroxelRecordBufferByteCount(driverApi);
mFroxelRecordBufferEntryCount = froxelRecordBufferByteCount / sizeof(uint8_t);
assert_invariant(mFroxelRecordBufferEntryCount <= std::numeric_limits<uint16_t>::max());
mRecordsBuffer = driverApi.createBufferObject(
froxelRecordBufferByteCount,
mRecordsBuffer = driverApi.createBufferObject(RECORD_BUFFER_ENTRY_COUNT,
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
mFroxelsBuffer = driverApi.createBufferObject(
@@ -197,13 +185,14 @@ void Froxelizer::terminate(DriverApi& driverApi) noexcept {
}
void Froxelizer::setOptions(float const zLightNear, float const zLightFar) noexcept {
if (UTILS_UNLIKELY(mUserZLightNear != zLightNear || mUserZLightFar != zLightFar)) {
mUserZLightNear = zLightNear;
mUserZLightFar = zLightFar;
mDirtyFlags |= OPTIONS_CHANGED;
if (UTILS_UNLIKELY(mZLightNear != zLightNear || mZLightFar != zLightFar)) {
mZLightNear = zLightNear;
mZLightFar = zLightFar;
mDirtyFlags |= VIEWPORT_CHANGED;
}
}
void Froxelizer::setViewport(filament::Viewport const& viewport) noexcept {
if (UTILS_UNLIKELY(mViewport != viewport)) {
mViewport = viewport;
@@ -212,11 +201,10 @@ void Froxelizer::setViewport(filament::Viewport const& viewport) noexcept {
}
void Froxelizer::setProjection(const mat4f& projection,
float const near, UTILS_UNUSED float const far) noexcept {
if (UTILS_UNLIKELY(!fuzzyEqual(mProjection, projection) || mNear != near || mFar != far)) {
float const near, UTILS_UNUSED float far) noexcept {
if (UTILS_UNLIKELY(!fuzzyEqual(mProjection, projection))) {
mProjection = projection;
mNear = near;
mFar = far;
mDirtyFlags |= PROJECTION_CHANGED;
}
}
@@ -242,26 +230,26 @@ bool Froxelizer::prepare(
* the command stream.
*/
// froxel buffer (16 KiB with 4096 froxels)
// froxel buffer (~32 KiB)
mFroxelBufferUser = {
driverApi.allocatePod<FroxelEntry>(mFroxelBufferEntryCount),
mFroxelBufferEntryCount };
driverApi.allocatePod<FroxelEntry>(getFroxelBufferEntryCount()),
getFroxelBufferEntryCount() };
// record buffer (64 KiB max)
// record buffer (~16 KiB)
mRecordBufferUser = {
driverApi.allocatePod<RecordBufferType>(mFroxelRecordBufferEntryCount),
mFroxelRecordBufferEntryCount };
driverApi.allocatePod<RecordBufferType>(RECORD_BUFFER_ENTRY_COUNT),
RECORD_BUFFER_ENTRY_COUNT };
/*
* Temporary allocations for processing all froxel data
*/
// light records per froxel (~256 KiB with 4096 froxels)
// light records per froxel (~256 KiB)
mLightRecords = {
rootArenaScope.allocate<LightRecord>(getFroxelBufferEntryCount(), CACHELINE_SIZE),
getFroxelBufferEntryCount() };
// froxel thread data (~256KiB with 8192 max froxels and 256 lights)
// froxel thread data (~256 KiB)
mFroxelShardedData = {
rootArenaScope.allocate<FroxelThreadData>(GROUP_COUNT, CACHELINE_SIZE),
uint32_t(GROUP_COUNT)
@@ -289,7 +277,7 @@ void Froxelizer::computeFroxelLayout(
// calculate froxel dimension from FROXEL_BUFFER_ENTRY_COUNT_MAX and viewport
// - Start from the maximum number of froxels we can use in the x-y plane
constexpr size_t froxelSliceCount = FROXEL_SLICE_COUNT;
size_t const froxelSliceCount = FROXEL_SLICE_COUNT;
size_t const froxelPlaneCount = froxelBufferEntryCount / froxelSliceCount;
// - compute the number of square froxels we need in width and height, rounded down
// solving: | froxelCountX * froxelCountY == froxelPlaneCount
@@ -382,18 +370,6 @@ void Froxelizer::updateBoundingSpheres(
UTILS_NOINLINE
bool Froxelizer::update() noexcept {
bool uniformsNeedUpdating = false;
if (UTILS_UNLIKELY(mDirtyFlags & (OPTIONS_CHANGED|PROJECTION_CHANGED))) {
float const zLightFar = clamp(mUserZLightFar, mNear, mFar);
float zLightNear = clamp(mUserZLightNear, mNear, mFar);
zLightNear = std::min(zLightNear, zLightFar);
if (zLightFar != mZLightFar || zLightNear != mZLightNear) {
mDirtyFlags |= VIEWPORT_CHANGED;
mZLightNear = zLightNear;
mZLightFar = zLightFar;
}
}
if (UTILS_UNLIKELY(mDirtyFlags & VIEWPORT_CHANGED)) {
filament::Viewport const& viewport = mViewport;
@@ -524,19 +500,6 @@ bool Froxelizer::update() noexcept {
uniformsNeedUpdating = true;
}
assert_invariant(mZLightNear >= mNear);
if (UTILS_UNLIKELY(mDirtyFlags)) {
mFroxelConfigurationInfo = {
mFroxelCountX, mFroxelCountY, mFroxelCountZ,
mViewport.width, mViewport.height,
mFroxelDimension,
mZLightFar,
mLinearizer[0],
mProjection,
mClipTransform
};
}
mDirtyFlags = 0;
return uniformsNeedUpdating;
}
@@ -556,20 +519,20 @@ Froxel Froxelizer::getFroxelAt(size_t const x, size_t const y, size_t const z) c
}
UTILS_NOINLINE
size_t Froxelizer::findSliceZ(float const viewSpaceZ) const noexcept {
size_t Froxelizer::findSliceZ(float const z) const noexcept {
// The vastly common case is that z<0, so we always do the math for this case
// and we "undo" it below otherwise. This works because we're using fast::log2 which
// doesn't care if given a negative number (we'd have to use abs() otherwise).
// This whole function is now branch-less.
int s = int( fast::log2(-viewSpaceZ / mZLightFar) * mLinearizer[1] + float(mFroxelCountZ) );
int s = int( fast::log2(-z / mZLightFar) * mLinearizer[1] + float(mFroxelCountZ) );
// there are cases where z can be negative here, e.g.:
// - the light is visible, but its center is behind the camera
// - the camera's near is behind the camera (e.g. with shadowmap cameras)
// in that case just return the first slice
s = viewSpaceZ < 0 ? s : 0;
s = z < 0 ? s : 0;
// clamp between [0, mFroxelCountZ)
return size_t(clamp(s, 0, mFroxelCountZ - 1));
@@ -588,10 +551,10 @@ std::pair<size_t, size_t> Froxelizer::clipToIndices(float2 const& clip) const no
void Froxelizer::commit(DriverApi& driverApi) {
// send data to GPU
driverApi.updateBufferObject(mFroxelsBuffer,
{ mFroxelBufferUser.data(), mFroxelBufferEntryCount * sizeof(FroxelEntry) }, 0);
{ mFroxelBufferUser.data(), getFroxelBufferEntryCount() * sizeof(FroxelEntry) }, 0);
driverApi.updateBufferObject(mRecordsBuffer,
{ mRecordBufferUser.data(), mFroxelRecordBufferEntryCount }, 0);
{ mRecordBufferUser.data(), RECORD_BUFFER_ENTRY_COUNT }, 0);
#ifndef NDEBUG
mFroxelBufferUser.clear();
@@ -618,7 +581,7 @@ void Froxelizer::froxelizeLights(FEngine& engine,
// go through every light for that froxel
for (size_t i = 0; i < entry.count(); i++) {
// get the light index
assert_invariant(entry.offset() + i < mFroxelRecordBufferEntryCount);
assert_invariant(entry.offset() + i < RECORD_BUFFER_ENTRY_COUNT);
size_t const lightIndex = recordBufferUser[entry.offset() + i];
assert_invariant(lightIndex <= CONFIG_MAX_LIGHT_INDEX);
@@ -633,7 +596,7 @@ void Froxelizer::froxelizeLights(FEngine& engine,
void Froxelizer::froxelizeLoop(FEngine& engine,
const mat4f& UTILS_RESTRICT viewMatrix,
const FScene::LightSoa& UTILS_RESTRICT lightData) const noexcept {
const FScene::LightSoa& UTILS_RESTRICT lightData) noexcept {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
Slice<FroxelThreadData> froxelThreadData = mFroxelShardedData;
@@ -705,20 +668,18 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
Slice<FroxelThreadData> const froxelThreadData = mFroxelShardedData;
// Convert froxel data from N groups of M bits to LightRecord::bitset, so we can
// convert froxel data from N groups of M bits to LightRecord::bitset, so we can
// easily compare adjacent froxels, for compaction. The conversion loops below get
// inlined and vectorized in release builds.
// this gets very well vectorized...
Slice records(mLightRecords);
for (size_t j = 0, jc = getFroxelBufferEntryCount() ; j < jc; j++) {
using container_type = LightRecord::bitset::container_type;
constexpr size_t r = sizeof(container_type) / sizeof(LightGroupType);
UTILS_UNROLL
Slice<LightRecord> records(mLightRecords);
for (size_t j = 0, jc = getFroxelBufferEntryCount(); j < jc; j++) {
for (size_t i = 0; i < LightRecord::bitset::WORLD_COUNT; i++) {
using container_type = LightRecord::bitset::container_type;
constexpr size_t r = sizeof(container_type) / sizeof(LightGroupType);
container_type b = froxelThreadData[i * r][j];
UTILS_UNROLL
for (size_t k = 0; k < r; k++) {
b |= (container_type(froxelThreadData[i * r + k][j]) << (LIGHT_PER_GROUP * k));
}
@@ -737,21 +698,19 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
const size_t froxelCountX = mFroxelCountX;
RecordBufferType* const UTILS_RESTRICT froxelRecords = mRecordBufferUser.data();
// Initialize the first record with all lights in the scene -- this will be used only if
// initialize the first record with all lights in the scene -- this will be used only if
// we run out of record space.
// Our light count cannot be larger than 255 because it's stored in a uint8_t. This should
// be guaranteed by CONFIG_MAX_LIGHT_COUNT
assert_invariant(allLights.count() <= std::numeric_limits<uint8_t>::max());
const uint8_t allLightsCount = allLights.count();
const uint8_t allLightsCount = (uint8_t)std::min(size_t(255), allLights.count());
offset += allLightsCount;
allLights.forEachSetBit([p = froxelRecords](size_t l) mutable {
allLights.forEachSetBit([point = froxelRecords, froxelRecords](size_t l) mutable {
// make sure to keep this code branch-less
const size_t word = l / LIGHT_PER_GROUP;
const size_t bit = l % LIGHT_PER_GROUP;
l = (bit * GROUP_COUNT) | (word % GROUP_COUNT);
*p++ = RecordBufferType(l);
*point = (RecordBufferType)l;
// we need to "cancel" the write operation if we have more than 255 spot or point lights
// (this is a limitation of the data type used to store the light counts per froxel)
point += (point - froxelRecords < 255) ? 1 : 0;
});
// how many froxel record entries were reused (for debugging)
@@ -765,13 +724,11 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
}
// We have a limitation of 255 spot + 255 point lights per froxel.
assert_invariant(b.lights.count() <= std::numeric_limits<uint8_t>::max());
// note: initializer list for union cannot have more than one element
FroxelEntry entry{ offset, uint8_t(b.lights.count()) };
FroxelEntry entry{ offset, uint8_t(std::min(size_t(255), b.lights.count())) };
const size_t lightCount = entry.count();
if (UTILS_UNLIKELY(offset + lightCount >= mFroxelRecordBufferEntryCount)) {
if (UTILS_UNLIKELY(offset + lightCount >= RECORD_BUFFER_ENTRY_COUNT)) {
// DLOG(INFO) << "out of space: " << i << ", at " << offset;
// note: instead of dropping froxels we could look for similar records we've already
// filed up.
@@ -786,12 +743,15 @@ void Froxelizer::froxelizeAssignRecordsCompress() noexcept {
// iterate the bitfield
auto * const beginPoint = froxelRecords + offset;
b.lights.forEachSetBit([p = beginPoint](size_t l) mutable {
b.lights.forEachSetBit([point = beginPoint, beginPoint](size_t l) mutable {
// make sure to keep this code branch-less
const size_t word = l / LIGHT_PER_GROUP;
const size_t bit = l % LIGHT_PER_GROUP;
l = (bit * GROUP_COUNT) | (word % GROUP_COUNT);
*p++ = RecordBufferType(l);
*point = RecordBufferType(l);
// we need to "cancel" the write operation if we have more than 255 spot or point lights
// (this is a limitation of the data type used to store the light counts per froxel)
point += (point - beginPoint < 255) ? 1 : 0;
});
offset += lightCount;
@@ -832,7 +792,7 @@ static float2 project(mat4f const& p, float3 const& v) noexcept {
void Froxelizer::froxelizePointAndSpotLight(
FroxelThreadData& froxelThread, size_t bit,
mat4f const& UTILS_RESTRICT projection,
mat4f const& UTILS_RESTRICT p,
const LightParams& UTILS_RESTRICT light) const noexcept {
if (UTILS_UNLIKELY(light.position.z + light.radius < -mZLightFar)) { // z values are negative
@@ -863,14 +823,14 @@ void Froxelizer::froxelizePointAndSpotLight(
// e.g.: we could only do the z-min/max which is much easier to compute.
const float2 pts[8] = {
project(projection, { aabb.center.xy + aabb.halfExtent.xy * float2{ 1, 1 }, znear }),
project(projection, { aabb.center.xy + aabb.halfExtent.xy * float2{ 1,-1 }, znear }),
project(projection, { aabb.center.xy + aabb.halfExtent.xy * float2{-1, 1 }, znear }),
project(projection, { aabb.center.xy + aabb.halfExtent.xy * float2{-1,-1 }, znear }),
project(projection, { aabb.center.xy + aabb.halfExtent.xy * float2{ 1, 1 }, zfar }),
project(projection, { aabb.center.xy + aabb.halfExtent.xy * float2{ 1,-1 }, zfar }),
project(projection, { aabb.center.xy + aabb.halfExtent.xy * float2{-1, 1 }, zfar }),
project(projection, { aabb.center.xy + aabb.halfExtent.xy * float2{-1,-1 }, zfar }),
project(p, { aabb.center.xy + aabb.halfExtent.xy * float2{ 1, 1 }, znear }),
project(p, { aabb.center.xy + aabb.halfExtent.xy * float2{ 1,-1 }, znear }),
project(p, { aabb.center.xy + aabb.halfExtent.xy * float2{-1, 1 }, znear }),
project(p, { aabb.center.xy + aabb.halfExtent.xy * float2{-1,-1 }, znear }),
project(p, { aabb.center.xy + aabb.halfExtent.xy * float2{ 1, 1 }, zfar }),
project(p, { aabb.center.xy + aabb.halfExtent.xy * float2{ 1,-1 }, zfar }),
project(p, { aabb.center.xy + aabb.halfExtent.xy * float2{-1, 1 }, zfar }),
project(p, { aabb.center.xy + aabb.halfExtent.xy * float2{-1,-1 }, zfar }),
};
float2 pmin = std::numeric_limits<float>::max();
@@ -889,7 +849,6 @@ void Froxelizer::froxelizePointAndSpotLight(
assert_invariant(x0 <= x1);
assert_invariant(y0 <= y1);
assert_invariant(z0 <= z1);
#endif
const size_t zcenter = findSliceZ(s.z);
@@ -909,7 +868,7 @@ void Froxelizer::froxelizePointAndSpotLight(
// the sphere (light) intersects this slice's plane, and we now have a new smaller
// sphere centered there. Now, find x & y slices that contain the sphere's center
// (note: this changes with the Z slices)
const float2 clip = project(projection, cz.xyz);
const float2 clip = project(p, cz.xyz);
auto const [xcenter, ycenter] = clipToIndices(clip);
for (size_t iy = y0; iy <= y1; ++iy) {
+33 -57
View File
@@ -22,31 +22,31 @@
#include "details/Scene.h"
#include "details/Engine.h"
#include "private/filament/EngineEnums.h"
#include "private/filament/UibStructs.h"
#include <filament/View.h>
#include <filament/Viewport.h>
#include <backend/Handle.h>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/bitset.h>
#include <utils/Slice.h>
#include <cstdint>
#include <cstddef>
#include <limits>
#include <utility>
#include <math/mat4.h>
#include <math/vec2.h>
#include <math/vec4.h>
namespace filament {
// The number of froxel buffer entries is determined by max UBO size (see
// getFroxelBufferByteCount()). We also introduce the limit below because increasing the number of
// froxels adds more pressure on the "record buffer" which stores the light indices per froxel. The
// record buffer is limited to min(16K[ubo], 64K[uint16]) entries. In practice some froxels are not
// used, so we can store more.
constexpr size_t FROXEL_BUFFER_MAX_ENTRY_COUNT = 8192;
// Froxel buffer UBO is an array of uvec4. Make sure that the buffer is properly aligned.
static_assert(FROXEL_BUFFER_MAX_ENTRY_COUNT % 4 == 0u);
class FEngine;
class FCamera;
class FTexture;
@@ -89,12 +89,12 @@ public:
void terminate(backend::DriverApi& driverApi) noexcept;
// GPU buffer containing records. Valid after construction.
// gpu buffer containing records. valid after construction.
backend::Handle<backend::HwBufferObject> getRecordBuffer() const noexcept {
return mRecordsBuffer;
}
// GPU buffer containing froxels. Valid after construction.
// gpu buffer containing froxels. valid after construction.
backend::Handle<backend::HwBufferObject> getFroxelBuffer() const noexcept {
return mFroxelsBuffer;
}
@@ -110,12 +110,10 @@ public:
* projection camera projection matrix
* projectionNear near plane
* projectionFar far plane
* clipTransform [debugging] the clipTransform that's already included in the projection
*
* return true if updateUniforms() needs to be called
*/
bool prepare(backend::DriverApi& driverApi, RootArenaScope& rootArenaScope,
Viewport const& viewport,
bool prepare(backend::DriverApi& driverApi, RootArenaScope& rootArenaScope, Viewport const& viewport,
const math::mat4f& projection, float projectionNear, float projectionFar,
math::float4 const& clipTransform) noexcept;
@@ -127,20 +125,16 @@ public:
float getLightFar() const noexcept { return mZLightFar; }
// Update Records and Froxels texture with lights data. This is thread-safe.
// update Records and Froxels texture with lights data. this is thread-safe.
void froxelizeLights(FEngine& engine, math::mat4f const& viewMatrix,
const FScene::LightSoa& lightData) noexcept;
void updateUniforms(PerViewUib& s) const {
void updateUniforms(PerViewUib& s) {
s.zParams = mParamsZ;
s.fParams = mParamsF;
s.froxelCountXY = math::float2{ mViewport.width, mViewport.height } / mFroxelDimension;
}
static size_t getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexcept;
static size_t getFroxelRecordBufferByteCount(FEngine::DriverApi& driverApi) noexcept;
// send froxel data to GPU
void commit(backend::DriverApi& driverApi);
@@ -150,35 +144,30 @@ public:
*/
struct FroxelEntry {
FroxelEntry(uint16_t const offset, uint8_t const count) noexcept
inline FroxelEntry(uint16_t const offset, uint8_t const count) noexcept
: u32((offset << 16) | count) { }
uint8_t count() const noexcept { return u32 & 0xFFu; }
uint16_t offset() const noexcept { return u32 >> 16u; }
inline uint8_t count() const noexcept { return u32 & 0xFFu; }
inline uint16_t offset() const noexcept { return u32 >> 16u; }
uint32_t u32 = 0;
};
static_assert(sizeof(FroxelEntry) == 4u);
// We can't change this easily because the shader expects 16 indices per uint4
// we can't change this easily because the shader expects 16 indices per uint4
using RecordBufferType = uint8_t;
const utils::Slice<FroxelEntry>& getFroxelBufferUser() const { return mFroxelBufferUser; }
const utils::Slice<RecordBufferType>& getRecordBufferUser() const { return mRecordBufferUser; }
// This is chosen so froxelizePointAndSpotLight() vectorizes 4 froxel tests / spotlight
// this is chosen so froxelizePointAndSpotLight() vectorizes 4 froxel tests / spotlight
// with 256 lights this implies 8 jobs (256 / 32) for froxelization.
using LightGroupType = uint32_t;
static size_t getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexcept;
View::FroxelConfigurationInfo getFroxelConfigurationInfo() const noexcept;
private:
size_t getFroxelBufferEntryCount() const noexcept {
// We guarantee that mFroxelBufferEntryCount is a multiple of 16. With this knowledge
// the compiler can do a much better job at vectorizing. For similar reasons, it's
// important to keep mFroxelBufferEntryCount an uint32_t (as opposed to a size_t).
assert_invariant((mFroxelBufferEntryCount & 0xF) == 0);
UTILS_ASSUME((mFroxelBufferEntryCount & 0xF) == 0);
UTILS_ASSUME(mFroxelBufferEntryCount >= 16);
return mFroxelBufferEntryCount;
}
@@ -216,7 +205,7 @@ private:
bool update() noexcept;
void froxelizeLoop(FEngine& engine,
math::mat4f const& viewMatrix, const FScene::LightSoa& lightData) const noexcept;
math::mat4f const& viewMatrix, const FScene::LightSoa& lightData) noexcept;
void froxelizeAssignRecordsCompress() noexcept;
@@ -255,28 +244,21 @@ private:
LinearAllocatorArena mArena; // ~256 KiB
// 4096 froxels fits in a 16KiB buffer, the minimum guaranteed in GLES 3.x and Vulkan 1.1
uint32_t mFroxelBufferEntryCount = 4096;
// 16384 entries is our minimum with a 16KiB buffer
uint32_t mFroxelRecordBufferEntryCount = 16384;
size_t mFroxelBufferEntryCount = 4096;
// allocations in the private froxel arena
float* mDistancesZ = nullptr;
math::float4* mPlanesX = nullptr;
math::float4* mPlanesY = nullptr;
math::float4* mBoundingSpheres = nullptr; // 64 KiB w/ 4096 froxels
math::float4* mBoundingSpheres = nullptr; // 128 KiB w/ 8192 froxels
// allocations in the per frame arena
// max | real | size
// 8192 | 4096 | 512 KiB
// 8192 | 8192 | 768 KiB
// 65536 | 65536 | 6.0 MiB
utils::Slice<LightRecord> mLightRecords; // 256 KiB w/ 256 lights and 4096 froxels
utils::Slice<FroxelThreadData> mFroxelShardedData; // 256 KiB w/ 256 lights and 8192 max froxels
utils::Slice<FroxelThreadData> mFroxelShardedData; // 256 KiB w/ 256 lights and 8192 froxels
utils::Slice<FroxelEntry> mFroxelBufferUser; // 32 KiB w/ 8192 froxels
utils::Slice<LightRecord> mLightRecords; // 256 KiB w/ 256 lights
// allocations in the command stream
utils::Slice<FroxelEntry> mFroxelBufferUser; // 16 KiB w/ 4096 froxels
utils::Slice<RecordBufferType> mRecordBufferUser; // 16 KiB to 64 KiB
utils::Slice<RecordBufferType> mRecordBufferUser; // 16 KiB
uint16_t mFroxelCountX = 0;
uint16_t mFroxelCountY = 0;
@@ -297,21 +279,15 @@ private:
math::float4 mParamsZ = {};
math::uint3 mParamsF = {};
float mNear = 0.0f; // camera near
float mFar = 0.0f; // culling camera far
float mZLightNear;
float mZLightFar;
float mUserZLightNear;
float mUserZLightFar;
// track if we need to update our internal state before froxelizing
uint8_t mDirtyFlags = 0;
enum {
VIEWPORT_CHANGED = 0x01,
PROJECTION_CHANGED = 0x02,
OPTIONS_CHANGED = 0x04
PROJECTION_CHANGED = 0x02
};
View::FroxelConfigurationInfo mFroxelConfigurationInfo{};
};
} // namespace filament
+40
View File
@@ -233,6 +233,46 @@ 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,6 +229,11 @@ 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);
@@ -761,6 +766,38 @@ 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,6 +49,7 @@ class BufferInterfaceBlock;
class SamplerInterfaceBlock;
struct SubpassInfo;
struct MaterialConstant;
struct MaterialMutableConstant;
struct MaterialPushConstant;
class MaterialParser {
@@ -79,6 +80,8 @@ 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;
@@ -238,6 +241,13 @@ 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);
+7 -2
View File
@@ -199,6 +199,8 @@ 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;
}
@@ -344,7 +346,7 @@ void PostProcessManager::init() noexcept {
engine.getDummyMorphTargetBuffer()->getTangentsHandle(), {});
driver.updateDescriptorSetTexture(mDummyPerRenderableDsh,
+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, engine.getZeroTexture(),
+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, engine.getZeroTextureArray(),
{});
mSsrPassDescriptorSet.init(engine);
@@ -465,7 +467,10 @@ UTILS_NOINLINE
PipelineState PostProcessManager::getPipelineState(
FMaterial const* const ma, PostProcessVariant variant) const noexcept {
return {
.program = ma->getProgram(Variant{ Variant::type_t(variant) }),
.program = ma->getProgram(Variant{ Variant::type_t(variant) },
// TODO(exv): allow post process materials to use mutable spec constants?
Program::MutableSpecConstantsInfo(0)
),
.vertexBufferInfo = mFullScreenQuadVbih,
.pipelineLayout = {
.setLayout = {
+5 -2
View File
@@ -243,7 +243,9 @@ 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, CompilerPriorityQueue::CRITICAL);
ma->prepareProgram(first->info.materialVariant,
first->info.mi->getMutableSpecConstants(),
CompilerPriorityQueue::CRITICAL);
}
}
}
@@ -1096,7 +1098,8 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
}
assert_invariant(ma);
pipeline.program = ma->getProgram(info.materialVariant);
pipeline.program = ma->getProgram(info.materialVariant,
info.mi->getMutableSpecConstants());
if (UTILS_UNLIKELY(memcmp(&pipeline, &currentPipeline, sizeof(PipelineState)) != 0)) {
currentPipeline = pipeline;
+1 -13
View File
@@ -27,19 +27,7 @@ bool ZstdHelper::isCompressed(const void* src, size_t src_size) noexcept {
if (src_size < 4) {
return false;
}
// `src` may not be aligned to 4 bytes, which is violating the alignment requirement of
// `UndefinedBehaviorSanitizer: misaligned-pointer-use`. So reconstruct the 32-bit integer from
// bytes in little-endian order as the expected byte sequence is 28 B5 2F FD and
// ZSTD_MAGICNUMBER refers to 0xFD2FB528. This should work correctly on both little-endian and
// big-endian systems.
const auto* p = static_cast<const uint8_t*>(src);
const uint32_t magic =
(uint32_t)p[0] |
(uint32_t)(p[1] << 8) |
(uint32_t)(p[2] << 16) |
(uint32_t)(p[3] << 24);
const uint32_t magic = *static_cast<const uint32_t*>(src);
return magic == ZSTD_MAGICNUMBER;
}
+2 -1
View File
@@ -127,8 +127,9 @@ void FInstanceBuffer::setLocalTransforms(
memcpy(mLocalTransforms.data() + offset, localTransforms, sizeof(math::mat4f) * count);
}
void FInstanceBuffer::prepare(DriverApi& driver, math::mat4f const& rootTransform,
void FInstanceBuffer::prepare(FEngine& engine, math::mat4f const& rootTransform,
const PerRenderableData& ubo) {
DriverApi& driver = engine.getDriverApi();
// TODO: allocate this staging buffer from a pool.
constexpr uint32_t stagingBufferSize = sizeof(PerRenderableUib);
+1 -5
View File
@@ -21,7 +21,6 @@
#include <filament/InstanceBuffer.h>
#include <backend/DriverApiForward.h>
#include <backend/Handle.h>
#include <math/mat4.h>
@@ -30,7 +29,6 @@
#include <utils/FixedCapacityVector.h>
#include <cstddef>
#include <cstdint>
namespace filament {
@@ -50,12 +48,11 @@ public:
void setLocalTransforms(math::mat4f const* localTransforms, size_t count, size_t offset);
void prepare(backend::DriverApi& driver, math::mat4f const& rootTransform, const PerRenderableData& ubo);
void prepare(FEngine& engine, math::mat4f const& rootTransform, const PerRenderableData& ubo);
utils::CString const& getName() const noexcept { return mName; }
backend::BufferObjectHandle getHandle() const noexcept { return mHandle; }
uint32_t getOffset() const noexcept { return mOffset; }
private:
friend class RenderableManager;
@@ -64,7 +61,6 @@ private:
utils::CString mName;
size_t mInstanceCount;
backend::BufferObjectHandle mHandle;
uint32_t mOffset = 0;
};
FILAMENT_DOWNCAST(InstanceBuffer)
+96 -38
View File
@@ -364,8 +364,24 @@ 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
@@ -466,13 +482,17 @@ 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)) {
prepareProgram(variant, priority);
for (int spec = 0; spec < mutableConstantVariations; spec++) {
prepareProgram(variant, Program::MutableSpecConstantsInfo(spec), priority);
}
}
}
}
@@ -553,14 +573,15 @@ 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, priorityQueue);
getSurfaceProgramSlow(variant, mutableSpecConstants, priorityQueue);
break;
case MaterialDomain::POST_PROCESS:
getPostProcessProgramSlow(variant, priorityQueue);
getPostProcessProgramSlow(variant, mutableSpecConstants, priorityQueue);
break;
case MaterialDomain::COMPUTE:
// TODO: implement MaterialDomain::COMPUTE
@@ -569,6 +590,7 @@ 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
@@ -579,25 +601,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) };
Program pb{ getProgramWithVariants(variant, vertexVariant, fragmentVariant, mutableSpecConstants) };
pb.priorityQueue(priorityQueue);
pb.multiview(
mEngine.getConfig().stereoscopicType == StereoscopicType::MULTIVIEW &&
Variant::isStereoVariant(variant));
createAndCacheProgram(std::move(pb), variant);
createAndCacheProgram(std::move(pb), variant, mutableSpecConstants);
}
void FMaterial::getPostProcessProgramSlow(Variant const variant,
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants,
CompilerPriorityQueue const priorityQueue) const noexcept {
Program pb{ getProgramWithVariants(variant, variant, variant) };
Program pb{ getProgramWithVariants(variant, variant, variant, mutableSpecConstants) };
pb.priorityQueue(priorityQueue);
createAndCacheProgram(std::move(pb), variant);
createAndCacheProgram(std::move(pb), variant, mutableSpecConstants);
}
Program FMaterial::getProgramWithVariants(
Variant variant,
Variant vertexVariant,
Variant fragmentVariant) const {
Program FMaterial::getProgramWithVariants(Variant variant,
Variant vertexVariant, Variant fragmentVariant,
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const {
FEngine const& engine = mEngine;
const ShaderModel sm = engine.getShaderModel();
const bool isNoop = engine.getBackend() == Backend::NOOP;
@@ -658,29 +680,34 @@ Program FMaterial::getProgramWithVariants(
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_RENDERABLE]);
program.descriptorBindings(+DescriptorSetBindingPoints::PER_MATERIAL,
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_MATERIAL]);
program.specializationConstants(mSpecializationConstants);
program.specializationConstants(mSpecializationConstants,
CONFIG_MAX_RESERVED_SPEC_CONSTANTS + mMaterialConstants.size(), mutableSpecConstants);
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), variant.key));
program.cacheId(
hash::combine(size_t(mCacheId),
hash::combine(variant.key, mutableSpecConstants.getValue())));
return program;
}
void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const noexcept {
void FMaterial::createAndCacheProgram(Program&& p, Variant const variant,
Program::MutableSpecConstantsInfo const mutableSpecConstants) 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[variant.key];
auto const program = pDefaultMaterial->mCachedPrograms[cacheIndex];
if (program) {
mCachedPrograms[variant.key] = program;
mCachedPrograms[cacheIndex] = program;
return;
}
}
@@ -689,17 +716,17 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const
auto const program = driverApi.createProgram(std::move(p));
driverApi.setDebugTag(program.getId(), mName);
assert_invariant(program);
mCachedPrograms[variant.key] = program;
mCachedPrograms[cacheIndex] = 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[variant.key]) {
if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[cacheIndex]) {
// set the tag to the default material name
driverApi.setDebugTag(program.getId(), mName);
pDefaultMaterial->mCachedPrograms[variant.key] = program;
pDefaultMaterial->mCachedPrograms[cacheIndex] = program;
}
}
}
@@ -747,6 +774,14 @@ 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
@@ -805,7 +840,9 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
#endif // FILAMENT_ENABLE_MATDBG
[[nodiscard]] Handle<HwProgram> FMaterial::getProgramWithMATDBG(Variant const variant) const noexcept {
[[nodiscard]] Handle<HwProgram> FMaterial::getProgramWithMATDBG(Variant const variant,
Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
size_t cacheIndex = getCachedProgramIndex(variant, mutableSpecConstants);
#if FILAMENT_ENABLE_MATDBG
assert_invariant((size_t)variant.key < VARIANT_COUNT);
std::unique_lock lock(mActiveProgramsLock);
@@ -820,13 +857,13 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pActiveVariants) {
lock.unlock();
if (isSharedVariant(variant)) {
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[variant.key]) {
return pDefaultMaterial->getProgram(variant);
if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[cacheIndex]) {
return pDefaultMaterial->getProgram(variant, mutableSpecConstants);
}
}
#endif
assert_invariant(mCachedPrograms[variant.key]);
return mCachedPrograms[variant.key];
assert_invariant(mCachedPrograms[cacheIndex]);
return mCachedPrograms[cacheIndex];
}
void FMaterial::destroyPrograms(FEngine& engine,
@@ -839,7 +876,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 = VARIANT_COUNT; k < n; ++k) {
for (size_t k = 0, n = cachedPrograms.size(); 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.
@@ -857,7 +894,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
UTILS_UNUSED_IN_RELEASE
auto const UTILS_NULLABLE pDefaultMaterial = engine.getDefaultMaterial();
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
for (size_t k = 0, n = cachedPrograms.size(); 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.
@@ -869,7 +906,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
// During Engine::shutdown, auto-cleanup destroys the
// default material first
assert_invariant(!pDefaultMaterial ||
pDefaultMaterial->mCachedPrograms[k]);
pDefaultMaterial->mCachedPrograms[k & (VARIANT_COUNT - 1)]);
// we don't own this variant, skip, but clear the entry.
cachedPrograms[k].clear();
continue;
@@ -883,7 +920,7 @@ void FMaterial::destroyPrograms(FEngine& engine,
break;
}
case MaterialDomain::POST_PROCESS: {
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
for (size_t k = 0, n = cachedPrograms.size(); 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.
@@ -895,8 +932,13 @@ void FMaterial::destroyPrograms(FEngine& engine,
break;
}
case MaterialDomain::COMPUTE: {
// Compute programs don't have variants
driverApi.destroyProgram(std::move(cachedPrograms[0]));
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]));
}
}
break;
}
}
@@ -1054,9 +1096,6 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
int const maxFroxelBufferHeight =
int(Froxelizer::getFroxelBufferByteCount(engine.getDriverApi()) / 16u);
int const froxelRecordBufferHeight =
int(Froxelizer::getFroxelRecordBufferByteCount(engine.getDriverApi()) / 16u);
bool const staticTextureWorkaround =
engine.getDriverApi().isWorkaroundNeeded(Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR);
@@ -1073,9 +1112,6 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT,
int(maxFroxelBufferHeight) });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_FROXEL_RECORD_BUFFER_HEIGHT,
int(froxelRecordBufferHeight) });
mSpecializationConstants.push_back({
+ReservedSpecializationConstants::CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP,
engine.debug.shadowmap.debug_directional_shadowmap });
@@ -1137,6 +1173,19 @@ 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)];
@@ -1192,7 +1241,9 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
}
assert_invariant(Variant::isValidDepthVariant(variant));
if (hasVariant(variant)) {
prepareProgram(variant, CompilerPriorityQueue::HIGH);
// The default material shouldn't ever have mutable spec constants.
prepareProgram(variant, Program::MutableSpecConstantsInfo(0),
CompilerPriorityQueue::HIGH);
}
}
return;
@@ -1205,9 +1256,16 @@ 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));
mCachedPrograms[variant.key] = pDefaultMaterial->mCachedPrograms[variant.key];
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];
}
}
}
}
+47 -13
View File
@@ -132,8 +132,9 @@ public:
FEngine& getEngine() const noexcept { return mEngine; }
bool isCached(Variant const variant) const noexcept {
return bool(mCachedPrograms[variant.key]);
bool isCached(Variant const variant,
const backend::Program::MutableSpecConstantsInfo mutableSpecConstants) const noexcept {
return bool(mCachedPrograms[getCachedProgramIndex(variant, mutableSpecConstants)]);
}
void invalidate(Variant::type_t variantMask = 0, Variant::type_t variantValue = 0) noexcept;
@@ -142,22 +143,26 @@ 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))) {
prepareProgramSlow(variant, priorityQueue);
if (UTILS_UNLIKELY(!isCached(variant, mutableSpecConstants))) {
prepareProgramSlow(variant, mutableSpecConstants, priorityQueue);
}
}
// getProgram returns the backend program for the material's given variant.
// getProgram returns the backend program for the material's given variant and set of mutable
// spec constants.
// Must be called after prepareProgram().
[[nodiscard]]
backend::Handle<backend::HwProgram> getProgram(Variant const variant) const noexcept {
backend::Handle<backend::HwProgram> getProgram(Variant const variant,
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept {
#if FILAMENT_ENABLE_MATDBG
return getProgramWithMATDBG(variant);
return getProgramWithMATDBG(variant, mutableSpecConstants);
#endif
assert_invariant(mCachedPrograms[variant.key]);
return mCachedPrograms[variant.key];
size_t index = getCachedProgramIndex(variant, mutableSpecConstants);
assert_invariant(mCachedPrograms[index]);
return mCachedPrograms[index];
}
// MaterialInstance::use() binds descriptor sets before drawing. For shared variants,
@@ -178,7 +183,8 @@ public:
}
[[nodiscard]]
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant) const noexcept;
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant,
backend::Program::MutableSpecConstantsInfo const mutableSpecConstants) const noexcept;
bool isVariantLit() const noexcept { return mIsVariantLit; }
@@ -235,6 +241,12 @@ 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,
@@ -288,34 +300,48 @@ 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) const;
Variant vertexVariant, Variant fragmentVariant,
backend::Program::MutableSpecConstantsInfo mutableSpecConstants) 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) const noexcept;
void createAndCacheProgram(backend::Program&& p, Variant variant,
backend::Program::MutableSpecConstantsInfo mutableSpecConstants) 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 std::array<backend::Handle<backend::HwProgram>, VARIANT_COUNT> mCachedPrograms;
mutable utils::FixedCapacityVector<backend::Handle<backend::HwProgram>> mCachedPrograms;
DescriptorSetLayout mPerViewDescriptorSetLayout;
DescriptorSetLayout mPerViewDescriptorSetLayoutVsm;
DescriptorSetLayout mDescriptorSetLayout;
@@ -351,6 +377,7 @@ 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;
@@ -375,6 +402,13 @@ 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,6 +63,7 @@ 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),
@@ -128,6 +129,7 @@ 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),
+13 -1
View File
@@ -33,6 +33,7 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/Program.h>
#include <utils/BitmaskEnum.h>
#include <utils/bitset.h>
@@ -68,7 +69,7 @@ public:
void terminate(FEngine& engine);
void commitStreamUniformAssociations(FEngine::DriverApi& driver);
void commit(FEngine& engine) const;
void commit(FEngine::DriverApi& driver) const;
@@ -244,6 +245,10 @@ public:
using MaterialInstance::setParameter;
const backend::Program::MutableSpecConstantsInfo getMutableSpecConstants() const noexcept {
return mConstants;
}
private:
friend class FMaterial;
friend class MaterialInstance;
@@ -266,6 +271,12 @@ 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;
@@ -279,6 +290,7 @@ private:
mutable DescriptorSet mDescriptorSet;
UniformBuffer mUniforms;
bool mHasStreamUniformAssociations = false;
backend::Program::MutableSpecConstantsInfo mConstants;
backend::PolygonOffset mPolygonOffset{};
backend::StencilState mStencilState{};
+66 -6
View File
@@ -17,18 +17,18 @@
#include "details/Scene.h"
#include "Allocators.h"
#include "BufferPoolAllocator.h"
#include "backend/Handle.h"
#include "components/LightManager.h"
#include "components/RenderableManager.h"
#include "components/TransformManager.h"
#include "details/Engine.h"
#include "details/InstanceBuffer.h"
#include "details/Skybox.h"
#include <backend/Handle.h>
#include <private/filament/UibStructs.h>
#include <private/utils/Tracing.h>
#include <filament/Box.h>
@@ -70,7 +70,7 @@ namespace filament {
// ------------------------------------------------------------------------------------------------
FScene::FScene(FEngine& engine) :
mEngine(engine) {
mEngine(engine), mSharedState(std::make_shared<SharedState>()) {
}
FScene::~FScene() noexcept = default;
@@ -406,6 +406,66 @@ void FScene::prepareVisibleRenderables(Range<uint32_t> visibleRenderables) noexc
}
}
void FScene::updateUBOs(
Range<uint32_t> visibleRenderables,
Handle<HwBufferObject> renderableUbh) noexcept {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
FEngine::DriverApi& driver = mEngine.getDriverApi();
// don't allocate more than 16 KiB directly into the render stream
static constexpr size_t MAX_STREAM_ALLOCATION_COUNT = 64; // 16 KiB
const size_t count = visibleRenderables.size();
PerRenderableData* buffer = [&]{
if (count >= MAX_STREAM_ALLOCATION_COUNT) {
// use the heap allocator
auto& bufferPoolAllocator = mSharedState->mBufferPoolAllocator;
return static_cast<PerRenderableData*>(bufferPoolAllocator.get(count * sizeof(PerRenderableData)));
} else {
// allocate space into the command stream directly
return driver.allocatePod<PerRenderableData>(count);
}
}();
PerRenderableData const* const uboData = mRenderableData.data<UBO>();
mat4f const* const worldTransformData = mRenderableData.data<WORLD_TRANSFORM>();
// prepare each InstanceBuffer.
FRenderableManager::InstancesInfo const* instancesData = mRenderableData.data<INSTANCES>();
for (uint32_t const i : visibleRenderables) {
auto& instancesInfo = instancesData[i];
if (UTILS_UNLIKELY(instancesInfo.buffer)) {
instancesInfo.buffer->prepare(
mEngine, worldTransformData[i], uboData[i]);
}
}
// copy our data into the UBO for each visible renderable
for (uint32_t const i : visibleRenderables) {
buffer[i] = uboData[i];
}
// We capture state shared between Scene and the update buffer callback, because the Scene could
// be destroyed before the callback executes.
std::weak_ptr<SharedState>* const weakShared =
new (std::nothrow) std::weak_ptr<SharedState>(mSharedState);
// update the UBO
driver.resetBufferObject(renderableUbh);
driver.updateBufferObjectUnsynchronized(renderableUbh, {
buffer, count * sizeof(PerRenderableData),
+[](void* p, size_t const s, void* user) {
std::weak_ptr<SharedState>* const weakShared =
static_cast<std::weak_ptr<SharedState>*>(user);
if (s >= MAX_STREAM_ALLOCATION_COUNT * sizeof(PerRenderableData)) {
if (auto state = weakShared->lock()) {
state->mBufferPoolAllocator.put(p);
}
}
delete weakShared;
}, weakShared
}, 0);
}
void FScene::terminate(FEngine&) {
}
@@ -437,7 +497,7 @@ void FScene::prepareDynamicLights(const CameraInfo& camera,
auto const* UTILS_RESTRICT shadowInfo = lightData.data<SHADOW_INFO>();
for (size_t i = DIRECTIONAL_LIGHTS_COUNT, c = size; i < c; ++i) {
const size_t gpuIndex = i - DIRECTIONAL_LIGHTS_COUNT;
auto const li = instances[i];
auto li = instances[i];
lp[gpuIndex].positionFalloff = { spheres[i].xyz, lcm.getSquaredFalloffInv(li) };
lp[gpuIndex].direction = directions[i];
lp[gpuIndex].reserved1 = {};
@@ -567,7 +627,7 @@ bool FScene::hasContactShadows() const noexcept {
// find out if at least one light has contact-shadow enabled
// TODO: we could cache the result of this Loop in the LightManager
auto const& lcm = mEngine.getLightManager();
auto& lcm = mEngine.getLightManager();
const auto *pFirst = mLightData.begin<LIGHT_INSTANCE>();
const auto *pLast = mLightData.end<LIGHT_INSTANCE>();
while (pFirst != pLast) {
+21 -2
View File
@@ -26,18 +26,28 @@
#include "components/LightManager.h"
#include "components/RenderableManager.h"
#include "components/TransformManager.h"
#include "BufferPoolAllocator.h"
#include <filament/Box.h>
#include <filament/Scene.h>
#include <math/mathfwd.h>
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <utils/Slice.h>
#include <utils/StructureOfArrays.h>
#include <utils/Range.h>
#include <utils/debug.h>
#include <stddef.h>
#include <tsl/robin_set.h>
#include <memory>
namespace filament {
struct CameraInfo;
@@ -126,14 +136,14 @@ public:
RenderableSoa const& getRenderableData() const noexcept { return mRenderableData; }
RenderableSoa& getRenderableData() noexcept { return mRenderableData; }
static uint32_t getPrimitiveCount(RenderableSoa const& soa,
static inline uint32_t getPrimitiveCount(RenderableSoa const& soa,
uint32_t const first, uint32_t const last) noexcept {
// the caller must guarantee that last is dereferenceable
return soa.elementAt<SUMMED_PRIMITIVE_COUNT>(last) -
soa.elementAt<SUMMED_PRIMITIVE_COUNT>(first);
}
static uint32_t getPrimitiveCount(RenderableSoa const& soa, uint32_t const last) noexcept {
static inline uint32_t getPrimitiveCount(RenderableSoa const& soa, uint32_t const last) noexcept {
// the caller must guarantee that last is dereferenceable
return soa.elementAt<SUMMED_PRIMITIVE_COUNT>(last);
}
@@ -176,6 +186,9 @@ public:
LightSoa const& getLightData() const noexcept { return mLightData; }
LightSoa& getLightData() noexcept { return mLightData; }
void updateUBOs(utils::Range<uint32_t> visibleRenderables,
backend::Handle<backend::HwBufferObject> renderableUbh) noexcept;
bool hasContactShadows() const noexcept;
private:
@@ -217,6 +230,12 @@ private:
RenderableSoa mRenderableData;
LightSoa mLightData;
bool mHasContactShadows = false;
// State shared between Scene and driver callbacks.
struct SharedState {
BufferPoolAllocator<3> mBufferPoolAllocator = {};
};
std::shared_ptr<SharedState> mSharedState;
};
FILAMENT_DOWNCAST(Scene)
+5 -72
View File
@@ -17,7 +17,6 @@
#include "details/View.h"
#include "Allocators.h"
#include "BufferPoolAllocator.h"
#include "Culler.h"
#include "DebugRegistry.h"
#include "FrameHistory.h"
@@ -28,11 +27,8 @@
#include "ShadowMap.h"
#include "ShadowMapManager.h"
#include "components/TransformManager.h"
#include "details/Engine.h"
#include "details/IndirectLight.h"
#include "details/InstanceBuffer.h"
#include "details/RenderTarget.h"
#include "details/Renderer.h"
#include "details/Scene.h"
@@ -58,7 +54,6 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Panic.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <utils/Zip2Iterator.h>
@@ -76,7 +71,6 @@
#include <chrono>
#include <functional>
#include <memory>
#include <new>
#include <ratio>
#include <utility>
@@ -99,8 +93,7 @@ FView::FView(FEngine& engine)
mUniforms(engine.getDriverApi()),
mColorPassDescriptorSet{
{ engine, false, mUniforms },
{ engine, true, mUniforms } },
mSharedState(std::make_shared<SharedState>())
{ engine, true, mUniforms } }
{
DriverApi& driver = engine.getDriverApi();
@@ -125,7 +118,7 @@ FView::FView(FEngine& engine)
mCommonRenderableDescriptorSet.setSampler(layout,
+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS,
engine.getZeroTexture(), {});
engine.getZeroTextureArray(), {});
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
@@ -773,7 +766,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
// TODO: should we shrink the underlying UBO at some point?
}
assert_invariant(mRenderableUbh);
updateUBOs(driver, renderableData, merged, mRenderableUbh);
scene->updateUBOs(merged, mRenderableUbh);
mCommonRenderableDescriptorSet.setBuffer(
engine.getPerRenderableDescriptorSetLayout(),
@@ -810,8 +803,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
descriptorSet.setBuffer(layout,
+PerRenderableBindingPoints::OBJECT_UNIFORMS,
instance.buffer ? instance.buffer->getHandle() : mRenderableUbh,
instance.buffer ? instance.buffer->getOffset() : 0,
sizeof(PerRenderableUib));
0, sizeof(PerRenderableUib));
descriptorSet.setBuffer(layout,
+PerRenderableBindingPoints::BONES_UNIFORMS,
@@ -831,7 +823,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
descriptorSet.setSampler(layout,
+PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS,
engine.getZeroTexture(), {});
engine.getZeroTextureArray(), {});
if (UTILS_UNLIKELY(skinning.handle || morphing.handle)) {
descriptorSet.setBuffer(layout,
@@ -893,65 +885,6 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
colorPassDescriptorSet.prepareMaterialGlobals(mMaterialGlobals);
}
void FView::updateUBOs(
FEngine::DriverApi& driver,
FScene::RenderableSoa& renderableData,
utils::Range<uint32_t> visibleRenderables,
Handle<HwBufferObject> renderableUbh) noexcept {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
// don't allocate more than 16 KiB directly into the render stream
static constexpr size_t MAX_STREAM_ALLOCATION_COUNT = 64; // 16 KiB
const size_t count = visibleRenderables.size();
PerRenderableData* buffer = [&]{
if (count >= MAX_STREAM_ALLOCATION_COUNT) {
// use the heap allocator
auto& bufferPoolAllocator = mSharedState->mBufferPoolAllocator;
return static_cast<PerRenderableData*>(bufferPoolAllocator.get(count * sizeof(PerRenderableData)));
}
// allocate space into the command stream directly
return driver.allocatePod<PerRenderableData>(count);
}();
PerRenderableData const* const uboData = renderableData.data<FScene::UBO>();
mat4f const* const worldTransformData = renderableData.data<FScene::WORLD_TRANSFORM>();
// prepare each InstanceBuffer.
FRenderableManager::InstancesInfo const* instancesData = renderableData.data<FScene::INSTANCES>();
for (uint32_t const i : visibleRenderables) {
auto& instancesInfo = instancesData[i];
if (UTILS_UNLIKELY(instancesInfo.buffer)) {
instancesInfo.buffer->prepare(driver, worldTransformData[i], uboData[i]);
}
}
// copy our data into the UBO for each visible renderable
for (uint32_t const i : visibleRenderables) {
buffer[i] = uboData[i];
}
// We capture state shared between Scene and the update buffer callback, because the Scene could
// be destroyed before the callback executes.
std::weak_ptr<SharedState>* const weakShared =
new (std::nothrow) std::weak_ptr(mSharedState);
// update the UBO
driver.resetBufferObject(renderableUbh);
driver.updateBufferObjectUnsynchronized(renderableUbh, {
buffer, count * sizeof(PerRenderableData),
+[](void* p, size_t const s, void* user) {
std::weak_ptr<SharedState> const* const weakShared =
static_cast<std::weak_ptr<SharedState>*>(user);
if (s >= MAX_STREAM_ALLOCATION_COUNT * sizeof(PerRenderableData)) {
if (auto state = weakShared->lock()) {
state->mBufferPoolAllocator.put(p);
}
}
delete weakShared;
}, weakShared
}, 0);
}
void FView::computeVisibilityMasks(
uint8_t const visibleLayers,
uint8_t const* UTILS_RESTRICT layers,
+13 -18
View File
@@ -20,7 +20,6 @@
#include "downcast.h"
#include "Allocators.h"
#include "BufferPoolAllocator.h"
#include "Culler.h"
#include "FrameHistory.h"
#include "FrameInfo.h"
@@ -30,6 +29,8 @@
#include "ds/ColorPassDescriptorSet.h"
#include "ds/DescriptorSet.h"
#include "ds/PostProcessDescriptorSet.h"
#include "ds/SsrPassDescriptorSet.h"
#include "ds/TypedUniformBuffer.h"
#include "components/LightManager.h"
@@ -40,8 +41,11 @@
#include "details/RenderTarget.h"
#include "details/Scene.h"
#include <private/filament/EngineEnums.h>
#include <private/filament/UibStructs.h>
#include <private/backend/DriverApi.h>
#include <filament/Frustum.h>
#include <filament/Renderer.h>
#include <filament/View.h>
@@ -50,6 +54,7 @@
#include <backend/Handle.h>
#include <utils/compiler.h>
#include <utils/Allocator.h>
#include <utils/Entity.h>
#include <utils/StructureOfArrays.h>
#include <utils/Range.h>
@@ -63,8 +68,8 @@ namespace filament::fgviewer {
}
#endif
#include <math/scalar.h>
#include <math/mat4.h>
#include <math/vec4.h>
#include <array>
#include <memory>
@@ -293,7 +298,7 @@ public:
return mGuardBandOptions;
}
void setColorGrading(FColorGrading const* colorGrading) noexcept {
void setColorGrading(FColorGrading* colorGrading) noexcept {
mColorGrading = colorGrading == nullptr ? mDefaultColorGrading : colorGrading;
}
@@ -436,8 +441,9 @@ public:
backend::TargetBufferFlags getRenderTargetAttachmentMask() const noexcept {
if (mRenderTarget == nullptr) {
return backend::TargetBufferFlags::NONE;
} else {
return mRenderTarget->getAttachmentMask();
}
return mRenderTarget->getAttachmentMask();
}
static void cullRenderables(utils::JobSystem& js, FScene::RenderableSoa& renderableData,
@@ -478,7 +484,7 @@ public:
return mFogEntity;
}
TypedUniformBuffer<PerViewUib>& getFrameUniforms() const noexcept {
TypedUniformBuffer<PerViewUib>& getFrameUniforms() noexcept {
return mUniforms;
}
@@ -502,7 +508,7 @@ private:
PickingQueryResultCallback const callback) noexcept {
return new(std::nothrow) FPickingQuery(x, y, handler, callback);
}
static void put(FPickingQuery const* pQuery) noexcept {
static void put(FPickingQuery* pQuery) noexcept {
delete pQuery;
}
mutable FPickingQuery* next = nullptr;
@@ -512,17 +518,12 @@ private:
backend::CallbackHandler* const handler;
PickingQueryResultCallback const callback;
// picking query result
PickingQueryResult result{};
PickingQueryResult result;
};
void prepareVisibleRenderables(utils::JobSystem& js,
Frustum const& frustum, FScene::RenderableSoa& renderableData) const noexcept;
void updateUBOs(backend::DriverApi& driver,
FScene::RenderableSoa& renderableData,
utils::Range<uint32_t> visibleRenderables,
backend::Handle<backend::HwBufferObject> renderableUbh) noexcept;
static void prepareVisibleLights(FLightManager const& lcm,
utils::Slice<float> scratch,
math::mat4f const& viewMatrix, Frustum const& frustum,
@@ -619,12 +620,6 @@ private:
mutable bool mHasShadowing = false;
mutable bool mNeedsShadowMap = false;
// State shared between Scene and driver callbacks.
struct SharedState {
BufferPoolAllocator<3> mBufferPoolAllocator = {};
};
std::shared_ptr<SharedState> mSharedState;
std::unique_ptr<ShadowMapManager> mShadowMapManager;
MaterialGlobals mMaterialGlobals = {{
@@ -63,7 +63,6 @@ void PerViewDescriptorSetUtils::prepareCamera(PerViewUib& s,
for (int i = 0; i < config.stereoscopicEyeCount; i++) {
mat4f const& eyeFromHead = camera.eyeFromView[i]; // identity for monoscopic rendering
mat4f const& clipFromEye = camera.eyeProjection[i];
s.eyeFromViewMatrix[i] = eyeFromHead;
// clipFromEye * eyeFromHead * headFromWorld
s.clipFromWorldMatrix[i] = highPrecisionMultiply(
clipFromEye, highPrecisionMultiply(eyeFromHead, headFromWorld));
+1 -1
View File
@@ -69,7 +69,7 @@ void RenderPassNode::execute(FrameGraphResources const& resources, DriverApi& dr
}
mPassBase->execute(resources, driver);
// destroy the render targets
for (auto& rt : mRenderTargetData) {
rt.destroy(resourceAllocator);
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.65.0"
spec.version = "1.64.0"
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.65.0/filament-v1.65.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.64.0/filament-v1.64.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
@@ -54,6 +54,7 @@ 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"),
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 65;
static constexpr size_t MATERIAL_VERSION = 64;
/**
* Supported shading models
@@ -30,7 +30,19 @@ struct MaterialConstant {
ConstantType type;
MaterialConstant() = default;
MaterialConstant(const char* name, ConstantType type) : name(name), type(type) {}
MaterialConstant(utils::CString name, ConstantType type) : name(std::move(name)), type(type) {}
};
struct MaterialMutableConstant {
utils::CString name;
// The Filament engine stores mutable spec constants as a bitfield, meaning that must override
// all of the default values built into in the shader souces. Therefore, unlike the immutable
// constants above, we have to store the default values in the material metadata.
bool defaultValue;
MaterialMutableConstant() = default;
MaterialMutableConstant(utils::CString name, bool defaultValue)
: name(std::move(name)), defaultValue(defaultValue) {}
};
}
@@ -83,8 +83,7 @@ enum class ReservedSpecializationConstants : uint8_t {
CONFIG_DEBUG_FROXEL_VISUALIZATION = 7,
CONFIG_STEREO_EYE_COUNT = 8, // don't change (hardcoded in ShaderCompilerService.cpp)
CONFIG_SH_BANDS_COUNT = 9,
CONFIG_SHADOW_SAMPLING_METHOD = 10,
CONFIG_FROXEL_RECORD_BUFFER_HEIGHT = 11,
CONFIG_SHADOW_SAMPLING_METHOD = 10
// check CONFIG_MAX_RESERVED_SPEC_CONSTANTS below
};
@@ -94,9 +93,7 @@ enum class PushConstantIds : uint8_t {
// This value is limited by UBO size, ES3.0 only guarantees 16 KiB.
// It's also limited by the Froxelizer's record buffer data type (uint8_t).
// And it's limited by the Froxelizer's Froxel data structure, which stores
// a light count in a uint8_t (so the count is limited to 255)
constexpr size_t CONFIG_MAX_LIGHT_COUNT = 255;
constexpr size_t CONFIG_MAX_LIGHT_COUNT = 256;
constexpr size_t CONFIG_MAX_LIGHT_INDEX = CONFIG_MAX_LIGHT_COUNT - 1;
// The number of specialization constants that Filament reserves for its own use. These are always
@@ -76,8 +76,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
math::mat4f worldFromViewMatrix; // clip view -> world : model matrix
math::mat4f clipFromViewMatrix; // clip <- view world : projection matrix
math::mat4f viewFromClipMatrix; // clip -> view world : inverse projection matrix
math::mat4f eyeFromViewMatrix[CONFIG_MAX_STEREOSCOPIC_EYES]; // clip eye <- view world
math::mat4f clipFromWorldMatrix[CONFIG_MAX_STEREOSCOPIC_EYES]; // clip <- eye <- view <- world
math::mat4f clipFromWorldMatrix[CONFIG_MAX_STEREOSCOPIC_EYES]; // clip <- view <- world
math::mat4f worldFromClipMatrix; // clip -> view -> world
math::mat4f userWorldFromWorldMatrix; // userWorld <- world
math::float4 clipTransform; // [sx, sy, tx, ty] only used by VERTEX_DOMAIN_DEVICE
@@ -209,7 +208,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init)
float es2Reserved2;
// bring PerViewUib to 2 KiB
math::float4 reserved[22];
math::float4 reserved[38];
};
// 2 KiB == 128 float4s
+1 -1
View File
@@ -98,7 +98,7 @@ static constexpr std::initializer_list<DescriptorSetLayoutBinding> perRenderable
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS, DescriptorFlags::UNFILTERABLE },
{ DescriptorType::SAMPLER_2D_ARRAY_INT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_TANGENTS },
{ DescriptorType::SAMPLER_2D_FLOAT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, DescriptorFlags::UNFILTERABLE },
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS },
};
struct PairSamplerTypeFormatHasher {
@@ -319,14 +319,15 @@ public:
MaterialBuilder& parameter(const char* name, size_t size, UniformType type,
ParameterPrecision precision = ParameterPrecision::DEFAULT);
//! Add a constant parameter to this material.
//! Add a (mutable) constant parameter to this material.
template<typename T>
using is_supported_constant_parameter_t = typename std::enable_if<
std::is_same<int32_t, T>::value ||
std::is_same<float, T>::value ||
std::is_same<bool, T>::value>::type;
template<typename T, typename = is_supported_constant_parameter_t<T>>
MaterialBuilder& constant(const char *name, ConstantType type, T defaultValue = 0);
MaterialBuilder& constant(const char* name, ConstantType type, bool isMutable,
T defaultValue = 0);
/**
* Add a sampler parameter to this material.
@@ -927,6 +928,7 @@ private:
PropertyList mProperties;
ParameterList mParameters;
ConstantList mConstants;
ConstantList mMutableConstants;
PushConstantList mPushConstants;
SubpassList mSubpasses;
VariableList mVariables;
+29 -12
View File
@@ -322,12 +322,19 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType sample
}
template<typename T, typename>
MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const type, T defaultValue) {
auto result = std::find_if(mConstants.begin(), mConstants.end(), [name](const Constant& c) {
return c.name == CString(name);
});
FILAMENT_CHECK_POSTCONDITION(result == mConstants.end())
MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const type,
bool isMutable, T defaultValue) {
ConstantList& constants = isMutable ? mMutableConstants : mConstants;
const ConstantList& otherConstants = isMutable ? mConstants : mMutableConstants;
auto result = std::find_if(constants.begin(), constants.end(),
[name](const Constant& c) { return c.name == CString(name); });
FILAMENT_CHECK_POSTCONDITION(result == constants.end())
<< "There is already a constant parameter present with the name " << name << ".";
auto otherResult = std::find_if(otherConstants.begin(), otherConstants.end(),
[name](const Constant& c) { return c.name == CString(name); });
FILAMENT_CHECK_POSTCONDITION(otherResult == otherConstants.end())
<< "There is already a constant parameter present with the name " << name << ".";
Constant constant {
.name = CString(name),
.type = type,
@@ -359,15 +366,20 @@ MaterialBuilder& MaterialBuilder::constant(const char* name, ConstantType const
assert_invariant(false);
}
mConstants.push_back(constant);
if (isMutable) {
FILAMENT_CHECK_POSTCONDITION(type == ConstantType::BOOL)
<< "Mutable spec constants must be bool type.";
}
constants.push_back(constant);
return *this;
}
template MaterialBuilder& MaterialBuilder::constant<int32_t>(
const char* name, ConstantType type, int32_t defaultValue);
const char* name, ConstantType type, bool isMutable, int32_t defaultValue);
template MaterialBuilder& MaterialBuilder::constant<float>(
const char* name, ConstantType type, float defaultValue);
const char* name, ConstantType type, bool isMutable, float defaultValue);
template MaterialBuilder& MaterialBuilder::constant<bool>(
const char* name, ConstantType type, bool defaultValue);
const char* name, ConstantType type, bool isMutable, bool defaultValue);
MaterialBuilder& MaterialBuilder::buffer(BufferInterfaceBlock bib) {
FILAMENT_CHECK_POSTCONDITION(mBuffers.size() < MAX_BUFFERS_COUNT) << "Too many buffers";
@@ -946,7 +958,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
BlobDictionary spirvDictionary;
// End: must be protected by lock
ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mConstants, mPushConstants,
ShaderGenerator sg(mProperties, mVariables, mOutputs, mDefines, mConstants, mMutableConstants, mPushConstants,
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
mMaterialDomain);
@@ -1548,7 +1560,7 @@ bool MaterialBuilder::needsStandardDepthProgram() const noexcept {
std::string MaterialBuilder::peek(backend::ShaderStage const stage,
const CodeGenParams& params, const PropertyList& properties) noexcept {
ShaderGenerator const sg(properties, mVariables, mOutputs, mDefines, mConstants, mPushConstants,
ShaderGenerator const sg(properties, mVariables, mOutputs, mDefines, mConstants, mMutableConstants, mPushConstants,
mMaterialFragmentCode.getResolved(), mMaterialFragmentCode.getLineOffset(),
mMaterialVertexCode.getResolved(), mMaterialVertexCode.getLineOffset(),
mMaterialDomain);
@@ -1676,9 +1688,14 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo&
// User constant parameters
FixedCapacityVector<MaterialConstant> constantsEntry(mConstants.size());
std::transform(mConstants.begin(), mConstants.end(), constantsEntry.begin(),
[](Constant const& c) { return MaterialConstant(c.name.c_str(), c.type); });
[](Constant const& c) { return MaterialConstant(c.name, c.type); });
container.push<MaterialConstantParametersChunk>(std::move(constantsEntry));
FixedCapacityVector<MaterialMutableConstant> mutableConstantsEntry(mMutableConstants.size());
std::transform(mMutableConstants.begin(), mMutableConstants.end(), mutableConstantsEntry.begin(),
[](Constant const& c) { return MaterialMutableConstant(c.name, c.defaultValue.b); });
container.push<MaterialMutableConstantParametersChunk>(std::move(mutableConstantsEntry));
FixedCapacityVector<MaterialPushConstant> pushConstantsEntry(mPushConstants.size());
std::transform(mPushConstants.begin(), mPushConstants.end(), pushConstantsEntry.begin(),
[](PushConstant const& c) {
@@ -118,6 +118,20 @@ void MaterialConstantParametersChunk::flatten(Flattener& f) {
// ------------------------------------------------------------------------------------------------
MaterialMutableConstantParametersChunk::MaterialMutableConstantParametersChunk(
FixedCapacityVector<MaterialMutableConstant> constants)
: Chunk(MaterialMutableConstants), mConstants(std::move(constants)) {}
void MaterialMutableConstantParametersChunk::flatten(Flattener& f) {
f.writeUint64(mConstants.size());
for (const auto& constant : mConstants) {
f.writeString(constant.name.c_str());
f.writeBool(constant.defaultValue);
}
}
// ------------------------------------------------------------------------------------------------
MaterialPushConstantParametersChunk::MaterialPushConstantParametersChunk(
CString const& structVarName, FixedCapacityVector<MaterialPushConstant> constants)
: Chunk(MaterialPushConstants),
@@ -18,6 +18,7 @@
#define TNT_FILAMAT_MAT_INTEFFACE_BLOCK_CHUNK_H
#include "Chunk.h"
#include "private/filament/ConstantInfo.h"
#include <backend/Program.h>
@@ -91,6 +92,20 @@ private:
// ------------------------------------------------------------------------------------------------
class MaterialMutableConstantParametersChunk final : public Chunk {
public:
explicit MaterialMutableConstantParametersChunk(
FixedCapacityVector<filament::MaterialMutableConstant> constants);
~MaterialMutableConstantParametersChunk() override = default;
private:
void flatten(Flattener&) override;
FixedCapacityVector<filament::MaterialMutableConstant> mConstants;
};
// ------------------------------------------------------------------------------------------------
class MaterialPushConstantParametersChunk final : public Chunk {
public:
explicit MaterialPushConstantParametersChunk(CString const& structVarName,
+27 -49
View File
@@ -173,7 +173,9 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
case TargetApi::METAL:
out << "#define TARGET_METAL_ENVIRONMENT\n";
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
//For now, no differences so inherit the same changes.
out << "#define TARGET_WEBGPU_ENVIRONMENT\n";
break;
case TargetApi::ALL:
@@ -244,13 +246,16 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
}
if (stage == ShaderStage::VERTEX) {
generateDefine(out, "FLIP_UV_ATTRIBUTE", material.flipUV);
generateDefine(out, "LEGACY_MORPHING", material.useLegacyMorphing);
CodeGenerator::generateDefine(out, "FLIP_UV_ATTRIBUTE", material.flipUV);
CodeGenerator::generateDefine(out, "LEGACY_MORPHING", material.useLegacyMorphing);
}
if (stage == ShaderStage::FRAGMENT) {
generateDefine(out, "FILAMENT_LINEAR_FOG", material.linearFog);
generateDefine(out, "FILAMENT_SHADOW_FAR_ATTENUATION", material.shadowFarAttenuation);
generateDefine(out, "MATERIAL_HAS_CUSTOM_DEPTH", material.userMaterialHasCustomDepth);
CodeGenerator::generateDefine(out, "FILAMENT_LINEAR_FOG",
material.linearFog);
CodeGenerator:generateDefine(out, "FILAMENT_SHADOW_FAR_ATTENUATION",
material.shadowFarAttenuation);
CodeGenerator::generateDefine(out, "MATERIAL_HAS_CUSTOM_DEPTH",
material.userMaterialHasCustomDepth);
}
if (mTargetLanguage == TargetLanguage::SPIRV ||
@@ -276,7 +281,7 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
}
};
generateDefine(out, getShadingDefine(material.shading), true);
CodeGenerator::generateDefine(out, getShadingDefine(material.shading), true);
generateQualityDefine(out, material.quality);
@@ -308,7 +313,6 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
// More information at https://github.com/gpuweb/gpuweb/issues/572#issuecomment-649760005
out << "const int CONFIG_MAX_INSTANCES = " << (int)CONFIG_MAX_INSTANCES << ";\n";
out << "const int CONFIG_FROXEL_BUFFER_HEIGHT = 2048;\n";
out << "const int CONFIG_FROXEL_RECORD_BUFFER_HEIGHT = 16384;\n";
} else {
generateSpecializationConstant(out, "CONFIG_MAX_INSTANCES",
+ReservedSpecializationConstants::CONFIG_MAX_INSTANCES, (int)CONFIG_MAX_INSTANCES);
@@ -316,9 +320,6 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
// the default of 1024 (16KiB) is needed for 32% of Android devices
generateSpecializationConstant(out, "CONFIG_FROXEL_BUFFER_HEIGHT",
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT, 1024);
generateSpecializationConstant(out, "CONFIG_FROXEL_RECORD_BUFFER_HEIGHT",
+ReservedSpecializationConstants::CONFIG_FROXEL_RECORD_BUFFER_HEIGHT, 16384);
}
// directional shadowmap visualization
@@ -369,20 +370,20 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
// This is the minimum required value according to the OpenGL ES Shading Language Version
// 1.00 document. glslang forbids defining symbols beginning with gl_ as const, hence the
// #define.
generateDefine(out, "gl_MaxVaryingVectors", "8");
CodeGenerator::generateDefine(out, "gl_MaxVaryingVectors", "8");
generateDefine(out, "texture2D", "texture");
generateDefine(out, "texture2DProj", "textureProj");
generateDefine(out, "texture3D", "texture");
generateDefine(out, "texture3DProj", "textureProj");
generateDefine(out, "textureCube", "texture");
CodeGenerator::generateDefine(out, "texture2D", "texture");
CodeGenerator::generateDefine(out, "texture2DProj", "textureProj");
CodeGenerator::generateDefine(out, "texture3D", "texture");
CodeGenerator::generateDefine(out, "texture3DProj", "textureProj");
CodeGenerator::generateDefine(out, "textureCube", "texture");
if (stage == ShaderStage::VERTEX) {
generateDefine(out, "texture2DLod", "textureLod");
generateDefine(out, "texture2DProjLod", "textureProjLod");
generateDefine(out, "texture3DLod", "textureLod");
generateDefine(out, "texture3DProjLod", "textureProjLod");
generateDefine(out, "textureCubeLod", "textureLod");
CodeGenerator::generateDefine(out, "texture2DLod", "textureLod");
CodeGenerator::generateDefine(out, "texture2DProjLod", "textureProjLod");
CodeGenerator::generateDefine(out, "texture3DLod", "textureLod");
CodeGenerator::generateDefine(out, "texture3DProjLod", "textureProjLod");
CodeGenerator::generateDefine(out, "textureCubeLod", "textureLod");
}
}
@@ -731,6 +732,7 @@ io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, Shade
// in the GLSL 4.5 / ESSL 3.1 case, the set is not used and binding is unique
out << "binding = " << +binding << ", ";
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
out << "set = " << +set << ", binding = " << +binding << ", ";
break;
@@ -820,6 +822,7 @@ io::sstream& CodeGenerator::generateCommonSamplers(utils::io::sstream& out,
// GLSL 4.5 / ESSL 3.1 require the 'binding' layout qualifier
out << "layout(binding = " << getUniqueSamplerBindingPoint() << ") ";
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
out << "layout(binding = " << +info.binding << ", set = " << +set << ") ";
break;
@@ -939,7 +942,7 @@ utils::io::sstream& CodeGenerator::generateSpecializationConstant(utils::io::sst
static const char* types[] = { "int", "float", "bool" };
// Spec constants aren't fully supported in Tint,
// workaround until https://issues.chromium.org/issues/42250586 is resolved
// workaround until https://issues.chromium.org/issues/42250586 is resolved
if (mTargetApi == TargetApi::WEBGPU) {
std::string const variableName = "FILAMENT_SPEC_CONST_" + std::to_string(id) + "_" + name;
out << " const " << types[value.index()] << " " << variableName << " = " << constantString << ";\n";
@@ -961,11 +964,10 @@ utils::io::sstream& CodeGenerator::generateSpecializationConstant(utils::io::sst
utils::io::sstream& CodeGenerator::generatePushConstants(utils::io::sstream& out,
MaterialBuilder::PushConstantList const& pushConstants, size_t const layoutLocation) const {
if (UTILS_UNLIKELY(pushConstants.empty())) {
return out;
}
static constexpr char const* STRUCT_NAME = "Constants";
bool const outputSpirv =
mTargetLanguage == TargetLanguage::SPIRV && mTargetApi != TargetApi::OPENGL;
auto const getType = [](ConstantType const& type) {
switch (type) {
case ConstantType::BOOL:
@@ -976,30 +978,6 @@ utils::io::sstream& CodeGenerator::generatePushConstants(utils::io::sstream& out
return "float";
}
};
// This is a workaround for WebGPU not supporting push constants for skinning.
// We replace the push constant with a regular constant struct initialized to 0.
if (mTargetApi == TargetApi::WEBGPU) {
assert_invariant(
pushConstants.size() == 1 &&
"The current workaround for WebGPU push constants assumes for now that only 1");
assert_invariant(pushConstants[0].name == CString("morphingBufferOffset") &&
"The current workaround for WebGPU push constants assumes only the "
"morphingBufferOffset constant is present.");
assert_invariant(pushConstants[0].type == ConstantType::INT &&
"The current workaround for WebGPU push constants assumes "
"morphingBufferOffset is an integer type.");
out << "struct " << STRUCT_NAME << " {\n";
for (auto const& constant: pushConstants) {
out << " " << getType(constant.type) << " " << constant.name.c_str() << ";\n";
}
out << "};\n";
out << "const " << STRUCT_NAME << " " << PUSH_CONSTANT_STRUCT_VAR_NAME << " = "
<< STRUCT_NAME << "(0);\n";
return out;
}
bool const outputSpirv =
mTargetLanguage == TargetLanguage::SPIRV && mTargetApi != TargetApi::OPENGL;
if (outputSpirv) {
out << "layout(push_constant) uniform " << STRUCT_NAME << " {\n ";
} else {
+17 -8
View File
@@ -285,10 +285,17 @@ void ShaderGenerator::appendShader(io::sstream& ss,
}
}
void ShaderGenerator::generateUserSpecConstants(
const CodeGenerator& cg, io::sstream& fs, MaterialBuilder::ConstantList const& constants) {
void ShaderGenerator::generateAllUserSpecConstants(const CodeGenerator& cg, io::sstream& fs,
MaterialBuilder::ConstantList const& constants,
MaterialBuilder::ConstantList const& mutableConstants) {
generateUserSpecConstants(cg, fs, constants, 0);
generateUserSpecConstants(cg, fs, mutableConstants, constants.size());
}
void ShaderGenerator::generateUserSpecConstants(const CodeGenerator& cg, io::sstream& fs,
MaterialBuilder::ConstantList const& constants, size_t offset) {
// Constants 0 to CONFIG_MAX_RESERVED_SPEC_CONSTANTS - 1 are reserved by Filament.
size_t index = CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
size_t index = CONFIG_MAX_RESERVED_SPEC_CONSTANTS + offset;
for (const auto& constant : constants) {
std::string const fullName = std::string("materialConstants_") + constant.name.c_str();
switch (constant.type) {
@@ -316,6 +323,7 @@ ShaderGenerator::ShaderGenerator(
MaterialBuilder::OutputList const& outputs,
MaterialBuilder::PreprocessorDefineList const& defines,
MaterialBuilder::ConstantList const& constants,
MaterialBuilder::ConstantList const& mutableConstants,
MaterialBuilder::PushConstantList const& pushConstants,
CString const& materialCode, size_t const lineOffset,
CString const& materialVertexCode, size_t const vertexLineOffset,
@@ -338,6 +346,7 @@ ShaderGenerator::ShaderGenerator(
mMaterialDomain = materialDomain;
mDefines = defines;
mConstants = constants;
mMutableConstants = mutableConstants;
mPushConstants = pushConstants;
if (mMaterialFragmentCode.empty()) {
@@ -394,7 +403,7 @@ std::string ShaderGenerator::createSurfaceVertexProgram(ShaderModel const shader
cg.generateCommonProlog(vs, ShaderStage::VERTEX, material, variant);
generateUserSpecConstants(cg, vs, mConstants);
generateAllUserSpecConstants(cg, vs, mConstants, mMutableConstants);
// note: even if the user vertex shader is empty, we can't use the "optimized" version if
// we're in masked mode because fragment shader needs the color varyings
@@ -527,7 +536,7 @@ std::string ShaderGenerator::createSurfaceFragmentProgram(ShaderModel const shad
io::sstream fs;
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, variant);
generateUserSpecConstants(cg, fs, mConstants);
generateAllUserSpecConstants(cg, fs, mConstants, mMutableConstants);
generateSurfaceMaterialVariantDefines(
fs, ShaderStage::FRAGMENT, featureLevel, material, variant);
@@ -683,7 +692,7 @@ std::string ShaderGenerator::createSurfaceComputeProgram(ShaderModel const shade
cg.generateCommonProlog(s, ShaderStage::COMPUTE, material, {});
generateUserSpecConstants(cg, s, mConstants);
generateAllUserSpecConstants(cg, s, mConstants, mMutableConstants);
CodeGenerator::generateSurfaceTypes(s, ShaderStage::COMPUTE);
@@ -723,7 +732,7 @@ std::string ShaderGenerator::createPostProcessVertexProgram(ShaderModel const sm
io::sstream vs;
cg.generateCommonProlog(vs, ShaderStage::VERTEX, material, {});
generateUserSpecConstants(cg, vs, mConstants);
generateAllUserSpecConstants(cg, vs, mConstants, mMutableConstants);
CodeGenerator::generateDefine(vs, "LOCATION_POSITION", uint32_t(POSITION));
@@ -767,7 +776,7 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel const
io::sstream fs;
cg.generateCommonProlog(fs, ShaderStage::FRAGMENT, material, {});
generateUserSpecConstants(cg, fs, mConstants);
generateAllUserSpecConstants(cg, fs, mConstants, mMutableConstants);
generatePostProcessMaterialVariantDefines(fs, PostProcessVariant(variant));
+8 -1
View File
@@ -51,6 +51,7 @@ public:
MaterialBuilder::OutputList const& outputs,
MaterialBuilder::PreprocessorDefineList const& defines,
MaterialBuilder::ConstantList const& constants,
MaterialBuilder::ConstantList const& mutableConstants,
MaterialBuilder::PushConstantList const& pushConstants,
utils::CString const& materialCode,
size_t lineOffset,
@@ -104,9 +105,14 @@ private:
static void generatePostProcessMaterialVariantDefines(utils::io::sstream& out,
filament::PostProcessVariant variant) noexcept;
static void generateAllUserSpecConstants(const CodeGenerator& cg, utils::io::sstream& fs,
MaterialBuilder::ConstantList const& constants,
MaterialBuilder::ConstantList const& mutableConstants);
static void generateUserSpecConstants(
const CodeGenerator& cg, utils::io::sstream& fs,
MaterialBuilder::ConstantList const& constants);
MaterialBuilder::ConstantList const& constants,
size_t offset);
std::string createPostProcessVertexProgram(filament::backend::ShaderModel sm,
MaterialBuilder::TargetApi targetApi, MaterialBuilder::TargetLanguage targetLanguage,
@@ -135,6 +141,7 @@ private:
MaterialBuilder::MaterialDomain mMaterialDomain;
MaterialBuilder::PreprocessorDefineList mDefines;
MaterialBuilder::ConstantList mConstants;
MaterialBuilder::ConstantList mMutableConstants;
MaterialBuilder::PushConstantList mPushConstants;
utils::CString mMaterialFragmentCode; // fragment or compute code
utils::CString mMaterialVertexCode;
+1 -1
View File
@@ -102,7 +102,7 @@ SamplerInterfaceBlock const& SibGenerator::getPerRenderableSib(Variant) noexcept
.stageFlags(backend::ShaderStageFlags::VERTEX)
.add({ {"positions", +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS, Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH, FILTERABLE, !MULTISAMPLE, ALL_STAGES },
{"tangents", +PerRenderableBindingPoints::MORPH_TARGET_TANGENTS, Type::SAMPLER_2D_ARRAY, Format::INT, Precision::HIGH, !FILTERABLE, !MULTISAMPLE, ALL_STAGES },
{"indicesAndWeights", +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH, !FILTERABLE, !MULTISAMPLE, ALL_STAGES }}
{"indicesAndWeights", +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS, Type::SAMPLER_2D, Format::FLOAT, Precision::HIGH, FILTERABLE, !MULTISAMPLE, ALL_STAGES }}
)
.build();
+1 -4
View File
@@ -99,8 +99,6 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept {
{ "worldFromViewMatrix", 0, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
{ "clipFromViewMatrix", 0, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
{ "viewFromClipMatrix", 0, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
{ "eyeFromViewMatrix", CONFIG_MAX_STEREOSCOPIC_EYES,
Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
{ "clipFromWorldMatrix", CONFIG_MAX_STEREOSCOPIC_EYES,
Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
{ "worldFromClipMatrix", 0, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 },
@@ -279,8 +277,7 @@ BufferInterfaceBlock const& UibGenerator::getPerRenderableMorphingUib() noexcept
BufferInterfaceBlock const& UibGenerator::getFroxelRecordUib() noexcept {
static BufferInterfaceBlock const uib = BufferInterfaceBlock::Builder()
.name(FroxelRecordUib::_name)
.add({{ "records", 1024, BufferInterfaceBlock::Type::UINT4, Precision::HIGH, {},
{}, {}, "CONFIG_FROXEL_RECORD_BUFFER_HEIGHT"}})
.add({{ "records", 1024, BufferInterfaceBlock::Type::UINT4, Precision::HIGH }})
.build();
return uib;
}
+7 -7
View File
@@ -888,10 +888,10 @@ TEST_F(MaterialCompiler, ConstantParameter) {
}
)");
filamat::MaterialBuilder builder;
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
builder.constant("myIntConstant", ConstantType::INT, 123);
builder.constant("myBoolConstant", ConstantType::BOOL, true);
builder.constant<bool>("myOtherBoolConstant", ConstantType::BOOL);
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
builder.constant("myIntConstant", ConstantType::INT, /*isMutable=*/false, 123);
builder.constant("myBoolConstant", ConstantType::BOOL, /*isMutable=*/false, true);
builder.constant<bool>("myOtherBoolConstant", ConstantType::BOOL, /*isMutable=*/false);
builder.shading(filament::Shading::LIT);
builder.material(shaderCode.c_str());
@@ -904,8 +904,8 @@ TEST_F(MaterialCompiler, ConstantParameterSameName) {
#ifdef __EXCEPTIONS
EXPECT_THROW({
filamat::MaterialBuilder builder;
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
builder.constant("myFloatConstant", ConstantType::FLOAT, 1.0f);
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 1.0f);
}, utils::PostconditionPanic);
#endif
}
@@ -914,7 +914,7 @@ TEST_F(MaterialCompiler, ConstantParameterWrongType) {
#ifdef __EXCEPTIONS
EXPECT_THROW({
filamat::MaterialBuilder builder;
builder.constant("myFloatConstant", ConstantType::FLOAT, 10);
builder.constant("myFloatConstant", ConstantType::FLOAT, /*isMutable=*/false, 10);
}, utils::PostconditionPanic);
#endif
}
+16 -3
View File
@@ -352,6 +352,19 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
return false;
}
const JsonishValue* mutableValue = jsonObject.getValue("mutable");
bool isMutable;
if (mutableValue) {
const JsonishBool* mutableBool = mutableValue->toJsonBool();
if (!mutableBool) {
std::cerr << "constants: mutable value must be BOOL." << std::endl;
return false;
}
isMutable = mutableBool->getBool();
} else {
isMutable = false;
}
auto typeString = typeValue->toJsonString()->getString();
auto nameString = nameValue->toJsonString()->getString();
const JsonishValue* defaultValue = jsonObject.getValue("default");
@@ -370,7 +383,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
// FIXME: Jsonish doesn't distinguish between integers and floats.
intDefault = (int32_t)defaultValue->toJsonNumber()->getFloat();
}
builder.constant(nameString.c_str(), type, intDefault);
builder.constant(nameString.c_str(), type, isMutable, intDefault);
break;
}
case ConstantType::FLOAT: {
@@ -383,7 +396,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
}
floatDefault = defaultValue->toJsonNumber()->getFloat();
}
builder.constant(nameString.c_str(), type, floatDefault);
builder.constant(nameString.c_str(), type, isMutable, floatDefault);
break;
}
case ConstantType::BOOL:
@@ -396,7 +409,7 @@ static bool processConstant(MaterialBuilder& builder, const JsonishObject& jsonO
}
boolDefault = defaultValue->toJsonBool()->getBool();
}
builder.constant(nameString.c_str(), type, boolDefault);
builder.constant(nameString.c_str(), type, isMutable, boolDefault);
break;
}
} else {
+1 -1
View File
@@ -841,7 +841,7 @@ class MaterialSidePanel extends LitElement {
const shaderModel = (isMatinfo ? shaderModelSymbol[variant.shader.shaderModel] : '');
return html`
<div class="${divClass}" @click="${onClickVariant}">
<div>${stage}&nbsp; ${vstring} ${shaderModel} </div>
<div>${stage}&nbsp ${vstring} ${shaderModel} </div>
</div>
${languagesDiv ?? nothing}
`
+4
View File
@@ -19,6 +19,7 @@ set(RESOURCE_BINS)
set(MATERIAL_SRCS
materials/aiDefaultMat.mat
materials/bakedColor.mat
materials/bakedColorWithMutableConstants.mat
materials/bakedTexture.mat
materials/aoPreview.mat
materials/arrayTexture.mat
@@ -281,6 +282,7 @@ if (NOT ANDROID)
add_demo(lightbulb)
add_demo(material_sandbox)
add_demo(multiple_windows)
add_demo(mutable_constants)
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
# https://github.com/gpuweb/gpuweb/issues/1190
if (NOT FILAMENT_SUPPORTS_WEBGPU)
@@ -291,6 +293,7 @@ if (NOT ANDROID)
add_demo(sample_full_pbr)
add_demo(sample_normal_map)
add_demo(shadowtest)
add_demo(simple_gltf_test)
add_demo(skinningtest)
add_demo(strobecolor)
add_demo(suzanne)
@@ -309,6 +312,7 @@ if (NOT ANDROID)
target_link_libraries(rendertarget PRIVATE filameshio suzanne-resources)
target_link_libraries(sample_cloth PRIVATE filameshio)
target_link_libraries(sample_normal_map PRIVATE filameshio)
target_link_libraries(simple_gltf_test PRIVATE gltf-demo-resources uberarchive gltfio viewer)
target_link_libraries(suzanne PRIVATE filameshio suzanne-resources)
if (FILAMENT_DISABLE_MATOPT)
-1
View File
@@ -174,7 +174,6 @@ int main(int argc, char** argv) {
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR)
.advancedSkinning(true)
.build(*engine);
app.vb2->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
@@ -0,0 +1,44 @@
material {
name : BakedColorWithMutableConstants,
requires : [
color
],
constants : [
{
name: swizzle,
type: bool,
mutable: true,
default: true,
},
{
name: darken,
type: bool,
mutable: true,
},
{
// In generated shader sources, we first specify immutable
// specialization constants, then mutable specialization constants.
// By including this dummy constant, we offset the ID of the first
// mutable constant by one. This is done here purely for testing
// purposes.
name: dummy,
type: bool,
}
],
shadingModel : unlit,
culling : none,
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
if (materialConstants_swizzle) {
material.baseColor = getColor().gbra;
} else {
material.baseColor = getColor();
}
if (materialConstants_darken) {
material.baseColor.rgb *= 0.5;
}
}
}
+214
View File
@@ -0,0 +1,214 @@
/*
* 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 <filament/Camera.h>
#include <filament/Engine.h>
#include <filament/IndexBuffer.h>
#include <filament/Material.h>
#include <filament/MaterialInstance.h>
#include <filament/RenderableManager.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
#include <filament/VertexBuffer.h>
#include <filament/View.h>
#include <imgui.h>
#include <utils/EntityManager.h>
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
#include <getopt/getopt.h>
#include <cmath>
#include <iostream>
#include "generated/resources/resources.h"
using namespace filament;
using utils::Entity;
using utils::EntityManager;
struct App {
Config config;
VertexBuffer* vb;
IndexBuffer* ib;
Material* mat;
Camera* cam;
Entity camera;
Skybox* skybox;
Entity renderable;
};
struct Vertex {
filament::math::float2 position;
uint32_t color;
};
static const Vertex TRIANGLE_VERTICES[3] = {
{{1, 0}, 0xffff0000u},
{{cos(M_PI * 2 / 3), sin(M_PI * 2 / 3)}, 0xff00ff00u},
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu},
};
static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
static void printUsage(char* name) {
std::string exec_name(utils::Path(name).getName());
std::string usage(
"MUTABLE_CONSTANTS renders a spinning colored triangle\n"
"Usage:\n"
" MUTABLE_CONSTANTS [options]\n"
"Options:\n"
" --help, -h\n"
" Prints this message\n\n"
" --api, -a\n"
" Specify the backend API: opengl, vulkan, metal, or webgpu\n"
" (note: webgpu is a no-op for now, printing backend\n"
" component info if FILAMENT_BACKEND_DEBUG_FLAG, \n"
" set at build time, includes the \n"
" FWGPU_PRINT_SYSTEM bit flag 0x2)\n"
);
const std::string from("MUTABLE_CONSTANTS");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
usage.replace(pos, from.length(), exec_name);
}
std::cout << usage;
}
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
static constexpr const char* OPTSTR = "ha:";
static const struct option OPTIONS[] = {
{ "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
{ nullptr, 0, nullptr, 0 }
};
int opt;
int option_index = 0;
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
std::string arg(optarg ? optarg : "");
switch (opt) {
default:
case 'h':
printUsage(argv[0]);
exit(0);
case 'a':
if (arg == "opengl") {
app->config.backend = Engine::Backend::OPENGL;
} else if (arg == "vulkan") {
app->config.backend = Engine::Backend::VULKAN;
} else if (arg == "metal") {
app->config.backend = Engine::Backend::METAL;
} else if (arg == "webgpu") {
app->config.backend = Engine::Backend::WEBGPU;
} else {
std::cerr << "Unrecognized backend. Must be "
"'opengl'|'vulkan'|'metal'|'webgpu'.\n";
exit(1);
}
break;
}
}
return optind;
}
int main(int argc, char** argv) {
App app{};
app.config.title = "mutable_constants";
handleCommandLineArguments(argc, argv, &app);
auto setup = [&app](Engine* engine, View* view, Scene* scene) {
app.skybox = Skybox::Builder().color({0.1, 0.125, 0.25, 1.0}).build(*engine);
scene->setSkybox(app.skybox);
view->setPostProcessingEnabled(false);
static_assert(sizeof(Vertex) == 12, "Strange vertex size.");
app.vb = VertexBuffer::Builder()
.vertexCount(3)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12)
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR)
.build(*engine);
app.vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
app.ib = IndexBuffer::Builder()
.indexCount(3)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*engine);
app.ib->setBuffer(*engine,
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
app.mat = Material::Builder().package(RESOURCES_BAKEDCOLORWITHMUTABLECONSTANTS_DATA,
RESOURCES_BAKEDCOLORWITHMUTABLECONSTANTS_SIZE)
.build(*engine);
app.renderable = EntityManager::get().create();
RenderableManager::Builder(1)
.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
.material(0, app.mat->getDefaultInstance())
.geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.vb, app.ib, 0, 3)
.culling(false)
.receiveShadows(false)
.castShadows(false)
.build(*engine, app.renderable);
scene->addEntity(app.renderable);
app.camera = utils::EntityManager::get().create();
app.cam = engine->createCamera(app.camera);
view->setCamera(app.cam);
};
auto cleanup = [&app](Engine* engine, View*, Scene*) {
engine->destroy(app.skybox);
engine->destroy(app.renderable);
engine->destroy(app.mat);
engine->destroy(app.vb);
engine->destroy(app.ib);
engine->destroyCameraComponent(app.camera);
utils::EntityManager::get().destroy(app.camera);
};
auto gui = [&app](Engine* engine, View* view) {
MaterialInstance* mi = app.mat->getDefaultInstance();
bool swizzle = mi->getConstant<bool>("swizzle");
bool darken = mi->getConstant<bool>("darken");
ImGui::Checkbox("Swizzle", &swizzle);
ImGui::Checkbox("Darken", &darken);
if (swizzle != mi->getConstant<bool>("swizzle")) {
mi->setConstant("swizzle", swizzle);
}
if (darken != mi->getConstant<bool>("darken")) {
mi->setConstant("darken", darken);
}
};
FilamentApp::get().animate([&app](Engine* engine, View* view, double now) {
constexpr float ZOOM = 1.5f;
const uint32_t w = view->getViewport().width;
const uint32_t h = view->getViewport().height;
const float aspect = (float) w / h;
app.cam->setProjection(Camera::Projection::ORTHO,
-aspect * ZOOM, aspect * ZOOM,
-ZOOM, ZOOM, 0, 1);
auto& tcm = engine->getTransformManager();
tcm.setTransform(tcm.getInstance(app.renderable),
filament::math::mat4f::rotation(now, filament::math::float3{ 0, 0, 1 }));
});
FilamentApp::get().run(app.config, setup, cleanup, gui);
return 0;
}
+278
View File
@@ -0,0 +1,278 @@
/*
* Copyright (C) 2020 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 "common/arguments.h"
#include "common/configuration.h"
#include <filamentapp/Config.h>
#include <filamentapp/FilamentApp.h>
#include <filamentapp/IBL.h>
#include <filament/Engine.h>
#include <filament/Scene.h>
#include <filament/Skybox.h>
#include <filament/TransformManager.h>
#include <filament/View.h>
#include <gltfio/AssetLoader.h>
#include <gltfio/FilamentAsset.h>
#include <gltfio/ResourceLoader.h>
#include <gltfio/TextureProvider.h>
#include <viewer/ViewerGui.h>
#include <camutils/Manipulator.h>
#include <getopt/getopt.h>
#include <utils/NameComponentManager.h>
#include <fstream>
#include <iostream>
#include <string>
#include <math/mat4.h>
#include "generated/resources/gltf_demo.h"
#include "materials/uberarchive.h"
using namespace filament;
using namespace filament::math;
using namespace filament::viewer;
using namespace filament::gltfio;
using namespace utils;
enum MaterialSource {
JITSHADER,
UBERSHADER,
};
struct App {
Engine* engine;
ViewerGui* viewer;
Config config;
AssetLoader* loader;
FilamentAsset* asset = nullptr;
NameComponentManager* names;
MaterialProvider* materials;
MaterialSource materialSource = UBERSHADER;
ResourceLoader* resourceLoader = nullptr;
gltfio::TextureProvider* stbDecoder = nullptr;
gltfio::TextureProvider* ktxDecoder = nullptr;
FilamentInstance* instance;
bool enableMSAA = false;
};
static const char* DEFAULT_IBL = "assets/ibl/lightroom_14b";
static void printUsage(char* name) {
std::string exec_name(Path(name).getName());
std::string usage("SHOWCASE renders the specified glTF file with minimal features.\nPrimarily "
"for early backend bringup.\n"
"Usage:\n"
" SHOWCASE [options] <gltf path>\n"
"Options:\n"
" --help, -h\n"
" Prints this message\n\n"
"API_USAGE"
" --ibl=<path to cmgen IBL>, -i <path>\n"
" Override the built-in IBL\n\n"
" --msaa, -m\n"
" setMultiSampleAntiAliasingOptions\n\n");
const std::string from("SHOWCASE");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
usage.replace(pos, from.length(), exec_name);
}
const std::string apiUsage("API_USAGE");
for (size_t pos = usage.find(apiUsage); pos != std::string::npos;
pos = usage.find(apiUsage, pos)) {
usage.replace(pos, apiUsage.length(), samples::getBackendAPIArgumentsUsage());
}
std::cout << usage;
}
static int handleCommandLineArguments(int argc, char* argv[], App* app) {
static constexpr const char* OPTSTR = "hma:i:";
static const struct option OPTIONS[] = { { "help", no_argument, nullptr, 'h' },
{ "api", required_argument, nullptr, 'a' },
{ "ibl", required_argument, nullptr, 'i' },
{ "msaa", no_argument, nullptr, 'm' },
{ nullptr, 0, nullptr, 0 } };
int opt;
int option_index = 0;
// Make WebGPU the default for this test
app->config.backend = Engine::Backend::WEBGPU;
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) {
std::string arg(optarg ? optarg : "");
switch (opt) {
default:
case 'h':
printUsage(argv[0]);
exit(0);
case 'a':
app->config.backend = samples::parseArgumentsForBackend(arg);
break;
case 'i':
app->config.iblDirectory = arg;
break;
case 'm':
app->enableMSAA = true;
break;
}
}
return optind;
}
static std::ifstream::pos_type getFileSize(const char* filename) {
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
return in.tellg();
}
int main(int argc, char** argv) {
App app;
app.config.title = "simple glTF";
app.config.iblDirectory = FilamentApp::getRootAssetsPath() + DEFAULT_IBL;
int optionIndex = handleCommandLineArguments(argc, argv, &app);
utils::Path filename;
int num_args = argc - optionIndex;
if (num_args >= 1) {
filename = argv[optionIndex];
if (!filename.exists()) {
std::cerr << "file " << filename << " not found!" << std::endl;
return 1;
}
}
auto loadAsset = [&app](utils::Path filename) {
// Peek at the file size to allow pre-allocation.
long contentSize = static_cast<long>(getFileSize(filename.c_str()));
if (contentSize <= 0) {
std::cerr << "Unable to open " << filename << std::endl;
exit(1);
}
// Consume the glTF file.
std::ifstream in(filename.c_str(), std::ifstream::binary | std::ifstream::in);
std::vector<uint8_t> buffer(static_cast<unsigned long>(contentSize));
if (!in.read((char*) buffer.data(), contentSize)) {
std::cerr << "Unable to read " << filename << std::endl;
exit(1);
}
// Parse the glTF file and create Filament entities.
app.asset = app.loader->createInstancedAsset(buffer.data(), buffer.size(),
&(app.instance), 1);
buffer.clear();
buffer.shrink_to_fit();
if (!app.asset) {
std::cerr << "Unable to parse " << filename << std::endl;
exit(1);
}
};
auto loadResources = [&app](utils::Path filename) {
// Load external textures and buffers.
std::string gltfPath = filename.getAbsolutePath();
ResourceConfiguration configuration;
configuration.engine = app.engine;
configuration.gltfPath = gltfPath.c_str();
configuration.normalizeSkinningWeights = true;
if (!app.resourceLoader) {
app.resourceLoader = new gltfio::ResourceLoader(configuration);
app.stbDecoder = createStbProvider(app.engine);
app.ktxDecoder = createKtx2Provider(app.engine);
app.resourceLoader->addTextureProvider("image/png", app.stbDecoder);
app.resourceLoader->addTextureProvider("image/jpeg", app.stbDecoder);
app.resourceLoader->addTextureProvider("image/ktx2", app.ktxDecoder);
}
if (!app.resourceLoader->asyncBeginLoad(app.asset)) {
std::cerr << "Unable to start loading resources for " << filename << std::endl;
exit(1);
}
auto ibl = FilamentApp::get().getIBL();
if (ibl) {
app.viewer->setIndirectLight(ibl->getIndirectLight(), ibl->getSphericalHarmonics());
}
};
auto setup = [&](Engine* engine, View* view, Scene* scene) {
if (app.enableMSAA) {
// TODO Investigate why
// Enabling MSAA fixes the "transmission" sample but breaks the others
view->setMultiSampleAntiAliasingOptions({ .enabled = true});
}
app.engine = engine;
app.names = new NameComponentManager(EntityManager::get());
app.viewer = new ViewerGui(engine, scene, view);
app.materials = (app.materialSource == JITSHADER)
? createJitShaderProvider(engine, false /* optimize */,
samples::getJitMaterialVariantFilter(app.config.backend))
: createUbershaderProvider(engine, UBERARCHIVE_DEFAULT_DATA,
UBERARCHIVE_DEFAULT_SIZE);
app.loader = AssetLoader::create({ engine, app.materials, app.names });
if (filename.isEmpty()) {
app.asset = app.loader->createInstancedAsset(GLTF_DEMO_DAMAGEDHELMET_DATA,
GLTF_DEMO_DAMAGEDHELMET_SIZE, &(app.instance), 1);
} else {
loadAsset(filename);
}
loadResources(filename);
app.viewer->setAsset(app.asset, app.instance);
};
auto cleanup = [&app](Engine* engine, View*, Scene*) {
app.loader->destroyAsset(app.asset);
app.materials->destroyMaterials();
delete app.viewer;
delete app.materials;
delete app.names;
delete app.resourceLoader;
delete app.stbDecoder;
delete app.ktxDecoder;
AssetLoader::destroy(&app.loader);
};
auto animate = [&app](Engine* engine, View* view, double now) {
app.resourceLoader->asyncUpdateLoad();
app.viewer->updateRootTransform();
app.viewer->populateScene();
app.viewer->applyAnimation(now, app.instance);
};
auto gui = [&app](Engine* engine, View* view) {};
auto preRender = [&app](Engine* engine, View* view, Scene* scene, Renderer* renderer) {};
FilamentApp& filamentApp = FilamentApp::get();
filamentApp.animate(animate);
filamentApp.run(app.config, setup, cleanup, gui, preRender);
return 0;
}
-2
View File
@@ -242,7 +242,6 @@ int main(int argc, char** argv) {
.attribute(VertexAttribute::COLOR, 0,
VertexBuffer::AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR)
.advancedSkinning(true)
.build(*engine);
app.vbs[app.vbCount]->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES_1, 36,
@@ -259,7 +258,6 @@ int main(int argc, char** argv) {
VertexBuffer::AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR)
.enableBufferObjects()
.advancedSkinning(true)
.build(*engine);
app.bos[app.boCount] = BufferObject::Builder()
.size(3 * sizeof(Vertex))
-15
View File
@@ -46,26 +46,11 @@ highp mat4 getViewFromClipMatrix() {
return frameUniforms.viewFromClipMatrix;
}
/** @public-api */
highp mat4 getEyeFromViewMatrix() {
return frameUniforms.eyeFromViewMatrix[getEyeIndex()];
}
/** @public-api */
highp mat4 getEyeFromViewMatrix(int eyeIndex) {
return frameUniforms.eyeFromViewMatrix[eyeIndex];
}
/** @public-api */
highp mat4 getClipFromWorldMatrix() {
return frameUniforms.clipFromWorldMatrix[getEyeIndex()];
}
/** @public-api */
highp mat4 getClipFromWorldMatrix(int eyeIndex) {
return frameUniforms.clipFromWorldMatrix[eyeIndex];
}
/** @public-api */
highp mat4 getWorldFromClipMatrix() {
return frameUniforms.worldFromClipMatrix;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.65.0",
"version": "1.64.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",