Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84cecd334b | ||
|
|
bb6ebd3aea |
+1
-1
@@ -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.
|
||||
|
||||
@@ -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**]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
+13
@@ -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,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.
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 > About Phone and tap the Build number 7 times). You need to go to
|
||||
Settings > System > 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 > System > 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 > 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
|
||||
</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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
@@ -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
|
||||
|
||||
|
||||
@@ -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)_
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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]{ //
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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, ¤tPipeline, sizeof(PipelineState)) != 0)) {
|
||||
currentPipeline = pipeline;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -841,7 +841,7 @@ class MaterialSidePanel extends LitElement {
|
||||
const shaderModel = (isMatinfo ? shaderModelSymbol[variant.shader.shaderModel] : '');
|
||||
return html`
|
||||
<div class="${divClass}" @click="${onClickVariant}">
|
||||
<div>${stage} ${vstring} ${shaderModel} </div>
|
||||
<div>${stage}  ${vstring} ${shaderModel} </div>
|
||||
</div>
|
||||
${languagesDiv ?? nothing}
|
||||
`
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
Reference in New Issue
Block a user