Compare commits

..
Author SHA1 Message Date
Benjamin Doherty 15424c85b5 Metal: use atomic for buffer tracking 2025-09-19 13:41:37 -07:00
159 changed files with 1490 additions and 5830 deletions
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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:
+1 -1
View File
@@ -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:
-6
View File
@@ -18,9 +18,3 @@ test*.json
results
/compile_commands.json
/.cache
# For /test/renderdiff dependencies
/mesa
/gltf
/filament-assets
/venv
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.65.4'
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.4'
pod 'Filament', '~> 1.65.0'
```
## Documentation
-9
View File
@@ -7,15 +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.4
## 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 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.65.4
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
}
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.65.2'
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', '~&gt; 1.65.2'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.65.0'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
-330
View File
@@ -1,330 +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>To run the same presbumit as <a href="presubmit-renderdiff"><code>test-renderdiff</code></a>, you can do
<pre><code>bash test/renderdiff/local_test.sh
</code></pre>
</li>
<li>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.</li>
<li>To just render without running the test, you could use the following script
<pre><code>bash test/renderdiff/generate.sh
</code></pre>
</li>
</ul>
<h2 id="filtering-tests"><a class="header" href="#filtering-tests">Filtering Tests</a></h2>
<p>You can run a subset of the tests by passing the <code>--test_filter</code> flag to <code>local_test.sh</code>. The
filter supports wildcards (<code>*</code>) to match test names. For example, to run all tests in the
<code>ClearCoat</code> suite, you can use the following command:</p>
<pre><code>bash test/renderdiff/local_test.sh --test_filter='ClearCoat.*.*'
</code></pre>
<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>Run interactive mode in the <code>update_golden.py</code> script.
<pre><code>python3 test/renderdiff/src/update_golden.py
</code></pre>
</li>
<li>This will guide you through a series of steps to push the changes to a remote branch on
<code>filament-assets</code>.</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>Check out the golden repo
<pre><code>git clone git@github.com:google/filament-assets.git
</code></pre>
</li>
<li>Create a branch on the golden repo
<pre><code>cd filament-assets
git switch -c my-pr-branch-golden
</code></pre>
</li>
<li>Copy the new images to their appropriate place in <code>filament-assets</code></li>
<li>Push the <code>filament-assets</code> working branch to remote
<pre><code>git push origin my-pr-branch-golden
</code></pre>
</li>
<li>In the commit message of your working branch on <code>filament</code>, add the following line
<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>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

+2 -2
View File
@@ -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>
+2 -2
View File
@@ -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>
-215
View File
@@ -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>
File diff suppressed because one or more lines are too long
Binary file not shown.
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
-6
View File
@@ -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/"
}
}
}
-2
View File
@@ -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)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

-4
View File
@@ -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).
File diff suppressed because one or more lines are too long
Binary file not shown.
-4
View File
@@ -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
-8
View File
@@ -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
@@ -179,7 +177,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h
src/vulkan/VulkanAsyncHandles.cpp
src/vulkan/VulkanAsyncHandles.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
@@ -218,10 +215,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanSamplerCache.cpp
src/vulkan/VulkanSamplerCache.h
src/vulkan/VulkanSemaphore.cpp
src/vulkan/VulkanSemaphore.h
src/vulkan/VulkanSemaphoreManager.cpp
src/vulkan/VulkanSemaphoreManager.h
src/vulkan/VulkanStagePool.cpp
src/vulkan/VulkanStagePool.h
src/vulkan/VulkanSwapChain.cpp
@@ -573,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
+4 -30
View File
@@ -197,7 +197,6 @@ constexpr std::string_view to_string(Backend const backend) noexcept {
* - The Metal backend can prefer precompiled Metal libraries, while falling back to MSL.
*/
enum class ShaderLanguage {
UNSPECIFIED = -1,
ESSL1 = 0,
ESSL3 = 1,
SPIRV = 2,
@@ -220,8 +219,6 @@ constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) noex
return "Metal precompiled library";
case ShaderLanguage::WGSL:
return "WGSL";
case ShaderLanguage::UNSPECIFIED:
return "Unspecified";
}
return "UNKNOWN";
}
@@ -559,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
};
/**
@@ -639,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
*/
@@ -1713,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
@@ -27,7 +27,6 @@
#include <backend/AcquiredImage.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include <functional>
@@ -72,11 +71,7 @@ public:
virtual ShaderModel getShaderModel() const noexcept = 0;
// The shader languages used for shaders for this driver in order of preference, used to inform
// matdbg.
//
// The `preferredLanguage` is only a hint. If the backend supports it, it will appear first in
// the list.
// The shader language used for shaders for this driver, used to inform matdbg.
//
// For OpenGL, this distinguishes whether the driver's shaders are powered by ESSL1 or ESSL3.
// This information is used by matdbg to display the correct shader code to the web UI and patch
@@ -84,8 +79,7 @@ public:
//
// Metal shaders can either be MSL or Metal libraries, but at time of writing, matdbg can only
// interface with MSL.
virtual utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept = 0;
virtual ShaderLanguage getShaderLanguage() const noexcept = 0;
// Returns the dispatcher. This is only called once during initialization of the CommandStream,
// so it doesn't matter that it's virtual.
@@ -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,14 +340,13 @@ 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)
DECL_DRIVER_API_SYNCHRONOUS_N(void, updateStreams, backend::DriverApi*, driver)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::FenceStatus, getFenceStatus, backend::FenceHandle, fh)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::FenceStatus, fenceWait, backend::FenceHandle, fh, uint64_t, timeout)
DECL_DRIVER_API_SYNCHRONOUS_N(void, getPlatformSync, backend::SyncHandle, sh,
backend::CallbackHandler*, handler, backend::Platform::SyncCallback, cb, void*, userData)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatSupported, backend::TextureFormat, format)
@@ -363,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)
@@ -388,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,
@@ -426,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,
@@ -437,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
*/
@@ -545,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,
+4 -10
View File
@@ -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);
+8 -25
View File
@@ -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;
}
+1 -4
View File
@@ -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));
}
+1 -11
View File
@@ -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,
+1 -1
View File
@@ -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;
+1 -6
View File
@@ -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.";
+2 -4
View File
@@ -25,9 +25,8 @@
#include <backend/SamplerDescriptor.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/debug.h>
#include <functional>
@@ -70,8 +69,7 @@ private:
MetalContext* mContext;
ShaderModel getShaderModel() const noexcept final;
utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;
// Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block.
void execute(std::function<void(void)> const& fn) noexcept final;
+37 -93
View File
@@ -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));
}
@@ -1002,12 +986,8 @@ ShaderModel MetalDriver::getShaderModel() const noexcept {
#endif
}
utils::FixedCapacityVector<ShaderLanguage> MetalDriver::getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept {
if (preferredLanguage == backend::ShaderLanguage::MSL) {
return { backend::ShaderLanguage::MSL, backend::ShaderLanguage::METAL_LIBRARY };
}
return { backend::ShaderLanguage::METAL_LIBRARY, backend::ShaderLanguage::MSL };
ShaderLanguage MetalDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::MSL;
}
Handle<HwStream> MetalDriver::createStreamNative(void* stream, utils::CString tag) {
@@ -1042,15 +1022,11 @@ void MetalDriver::destroyFence(Handle<HwFence> fh) {
}
FenceStatus MetalDriver::getFenceStatus(Handle<HwFence> fh) {
return fenceWait(fh, 0);
}
FenceStatus MetalDriver::fenceWait(FenceHandle fh, uint64_t const timeout) {
auto* fence = handle_cast<MetalFence>(fh);
if (!fence) {
return FenceStatus::ERROR;
}
return fence->wait(timeout);
return fence->wait(0);
}
void MetalDriver::destroySync(Handle<HwSync> sh) {
@@ -2225,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>;
-32
View File
@@ -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
-28
View File
@@ -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
-36
View File
@@ -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];
@@ -134,7 +134,6 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
case ShaderLanguage::ESSL3:
case ShaderLanguage::SPIRV:
case ShaderLanguage::WGSL:
case ShaderLanguage::UNSPECIFIED:
break;
}
+3 -5
View File
@@ -155,11 +155,9 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
// Older Apple devices (and the simulator) don't support setting a comparison function in
// MTLSamplerDescriptor.
// In practice, this means shadows are not supported when running in the simulator.
if (samplerDescriptor.compareFunction != MTLCompareFunctionNever) {
if (![device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
LOG(WARNING) << "Warning: sample comparison not supported by this GPU";
samplerDescriptor.compareFunction = MTLCompareFunctionNever;
}
if (![device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
LOG(WARNING) << "Warning: sample comparison not supported by this GPU";
samplerDescriptor.compareFunction = MTLCompareFunctionNever;
}
#endif
+15 -34
View File
@@ -45,16 +45,8 @@ ShaderModel NoopDriver::getShaderModel() const noexcept {
#endif
}
utils::FixedCapacityVector<ShaderLanguage> NoopDriver::getShaderLanguages(
ShaderLanguage /*preferredLanguage*/) const noexcept {
return {
ShaderLanguage::ESSL3,
ShaderLanguage::ESSL1,
ShaderLanguage::SPIRV,
ShaderLanguage::MSL,
ShaderLanguage::METAL_LIBRARY,
ShaderLanguage::WGSL,
};
ShaderLanguage NoopDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::ESSL3;
}
// explicit instantiation of the Dispatcher
@@ -168,10 +160,6 @@ FenceStatus NoopDriver::getFenceStatus(Handle<HwFence> fh) {
return FenceStatus::CONDITION_SATISFIED;
}
FenceStatus NoopDriver::fenceWait(Handle<HwFence> fh, uint64_t timeout) {
return FenceStatus::ERROR;
}
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
// the GPU supports the given texture format with non-zero optimal tiling features.
bool NoopDriver::isTextureFormatSupported(TextureFormat format) {
@@ -340,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) {
@@ -365,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));
}
@@ -417,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
+1 -3
View File
@@ -20,7 +20,6 @@
#include "private/backend/Driver.h"
#include "DriverBase.h"
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
namespace filament::backend {
@@ -35,8 +34,7 @@ public:
private:
ShaderModel getShaderModel() const noexcept final;
utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;
uint64_t nextFakeHandle = 1;
+3 -5
View File
@@ -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
+16 -55
View File
@@ -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);
@@ -507,40 +503,20 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "Mali")) {
// ARM GPU
bugs->vao_doesnt_store_element_array_buffer_binding = true;
// We have run into several problems with timer queries on Mali-Gxx:
// - timer queries seem to cause memory corruptions in some cases on some devices
// (see b/233754398)
// - appeared at least in: "OpenGL ES 3.2 v1.r26p0-01eac0"
// - wasn't present in: "OpenGL ES 3.2 v1.r32p1-00pxl1"
// - timer queries sometime crash with an NPE (see b/273759031)
// (see b/273759031)
// - possibly not present in: "OpenGL ES 3.2 v1.r46p0"
bugs->dont_use_timer_query = true;
// at least some version of Mali have problems with framebuffer_fetch
// (see b/445721121, https://github.com/google/filament/issues/7794)
bugs->disable_framebuffer_fetch_extension = true;
if (strstr(renderer, "Mali-T")) {
bugs->disable_glFlush = true;
bugs->disable_shared_context_draws = true;
// We have not verified that timer queries work on Mali-T, so we disable to be safe.
bugs->dont_use_timer_query = true;
}
if (strstr(renderer, "Mali-G")) {
int maj, min, driverVersion, driverRelease;
int const c = sscanf(version, "OpenGL ES %d.%d v%d.r%d",
&maj, &min, &driverVersion, &driverRelease);
if (UTILS_LIKELY(c == 4)) {
// we were able to parse the version string
if (driverVersion > 1 || (driverVersion == 1 && driverRelease >= 46)) {
// this driver version is known to be good
bugs->dont_use_timer_query = false;
}
if (driverVersion > 1 || (driverVersion == 1 && driverRelease >= 53)) {
// this driver version is known to be good
bugs->disable_framebuffer_fetch_extension = false;
}
}
// We have run into several problems with timer queries on Mali-Gxx:
// - timer queries seem to cause memory corruptions in some cases on some devices
// (see b/233754398)
// - appeared at least in: "OpenGL ES 3.2 v1.r26p0-01eac0"
// - wasn't present in: "OpenGL ES 3.2 v1.r32p1-00pxl1"
// - timer queries sometime crash with an NPE (see b/273759031)
bugs->dont_use_timer_query = true;
}
// Mali seems to have no problem with this (which is good for us)
bugs->allow_read_only_ancillary_feedback_loop = true;
@@ -583,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.
@@ -644,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,13 +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.
// Some Mali drivers also have problems with this (b/445721121)
bool disable_framebuffer_fetch_extension;
} bugs = {};
// state getters -- as needed.
@@ -584,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
@@ -617,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,
File diff suppressed because it is too large Load Diff
+4 -10
View File
@@ -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 {
@@ -194,8 +191,8 @@ public:
struct GLFence : public HwFence {
using HwFence::HwFence;
struct State {
utils::Mutex lock; // NOLINT(*-include-cleaner)
utils::Condition cond; // NOLINT(*-include-cleaner)
std::mutex lock;
std::condition_variable cond;
FenceStatus status{ FenceStatus::TIMEOUT_EXPIRED };
};
std::shared_ptr<State> state{ std::make_shared<State>() };
@@ -217,8 +214,6 @@ public:
OpenGLDriver(OpenGLDriver const&) = delete;
OpenGLDriver& operator=(OpenGLDriver const&) = delete;
using DriverBase::scheduleDestroy;
private:
OpenGLPlatform& mPlatform;
OpenGLContext mContext;
@@ -233,8 +228,7 @@ private:
}
ShaderModel getShaderModel() const noexcept override;
utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept override;
ShaderLanguage getShaderLanguage() const noexcept override;
/*
* OpenGLDriver interface
@@ -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;
+5 -1
View File
@@ -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) {
@@ -1,82 +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 "VulkanAsyncHandles.h"
#include <backend/DriverEnums.h>
#include <utils/debug.h>
#include <cstdint>
#include <mutex>
#include <shared_mutex>
#include <chrono>
using namespace bluevk;
namespace filament::backend {
FenceStatus VulkanCmdFence::wait(VkDevice device, uint64_t const timeout,
std::chrono::steady_clock::time_point const until) {
std::shared_lock l(mLock);
// If the vulkan fence has not been submitted yet, we need to wait for that before we
// can use vkWaitForFences()
if (mStatus == VK_INCOMPLETE) {
bool const success = mCond.wait_until(l, until, [this] {
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted".
// When this fence gets submitted, its status changes to VK_NOT_READY.
return mStatus != VK_INCOMPLETE;
});
if (!success) {
// !success indicates a timeout
return FenceStatus::TIMEOUT_EXPIRED;
}
}
// The fence could have already signaled, avoid calling into vkWaitForFences()
if (mStatus == VK_SUCCESS) {
return FenceStatus::CONDITION_SATISFIED;
}
// If we're here, we know that vkQueueSubmit has been called (because it sets the status
// to VK_NOT_READY).
// Now really wait for the fence while holding the shared_lock, this allows several
// threads to call vkWaitForFences(), but will prevent vkResetFence from taking
// place simultaneously. vkResetFence is only called once it knows the fence has signaled,
// which guaranties that vkResetFence won't have to wait too long, just enough for
// all the vkWaitForFences() to return.
mStatus = vkWaitForFences(device, 1, &mFence, VK_TRUE, timeout);
if (mStatus == VK_SUCCESS) {
return FenceStatus::CONDITION_SATISFIED;
}
if (mStatus == VK_TIMEOUT) {
return FenceStatus::TIMEOUT_EXPIRED;
}
return FenceStatus::ERROR; // not supported
}
void VulkanCmdFence::resetFence(VkDevice device) {
// This lock prevents vkResetFences() from being called simultaneously with vkWaitForFences(),
// but by construction, when we're here, we know that the fence has signaled and
// vkWaitForFences() will return shortly.
std::lock_guard const l(mLock);
assert_invariant(mStatus == VK_SUCCESS);
vkResetFences(device, 1, &mFence);
}
} // namespace filament::backend
@@ -20,81 +20,37 @@
#include <bluevk/BlueVK.h>
#include "DriverBase.h"
#include "backend/DriverEnums.h"
#include "backend/Platform.h"
#include "vulkan/memory/Resource.h"
#include <chrono>
#include <cstdint>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <utility>
#include <vector>
#include <utils/Mutex.h>
#include <utils/Condition.h>
namespace filament::backend {
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
struct VulkanCmdFence {
explicit VulkanCmdFence(VkFence fence) : mFence(fence) { }
VulkanCmdFence(VkResult initialStatus) {
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
// gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
// finishes executing the command buffer, the status changes to VK_SUCCESS.
status.store(initialStatus);
}
~VulkanCmdFence() = default;
void setStatus(VkResult const value) {
std::lock_guard const l(mLock);
mStatus = value;
mCond.notify_all();
}
void setStatus(VkResult value) { status.store(value); }
VkResult getStatus() {
std::shared_lock const l(mLock);
return mStatus;
}
void resetFence(VkDevice device);
FenceStatus wait(VkDevice device, uint64_t timeout,
std::chrono::steady_clock::time_point until);
VkResult getStatus() { return status.load(std::memory_order_acquire); }
private:
std::shared_mutex mLock; // NOLINT(*-include-cleaner)
std::condition_variable_any mCond;
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
// gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
// finishes executing the command buffer, the status changes to VK_SUCCESS.
VkResult mStatus{ VK_INCOMPLETE };
VkFence mFence;
std::atomic<VkResult> status;
};
struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
VulkanFence() {}
void setFence(std::shared_ptr<VulkanCmdFence> fence) {
std::lock_guard lock(mState->lock);
mState->sharedFence = std::move(fence);
mState->cond.notify_all();
}
std::shared_ptr<VulkanCmdFence>& getSharedFence() {
std::lock_guard lock(mState->lock);
return mState->sharedFence;
}
std::shared_ptr<VulkanCmdFence> wait(std::chrono::steady_clock::time_point const until) {
// hold a reference so that our state doesn't disappear while we wait
std::shared_ptr state{ mState };
std::unique_lock lock(state->lock);
state->cond.wait_until(lock, until, [&state] { return bool(state->sharedFence); });
// here mSharedFence will be null if we timed out
return state->sharedFence;
}
private:
struct State {
std::mutex lock;
std::condition_variable cond;
std::shared_ptr<VulkanCmdFence> sharedFence;
};
std::shared_ptr<State> mState{ std::make_shared<State>() };
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
@@ -116,12 +72,12 @@ struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
mStoppingQueryIndex(stoppingIndex) {}
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
std::unique_lock const lock(mFenceMutex);
mFence = std::move(fence);
std::unique_lock<utils::Mutex> lock(mFenceMutex);
mFence = fence;
}
bool isCompleted() noexcept {
std::unique_lock const lock(mFenceMutex);
std::unique_lock<utils::Mutex> lock(mFenceMutex);
// QueryValue is a synchronous call and might occur before beginTimerQuery has written
// anything into the command buffer, which is an error according to the validation layer
// that ships in the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to
@@ -28,24 +28,24 @@ namespace filament::backend {
namespace {
VkBufferUsageFlags getVkBufferUsage(VulkanBufferBinding usage) {
VkBufferUsageFlags getVkBufferUsage(VulkanBufferUsage usage) {
switch (usage) {
case VulkanBufferBinding::VERTEX:
case VulkanBufferUsage::VERTEX:
return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case VulkanBufferBinding::INDEX:
case VulkanBufferUsage::INDEX:
return VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
case VulkanBufferBinding::UNIFORM:
case VulkanBufferUsage::UNIFORM:
return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
case VulkanBufferBinding::SHADER_STORAGE:
case VulkanBufferUsage::SHADER_STORAGE:
return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
case VulkanBufferBinding::UNKNOWN:
case VulkanBufferUsage::UNKNOWN:
return 0;
}
return 0;
}
} // namespace
}// namespace
VulkanBufferCache::VulkanBufferCache(VulkanContext const& context,
fvkmemory::ResourceManager& resourceManager, VmaAllocator allocator)
@@ -53,11 +53,11 @@ VulkanBufferCache::VulkanBufferCache(VulkanContext const& context,
mResourceManager(resourceManager),
mAllocator(allocator) {}
fvkmemory::resource_ptr<VulkanBuffer> VulkanBufferCache::acquire(VulkanBufferBinding binding,
fvkmemory::resource_ptr<VulkanBuffer> VulkanBufferCache::acquire(VulkanBufferUsage usage,
uint32_t numBytes) noexcept {
assert_invariant(binding != VulkanBufferBinding::UNKNOWN);
assert_invariant(usage != VulkanBufferUsage::UNKNOWN);
BufferPool& bufferPool = getPool(binding);
BufferPool& bufferPool = getPool(usage);
// First check if an allocation exists whose capacity is greater than or equal to the requested
// size.
@@ -71,7 +71,7 @@ fvkmemory::resource_ptr<VulkanBuffer> VulkanBufferCache::acquire(VulkanBufferBin
// We were not able to find a sufficiently large allocation, so create a new one that is
// recycled after being yielded.
VulkanGpuBuffer const* gpuBuffer = allocate(binding, numBytes);
VulkanGpuBuffer const* gpuBuffer = allocate(usage, numBytes);
return fvkmemory::resource_ptr<VulkanBuffer>::construct(&mResourceManager, gpuBuffer,
[this](VulkanGpuBuffer const* gpuBuffer) { this->release(gpuBuffer); });
}
@@ -93,10 +93,9 @@ void VulkanBufferCache::gc() noexcept {
if (poolIter->second.lastAccessed < evictionTime) {
#if FVK_ENABLED(FVK_DEBUG_VULKAN_BUFFER_CACHE)
FVK_LOGD << "VulkanBufferCache - Destroyed vkBuffer "
<< poolIter->second.gpuBuffer->vkbuffer << " with binding "
<< static_cast<int>(poolIter->second.gpuBuffer->binding)
<< utils::io::endl;
#endif // FVK_DEBUG_VULKAN_BUFFER_CACHE
<< poolIter->second.gpuBuffer->vkbuffer << " with usage "
<< static_cast<int>(poolIter->second.gpuBuffer->usage) << utils::io::endl;
#endif// FVK_DEBUG_VULKAN_BUFFER_CACHE
destroy(poolIter->second.gpuBuffer);
poolIter = bufferPool.erase(poolIter);
@@ -121,33 +120,35 @@ void VulkanBufferCache::terminate() noexcept {
void VulkanBufferCache::release(VulkanGpuBuffer const* gpuBuffer) noexcept {
assert_invariant(gpuBuffer != nullptr);
BufferPool& bufferPool = getPool(gpuBuffer->binding);
BufferPool& bufferPool = getPool(gpuBuffer->usage);
bufferPool.insert(std::make_pair(gpuBuffer->numBytes, UnusedGpuBuffer{
.lastAccessed = mCurrentFrame,
.gpuBuffer = gpuBuffer,
}));
}
VulkanGpuBuffer const* VulkanBufferCache::allocate(VulkanBufferBinding binding,
VulkanGpuBuffer const* VulkanBufferCache::allocate(VulkanBufferUsage usage,
uint32_t numBytes) noexcept {
VkBufferCreateInfo const bufferInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = numBytes,
// `VK_BUFFER_USAGE_TRANSFER_DST_BIT` is needed to allow updating the buffer through
// a staging using `vkCmdCopyBuffer`.
.usage = getVkBufferUsage(binding) | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.usage = getVkBufferUsage(usage) | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
};
VmaAllocationCreateFlags vmaFlags = 0;
// In the case of UMA, the buffers will always be mappable
if (mContext.isUnifiedMemoryArchitecture()) {
vmaFlags |= VMA_ALLOCATION_CREATE_MAPPED_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
if (usage == VulkanBufferUsage::UNIFORM) {
// In the case of UMA, the uniform buffers will always be mappable
if (mContext.isUnifiedMemoryArchitecture()) {
vmaFlags |= VMA_ALLOCATION_CREATE_MAPPED_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
}
}
VulkanGpuBuffer* gpuBuffer = new VulkanGpuBuffer{
.numBytes = numBytes,
.binding = binding,
.usage = usage,
};
VmaAllocationCreateInfo const allocInfo{
.flags = vmaFlags,
@@ -160,14 +161,14 @@ VulkanGpuBuffer const* VulkanBufferCache::allocate(VulkanBufferBinding binding,
#if FVK_ENABLED(FVK_DEBUG_VULKAN_BUFFER_CACHE)
if (result != VK_SUCCESS) {
FVK_LOGE << "VulkanBufferCache - failed to allocate a new vkBuffer of size " << numBytes
<< " and binding " << static_cast<int>(binding) << ", error: " << result
<< " and usage " << static_cast<int>(usage) << ", error: " << result
<< utils::io::endl;
} else {
FVK_LOGD << "VulkanBufferCache - allocated a vkBuffer " << gpuBuffer->vkbuffer
<< " of size " << numBytes << " and binding = " << static_cast<int>(binding)
<< " of size " << numBytes << " and usage = " << static_cast<int>(usage)
<< " successfully" << utils::io::endl;
}
#endif // FVK_DEBUG_VULKAN_BUFFER_CACHE
#endif// FVK_DEBUG_VULKAN_BUFFER_CACHE
return gpuBuffer;
}
@@ -178,24 +179,24 @@ void VulkanBufferCache::destroy(VulkanGpuBuffer const* gpuBuffer) noexcept {
gpuBuffer = nullptr;
}
VulkanBufferCache::BufferPool& VulkanBufferCache::getPool(VulkanBufferBinding binding) noexcept {
VulkanBufferCache::BufferPool& VulkanBufferCache::getPool(VulkanBufferUsage usage) noexcept {
int poolIndex = -1;
switch (binding) {
case VulkanBufferBinding::VERTEX:
switch (usage) {
case VulkanBufferUsage::VERTEX:
poolIndex = 0;
break;
case VulkanBufferBinding::INDEX:
case VulkanBufferUsage::INDEX:
poolIndex = 1;
break;
case VulkanBufferBinding::UNIFORM:
case VulkanBufferUsage::UNIFORM:
poolIndex = 2;
break;
case VulkanBufferBinding::SHADER_STORAGE:
case VulkanBufferUsage::SHADER_STORAGE:
poolIndex = 3;
break;
case VulkanBufferBinding::UNKNOWN:
PANIC_LOG("There's no pool for buffers with unkown binding.");
case VulkanBufferUsage::UNKNOWN:
PANIC_LOG("There's no pool for buffers with unkown usage.");
break;
}
@@ -203,4 +204,4 @@ VulkanBufferCache::BufferPool& VulkanBufferCache::getPool(VulkanBufferBinding bi
return mGpuBufferPools[poolIndex];
}
} // namespace filament::backend
}// namespace filament::backend
@@ -38,7 +38,7 @@ public:
// Allocates or reuse a new VkBuffer that is device local.
// In the case of Unified memory architecture, uniform buffers are also host visible.
fvkmemory::resource_ptr<VulkanBuffer> acquire(VulkanBufferBinding binding,
fvkmemory::resource_ptr<VulkanBuffer> acquire(VulkanBufferUsage usage,
uint32_t numBytes) noexcept;
// Evicts old unused `VulkanGpuBuffer` and bumps the current frame number
@@ -60,12 +60,12 @@ private:
void release(VulkanGpuBuffer const* gpuBuffer) noexcept;
// Allocate a new VkBuffer from the VMA pool of the corresponding `numBytes` and `usage`.
VulkanGpuBuffer const* allocate(VulkanBufferBinding binding, uint32_t numBytes) noexcept;
VulkanGpuBuffer const* allocate(VulkanBufferUsage usage, uint32_t numBytes) noexcept;
// Destroy the corresponding VkBuffer and return the VkDeviceMemory to the VMA pool.
void destroy(VulkanGpuBuffer const* gpuBuffer) noexcept;
BufferPool& getPool(VulkanBufferBinding binding) noexcept;
BufferPool& getPool(VulkanBufferUsage usage) noexcept;
VulkanContext const& mContext;
fvkmemory::ResourceManager& mResourceManager;
@@ -19,25 +19,22 @@
#include "VulkanBufferCache.h"
#include "VulkanCommands.h"
#include "VulkanContext.h"
#include "VulkanHandles.h"
#include "VulkanMemory.h"
#include <backend/DriverEnums.h>
#include "VulkanHandles.h"
using namespace bluevk;
namespace filament::backend {
VulkanBufferProxy::VulkanBufferProxy(VulkanContext const& context, VmaAllocator allocator,
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, VulkanBufferBinding binding,
BufferUsage usage, uint32_t numBytes)
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, VulkanBufferUsage usage,
uint32_t numBytes)
: mStagingBufferBypassEnabled(context.stagingBufferBypassEnabled()),
mAllocator(allocator),
mStagePool(stagePool),
mBufferCache(bufferCache),
mBuffer(mBufferCache.acquire(binding, numBytes)),
mLastReadAge(0),
mUsage(usage) {}
mBuffer(mBufferCache.acquire(usage, numBytes)),
mLastReadAge(0) {}
void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData,
uint32_t byteOffset, uint32_t numBytes) {
@@ -52,18 +49,12 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
commands.acquire(mBuffer);
// Check if we can just memcpy directly to the GPU memory.
// This is only allowed for UNIFORMS that are AVAILABLE and the memory is HOST_VISIBLE
// (supports memcpy from host). This works regardless if it's a full or partial update of the
// buffer.
bool const isMemcopyable = mBuffer->getGpuBuffer()->allocationInfo.pMappedData != nullptr;
// In the case of UNIFORMS, check that is available to see to know if a memcpy is possible.
// This works regardless if it's a full or partial update of the buffer.
bool const isUniformAndAvailable = getBinding() == VulkanBufferBinding::UNIFORM && isAvailable;
// In the case the content is marked as memory mapped or static, is guaranteed to be safe to do
// a memcpy if its available.
bool const isStaticOrShared =
any(mUsage & (BufferUsage::STATIC | BufferUsage::SHARED_WRITE_BIT));
bool const useMemcpy = (isUniformAndAvailable || isStaticOrShared) && isMemcopyable &&
mStagingBufferBypassEnabled;
bool const isUniform = getUsage() == VulkanBufferUsage::UNIFORM;
bool const useMemcpy = isUniform && isMemcopyable && isAvailable && mStagingBufferBypassEnabled;
if (useMemcpy) {
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
byteOffset;
@@ -90,13 +81,13 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
if (!isAvailable) {
VkAccessFlags srcAccess = 0;
VkPipelineStageFlags srcStage = 0;
if (getBinding() == VulkanBufferBinding::UNIFORM) {
if (getUsage() == VulkanBufferUsage::UNIFORM) {
srcAccess = VK_ACCESS_SHADER_READ_BIT;
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
} else if (getBinding() == VulkanBufferBinding::VERTEX) {
} else if (getUsage() == VulkanBufferUsage::VERTEX) {
srcAccess = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
srcStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
} else if (getBinding() == VulkanBufferBinding::INDEX) {
} else if (getUsage() == VulkanBufferUsage::INDEX) {
srcAccess = VK_ACCESS_INDEX_READ_BIT;
srcStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
}
@@ -129,16 +120,16 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
VkAccessFlags dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
if (getBinding() == VulkanBufferBinding::VERTEX) {
if (getUsage() == VulkanBufferUsage::VERTEX) {
dstAccessMask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
} else if (getBinding() == VulkanBufferBinding::INDEX) {
} else if (getUsage() == VulkanBufferUsage::INDEX) {
dstAccessMask |= VK_ACCESS_INDEX_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
} else if (getBinding() == VulkanBufferBinding::UNIFORM) {
} else if (getUsage() == VulkanBufferUsage::UNIFORM) {
dstAccessMask |= VK_ACCESS_SHADER_READ_BIT;
dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
} else if (getBinding() == VulkanBufferBinding::SHADER_STORAGE) {
} else if (getUsage() == VulkanBufferUsage::SHADER_STORAGE) {
// TODO: implement me
}
@@ -161,8 +152,8 @@ VkBuffer VulkanBufferProxy::getVkBuffer() const noexcept {
return mBuffer->getGpuBuffer()->vkbuffer;
}
VulkanBufferBinding VulkanBufferProxy::getBinding() const noexcept {
return mBuffer->getGpuBuffer()->binding;
VulkanBufferUsage VulkanBufferProxy::getUsage() const noexcept {
return mBuffer->getGpuBuffer()->usage;
}
void VulkanBufferProxy::referencedBy(VulkanCommandBuffer& commands) {
@@ -23,8 +23,6 @@
#include "VulkanMemory.h"
#include "VulkanStagePool.h"
#include <backend/DriverEnums.h>
namespace filament::backend {
struct VulkanDescriptorSet;
@@ -35,8 +33,8 @@ struct VulkanCommandBuffer;
class VulkanBufferProxy {
public:
VulkanBufferProxy(VulkanContext const& context, VmaAllocator allocator,
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, VulkanBufferBinding binding,
BufferUsage usage, uint32_t numBytes);
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, VulkanBufferUsage usage,
uint32_t numBytes);
void loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData, uint32_t byteOffset,
uint32_t numBytes);
@@ -46,7 +44,7 @@ public:
void referencedBy(VulkanCommandBuffer& commands);
private:
VulkanBufferBinding getBinding() const noexcept;
VulkanBufferUsage getUsage() const noexcept;
bool const mStagingBufferBypassEnabled;
VmaAllocator mAllocator;
@@ -56,7 +54,6 @@ private:
fvkmemory::resource_ptr<VulkanBuffer> mBuffer;
uint32_t mLastReadAge;
BufferUsage mUsage;
};
} // namespace filament::backend
+28 -40
View File
@@ -90,33 +90,30 @@ bool VulkanGroupMarkers::empty() const noexcept {
uint32_t VulkanCommandBuffer::sAgeCounter = 0;
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice device,
VkQueue queue, VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager,
bool isProtected)
VkQueue queue, VkCommandPool pool, bool isProtected)
: mContext(context),
mMarkerCount(0),
isProtected(isProtected),
mDevice(device),
mQueue(queue),
mSemaphoreManager(semaphoreManager),
mBuffer(createCommandBuffer(device, pool)),
mSubmission(semaphoreManager->acquire()),
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)),
mAge(++sAgeCounter) {
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
VkExportFenceCreateInfo exportFenceCreateInfo{
.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);
mFenceStatus = std::make_shared<VulkanCmdFence>(mFence);
}
VulkanCommandBuffer::~VulkanCommandBuffer() {
vkDestroySemaphore(mDevice, mSubmission, VKALLOC);
vkDestroyFence(mDevice, mFence, VKALLOC);
}
@@ -126,15 +123,12 @@ void VulkanCommandBuffer::reset() noexcept {
mWaitSemaphores.clear();
mWaitSemaphoreStages.clear();
mAge = ++sAgeCounter;
mSubmission = mSemaphoreManager->acquire();
// reset the fence with proper host synchronization
mFenceStatus->resetFence(mDevice);
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
// gets, gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
// finishes executing the command buffer, the status changes to VK_SUCCESS.
mFenceStatus = std::make_shared<VulkanCmdFence>(mFence);
mFenceStatus = std::make_shared<VulkanCmdFence>(VK_INCOMPLETE);
vkResetFences(mDevice, 1, &mFence);
}
void VulkanCommandBuffer::pushMarker(char const* marker) noexcept {
@@ -193,14 +187,13 @@ void VulkanCommandBuffer::begin() noexcept {
vkBeginCommandBuffer(mBuffer, &binfo);
}
fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
VkSemaphore VulkanCommandBuffer::submit() {
while (mMarkerCount > 0) {
popMarker();
}
vkEndCommandBuffer(mBuffer);
VkSemaphore submissionSemaphore = mSubmission->getVkSemaphore();
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = mWaitSemaphores.size(),
@@ -209,7 +202,7 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
.commandBufferCount = 1u,
.pCommandBuffers = &mBuffer,
.signalSemaphoreCount = 1u,
.pSignalSemaphores = &submissionSemaphore,
.pSignalSemaphores = &mSubmission,
};
// add submit protection if needed
VkProtectedSubmitInfo protectedSubmitInfo{
@@ -230,13 +223,13 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
<< (s < mWaitSemaphores.size() - 1 ? "\n" : "");
}
FVK_LOGI << ") "
<< " signal=" << submissionSemaphore
<< " signal=" << mSubmission
<< " fence=" << mFence;
#endif
mFenceStatus->setStatus(VK_NOT_READY);
UTILS_UNUSED_IN_RELEASE VkResult result =
vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
mFenceStatus->setStatus(VK_NOT_READY);
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
if (result != VK_SUCCESS) {
@@ -249,7 +242,7 @@ fvkmemory::resource_ptr<VulkanSemaphore> VulkanCommandBuffer::submit() {
}
CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice device, VkQueue queue,
uint8_t queueFamilyIndex, VulkanSemaphoreManager* semaphoreManager, bool isProtected)
uint8_t queueFamilyIndex, bool isProtected)
: mDevice(device),
mRecording(INVALID) {
VkCommandPoolCreateInfo createInfo = {
@@ -262,8 +255,8 @@ CommandBufferPool::CommandBufferPool(VulkanContext const& context, VkDevice devi
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
for (size_t i = 0; i < CAPACITY; ++i) {
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(
context, device, queue, mPool, semaphoreManager, isProtected));
mBuffers.emplace_back(
std::make_unique<VulkanCommandBuffer>(context, device, queue, mPool, isProtected));
}
}
@@ -335,10 +328,10 @@ void CommandBufferPool::update() {
});
}
fvkmemory::resource_ptr<VulkanSemaphore> CommandBufferPool::flush() {
VkSemaphore CommandBufferPool::flush() {
// We're not recording right now.
if (!isRecording()) {
return {};
return VK_NULL_HANDLE;
}
auto submitSemaphore = mBuffers[mRecording]->submit();
mSubmitted.set(mRecording, true);
@@ -400,21 +393,16 @@ void CommandBufferPool::insertEvent(char const* marker) {
#endif // FVK_DEBUG_GROUP_MARKERS
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext const& context,
VulkanSemaphoreManager* semaphoreManager)
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext const& context)
: mDevice(device),
mProtectedQueue(protectedQueue),
mProtectedQueueFamilyIndex(protectedQueueFamilyIndex),
mContext(context),
mSemaphoreManager(semaphoreManager),
mPool(std::make_unique<CommandBufferPool>(
context, device, queue, queueFamilyIndex, semaphoreManager, false)) {}
mPool(std::make_unique<CommandBufferPool>(context, device, queue, queueFamilyIndex, false)) {}
void VulkanCommands::terminate() {
mPool.reset();
mProtectedPool.reset();
mLastSubmit = {};
mLastFenceStatus = {};
}
VulkanCommandBuffer& VulkanCommands::get() {
@@ -427,7 +415,7 @@ VulkanCommandBuffer& VulkanCommands::getProtected() {
if (!mProtectedPool) {
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mDevice, mProtectedQueue,
mProtectedQueueFamilyIndex, mSemaphoreManager, true);
mProtectedQueueFamilyIndex, true);
}
auto& ret = mProtectedPool->getRecording();
return ret;
@@ -439,8 +427,8 @@ bool VulkanCommands::flush() {
return false;
}
VkSemaphore injectedDependency = mInjectedDependency;
fvkmemory::resource_ptr<VulkanSemaphore> dependency;
VkSemaphore dependency = mInjectedDependency;
VkSemaphore lastSubmit = mLastSubmit;
bool hasFlushed = false;
VkFence flushedFence = VK_NULL_HANDLE;
@@ -453,19 +441,19 @@ bool VulkanCommands::flush() {
if (!pool || !pool->isRecording()) {
continue;
}
if (injectedDependency != VK_NULL_HANDLE) {
pool->waitFor(injectedDependency, mInjectedDependencyWaitStage);
if (dependency != VK_NULL_HANDLE) {
pool->waitFor(dependency, mInjectedDependencyWaitStage);
}
if (mLastSubmit) {
if (lastSubmit != VK_NULL_HANDLE) {
// Note that the stage we're waiting on is the fragment shader stage. This assumes
// that the subsequent command buffer will only depend on
// 1) fragment output of the previous command buffer
// 2) reading/writing of buffers (i.e. UBO) of the previous command buffer
// Restricting the wait stages will allow for vertex work to proceed (more overlapping
// vertex/fragment work).
pool->waitFor(mLastSubmit->getVkSemaphore(),
pool->waitFor(lastSubmit,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
mLastSubmit = {};
lastSubmit = VK_NULL_HANDLE;
}
flushedFence = pool->getRecording().getVkFence();
flushedFenceStatus = pool->getRecording().getFenceStatus();
+12 -15
View File
@@ -24,7 +24,6 @@
#include "VulkanAsyncHandles.h"
#include "VulkanConstants.h"
#include "VulkanContext.h"
#include "VulkanSemaphoreManager.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/StaticVector.h"
@@ -64,8 +63,8 @@ private:
// DriverApi fence object and should not be destroyed until both the DriverApi object is freed and
// we're done waiting on the most recent submission of the given command buffer.
struct VulkanCommandBuffer {
VulkanCommandBuffer(VulkanContext const& mContext, VkDevice device, VkQueue queue,
VkCommandPool pool, VulkanSemaphoreManager* semaphoreManager, bool isProtected);
VulkanCommandBuffer(VulkanContext const& mContext,
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected);
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
@@ -88,7 +87,7 @@ struct VulkanCommandBuffer {
void insertEvent(char const* marker) noexcept;
void begin() noexcept;
fvkmemory::resource_ptr<VulkanSemaphore> submit();
VkSemaphore submit();
inline void setComplete() {
mFenceStatus->setStatus(VK_SUCCESS);
@@ -122,11 +121,10 @@ private:
bool const isProtected;
VkDevice mDevice;
VkQueue mQueue;
VulkanSemaphoreManager* mSemaphoreManager;
fvkutils::StaticVector<VkSemaphore, 2> mWaitSemaphores;
fvkutils::StaticVector<VkPipelineStageFlags, 2> mWaitSemaphoreStages;
VkCommandBuffer mBuffer;
fvkmemory::resource_ptr<VulkanSemaphore> mSubmission;
VkSemaphore mSubmission;
VkFence mFence;
std::shared_ptr<VulkanCmdFence> mFenceStatus;
std::vector<fvkmemory::resource_ptr<Resource>> mResources;
@@ -138,14 +136,14 @@ struct CommandBufferPool {
static constexpr int8_t INVALID = -1;
CommandBufferPool(VulkanContext const& context, VkDevice device, VkQueue queue,
uint8_t queueFamilyIndex, VulkanSemaphoreManager* semaphoreManager, bool isProtected);
uint8_t queueFamilyIndex, bool isProtected);
~CommandBufferPool();
VulkanCommandBuffer& getRecording();
void gc();
void update();
fvkmemory::resource_ptr<VulkanSemaphore> flush();
VkSemaphore flush();
void wait();
void waitFor(VkSemaphore previousAction, VkPipelineStageFlags waitStage);
@@ -206,7 +204,7 @@ class VulkanCommands {
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex,
VulkanContext const& context, VulkanSemaphoreManager* semaphoreManager);
VulkanContext const& context);
void terminate();
@@ -224,10 +222,10 @@ public:
// Returns the "rendering finished" semaphore for the most recent flush and removes
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
fvkmemory::resource_ptr<VulkanSemaphore> acquireFinishedSignal() {
fvkmemory::resource_ptr<VulkanSemaphore> sem = mLastSubmit;
mLastSubmit = {};
return sem;
VkSemaphore acquireFinishedSignal() {
VkSemaphore ret = mLastSubmit;
mLastSubmit = VK_NULL_HANDLE;
return ret;
}
VkFence getMostRecentFence() {
@@ -268,13 +266,12 @@ private:
// For defered initialization if/when we need protected content
uint32_t const mProtectedQueueFamilyIndex;
VulkanContext const& mContext;
VulkanSemaphoreManager* mSemaphoreManager;
std::unique_ptr<CommandBufferPool> mPool;
std::unique_ptr<CommandBufferPool> mProtectedPool;
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
fvkmemory::resource_ptr<VulkanSemaphore> mLastSubmit;
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
VkFence mLastFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> mLastFenceStatus;
+44 -90
View File
@@ -43,8 +43,6 @@
#include <set> // For VulkanDriver::debugCommandBegin
#endif
#include <chrono>
using namespace bluevk;
#if defined(__clang__)
@@ -224,10 +222,9 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
mPlatform->getDevice())),
mContext(context),
mSemaphoreManager(mPlatform->getDevice(), &mResourceManager),
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(),
mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager),
mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext),
mPipelineLayoutCache(mPlatform->getDevice()),
mPipelineCache(mPlatform->getDevice()),
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
@@ -322,9 +319,8 @@ ShaderModel VulkanDriver::getShaderModel() const noexcept {
#endif
}
utils::FixedCapacityVector<ShaderLanguage> VulkanDriver::getShaderLanguages(
ShaderLanguage /*prefferedLanguage*/) const noexcept {
return { ShaderLanguage::SPIRV };
ShaderLanguage VulkanDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::SPIRV;
}
void VulkanDriver::terminate() {
@@ -369,9 +365,6 @@ void VulkanDriver::terminate() {
// reclaimed, as they perform cleanup within the stage pool.
mStagePool.terminate();
// By this point, all of the VkSemaphores should have been returned to the pool.
mSemaphoreManager.terminate();
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
mResourceManager.print();
#endif
@@ -483,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();
@@ -524,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);
@@ -543,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);
@@ -561,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,
@@ -580,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,
@@ -599,10 +577,10 @@ 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, usage);
mStagePool, mBufferCache, byteCount, bindingType);
bo.inc();
mResourceManager.associateHandle(boh.getId(), std::move(tag));
}
@@ -618,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,
@@ -635,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,
@@ -646,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);
@@ -658,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);
@@ -709,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
@@ -717,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));
@@ -740,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);
@@ -756,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,
@@ -769,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();
@@ -846,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;
@@ -857,11 +835,11 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
// it with appropriate VulkanCmdFence, which is associated with the current, recording command
// buffer.
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
fence->setFence(cmdbuf->getFenceStatus());
fence->fence = cmdbuf->getFenceStatus();
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;
@@ -892,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
@@ -921,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);
@@ -934,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();
@@ -948,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);
@@ -962,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>();
}
@@ -1070,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;
@@ -1158,30 +1113,29 @@ void VulkanDriver::destroyFence(Handle<HwFence> fh) {
fence.dec();
}
FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> const fh) {
return fenceWait(fh, 0);
}
FenceStatus VulkanDriver::fenceWait(FenceHandle const fh, uint64_t const timeout) {
FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> fh) {
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
// we have to take into account that the STL's wait_for() actually works with
// time_points relative to steady_clock::now() internally.
using namespace std::chrono;
auto const now = steady_clock::now();
steady_clock::time_point until = steady_clock::time_point::max();
if (now <= steady_clock::time_point::max() - nanoseconds(timeout)) {
until = now + nanoseconds(timeout);
}
std::shared_ptr const cmdfence = fence->wait(until);
auto& cmdfence = fence->fence;
if (!cmdfence) {
// If wait is called before a fence actually exists, we return timeout. This matches the
// current behavior in OpenGLDriver, but we should eventually reconsider a different error
// code.
return FenceStatus::TIMEOUT_EXPIRED;
}
// now we are holding a reference to our VulkanCmdFence, so we know it can't
// disappear while we wait
return cmdfence->wait(mPlatform->getDevice(), timeout, until);
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted".
// When this fence gets submitted, its status changes to VK_NOT_READY.
if (cmdfence->getStatus() == VK_SUCCESS) {
return FenceStatus::CONDITION_SATISFIED;
}
// Two other states are possible:
// - VK_INCOMPLETE: the corresponding buffer has not yet been submitted.
// - VK_NOT_READY: the buffer has been submitted but not yet signaled.
// In either case, we return TIMEOUT_EXPIRED to indicate the fence has not been signaled.
return FenceStatus::TIMEOUT_EXPIRED;
}
void VulkanDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
+1 -5
View File
@@ -28,7 +28,6 @@
#include "VulkanQueryManager.h"
#include "VulkanReadPixels.h"
#include "VulkanSamplerCache.h"
#include "VulkanSemaphoreManager.h"
#include "VulkanStagePool.h"
#include "VulkanYcbcrConversionCache.h"
#include "vulkan/VulkanDescriptorSetCache.h"
@@ -44,7 +43,6 @@
#include "DriverBase.h"
#include "private/backend/Driver.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Allocator.h>
#include <utils/compiler.h>
@@ -102,8 +100,7 @@ private:
Dispatcher getDispatcher() const noexcept final;
ShaderModel getShaderModel() const noexcept final;
utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;
template<typename T>
friend class ConcreteDispatcher;
@@ -142,7 +139,6 @@ private:
VulkanContext& mContext;
VulkanSemaphoreManager mSemaphoreManager;
VulkanCommands mCommands;
VulkanPipelineLayoutCache mPipelineLayoutCache;
VulkanPipelineCache mPipelineCache;
@@ -40,18 +40,18 @@ namespace filament::backend {
namespace {
inline VulkanBufferBinding getBufferObjectBinding(BufferObjectBinding bindingType) noexcept {
inline VulkanBufferUsage getBufferObjectUsage(BufferObjectBinding bindingType) noexcept {
switch (bindingType) {
case BufferObjectBinding::VERTEX:
return VulkanBufferBinding::VERTEX;
return VulkanBufferUsage::VERTEX;
case BufferObjectBinding::UNIFORM:
return VulkanBufferBinding::UNIFORM;
return VulkanBufferUsage::UNIFORM;
case BufferObjectBinding::SHADER_STORAGE:
return VulkanBufferBinding::SHADER_STORAGE;
return VulkanBufferUsage::SHADER_STORAGE;
// when adding more buffer-types here, make sure to update VulkanBuffer::loadFromCpu()
// if necessary.
}
return VulkanBufferBinding::UNKNOWN;
return VulkanBufferUsage::UNKNOWN;
}
void flipVertically(VkViewport* rect, uint32_t framebufferHeight) {
@@ -614,11 +614,10 @@ void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> b
VulkanBufferObject::VulkanBufferObject(VulkanContext const& context, VmaAllocator allocator,
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage)
BufferObjectBinding bindingType)
: HwBufferObject(byteCount),
bindingType(bindingType),
mBuffer(context, allocator, stagePool, bufferCache, getBufferObjectBinding(bindingType),
usage, byteCount) {}
mBuffer(context, allocator, stagePool, bufferCache, getBufferObjectUsage(bindingType), byteCount) {}
VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
+3 -14
View File
@@ -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>;
@@ -477,8 +466,8 @@ struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource {
uint32_t indexCount)
: HwIndexBuffer(elementSize, indexCount),
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32),
mBuffer(context, allocator, stagePool, bufferCache, VulkanBufferBinding::INDEX,
BufferUsage::STATIC, elementSize * indexCount) {}
mBuffer(context, allocator, stagePool, bufferCache, VulkanBufferUsage::INDEX,
elementSize * indexCount) {}
inline void loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData, uint32_t byteOffset,
uint32_t numBytes) {
@@ -496,7 +485,7 @@ private:
struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource {
VulkanBufferObject(VulkanContext const& context, VmaAllocator allocator,
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage);
BufferObjectBinding bindingType);
inline void loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData, uint32_t byteOffset,
uint32_t numBytes) {
+2 -2
View File
@@ -39,7 +39,7 @@ VK_DEFINE_HANDLE(VmaPool)
namespace filament::backend {
enum class VulkanBufferBinding : uint8_t {
enum class VulkanBufferUsage : uint8_t {
UNKNOWN,
VERTEX,
INDEX,
@@ -52,7 +52,7 @@ struct VulkanGpuBuffer {
VmaAllocation vmaAllocation = VK_NULL_HANDLE;
VmaAllocationInfo allocationInfo;
uint32_t numBytes = 0;
VulkanBufferBinding binding = VulkanBufferBinding::UNKNOWN;
VulkanBufferUsage usage = VulkanBufferUsage::UNKNOWN;
};
} // namespace filament::backend
@@ -1,29 +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 "vulkan/VulkanSemaphore.h"
#include "vulkan/VulkanSemaphoreManager.h"
namespace filament::backend {
VulkanSemaphore::VulkanSemaphore(VulkanSemaphoreManager* manager, VkSemaphore semaphore)
: mManager(manager), mSemaphore(semaphore) {}
VulkanSemaphore::~VulkanSemaphore() {
mManager->recycle(mSemaphore);
}
} // namespace filament::backend
@@ -1,42 +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_VULKAN_VULKANSEMAPHORE_H
#define TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHORE_H
#include "memory/Resource.h"
#include <bluevk/BlueVK.h>
namespace filament::backend {
class VulkanSemaphoreManager;
struct VulkanSemaphore : public fvkmemory::Resource {
public:
VulkanSemaphore(VulkanSemaphoreManager* manager, VkSemaphore semaphore);
~VulkanSemaphore();
VkSemaphore getVkSemaphore() const { return mSemaphore; }
private:
VulkanSemaphoreManager* mManager;
VkSemaphore mSemaphore;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHORE_H
@@ -1,70 +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 "VulkanSemaphoreManager.h"
#include "VulkanConstants.h"
using namespace bluevk;
namespace {
constexpr size_t INITIAL_POOL_SIZE = FVK_MAX_COMMAND_BUFFERS;
VkSemaphore createSemaphore(VkDevice device) {
VkSemaphore semaphore;
VkSemaphoreCreateInfo semaphoreInfo = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
};
vkCreateSemaphore(device, &semaphoreInfo, VKALLOC, &semaphore);
return semaphore;
}
} // namespace
namespace filament::backend {
VulkanSemaphoreManager::VulkanSemaphoreManager(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager) {
for (size_t i= 0; i < INITIAL_POOL_SIZE; ++i) {
mPool.push_back(createSemaphore(mDevice));
}
}
void VulkanSemaphoreManager::terminate() {
for (VkSemaphore semaphore : mPool) {
vkDestroySemaphore(mDevice, semaphore, VKALLOC);
}
mPool.clear();
}
VulkanSemaphoreManager::Semaphore VulkanSemaphoreManager::acquire() {
VkSemaphore semaphore;
if (!mPool.empty()) {
semaphore = mPool.back();
mPool.pop_back();
} else {
semaphore = createSemaphore(mDevice);
}
return Semaphore::construct(mResourceManager, this, semaphore);
}
void VulkanSemaphoreManager::recycle(VkSemaphore semaphore) {
mPool.push_back(semaphore);
}
} // namespace filament::backend
@@ -1,51 +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_VULKAN_VULKANSEMAPHOREMANAGER_H
#define TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHOREMANAGER_H
#include "vulkan/VulkanSemaphore.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
#include <bluevk/BlueVK.h>
#include <vector>
namespace filament::backend {
class VulkanSemaphoreManager {
public:
using Semaphore = fvkmemory::resource_ptr<VulkanSemaphore>;
VulkanSemaphoreManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanSemaphoreManager() = default;
void terminate();
Semaphore acquire();
private:
friend struct VulkanSemaphore;
void recycle(VkSemaphore semaphore);
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
std::vector<VkSemaphore> mPool;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKAN_VULKANSEMAPHOREMANAGER_H
@@ -59,10 +59,7 @@ VulkanSwapChain::~VulkanSwapChain() {
mColors = {};
mDepth = {};
for (auto& semaphore : mFinishedDrawing) {
semaphore = {};
}
mFinishedDrawing.clear();
mPlatform->destroy(swapChain);
}
@@ -70,17 +67,9 @@ void VulkanSwapChain::update() {
mColors.clear();
auto const bundle = mPlatform->getSwapChainBundle(swapChain);
size_t const swapChainCount = bundle.colors.size();
mColors.reserve(bundle.colors.size());
VkDevice const device = mPlatform->getDevice();
mFinishedDrawing.clear();
mFinishedDrawing.reserve(swapChainCount);
mFinishedDrawing.resize(swapChainCount);
for (size_t i = 0; i < swapChainCount; ++i) {
mFinishedDrawing[i] = {};
}
TextureUsage depthUsage = TextureUsage::DEPTH_ATTACHMENT;
TextureUsage colorUsage = TextureUsage::COLOR_ATTACHMENT;
if (bundle.isProtected) {
@@ -121,10 +110,8 @@ void VulkanSwapChain::present(DriverBase& driver) {
// We only present if it is not headless. No-op for headless.
if (!mHeadless) {
auto finishedDrawing = mCommands->acquireFinishedSignal();
mFinishedDrawing[mCurrentSwapIndex] = finishedDrawing;
VkResult const result =
mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing->getVkSemaphore());
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
VkResult const result = mPlatform->present(swapChain, mCurrentSwapIndex, finishedDrawing);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR ||
result == VK_ERROR_OUT_OF_DATE_KHR)
<< "Cannot present in swapchain. error=" << static_cast<int32_t>(result);
@@ -136,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);
});
}
}
@@ -162,7 +149,6 @@ void VulkanSwapChain::acquire(bool& resized) {
VulkanPlatform::ImageSyncData imageSyncData;
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
mCurrentSwapIndex = imageSyncData.imageIndex;
mFinishedDrawing[mCurrentSwapIndex] = {};
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
@@ -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,13 +104,12 @@ 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
// transitions, which are useful here.
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanTexture>> mColors;
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanSemaphore>> mFinishedDrawing;
fvkmemory::resource_ptr<VulkanTexture> mDepth;
VkExtent2D mExtent;
uint32_t mLayerCount;
@@ -39,8 +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 ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
template<typename D>
ResourceType getTypeEnum() noexcept {
@@ -98,12 +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;
}
if constexpr (std::is_same_v<D, VulkanSemaphore>) {
return ResourceType::SEMAPHORE;
}
return ResourceType::UNDEFINED_TYPE;
}
@@ -145,10 +137,6 @@ std::string_view getTypeStr(ResourceType type) {
return "VulkanBuffer";
case ResourceType::SYNC:
return "Sync";
case ResourceType::MEMORY_MAPPED_BUFFER:
return "VulkanMemoryMappedBuffer";
case ResourceType::SEMAPHORE:
return "Semaphore";
case ResourceType::UNDEFINED_TYPE:
return "";
}
@@ -53,9 +53,7 @@ enum class ResourceType : uint8_t {
STAGE_SEGMENT = 15,
STAGE_IMAGE = 16,
SYNC = 17,
MEMORY_MAPPED_BUFFER = 18,
SEMAPHORE = 19,
UNDEFINED_TYPE = 20, // 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,12 +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::SEMAPHORE:
destruct<VulkanSemaphore>(Handle<VulkanSemaphore>(id));
break;
case ResourceType::UNDEFINED_TYPE:
break;
}
@@ -201,9 +201,7 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
fvkutils::enumerate(vkEnumerateInstanceExtensionProperties,
static_cast<char const*>(nullptr) /* pLayerName */);
for (auto const& extension: availableExts) {
// The cast is to force the non-literal constructor of CString, which assumes
// null-terminated strings.
utils::CString name{ (char const*) extension.extensionName };
utils::CString name { extension.extensionName };
// To workaround an Adreno bug where the extension name could be of 0 length.
if (name.size() == 0) {
@@ -245,9 +243,7 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
= fvkutils::enumerate(vkEnumerateDeviceExtensionProperties, device,
static_cast<const char*>(nullptr) /* pLayerName */);
for (auto const& extension: extensions) {
// The cast is to force the non-literal constructor of CString, which assumes
// null-terminated strings.
utils::CString name { (char const*) extension.extensionName };
utils::CString name { extension.extensionName };
// To workaround an Adreno bug where the extension name could be of 0 length.
if (name.size() == 0) {
+26 -56
View File
@@ -132,9 +132,8 @@ ShaderModel WebGPUDriver::getShaderModel() const noexcept {
#endif
}
utils::FixedCapacityVector<ShaderLanguage> WebGPUDriver::getShaderLanguages(
ShaderLanguage /*preferredLanguage*/) const noexcept {
return { ShaderLanguage::WGSL };
ShaderLanguage WebGPUDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::WGSL;
}
// explicit instantiation of the Dispatcher
@@ -451,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
@@ -481,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);
@@ -494,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);
@@ -503,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,
@@ -513,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);
@@ -522,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));
@@ -531,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);
@@ -540,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);
@@ -551,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 "
@@ -578,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");
}
@@ -586,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);
@@ -619,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);
@@ -639,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,
@@ -651,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);
@@ -659,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(),
@@ -733,14 +732,6 @@ FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fenceHandle) {
return fence->getStatus();
}
FenceStatus WebGPUDriver::fenceWait(FenceHandle fenceHandle, uint64_t const timeout) {
const auto fence = handleCast<WebGPUFence>(fenceHandle);
if (!fence) {
return FenceStatus::ERROR;
}
return fence->wait(timeout);
}
void WebGPUDriver::destroySync(Handle<HwSync> syncHandle) {
if (syncHandle) {
destructHandle<WebGPUSync>(syncHandle);
@@ -791,7 +782,7 @@ bool WebGPUDriver::isFrameBufferFetchMultiSampleSupported() {
}
bool WebGPUDriver::isFrameTimeSupported() {
return false;
return true;
}
bool WebGPUDriver::isAutoDepthResolveSupported() {
@@ -2100,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
}
@@ -2216,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
+2 -4
View File
@@ -33,7 +33,6 @@
#include "private/backend/HandleAllocator.h"
#include <backend/DriverEnums.h>
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include "SpdMipmapGenerator/SpdMipmapGenerator.h"
@@ -66,11 +65,10 @@ public:
private:
WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
[[nodiscard]] utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept final;
[[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.
+15 -56
View File
@@ -19,72 +19,31 @@
#include <backend/DriverEnums.h>
#include <memory>
#include <webgpu/webgpu_cpp.h>
#include <condition_variable>
#include <chrono>
#include <cstdint>
#include <mutex>
#include <atomic>
namespace filament::backend {
WebGPUFence::~WebGPUFence() noexcept {
// if the fence was being waited on, make sure to return with an error
std::lock_guard const lock(state->lock);
state->status = FenceStatus::ERROR;
state->cond.notify_all();
}
FenceStatus WebGPUFence::getStatus() const {
std::lock_guard const lock(state->lock);
return state->status;
}
FenceStatus WebGPUFence::wait(uint64_t const timeout) {
// we have to take into account that the STL's wait_for() actually works with
// time_points relative to steady_clock::now() internally.
using namespace std::chrono;
auto const now = steady_clock::now();
steady_clock::time_point until = steady_clock::time_point::max();
if (now <= steady_clock::time_point::max() - nanoseconds(timeout)) {
until = now + nanoseconds(timeout);
}
std::unique_lock lock(state->lock);
state->cond.wait_until(lock, until,
[state = state] { return state->status != FenceStatus::TIMEOUT_EXPIRED; });
return state->status;
}
FenceStatus WebGPUFence::getStatus() { return mStatus.load(); }
void WebGPUFence::addMarkerToQueueState(wgpu::Queue const& queue) {
// The lambda function is called when the work is done. It updates the fence status based on the
// result of the work.
// Because the callback holds a weak_ptr to the state of the HwFence object, it can be
// destroyed before the fence signals.
std::weak_ptr const weak = state;
queue.OnSubmittedWorkDone(
wgpu::CallbackMode::AllowSpontaneous,
[weak](const wgpu::QueueWorkDoneStatus status, wgpu::StringView message) {
if (auto const state = weak.lock()) {
std::unique_lock const lock(state->lock);
if (state->status != FenceStatus::ERROR) {
switch (status) {
case wgpu::QueueWorkDoneStatus::Success:
state->status = FenceStatus::CONDITION_SATISFIED;
state->cond.notify_all();
break;
case wgpu::QueueWorkDoneStatus::CallbackCancelled:
case wgpu::QueueWorkDoneStatus::Error:
state->status = FenceStatus::ERROR;
state->cond.notify_all();
FWGPU_LOGW << "WebGPUFence: wgpu::QueueWorkDoneStatus::Error. " <<
message;
break;
}
}
}
});
wgpu::CallbackMode::AllowSpontaneous,
[this](const wgpu::QueueWorkDoneStatus status, wgpu::StringView message) {
switch (status) {
case wgpu::QueueWorkDoneStatus::Success:
mStatus.store(FenceStatus::CONDITION_SATISFIED);
break;
case wgpu::QueueWorkDoneStatus::CallbackCancelled:
case wgpu::QueueWorkDoneStatus::Error:
mStatus.store(FenceStatus::ERROR);
FWGPU_LOGW << "WebGPUFence: wgpu::QueueWorkDoneStatus::Error. " << message;
break;
}
});
}
} // namespace filament::backend
+6 -16
View File
@@ -18,14 +18,9 @@
#define TNT_FILAMENT_BACKEND_WEBGPUFENCE_H
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <utils/Mutex.h> // NOLINT(*-include-cleaner)
#include <utils/Condition.h> // NOLINT(*-include-cleaner)
#include <cstdint>
#include <memory>
#include <atomic>
namespace wgpu {
class Queue;
@@ -40,20 +35,15 @@ namespace filament::backend {
*/
class WebGPUFence final : public HwFence {
public:
~WebGPUFence() noexcept;
[[nodiscard]] FenceStatus getStatus() const;
[[nodiscard]] FenceStatus wait(uint64_t timeout);
[[nodiscard]] FenceStatus getStatus();
void addMarkerToQueueState(wgpu::Queue const&);
private:
struct State {
utils::Mutex lock; // NOLINT(*-include-cleaner)
utils::Condition cond; // NOLINT(*-include-cleaner)
FenceStatus status{ FenceStatus::TIMEOUT_EXPIRED };
};
// we need a shared_ptr because the Fence could be destroyed while we're waiting
std::shared_ptr<State> state{ std::make_shared<State>() };
// The current status of the fence.
// This is atomic because it can be updated by a WebGPU callback thread and read from the main
// thread.
std::atomic<FenceStatus> mStatus{ FenceStatus::TIMEOUT_EXPIRED };
};
} // namespace filament::backend
-37
View File
@@ -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));
-5
View File
@@ -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;
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

-4
View File
@@ -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;
+3 -12
View File
@@ -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, {});
+14 -3
View File
@@ -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

Some files were not shown because too many files have changed in this diff Show More