Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15424c85b5 |
@@ -12,7 +12,7 @@ jobs:
|
||||
name: build-android
|
||||
# We intentially use a larger runner here to enable larger disk space
|
||||
# (standard linux runner will fail on disk space and faster build time).
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: 'ubuntu-24.04-32core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
build-desktop-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-16core
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
@@ -171,7 +171,7 @@ jobs:
|
||||
|
||||
validate-wgsl-webgpu:
|
||||
name: validate-wgsl-webgpu
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: 'ubuntu-24.04-8core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
|
||||
@@ -31,7 +31,7 @@ on:
|
||||
jobs:
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-32core
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
|
||||
|
||||
steps:
|
||||
|
||||
@@ -18,9 +18,3 @@ test*.json
|
||||
results
|
||||
/compile_commands.json
|
||||
/.cache
|
||||
|
||||
# For /test/renderdiff dependencies
|
||||
/mesa
|
||||
/gltf
|
||||
/filament-assets
|
||||
/venv
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.65.2'
|
||||
implementation 'com.google.android.filament:filament-android:1.65.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.2'
|
||||
pod 'Filament', '~> 1.65.0'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,12 +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.3
|
||||
|
||||
|
||||
## v1.65.2
|
||||
|
||||
|
||||
## v1.65.1
|
||||
|
||||
- `setFrameScheduledCallback` now works on all backends (frame presentation scheduling is still only
|
||||
|
||||
@@ -28,8 +28,9 @@ Java_com_google_android_filament_SwapChain_nSetFrameCompletedCallback(JNIEnv* en
|
||||
jlong nativeSwapChain, jobject handler, jobject runnable) {
|
||||
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
|
||||
auto* callback = JniCallback::make(env, handler, runnable);
|
||||
swapChain->setFrameCompletedCallback(callback->getHandler(),
|
||||
[callback](SwapChain* swapChain) { JniCallback::postToJavaAndDestroy(callback); });
|
||||
swapChain->setFrameCompletedCallback(nullptr, [callback](SwapChain* swapChain) {
|
||||
JniCallback::postToJavaAndDestroy(callback);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
@@ -52,22 +53,3 @@ Java_com_google_android_filament_SwapChain_nIsProtectedContentSupported(
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jboolean)SwapChain::isProtectedContentSupported(*engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_SwapChain_nSetFrameScheduledCallback(JNIEnv* env, jclass,
|
||||
jlong nativeSwapChain, jobject handler, jobject runnable) {
|
||||
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
|
||||
auto* callback = JniCallback::make(env, handler, runnable);
|
||||
swapChain->setFrameScheduledCallback(callback->getHandler(),
|
||||
[callback](backend::PresentCallable) {
|
||||
// Ignore PresentCallable, which is only meaningful with the Metal backend.
|
||||
JniCallback::postToJavaAndDestroy(callback);
|
||||
});
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_SwapChain_nIsFrameScheduledCallbackSet(
|
||||
JNIEnv *, jclass, jlong nativeSwapChain) {
|
||||
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
|
||||
return (jboolean)swapChain->isFrameScheduledCallbackSet();
|
||||
}
|
||||
|
||||
@@ -139,78 +139,6 @@ public class SwapChain {
|
||||
nSetFrameCompletedCallback(getNativeObject(), handler, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* FrameScheduledCallback is a callback function that notifies an application about the status
|
||||
* of a frame after Filament has finished its processing.
|
||||
*
|
||||
* <p>
|
||||
* The exact timing and semantics of this callback differ depending on the graphics backend in
|
||||
* use.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Metal Backend</h3>
|
||||
* <p>
|
||||
* With the Metal backend, this callback signifies that Filament has completed all CPU-side
|
||||
* processing for a frame and the frame is ready to be scheduled for presentation.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Typically, Filament is responsible for scheduling the frame's presentation to the SwapChain.
|
||||
* If a FrameScheduledCallback is set, however, the application bears the responsibility of
|
||||
* scheduling the frame for presentation by calling the PresentCallable passed to the callback
|
||||
* function. In this mode, Filament will not automatically schedule the frame for presentation.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* When using the Metal backend, if your application delays the call to the PresentCallable
|
||||
* (e.g., by invoking it on a separate thread), you must ensure all PresentCallables have been
|
||||
* called before shutting down the Filament Engine. You can guarantee this by calling
|
||||
* Engine.flushAndWait() before Engine.shutdown(). This is necessary to ensure the Engine has
|
||||
* a chance to clean up all memory related to frame presentation.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Other Backends (OpenGL, Vulkan, WebGPU)</h3>
|
||||
* <p>
|
||||
* On other backends, this callback serves as a notification that Filament has completed all
|
||||
* CPU-side processing for a frame. Filament proceeds with its normal presentation logic
|
||||
* automatically, and the PresentCallable passed to the callback is a no-op that can be safely
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <h3>General Behavior</h3>
|
||||
* <p>
|
||||
* A FrameScheduledCallback can be set on an individual SwapChain through
|
||||
* setFrameScheduledCallback. Each SwapChain can have only one callback set per frame. If
|
||||
* setFrameScheduledCallback is called multiple times on the same SwapChain before
|
||||
* Renderer.endFrame(), the most recent call effectively overwrites any previously set callback.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* The callback set by setFrameScheduledCallback is "latched" when Renderer.endFrame() is
|
||||
* executed. At this point, the callback is fixed for the frame that was just encoded.
|
||||
* Subsequent calls to setFrameScheduledCallback after endFrame() will apply to the next frame.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Use setFrameScheduledCallback() (with default arguments) to unset the callback.
|
||||
* </p>
|
||||
*
|
||||
* @param handler A {@link java.util.concurrent.Executor Executor}.
|
||||
* @param callback The Runnable callback to invoke when frame processing is complete.
|
||||
*/
|
||||
public void setFrameScheduledCallback(@NonNull Object handler, @NonNull Runnable callback) {
|
||||
nSetFrameScheduledCallback(getNativeObject(), handler, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this SwapChain currently has a FrameScheduledCallback set.
|
||||
*
|
||||
* @return true, if the last call to setFrameScheduledCallback set a callback
|
||||
*/
|
||||
public boolean isFrameScheduledCallbackSet() {
|
||||
return nIsFrameScheduledCallbackSet(getNativeObject());
|
||||
}
|
||||
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
throw new IllegalStateException("Calling method on destroyed SwapChain");
|
||||
@@ -223,8 +151,6 @@ public class SwapChain {
|
||||
}
|
||||
|
||||
private static native void nSetFrameCompletedCallback(long nativeSwapChain, Object handler, Runnable callback);
|
||||
private static native void nSetFrameScheduledCallback(long nativeSwapChain, Object handler, Runnable callback);
|
||||
private static native boolean nIsFrameScheduledCallbackSet(long nativeSwapChain);
|
||||
private static native boolean nIsSRGBSwapChainSupported(long nativeEngine);
|
||||
private static native boolean nIsMSAASwapChainSupported(long nativeEngine, int samples);
|
||||
private static native boolean nIsProtectedContentSupported(long nativeEngine);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.65.2
|
||||
VERSION_NAME=1.65.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ android {
|
||||
|
||||
compileSdkVersion versions.compileSdk
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.filament.hellocam"
|
||||
applicationId "com.google.android.filament.hellocamera"
|
||||
minSdkVersion 23
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.65.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.65.0'
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||
@@ -196,7 +196,7 @@ dependencies {
|
||||
</div>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.65.1'
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.65.0'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>renderdiff - 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="rendering-difference-test"><a class="header" href="#rendering-difference-test">Rendering Difference Test</a></h1>
|
||||
<p>We created a few scripts to run <code>gltf_viewer</code> and produce headless renderings.</p>
|
||||
<p>This is mainly useful for continuous integration where GPUs are generally not available on cloud
|
||||
machines. To perform software rasterization, these scripts are centered around <a href="https://docs.mesa3d.org">Mesa</a>'s software
|
||||
rasterizers, but nothing bars us from using another rasterizer like <a href="https://github.com/google/swiftshader">SwiftShader</a>. Additionally,
|
||||
we should be able to use GPUs where available (though this is more of a future work).</p>
|
||||
<p>The script <code>render.py</code> contains the core logic for taking input parameters (such as the test
|
||||
description file) and then running gltf_viewer to produce the renderings.</p>
|
||||
<p>In the <code>test</code> directory is a list of test descriptions that are specified in json. Please see
|
||||
<code>sample.json</code> to parse the structure.</p>
|
||||
<h2 id="setting-up-python"><a class="header" href="#setting-up-python">Setting up python</a></h2>
|
||||
<p>The <code>renderdiff</code> project uses <code>python</code> extensively. To install the dependencies for producing
|
||||
renderings, do the following step</p>
|
||||
<ul>
|
||||
<li>Set up a virtual environment (from the root directory)
|
||||
<pre><code> python3 -m venv venv
|
||||
. ./venv/bin/activate
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>Install the rendering dependencies
|
||||
<pre><code> pip install -r test/renderdiff/src/rendering_requirements.txt
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>Install the viewer depdencies
|
||||
<pre><code> pip install -r test/renderdiff/src/viewer_requirements.txt
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>For the commands in the following section, do not exit the virtual environment. Once you've
|
||||
completed all your work, you can exit with
|
||||
<pre><code> deactivate
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="running-the-test-locally"><a class="header" href="#running-the-test-locally">Running the test locally</a></h2>
|
||||
<ul>
|
||||
<li>
|
||||
<p>To run the same presbumit as <a href="presubmit-renderdiff"><code>test-renderdiff</code></a>, you can do</p>
|
||||
<pre><code>bash test/renderdiff/local_test.sh
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>This script will generate the renderings based on the current state of your repo.
|
||||
Additionally, it will also compare the generated images with corresponding images in the
|
||||
golden repo.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>To just render without running the test, you could use the following script</p>
|
||||
<pre><code>bash test/renderdiff/generate.sh
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 id="update-the-golden-images"><a class="header" href="#update-the-golden-images">Update the golden images</a></h2>
|
||||
<p>The golden images are stored in a github repository: https://github.com/google/filament-assets.
|
||||
Filament team members should have access to write to the repository. A typical flow for updating
|
||||
the goldens is to upload your changed images into <strong>branch</strong> of <code>filament-assets</code>. This branch is
|
||||
paired with a PR or commit on the <code>filament</code> repo.</p>
|
||||
<p>As an example, imagine I am working on a PR, and I've uploaded my change, which is in a branch
|
||||
called <code>my-pr-branch</code>, to <code>filament</code>. This PR requires updating the golden. We would do it
|
||||
in the following fashion</p>
|
||||
<h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Run interactive mode in the <code>update_golden.py</code> script.</p>
|
||||
<pre><code>python3 test/renderdiff/src/update_golden.py
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>This will guide you through a series of steps to push the changes to a remote branch
|
||||
on <code>filament-assets</code>.</p>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 id="manually-updating-the-golden-repo"><a class="header" href="#manually-updating-the-golden-repo">Manually updating the golden repo</a></h3>
|
||||
<ul>
|
||||
<li>
|
||||
<p>Check out the golden repo</p>
|
||||
<pre><code>git clone git@github.com:google/filament-assets.git
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Create a branch on the golden repo</p>
|
||||
<pre><code>cd filament-assets
|
||||
git switch -c my-pr-branch-golden
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>Copy the new images to their appropriate place in <code>filament-assets</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Push the <code>filament-assets</code> working branch to remote</p>
|
||||
<pre><code>git push origin my-pr-branch-golden
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>
|
||||
<p>In the commit message of your working branch on <code>filament</code>, add the following line</p>
|
||||
<pre><code>RDIFF_BBRANCH=my-pr-branch-golden
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Doing the above has multiple effects:</p>
|
||||
<ul>
|
||||
<li>The presubmit test <a href="https://github.com/google/filament/blob/e85dfe75c86106a05019e13ccdbef67e030af675/.github/workflows/presubmit.yml#L118"><code>test-renderdiff</code></a> will test against the provided
|
||||
branch of the golden repo (i.e. <code>my-pr-branch-golden</code>).</li>
|
||||
<li>If the PR is merged, then there is another workflow that will merge <code>my-pr-branch-golden</code> to
|
||||
the <code>main</code> branch of the golden repo.</li>
|
||||
</ul>
|
||||
<h2 id="viewing-test-results"><a class="header" href="#viewing-test-results">Viewing test results</a></h2>
|
||||
<p>We provide a viewer for looking at the result of a test run. The viewer is a webapp that can be used by
|
||||
pointing your browser to a localhost port. If you input the viewer with a PR or a directory, it will
|
||||
parse the test result and show the results and the rendered and/or golden images.</p>
|
||||
<p><img src="../images/renderdiff_example.png" alt="Viewer" /></p>
|
||||
<p>To run the viewer of a test output directory that has been generated locally, you would run the
|
||||
following</p>
|
||||
<pre><code>python3 test/renderdiff/src/viewer.py --diff=[test output]
|
||||
</code></pre>
|
||||
<p>where <code>[test output]</code> is a directory containing the <code>compare_results.json</code> of the test run.
|
||||
For example, it could be <code>out/renderdiff/diffs/presubmit</code> for the standard path to the
|
||||
<code>presubmit</code> test output.</p>
|
||||
<p>To see the results of a Pull Request initiated test run, you would do the following</p>
|
||||
<pre><code>python3 test/renderdiff/src/viewer.py --pr_number=[PR #] --github_token=[github token]
|
||||
</code></pre>
|
||||
<p>where <code>[PR #]</code> is the numeric ID of your pull request, and the <code>[github token]</code> is an acess token
|
||||
that you (as a github user) needs to generate (<a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens">reference</a>).</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../notes/tests.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/libs.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/tests.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/libs.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>
|
||||
|
Before Width: | Height: | Size: 361 KiB |
@@ -335,7 +335,7 @@ nodes</li>
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../notes/tests.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/libs.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -349,7 +349,7 @@ nodes</li>
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../notes/tests.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/libs.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/renderdiff.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/framegraph.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/renderdiff.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/framegraph.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Tests - 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="tests"><a class="header" href="#tests">Tests</a></h1>
|
||||
<p>Filament has a collection of tests that can be run locally. Many of them are used
|
||||
for in our Continuation Integration flow <a href="https://https://github.com/google/filament/tree/main/.github/workflows">on github</a>.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../notes/framegraph.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="../dup/renderdiff.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/framegraph.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="../dup/renderdiff.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>
|
||||
@@ -78,11 +78,5 @@
|
||||
},
|
||||
"docs_src/README.md": {
|
||||
"dest": "dup/docs.md"
|
||||
},
|
||||
"test/renderdiff/README.md": {
|
||||
"dest": "dup/renderdiff.md",
|
||||
"link_transforms": {
|
||||
"docs/images/": "../images/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
- [Code coverage analysis](./notes/coverage.md)
|
||||
- [Performance analysis](./notes/performance_analysis.md)
|
||||
- [Framegraph](./notes/framegraph.md)
|
||||
- [Tests](./notes/tests.md)
|
||||
- [renderdiff](./dup/renderdiff.md)
|
||||
- [Libraries](./notes/libs.md)
|
||||
- [bluegl](./dup/bluegl.md)
|
||||
- [bluevk](./dup/bluevk.md)
|
||||
|
||||
|
Before Width: | Height: | Size: 361 KiB |
@@ -1,4 +0,0 @@
|
||||
# Tests
|
||||
|
||||
Filament has a collection of tests that can be run locally. Many of them are used
|
||||
for in our Continuation Integration flow [on github](https://https://github.com/google/filament/tree/main/.github/workflows).
|
||||
@@ -84,8 +84,6 @@ set(SRCS
|
||||
src/InstanceBuffer.cpp
|
||||
src/LightManager.cpp
|
||||
src/Material.cpp
|
||||
src/MaterialCache.cpp
|
||||
src/MaterialDefinition.cpp
|
||||
src/MaterialInstance.cpp
|
||||
src/MaterialInstanceManager.cpp
|
||||
src/MaterialParser.cpp
|
||||
@@ -177,8 +175,6 @@ set(PRIVATE_HDRS
|
||||
src/HwRenderPrimitiveFactory.h
|
||||
src/HwVertexBufferInfoFactory.h
|
||||
src/Intersections.h
|
||||
src/MaterialCache.h
|
||||
src/MaterialDefinition.h
|
||||
src/MaterialParser.h
|
||||
src/MaterialInstanceManager.h
|
||||
src/PIDController.h
|
||||
|
||||
@@ -78,8 +78,6 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
|
||||
src/opengl/GLTexture.h
|
||||
src/opengl/GLUtils.cpp
|
||||
src/opengl/GLUtils.h
|
||||
src/opengl/GLMemoryMappedBuffer.cpp
|
||||
src/opengl/GLMemoryMappedBuffer.h
|
||||
src/opengl/OpenGLBlobCache.cpp
|
||||
src/opengl/OpenGLBlobCache.h
|
||||
src/opengl/OpenGLContext.cpp
|
||||
@@ -568,7 +566,6 @@ if (APPLE OR LINUX)
|
||||
test/test_ReadPixels.cpp
|
||||
test/test_BufferUpdates.cpp
|
||||
test/test_Callbacks.cpp
|
||||
test/test_MemoryMappedBuffer.cpp
|
||||
test/test_MRT.cpp
|
||||
test/test_PushConstants.cpp
|
||||
test/test_LoadImage.cpp
|
||||
|
||||
@@ -556,21 +556,12 @@ constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept {
|
||||
}
|
||||
|
||||
/**
|
||||
* How the buffer will be used.
|
||||
* Frequency at which a buffer is expected to be modified and used. This is used as an hint
|
||||
* for the driver to make better decisions about managing memory internally.
|
||||
*/
|
||||
enum class BufferUsage : uint8_t {
|
||||
STATIC = 0, //!< (legacy) content modified once, used many times
|
||||
DYNAMIC = 1, //!< (legacy) content modified frequently, used many times
|
||||
DYNAMIC_BIT = 0x1, //!< buffer can be modified frequently, used many times
|
||||
SHARED_WRITE_BIT = 0x04, //!< buffer can be memory mapped for write operations
|
||||
};
|
||||
|
||||
/**
|
||||
* How the buffer will be mapped.
|
||||
*/
|
||||
enum class MapBufferAccessFlags : uint8_t {
|
||||
WRITE_BIT = 0x2, //!< buffer is mapped from writing
|
||||
INVALIDATE_RANGE_BIT = 0x4, //!< the mapped range content is lost
|
||||
STATIC, //!< content modified once, used many times
|
||||
DYNAMIC, //!< content modified frequently, used many times
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -636,15 +627,6 @@ enum class ShaderModel : uint8_t {
|
||||
};
|
||||
static constexpr size_t SHADER_MODEL_COUNT = 2;
|
||||
|
||||
constexpr std::string_view to_string(ShaderModel model) noexcept {
|
||||
switch (model) {
|
||||
case ShaderModel::MOBILE:
|
||||
return "mobile";
|
||||
case ShaderModel::DESKTOP:
|
||||
return "desktop";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Primitive types
|
||||
*/
|
||||
@@ -1710,11 +1692,6 @@ template<> struct utils::EnableBitMaskOperators<filament::backend::TextureUsage>
|
||||
: public std::true_type {};
|
||||
template<> struct utils::EnableBitMaskOperators<filament::backend::StencilFace>
|
||||
: public std::true_type {};
|
||||
template<> struct utils::EnableBitMaskOperators<filament::backend::BufferUsage>
|
||||
: public std::true_type {};
|
||||
template<> struct utils::EnableBitMaskOperators<filament::backend::MapBufferAccessFlags>
|
||||
: public std::true_type {};
|
||||
|
||||
template<> struct utils::EnableIntegerOperators<filament::backend::TextureCubemapFace>
|
||||
: public std::true_type {};
|
||||
template<> struct utils::EnableIntegerOperators<filament::backend::FeatureLevel>
|
||||
|
||||
@@ -45,7 +45,6 @@ struct HwVertexBufferInfo;
|
||||
struct HwVertexBuffer;
|
||||
struct HwDescriptorSetLayout;
|
||||
struct HwDescriptorSet;
|
||||
struct HwMemoryMappedBuffer;
|
||||
|
||||
/*
|
||||
* A handle to a backend resource. HandleBase is for internal use only.
|
||||
@@ -160,7 +159,6 @@ using VertexBufferHandle = Handle<HwVertexBuffer>;
|
||||
using VertexBufferInfoHandle = Handle<HwVertexBufferInfo>;
|
||||
using DescriptorSetLayoutHandle = Handle<HwDescriptorSetLayout>;
|
||||
using DescriptorSetHandle = Handle<HwDescriptorSet>;
|
||||
using MemoryMappedBufferHandle = Handle<HwMemoryMappedBuffer>;
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -168,7 +168,6 @@ protected:
|
||||
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
|
||||
Config mContextAttribs;
|
||||
std::vector<EGLContext> mAdditionalContexts;
|
||||
bool mMSAA4XSupport = false;
|
||||
|
||||
// supported extensions detected at runtime
|
||||
struct {
|
||||
@@ -219,8 +218,6 @@ private:
|
||||
return makeCurrent(mCurrentContext, drawSurface, readSurface);
|
||||
}
|
||||
} egl{ mEGLDisplay };
|
||||
|
||||
bool checkIfMSAASwapChainSupported(uint32_t samples) const noexcept;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -108,16 +108,12 @@
|
||||
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
|
||||
|
||||
#define DECL_DRIVER_API_TAGGED_R_N(R, N, ...) \
|
||||
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString&&, tag = {}), \
|
||||
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString&&, tag))
|
||||
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString, tag = {}), \
|
||||
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString, tag))
|
||||
|
||||
#define DECL_DRIVER_API_SYNCHRONOUS_N(R, N, ...) \
|
||||
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
|
||||
|
||||
#define DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(R, N, ...) \
|
||||
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString, tag = {}), \
|
||||
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString, tag))
|
||||
|
||||
// on some compilers the ##__VA_ARGS__ hack is not supported, so we can't handle 0-parameter APIs
|
||||
// with DECL_DRIVER_API_SYNCHRONOUS_N
|
||||
#define DECL_DRIVER_API_SYNCHRONOUS_0(R, N) \
|
||||
@@ -295,11 +291,27 @@ DECL_DRIVER_API_TAGGED_R_N(backend::DescriptorSetLayoutHandle, createDescriptorS
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::DescriptorSetHandle, createDescriptorSet,
|
||||
backend::DescriptorSetLayoutHandle, dslh)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::MemoryMappedBufferHandle, mapBuffer,
|
||||
DECL_DRIVER_API_N(updateDescriptorSetBuffer,
|
||||
backend::DescriptorSetHandle, dsh,
|
||||
backend::descriptor_binding_t, binding,
|
||||
backend::BufferObjectHandle, boh,
|
||||
size_t, offset,
|
||||
size_t, size,
|
||||
backend::MapBufferAccessFlags, access)
|
||||
uint32_t, offset,
|
||||
uint32_t, size
|
||||
)
|
||||
|
||||
DECL_DRIVER_API_N(updateDescriptorSetTexture,
|
||||
backend::DescriptorSetHandle, dsh,
|
||||
backend::descriptor_binding_t, binding,
|
||||
backend::TextureHandle, th,
|
||||
SamplerParams, params
|
||||
)
|
||||
|
||||
DECL_DRIVER_API_N(bindDescriptorSet,
|
||||
backend::DescriptorSetHandle, dsh,
|
||||
backend::descriptor_set_t, set,
|
||||
backend::DescriptorSetOffsetArray&&, offsets
|
||||
)
|
||||
|
||||
|
||||
/*
|
||||
* Destroying driver objects
|
||||
@@ -309,7 +321,7 @@ DECL_DRIVER_API_TAGGED_R_N(backend::MemoryMappedBufferHandle, mapBuffer,
|
||||
DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh)
|
||||
DECL_DRIVER_API_N(destroyVertexBufferInfo, backend::VertexBufferInfoHandle, vbih)
|
||||
DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh)
|
||||
DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, boh)
|
||||
DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh)
|
||||
DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph)
|
||||
DECL_DRIVER_API_N(destroyProgram, backend::ProgramHandle, ph)
|
||||
DECL_DRIVER_API_N(destroyTexture, backend::TextureHandle, th)
|
||||
@@ -321,7 +333,6 @@ DECL_DRIVER_API_N(destroyFence, backend::FenceHandle, fh)
|
||||
DECL_DRIVER_API_N(destroySync, backend::SyncHandle, sh)
|
||||
DECL_DRIVER_API_N(destroyDescriptorSetLayout, backend::DescriptorSetLayoutHandle, dslh)
|
||||
DECL_DRIVER_API_N(destroyDescriptorSet, backend::DescriptorSetHandle, dsh)
|
||||
DECL_DRIVER_API_N(unmapBuffer, backend::MemoryMappedBufferHandle, mmbh)
|
||||
|
||||
/*
|
||||
* Synchronous APIs
|
||||
@@ -329,8 +340,8 @@ DECL_DRIVER_API_N(unmapBuffer, backend::MemoryMappedBufferHandl
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(void, terminate)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(backend::StreamHandle, createStreamNative, void*, stream)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(backend::StreamHandle, createStreamAcquired)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(backend::StreamHandle, createStreamNative, void*, stream, utils::CString, tag)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(backend::StreamHandle, createStreamAcquired, utils::CString, tag)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setAcquiredImage, backend::StreamHandle, stream, void*, image, const math::mat3f&, transform, backend::CallbackHandler*, handler, backend::StreamCallback, cb, void*, userData)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setStreamDimensions, backend::StreamHandle, stream, uint32_t, width, uint32_t, height)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(int64_t, getStreamTimestamp, backend::StreamHandle, stream)
|
||||
@@ -362,7 +373,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxArrayTextureLayers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage2, backend::Platform::ExternalImageHandleRef, image)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(backend::TimerQueryResult, getTimerQueryValue, backend::TimerQueryHandle, tqh, uint64_t*, elapsedTime)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(backend::TimerQueryResult, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(backend::FeatureLevel, getFeatureLevel)
|
||||
|
||||
@@ -387,16 +398,16 @@ DECL_DRIVER_API_N(updateBufferObject,
|
||||
uint32_t, byteOffset)
|
||||
|
||||
DECL_DRIVER_API_N(registerBufferObjectStreams,
|
||||
backend::BufferObjectHandle, boh,
|
||||
backend::BufferObjectHandle, ibh,
|
||||
backend::BufferObjectStreamDescriptor&&, streams)
|
||||
|
||||
DECL_DRIVER_API_N(updateBufferObjectUnsynchronized,
|
||||
backend::BufferObjectHandle, boh,
|
||||
backend::BufferObjectHandle, ibh,
|
||||
backend::BufferDescriptor&&, data,
|
||||
uint32_t, byteOffset)
|
||||
|
||||
DECL_DRIVER_API_N(resetBufferObject,
|
||||
backend::BufferObjectHandle, boh)
|
||||
backend::BufferObjectHandle, ibh)
|
||||
|
||||
DECL_DRIVER_API_N(update3DImage,
|
||||
backend::TextureHandle, th,
|
||||
@@ -425,10 +436,10 @@ DECL_DRIVER_API_0(endRenderPass)
|
||||
DECL_DRIVER_API_0(nextSubpass)
|
||||
|
||||
DECL_DRIVER_API_N(beginTimerQuery,
|
||||
backend::TimerQueryHandle, tqh)
|
||||
backend::TimerQueryHandle, query)
|
||||
|
||||
DECL_DRIVER_API_N(endTimerQuery,
|
||||
backend::TimerQueryHandle, tqh)
|
||||
backend::TimerQueryHandle, query)
|
||||
|
||||
DECL_DRIVER_API_N(compilePrograms,
|
||||
backend::CompilerPriorityQueue, priority,
|
||||
@@ -436,26 +447,6 @@ DECL_DRIVER_API_N(compilePrograms,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_N(updateDescriptorSetBuffer,
|
||||
backend::DescriptorSetHandle, dsh,
|
||||
backend::descriptor_binding_t, binding,
|
||||
backend::BufferObjectHandle, boh,
|
||||
uint32_t, offset,
|
||||
uint32_t, size
|
||||
)
|
||||
|
||||
DECL_DRIVER_API_N(updateDescriptorSetTexture,
|
||||
backend::DescriptorSetHandle, dsh,
|
||||
backend::descriptor_binding_t, binding,
|
||||
backend::TextureHandle, th,
|
||||
SamplerParams, params
|
||||
)
|
||||
|
||||
DECL_DRIVER_API_N(copyToMemoryMappedBuffer,
|
||||
backend::MemoryMappedBufferHandle, mmbh,
|
||||
size_t, offset,
|
||||
backend::BufferDescriptor&&, data)
|
||||
|
||||
/*
|
||||
* Swap chain
|
||||
*/
|
||||
@@ -544,12 +535,6 @@ DECL_DRIVER_API_N(bindPipeline,
|
||||
DECL_DRIVER_API_N(bindRenderPrimitive,
|
||||
backend::RenderPrimitiveHandle, rph)
|
||||
|
||||
DECL_DRIVER_API_N(bindDescriptorSet,
|
||||
backend::DescriptorSetHandle, dsh,
|
||||
backend::descriptor_set_t, set,
|
||||
backend::DescriptorSetOffsetArray&&, offsets
|
||||
)
|
||||
|
||||
DECL_DRIVER_API_N(draw2,
|
||||
uint32_t, indexOffset,
|
||||
uint32_t, indexCount,
|
||||
|
||||
@@ -18,16 +18,10 @@
|
||||
|
||||
#include "DataReshaper.h"
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <string_view>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -642,7 +636,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
switch (data.type) {
|
||||
case PixelDataType::BYTE:
|
||||
case PixelDataType::UBYTE: {
|
||||
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint8_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
|
||||
data.alignment, data.left, data.top, data.stride, freeFunc);
|
||||
@@ -652,7 +646,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
case PixelDataType::SHORT:
|
||||
case PixelDataType::USHORT:
|
||||
case PixelDataType::HALF: {
|
||||
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint16_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
|
||||
data.alignment, data.left, data.top, data.stride, freeFunc);
|
||||
@@ -661,7 +655,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
}
|
||||
case PixelDataType::INT:
|
||||
case PixelDataType::UINT: {
|
||||
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint32_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
|
||||
data.alignment, data.left, data.top, data.stride, freeFunc);
|
||||
@@ -669,7 +663,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
return true;
|
||||
}
|
||||
case PixelDataType::FLOAT: {
|
||||
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<float, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
|
||||
data.alignment, data.left, data.top, data.stride, freeFunc);
|
||||
|
||||
@@ -63,7 +63,6 @@ public:
|
||||
static void copyImage(uint8_t* UTILS_RESTRICT dest,
|
||||
const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow, size_t /*srcChannelCount*/,
|
||||
size_t /*dstRowOffset */, size_t /*dstColumnOffset */,
|
||||
size_t dstBytesPerRow, size_t /*dstChannelCount*/,
|
||||
size_t /*width*/, size_t height, bool /*swizzle*/) {
|
||||
if (srcBytesPerRow == dstBytesPerRow) {
|
||||
@@ -80,20 +79,17 @@ public:
|
||||
template<typename dstComponentType, typename srcComponentType>
|
||||
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow,
|
||||
size_t srcChannelCount,
|
||||
size_t dstRowOffset, size_t dstColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t srcChannelCount, size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t width, size_t height, bool swizzle) {
|
||||
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
|
||||
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
|
||||
const size_t minChannelCount = math::min(srcChannelCount, dstChannelCount);
|
||||
assert_invariant(minChannelCount <= 4);
|
||||
UTILS_ASSUME(minChannelCount <= 4);
|
||||
dest += (dstRowOffset * dstBytesPerRow);
|
||||
const int inds[4] = { swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3 };
|
||||
for (size_t row = 0; row < height; ++row) {
|
||||
const srcComponentType* in = (const srcComponentType*) src;
|
||||
dstComponentType* out = (dstComponentType*)dest + (dstColumnOffset * dstChannelCount);
|
||||
const srcComponentType* in = (const srcComponentType*)src;
|
||||
dstComponentType* out = (dstComponentType*)dest;
|
||||
for (size_t column = 0; column < width; ++column) {
|
||||
for (size_t channel = 0; channel < minChannelCount; ++channel) {
|
||||
if constexpr (std::is_same_v<dstComponentType, srcComponentType>) {
|
||||
@@ -132,23 +128,17 @@ public:
|
||||
default: return false;
|
||||
}
|
||||
void (*reshaper)(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
|
||||
size_t srcChannelCount,
|
||||
size_t srcRowOffset, size_t srcColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t srcChannelCount, size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t width, size_t height, bool swizzle) = nullptr;
|
||||
constexpr auto UBYTE = PixelDataType::UBYTE;
|
||||
constexpr auto FLOAT = PixelDataType::FLOAT;
|
||||
constexpr auto UINT = PixelDataType::UINT;
|
||||
constexpr auto INT = PixelDataType::INT;
|
||||
constexpr auto HALF = PixelDataType::HALF;
|
||||
constexpr auto UBYTE = PixelDataType::UBYTE, FLOAT = PixelDataType::FLOAT,
|
||||
UINT = PixelDataType::UINT, INT = PixelDataType::INT;
|
||||
switch (dst->type) {
|
||||
case UBYTE:
|
||||
switch (srcType) {
|
||||
case UBYTE:
|
||||
reshaper = reshapeImage<uint8_t, uint8_t>;
|
||||
if (dst->format == PixelDataFormat::RGBA &&
|
||||
dstChannelCount == srcChannelCount && !swizzle && dst->top == 0 &&
|
||||
dst->left == 0) {
|
||||
dstChannelCount == srcChannelCount && !swizzle) {
|
||||
reshaper = copyImage;
|
||||
}
|
||||
break;
|
||||
@@ -185,20 +175,13 @@ public:
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case HALF:
|
||||
switch (srcType) {
|
||||
case HALF: reshaper = copyImage; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
uint8_t* dstBytes = (uint8_t*) dst->buffer;
|
||||
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(dst->format, dst->type,
|
||||
dst->stride ? dst->stride : width, 1, dst->alignment);
|
||||
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount,
|
||||
dst->top, dst->left, dstBytesPerRow,
|
||||
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount, dstBytesPerRow,
|
||||
dstChannelCount, width, height, swizzle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -78,9 +78,6 @@ struct HwBufferObject : public HwBase {
|
||||
explicit HwBufferObject(uint32_t byteCount) noexcept : byteCount(byteCount) {}
|
||||
};
|
||||
|
||||
struct HwMemoryMappedBuffer : public HwBase {
|
||||
};
|
||||
|
||||
struct HwIndexBuffer : public HwBase {
|
||||
uint32_t count : 27;
|
||||
uint32_t elementSize : 5;
|
||||
@@ -214,7 +211,7 @@ public:
|
||||
protected:
|
||||
class CallbackDataDetails;
|
||||
|
||||
void scheduleDestroy(BufferDescriptor&& buffer) noexcept {
|
||||
inline void scheduleDestroy(BufferDescriptor&& buffer) noexcept {
|
||||
if (buffer.hasCallback()) {
|
||||
scheduleDestroySlow(std::move(buffer));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
|
||||
|
||||
#include "MetalContext.h"
|
||||
#include "MetalFlags.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/platforms/PlatformMetal.h>
|
||||
@@ -154,7 +153,7 @@ private:
|
||||
Type mType = Type::NONE;
|
||||
|
||||
static PlatformMetal* platform;
|
||||
static std::array<uint64_t, TypeCount> aliveBuffers;
|
||||
static std::array<std::atomic<uint64_t>, TypeCount> aliveBuffers;
|
||||
};
|
||||
|
||||
class MetalBuffer {
|
||||
@@ -192,15 +191,6 @@ public:
|
||||
|
||||
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
|
||||
|
||||
void setLabel(const utils::CString& label) {
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
if (label.empty()) {
|
||||
return;
|
||||
}
|
||||
mBuffer.get().label = @(label.c_str_safe());
|
||||
#endif
|
||||
}
|
||||
|
||||
enum Stage : uint8_t {
|
||||
VERTEX = 1u << 0u,
|
||||
FRAGMENT = 1u << 1u,
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
std::array<uint64_t, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
|
||||
std::array<std::atomic<uint64_t>, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
|
||||
PlatformMetal* TrackedMetalBuffer::platform = nullptr;
|
||||
PlatformMetal* ScopedAllocationTimer::platform = nullptr;
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
context->pendingCommandBuffer = [context->commandQueue commandBuffer];
|
||||
// It's safe for this block to capture the context variable. MetalDriver::terminate will ensure
|
||||
// all frames and their completion handlers finish before context is deallocated.
|
||||
const uint64_t thisCommandBufferId = context->pendingCommandBufferId;
|
||||
uint64_t thisCommandBufferId = context->pendingCommandBufferId;
|
||||
[context->pendingCommandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
|
||||
context->resourceTracker.clearResources((__bridge void*) buffer);
|
||||
|
||||
@@ -118,11 +118,6 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
context->memorylessLimitsReached = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
|
||||
LOG(ERROR) << "Filament Metal command buffer errored with code: " << errorCode << " ("
|
||||
<< stringifyMTLCommandBufferError(errorCode) << ").";
|
||||
}
|
||||
}];
|
||||
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
|
||||
<< "Could not obtain command buffer.";
|
||||
|
||||
@@ -418,14 +418,14 @@ void MetalDriver::finish(int) {
|
||||
}
|
||||
|
||||
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) {
|
||||
uint8_t attributeCount, AttributeArray attributes, utils::CString tag) {
|
||||
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
|
||||
bufferCount, attributeCount, attributes);
|
||||
mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih, utils::CString&& tag) {
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih, utils::CString tag) {
|
||||
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
|
||||
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
|
||||
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
|
||||
@@ -433,7 +433,7 @@ void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
|
||||
}
|
||||
|
||||
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
uint32_t indexCount, BufferUsage usage, utils::CString&& tag) {
|
||||
uint32_t indexCount, BufferUsage usage, utils::CString tag) {
|
||||
auto elementSize = (uint8_t)getElementTypeSize(elementType);
|
||||
auto* indexBuffer =
|
||||
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
|
||||
@@ -441,18 +441,16 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
|
||||
FILAMENT_CHECK_POSTCONDITION(buffer.wasAllocationSuccessful())
|
||||
<< "Could not allocate Metal index buffer of size " << buffer.getSize()
|
||||
<< ", tag=" << tag.c_str_safe();
|
||||
buffer.setLabel(tag);
|
||||
mHandleAllocator.associateTagToHandle(ibh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) {
|
||||
BufferObjectBinding bindingType, BufferUsage usage, utils::CString tag) {
|
||||
auto* bufferObject =
|
||||
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
|
||||
FILAMENT_CHECK_POSTCONDITION(bufferObject->getBuffer()->wasAllocationSuccessful())
|
||||
<< "Could not allocate Metal buffer of size " << byteCount
|
||||
<< ", tag=" << tag.c_str_safe();
|
||||
bufferObject->getBuffer()->setLabel(tag);
|
||||
mHandleAllocator.associateTagToHandle(boh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
@@ -488,16 +486,13 @@ inline const char* stringify(SamplerType samplerType) {
|
||||
|
||||
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
|
||||
uint32_t depth, TextureUsage usage, utils::CString&& tag) {
|
||||
uint32_t depth, TextureUsage usage, utils::CString tag) {
|
||||
// Clamp sample count to what the device supports.
|
||||
auto& sc = mContext->sampleCountLookup;
|
||||
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
|
||||
|
||||
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, target, levels, format,
|
||||
samples, width, height, depth, usage);
|
||||
mContext->textures.insert(texture);
|
||||
|
||||
texture->setLabel(tag);
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(
|
||||
th, *mContext, target, levels, format, samples, width, height, depth, usage));
|
||||
|
||||
DEBUG_LOG(
|
||||
"createTextureR(th = %d, target = %s, levels = %d, format = ?, samples = %d, width = "
|
||||
@@ -508,22 +503,18 @@ void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) {
|
||||
uint8_t baseLevel, uint8_t levelCount, utils::CString tag) {
|
||||
MetalTexture const* src = handle_cast<MetalTexture>(srch);
|
||||
MetalTexture* texture =
|
||||
construct_handle<MetalTexture>(th, *mContext, src, baseLevel, levelCount);
|
||||
mContext->textures.insert(texture);
|
||||
texture->setLabel(tag);
|
||||
mContext->textures.insert(
|
||||
construct_handle<MetalTexture>(th, *mContext, src, baseLevel, levelCount));
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a, utils::CString&& tag) {
|
||||
backend::TextureSwizzle a, utils::CString tag) {
|
||||
MetalTexture const* src = handle_cast<MetalTexture>(srch);
|
||||
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, src, r, g, b, a);
|
||||
mContext->textures.insert(texture);
|
||||
texture->setLabel(tag);
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext, src, r, g, b, a));
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
@@ -531,17 +522,15 @@ void MetalDriver::createTextureExternalImage2R(Handle<HwTexture> th,
|
||||
backend::SamplerType target,
|
||||
backend::TextureFormat format,
|
||||
uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef image, utils::CString&& tag) {
|
||||
Platform::ExternalImageHandleRef image, utils::CString tag) {
|
||||
// FIXME: implement createTextureExternalImage2R
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, utils::CString&& tag) {
|
||||
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, format, width, height,
|
||||
usage, (CVPixelBufferRef) image);
|
||||
mContext->textures.insert(texture);
|
||||
texture->setLabel(tag);
|
||||
void* image, utils::CString tag) {
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(
|
||||
th, *mContext, format, width, height, usage, (CVPixelBufferRef)image));
|
||||
// This release matches the retain call in setupExternalImage. The MetalTexture will have
|
||||
// retained the buffer by now.
|
||||
CVPixelBufferRelease((CVPixelBufferRef)image);
|
||||
@@ -550,11 +539,9 @@ void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sam
|
||||
|
||||
void MetalDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane, utils::CString&& tag) {
|
||||
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, format, width, height,
|
||||
usage, (CVPixelBufferRef) image, plane);
|
||||
mContext->textures.insert(texture);
|
||||
texture->setLabel(tag);
|
||||
void* image, uint32_t plane, utils::CString tag) {
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(
|
||||
th, *mContext, format, width, height, usage, (CVPixelBufferRef)image, plane));
|
||||
// This release matches the retain call in setupExternalImage. The MetalTexture will have
|
||||
// retained the buffer by now.
|
||||
CVPixelBufferRelease((CVPixelBufferRef)image);
|
||||
@@ -564,7 +551,7 @@ void MetalDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
|
||||
uint32_t depth, TextureUsage usage, utils::CString&& tag) {
|
||||
uint32_t depth, TextureUsage usage, utils::CString tag) {
|
||||
id<MTLTexture> metalTexture = (id<MTLTexture>) CFBridgingRelease((void*) i);
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.width == width)
|
||||
<< "Imported id<MTLTexture> width (" << metalTexture.width
|
||||
@@ -579,22 +566,20 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
FILAMENT_CHECK_PRECONDITION(metalTexture.textureType == filamentMetalType)
|
||||
<< "Imported id<MTLTexture> type (" << metalTexture.textureType
|
||||
<< ") != Filament texture type (" << filamentMetalType << ")";
|
||||
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, target, levels, format,
|
||||
samples, width, height, depth, usage, metalTexture);
|
||||
mContext->textures.insert(texture);
|
||||
texture->setLabel(tag);
|
||||
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
|
||||
target, levels, format, samples, width, height, depth, usage, metalTexture));
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt, utils::CString&& tag) {
|
||||
PrimitiveType pt, utils::CString tag) {
|
||||
construct_handle<MetalRenderPrimitive>(rph);
|
||||
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
|
||||
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils::CString&& tag) {
|
||||
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils::CString tag) {
|
||||
#if FILAMENT_METAL_DEBUG_LOG
|
||||
auto handleId = rph.getId();
|
||||
DEBUG_LOG("createProgramR(rph = %d, program = ", handleId);
|
||||
@@ -604,7 +589,7 @@ void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils
|
||||
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString&& tag) {
|
||||
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString tag) {
|
||||
construct_handle<MetalRenderTarget>(rth, mContext);
|
||||
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
|
||||
}
|
||||
@@ -612,7 +597,7 @@ void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::
|
||||
void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
|
||||
uint8_t samples, uint8_t layerCount, MRT color,
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil, utils::CString&& tag) {
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil, utils::CString tag) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
<< "createRenderTarget must be called outside of a render pass.";
|
||||
// Clamp sample count to what the device supports.
|
||||
@@ -659,14 +644,14 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
|
||||
void MetalDriver::createFenceR(Handle<HwFence> fh, utils::CString tag) {
|
||||
auto* fence = handle_cast<MetalFence>(fh);
|
||||
fence->encode();
|
||||
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
// TODO: support MSAA swapchain
|
||||
|
||||
if (UTILS_UNLIKELY(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER)) {
|
||||
@@ -683,18 +668,18 @@ void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
}
|
||||
|
||||
void MetalDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
|
||||
uint32_t width, uint32_t height, uint64_t flags, utils::CString&& tag) {
|
||||
uint32_t width, uint32_t height, uint64_t flags, utils::CString tag) {
|
||||
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, width, height, flags);
|
||||
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
|
||||
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::CString tag) {
|
||||
// TODO: Ensure sync is active, and then invoke and clear all pending
|
||||
// callbacks.
|
||||
mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {
|
||||
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
mHandleAllocator.associateTagToHandle(tqh.getId(), std::move(tag));
|
||||
}
|
||||
@@ -733,7 +718,7 @@ const char* toString(DescriptorFlags flags) {
|
||||
}
|
||||
|
||||
void MetalDriver::createDescriptorSetLayoutR(
|
||||
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info, utils::CString&& tag) {
|
||||
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info, utils::CString tag) {
|
||||
#if FILAMENT_METAL_DEBUG_LOG == 1
|
||||
const char* labelStr = "";
|
||||
std::visit([&labelStr](auto&& arg) {
|
||||
@@ -758,11 +743,10 @@ void MetalDriver::createDescriptorSetLayoutR(
|
||||
}
|
||||
|
||||
void MetalDriver::createDescriptorSetR(
|
||||
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh, utils::CString&& tag) {
|
||||
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh, utils::CString tag) {
|
||||
DEBUG_LOG("createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId());
|
||||
MetalDescriptorSetLayout* layout = handle_cast<MetalDescriptorSetLayout>(dslh);
|
||||
MetalDescriptorSet* ds = construct_handle<MetalDescriptorSet>(dsh, layout);
|
||||
ds->setLabel(tag);
|
||||
construct_handle<MetalDescriptorSet>(dsh, layout);
|
||||
mHandleAllocator.associateTagToHandle(dsh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
@@ -2217,38 +2201,6 @@ void MetalDriver::executeDeferredOps() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
MemoryMappedBufferHandle MetalDriver::mapBufferS() noexcept {
|
||||
return alloc_handle<MetalMemoryMappedBuffer>();
|
||||
}
|
||||
|
||||
void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
|
||||
BufferObjectHandle boh, size_t offset,
|
||||
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
|
||||
construct_handle<MetalMemoryMappedBuffer>(mmbh, boh, offset, size, access);
|
||||
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
|
||||
if (UTILS_UNLIKELY(!mmbh)) {
|
||||
return;
|
||||
}
|
||||
destruct_handle<MetalMemoryMappedBuffer>(mmbh);
|
||||
}
|
||||
|
||||
void MetalDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
|
||||
BufferDescriptor&& data) {
|
||||
auto mmb = handle_cast<MetalMemoryMappedBuffer>(mmbh);
|
||||
|
||||
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
|
||||
assert_invariant(offset + data.size <= mmb->size);
|
||||
|
||||
// TODO: this isa zero-effort implementation of copyToMemoryMappedBuffer(), where we just
|
||||
// call updateBufferObject(). This could be a fallback implementation for when
|
||||
// shared memory is not available.
|
||||
// On UMA systems, this should just be a memcpy into the memory-mapped buffer.
|
||||
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<MetalDriver>;
|
||||
|
||||
|
||||
@@ -451,38 +451,6 @@ inline MTLTextureSwizzleChannels getSwizzleChannels(TextureSwizzle r, TextureSwi
|
||||
getSwizzle(a));
|
||||
}
|
||||
|
||||
inline const char* stringifyMTLCommandBufferError(MTLCommandBufferError error) {
|
||||
#if !defined(FILAMENT_IOS)
|
||||
if (error == MTLCommandBufferErrorDeviceRemoved) {
|
||||
return "MTLCommandBufferErrorDeviceRemoved";
|
||||
}
|
||||
#endif
|
||||
switch (error) {
|
||||
case MTLCommandBufferErrorNone:
|
||||
return "MTLCommandBufferErrorNone";
|
||||
case MTLCommandBufferErrorInternal:
|
||||
return "MTLCommandBufferErrorInternal";
|
||||
case MTLCommandBufferErrorTimeout:
|
||||
return "MTLCommandBufferErrorTimeout";
|
||||
case MTLCommandBufferErrorPageFault:
|
||||
return "MTLCommandBufferErrorPageFault";
|
||||
case MTLCommandBufferErrorAccessRevoked:
|
||||
return "MTLCommandBufferErrorAccessRevoked";
|
||||
case MTLCommandBufferErrorNotPermitted:
|
||||
return "MTLCommandBufferErrorNotPermitted";
|
||||
case MTLCommandBufferErrorOutOfMemory:
|
||||
return "MTLCommandBufferErrorOutOfMemory";
|
||||
case MTLCommandBufferErrorInvalidResource:
|
||||
return "MTLCommandBufferErrorInvalidResource";
|
||||
case MTLCommandBufferErrorMemoryless:
|
||||
return "MTLCommandBufferErrorMemoryless";
|
||||
case MTLCommandBufferErrorStackOverflow:
|
||||
return "MTLCommandBufferErrorStackOverflow";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_DRIVER_METALFLAGS_H
|
||||
#define TNT_FILAMENT_DRIVER_METALFLAGS_H
|
||||
|
||||
#ifndef FILAMENT_METAL_DEBUG_LABELS
|
||||
#ifndef NDEBUG
|
||||
#define FILAMENT_METAL_DEBUG_LABELS 1
|
||||
#else
|
||||
#define FILAMENT_METAL_DEBUG_LABELS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "MetalContext.h"
|
||||
#include "MetalEnums.h"
|
||||
#include "MetalExternalImage.h"
|
||||
#include "MetalFlags.h"
|
||||
#include "MetalState.h" // for MetalState::VertexDescription
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -267,16 +266,6 @@ public:
|
||||
|
||||
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
|
||||
|
||||
void setLabel(const utils::CString& label) {
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
if (label.empty()) {
|
||||
return;
|
||||
}
|
||||
texture.label = @(label.c_str_safe());
|
||||
swizzledTextureView.label = @(label.c_str_safe());
|
||||
#endif
|
||||
}
|
||||
|
||||
MetalContext& context;
|
||||
|
||||
// A "sidecar" texture used to implement automatic MSAA resolve.
|
||||
@@ -494,15 +483,6 @@ struct MetalDescriptorSet : public HwDescriptorSet {
|
||||
|
||||
void finalize(MetalDriver* driver);
|
||||
|
||||
void setLabel(const utils::CString& l) {
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
if (l.empty()) {
|
||||
return;
|
||||
}
|
||||
label = l;
|
||||
#endif
|
||||
}
|
||||
|
||||
id<MTLBuffer> finalizeAndGetBuffer(MetalDriver* driver, ShaderStage stage);
|
||||
|
||||
MetalDescriptorSetLayout* layout;
|
||||
@@ -525,22 +505,6 @@ struct MetalDescriptorSet : public HwDescriptorSet {
|
||||
std::vector<std::shared_ptr<MetalExternalImage>> externalImages;
|
||||
|
||||
std::array<TrackedMetalBuffer, Program::SHADER_TYPE_COUNT> cachedBuffer = { nil };
|
||||
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
utils::CString label;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct MetalMemoryMappedBuffer : public HwMemoryMappedBuffer {
|
||||
MetalMemoryMappedBuffer(BufferObjectHandle boh, size_t const offset,
|
||||
size_t const size, MapBufferAccessFlags const access)
|
||||
: boh(boh), access(access), size(size), offset(offset) {
|
||||
}
|
||||
BufferObjectHandle boh{};
|
||||
MapBufferAccessFlags access{};
|
||||
uint32_t size = 0;
|
||||
uint32_t offset = 0;
|
||||
};
|
||||
|
||||
} // namespace backend
|
||||
|
||||
@@ -295,12 +295,6 @@ void MetalSwapChain::ensureDepthStencilTexture() {
|
||||
|
||||
void MetalSwapChain::setFrameScheduledCallback(
|
||||
CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags) {
|
||||
if (!callback) {
|
||||
frameScheduled.handler = nullptr;
|
||||
frameScheduled.callback.reset();
|
||||
frameScheduled.flags = 0;
|
||||
return;
|
||||
}
|
||||
frameScheduled.handler = handler;
|
||||
frameScheduled.callback = std::make_shared<FrameScheduledCallback>(std::move(callback));
|
||||
frameScheduled.flags = flags;
|
||||
@@ -1492,11 +1486,6 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
|
||||
buffer = { [context.device newBufferWithLength:encoder.encodedLength
|
||||
options:MTLResourceStorageModeShared],
|
||||
TrackedMetalBuffer::Type::DESCRIPTOR_SET };
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
if (!label.empty()) {
|
||||
buffer.get().label = @(label.c_str_safe());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
[encoder setArgumentBuffer:buffer.get() offset:0];
|
||||
|
||||
|
||||
@@ -328,8 +328,8 @@ void NoopDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> re
|
||||
void NoopDriver::commit(Handle<HwSwapChain> sch) {
|
||||
}
|
||||
|
||||
void NoopDriver::setPushConstant(ShaderStage stage, uint8_t index,
|
||||
PushConstantVariant value) {
|
||||
void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
}
|
||||
|
||||
void NoopDriver::insertEventMarker(char const* string) {
|
||||
@@ -353,8 +353,8 @@ void NoopDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void NoopDriver::readBufferSubData(BufferObjectHandle boh,
|
||||
uint32_t offset, uint32_t size, BufferDescriptor&& p) {
|
||||
void NoopDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
@@ -405,31 +405,24 @@ void NoopDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void NoopDriver::updateDescriptorSetBuffer(
|
||||
DescriptorSetHandle dsh,
|
||||
descriptor_binding_t binding,
|
||||
BufferObjectHandle boh,
|
||||
backend::DescriptorSetHandle dsh,
|
||||
backend::descriptor_binding_t binding,
|
||||
backend::BufferObjectHandle boh,
|
||||
uint32_t offset,
|
||||
uint32_t size) {
|
||||
}
|
||||
|
||||
void NoopDriver::updateDescriptorSetTexture(
|
||||
DescriptorSetHandle dsh,
|
||||
descriptor_binding_t binding,
|
||||
TextureHandle th,
|
||||
backend::DescriptorSetHandle dsh,
|
||||
backend::descriptor_binding_t binding,
|
||||
backend::TextureHandle th,
|
||||
SamplerParams params) {
|
||||
}
|
||||
|
||||
void NoopDriver::bindDescriptorSet(
|
||||
DescriptorSetHandle dsh,
|
||||
descriptor_set_t set,
|
||||
DescriptorSetOffsetArray&& offsets) {
|
||||
}
|
||||
|
||||
void NoopDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
|
||||
}
|
||||
|
||||
void NoopDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
|
||||
BufferDescriptor&& data) {
|
||||
backend::DescriptorSetHandle dsh,
|
||||
backend::descriptor_set_t set,
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -40,12 +40,10 @@ struct GLBufferObject : public HwBufferObject {
|
||||
GLenum binding;
|
||||
void* buffer;
|
||||
};
|
||||
} gl{};
|
||||
BufferUsage usage : 4;
|
||||
BufferObjectBinding bindingType : 4;
|
||||
uint8_t mappingCount = 0;
|
||||
} gl;
|
||||
BufferUsage usage;
|
||||
BufferObjectBinding bindingType;
|
||||
uint16_t age = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -1,154 +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 "GLMemoryMappedBuffer.h"
|
||||
|
||||
#include "GLBufferObject.h"
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "OpenGLDriver.h"
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <private/backend/HandleAllocator.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
GLMemoryMappedBuffer::GLMemoryMappedBuffer() = default;
|
||||
|
||||
GLMemoryMappedBuffer::~GLMemoryMappedBuffer() = default;
|
||||
|
||||
GLMemoryMappedBuffer::GLMemoryMappedBuffer(OpenGLContext& glc, HandleAllocatorGL& handleAllocator,
|
||||
BufferObjectHandle boh,
|
||||
size_t const offset, size_t const size, MapBufferAccessFlags const access)
|
||||
: boh(boh), access(access) {
|
||||
|
||||
GLBufferObject* const bo = handleAllocator.handle_cast<GLBufferObject*>(boh);
|
||||
|
||||
assert_invariant(bo);
|
||||
assert_invariant(bo->mappingCount < std::numeric_limits<uint8_t>::max());
|
||||
assert_invariant(offset + size <= bo->byteCount);
|
||||
|
||||
if (any(access & MapBufferAccessFlags::WRITE_BIT)) {
|
||||
assert_invariant(any(bo->usage & BufferUsage::SHARED_WRITE_BIT));
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(glc.isES2())) {
|
||||
gl.vaddr = static_cast<char*>(bo->gl.buffer) + offset;
|
||||
gl.size = size;
|
||||
gl.offset = offset;
|
||||
gl.binding = 0; // in ES2 mode, bo->gl.binding is not available
|
||||
gl.id = bo->gl.id;
|
||||
bo->age++; // technically we could do this only in copy()
|
||||
bo->mappingCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
void* addr = nullptr;
|
||||
|
||||
# if !defined(__EMSCRIPTEN__)
|
||||
// unsynchronized is implied (but is incompatible with reading)
|
||||
GLbitfield gl_access = GL_MAP_UNSYNCHRONIZED_BIT;
|
||||
if (any(access & MapBufferAccessFlags::WRITE_BIT)) {
|
||||
gl_access |= GL_MAP_WRITE_BIT;
|
||||
}
|
||||
if (any(access & MapBufferAccessFlags::INVALIDATE_RANGE_BIT)) {
|
||||
// note: GL_MAP_INVALIDATE_RANGE_BIT is incompatible with GL_MAP_READ_BIT
|
||||
gl_access |= GL_MAP_INVALIDATE_RANGE_BIT;
|
||||
}
|
||||
|
||||
glc.bindBuffer(bo->gl.binding, bo->gl.id);
|
||||
|
||||
addr = glMapBufferRange(bo->gl.binding, GLsizeiptr(offset), GLsizeiptr(size), gl_access);
|
||||
|
||||
CHECK_GL_ERROR();
|
||||
# endif
|
||||
|
||||
// if we failed here, addr would be nullptr
|
||||
gl.vaddr = addr;
|
||||
gl.size = size;
|
||||
gl.offset = offset;
|
||||
gl.binding = bo->gl.binding;
|
||||
gl.id = bo->gl.id;
|
||||
bo->mappingCount++;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLMemoryMappedBuffer::unmap(OpenGLContext& glc, HandleAllocatorGL& handleAllocator) const {
|
||||
GLBufferObject* const bo = handleAllocator.handle_cast<GLBufferObject*>(boh);
|
||||
assert_invariant(bo);
|
||||
assert_invariant(bo->mappingCount > 0);
|
||||
|
||||
bo->mappingCount--;
|
||||
|
||||
if (UTILS_UNLIKELY(glc.isES2())) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
glc.bindBuffer(gl.binding, gl.id);
|
||||
# if !defined(__EMSCRIPTEN__)
|
||||
// don't unmap if we don't have a mapping or it didn't work
|
||||
if (UTILS_LIKELY(gl.vaddr)) {
|
||||
if (UTILS_UNLIKELY(glUnmapBuffer(gl.binding) == GL_FALSE)) {
|
||||
// TODO: According to the spec, UnmapBuffer can return FALSE in rare conditions (e.g.
|
||||
// during a screen mode change). Note that this is not a GL error, but the whole
|
||||
// mapping is lost. This is very problematic for us here.
|
||||
}
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLMemoryMappedBuffer::copy(OpenGLContext& glc, OpenGLDriver& gld,
|
||||
size_t const offset, BufferDescriptor&& data) const {
|
||||
assert_invariant(any(access & MapBufferAccessFlags::WRITE_BIT));
|
||||
assert_invariant(offset + data.size <= gl.size);
|
||||
|
||||
if (UTILS_LIKELY(gl.vaddr)) {
|
||||
memcpy(static_cast<char *>(gl.vaddr) + offset, data.buffer, data.size);
|
||||
} else {
|
||||
assert_invariant(!glc.isES2());
|
||||
// we couldn't map (WebGL or error), revert to glBufferSubData. In the future we can
|
||||
// improve this by keeping around the BufferDescriptor and coalescing the calls
|
||||
// to glBufferSubData.
|
||||
glc.bindBuffer(gl.binding, gl.id);
|
||||
glBufferSubData(gl.binding, GLintptr(gl.offset + offset), GLsizeiptr(data.size), data.buffer);
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
gld.scheduleDestroy(std::move(data));
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include <private/backend/HandleAllocator.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class OpenGLContext;
|
||||
class OpenGLDriver;
|
||||
class BufferDescriptor;
|
||||
|
||||
struct GLBufferObject;
|
||||
|
||||
struct GLMemoryMappedBuffer : public HwMemoryMappedBuffer {
|
||||
BufferObjectHandle boh;
|
||||
MapBufferAccessFlags access;
|
||||
struct {
|
||||
void* vaddr = nullptr;
|
||||
uint32_t size = 0;
|
||||
uint32_t offset = 0;
|
||||
GLenum binding = 0;
|
||||
GLuint id = 0;
|
||||
} gl;
|
||||
|
||||
GLMemoryMappedBuffer();
|
||||
|
||||
GLMemoryMappedBuffer(OpenGLContext& glc, HandleAllocatorGL& handleAllocator,
|
||||
BufferObjectHandle boh, size_t offset, size_t size, MapBufferAccessFlags access);
|
||||
|
||||
~GLMemoryMappedBuffer();
|
||||
|
||||
void unmap(OpenGLContext& gl, HandleAllocatorGL& handleAllocator) const;
|
||||
|
||||
void copy(OpenGLContext& glc, OpenGLDriver& gld,
|
||||
size_t offset, BufferDescriptor&& data) const;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif //TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
|
||||
@@ -21,14 +21,12 @@
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -103,8 +101,6 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
initBugs(&bugs, ext, state.major, state.minor,
|
||||
state.vendor, state.renderer, state.version, state.shader);
|
||||
|
||||
initWorkarounds(bugs, &ext);
|
||||
|
||||
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
|
||||
@@ -563,22 +559,13 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
}
|
||||
|
||||
if (strstr(vendor, "Mesa")) {
|
||||
if (strstr(renderer, "llvmpipe")) {
|
||||
// Seen on
|
||||
// [Mesa],
|
||||
// [llvmpipe (LLVM 17.0.6, 256 bits)],
|
||||
// [4.5 (Core Profile) Mesa 24.0.6-1],
|
||||
// [4.50]
|
||||
// not known which version are affected
|
||||
bugs->rebind_buffer_after_deletion = true;
|
||||
|
||||
// Seen on
|
||||
// [Mesa]
|
||||
// [llvmpipe (LLVM 17.0.6, 256 bits)]
|
||||
// [4.5 (Core Profile) Mesa 24.2.1 (git-c222f7299c)]
|
||||
// [4.50]
|
||||
bugs->disable_framebuffer_fetch_extension = true;
|
||||
}
|
||||
// Seen on
|
||||
// [Mesa],
|
||||
// [llvmpipe (LLVM 17.0.6, 256 bits)],
|
||||
// [4.5 (Core Profile) Mesa 24.0.6-1],
|
||||
// [4.50]
|
||||
// not known which version are affected
|
||||
bugs->rebind_buffer_after_deletion = true;
|
||||
}
|
||||
} else {
|
||||
// When running under ANGLE, it's a different set of workaround that we need.
|
||||
@@ -624,12 +611,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLContext::initWorkarounds(Bugs const& bugs, Extensions* ext) {
|
||||
if (bugs.disable_framebuffer_fetch_extension) {
|
||||
ext->EXT_shader_framebuffer_fetch = false;
|
||||
}
|
||||
}
|
||||
|
||||
FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor,
|
||||
Extensions const& exts,
|
||||
Gets const& gets,
|
||||
|
||||
@@ -334,12 +334,6 @@ public:
|
||||
// those particular browsers.
|
||||
bool disable_depth_precache_for_default_material;
|
||||
|
||||
// On llvmpipe (mesa), enabling framebuffer fetch causes a crash in draw2
|
||||
// 'OpenGL error 0x502 (GL_INVALID_OPERATION) in "draw2" at line 4389'
|
||||
// This coincides with the use of framebuffer fetch (ColorGradingAsSubpass). We disable
|
||||
// framebuffer fetch in the case of llvmpipe.
|
||||
bool disable_framebuffer_fetch_extension;
|
||||
|
||||
} bugs = {};
|
||||
|
||||
// state getters -- as needed.
|
||||
@@ -583,9 +577,6 @@ private:
|
||||
{ bugs.disable_depth_precache_for_default_material,
|
||||
"disable_depth_precache_for_default_material",
|
||||
""},
|
||||
{ bugs.disable_framebuffer_fetch_extension,
|
||||
"disable_framebuffer_fetch_extension",
|
||||
""},
|
||||
}};
|
||||
|
||||
// this is chosen to minimize code size
|
||||
@@ -616,8 +607,6 @@ private:
|
||||
static void initProcs(Procs* procs,
|
||||
Extensions const& exts, GLint major, GLint minor) noexcept;
|
||||
|
||||
static void initWorkarounds(Bugs const& bugs, Extensions* ext);
|
||||
|
||||
static FeatureLevel resolveFeatureLevel(GLint major, GLint minor,
|
||||
Extensions const& exts,
|
||||
Gets const& gets,
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "GLBufferObject.h"
|
||||
#include "GLDescriptorSet.h"
|
||||
#include "GLDescriptorSetLayout.h"
|
||||
#include "GLMemoryMappedBuffer.h"
|
||||
#include "GLTexture.h"
|
||||
#include "ShaderCompilerService.h"
|
||||
|
||||
@@ -103,7 +102,7 @@ public:
|
||||
bool rec709 = false;
|
||||
struct {
|
||||
CallbackHandler* handler = nullptr;
|
||||
std::shared_ptr<FrameScheduledCallback> callback = nullptr;
|
||||
FrameScheduledCallback callback;
|
||||
} frameScheduled;
|
||||
};
|
||||
|
||||
@@ -152,8 +151,6 @@ public:
|
||||
|
||||
using GLDescriptorSet = filament::backend::GLDescriptorSet;
|
||||
|
||||
using GLMemoryMappedBuffer = filament::backend::GLMemoryMappedBuffer;
|
||||
|
||||
struct GLStream : public HwStream {
|
||||
using HwStream::HwStream;
|
||||
struct Info {
|
||||
@@ -217,8 +214,6 @@ public:
|
||||
OpenGLDriver(OpenGLDriver const&) = delete;
|
||||
OpenGLDriver& operator=(OpenGLDriver const&) = delete;
|
||||
|
||||
using DriverBase::scheduleDestroy;
|
||||
|
||||
private:
|
||||
OpenGLPlatform& mPlatform;
|
||||
OpenGLContext mContext;
|
||||
|
||||
@@ -322,7 +322,6 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& drive
|
||||
|
||||
mCurrentContextType = ContextType::UNPROTECTED;
|
||||
mContextAttribs = std::move(contextAttribs);
|
||||
mMSAA4XSupport = checkIfMSAASwapChainSupported(4);
|
||||
|
||||
initializeGlExtensions();
|
||||
|
||||
@@ -492,11 +491,34 @@ bool PlatformEGL::isMSAASwapChainSupported(uint32_t samples) const noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (samples == 4) {
|
||||
return mMSAA4XSupport;
|
||||
Config configAttribs = {
|
||||
{ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT },
|
||||
{ EGL_RED_SIZE, 8 },
|
||||
{ EGL_GREEN_SIZE, 8 },
|
||||
{ EGL_BLUE_SIZE, 8 },
|
||||
{ EGL_DEPTH_SIZE, 24 },
|
||||
{ EGL_SAMPLE_BUFFERS, 1 },
|
||||
{ EGL_SAMPLES, samples },
|
||||
};
|
||||
|
||||
if (!ext.egl.KHR_no_config_context) {
|
||||
if (isOpenGL()) {
|
||||
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_BIT;
|
||||
} else {
|
||||
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT;
|
||||
if (ext.egl.KHR_create_context) {
|
||||
configAttribs[EGL_RENDERABLE_TYPE] |= EGL_OPENGL_ES3_BIT_KHR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
EGLint configsCount;
|
||||
if (!eglChooseConfig(mEGLDisplay, configAttribs.data(), &config, 1, &configsCount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return configsCount > 0;
|
||||
}
|
||||
|
||||
Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
@@ -816,38 +838,6 @@ EGLContext PlatformEGL::getContextForType(ContextType type) const noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
bool PlatformEGL::checkIfMSAASwapChainSupported(uint32_t samples) const noexcept {
|
||||
// Retrieve the config to see if the given number of samples is supported. The result is cached.
|
||||
Config configAttribs = {
|
||||
{ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT },
|
||||
{ EGL_RED_SIZE, 8 },
|
||||
{ EGL_GREEN_SIZE, 8 },
|
||||
{ EGL_BLUE_SIZE, 8 },
|
||||
{ EGL_DEPTH_SIZE, 24 },
|
||||
{ EGL_SAMPLE_BUFFERS, 1 },
|
||||
{ EGL_SAMPLES, (EGLint)samples },
|
||||
};
|
||||
|
||||
if (!ext.egl.KHR_no_config_context) {
|
||||
if (isOpenGL()) {
|
||||
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_BIT;
|
||||
} else {
|
||||
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT;
|
||||
if (ext.egl.KHR_create_context) {
|
||||
configAttribs[EGL_RENDERABLE_TYPE] |= EGL_OPENGL_ES3_BIT_KHR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EGLConfig config = EGL_NO_CONFIG_KHR;
|
||||
EGLint configsCount;
|
||||
if (!eglChooseConfig(mEGLDisplay, configAttribs.data(), &config, 1, &configsCount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return configsCount > 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
|
||||
PlatformEGL::Config::Config() = default;
|
||||
|
||||
@@ -95,7 +95,11 @@ io::ostream& operator<<(io::ostream& out, ElementType type) {
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, BufferUsage usage) {
|
||||
return out << io::hex << uint32_t(usage) << io::dec;
|
||||
switch (usage) {
|
||||
CASE(BufferUsage, STATIC)
|
||||
CASE(BufferUsage, DYNAMIC)
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, CullingMode mode) {
|
||||
|
||||
@@ -107,11 +107,8 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice
|
||||
.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO,
|
||||
.handleTypes = context.getFenceExportFlags()
|
||||
};
|
||||
fenceCreateInfo.pNext = &exportFenceCreateInfo;
|
||||
|
||||
// Necessary to guard this. Otherwise, swiftshader would throw an error.
|
||||
if (context.getFenceExportFlags()) {
|
||||
fenceCreateInfo.pNext = &exportFenceCreateInfo;
|
||||
}
|
||||
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
|
||||
}
|
||||
|
||||
|
||||
@@ -476,21 +476,6 @@ void VulkanDriver::updateDescriptorSetTexture(
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
|
||||
BufferDescriptor&& data) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto mmb = resource_ptr<VulkanMemoryMappedBuffer>::cast(&mResourceManager, mmbh);
|
||||
|
||||
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
|
||||
assert_invariant(offset + data.size <= mmb->size);
|
||||
|
||||
// TODO: this isa zero-effort implementation of copyToMemoryMappedBuffer(), where we just
|
||||
// call updateBufferObject(). This could be a fallback implementation for when
|
||||
// shared memory is not available.
|
||||
// On UMA systems, this should just be a memcpy into the memory-mapped buffer.
|
||||
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
|
||||
}
|
||||
|
||||
void VulkanDriver::flush(int) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
endCommandRecording();
|
||||
@@ -517,7 +502,7 @@ void VulkanDriver::finish(int dummy) {
|
||||
|
||||
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt, utils::CString&& tag) {
|
||||
PrimitiveType pt, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
|
||||
@@ -536,7 +521,7 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) {
|
||||
uint8_t attributeCount, AttributeArray attributes, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::make(&mResourceManager, vbih, bufferCount,
|
||||
attributeCount, attributes);
|
||||
@@ -554,7 +539,7 @@ void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
|
||||
Handle<HwVertexBufferInfo> vbih, utils::CString&& tag) {
|
||||
Handle<HwVertexBufferInfo> vbih, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
|
||||
auto vb = resource_ptr<VulkanVertexBuffer>::make(&mResourceManager, vbh, mContext, mStagePool,
|
||||
@@ -573,7 +558,7 @@ void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
uint32_t indexCount, BufferUsage usage, utils::CString&& tag) {
|
||||
uint32_t indexCount, BufferUsage usage, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto elementSize = (uint8_t) getElementTypeSize(elementType);
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mContext, mAllocator,
|
||||
@@ -592,7 +577,7 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) {
|
||||
BufferObjectBinding bindingType, BufferUsage usage, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mContext, mAllocator,
|
||||
mStagePool, mBufferCache, byteCount, bindingType);
|
||||
@@ -611,7 +596,7 @@ void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
||||
|
||||
void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage, utils::CString&& tag) {
|
||||
TextureUsage usage, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mResourceManager, &mCommands,
|
||||
@@ -628,7 +613,7 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) {
|
||||
uint8_t baseLevel, uint8_t levelCount, utils::CString tag) {
|
||||
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
|
||||
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, baseLevel,
|
||||
@@ -639,7 +624,7 @@ void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> sr
|
||||
|
||||
void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a, utils::CString&& tag) {
|
||||
backend::TextureSwizzle a, utils::CString tag) {
|
||||
TextureSwizzle const swizzleArray[] = { r, g, b, a };
|
||||
VkComponentMapping const swizzle = fvkutils::getSwizzleMap(swizzleArray);
|
||||
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
|
||||
@@ -651,7 +636,7 @@ void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
|
||||
|
||||
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage, utils::CString&& tag) {
|
||||
Platform::ExternalImageHandleRef externalImage, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
|
||||
|
||||
@@ -702,7 +687,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
||||
|
||||
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* externalImage, utils::CString&& tag) {
|
||||
void* externalImage, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
// not supported in this backend
|
||||
@@ -710,14 +695,14 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
|
||||
|
||||
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane, utils::CString&& tag) {
|
||||
void* image, uint32_t plane, utils::CString tag) {
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
}
|
||||
|
||||
void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage, utils::CString&& tag) {
|
||||
TextureUsage usage, utils::CString tag) {
|
||||
// not supported in this backend
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
mResourceManager.associateHandle(th.getId(), std::move(tag));
|
||||
@@ -733,7 +718,7 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
mExternalImageManager.removeExternallySampledTexture(texture);
|
||||
}
|
||||
|
||||
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils::CString&& tag) {
|
||||
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vprogram = resource_ptr<VulkanProgram>::make(&mResourceManager, ph, mPlatform->getDevice(),
|
||||
program);
|
||||
@@ -749,7 +734,7 @@ void VulkanDriver::destroyProgram(Handle<HwProgram> ph) {
|
||||
vprogram.dec();
|
||||
}
|
||||
|
||||
void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString&& tag) {
|
||||
void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString tag) {
|
||||
assert_invariant(mDefaultRenderTarget); // Default render target should already exist.
|
||||
|
||||
auto renderTarget = resource_ptr<VulkanRenderTarget>::make(&mResourceManager, rth,
|
||||
@@ -762,7 +747,7 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils:
|
||||
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
|
||||
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
|
||||
@@ -839,7 +824,7 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
|
||||
void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString tag) {
|
||||
VulkanCommandBuffer* cmdbuf;
|
||||
if (mCurrentRenderPass.commandBuffer) {
|
||||
cmdbuf = mCurrentRenderPass.commandBuffer;
|
||||
@@ -854,7 +839,7 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
|
||||
mResourceManager.associateHandle(fh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
|
||||
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::CString tag) {
|
||||
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
|
||||
VkFence fence = VK_NULL_HANDLE;
|
||||
std::shared_ptr<VulkanCmdFence> fenceStatus;
|
||||
@@ -885,7 +870,7 @@ void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
// Running gc() to guard against an edge case where the old swapchains need to have been
|
||||
// destroyed before the new swapchain can be created. Otherwise, we would fail
|
||||
@@ -914,7 +899,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
}
|
||||
|
||||
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
uint32_t height, uint64_t flags, utils::CString&& tag) {
|
||||
uint32_t height, uint64_t flags, utils::CString tag) {
|
||||
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
|
||||
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
@@ -927,13 +912,13 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
|
||||
mResourceManager.associateHandle(sch.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {
|
||||
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
mResourceManager.associateHandle(tqh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
||||
backend::DescriptorSetLayout&& info, utils::CString&& tag) {
|
||||
backend::DescriptorSetLayout&& info, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
|
||||
layout.inc();
|
||||
@@ -941,7 +926,7 @@ void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh
|
||||
}
|
||||
|
||||
void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
Handle<HwDescriptorSetLayout> dslh, utils::CString&& tag) {
|
||||
Handle<HwDescriptorSetLayout> dslh, utils::CString tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
||||
@@ -955,16 +940,6 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
mResourceManager.associateHandle(dsh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
|
||||
BufferObjectHandle boh, size_t offset,
|
||||
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto mmb = resource_ptr<VulkanMemoryMappedBuffer>::make(&mResourceManager, mmbh,
|
||||
boh, offset, size, access);
|
||||
mmb.inc();
|
||||
mResourceManager.associateHandle(mmbh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanVertexBufferInfo>();
|
||||
}
|
||||
@@ -1063,19 +1038,6 @@ Handle<HwDescriptorSet> VulkanDriver::createDescriptorSetS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanDescriptorSet>();
|
||||
}
|
||||
|
||||
MemoryMappedBufferHandle VulkanDriver::mapBufferS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanMemoryMappedBuffer>();
|
||||
}
|
||||
|
||||
void VulkanDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
|
||||
if (!mmbh) {
|
||||
return;
|
||||
}
|
||||
auto mmb = resource_ptr<VulkanMemoryMappedBuffer>::cast(&mResourceManager, mmbh);
|
||||
mmb.dec();
|
||||
}
|
||||
|
||||
|
||||
void VulkanDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
||||
if (!sch) {
|
||||
return;
|
||||
|
||||
@@ -225,17 +225,6 @@ private:
|
||||
fvkutils::UniformBufferBitmask mUboMask;
|
||||
};
|
||||
|
||||
struct VulkanMemoryMappedBuffer : public HwMemoryMappedBuffer, Resource {
|
||||
VulkanMemoryMappedBuffer(BufferObjectHandle boh, size_t const offset,
|
||||
size_t const size, MapBufferAccessFlags const access)
|
||||
: boh(boh), access(access), size(size), offset(offset) {
|
||||
}
|
||||
BufferObjectHandle boh{};
|
||||
MapBufferAccessFlags access{};
|
||||
uint32_t size = 0;
|
||||
uint32_t offset = 0;
|
||||
};
|
||||
|
||||
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
|
||||
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
|
||||
|
||||
|
||||
@@ -123,9 +123,9 @@ void VulkanSwapChain::present(DriverBase& driver) {
|
||||
|
||||
if (frameScheduled.callback) {
|
||||
driver.scheduleCallback(frameScheduled.handler,
|
||||
[callback = frameScheduled.callback]() {
|
||||
[callback = std::move(frameScheduled.callback)]() {
|
||||
PresentCallable noop = PresentCallable(PresentCallable::noopPresent, nullptr);
|
||||
callback->operator()(noop);
|
||||
callback(noop);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
@@ -83,13 +82,8 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
|
||||
|
||||
inline void setFrameScheduledCallback(CallbackHandler* handler,
|
||||
FrameScheduledCallback&& callback) noexcept {
|
||||
if (!callback) {
|
||||
frameScheduled.handler = nullptr;
|
||||
frameScheduled.callback.reset();
|
||||
return;
|
||||
}
|
||||
frameScheduled.handler = handler;
|
||||
frameScheduled.callback = std::make_shared<FrameScheduledCallback>(std::move(callback));
|
||||
frameScheduled.callback = std::move(callback);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -110,7 +104,7 @@ private:
|
||||
// These fields store a callback to notify the client that Filament is commiting a frame.
|
||||
struct {
|
||||
CallbackHandler* handler = nullptr;
|
||||
std::shared_ptr<FrameScheduledCallback> callback = nullptr;
|
||||
FrameScheduledCallback callback;
|
||||
} frameScheduled;
|
||||
|
||||
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
|
||||
|
||||
@@ -39,7 +39,6 @@ template ResourceType getTypeEnum<VulkanDescriptorSet>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanFence>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanBuffer>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanSync>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
|
||||
|
||||
template<typename D>
|
||||
ResourceType getTypeEnum() noexcept {
|
||||
@@ -97,9 +96,6 @@ ResourceType getTypeEnum() noexcept {
|
||||
if constexpr (std::is_same_v<D, VulkanSync>) {
|
||||
return ResourceType::SYNC;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanMemoryMappedBuffer>) {
|
||||
return ResourceType::MEMORY_MAPPED_BUFFER;
|
||||
}
|
||||
return ResourceType::UNDEFINED_TYPE;
|
||||
}
|
||||
|
||||
@@ -141,8 +137,6 @@ std::string_view getTypeStr(ResourceType type) {
|
||||
return "VulkanBuffer";
|
||||
case ResourceType::SYNC:
|
||||
return "Sync";
|
||||
case ResourceType::MEMORY_MAPPED_BUFFER:
|
||||
return "VulkanMemoryMappedBuffer";
|
||||
case ResourceType::UNDEFINED_TYPE:
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -53,8 +53,7 @@ enum class ResourceType : uint8_t {
|
||||
STAGE_SEGMENT = 15,
|
||||
STAGE_IMAGE = 16,
|
||||
SYNC = 17,
|
||||
MEMORY_MAPPED_BUFFER = 18,
|
||||
UNDEFINED_TYPE = 19, // Must be the last enum because we use it for iterating over the enums.
|
||||
UNDEFINED_TYPE = 18, // Must be the last enum because we use it for iterating over the enums.
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
|
||||
@@ -117,9 +117,6 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
|
||||
case ResourceType::SYNC:
|
||||
destruct<VulkanSync>(Handle<VulkanSync>(id));
|
||||
break;
|
||||
case ResourceType::MEMORY_MAPPED_BUFFER:
|
||||
destruct<VulkanMemoryMappedBuffer>(Handle<VulkanMemoryMappedBuffer>(id));
|
||||
break;
|
||||
case ResourceType::UNDEFINED_TYPE:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
const uint64_t flags, utils::CString&& tag) {
|
||||
const uint64_t flags, utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
|
||||
// TODO: support MSAA swapchain
|
||||
@@ -480,7 +480,7 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
uint32_t height, uint64_t flags, utils::CString&& tag) {
|
||||
uint32_t height, uint64_t flags, utils::CString tag) {
|
||||
wgpu::Extent2D extent = { .width = width, .height = height };
|
||||
mSwapChain = constructHandle<WebGPUSwapChain>(sch, extent, mAdapter,
|
||||
mDevice, flags);
|
||||
@@ -493,7 +493,7 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
|
||||
|
||||
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
|
||||
const uint8_t bufferCount, const uint8_t attributeCount, const AttributeArray attributes,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle, bufferCount, attributeCount,
|
||||
attributes, mDeviceLimits);
|
||||
@@ -502,7 +502,7 @@ void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vertexBuff
|
||||
|
||||
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle,
|
||||
const uint32_t vertexCount, Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto vertexBufferInfo = handleCast<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
|
||||
constructHandle<WebGPUVertexBuffer>(vertexBufferHandle, vertexCount,
|
||||
@@ -512,7 +512,7 @@ void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle
|
||||
|
||||
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
const ElementType elementType, const uint32_t indexCount, const BufferUsage usage,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
const auto elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
|
||||
constructHandle<WebGPUIndexBuffer>(indexBufferHandle, mDevice, elementSize, indexCount);
|
||||
@@ -521,7 +521,7 @@ void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
|
||||
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle,
|
||||
const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUBufferObject>(bufferObjectHandle, mDevice, bindingType, byteCount);
|
||||
setDebugTag(bufferObjectHandle.getId(), std::move(tag));
|
||||
@@ -530,7 +530,7 @@ void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle
|
||||
void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const SamplerType target,
|
||||
const uint8_t levels, const TextureFormat format, const uint8_t samples,
|
||||
const uint32_t width, const uint32_t height, const uint32_t depth,
|
||||
const TextureUsage usage, utils::CString&& tag) {
|
||||
const TextureUsage usage, utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUTexture>(textureHandle, target, levels, format, samples, width, height,
|
||||
depth, usage, mDevice);
|
||||
@@ -539,7 +539,7 @@ void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const Sampler
|
||||
|
||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> textureHandle,
|
||||
Handle<HwTexture> sourceTextureHandle, const uint8_t baseLevel, const uint8_t levelCount,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
auto source = handleCast<WebGPUTexture>(sourceTextureHandle);
|
||||
|
||||
constructHandle<WebGPUTexture>(textureHandle, source, baseLevel, levelCount);
|
||||
@@ -550,7 +550,7 @@ void WebGPUDriver::createTextureViewR(Handle<HwTexture> textureHandle,
|
||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
|
||||
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle r,
|
||||
const backend::TextureSwizzle g, const backend::TextureSwizzle b,
|
||||
const backend::TextureSwizzle a, utils::CString&& tag) {
|
||||
const backend::TextureSwizzle a, utils::CString tag) {
|
||||
|
||||
if (!isTextureSwizzleSupported()) {
|
||||
FWGPU_LOGW << "WebGPUDriver::createTextureViewSwizzleR called while texture swizzling is "
|
||||
@@ -577,7 +577,7 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
|
||||
const backend::SamplerType target, const backend::TextureFormat format,
|
||||
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage, utils::CString&& tag) {
|
||||
Platform::ExternalImageHandleRef externalImage, utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
@@ -585,27 +585,27 @@ void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
|
||||
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> textureHandle,
|
||||
const backend::SamplerType target, const backend::TextureFormat format,
|
||||
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
|
||||
void* externalImage, utils::CString&& tag) {
|
||||
void* externalImage, utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> textureHandle,
|
||||
const backend::TextureFormat format, const uint32_t width, const uint32_t height,
|
||||
const backend::TextureUsage usage, void* image, const uint32_t plane, utils::CString&& tag) {
|
||||
const backend::TextureUsage usage, void* image, const uint32_t plane, utils::CString tag) {
|
||||
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::importTextureR(Handle<HwTexture> textureHandle, const intptr_t id,
|
||||
const SamplerType target, const uint8_t levels, const TextureFormat format,
|
||||
const uint8_t samples, const uint32_t width, const uint32_t height, const uint32_t depth,
|
||||
const TextureUsage usage, utils::CString&& tag) {
|
||||
const TextureUsage usage, utils::CString tag) {
|
||||
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimitiveHandle,
|
||||
Handle<HwVertexBuffer> vertexBufferHandle, Handle<HwIndexBuffer> indexBufferHandle,
|
||||
const PrimitiveType primitiveType, utils::CString&& tag) {
|
||||
const PrimitiveType primitiveType, utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
assert_invariant(mDevice);
|
||||
const auto renderPrimitive = constructHandle<WebGPURenderPrimitive>(renderPrimitiveHandle);
|
||||
@@ -618,14 +618,14 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimit
|
||||
}
|
||||
|
||||
void WebGPUDriver::createProgramR(Handle<HwProgram> programHandle, Program&& program,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUProgram>(programHandle, mDevice, program);
|
||||
setDebugTag(programHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> renderTargetHandle,
|
||||
utils::CString&& tag) {
|
||||
utils::CString tag) {
|
||||
assert_invariant(!mDefaultRenderTarget);
|
||||
mDefaultRenderTarget = constructHandle<WebGPURenderTarget>(renderTargetHandle);
|
||||
assert_invariant(mDefaultRenderTarget);
|
||||
@@ -638,7 +638,7 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> renderTarge
|
||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle,
|
||||
const TargetBufferFlags targetFlags, const uint32_t width, const uint32_t height,
|
||||
const uint8_t samples, const uint8_t layerCount, const MRT color,
|
||||
const TargetBufferInfo depth, const TargetBufferInfo stencil, utils::CString&& tag) {
|
||||
const TargetBufferInfo depth, const TargetBufferInfo stencil, utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPURenderTarget>(
|
||||
renderTargetHandle, width, height, samples, layerCount, color, depth, stencil,
|
||||
@@ -650,7 +650,7 @@ void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle
|
||||
setDebugTag(renderTargetHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, utils::CString&& tag) {
|
||||
void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, utils::CString tag) {
|
||||
// The handle is constructed synchronously in createFenceS.
|
||||
const auto fence = handleCast<WebGPUFence>(fenceHandle);
|
||||
assert_invariant(mQueue);
|
||||
@@ -658,24 +658,24 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, utils::CString&& ta
|
||||
setDebugTag(fenceHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSyncR(Handle<HwSync> syncHandle, utils::CString&& tag) {
|
||||
void WebGPUDriver::createSyncR(Handle<HwSync> syncHandle, utils::CString tag) {
|
||||
// TODO: Ensure sync is active, and then invoke and clear all pending
|
||||
// callbacks.
|
||||
setDebugTag(syncHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {}
|
||||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetLayoutR(
|
||||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle,
|
||||
backend::DescriptorSetLayout&& info, utils::CString&& tag) {
|
||||
backend::DescriptorSetLayout&& info, utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
constructHandle<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle, std::move(info), mDevice);
|
||||
setDebugTag(descriptorSetLayoutHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> descriptorSetHandle,
|
||||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle, utils::CString&& tag) {
|
||||
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle, utils::CString tag) {
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
auto layout = handleCast<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle);
|
||||
constructHandle<WebGPUDescriptorSet>(descriptorSetHandle, layout->getLayout(),
|
||||
@@ -2091,7 +2091,7 @@ void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> descriptorSetHandle
|
||||
.offsets = std::move(offsets) };
|
||||
}
|
||||
|
||||
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString&& tag) {
|
||||
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
//todo
|
||||
}
|
||||
|
||||
@@ -2207,25 +2207,4 @@ wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& f
|
||||
return wgpu::AddressMode::Undefined;
|
||||
}
|
||||
|
||||
MemoryMappedBufferHandle WebGPUDriver::mapBufferS() noexcept {
|
||||
// TODO: MetalDriver::mapBufferS
|
||||
return {};
|
||||
}
|
||||
|
||||
void WebGPUDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
|
||||
BufferObjectHandle boh, size_t offset,
|
||||
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
|
||||
// TODO: MetalDriver::mapBufferR
|
||||
}
|
||||
|
||||
void WebGPUDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
|
||||
// TODO: MetalDriver::unmapBuffer
|
||||
}
|
||||
|
||||
void WebGPUDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
|
||||
BufferDescriptor&& data) {
|
||||
// TODO: MetalDriver::copyToMemoryMappedBuffer
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -68,7 +68,7 @@ private:
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
|
||||
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
|
||||
void setDebugTag(HandleBase::HandleId handleId, utils::CString&& tag);
|
||||
void setDebugTag(HandleBase::HandleId handleId, utils::CString tag);
|
||||
|
||||
// The platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
|
||||
// handled in the WebGPUPlatform.
|
||||
|
||||
@@ -39,12 +39,7 @@ Cleanup::~Cleanup() {
|
||||
for (const auto descriptorSetLayout : mDescriptorSetLayouts) {
|
||||
mDriverApi.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
}
|
||||
for (const auto memoryMappedBuffer : mMemoryMappedBuffers) {
|
||||
// destroy those before buffer objects
|
||||
mDriverApi.unmapBuffer(memoryMappedBuffer);
|
||||
}
|
||||
for (const auto bufferObject : mBufferObjects) {
|
||||
// destroy those after memory mapped buffers
|
||||
mDriverApi.destroyBufferObject(bufferObject);
|
||||
}
|
||||
for (const auto program : mPrograms) {
|
||||
@@ -56,18 +51,6 @@ Cleanup::~Cleanup() {
|
||||
for (const auto renderTarget : mRenderTargets) {
|
||||
mDriverApi.destroyRenderTarget(renderTarget);
|
||||
}
|
||||
for (const auto renderPrimitive : mRenderPrimitives) {
|
||||
mDriverApi.destroyRenderPrimitive(renderPrimitive);
|
||||
}
|
||||
for (const auto vertexBufferInfo : mVertexBufferInfos) {
|
||||
mDriverApi.destroyVertexBufferInfo(vertexBufferInfo);
|
||||
}
|
||||
for (const auto indexBuffer : mIndexBuffers) {
|
||||
mDriverApi.destroyIndexBuffer(indexBuffer);
|
||||
}
|
||||
for (const auto vertexBuffer : mVertexBuffers) {
|
||||
mDriverApi.destroyVertexBuffer(vertexBuffer);
|
||||
}
|
||||
|
||||
for (const auto& callback : mCallbacks) {
|
||||
callback();
|
||||
@@ -103,26 +86,6 @@ template <>
|
||||
void Cleanup::addInternal(filament::backend::RenderTargetHandle handle) {
|
||||
mRenderTargets.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::MemoryMappedBufferHandle handle) {
|
||||
mMemoryMappedBuffers.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::RenderPrimitiveHandle handle) {
|
||||
mRenderPrimitives.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::VertexBufferInfoHandle handle) {
|
||||
mVertexBufferInfos.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::VertexBufferHandle handle) {
|
||||
mVertexBuffers.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::IndexBufferHandle handle) {
|
||||
mIndexBuffers.push_back(handle);
|
||||
}
|
||||
|
||||
void Cleanup::addPostCall(std::function<void()> callback){
|
||||
mCallbacks.emplace_back(std::move(callback));
|
||||
|
||||
@@ -68,11 +68,6 @@ private:
|
||||
std::vector<filament::backend::ProgramHandle> mPrograms;
|
||||
std::vector<filament::backend::TextureHandle> mTextures;
|
||||
std::vector<filament::backend::RenderTargetHandle> mRenderTargets;
|
||||
std::vector<filament::backend::RenderPrimitiveHandle> mRenderPrimitives;
|
||||
std::vector<filament::backend::VertexBufferInfoHandle> mVertexBufferInfos;
|
||||
std::vector<filament::backend::VertexBufferHandle> mVertexBuffers;
|
||||
std::vector<filament::backend::IndexBufferHandle> mIndexBuffers;
|
||||
std::vector<filament::backend::MemoryMappedBufferHandle> mMemoryMappedBuffers;
|
||||
|
||||
std::vector<std::function<void()>> mCallbacks;
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -269,10 +269,6 @@ TEST_F(BlitTest, ColorResolve) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
auto& api = getDriverApi();
|
||||
|
||||
if (api.getFeatureLevel() < FeatureLevel::FEATURE_LEVEL_2) {
|
||||
GTEST_SKIP() << "Skipping test because multi-sampled textures are not supported at Feature level < 2";
|
||||
}
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
constexpr int kDstTexWidth = 256;
|
||||
|
||||
@@ -25,6 +25,8 @@ using namespace filament::backend;
|
||||
namespace test {
|
||||
|
||||
TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
|
||||
SKIP_IF(Backend::VULKAN, "Frame callbacks are unsupported in Vulkan, see b/417254479");
|
||||
SKIP_IF(Backend::WEBGPU, "Frame callbacks are unsupported in WebGPU");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
@@ -66,18 +68,7 @@ TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
callbackCountB++;
|
||||
}, 0);
|
||||
|
||||
// Render another frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
api.endRenderPass(0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Now, unset the callback
|
||||
api.setFrameScheduledCallback(swapChain, nullptr, {}, 0);
|
||||
|
||||
// Render a final frame. This time no callback should be called.
|
||||
// Render one final frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
|
||||
@@ -392,6 +392,10 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
||||
}
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Crashing when reading pixels without a redundant call to makeCurrent right before the"
|
||||
"render pass. b/422798473");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
@@ -476,6 +480,10 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
}
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Crashing when reading pixels without a redundant call to makeCurrent right before the"
|
||||
"render pass. b/422798473");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
@@ -511,6 +519,8 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, kTexSize);
|
||||
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, kTexSize, kTexSize, 1, std::move(descriptor));
|
||||
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
@@ -534,12 +544,13 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
api.bindRenderPrimitive(mTriangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(kTexSize, kTexSize, "UpdateImageMipLevel", 1875922935));
|
||||
}
|
||||
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(kTexSize, kTexSize, "UpdateImageMipLevel", 1875922935));
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture();
|
||||
}
|
||||
|
||||
@@ -1,388 +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 "BackendTest.h"
|
||||
|
||||
#include "ImageExpectations.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "SharedShadersConstants.h"
|
||||
#include "Skip.h"
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/PipelineState.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
class MemoryMappedTest : public BackendTest {
|
||||
protected:
|
||||
RenderTargetHandle makeRenderTarget(Cleanup& cleanup) {
|
||||
auto& api = getDriverApi();
|
||||
auto colorTexture = cleanup.add(
|
||||
api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
|
||||
screenWidth(), screenHeight(), 1,
|
||||
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR0,
|
||||
screenWidth(), screenHeight(), 1, 1, { { colorTexture } }, {}, {}));
|
||||
return renderTarget;
|
||||
}
|
||||
|
||||
std::tuple<VertexBufferInfoHandle, VertexBufferHandle, IndexBufferHandle>
|
||||
setupGeometryBuffer(Cleanup& cleanup,
|
||||
uint32_t const count, uint8_t const stride, uint32_t const baseVertex,
|
||||
BufferObjectHandle buffer) {
|
||||
auto& api = getDriverApi();
|
||||
AttributeArray attributes = {};
|
||||
attributes[0] = { .offset = 0, .stride = stride, .buffer = 0, .type = ElementType::FLOAT2 };
|
||||
auto vbih = cleanup.add(api.createVertexBufferInfo(1, 1, attributes));
|
||||
// The vertex buffer must be large enough to hold the "garbage" vertices created by the offset.
|
||||
auto vbh = cleanup.add(api.createVertexBuffer(count + baseVertex, vbih));
|
||||
api.setVertexBufferObject(vbh, 0, buffer);
|
||||
|
||||
auto* const indices = new(std::nothrow)uint16_t[count];
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
indices[i] = i + baseVertex;
|
||||
}
|
||||
|
||||
auto ibh = cleanup.add(
|
||||
api.createIndexBuffer(ElementType::USHORT, count, BufferUsage::STATIC));
|
||||
|
||||
api.updateIndexBuffer(ibh, {
|
||||
indices, count * sizeof(uint16_t),
|
||||
[](void* b, size_t, void*) {
|
||||
delete [] static_cast<uint16_t*>(b);
|
||||
}, indices }, 0);
|
||||
|
||||
return { vbih, vbh, ibh };
|
||||
}
|
||||
|
||||
Shader getSimpleShader(Cleanup& cleanup, math::float4 const& color) {
|
||||
auto& api = getDriverApi();
|
||||
Shader const shader = SharedShaders::makeShader(getDriverApi(), cleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Simple,
|
||||
.mFragmentType = FragmentShaderType::SolidColored,
|
||||
.mUniformType = ShaderUniformType::Simple
|
||||
});
|
||||
|
||||
auto const ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC_BIT));
|
||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer);
|
||||
shader.uploadUniform(api, ubuffer, SimpleMaterialParams{ .color = color });
|
||||
return shader;
|
||||
}
|
||||
|
||||
void copyData(const void* data, size_t const size, size_t const offset,
|
||||
MemoryMappedBufferHandle const& mmb, int& callbacksExecuted) {
|
||||
auto& api = getDriverApi();
|
||||
void* p = malloc(size);
|
||||
memcpy(p, data, size);
|
||||
api.copyToMemoryMappedBuffer(mmb, offset, {
|
||||
p, size, [](void* buffer, size_t, void* user) {
|
||||
free(buffer);
|
||||
(*static_cast<int*>(user))++;
|
||||
}, &callbacksExecuted
|
||||
});
|
||||
}
|
||||
|
||||
void render(uint32_t count,
|
||||
int64_t const frame,
|
||||
SwapChainHandle swapChain,
|
||||
RenderTargetHandle renderTarget,
|
||||
RenderPrimitiveHandle renderPrimitive, PipelineState const& state) {
|
||||
auto& api = getDriverApi();
|
||||
RenderPassParams params = getClearColorRenderPass({0,0,0,1});
|
||||
params.viewport = getFullViewport();
|
||||
|
||||
api.beginFrame(frame, 0, 0);
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(renderPrimitive);
|
||||
api.draw2(0, count, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
void runOffsetRenderTest(size_t const mapOffset,
|
||||
size_t const copyOffset,
|
||||
const math::float4& color,
|
||||
const char* screenshotName) {
|
||||
|
||||
SKIP_IF(Backend::WEBGPU, "HwMemoryMappedBuffer APIs not yet implemented");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
auto const swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
auto const renderTarget = makeRenderTarget(cleanup);
|
||||
|
||||
constexpr std::array<math::float2, 3> vertices = {{
|
||||
{-0.25f, -0.25f},
|
||||
{ 0.25f, -0.25f},
|
||||
{-0.25f, 0.25f},
|
||||
}};
|
||||
constexpr size_t vertexDataSize = vertices.size() * sizeof(math::float2);
|
||||
constexpr size_t stride = sizeof(math::float2);
|
||||
|
||||
// The total offset is the sum of the map and copy offsets.
|
||||
const size_t totalOffset = mapOffset + copyOffset;
|
||||
|
||||
// Calculate how many vertices we need to skip to account for the total offset.
|
||||
const uint32_t baseVertex = totalOffset / stride;
|
||||
|
||||
// Create a buffer large enough to hold the offset and the vertex data.
|
||||
auto const bufferObject = cleanup.add(api.createBufferObject(vertexDataSize + totalOffset,
|
||||
BufferObjectBinding::VERTEX, BufferUsage::SHARED_WRITE_BIT));
|
||||
|
||||
int callbackExecuted = 0;
|
||||
// Map the buffer with a specific offset.
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer =
|
||||
api.mapBuffer(bufferObject, mapOffset, vertexDataSize + copyOffset,
|
||||
MapBufferAccessFlags::WRITE_BIT, utils::CString{ screenshotName });
|
||||
|
||||
copyData(vertices.data(), vertexDataSize, copyOffset, memoryMappedBuffer, callbackExecuted);
|
||||
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
|
||||
flushAndWait();
|
||||
EXPECT_EQ(callbackExecuted, 1);
|
||||
|
||||
Shader const shader = getSimpleShader(cleanup, color);
|
||||
|
||||
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 3, stride, baseVertex, bufferObject);
|
||||
|
||||
auto const renderPrimitive = cleanup.add(api.createRenderPrimitive(vbh, ibh, PrimitiveType::TRIANGLES));
|
||||
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(state);
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = vbih;
|
||||
|
||||
render(3, 0, swapChain, renderTarget, renderPrimitive, state);
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), screenshotName, 0));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MemoryMappedTest, MapCopyUnmap) {
|
||||
SKIP_IF(Backend::WEBGPU, "HwMemoryMappedBuffer APIs not yet implemented");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a buffer object.
|
||||
BufferObjectHandle const bufferObject = cleanup.add(api.createBufferObject(1024,
|
||||
BufferObjectBinding::VERTEX, BufferUsage::DYNAMIC_BIT | BufferUsage::SHARED_WRITE_BIT));
|
||||
|
||||
// Map the buffer.
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer = api.mapBuffer(bufferObject, 0, 1024,
|
||||
MapBufferAccessFlags::WRITE_BIT, "MemoryMappedBuffer");
|
||||
|
||||
if (memoryMappedBuffer) {
|
||||
// Create some data to copy.
|
||||
constexpr size_t dataSize = 256;
|
||||
auto* const data = new uint8_t[dataSize];
|
||||
for (size_t i = 0; i < dataSize; ++i) {
|
||||
data[i] = uint8_t(i);
|
||||
}
|
||||
|
||||
// Copy the data into the mapped buffer.
|
||||
BufferDescriptor bufferDescriptor(data, dataSize, [](void* buffer, size_t, void*) {
|
||||
delete[] static_cast<uint8_t*>(buffer);
|
||||
});
|
||||
api.copyToMemoryMappedBuffer(memoryMappedBuffer, 0, std::move(bufferDescriptor));
|
||||
|
||||
// Unmap the buffer.
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, WriteAndRender) {
|
||||
runOffsetRenderTest(0, 0, {1, 0, 0, 1}, "WriteAndRender");
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, MapWithOffset) {
|
||||
runOffsetRenderTest(16, 0, {0, 1, 0, 1}, "MapWithOffset");
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, CopyWithOffset) {
|
||||
runOffsetRenderTest(0, 16, {0, 0, 1, 1}, "CopyWithOffset");
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, MapAndCopyWithOffsets) {
|
||||
runOffsetRenderTest(16, 32, {1, 0, 1, 1}, "MapAndCopyWithOffsets");
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, MultipleCopies) {
|
||||
SKIP_IF(Backend::WEBGPU, "HwMemoryMappedBuffer APIs not yet implemented");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
auto const swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
auto const renderTarget = makeRenderTarget(cleanup);
|
||||
|
||||
constexpr std::array<math::float2, 3> triangle1 = {{{-0.5f, 0.5f}, {-0.8f, 0.2f}, {-0.2f, 0.2f}}};
|
||||
constexpr std::array<math::float2, 3> triangle2 = {{{0.5f, 0.5f}, {0.2f, 0.2f}, {0.8f, 0.2f}}};
|
||||
constexpr std::array<math::float2, 3> triangle3 = {{{-0.5f, -0.5f}, {-0.8f, -0.8f}, {-0.2f, -0.8f}}};
|
||||
constexpr size_t singleTriangleSize = triangle1.size() * sizeof(math::float2);
|
||||
constexpr size_t totalDataSize = singleTriangleSize * 3;
|
||||
|
||||
auto const bufferObject = cleanup.add(api.createBufferObject(totalDataSize,
|
||||
BufferObjectBinding::VERTEX, BufferUsage::SHARED_WRITE_BIT));
|
||||
|
||||
int callbacksExecuted = 0;
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer = api.mapBuffer(bufferObject, 0, totalDataSize,
|
||||
MapBufferAccessFlags::WRITE_BIT, "MultipleCopies");
|
||||
|
||||
copyData(triangle1.data(), singleTriangleSize, 0,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
copyData(triangle2.data(), singleTriangleSize, singleTriangleSize,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
copyData(triangle3.data(), singleTriangleSize, singleTriangleSize * 2,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
|
||||
flushAndWait();
|
||||
EXPECT_EQ(callbacksExecuted, 3);
|
||||
|
||||
Shader const shader = getSimpleShader(cleanup, { 1, 1, 0, 1 });
|
||||
|
||||
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 9, sizeof(math::float2), 0, bufferObject);
|
||||
|
||||
auto const renderPrimitive = cleanup.add(api.createRenderPrimitive(vbh, ibh, PrimitiveType::TRIANGLES));
|
||||
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(state);
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = vbih;
|
||||
|
||||
render(9, 0, swapChain, renderTarget, renderPrimitive, state);
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "MultipleCopies", 0));
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, UpdatePartial) {
|
||||
SKIP_IF(Backend::WEBGPU, "HwMemoryMappedBuffer APIs not yet implemented");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
auto renderTarget = makeRenderTarget(cleanup);
|
||||
|
||||
constexpr std::array<math::float2, 3> triangle1 = {{{-0.5f, 0.5f}, {-0.8f, 0.2f}, {-0.2f, 0.2f}}};
|
||||
constexpr std::array<math::float2, 3> triangle2 = {{{0.5f, 0.5f}, {0.2f, 0.2f}, {0.8f, 0.2f}}};
|
||||
constexpr std::array<math::float2, 3> triangle3 = {{{-0.5f, -0.5f}, {-0.8f, -0.8f}, {-0.2f, -0.8f}}};
|
||||
constexpr size_t singleTriangleSize = triangle1.size() * sizeof(math::float2);
|
||||
constexpr size_t totalDataSize = singleTriangleSize * 3;
|
||||
|
||||
auto bufferObject = cleanup.add(api.createBufferObject(totalDataSize,
|
||||
BufferObjectBinding::VERTEX, BufferUsage::SHARED_WRITE_BIT));
|
||||
|
||||
int callbacksExecuted = 0;
|
||||
|
||||
// Initial data upload
|
||||
{
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer = api.mapBuffer(bufferObject, 0, totalDataSize,
|
||||
MapBufferAccessFlags::WRITE_BIT, "UpdatePartial_initial");
|
||||
|
||||
copyData(triangle1.data(), singleTriangleSize, 0,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
copyData(triangle2.data(), singleTriangleSize, singleTriangleSize,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
copyData(triangle3.data(), singleTriangleSize, singleTriangleSize * 2,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
|
||||
flushAndWait();
|
||||
EXPECT_EQ(callbacksExecuted, 3);
|
||||
}
|
||||
|
||||
|
||||
Shader const shader = getSimpleShader(cleanup, { 1, 1, 0, 1 });
|
||||
|
||||
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 9, sizeof(math::float2), 0, bufferObject);
|
||||
|
||||
auto renderPrimitive = cleanup.add(api.createRenderPrimitive(vbh, ibh, PrimitiveType::TRIANGLES));
|
||||
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(state);
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = vbih;
|
||||
|
||||
render(9, 0, swapChain, renderTarget, renderPrimitive, state);
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "UpdatePartial_before", 0));
|
||||
|
||||
// Now, update the middle triangle
|
||||
constexpr std::array<math::float2, 3> triangle2_updated = {{{0.5f, -0.5f}, {0.2f, -0.8f}, {0.8f, -0.8f}}};
|
||||
callbacksExecuted = 0;
|
||||
{
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer =
|
||||
api.mapBuffer(bufferObject, singleTriangleSize, singleTriangleSize,
|
||||
MapBufferAccessFlags::WRITE_BIT, "UpdatePartial_update");
|
||||
|
||||
copyData(triangle2_updated.data(), singleTriangleSize, 0, memoryMappedBuffer, callbacksExecuted);
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
|
||||
flushAndWait();
|
||||
EXPECT_EQ(callbacksExecuted, 1);
|
||||
}
|
||||
|
||||
// need this because render() above calls commit()
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Second render, after update
|
||||
render(9, 1, swapChain, renderTarget, renderPrimitive, state);
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "UpdatePartial_after", 0));
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "Skip.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <fstream>
|
||||
@@ -82,6 +81,8 @@ public:
|
||||
|
||||
TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Two cases fail, see b/417255941 and b/417255943");
|
||||
// These test scenarios use a known hash of the result pixel buffer to decide pass / fail,
|
||||
// asserting an exact pixel-for-pixel match. So far, rendering on macOS and iPhone have had
|
||||
// deterministic results. Take this test with a grain of salt, however, as other platform / GPU
|
||||
@@ -123,15 +124,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
size_t bufferDimension = getRenderTargetSize();
|
||||
|
||||
size_t getBufferSizeBytes() const {
|
||||
return bufferDimension * bufferDimension * getBytesPerPixels();
|
||||
}
|
||||
|
||||
size_t getBytesPerPixels() const {
|
||||
int bpp;
|
||||
size_t components;
|
||||
int bpp;
|
||||
getPixelInfo(format, type, components, bpp);
|
||||
return bpp;
|
||||
};
|
||||
return bufferDimension * bufferDimension * bpp;
|
||||
}
|
||||
|
||||
// The offset and stride set on the pixel buffer.
|
||||
size_t left = 0, top = 0, alignment = 1;
|
||||
@@ -148,21 +145,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
const size_t width = readRect.width, height = readRect.height;
|
||||
LinearImage image(width, height, 4);
|
||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
|
||||
if (top == 0 && left == 0 && renderTargetBaseSize == readRect.width) {
|
||||
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*)pixelData);
|
||||
} else {
|
||||
float* pixelRef = image.getPixelRef();
|
||||
size_t const bpp = getBytesPerPixels();
|
||||
size_t const bpr = bpp * bufferDimension;
|
||||
for (size_t l = left; l < left + width; ++l) {
|
||||
for (size_t t = top; t < top + height; ++t) {
|
||||
for (size_t c = 0; c < 4; ++c) {
|
||||
pixelRef[c + (l - left) * 4 + ((t - top) * 4 * width)] =
|
||||
((uint8_t*)pixelData)[c + l * bpp + t * bpr] / 255.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*)pixelData);
|
||||
}
|
||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
|
||||
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
|
||||
@@ -186,17 +169,14 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
// The normative read pixels test case. Render a white triangle over a blue background and read
|
||||
// the full viewport into a pixel buffer.
|
||||
TestCase const t0(renderTargetBaseSize);
|
||||
TestCase::Rect const subregionRect = {
|
||||
.x = 90,
|
||||
.y = 403,
|
||||
.width = 64,
|
||||
.height = 64,
|
||||
};
|
||||
|
||||
// Check that a subregion of the render target can be read into a pixel buffer.
|
||||
TestCase t2(renderTargetBaseSize);
|
||||
t2.testName = "readPixels_subregion";
|
||||
t2.readRect = subregionRect;
|
||||
t2.readRect.x = 90;
|
||||
t2.readRect.y = 403;
|
||||
t2.readRect.width = 64;
|
||||
t2.readRect.height = 64;
|
||||
t2.bufferDimension = 64;
|
||||
t2.hash = 0xcba7675a;
|
||||
|
||||
@@ -219,17 +199,16 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
|
||||
// Check that readPixels can read a region of the render target into a subregion of a large
|
||||
// buffer.
|
||||
const size_t actualBufferSize = renderTargetBaseSize * 2;
|
||||
constexpr size_t readOffset = 64;;
|
||||
|
||||
assert_invariant(renderTargetBaseSize + readOffset < actualBufferSize);
|
||||
TestCase t5(renderTargetBaseSize);
|
||||
t5.testName = "readPixels_subbuffer";
|
||||
t5.readRect = subregionRect;
|
||||
t5.bufferDimension = actualBufferSize;
|
||||
t5.left = readOffset;
|
||||
t5.top = readOffset;
|
||||
t5.hash = 0x64585D10;
|
||||
t5.readRect.x = 90;
|
||||
t5.readRect.y = 403;
|
||||
t5.readRect.width = 64;
|
||||
t5.readRect.height = 64;
|
||||
t5.bufferDimension = 512;
|
||||
t5.left = 64;
|
||||
t5.top = 64;
|
||||
t5.hash = 0xbaefdb54;
|
||||
|
||||
// Check that readPixels works with integer formats.
|
||||
TestCase t6(renderTargetBaseSize);
|
||||
@@ -374,6 +353,8 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
}
|
||||
|
||||
// This ensures all driver commands have finished before exiting the test.
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,65 +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 "MaterialCache.h"
|
||||
#include "MaterialParser.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <details/Engine.h>
|
||||
#include <details/Material.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
size_t MaterialCache::Key::Hash::operator()(
|
||||
filament::MaterialCache::Key const& key) const noexcept {
|
||||
uint32_t crc;
|
||||
if (key.parser->getMaterialCrc32(&crc)) {
|
||||
return size_t(crc);
|
||||
}
|
||||
return size_t(key.parser->computeCrc32());
|
||||
}
|
||||
|
||||
bool MaterialCache::Key::operator==(Key const& rhs) const noexcept {
|
||||
return parser == rhs.parser;
|
||||
}
|
||||
|
||||
MaterialCache::~MaterialCache() {
|
||||
if (!mDefinitions.empty()) {
|
||||
LOG(WARNING) << "MaterialCache was destroyed but wasn't empty";
|
||||
}
|
||||
}
|
||||
|
||||
MaterialDefinition* UTILS_NULLABLE MaterialCache::acquire(FEngine& engine,
|
||||
const void* UTILS_NONNULL data, size_t size) noexcept {
|
||||
std::unique_ptr<MaterialParser> parser = MaterialDefinition::createParser(engine.getBackend(),
|
||||
engine.getShaderLanguage(), data, size);
|
||||
assert_invariant(parser);
|
||||
|
||||
return mDefinitions.acquire(Key{parser.get()}, [&engine, parser = std::move(parser)]() mutable {
|
||||
return MaterialDefinition::create(engine, std::move(parser));
|
||||
});
|
||||
}
|
||||
|
||||
void MaterialCache::release(FEngine& engine, MaterialParser const& parser) noexcept {
|
||||
mDefinitions.release(Key{&parser}, [&engine](MaterialDefinition& definition) {
|
||||
definition.terminate(engine);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef TNT_FILAMENT_MATERIALCACHE_H
|
||||
#define TNT_FILAMENT_MATERIALCACHE_H
|
||||
|
||||
#include "MaterialDefinition.h"
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
|
||||
#include <backend/CallbackHandler.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/RefCountedMap.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Material;
|
||||
|
||||
class MaterialCache {
|
||||
// A newtype around a material parser used as a key for the material cache. The material file's
|
||||
// CRC32 is used as the hash function.
|
||||
struct Key {
|
||||
struct Hash {
|
||||
size_t operator()(Key const& x) const noexcept;
|
||||
};
|
||||
bool operator==(Key const& rhs) const noexcept;
|
||||
|
||||
MaterialParser const* UTILS_NONNULL parser;
|
||||
};
|
||||
|
||||
public:
|
||||
~MaterialCache();
|
||||
|
||||
/** Acquire or create a new entry in the cache for the given material data. */
|
||||
MaterialDefinition* UTILS_NULLABLE acquire(FEngine& engine, const void* UTILS_NONNULL data,
|
||||
size_t size) noexcept;
|
||||
|
||||
/** Release an entry in the cache, potentially freeing its GPU resources. */
|
||||
void release(FEngine& engine, MaterialParser const& parser) noexcept;
|
||||
|
||||
private:
|
||||
// We use unique_ptr here because we need these pointers to be stable.
|
||||
// TODO: investigate using a custom allocator here?
|
||||
utils::RefCountedMap<Key, std::unique_ptr<MaterialDefinition>, Key::Hash> mDefinitions;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_MATERIALCACHE_H
|
||||
@@ -1,391 +0,0 @@
|
||||
#include "MaterialDefinition.h"
|
||||
|
||||
#include "Froxelizer.h"
|
||||
#include "MaterialParser.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <ds/ColorPassDescriptorSet.h>
|
||||
|
||||
#include <details/Engine.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
using namespace utils;
|
||||
|
||||
std::unique_ptr<MaterialParser> MaterialDefinition::createParser(Backend const backend,
|
||||
FixedCapacityVector<ShaderLanguage> languages, const void* data, size_t size) {
|
||||
// unique_ptr so we don't leak MaterialParser on failures below
|
||||
auto materialParser = std::make_unique<MaterialParser>(languages, data, size);
|
||||
|
||||
MaterialParser::ParseResult const materialResult = materialParser->parse();
|
||||
|
||||
if (UTILS_UNLIKELY(materialResult == MaterialParser::ParseResult::ERROR_MISSING_BACKEND)) {
|
||||
CString languageNames;
|
||||
for (auto it = languages.begin(); it != languages.end(); ++it) {
|
||||
languageNames.append(CString{shaderLanguageToString(*it)});
|
||||
if (std::next(it) != languages.end()) {
|
||||
languageNames.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND)
|
||||
<< "the material was not built for any of the " << to_string(backend)
|
||||
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
|
||||
}
|
||||
|
||||
if (backend == Backend::NOOP) {
|
||||
return materialParser;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(materialResult == MaterialParser::ParseResult::SUCCESS)
|
||||
<< "could not parse the material package";
|
||||
|
||||
uint32_t version = 0;
|
||||
materialParser->getMaterialVersion(&version);
|
||||
FILAMENT_CHECK_POSTCONDITION(version == MATERIAL_VERSION)
|
||||
<< "Material version mismatch. Expected " << MATERIAL_VERSION << " but received "
|
||||
<< version << ".";
|
||||
|
||||
assert_invariant(backend != Backend::DEFAULT && "Default backend has not been resolved.");
|
||||
|
||||
return materialParser;
|
||||
}
|
||||
|
||||
std::unique_ptr<MaterialDefinition> MaterialDefinition::create(FEngine& engine,
|
||||
std::unique_ptr<MaterialParser> parser) {
|
||||
// Try checking CRC32 value for the package and skip if it's unavailable.
|
||||
if (downcast(engine).features.material.check_crc32_after_loading) {
|
||||
uint32_t parsedCrc32 = 0;
|
||||
parser->getMaterialCrc32(&parsedCrc32);
|
||||
|
||||
uint32_t expectedCrc32 = parser->computeCrc32();
|
||||
|
||||
if (parsedCrc32 != expectedCrc32) {
|
||||
CString name;
|
||||
parser->getName(&name);
|
||||
LOG(ERROR) << "The material '" << name.c_str_safe()
|
||||
<< "' is corrupted: crc32_expected=" << expectedCrc32
|
||||
<< ", crc32_parsed=" << parsedCrc32;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t v = 0;
|
||||
parser->getShaderModels(&v);
|
||||
bitset32 shaderModels;
|
||||
shaderModels.setValue(v);
|
||||
|
||||
ShaderModel const shaderModel = downcast(engine).getShaderModel();
|
||||
if (!shaderModels.test(static_cast<uint32_t>(shaderModel))) {
|
||||
CString name;
|
||||
parser->getName(&name);
|
||||
char shaderModelsString[16];
|
||||
snprintf(shaderModelsString, sizeof(shaderModelsString), "%#x", shaderModels.getValue());
|
||||
LOG(ERROR) << "The material '" << name.c_str_safe() << "' was not built for "
|
||||
<< to_string(shaderModel) << ".";
|
||||
LOG(ERROR) << "Compiled material contains shader models " << shaderModelsString << ".";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Print a warning if the material's stereo type doesn't align with the engine's
|
||||
// setting.
|
||||
MaterialDomain materialDomain;
|
||||
UserVariantFilterMask variantFilterMask;
|
||||
parser->getMaterialDomain(&materialDomain);
|
||||
parser->getMaterialVariantFilterMask(&variantFilterMask);
|
||||
bool const hasStereoVariants =
|
||||
!(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::STE));
|
||||
if (materialDomain == MaterialDomain::SURFACE && hasStereoVariants) {
|
||||
StereoscopicType const engineStereoscopicType = engine.getConfig().stereoscopicType;
|
||||
// Default materials are always compiled with either 'instanced' or 'multiview'.
|
||||
// So, we only verify compatibility if the engine is set up for stereo.
|
||||
if (engineStereoscopicType != StereoscopicType::NONE) {
|
||||
StereoscopicType materialStereoscopicType = StereoscopicType::NONE;
|
||||
parser->getStereoscopicType(&materialStereoscopicType);
|
||||
if (materialStereoscopicType != engineStereoscopicType) {
|
||||
CString name;
|
||||
parser->getName(&name);
|
||||
LOG(WARNING) << "The stereoscopic type in the compiled material '"
|
||||
<< name.c_str_safe() << "' is " << (int) materialStereoscopicType
|
||||
<< ", which is not compatible with the engine's setting "
|
||||
<< (int) engineStereoscopicType << ".";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_unique<MaterialDefinition>(engine, std::move(parser));
|
||||
}
|
||||
|
||||
void MaterialDefinition::terminate(FEngine& engine) {
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
perViewDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
perViewDescriptorSetLayoutVsm.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
descriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
}
|
||||
|
||||
MaterialDefinition::MaterialDefinition(FEngine& engine,
|
||||
std::unique_ptr<MaterialParser> materialParser)
|
||||
: mMaterialParser(std::move(materialParser)) {
|
||||
|
||||
processMain();
|
||||
processBlendingMode();
|
||||
processSpecializationConstants();
|
||||
processDescriptorSets(engine);
|
||||
}
|
||||
|
||||
void MaterialDefinition::processMain() {
|
||||
UTILS_UNUSED_IN_RELEASE bool const nameOk = mMaterialParser->getName(&name);
|
||||
assert_invariant(nameOk);
|
||||
|
||||
featureLevel = [this]() -> FeatureLevel {
|
||||
// code written this way so the IDE will complain when/if we add a FeatureLevel
|
||||
uint8_t level = 1;
|
||||
mMaterialParser->getFeatureLevel(&level);
|
||||
assert_invariant(level <= 3);
|
||||
FeatureLevel featureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
switch (FeatureLevel(level)) {
|
||||
case FeatureLevel::FEATURE_LEVEL_0:
|
||||
case FeatureLevel::FEATURE_LEVEL_1:
|
||||
case FeatureLevel::FEATURE_LEVEL_2:
|
||||
case FeatureLevel::FEATURE_LEVEL_3:
|
||||
featureLevel = FeatureLevel(level);
|
||||
break;
|
||||
}
|
||||
return featureLevel;
|
||||
}();
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool success;
|
||||
|
||||
success = mMaterialParser->getCacheId(&cacheId);
|
||||
assert_invariant(success);
|
||||
|
||||
success = mMaterialParser->getSIB(&samplerInterfaceBlock);
|
||||
assert_invariant(success);
|
||||
|
||||
success = mMaterialParser->getUIB(&uniformInterfaceBlock);
|
||||
assert_invariant(success);
|
||||
|
||||
if (UTILS_UNLIKELY(mMaterialParser->getShaderLanguage() == ShaderLanguage::ESSL1)) {
|
||||
success = mMaterialParser->getAttributeInfo(&attributeInfo);
|
||||
assert_invariant(success);
|
||||
|
||||
success = mMaterialParser->getBindingUniformInfo(&bindingUniformInfo);
|
||||
assert_invariant(success);
|
||||
}
|
||||
|
||||
// Older materials will not have a subpass chunk; this should not be an error.
|
||||
if (!mMaterialParser->getSubpasses(&subpassInfo)) {
|
||||
subpassInfo.isValid = false;
|
||||
}
|
||||
|
||||
mMaterialParser->getShading(&shading);
|
||||
mMaterialParser->getMaterialProperties(&materialProperties);
|
||||
mMaterialParser->getInterpolation(&interpolation);
|
||||
mMaterialParser->getVertexDomain(&vertexDomain);
|
||||
mMaterialParser->getMaterialDomain(&materialDomain);
|
||||
mMaterialParser->getMaterialVariantFilterMask(&variantFilterMask);
|
||||
mMaterialParser->getRequiredAttributes(&requiredAttributes);
|
||||
mMaterialParser->getRefractionMode(&refractionMode);
|
||||
mMaterialParser->getRefractionType(&refractionType);
|
||||
mMaterialParser->getReflectionMode(&reflectionMode);
|
||||
mMaterialParser->getTransparencyMode(&transparencyMode);
|
||||
mMaterialParser->getDoubleSided(&doubleSided);
|
||||
mMaterialParser->getCullingMode(&cullingMode);
|
||||
|
||||
if (shading == Shading::UNLIT) {
|
||||
mMaterialParser->hasShadowMultiplier(&hasShadowMultiplier);
|
||||
}
|
||||
|
||||
isVariantLit = shading != Shading::UNLIT || hasShadowMultiplier;
|
||||
|
||||
// color write
|
||||
bool colorWrite = false;
|
||||
mMaterialParser->getColorWrite(&colorWrite);
|
||||
rasterState.colorWrite = colorWrite;
|
||||
|
||||
// depth test
|
||||
bool depthTest = false;
|
||||
mMaterialParser->getDepthTest(&depthTest);
|
||||
rasterState.depthFunc = depthTest ? RasterState::DepthFunc::GE : RasterState::DepthFunc::A;
|
||||
|
||||
// if doubleSided() was called we override culling()
|
||||
bool doubleSideSet = false;
|
||||
mMaterialParser->getDoubleSidedSet(&doubleSideSet);
|
||||
if (doubleSideSet) {
|
||||
doubleSidedCapability = true;
|
||||
rasterState.culling = doubleSided ? CullingMode::NONE : cullingMode;
|
||||
} else {
|
||||
rasterState.culling = cullingMode;
|
||||
}
|
||||
|
||||
// specular anti-aliasing
|
||||
mMaterialParser->hasSpecularAntiAliasing(&specularAntiAliasing);
|
||||
if (specularAntiAliasing) {
|
||||
mMaterialParser->getSpecularAntiAliasingVariance(&specularAntiAliasingVariance);
|
||||
mMaterialParser->getSpecularAntiAliasingThreshold(&specularAntiAliasingThreshold);
|
||||
}
|
||||
|
||||
mMaterialParser->hasCustomDepthShader(&hasCustomDepthShader);
|
||||
|
||||
bool const isLit = isVariantLit || hasShadowMultiplier;
|
||||
bool const isSSR = reflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
refractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
perViewLayoutIndex = ColorPassDescriptorSet::getIndex(isLit, isSSR, hasFog);
|
||||
}
|
||||
|
||||
void MaterialDefinition::processBlendingMode() {
|
||||
mMaterialParser->getBlendingMode(&blendingMode);
|
||||
|
||||
if (blendingMode == BlendingMode::MASKED) {
|
||||
mMaterialParser->getMaskThreshold(&maskThreshold);
|
||||
}
|
||||
|
||||
if (blendingMode == BlendingMode::CUSTOM) {
|
||||
mMaterialParser->getCustomBlendFunction(&customBlendFunctions);
|
||||
}
|
||||
|
||||
// blending mode
|
||||
switch (blendingMode) {
|
||||
// Do not change the MASKED behavior without checking for regressions with
|
||||
// AlphaBlendModeTest and TextureLinearInterpolationTest, with and without
|
||||
// View::BlendMode::TRANSLUCENT.
|
||||
case BlendingMode::MASKED:
|
||||
case BlendingMode::OPAQUE:
|
||||
rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
rasterState.blendFunctionDstRGB = BlendFunction::ZERO;
|
||||
rasterState.blendFunctionDstAlpha = BlendFunction::ZERO;
|
||||
rasterState.depthWrite = true;
|
||||
break;
|
||||
case BlendingMode::TRANSPARENT:
|
||||
case BlendingMode::FADE:
|
||||
rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA;
|
||||
rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
|
||||
rasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::ADD:
|
||||
rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
rasterState.blendFunctionDstRGB = BlendFunction::ONE;
|
||||
rasterState.blendFunctionDstAlpha = BlendFunction::ONE;
|
||||
rasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::MULTIPLY:
|
||||
rasterState.blendFunctionSrcRGB = BlendFunction::ZERO;
|
||||
rasterState.blendFunctionSrcAlpha = BlendFunction::ZERO;
|
||||
rasterState.blendFunctionDstRGB = BlendFunction::SRC_COLOR;
|
||||
rasterState.blendFunctionDstAlpha = BlendFunction::SRC_COLOR;
|
||||
rasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::SCREEN:
|
||||
rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_COLOR;
|
||||
rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_COLOR;
|
||||
rasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::CUSTOM:
|
||||
rasterState.blendFunctionSrcRGB = customBlendFunctions[0];
|
||||
rasterState.blendFunctionSrcAlpha = customBlendFunctions[1];
|
||||
rasterState.blendFunctionDstRGB = customBlendFunctions[2];
|
||||
rasterState.blendFunctionDstAlpha = customBlendFunctions[3];
|
||||
rasterState.depthWrite = false;
|
||||
}
|
||||
|
||||
// depth write
|
||||
bool depthWriteSet = false;
|
||||
mMaterialParser->getDepthWriteSet(&depthWriteSet);
|
||||
if (depthWriteSet) {
|
||||
bool depthWrite = false;
|
||||
mMaterialParser->getDepthWrite(&depthWrite);
|
||||
rasterState.depthWrite = depthWrite;
|
||||
}
|
||||
|
||||
// alpha to coverage
|
||||
bool alphaToCoverageSet = false;
|
||||
mMaterialParser->getAlphaToCoverageSet(&alphaToCoverageSet);
|
||||
if (alphaToCoverageSet) {
|
||||
bool alphaToCoverage = false;
|
||||
mMaterialParser->getAlphaToCoverage(&alphaToCoverage);
|
||||
rasterState.alphaToCoverage = alphaToCoverage;
|
||||
} else {
|
||||
rasterState.alphaToCoverage = blendingMode == BlendingMode::MASKED;
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialDefinition::processSpecializationConstants() {
|
||||
// Older materials won't have a constants chunk, but that's okay.
|
||||
mMaterialParser->getConstants(&materialConstants);
|
||||
for (size_t i = 0, c = materialConstants.size(); i < c; i++) {
|
||||
auto& item = materialConstants[i];
|
||||
// the key can be a string_view because mMaterialConstant owns the CString
|
||||
std::string_view const key{ item.name.data(), item.name.size() };
|
||||
specializationConstantsNameToIndex[key] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
||||
UTILS_UNUSED_IN_RELEASE bool success;
|
||||
|
||||
success = mMaterialParser->getDescriptorBindings(&programDescriptorBindings);
|
||||
assert_invariant(success);
|
||||
|
||||
backend::DescriptorSetLayout descriptorSetLayout;
|
||||
success = mMaterialParser->getDescriptorSetLayout(&descriptorSetLayout);
|
||||
assert_invariant(success);
|
||||
|
||||
// get the PER_VIEW descriptor binding info
|
||||
bool const isLit = isVariantLit || hasShadowMultiplier;
|
||||
bool const isSSR = reflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
refractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
auto perViewDescriptorSetLayout = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
materialDomain, isLit, isSSR, hasFog, false);
|
||||
|
||||
auto perViewDescriptorSetLayoutVsm = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
materialDomain, isLit, isSSR, hasFog, true);
|
||||
|
||||
// set the labels
|
||||
descriptorSetLayout.label = CString{ name }.append("_perMat");
|
||||
perViewDescriptorSetLayout.label = CString{ name }.append("_perView");
|
||||
perViewDescriptorSetLayoutVsm.label = CString{ name }.append("_perViewVsm");
|
||||
|
||||
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
|
||||
for (auto&& [bindingPoint, descriptorSetLayout] : {
|
||||
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
descriptor_sets::getPerRenderableLayout() },
|
||||
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
|
||||
perViewDescriptorSetLayout }}) {
|
||||
Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint];
|
||||
descriptors.reserve(descriptorSetLayout.bindings.size());
|
||||
for (auto const& entry: descriptorSetLayout.bindings) {
|
||||
auto const& name = descriptor_sets::getDescriptorName(bindingPoint, entry.binding);
|
||||
descriptors.push_back({ name, entry.type, entry.binding });
|
||||
}
|
||||
}
|
||||
|
||||
this->descriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(descriptorSetLayout) };
|
||||
|
||||
this->perViewDescriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(perViewDescriptorSetLayout) };
|
||||
|
||||
this->perViewDescriptorSetLayoutVsm = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(perViewDescriptorSetLayoutVsm) };
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef TNT_FILAMENT_MATERIALDEFINITION_H
|
||||
#define TNT_FILAMENT_MATERIALDEFINITION_H
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
#include <private/filament/BufferInterfaceBlock.h>
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
#include <private/filament/SubpassInfo.h>
|
||||
#include <private/filament/ConstantInfo.h>
|
||||
|
||||
#include <ds/DescriptorSetLayout.h>
|
||||
|
||||
#include <backend/Program.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FEngine;
|
||||
class MaterialParser;
|
||||
|
||||
/** A MaterialDefinition is a parsed, unmarshalled material file, containing no state.
|
||||
*
|
||||
* Given that this is a pure read-only class, nearly all members are public without getters.
|
||||
*/
|
||||
struct MaterialDefinition {
|
||||
using BlendingMode = filament::BlendingMode;
|
||||
using Shading = filament::Shading;
|
||||
using Interpolation = filament::Interpolation;
|
||||
using VertexDomain = filament::VertexDomain;
|
||||
using TransparencyMode = filament::TransparencyMode;
|
||||
using CullingMode = backend::CullingMode;
|
||||
|
||||
using AttributeInfoContainer = utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>>;
|
||||
|
||||
using BindingUniformInfoContainer = utils::FixedCapacityVector<
|
||||
std::tuple<uint8_t, utils::CString, backend::Program::UniformInfo>>;
|
||||
|
||||
// public only due to std::make_unique().
|
||||
MaterialDefinition(FEngine& engine, std::unique_ptr<MaterialParser> parser);
|
||||
|
||||
// Free GPU resources owned by this MaterialDefinition.
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
MaterialParser const& getMaterialParser() const noexcept { return *mMaterialParser; }
|
||||
|
||||
// try to order by frequency of use
|
||||
DescriptorSetLayout perViewDescriptorSetLayout;
|
||||
DescriptorSetLayout perViewDescriptorSetLayoutVsm;
|
||||
DescriptorSetLayout descriptorSetLayout;
|
||||
backend::Program::DescriptorSetInfo programDescriptorBindings;
|
||||
|
||||
backend::RasterState rasterState;
|
||||
TransparencyMode transparencyMode = TransparencyMode::DEFAULT;
|
||||
bool isVariantLit = false;
|
||||
backend::FeatureLevel featureLevel = backend::FeatureLevel::FEATURE_LEVEL_1;
|
||||
Shading shading = Shading::UNLIT;
|
||||
|
||||
BlendingMode blendingMode = BlendingMode::OPAQUE;
|
||||
std::array<backend::BlendFunction, 4> customBlendFunctions = {};
|
||||
Interpolation interpolation = Interpolation::SMOOTH;
|
||||
VertexDomain vertexDomain = VertexDomain::OBJECT;
|
||||
MaterialDomain materialDomain = MaterialDomain::SURFACE;
|
||||
CullingMode cullingMode = CullingMode::NONE;
|
||||
AttributeBitset requiredAttributes;
|
||||
UserVariantFilterMask variantFilterMask = 0;
|
||||
RefractionMode refractionMode = RefractionMode::NONE;
|
||||
RefractionType refractionType = RefractionType::SOLID;
|
||||
ReflectionMode reflectionMode = ReflectionMode::DEFAULT;
|
||||
uint64_t materialProperties = 0;
|
||||
uint8_t perViewLayoutIndex = 0;
|
||||
|
||||
float maskThreshold = 0.4f;
|
||||
float specularAntiAliasingVariance = 0.0f;
|
||||
float specularAntiAliasingThreshold = 0.0f;
|
||||
|
||||
bool doubleSided = false;
|
||||
bool doubleSidedCapability = false;
|
||||
bool hasShadowMultiplier = false;
|
||||
bool hasCustomDepthShader = false;
|
||||
bool specularAntiAliasing = false;
|
||||
|
||||
SamplerInterfaceBlock samplerInterfaceBlock;
|
||||
BufferInterfaceBlock uniformInterfaceBlock;
|
||||
SubpassInfo subpassInfo;
|
||||
|
||||
BindingUniformInfoContainer bindingUniformInfo;
|
||||
AttributeInfoContainer attributeInfo;
|
||||
|
||||
// Constants defined by this Material
|
||||
utils::FixedCapacityVector<MaterialConstant> materialConstants;
|
||||
// A map from the Constant name to the materialConstants index
|
||||
std::unordered_map<std::string_view, uint32_t> specializationConstantsNameToIndex;
|
||||
|
||||
utils::CString name;
|
||||
uint64_t cacheId = 0;
|
||||
|
||||
private:
|
||||
friend class MaterialCache;
|
||||
friend class FMaterial;
|
||||
|
||||
static std::unique_ptr<MaterialParser> createParser(backend::Backend const backend,
|
||||
utils::FixedCapacityVector<backend::ShaderLanguage> languages,
|
||||
const void* UTILS_NONNULL data, size_t size);
|
||||
|
||||
static std::unique_ptr<MaterialDefinition> create(FEngine& engine,
|
||||
std::unique_ptr<MaterialParser> parser);
|
||||
|
||||
void processMain();
|
||||
void processBlendingMode();
|
||||
void processSpecializationConstants();
|
||||
void processDescriptorSets(FEngine& engine);
|
||||
|
||||
std::unique_ptr<MaterialParser> mMaterialParser;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_MATERIALDEFINITION_H
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
@@ -107,14 +106,6 @@ MaterialParser::MaterialParserDetails::ManagedBuffer::~ManagedBuffer() noexcept
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
bool MaterialParser::operator==(MaterialParser const& rhs) const noexcept {
|
||||
if (mImpl.mManagedBuffer.size() != rhs.mImpl.mManagedBuffer.size()) {
|
||||
return false;
|
||||
}
|
||||
return !std::memcmp(mImpl.mManagedBuffer.data(), rhs.mImpl.mManagedBuffer.data(),
|
||||
mImpl.mManagedBuffer.size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool MaterialParser::get(typename T::Container* container) const noexcept {
|
||||
auto [start, end] = mImpl.mChunkContainer.getChunkRange(T::tag);
|
||||
@@ -171,19 +162,6 @@ MaterialParser::ParseResult MaterialParser::parse() noexcept {
|
||||
return ParseResult::SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t MaterialParser::computeCrc32() const noexcept {
|
||||
const size_t size = mImpl.mManagedBuffer.size();
|
||||
const void* const UTILS_NONNULL payload = mImpl.mManagedBuffer.data();
|
||||
|
||||
constexpr size_t crc32ChunkSize = sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t);
|
||||
const size_t originalSize = size - crc32ChunkSize;
|
||||
assert_invariant(size > crc32ChunkSize);
|
||||
|
||||
std::vector<uint32_t> crc32Table;
|
||||
utils::hash::crc32GenerateTable(crc32Table);
|
||||
return utils::hash::crc32Update(0, payload, originalSize, crc32Table);
|
||||
}
|
||||
|
||||
ShaderLanguage MaterialParser::getShaderLanguage() const noexcept {
|
||||
return mImpl.mChosenLanguage;
|
||||
}
|
||||
@@ -418,7 +396,7 @@ bool MaterialParser::getReflectionMode(ReflectionMode* value) const noexcept {
|
||||
}
|
||||
|
||||
bool MaterialParser::getShader(ShaderContent& shader,
|
||||
ShaderModel const shaderModel, Variant const variant, ShaderStage const stage) const noexcept {
|
||||
ShaderModel const shaderModel, Variant const variant, ShaderStage const stage) noexcept {
|
||||
return mImpl.mMaterialChunk.getShader(shader,
|
||||
mImpl.mBlobDictionary, shaderModel, variant, stage);
|
||||
}
|
||||
|
||||
@@ -65,11 +65,7 @@ public:
|
||||
ERROR_OTHER
|
||||
};
|
||||
|
||||
bool operator==(MaterialParser const& rhs) const noexcept;
|
||||
|
||||
ParseResult parse() noexcept;
|
||||
|
||||
uint32_t computeCrc32() const noexcept;
|
||||
backend::ShaderLanguage getShaderLanguage() const noexcept;
|
||||
|
||||
// Accessors
|
||||
@@ -133,7 +129,7 @@ public:
|
||||
bool getMaterialCrc32(uint32_t* value) const noexcept;
|
||||
|
||||
bool getShader(filaflat::ShaderContent& shader, backend::ShaderModel shaderModel,
|
||||
Variant variant, backend::ShaderStage stage) const noexcept;
|
||||
Variant variant, backend::ShaderStage stage) noexcept;
|
||||
|
||||
bool hasShader(backend::ShaderModel const model,
|
||||
Variant const variant, backend::ShaderStage const stage) const noexcept {
|
||||
|
||||
@@ -569,7 +569,7 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
// FIXME: the DescriptorSetLayout must specify SAMPLER_FLOAT
|
||||
data.picking = builder.createTexture("Picking Buffer", {
|
||||
.width = width, .height = height,
|
||||
.format = isES2 ? TextureFormat::RGBA8 : TextureFormat::RG32UI });
|
||||
.format = isES2 ? TextureFormat::RGBA8 : TextureFormat::RG32F });
|
||||
|
||||
data.picking = builder.write(data.picking,
|
||||
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
|
||||
|
||||
@@ -76,7 +76,7 @@ FBufferObject::FBufferObject(FEngine& engine, const Builder& builder)
|
||||
: mByteCount(builder->mByteCount), mBindingType(builder->mBindingType) {
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
mHandle = driver.createBufferObject(builder->mByteCount, builder->mBindingType,
|
||||
backend::BufferUsage::STATIC, utils::CString{ builder.getName() });
|
||||
backend::BufferUsage::STATIC, builder.getName());
|
||||
}
|
||||
|
||||
void FBufferObject::terminate(FEngine& engine) {
|
||||
|
||||
@@ -908,8 +908,8 @@ FIndirectLight* FEngine::createIndirectLight(const IndirectLight::Builder& build
|
||||
}
|
||||
|
||||
FMaterial* FEngine::createMaterial(const Material::Builder& builder,
|
||||
MaterialDefinition const& definition) noexcept {
|
||||
return create(mMaterials, builder, definition);
|
||||
std::unique_ptr<MaterialParser> materialParser) noexcept {
|
||||
return create(mMaterials, builder, std::move(materialParser));
|
||||
}
|
||||
|
||||
FSkybox* FEngine::createSkybox(const Skybox::Builder& builder) noexcept {
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "ResourceList.h"
|
||||
#include "HwDescriptorSetLayoutFactory.h"
|
||||
#include "HwVertexBufferInfoFactory.h"
|
||||
#include "MaterialCache.h"
|
||||
|
||||
#include "components/CameraManager.h"
|
||||
#include "components/LightManager.h"
|
||||
@@ -292,10 +291,6 @@ public:
|
||||
return mResourceAllocatorDisposer;
|
||||
}
|
||||
|
||||
MaterialCache& getMaterialCache() const noexcept {
|
||||
return mMaterialCache;
|
||||
}
|
||||
|
||||
void* streamAlloc(size_t size, size_t alignment) noexcept;
|
||||
|
||||
Epoch getEngineEpoch() const { return mEngineEpoch; }
|
||||
@@ -317,8 +312,7 @@ public:
|
||||
FMorphTargetBuffer* createMorphTargetBuffer(const MorphTargetBuffer::Builder& builder) noexcept;
|
||||
FInstanceBuffer* createInstanceBuffer(const InstanceBuffer::Builder& builder) noexcept;
|
||||
FIndirectLight* createIndirectLight(const IndirectLight::Builder& builder) noexcept;
|
||||
FMaterial* createMaterial(const Material::Builder& builder,
|
||||
MaterialDefinition const& definition) noexcept;
|
||||
FMaterial* createMaterial(const Material::Builder& builder, std::unique_ptr<MaterialParser> materialParser) noexcept;
|
||||
FTexture* createTexture(const Texture::Builder& builder) noexcept;
|
||||
FSkybox* createSkybox(const Skybox::Builder& builder) noexcept;
|
||||
FColorGrading* createColorGrading(const ColorGrading::Builder& builder) noexcept;
|
||||
@@ -596,7 +590,6 @@ private:
|
||||
FLightManager mLightManager;
|
||||
FCameraManager mCameraManager;
|
||||
std::shared_ptr<ResourceAllocatorDisposer> mResourceAllocatorDisposer;
|
||||
mutable MaterialCache mMaterialCache;
|
||||
HwVertexBufferInfoFactory mHwVertexBufferInfoFactory;
|
||||
HwDescriptorSetLayoutFactory mHwDescriptorSetLayoutFactory;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayoutDepthVariant;
|
||||
|
||||
@@ -58,8 +58,12 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@@ -74,6 +78,46 @@ using namespace backend;
|
||||
using namespace filaflat;
|
||||
using namespace utils;
|
||||
|
||||
static std::unique_ptr<MaterialParser> createParser(Backend const backend,
|
||||
FixedCapacityVector<ShaderLanguage> languages, const void* data, size_t size) {
|
||||
// unique_ptr so we don't leak MaterialParser on failures below
|
||||
auto materialParser = std::make_unique<MaterialParser>(languages, data, size);
|
||||
|
||||
MaterialParser::ParseResult const materialResult = materialParser->parse();
|
||||
|
||||
if (UTILS_UNLIKELY(materialResult == MaterialParser::ParseResult::ERROR_MISSING_BACKEND)) {
|
||||
CString languageNames;
|
||||
for (auto it = languages.begin(); it != languages.end(); ++it) {
|
||||
languageNames.append(CString{shaderLanguageToString(*it)});
|
||||
if (std::next(it) != languages.end()) {
|
||||
languageNames.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND)
|
||||
<< "the material was not built for any of the " << to_string(backend)
|
||||
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
|
||||
}
|
||||
|
||||
if (backend == Backend::NOOP) {
|
||||
return materialParser;
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(materialResult == MaterialParser::ParseResult::SUCCESS)
|
||||
<< "could not parse the material package";
|
||||
|
||||
uint32_t version = 0;
|
||||
materialParser->getMaterialVersion(&version);
|
||||
FILAMENT_CHECK_POSTCONDITION(version == MATERIAL_VERSION)
|
||||
<< "Material version mismatch. Expected " << MATERIAL_VERSION << " but received "
|
||||
<< version << ".";
|
||||
|
||||
assert_invariant(backend != Backend::DEFAULT && "Default backend has not been resolved.");
|
||||
|
||||
return materialParser;
|
||||
}
|
||||
|
||||
struct Material::BuilderDetails {
|
||||
const void* mPayload = nullptr;
|
||||
size_t mSize = 0;
|
||||
@@ -136,21 +180,192 @@ const char* toString(ShaderModel model) {
|
||||
}
|
||||
|
||||
Material* Material::Builder::build(Engine& engine) const {
|
||||
MaterialDefinition* r = downcast(engine).getMaterialCache().acquire(downcast(engine),
|
||||
mImpl->mPayload, mImpl->mSize);
|
||||
if (r) {
|
||||
return downcast(engine).createMaterial(*this, *r);
|
||||
std::unique_ptr<MaterialParser> materialParser = createParser(
|
||||
downcast(engine).getBackend(), downcast(engine).getShaderLanguage(),
|
||||
mImpl->mPayload, mImpl->mSize);
|
||||
|
||||
if (!materialParser) {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
// Try checking CRC32 value for the package and skip if it's unavailable.
|
||||
if (downcast(engine).features.material.check_crc32_after_loading) {
|
||||
uint32_t parsedCrc32 = 0;
|
||||
materialParser->getMaterialCrc32(&parsedCrc32);
|
||||
|
||||
constexpr size_t crc32ChunkSize = sizeof(uint64_t) + sizeof(uint32_t) + sizeof(uint32_t);
|
||||
const size_t originalSize = mImpl->mSize - crc32ChunkSize;
|
||||
assert_invariant(mImpl->mSize > crc32ChunkSize);
|
||||
|
||||
std::vector<uint32_t> crc32Table;
|
||||
hash::crc32GenerateTable(crc32Table);
|
||||
uint32_t expectedCrc32 = hash::crc32Update(0, mImpl->mPayload, originalSize, crc32Table);
|
||||
if (parsedCrc32 != expectedCrc32) {
|
||||
CString name;
|
||||
materialParser->getName(&name);
|
||||
LOG(ERROR) << "The material '" << name.c_str_safe()
|
||||
<< "' is corrupted: crc32_expected=" << expectedCrc32
|
||||
<< ", crc32_parsed=" << parsedCrc32;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t v = 0;
|
||||
materialParser->getShaderModels(&v);
|
||||
bitset32 shaderModels;
|
||||
shaderModels.setValue(v);
|
||||
|
||||
ShaderModel const shaderModel = downcast(engine).getShaderModel();
|
||||
if (!shaderModels.test(static_cast<uint32_t>(shaderModel))) {
|
||||
CString name;
|
||||
materialParser->getName(&name);
|
||||
char shaderModelsString[16];
|
||||
snprintf(shaderModelsString, sizeof(shaderModelsString), "%#x", shaderModels.getValue());
|
||||
LOG(ERROR) << "The material '" << name.c_str_safe() << "' was not built for "
|
||||
<< toString(shaderModel) << ".";
|
||||
LOG(ERROR) << "Compiled material contains shader models " << shaderModelsString << ".";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Print a warning if the material's stereo type doesn't align with the engine's setting.
|
||||
MaterialDomain materialDomain;
|
||||
UserVariantFilterMask variantFilterMask;
|
||||
materialParser->getMaterialDomain(&materialDomain);
|
||||
materialParser->getMaterialVariantFilterMask(&variantFilterMask);
|
||||
bool const hasStereoVariants = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::STE));
|
||||
if (materialDomain == MaterialDomain::SURFACE && hasStereoVariants) {
|
||||
StereoscopicType const engineStereoscopicType = engine.getConfig().stereoscopicType;
|
||||
// Default materials are always compiled with either 'instanced' or 'multiview'.
|
||||
// So, we only verify compatibility if the engine is set up for stereo.
|
||||
if (engineStereoscopicType != StereoscopicType::NONE) {
|
||||
StereoscopicType materialStereoscopicType = StereoscopicType::NONE;
|
||||
materialParser->getStereoscopicType(&materialStereoscopicType);
|
||||
if (materialStereoscopicType != engineStereoscopicType) {
|
||||
CString name;
|
||||
materialParser->getName(&name);
|
||||
LOG(WARNING) << "The stereoscopic type in the compiled material '"
|
||||
<< name.c_str_safe() << "' is " << (int) materialStereoscopicType
|
||||
<< ", which is not compatible with the engine's setting "
|
||||
<< (int) engineStereoscopicType << ".";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return downcast(engine).createMaterial(*this, std::move(materialParser));
|
||||
}
|
||||
|
||||
FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition const& definition)
|
||||
: mDefinition(definition),
|
||||
mIsDefaultMaterial(builder->mDefaultMaterial),
|
||||
FMaterial::FMaterial(FEngine& engine, const Builder& builder,
|
||||
std::unique_ptr<MaterialParser> materialParser)
|
||||
: mIsDefaultMaterial(builder->mDefaultMaterial),
|
||||
mEngine(engine),
|
||||
mMaterialId(engine.getMaterialId()) {
|
||||
processSpecializationConstants(engine, builder);
|
||||
processPushConstants(engine);
|
||||
mMaterialId(engine.getMaterialId()),
|
||||
mMaterialParser(std::move(materialParser)) {
|
||||
MaterialParser* const parser = mMaterialParser.get();
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool const nameOk = parser->getName(&mName);
|
||||
assert_invariant(nameOk);
|
||||
|
||||
mFeatureLevel = [parser]() -> FeatureLevel {
|
||||
// code written this way so the IDE will complain when/if we add a FeatureLevel
|
||||
uint8_t level = 1;
|
||||
parser->getFeatureLevel(&level);
|
||||
assert_invariant(level <= 3);
|
||||
FeatureLevel featureLevel = FeatureLevel::FEATURE_LEVEL_1;
|
||||
switch (FeatureLevel(level)) {
|
||||
case FeatureLevel::FEATURE_LEVEL_0:
|
||||
case FeatureLevel::FEATURE_LEVEL_1:
|
||||
case FeatureLevel::FEATURE_LEVEL_2:
|
||||
case FeatureLevel::FEATURE_LEVEL_3:
|
||||
featureLevel = FeatureLevel(level);
|
||||
break;
|
||||
}
|
||||
return featureLevel;
|
||||
}();
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool success;
|
||||
|
||||
success = parser->getCacheId(&mCacheId);
|
||||
assert_invariant(success);
|
||||
|
||||
success = parser->getSIB(&mSamplerInterfaceBlock);
|
||||
assert_invariant(success);
|
||||
|
||||
success = parser->getUIB(&mUniformInterfaceBlock);
|
||||
assert_invariant(success);
|
||||
|
||||
if (UTILS_UNLIKELY(parser->getShaderLanguage() == ShaderLanguage::ESSL1)) {
|
||||
success = parser->getAttributeInfo(&mAttributeInfo);
|
||||
assert_invariant(success);
|
||||
|
||||
success = parser->getBindingUniformInfo(&mBindingUniformInfo);
|
||||
assert_invariant(success);
|
||||
}
|
||||
|
||||
// Older materials will not have a subpass chunk; this should not be an error.
|
||||
if (!parser->getSubpasses(&mSubpassInfo)) {
|
||||
mSubpassInfo.isValid = false;
|
||||
}
|
||||
|
||||
parser->getShading(&mShading);
|
||||
parser->getMaterialProperties(&mMaterialProperties);
|
||||
parser->getInterpolation(&mInterpolation);
|
||||
parser->getVertexDomain(&mVertexDomain);
|
||||
parser->getMaterialDomain(&mMaterialDomain);
|
||||
parser->getMaterialVariantFilterMask(&mVariantFilterMask);
|
||||
parser->getRequiredAttributes(&mRequiredAttributes);
|
||||
parser->getRefractionMode(&mRefractionMode);
|
||||
parser->getRefractionType(&mRefractionType);
|
||||
parser->getReflectionMode(&mReflectionMode);
|
||||
parser->getTransparencyMode(&mTransparencyMode);
|
||||
parser->getDoubleSided(&mDoubleSided);
|
||||
parser->getCullingMode(&mCullingMode);
|
||||
|
||||
if (mShading == Shading::UNLIT) {
|
||||
parser->hasShadowMultiplier(&mHasShadowMultiplier);
|
||||
}
|
||||
|
||||
mIsVariantLit = mShading != Shading::UNLIT || mHasShadowMultiplier;
|
||||
|
||||
// color write
|
||||
bool colorWrite = false;
|
||||
parser->getColorWrite(&colorWrite);
|
||||
mRasterState.colorWrite = colorWrite;
|
||||
|
||||
// depth test
|
||||
bool depthTest = false;
|
||||
parser->getDepthTest(&depthTest);
|
||||
mRasterState.depthFunc = depthTest ? RasterState::DepthFunc::GE : RasterState::DepthFunc::A;
|
||||
|
||||
// if doubleSided() was called we override culling()
|
||||
bool doubleSideSet = false;
|
||||
parser->getDoubleSidedSet(&doubleSideSet);
|
||||
if (doubleSideSet) {
|
||||
mDoubleSidedCapability = true;
|
||||
mRasterState.culling = mDoubleSided ? CullingMode::NONE : mCullingMode;
|
||||
} else {
|
||||
mRasterState.culling = mCullingMode;
|
||||
}
|
||||
|
||||
// specular anti-aliasing
|
||||
parser->hasSpecularAntiAliasing(&mSpecularAntiAliasing);
|
||||
if (mSpecularAntiAliasing) {
|
||||
parser->getSpecularAntiAliasingVariance(&mSpecularAntiAliasingVariance);
|
||||
parser->getSpecularAntiAliasingThreshold(&mSpecularAntiAliasingThreshold);
|
||||
}
|
||||
|
||||
parser->hasCustomDepthShader(&mHasCustomDepthShader);
|
||||
|
||||
bool const isLit = mIsVariantLit || mHasShadowMultiplier;
|
||||
bool const isSSR = mReflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
mRefractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(mVariantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
mPerViewLayoutIndex = ColorPassDescriptorSet::getIndex(isLit, isSSR, hasFog);
|
||||
|
||||
processBlendingMode(parser);
|
||||
processSpecializationConstants(engine, builder, parser);
|
||||
processPushConstants(engine, parser);
|
||||
processDescriptorSets(engine, parser);
|
||||
precacheDepthVariants(engine);
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
@@ -158,7 +373,7 @@ FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition
|
||||
matdbg::DebugServer* server = downcast(engine).debug.server;
|
||||
if (UTILS_UNLIKELY(server)) {
|
||||
auto const details = builder.mImpl;
|
||||
mDebuggerId = server->addMaterial(mDefinition.name, details->mPayload, details->mSize, this);
|
||||
mDebuggerId = server->addMaterial(mName, details->mPayload, details->mSize, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -168,9 +383,9 @@ FMaterial::~FMaterial() noexcept = default;
|
||||
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
|
||||
// Note: This API is not public at the moment, so it's okay to have some debugging logs
|
||||
// and extra checks.
|
||||
if (mDefinition.materialDomain == MaterialDomain::SURFACE &&
|
||||
!mIsDefaultMaterial &&
|
||||
!mDefinition.hasCustomDepthShader) {
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE &&
|
||||
!mIsDefaultMaterial &&
|
||||
!mHasCustomDepthShader) {
|
||||
// it would be unsafe to invalidate any of the cached depth variant
|
||||
if (UTILS_UNLIKELY(!((variantMask & Variant::DEP) && !(variantValue & Variant::DEP)))) {
|
||||
char variantMaskString[16];
|
||||
@@ -187,6 +402,7 @@ void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantV
|
||||
}
|
||||
|
||||
void FMaterial::terminate(FEngine& engine) {
|
||||
|
||||
if (mDefaultMaterialInstance) {
|
||||
mDefaultMaterialInstance->setDefaultInstance(false);
|
||||
engine.destroy(mDefaultMaterialInstance);
|
||||
@@ -213,24 +429,28 @@ void FMaterial::terminate(FEngine& engine) {
|
||||
#endif
|
||||
|
||||
destroyPrograms(engine);
|
||||
engine.getMaterialCache().release(engine, mDefinition.getMaterialParser());
|
||||
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
mPerViewDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
mPerViewDescriptorSetLayoutVsm.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
mDescriptorSetLayout.terminate(engine.getDescriptorSetLayoutFactory(), driver);
|
||||
}
|
||||
|
||||
filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
|
||||
Variant const variant, bool const useVsmDescriptorSetLayout) const noexcept {
|
||||
if (Variant::isValidDepthVariant(variant)) {
|
||||
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
|
||||
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
|
||||
return mEngine.getPerViewDescriptorSetLayoutDepthVariant();
|
||||
}
|
||||
if (Variant::isSSRVariant(variant)) {
|
||||
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
|
||||
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
|
||||
return mEngine.getPerViewDescriptorSetLayoutSsrVariant();
|
||||
}
|
||||
if (useVsmDescriptorSetLayout) {
|
||||
assert_invariant(mDefinition.materialDomain == MaterialDomain::SURFACE);
|
||||
return mDefinition.perViewDescriptorSetLayoutVsm;
|
||||
assert_invariant(mMaterialDomain == MaterialDomain::SURFACE);
|
||||
return mPerViewDescriptorSetLayoutVsm;
|
||||
}
|
||||
return mDefinition.perViewDescriptorSetLayout;
|
||||
return mPerViewDescriptorSetLayout;
|
||||
}
|
||||
|
||||
void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
@@ -287,35 +507,25 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
|
||||
|
||||
FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
|
||||
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
|
||||
mDefaultMaterialInstance = mEngine.createMaterialInstance(this,
|
||||
mDefinition.name.c_str());
|
||||
mDefaultMaterialInstance = mEngine.createMaterialInstance(this, mName.c_str());
|
||||
mDefaultMaterialInstance->setDefaultInstance(true);
|
||||
}
|
||||
return mDefaultMaterialInstance;
|
||||
}
|
||||
|
||||
bool FMaterial::hasParameter(const char* name) const noexcept {
|
||||
return mDefinition.uniformInterfaceBlock.hasField(name) ||
|
||||
mDefinition.samplerInterfaceBlock.hasSampler(name) ||
|
||||
mDefinition.subpassInfo.name == CString(name);
|
||||
return mUniformInterfaceBlock.hasField(name) ||
|
||||
mSamplerInterfaceBlock.hasSampler(name) ||
|
||||
mSubpassInfo.name == CString(name);
|
||||
}
|
||||
|
||||
bool FMaterial::isSampler(const char* name) const noexcept {
|
||||
return mDefinition.samplerInterfaceBlock.hasSampler(name);
|
||||
return mSamplerInterfaceBlock.hasSampler(name);
|
||||
}
|
||||
|
||||
BufferInterfaceBlock::FieldInfo const* FMaterial::reflect(
|
||||
std::string_view const name) const noexcept {
|
||||
return mDefinition.uniformInterfaceBlock.getFieldInfo(name);
|
||||
}
|
||||
|
||||
MaterialParser const& FMaterial::getMaterialParser() const noexcept {
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
if (mEditedMaterialParser) {
|
||||
return *mEditedMaterialParser;
|
||||
}
|
||||
#endif
|
||||
return mDefinition.getMaterialParser();
|
||||
return mUniformInterfaceBlock.getFieldInfo(name);
|
||||
}
|
||||
|
||||
bool FMaterial::hasVariant(Variant const variant) const noexcept {
|
||||
@@ -333,10 +543,10 @@ bool FMaterial::hasVariant(Variant const variant) const noexcept {
|
||||
return false;
|
||||
}
|
||||
const ShaderModel sm = mEngine.getShaderModel();
|
||||
if (!mDefinition.getMaterialParser().hasShader(sm, vertexVariant, ShaderStage::VERTEX)) {
|
||||
if (!mMaterialParser->hasShader(sm, vertexVariant, ShaderStage::VERTEX)) {
|
||||
return false;
|
||||
}
|
||||
if (!mDefinition.getMaterialParser().hasShader(sm, fragmentVariant, ShaderStage::FRAGMENT)) {
|
||||
if (!mMaterialParser->hasShader(sm, fragmentVariant, ShaderStage::FRAGMENT)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -344,7 +554,7 @@ bool FMaterial::hasVariant(Variant const variant) const noexcept {
|
||||
|
||||
void FMaterial::prepareProgramSlow(Variant const variant,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
assert_invariant(mEngine.hasFeatureLevel(mDefinition.featureLevel));
|
||||
assert_invariant(mEngine.hasFeatureLevel(mFeatureLevel));
|
||||
switch (getMaterialDomain()) {
|
||||
case MaterialDomain::SURFACE:
|
||||
getSurfaceProgramSlow(variant, priorityQueue);
|
||||
@@ -395,15 +605,13 @@ Program FMaterial::getProgramWithVariants(
|
||||
* Vertex shader
|
||||
*/
|
||||
|
||||
MaterialParser const& parser = getMaterialParser();
|
||||
|
||||
ShaderContent& vsBuilder = engine.getVertexShaderContent();
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool const vsOK = parser.getShader(vsBuilder, sm,
|
||||
UTILS_UNUSED_IN_RELEASE bool const vsOK = mMaterialParser->getShader(vsBuilder, sm,
|
||||
vertexVariant, ShaderStage::VERTEX);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(isNoop || (vsOK && !vsBuilder.empty()))
|
||||
<< "The material '" << mDefinition.name.c_str()
|
||||
<< "The material '" << mName.c_str()
|
||||
<< "' has not been compiled to include the required GLSL or SPIR-V chunks for the "
|
||||
"vertex shader (variant="
|
||||
<< +variant.key << ", filtered=" << +vertexVariant.key << ").";
|
||||
@@ -414,11 +622,11 @@ Program FMaterial::getProgramWithVariants(
|
||||
|
||||
ShaderContent& fsBuilder = engine.getFragmentShaderContent();
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE bool const fsOK = parser.getShader(fsBuilder, sm,
|
||||
UTILS_UNUSED_IN_RELEASE bool const fsOK = mMaterialParser->getShader(fsBuilder, sm,
|
||||
fragmentVariant, ShaderStage::FRAGMENT);
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(isNoop || (fsOK && !fsBuilder.empty()))
|
||||
<< "The material '" << mDefinition.name.c_str()
|
||||
<< "The material '" << mName.c_str()
|
||||
<< "' has not been compiled to include the required GLSL or SPIR-V chunks for the "
|
||||
"fragment shader (variant="
|
||||
<< +variant.key << ", filtered=" << +fragmentVariant.key << ").";
|
||||
@@ -426,8 +634,8 @@ Program FMaterial::getProgramWithVariants(
|
||||
Program program;
|
||||
program.shader(ShaderStage::VERTEX, vsBuilder.data(), vsBuilder.size())
|
||||
.shader(ShaderStage::FRAGMENT, fsBuilder.data(), fsBuilder.size())
|
||||
.shaderLanguage(parser.getShaderLanguage())
|
||||
.diagnostics(mDefinition.name,
|
||||
.shaderLanguage(mMaterialParser->getShaderLanguage())
|
||||
.diagnostics(mName,
|
||||
[variant, vertexVariant, fragmentVariant](utils::CString const& name,
|
||||
io::ostream& out) -> io::ostream& {
|
||||
return out << name.c_str_safe() << ", variant=(" << io::hex << +variant.key
|
||||
@@ -436,26 +644,26 @@ Program FMaterial::getProgramWithVariants(
|
||||
<< io::hex << +fragmentVariant.key << io::dec << ")";
|
||||
});
|
||||
|
||||
if (UTILS_UNLIKELY(parser.getShaderLanguage() == ShaderLanguage::ESSL1)) {
|
||||
assert_invariant(!mDefinition.bindingUniformInfo.empty());
|
||||
for (auto const& [index, name, uniforms] : mDefinition.bindingUniformInfo) {
|
||||
if (UTILS_UNLIKELY(mMaterialParser->getShaderLanguage() == ShaderLanguage::ESSL1)) {
|
||||
assert_invariant(!mBindingUniformInfo.empty());
|
||||
for (auto const& [index, name, uniforms] : mBindingUniformInfo) {
|
||||
program.uniforms(uint32_t(index), name, uniforms);
|
||||
}
|
||||
program.attributes(mDefinition.attributeInfo);
|
||||
program.attributes(mAttributeInfo);
|
||||
}
|
||||
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_VIEW,
|
||||
mDefinition.programDescriptorBindings[+DescriptorSetBindingPoints::PER_VIEW]);
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_VIEW]);
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
mDefinition.programDescriptorBindings[+DescriptorSetBindingPoints::PER_RENDERABLE]);
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_RENDERABLE]);
|
||||
program.descriptorBindings(+DescriptorSetBindingPoints::PER_MATERIAL,
|
||||
mDefinition.programDescriptorBindings[+DescriptorSetBindingPoints::PER_MATERIAL]);
|
||||
mProgramDescriptorBindings[+DescriptorSetBindingPoints::PER_MATERIAL]);
|
||||
program.specializationConstants(mSpecializationConstants);
|
||||
|
||||
program.pushConstants(ShaderStage::VERTEX, mPushConstants[uint8_t(ShaderStage::VERTEX)]);
|
||||
program.pushConstants(ShaderStage::FRAGMENT, mPushConstants[uint8_t(ShaderStage::FRAGMENT)]);
|
||||
|
||||
program.cacheId(hash::combine(size_t(mDefinition.cacheId), variant.key));
|
||||
program.cacheId(hash::combine(size_t(mCacheId), variant.key));
|
||||
|
||||
return program;
|
||||
}
|
||||
@@ -478,7 +686,7 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const
|
||||
}
|
||||
}
|
||||
|
||||
auto const program = driverApi.createProgram(std::move(p), CString{ mDefinition.name });
|
||||
auto const program = driverApi.createProgram(std::move(p), mName);
|
||||
assert_invariant(program);
|
||||
mCachedPrograms[variant.key] = program;
|
||||
|
||||
@@ -496,7 +704,7 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant const variant) const
|
||||
size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const noexcept {
|
||||
count = std::min(count, getParameterCount());
|
||||
|
||||
const auto& uniforms = mDefinition.uniformInterfaceBlock.getFieldInfoList();
|
||||
const auto& uniforms = mUniformInterfaceBlock.getFieldInfoList();
|
||||
size_t i = 0;
|
||||
size_t const uniformCount = std::min(count, size_t(uniforms.size()));
|
||||
for ( ; i < uniformCount; i++) {
|
||||
@@ -510,7 +718,7 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
|
||||
info.precision = uniformInfo.precision;
|
||||
}
|
||||
|
||||
const auto& samplers = mDefinition.samplerInterfaceBlock.getSamplerInfoList();
|
||||
const auto& samplers = mSamplerInterfaceBlock.getSamplerInfoList();
|
||||
size_t const samplerCount = samplers.size();
|
||||
for (size_t j = 0; i < count && j < samplerCount; i++, j++) {
|
||||
ParameterInfo& info = parameters[i];
|
||||
@@ -523,14 +731,14 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
|
||||
info.precision = samplerInfo.precision;
|
||||
}
|
||||
|
||||
if (mDefinition.subpassInfo.isValid && i < count) {
|
||||
if (mSubpassInfo.isValid && i < count) {
|
||||
ParameterInfo& info = parameters[i];
|
||||
info.name = mDefinition.subpassInfo.name.c_str();
|
||||
info.name = mSubpassInfo.name.c_str();
|
||||
info.isSampler = false;
|
||||
info.isSubpass = true;
|
||||
info.subpassType = mDefinition.subpassInfo.type;
|
||||
info.subpassType = mSubpassInfo.type;
|
||||
info.count = 1;
|
||||
info.precision = mDefinition.subpassInfo.precision;
|
||||
info.precision = mSubpassInfo.precision;
|
||||
}
|
||||
|
||||
return count;
|
||||
@@ -543,7 +751,7 @@ size_t FMaterial::getParameters(ParameterInfo* parameters, size_t count) const n
|
||||
// Material Debugger is attached. The only editable features of a material package are the shader
|
||||
// source strings, so here we trigger a rebuild of the HwProgram objects.
|
||||
void FMaterial::applyPendingEdits() noexcept {
|
||||
const char* name = mDefinition.name.c_str();
|
||||
const char* name = mName.c_str();
|
||||
DLOG(INFO) << "Applying edits to " << (name ? name : "(untitled)");
|
||||
destroyPrograms(mEngine); // FIXME: this will not destroy the shared variants
|
||||
latchPendingEdits();
|
||||
@@ -561,7 +769,7 @@ bool FMaterial::hasPendingEdits() const noexcept {
|
||||
|
||||
void FMaterial::latchPendingEdits() noexcept {
|
||||
std::lock_guard const lock(mPendingEditsLock);
|
||||
mEditedMaterialParser = std::move(mPendingEdits);
|
||||
mMaterialParser = std::move(mPendingEdits);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -578,7 +786,7 @@ void FMaterial::onEditCallback(void* userdata, const CString&, const void* packa
|
||||
|
||||
// This is called on a web server thread, so we defer clearing the program cache
|
||||
// and swapping out the MaterialParser until the next getProgram call.
|
||||
std::unique_ptr<MaterialParser> pending = MaterialDefinition::createParser(
|
||||
std::unique_ptr<MaterialParser> pending = createParser(
|
||||
engine.getBackend(), engine.getShaderLanguage(), packageData, packageSize);
|
||||
material->setPendingEdits(std::move(pending));
|
||||
}
|
||||
@@ -624,9 +832,9 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
DriverApi& driverApi = engine.getDriverApi();
|
||||
auto& cachedPrograms = mCachedPrograms;
|
||||
|
||||
switch (mDefinition.materialDomain) {
|
||||
switch (mMaterialDomain) {
|
||||
case MaterialDomain::SURFACE: {
|
||||
if (mIsDefaultMaterial || mDefinition.hasCustomDepthShader) {
|
||||
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) {
|
||||
if ((k & variantMask) == variantValue) {
|
||||
@@ -692,8 +900,8 @@ void FMaterial::destroyPrograms(FEngine& engine,
|
||||
}
|
||||
|
||||
std::optional<uint32_t> FMaterial::getSpecializationConstantId(std::string_view const name) const noexcept {
|
||||
auto const pos = mDefinition.specializationConstantsNameToIndex.find(name);
|
||||
if (pos != mDefinition.specializationConstantsNameToIndex.end()) {
|
||||
auto const pos = mSpecializationConstantsNameToIndex.find(name);
|
||||
if (pos != mSpecializationConstantsNameToIndex.end()) {
|
||||
return pos->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
||||
}
|
||||
return std::nullopt;
|
||||
@@ -701,11 +909,11 @@ std::optional<uint32_t> FMaterial::getSpecializationConstantId(std::string_view
|
||||
|
||||
template<typename T, typename>
|
||||
bool FMaterial::setConstant(uint32_t id, T value) noexcept {
|
||||
size_t const maxId = mDefinition.materialConstants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
||||
size_t const maxId = mMaterialConstants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
||||
if (UTILS_LIKELY(id < maxId)) {
|
||||
if (id >= CONFIG_MAX_RESERVED_SPEC_CONSTANTS) {
|
||||
// Constant from the material itself (as opposed to the reserved ones)
|
||||
auto const& constant = mDefinition.materialConstants[id - CONFIG_MAX_RESERVED_SPEC_CONSTANTS];
|
||||
auto const& constant = mMaterialConstants[id - CONFIG_MAX_RESERVED_SPEC_CONSTANTS];
|
||||
using ConstantType = ConstantType;
|
||||
switch (constant.type) {
|
||||
case ConstantType::INT:
|
||||
@@ -738,8 +946,98 @@ bool FMaterial::setConstant(uint32_t id, T value) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& builder) {
|
||||
// TODO: migrate some or all of this into MaterialDefinition.
|
||||
void FMaterial::processBlendingMode(MaterialParser const* const parser) {
|
||||
parser->getBlendingMode(&mBlendingMode);
|
||||
|
||||
if (mBlendingMode == BlendingMode::MASKED) {
|
||||
parser->getMaskThreshold(&mMaskThreshold);
|
||||
}
|
||||
|
||||
if (mBlendingMode == BlendingMode::CUSTOM) {
|
||||
parser->getCustomBlendFunction(&mCustomBlendFunctions);
|
||||
}
|
||||
|
||||
// blending mode
|
||||
switch (mBlendingMode) {
|
||||
// Do not change the MASKED behavior without checking for regressions with
|
||||
// AlphaBlendModeTest and TextureLinearInterpolationTest, with and without
|
||||
// View::BlendMode::TRANSLUCENT.
|
||||
case BlendingMode::MASKED:
|
||||
case BlendingMode::OPAQUE:
|
||||
mRasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionDstRGB = BlendFunction::ZERO;
|
||||
mRasterState.blendFunctionDstAlpha = BlendFunction::ZERO;
|
||||
mRasterState.depthWrite = true;
|
||||
break;
|
||||
case BlendingMode::TRANSPARENT:
|
||||
case BlendingMode::FADE:
|
||||
mRasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA;
|
||||
mRasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
|
||||
mRasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::ADD:
|
||||
mRasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionDstRGB = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionDstAlpha = BlendFunction::ONE;
|
||||
mRasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::MULTIPLY:
|
||||
mRasterState.blendFunctionSrcRGB = BlendFunction::ZERO;
|
||||
mRasterState.blendFunctionSrcAlpha = BlendFunction::ZERO;
|
||||
mRasterState.blendFunctionDstRGB = BlendFunction::SRC_COLOR;
|
||||
mRasterState.blendFunctionDstAlpha = BlendFunction::SRC_COLOR;
|
||||
mRasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::SCREEN:
|
||||
mRasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
mRasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_COLOR;
|
||||
mRasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_COLOR;
|
||||
mRasterState.depthWrite = false;
|
||||
break;
|
||||
case BlendingMode::CUSTOM:
|
||||
mRasterState.blendFunctionSrcRGB = mCustomBlendFunctions[0];
|
||||
mRasterState.blendFunctionSrcAlpha = mCustomBlendFunctions[1];
|
||||
mRasterState.blendFunctionDstRGB = mCustomBlendFunctions[2];
|
||||
mRasterState.blendFunctionDstAlpha = mCustomBlendFunctions[3];
|
||||
mRasterState.depthWrite = false;
|
||||
}
|
||||
|
||||
// depth write
|
||||
bool depthWriteSet = false;
|
||||
parser->getDepthWriteSet(&depthWriteSet);
|
||||
if (depthWriteSet) {
|
||||
bool depthWrite = false;
|
||||
parser->getDepthWrite(&depthWrite);
|
||||
mRasterState.depthWrite = depthWrite;
|
||||
}
|
||||
|
||||
// alpha to coverage
|
||||
bool alphaToCoverageSet = false;
|
||||
parser->getAlphaToCoverageSet(&alphaToCoverageSet);
|
||||
if (alphaToCoverageSet) {
|
||||
bool alphaToCoverage = false;
|
||||
parser->getAlphaToCoverage(&alphaToCoverage);
|
||||
mRasterState.alphaToCoverage = alphaToCoverage;
|
||||
} else {
|
||||
mRasterState.alphaToCoverage = mBlendingMode == BlendingMode::MASKED;
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& builder,
|
||||
MaterialParser const* const parser) {
|
||||
// Older materials won't have a constants chunk, but that's okay.
|
||||
parser->getConstants(&mMaterialConstants);
|
||||
for (size_t i = 0, c = mMaterialConstants.size(); i < c; i++) {
|
||||
auto& item = mMaterialConstants[i];
|
||||
// the key can be a string_view because mMaterialConstant owns the CString
|
||||
std::string_view const key{ item.name.data(), item.name.size() };
|
||||
mSpecializationConstantsNameToIndex[key] = i;
|
||||
}
|
||||
|
||||
// Verify that all the constant specializations exist in the material and that their types match.
|
||||
// The first specialization constants are defined internally by Filament.
|
||||
@@ -762,8 +1060,7 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
|
||||
bool const powerVrShaderWorkarounds =
|
||||
engine.getDriverApi().isWorkaroundNeeded(Workaround::POWER_VR_SHADER_WORKAROUNDS);
|
||||
|
||||
mSpecializationConstants.reserve(
|
||||
mDefinition.materialConstants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
|
||||
mSpecializationConstants.reserve(mMaterialConstants.size() + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
|
||||
mSpecializationConstants.push_back({
|
||||
+ReservedSpecializationConstants::BACKEND_FEATURE_LEVEL,
|
||||
int(engine.getSupportedFeatureLevel()) });
|
||||
@@ -797,8 +1094,7 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
|
||||
mSpecializationConstants.push_back({
|
||||
+ReservedSpecializationConstants::CONFIG_SHADOW_SAMPLING_METHOD,
|
||||
int32_t(builder->mShadowSamplingQuality) });
|
||||
if (UTILS_UNLIKELY(
|
||||
mDefinition.getMaterialParser().getShaderLanguage() == ShaderLanguage::ESSL1)) {
|
||||
if (UTILS_UNLIKELY(parser->getShaderLanguage() == ShaderLanguage::ESSL1)) {
|
||||
// The actual value of this spec-constant is set in the OpenGLDriver backend.
|
||||
mSpecializationConstants.push_back({
|
||||
+ReservedSpecializationConstants::CONFIG_SRGB_SWAPCHAIN_EMULATION,
|
||||
@@ -807,29 +1103,29 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
|
||||
|
||||
for (auto const& [name, value] : builder->mConstantSpecializations) {
|
||||
std::string_view const key{ name.data(), name.size() };
|
||||
auto pos = mDefinition.specializationConstantsNameToIndex.find(key);
|
||||
FILAMENT_CHECK_PRECONDITION(pos != mDefinition.specializationConstantsNameToIndex.end())
|
||||
<< "The material " << mDefinition.name.c_str_safe()
|
||||
auto pos = mSpecializationConstantsNameToIndex.find(key);
|
||||
FILAMENT_CHECK_PRECONDITION(pos != mSpecializationConstantsNameToIndex.end())
|
||||
<< "The material " << mName.c_str_safe()
|
||||
<< " does not have a constant parameter named " << name.c_str() << ".";
|
||||
constexpr char const* const types[3] = {"an int", "a float", "a bool"};
|
||||
auto const& constant = mDefinition.materialConstants[pos->second];
|
||||
auto const& constant = mMaterialConstants[pos->second];
|
||||
switch (constant.type) {
|
||||
case ConstantType::INT:
|
||||
FILAMENT_CHECK_PRECONDITION(std::holds_alternative<int32_t>(value))
|
||||
<< "The constant parameter " << name.c_str() << " on material "
|
||||
<< mDefinition.name.c_str_safe() << " is of type int, but " << types[value.index()]
|
||||
<< mName.c_str_safe() << " is of type int, but " << types[value.index()]
|
||||
<< " was provided.";
|
||||
break;
|
||||
case ConstantType::FLOAT:
|
||||
FILAMENT_CHECK_PRECONDITION(std::holds_alternative<float>(value))
|
||||
<< "The constant parameter " << name.c_str() << " on material "
|
||||
<< mDefinition.name.c_str_safe() << " is of type float, but " << types[value.index()]
|
||||
<< mName.c_str_safe() << " is of type float, but " << types[value.index()]
|
||||
<< " was provided.";
|
||||
break;
|
||||
case ConstantType::BOOL:
|
||||
FILAMENT_CHECK_PRECONDITION(std::holds_alternative<bool>(value))
|
||||
<< "The constant parameter " << name.c_str() << " on material "
|
||||
<< mDefinition.name.c_str_safe() << " is of type bool, but " << types[value.index()]
|
||||
<< mName.c_str_safe() << " is of type bool, but " << types[value.index()]
|
||||
<< " was provided.";
|
||||
break;
|
||||
}
|
||||
@@ -838,9 +1134,7 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processPushConstants(FEngine&) {
|
||||
// TODO: move some or all of this into MaterialDefinition.
|
||||
|
||||
void FMaterial::processPushConstants(FEngine&, MaterialParser const* parser) {
|
||||
FixedCapacityVector<Program::PushConstant>& vertexConstants =
|
||||
mPushConstants[uint8_t(ShaderStage::VERTEX)];
|
||||
FixedCapacityVector<Program::PushConstant>& fragmentConstants =
|
||||
@@ -848,7 +1142,7 @@ void FMaterial::processPushConstants(FEngine&) {
|
||||
|
||||
CString structVarName;
|
||||
FixedCapacityVector<MaterialPushConstant> pushConstants;
|
||||
mDefinition.getMaterialParser().getPushConstants(&structVarName, &pushConstants);
|
||||
parser->getPushConstants(&structVarName, &pushConstants);
|
||||
|
||||
vertexConstants.reserve(pushConstants.size());
|
||||
fragmentConstants.reserve(pushConstants.size());
|
||||
@@ -902,9 +1196,9 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
}
|
||||
|
||||
// if possible pre-cache all depth variants from the default material
|
||||
if (mDefinition.materialDomain == MaterialDomain::SURFACE &&
|
||||
if (mMaterialDomain == MaterialDomain::SURFACE &&
|
||||
!mIsDefaultMaterial &&
|
||||
!mDefinition.hasCustomDepthShader) {
|
||||
!mHasCustomDepthShader) {
|
||||
FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial();
|
||||
assert_invariant(pDefaultMaterial);
|
||||
auto const allDepthVariants = VariantUtils::getDepthVariants();
|
||||
@@ -915,9 +1209,63 @@ void FMaterial::precacheDepthVariants(FEngine& engine) {
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterial::processDescriptorSets(FEngine& engine, MaterialParser const* const parser) {
|
||||
UTILS_UNUSED_IN_RELEASE bool success;
|
||||
|
||||
success = parser->getDescriptorBindings(&mProgramDescriptorBindings);
|
||||
assert_invariant(success);
|
||||
|
||||
backend::DescriptorSetLayout descriptorSetLayout;
|
||||
success = parser->getDescriptorSetLayout(&descriptorSetLayout);
|
||||
assert_invariant(success);
|
||||
|
||||
// get the PER_VIEW descriptor binding info
|
||||
bool const isLit = mIsVariantLit || mHasShadowMultiplier;
|
||||
bool const isSSR = mReflectionMode == ReflectionMode::SCREEN_SPACE ||
|
||||
mRefractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(mVariantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
auto perViewDescriptorSetLayout = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
mMaterialDomain, isLit, isSSR, hasFog, false);
|
||||
|
||||
auto perViewDescriptorSetLayoutVsm = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
mMaterialDomain, isLit, isSSR, hasFog, true);
|
||||
|
||||
// set the labels
|
||||
descriptorSetLayout.label = CString{ mName }.append("_perMat");
|
||||
perViewDescriptorSetLayout.label = CString{ mName }.append("_perView");
|
||||
perViewDescriptorSetLayoutVsm.label = CString{ mName }.append("_perViewVsm");
|
||||
|
||||
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
|
||||
for (auto&& [bindingPoint, descriptorSetLayout] : {
|
||||
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
descriptor_sets::getPerRenderableLayout() },
|
||||
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
|
||||
perViewDescriptorSetLayout }}) {
|
||||
Program::DescriptorBindingsInfo& descriptors = mProgramDescriptorBindings[+bindingPoint];
|
||||
descriptors.reserve(descriptorSetLayout.bindings.size());
|
||||
for (auto const& entry: descriptorSetLayout.bindings) {
|
||||
auto const& name = descriptor_sets::getDescriptorName(bindingPoint, entry.binding);
|
||||
descriptors.push_back({ name, entry.type, entry.binding });
|
||||
}
|
||||
}
|
||||
|
||||
mDescriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(descriptorSetLayout) };
|
||||
|
||||
mPerViewDescriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(perViewDescriptorSetLayout) };
|
||||
|
||||
mPerViewDescriptorSetLayoutVsm = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(perViewDescriptorSetLayoutVsm) };
|
||||
}
|
||||
|
||||
descriptor_binding_t FMaterial::getSamplerBinding(
|
||||
std::string_view const& name) const {
|
||||
return mDefinition.samplerInterfaceBlock.getSamplerInfo(name)->binding;
|
||||
return mSamplerInterfaceBlock.getSamplerInfo(name)->binding;
|
||||
}
|
||||
|
||||
template bool FMaterial::setConstant<int32_t>(uint32_t id, int32_t value) noexcept;
|
||||
|
||||
@@ -46,8 +46,15 @@
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <new>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -65,7 +72,7 @@ class FEngine;
|
||||
class FMaterial : public Material {
|
||||
public:
|
||||
FMaterial(FEngine& engine, const Builder& builder,
|
||||
MaterialDefinition const& definition);
|
||||
std::unique_ptr<MaterialParser> materialParser);
|
||||
~FMaterial() noexcept;
|
||||
|
||||
class DefaultMaterialBuilder : public Builder {
|
||||
@@ -78,12 +85,12 @@ public:
|
||||
|
||||
// return the uniform interface block for this material
|
||||
const BufferInterfaceBlock& getUniformInterfaceBlock() const noexcept {
|
||||
return mDefinition.uniformInterfaceBlock;
|
||||
return mUniformInterfaceBlock;
|
||||
}
|
||||
|
||||
DescriptorSetLayout const& getPerViewDescriptorSetLayout() const noexcept {
|
||||
assert_invariant(mDefinition.materialDomain == MaterialDomain::POST_PROCESS);
|
||||
return mDefinition.perViewDescriptorSetLayout;
|
||||
assert_invariant(mMaterialDomain == MaterialDomain::POST_PROCESS);
|
||||
return mPerViewDescriptorSetLayout;
|
||||
}
|
||||
|
||||
DescriptorSetLayout const& getPerViewDescriptorSetLayout(
|
||||
@@ -94,11 +101,11 @@ public:
|
||||
// also use the default material's layout.
|
||||
DescriptorSetLayout const& getDescriptorSetLayout(Variant variant = {}) const noexcept {
|
||||
if (!isSharedVariant(variant)) {
|
||||
return mDefinition.descriptorSetLayout;
|
||||
return mDescriptorSetLayout;
|
||||
}
|
||||
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
|
||||
if (UTILS_UNLIKELY(!pDefaultMaterial)) {
|
||||
return mDefinition.descriptorSetLayout;
|
||||
return mDescriptorSetLayout;
|
||||
}
|
||||
return pDefaultMaterial->getDescriptorSetLayout();
|
||||
}
|
||||
@@ -173,58 +180,58 @@ public:
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgramWithMATDBG(Variant variant) const noexcept;
|
||||
|
||||
bool isVariantLit() const noexcept { return mDefinition.isVariantLit; }
|
||||
bool isVariantLit() const noexcept { return mIsVariantLit; }
|
||||
|
||||
const utils::CString& getName() const noexcept { return mDefinition.name; }
|
||||
backend::FeatureLevel getFeatureLevel() const noexcept { return mDefinition.featureLevel; }
|
||||
backend::RasterState getRasterState() const noexcept { return mDefinition.rasterState; }
|
||||
const utils::CString& getName() const noexcept { return mName; }
|
||||
backend::FeatureLevel getFeatureLevel() const noexcept { return mFeatureLevel; }
|
||||
backend::RasterState getRasterState() const noexcept { return mRasterState; }
|
||||
uint32_t getId() const noexcept { return mMaterialId; }
|
||||
|
||||
UserVariantFilterMask getSupportedVariants() const noexcept {
|
||||
return UserVariantFilterMask(UserVariantFilterBit::ALL) & ~mDefinition.variantFilterMask;
|
||||
return UserVariantFilterMask(UserVariantFilterBit::ALL) & ~mVariantFilterMask;
|
||||
}
|
||||
|
||||
Shading getShading() const noexcept { return mDefinition.shading; }
|
||||
Interpolation getInterpolation() const noexcept { return mDefinition.interpolation; }
|
||||
BlendingMode getBlendingMode() const noexcept { return mDefinition.blendingMode; }
|
||||
VertexDomain getVertexDomain() const noexcept { return mDefinition.vertexDomain; }
|
||||
MaterialDomain getMaterialDomain() const noexcept { return mDefinition.materialDomain; }
|
||||
CullingMode getCullingMode() const noexcept { return mDefinition.cullingMode; }
|
||||
TransparencyMode getTransparencyMode() const noexcept { return mDefinition.transparencyMode; }
|
||||
bool isColorWriteEnabled() const noexcept { return mDefinition.rasterState.colorWrite; }
|
||||
bool isDepthWriteEnabled() const noexcept { return mDefinition.rasterState.depthWrite; }
|
||||
Shading getShading() const noexcept { return mShading; }
|
||||
Interpolation getInterpolation() const noexcept { return mInterpolation; }
|
||||
BlendingMode getBlendingMode() const noexcept { return mBlendingMode; }
|
||||
VertexDomain getVertexDomain() const noexcept { return mVertexDomain; }
|
||||
MaterialDomain getMaterialDomain() const noexcept { return mMaterialDomain; }
|
||||
CullingMode getCullingMode() const noexcept { return mCullingMode; }
|
||||
TransparencyMode getTransparencyMode() const noexcept { return mTransparencyMode; }
|
||||
bool isColorWriteEnabled() const noexcept { return mRasterState.colorWrite; }
|
||||
bool isDepthWriteEnabled() const noexcept { return mRasterState.depthWrite; }
|
||||
bool isDepthCullingEnabled() const noexcept {
|
||||
return mDefinition.rasterState.depthFunc != backend::RasterState::DepthFunc::A;
|
||||
return mRasterState.depthFunc != backend::RasterState::DepthFunc::A;
|
||||
}
|
||||
bool isDoubleSided() const noexcept { return mDefinition.doubleSided; }
|
||||
bool hasDoubleSidedCapability() const noexcept { return mDefinition.doubleSidedCapability; }
|
||||
bool isAlphaToCoverageEnabled() const noexcept { return mDefinition.rasterState.alphaToCoverage; }
|
||||
float getMaskThreshold() const noexcept { return mDefinition.maskThreshold; }
|
||||
bool hasShadowMultiplier() const noexcept { return mDefinition.hasShadowMultiplier; }
|
||||
AttributeBitset getRequiredAttributes() const noexcept { return mDefinition.requiredAttributes; }
|
||||
RefractionMode getRefractionMode() const noexcept { return mDefinition.refractionMode; }
|
||||
RefractionType getRefractionType() const noexcept { return mDefinition.refractionType; }
|
||||
ReflectionMode getReflectionMode() const noexcept { return mDefinition.reflectionMode; }
|
||||
bool isDoubleSided() const noexcept { return mDoubleSided; }
|
||||
bool hasDoubleSidedCapability() const noexcept { return mDoubleSidedCapability; }
|
||||
bool isAlphaToCoverageEnabled() const noexcept { return mRasterState.alphaToCoverage; }
|
||||
float getMaskThreshold() const noexcept { return mMaskThreshold; }
|
||||
bool hasShadowMultiplier() const noexcept { return mHasShadowMultiplier; }
|
||||
AttributeBitset getRequiredAttributes() const noexcept { return mRequiredAttributes; }
|
||||
RefractionMode getRefractionMode() const noexcept { return mRefractionMode; }
|
||||
RefractionType getRefractionType() const noexcept { return mRefractionType; }
|
||||
ReflectionMode getReflectionMode() const noexcept { return mReflectionMode; }
|
||||
|
||||
bool hasSpecularAntiAliasing() const noexcept { return mDefinition.specularAntiAliasing; }
|
||||
float getSpecularAntiAliasingVariance() const noexcept { return mDefinition.specularAntiAliasingVariance; }
|
||||
float getSpecularAntiAliasingThreshold() const noexcept { return mDefinition.specularAntiAliasingThreshold; }
|
||||
bool hasSpecularAntiAliasing() const noexcept { return mSpecularAntiAliasing; }
|
||||
float getSpecularAntiAliasingVariance() const noexcept { return mSpecularAntiAliasingVariance; }
|
||||
float getSpecularAntiAliasingThreshold() const noexcept { return mSpecularAntiAliasingThreshold; }
|
||||
|
||||
backend::descriptor_binding_t getSamplerBinding(
|
||||
std::string_view const& name) const;
|
||||
|
||||
bool hasMaterialProperty(Property property) const noexcept {
|
||||
return bool(mDefinition.materialProperties & uint64_t(property));
|
||||
return bool(mMaterialProperties & uint64_t(property));
|
||||
}
|
||||
|
||||
SamplerInterfaceBlock const& getSamplerInterfaceBlock() const noexcept {
|
||||
return mDefinition.samplerInterfaceBlock;
|
||||
return mSamplerInterfaceBlock;
|
||||
}
|
||||
|
||||
size_t getParameterCount() const noexcept {
|
||||
return mDefinition.uniformInterfaceBlock.getFieldInfoList().size() +
|
||||
mDefinition.samplerInterfaceBlock.getSamplerInfoList().size() +
|
||||
(mDefinition.subpassInfo.isValid ? 1 : 0);
|
||||
return mUniformInterfaceBlock.getFieldInfoList().size() +
|
||||
mSamplerInterfaceBlock.getSamplerInfoList().size() +
|
||||
(mSubpassInfo.isValid ? 1 : 0);
|
||||
}
|
||||
size_t getParameters(ParameterInfo* parameters, size_t count) const noexcept;
|
||||
|
||||
@@ -243,7 +250,7 @@ public:
|
||||
bool setConstant(uint32_t id, T value) noexcept;
|
||||
|
||||
uint8_t getPerViewLayoutIndex() const noexcept {
|
||||
return mDefinition.perViewLayoutIndex;
|
||||
return mPerViewLayoutIndex;
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
@@ -279,8 +286,6 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
MaterialParser const& getMaterialParser() const noexcept;
|
||||
|
||||
bool hasVariant(Variant variant) const noexcept;
|
||||
void prepareProgramSlow(Variant variant,
|
||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||
@@ -291,25 +296,82 @@ private:
|
||||
backend::Program getProgramWithVariants(Variant variant,
|
||||
Variant vertexVariant, Variant fragmentVariant) const;
|
||||
|
||||
void processSpecializationConstants(FEngine& engine, Builder const& builder);
|
||||
void processPushConstants(FEngine& engine);
|
||||
void processBlendingMode(MaterialParser const* parser);
|
||||
|
||||
void processSpecializationConstants(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;
|
||||
|
||||
inline bool isSharedVariant(Variant const variant) const {
|
||||
return (mDefinition.materialDomain == MaterialDomain::SURFACE) && !mIsDefaultMaterial &&
|
||||
!mDefinition.hasCustomDepthShader && Variant::isValidDepthVariant(variant);
|
||||
return (mMaterialDomain == MaterialDomain::SURFACE) && !mIsDefaultMaterial &&
|
||||
!mHasCustomDepthShader && Variant::isValidDepthVariant(variant);
|
||||
}
|
||||
|
||||
// try to order by frequency of use
|
||||
mutable std::array<backend::Handle<backend::HwProgram>, VARIANT_COUNT> mCachedPrograms;
|
||||
MaterialDefinition const& mDefinition;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayout;
|
||||
DescriptorSetLayout mPerViewDescriptorSetLayoutVsm;
|
||||
DescriptorSetLayout mDescriptorSetLayout;
|
||||
backend::Program::DescriptorSetInfo mProgramDescriptorBindings;
|
||||
|
||||
backend::RasterState mRasterState;
|
||||
TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT;
|
||||
bool mIsVariantLit = false;
|
||||
backend::FeatureLevel mFeatureLevel = backend::FeatureLevel::FEATURE_LEVEL_1;
|
||||
Shading mShading = Shading::UNLIT;
|
||||
|
||||
BlendingMode mBlendingMode = BlendingMode::OPAQUE;
|
||||
std::array<backend::BlendFunction, 4> mCustomBlendFunctions = {};
|
||||
Interpolation mInterpolation = Interpolation::SMOOTH;
|
||||
VertexDomain mVertexDomain = VertexDomain::OBJECT;
|
||||
MaterialDomain mMaterialDomain = MaterialDomain::SURFACE;
|
||||
CullingMode mCullingMode = CullingMode::NONE;
|
||||
AttributeBitset mRequiredAttributes;
|
||||
UserVariantFilterMask mVariantFilterMask = 0;
|
||||
RefractionMode mRefractionMode = RefractionMode::NONE;
|
||||
RefractionType mRefractionType = RefractionType::SOLID;
|
||||
ReflectionMode mReflectionMode = ReflectionMode::DEFAULT;
|
||||
uint64_t mMaterialProperties = 0;
|
||||
uint8_t mPerViewLayoutIndex = 0;
|
||||
|
||||
float mMaskThreshold = 0.4f;
|
||||
float mSpecularAntiAliasingVariance = 0.0f;
|
||||
float mSpecularAntiAliasingThreshold = 0.0f;
|
||||
|
||||
bool mDoubleSided = false;
|
||||
bool mDoubleSidedCapability = false;
|
||||
bool mHasShadowMultiplier = false;
|
||||
bool mHasCustomDepthShader = false;
|
||||
bool mIsDefaultMaterial = false;
|
||||
bool mSpecularAntiAliasing = false;
|
||||
|
||||
// reserve some space to construct the default material instance
|
||||
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
|
||||
|
||||
SamplerInterfaceBlock mSamplerInterfaceBlock;
|
||||
BufferInterfaceBlock mUniformInterfaceBlock;
|
||||
SubpassInfo mSubpassInfo;
|
||||
|
||||
using BindingUniformInfoContainer = utils::FixedCapacityVector<std::tuple<
|
||||
uint8_t, utils::CString, backend::Program::UniformInfo>>;
|
||||
|
||||
BindingUniformInfoContainer mBindingUniformInfo;
|
||||
|
||||
using AttributeInfoContainer = utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>>;
|
||||
|
||||
AttributeInfoContainer mAttributeInfo;
|
||||
|
||||
// Constants defined by this Material
|
||||
utils::FixedCapacityVector<MaterialConstant> mMaterialConstants;
|
||||
// A map from the Constant name to the mMaterialConstant index
|
||||
std::unordered_map<std::string_view, uint32_t> mSpecializationConstantsNameToIndex;
|
||||
// current specialization constants for the HwProgram
|
||||
utils::FixedCapacityVector<backend::Program::SpecializationConstant> mSpecializationConstants;
|
||||
|
||||
@@ -324,15 +386,17 @@ private:
|
||||
mutable VariantList mActivePrograms;
|
||||
mutable utils::Mutex mPendingEditsLock;
|
||||
std::unique_ptr<MaterialParser> mPendingEdits;
|
||||
std::unique_ptr<MaterialParser> mEditedMaterialParser;
|
||||
void setPendingEdits(std::unique_ptr<MaterialParser> pendingEdits) noexcept;
|
||||
bool hasPendingEdits() const noexcept;
|
||||
void latchPendingEdits() noexcept;
|
||||
#endif
|
||||
|
||||
utils::CString mName;
|
||||
FEngine& mEngine;
|
||||
const uint32_t mMaterialId;
|
||||
uint64_t mCacheId = 0;
|
||||
mutable uint32_t mMaterialInstanceId = 0;
|
||||
std::unique_ptr<MaterialParser> mMaterialParser;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
size_t const uboSize = std::max(size_t(16), material->getUniformInterfaceBlock().getSize());
|
||||
mUniforms = UniformBuffer(uboSize);
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
|
||||
BufferUsage::STATIC, utils::CString{ material->getName() });
|
||||
BufferUsage::STATIC, material->getName());
|
||||
|
||||
// set the UBO, always descriptor 0
|
||||
mDescriptorSet.setBuffer(material->getDescriptorSetLayout(),
|
||||
@@ -148,7 +148,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
|
||||
mUniforms.setUniforms(other->getUniformBuffer());
|
||||
mUbHandle = driver.createBufferObject(mUniforms.getSize(), BufferObjectBinding::UNIFORM,
|
||||
BufferUsage::DYNAMIC, CString{ material->getName() });
|
||||
BufferUsage::DYNAMIC, material->getName());
|
||||
|
||||
// set the UBO, always descriptor 0
|
||||
mDescriptorSet.setBuffer(mMaterial->getDescriptorSetLayout(),
|
||||
|
||||
@@ -128,7 +128,7 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder)
|
||||
getHeight(mVertexCount),
|
||||
mCount,
|
||||
TextureUsage::DEFAULT,
|
||||
utils::CString{ builder.getName() });
|
||||
builder.getName());
|
||||
|
||||
mTbHandle = driver.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1,
|
||||
TextureFormat::RGBA16I, 1,
|
||||
@@ -136,7 +136,7 @@ FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder)
|
||||
getHeight(mVertexCount),
|
||||
mCount,
|
||||
TextureUsage::DEFAULT,
|
||||
utils::CString{ builder.getName() });
|
||||
builder.getName());
|
||||
}
|
||||
|
||||
void FMorphTargetBuffer::terminate(FEngine& engine) {
|
||||
|
||||
@@ -221,7 +221,7 @@ FRenderTarget::FRenderTarget(FEngine& engine, const Builder& builder)
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
mHandle = driver.createRenderTarget(mAttachmentMask,
|
||||
builder.mImpl->mWidth, builder.mImpl->mHeight, builder.mImpl->mSamples,
|
||||
builder.mImpl->mLayerCount, mrt, dinfo, {}, utils::CString{ builder.getName() });
|
||||
builder.mImpl->mLayerCount, mrt, dinfo, {}, builder.getName());
|
||||
}
|
||||
|
||||
void FRenderTarget::terminate(FEngine& engine) {
|
||||
|
||||
@@ -585,6 +585,12 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
||||
// for endFrame() above. This operation in actually not too heavy, it just kicks the
|
||||
// driver thread, which is mostlikely already running.
|
||||
engine.flush();
|
||||
|
||||
// FIXME: This is a workaround for internal bug b/361822355.
|
||||
// properly address the bug and remove this workaround.
|
||||
if (engine.getBackend() == Backend::VULKAN) {
|
||||
engine.flushAndWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ FSkinningBuffer::FSkinningBuffer(FEngine& engine, const Builder& builder)
|
||||
getPhysicalBoneCount(mBoneCount) * sizeof(PerRenderableBoneUib::BoneData),
|
||||
BufferObjectBinding::UNIFORM,
|
||||
BufferUsage::DYNAMIC,
|
||||
utils::CString{ builder.getName() });
|
||||
builder.getName());
|
||||
|
||||
if (builder->mInitialize) {
|
||||
// initialize the bones to identity (before rounding up)
|
||||
|
||||
@@ -313,7 +313,7 @@ FTexture::FTexture(FEngine& engine, const Builder& builder)
|
||||
return;
|
||||
}
|
||||
|
||||
CString tag{ builder.getName() };
|
||||
auto tag = builder.getName();
|
||||
if (tag.empty()) {
|
||||
tag = CString{"FTexture"};
|
||||
}
|
||||
|
||||
@@ -280,8 +280,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder)
|
||||
mVertexBufferInfoHandle = engine.getVertexBufferInfoFactory().create(driver,
|
||||
mBufferCount, mDeclaredAttributes.count(), mAttributes);
|
||||
|
||||
mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle,
|
||||
utils::CString{ builder.getName() });
|
||||
mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle, builder.getName());
|
||||
|
||||
// calculate buffer sizes
|
||||
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
|
||||
@@ -308,8 +307,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder)
|
||||
assert_invariant(bufferSizes[i] > 0);
|
||||
if (!mBufferObjects[i]) {
|
||||
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
|
||||
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
|
||||
utils::CString{ builder.getName() });
|
||||
BufferObjectBinding::VERTEX, BufferUsage::STATIC, builder.getName());
|
||||
driver.setVertexBufferObject(mHandle, i, bo);
|
||||
mBufferObjects[i] = bo;
|
||||
}
|
||||
@@ -325,8 +323,7 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const Builder& builder)
|
||||
assert_invariant(bufferSizes[i] > 0);
|
||||
if (!mBufferObjects[i]) {
|
||||
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
|
||||
BufferObjectBinding::VERTEX, BufferUsage::STATIC,
|
||||
utils::CString{ builder.getName() });
|
||||
BufferObjectBinding::VERTEX, BufferUsage::STATIC, builder.getName());
|
||||
driver.setVertexBufferObject(mHandle, i, bo);
|
||||
mBufferObjects[i] = bo;
|
||||
}
|
||||
|
||||
@@ -1399,8 +1399,8 @@ void FView::executePickingQueries(DriverApi& driver,
|
||||
});
|
||||
} else {
|
||||
driver.readPixels(handle, x, y, 1, 1, {
|
||||
&pQuery->result.renderable, 4u * 4u, // 4*uint
|
||||
PixelDataFormat::RGBA_INTEGER, PixelDataType::UINT,
|
||||
&pQuery->result.renderable, 4u * 4u, // 4*float
|
||||
PixelDataFormat::RG, PixelDataType::FLOAT,
|
||||
pQuery->handler, [](void*, size_t, void* user) {
|
||||
FPickingQuery* const pQuery = static_cast<FPickingQuery*>(user);
|
||||
// pQuery->result.renderable already contains the right value!
|
||||
|
||||
@@ -67,7 +67,6 @@ DescriptorSet& DescriptorSet::operator=(DescriptorSet&& rhs) noexcept {
|
||||
mValid = rhs.mValid;
|
||||
mSetAfterCommitWarning = rhs.mSetAfterCommitWarning;
|
||||
mSetUndefinedParameterWarning = rhs.mSetUndefinedParameterWarning;
|
||||
mName = rhs.mName;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||