Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15424c85b5 |
@@ -12,7 +12,7 @@ jobs:
|
||||
name: build-android
|
||||
# We intentially use a larger runner here to enable larger disk space
|
||||
# (standard linux runner will fail on disk space and faster build time).
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: 'ubuntu-24.04-32core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
build-desktop-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-16core
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
@@ -171,7 +171,7 @@ jobs:
|
||||
|
||||
validate-wgsl-webgpu:
|
||||
name: validate-wgsl-webgpu
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: 'ubuntu-24.04-8core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
|
||||
@@ -31,7 +31,7 @@ on:
|
||||
jobs:
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
runs-on: ubuntu-22.04-32core
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
|
||||
|
||||
steps:
|
||||
|
||||
@@ -18,9 +18,3 @@ test*.json
|
||||
results
|
||||
/compile_commands.json
|
||||
/.cache
|
||||
|
||||
# For /test/renderdiff dependencies
|
||||
/mesa
|
||||
/gltf
|
||||
/filament-assets
|
||||
/venv
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.65.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
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
|
||||
@@ -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', '~> 1.65.2'
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.65.0'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
|
||||
@@ -1,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>
|
||||
|
Before Width: | Height: | Size: 361 KiB |
@@ -335,7 +335,7 @@ nodes</li>
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../notes/tests.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/libs.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
@@ -349,7 +349,7 @@ nodes</li>
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../notes/tests.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<a rel="next prefetch" href="../notes/libs.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../dup/renderdiff.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/framegraph.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
@@ -179,7 +179,7 @@
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../dup/renderdiff.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<a rel="prev" href="../notes/framegraph.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1,215 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="en" class="light sidebar-visible" dir="ltr">
|
||||
<head>
|
||||
<!-- Book generated using mdBook -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Tests - Filament</title>
|
||||
|
||||
|
||||
<!-- Custom HTML head -->
|
||||
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="shortcut icon" href="../favicon.png">
|
||||
<link rel="stylesheet" href="../css/variables.css">
|
||||
<link rel="stylesheet" href="../css/general.css">
|
||||
<link rel="stylesheet" href="../css/chrome.css">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
|
||||
<link rel="stylesheet" href="../fonts/fonts.css">
|
||||
|
||||
<!-- Highlight.js Stylesheets -->
|
||||
<link rel="stylesheet" href="../highlight.css">
|
||||
<link rel="stylesheet" href="../tomorrow-night.css">
|
||||
<link rel="stylesheet" href="../ayu-highlight.css">
|
||||
|
||||
<!-- Custom theme stylesheets -->
|
||||
|
||||
<!-- MathJax -->
|
||||
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||||
|
||||
<!-- Provide site root to javascript -->
|
||||
<script>
|
||||
var path_to_root = "../";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
|
||||
</script>
|
||||
<!-- Start loading toc.js asap -->
|
||||
<script src="../toc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="body-container">
|
||||
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
||||
<script>
|
||||
try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }
|
||||
</script>
|
||||
|
||||
<!-- Set the theme before any content is loaded, prevents flash -->
|
||||
<script>
|
||||
var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
const html = document.documentElement;
|
||||
html.classList.remove('light')
|
||||
html.classList.add(theme);
|
||||
html.classList.add("js");
|
||||
</script>
|
||||
|
||||
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
|
||||
|
||||
<!-- Hide / unhide sidebar before it is displayed -->
|
||||
<script>
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
html.classList.remove('sidebar-visible');
|
||||
html.classList.add("sidebar-" + sidebar);
|
||||
</script>
|
||||
|
||||
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
||||
<div style="display:flex;align-items:center;justify-content:center">
|
||||
<img class="flogo" src="../images/filament_logo_small.png"></img>
|
||||
</div>
|
||||
<!-- populated by js -->
|
||||
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
|
||||
<noscript>
|
||||
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
|
||||
</noscript>
|
||||
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
|
||||
<div class="sidebar-resize-indicator"></div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper" class="page-wrapper">
|
||||
|
||||
<div class="page">
|
||||
<div id="menu-bar-hover-placeholder"></div>
|
||||
<div id="menu-bar" class="menu-bar sticky">
|
||||
<div class="left-buttons">
|
||||
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
||||
<i class="fa fa-bars"></i>
|
||||
</label>
|
||||
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
|
||||
<!--
|
||||
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
<i class="fa fa-paint-brush"></i>
|
||||
</button>
|
||||
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
||||
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
||||
</ul>
|
||||
-->
|
||||
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<h1 class="menu-title">Filament</h1>
|
||||
|
||||
<div class="right-buttons">
|
||||
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
|
||||
<i id="git-repository-button" class="fa fa-github"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="search-wrapper" class="hidden">
|
||||
<form id="searchbar-outer" class="searchbar-outer">
|
||||
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
||||
</form>
|
||||
<div id="searchresults-outer" class="searchresults-outer hidden">
|
||||
<div id="searchresults-header" class="searchresults-header"></div>
|
||||
<ul id="searchresults">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
||||
<script>
|
||||
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div id="content" class="content">
|
||||
<main>
|
||||
<h1 id="tests"><a class="header" href="#tests">Tests</a></h1>
|
||||
<p>Filament has a collection of tests that can be run locally. Many of them are used
|
||||
for in our Continuation Integration flow <a href="https://https://github.com/google/filament/tree/main/.github/workflows">on github</a>.</p>
|
||||
|
||||
</main>
|
||||
|
||||
<nav class="nav-wrapper" aria-label="Page navigation">
|
||||
<!-- Mobile navigation buttons -->
|
||||
<a rel="prev" href="../notes/framegraph.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/renderdiff.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
|
||||
<div style="clear: both"></div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
||||
<a rel="prev" href="../notes/framegraph.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
||||
<i class="fa fa-angle-left"></i>
|
||||
</a>
|
||||
|
||||
<a rel="next prefetch" href="../dup/renderdiff.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
||||
<i class="fa fa-angle-right"></i>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
window.playground_copyable = true;
|
||||
</script>
|
||||
|
||||
|
||||
<script src="../elasticlunr.min.js"></script>
|
||||
<script src="../mark.min.js"></script>
|
||||
<script src="../searcher.js"></script>
|
||||
|
||||
<script src="../clipboard.min.js"></script>
|
||||
<script src="../highlight.js"></script>
|
||||
<script src="../book.js"></script>
|
||||
|
||||
<!-- Custom JS scripts -->
|
||||
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -78,11 +78,5 @@
|
||||
},
|
||||
"docs_src/README.md": {
|
||||
"dest": "dup/docs.md"
|
||||
},
|
||||
"test/renderdiff/README.md": {
|
||||
"dest": "dup/renderdiff.md",
|
||||
"link_transforms": {
|
||||
"docs/images/": "../images/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
- [Code coverage analysis](./notes/coverage.md)
|
||||
- [Performance analysis](./notes/performance_analysis.md)
|
||||
- [Framegraph](./notes/framegraph.md)
|
||||
- [Tests](./notes/tests.md)
|
||||
- [renderdiff](./dup/renderdiff.md)
|
||||
- [Libraries](./notes/libs.md)
|
||||
- [bluegl](./dup/bluegl.md)
|
||||
- [bluevk](./dup/bluevk.md)
|
||||
|
||||
|
Before Width: | Height: | Size: 361 KiB |
@@ -1,4 +0,0 @@
|
||||
# Tests
|
||||
|
||||
Filament has a collection of tests that can be run locally. Many of them are used
|
||||
for in our Continuation Integration flow [on github](https://https://github.com/google/filament/tree/main/.github/workflows).
|
||||
@@ -84,8 +84,6 @@ set(SRCS
|
||||
src/InstanceBuffer.cpp
|
||||
src/LightManager.cpp
|
||||
src/Material.cpp
|
||||
src/MaterialCache.cpp
|
||||
src/MaterialDefinition.cpp
|
||||
src/MaterialInstance.cpp
|
||||
src/MaterialInstanceManager.cpp
|
||||
src/MaterialParser.cpp
|
||||
@@ -177,8 +175,6 @@ set(PRIVATE_HDRS
|
||||
src/HwRenderPrimitiveFactory.h
|
||||
src/HwVertexBufferInfoFactory.h
|
||||
src/Intersections.h
|
||||
src/MaterialCache.h
|
||||
src/MaterialDefinition.h
|
||||
src/MaterialParser.h
|
||||
src/MaterialInstanceManager.h
|
||||
src/PIDController.h
|
||||
|
||||
@@ -78,8 +78,6 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
|
||||
src/opengl/GLTexture.h
|
||||
src/opengl/GLUtils.cpp
|
||||
src/opengl/GLUtils.h
|
||||
src/opengl/GLMemoryMappedBuffer.cpp
|
||||
src/opengl/GLMemoryMappedBuffer.h
|
||||
src/opengl/OpenGLBlobCache.cpp
|
||||
src/opengl/OpenGLBlobCache.h
|
||||
src/opengl/OpenGLContext.cpp
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -18,16 +18,10 @@
|
||||
|
||||
#include "DataReshaper.h"
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <string_view>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -642,7 +636,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
switch (data.type) {
|
||||
case PixelDataType::BYTE:
|
||||
case PixelDataType::UBYTE: {
|
||||
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint8_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
|
||||
data.alignment, data.left, data.top, data.stride, freeFunc);
|
||||
@@ -652,7 +646,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
case PixelDataType::SHORT:
|
||||
case PixelDataType::USHORT:
|
||||
case PixelDataType::HALF: {
|
||||
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint16_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
|
||||
data.alignment, data.left, data.top, data.stride, freeFunc);
|
||||
@@ -661,7 +655,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
}
|
||||
case PixelDataType::INT:
|
||||
case PixelDataType::UINT: {
|
||||
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<uint32_t, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
|
||||
data.alignment, data.left, data.top, data.stride, freeFunc);
|
||||
@@ -669,7 +663,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
|
||||
return true;
|
||||
}
|
||||
case PixelDataType::FLOAT: {
|
||||
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
|
||||
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
|
||||
DataReshaper::reshape<float, 3, 4>(bytes, data.buffer, data.size);
|
||||
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
|
||||
data.alignment, data.left, data.top, data.stride, freeFunc);
|
||||
|
||||
@@ -63,7 +63,6 @@ public:
|
||||
static void copyImage(uint8_t* UTILS_RESTRICT dest,
|
||||
const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow, size_t /*srcChannelCount*/,
|
||||
size_t /*dstRowOffset */, size_t /*dstColumnOffset */,
|
||||
size_t dstBytesPerRow, size_t /*dstChannelCount*/,
|
||||
size_t /*width*/, size_t height, bool /*swizzle*/) {
|
||||
if (srcBytesPerRow == dstBytesPerRow) {
|
||||
@@ -80,20 +79,17 @@ public:
|
||||
template<typename dstComponentType, typename srcComponentType>
|
||||
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow,
|
||||
size_t srcChannelCount,
|
||||
size_t dstRowOffset, size_t dstColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t srcChannelCount, size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t width, size_t height, bool swizzle) {
|
||||
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
|
||||
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
|
||||
const size_t minChannelCount = math::min(srcChannelCount, dstChannelCount);
|
||||
assert_invariant(minChannelCount <= 4);
|
||||
UTILS_ASSUME(minChannelCount <= 4);
|
||||
dest += (dstRowOffset * dstBytesPerRow);
|
||||
const int inds[4] = { swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3 };
|
||||
for (size_t row = 0; row < height; ++row) {
|
||||
const srcComponentType* in = (const srcComponentType*) src;
|
||||
dstComponentType* out = (dstComponentType*)dest + (dstColumnOffset * dstChannelCount);
|
||||
const srcComponentType* in = (const srcComponentType*)src;
|
||||
dstComponentType* out = (dstComponentType*)dest;
|
||||
for (size_t column = 0; column < width; ++column) {
|
||||
for (size_t channel = 0; channel < minChannelCount; ++channel) {
|
||||
if constexpr (std::is_same_v<dstComponentType, srcComponentType>) {
|
||||
@@ -132,23 +128,17 @@ public:
|
||||
default: return false;
|
||||
}
|
||||
void (*reshaper)(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
|
||||
size_t srcChannelCount,
|
||||
size_t srcRowOffset, size_t srcColumnOffset,
|
||||
size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t srcChannelCount, size_t dstBytesPerRow, size_t dstChannelCount,
|
||||
size_t width, size_t height, bool swizzle) = nullptr;
|
||||
constexpr auto UBYTE = PixelDataType::UBYTE;
|
||||
constexpr auto FLOAT = PixelDataType::FLOAT;
|
||||
constexpr auto UINT = PixelDataType::UINT;
|
||||
constexpr auto INT = PixelDataType::INT;
|
||||
constexpr auto HALF = PixelDataType::HALF;
|
||||
constexpr auto UBYTE = PixelDataType::UBYTE, FLOAT = PixelDataType::FLOAT,
|
||||
UINT = PixelDataType::UINT, INT = PixelDataType::INT;
|
||||
switch (dst->type) {
|
||||
case UBYTE:
|
||||
switch (srcType) {
|
||||
case UBYTE:
|
||||
reshaper = reshapeImage<uint8_t, uint8_t>;
|
||||
if (dst->format == PixelDataFormat::RGBA &&
|
||||
dstChannelCount == srcChannelCount && !swizzle && dst->top == 0 &&
|
||||
dst->left == 0) {
|
||||
dstChannelCount == srcChannelCount && !swizzle) {
|
||||
reshaper = copyImage;
|
||||
}
|
||||
break;
|
||||
@@ -185,20 +175,13 @@ public:
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
case HALF:
|
||||
switch (srcType) {
|
||||
case HALF: reshaper = copyImage; break;
|
||||
default: return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
uint8_t* dstBytes = (uint8_t*) dst->buffer;
|
||||
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(dst->format, dst->type,
|
||||
dst->stride ? dst->stride : width, 1, dst->alignment);
|
||||
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount,
|
||||
dst->top, dst->left, dstBytesPerRow,
|
||||
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount, dstBytesPerRow,
|
||||
dstChannelCount, width, height, swizzle);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -78,9 +78,6 @@ struct HwBufferObject : public HwBase {
|
||||
explicit HwBufferObject(uint32_t byteCount) noexcept : byteCount(byteCount) {}
|
||||
};
|
||||
|
||||
struct HwMemoryMappedBuffer : public HwBase {
|
||||
};
|
||||
|
||||
struct HwIndexBuffer : public HwBase {
|
||||
uint32_t count : 27;
|
||||
uint32_t elementSize : 5;
|
||||
@@ -214,7 +211,7 @@ public:
|
||||
protected:
|
||||
class CallbackDataDetails;
|
||||
|
||||
void scheduleDestroy(BufferDescriptor&& buffer) noexcept {
|
||||
inline void scheduleDestroy(BufferDescriptor&& buffer) noexcept {
|
||||
if (buffer.hasCallback()) {
|
||||
scheduleDestroySlow(std::move(buffer));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
|
||||
|
||||
#include "MetalContext.h"
|
||||
#include "MetalFlags.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/platforms/PlatformMetal.h>
|
||||
@@ -154,7 +153,7 @@ private:
|
||||
Type mType = Type::NONE;
|
||||
|
||||
static PlatformMetal* platform;
|
||||
static std::array<uint64_t, TypeCount> aliveBuffers;
|
||||
static std::array<std::atomic<uint64_t>, TypeCount> aliveBuffers;
|
||||
};
|
||||
|
||||
class MetalBuffer {
|
||||
@@ -192,15 +191,6 @@ public:
|
||||
|
||||
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
|
||||
|
||||
void setLabel(const utils::CString& label) {
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
if (label.empty()) {
|
||||
return;
|
||||
}
|
||||
mBuffer.get().label = @(label.c_str_safe());
|
||||
#endif
|
||||
}
|
||||
|
||||
enum Stage : uint8_t {
|
||||
VERTEX = 1u << 0u,
|
||||
FRAGMENT = 1u << 1u,
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
std::array<uint64_t, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
|
||||
std::array<std::atomic<uint64_t>, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
|
||||
PlatformMetal* TrackedMetalBuffer::platform = nullptr;
|
||||
PlatformMetal* ScopedAllocationTimer::platform = nullptr;
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
context->pendingCommandBuffer = [context->commandQueue commandBuffer];
|
||||
// It's safe for this block to capture the context variable. MetalDriver::terminate will ensure
|
||||
// all frames and their completion handlers finish before context is deallocated.
|
||||
const uint64_t thisCommandBufferId = context->pendingCommandBufferId;
|
||||
uint64_t thisCommandBufferId = context->pendingCommandBufferId;
|
||||
[context->pendingCommandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
|
||||
context->resourceTracker.clearResources((__bridge void*) buffer);
|
||||
|
||||
@@ -118,11 +118,6 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
context->memorylessLimitsReached = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
|
||||
LOG(ERROR) << "Filament Metal command buffer errored with code: " << errorCode << " ("
|
||||
<< stringifyMTLCommandBufferError(errorCode) << ").";
|
||||
}
|
||||
}];
|
||||
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
|
||||
<< "Could not obtain command buffer.";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -451,38 +451,6 @@ inline MTLTextureSwizzleChannels getSwizzleChannels(TextureSwizzle r, TextureSwi
|
||||
getSwizzle(a));
|
||||
}
|
||||
|
||||
inline const char* stringifyMTLCommandBufferError(MTLCommandBufferError error) {
|
||||
#if !defined(FILAMENT_IOS)
|
||||
if (error == MTLCommandBufferErrorDeviceRemoved) {
|
||||
return "MTLCommandBufferErrorDeviceRemoved";
|
||||
}
|
||||
#endif
|
||||
switch (error) {
|
||||
case MTLCommandBufferErrorNone:
|
||||
return "MTLCommandBufferErrorNone";
|
||||
case MTLCommandBufferErrorInternal:
|
||||
return "MTLCommandBufferErrorInternal";
|
||||
case MTLCommandBufferErrorTimeout:
|
||||
return "MTLCommandBufferErrorTimeout";
|
||||
case MTLCommandBufferErrorPageFault:
|
||||
return "MTLCommandBufferErrorPageFault";
|
||||
case MTLCommandBufferErrorAccessRevoked:
|
||||
return "MTLCommandBufferErrorAccessRevoked";
|
||||
case MTLCommandBufferErrorNotPermitted:
|
||||
return "MTLCommandBufferErrorNotPermitted";
|
||||
case MTLCommandBufferErrorOutOfMemory:
|
||||
return "MTLCommandBufferErrorOutOfMemory";
|
||||
case MTLCommandBufferErrorInvalidResource:
|
||||
return "MTLCommandBufferErrorInvalidResource";
|
||||
case MTLCommandBufferErrorMemoryless:
|
||||
return "MTLCommandBufferErrorMemoryless";
|
||||
case MTLCommandBufferErrorStackOverflow:
|
||||
return "MTLCommandBufferErrorStackOverflow";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_DRIVER_METALFLAGS_H
|
||||
#define TNT_FILAMENT_DRIVER_METALFLAGS_H
|
||||
|
||||
#ifndef FILAMENT_METAL_DEBUG_LABELS
|
||||
#ifndef NDEBUG
|
||||
#define FILAMENT_METAL_DEBUG_LABELS 1
|
||||
#else
|
||||
#define FILAMENT_METAL_DEBUG_LABELS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "MetalContext.h"
|
||||
#include "MetalEnums.h"
|
||||
#include "MetalExternalImage.h"
|
||||
#include "MetalFlags.h"
|
||||
#include "MetalState.h" // for MetalState::VertexDescription
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -267,16 +266,6 @@ public:
|
||||
|
||||
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
|
||||
|
||||
void setLabel(const utils::CString& label) {
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
if (label.empty()) {
|
||||
return;
|
||||
}
|
||||
texture.label = @(label.c_str_safe());
|
||||
swizzledTextureView.label = @(label.c_str_safe());
|
||||
#endif
|
||||
}
|
||||
|
||||
MetalContext& context;
|
||||
|
||||
// A "sidecar" texture used to implement automatic MSAA resolve.
|
||||
@@ -494,15 +483,6 @@ struct MetalDescriptorSet : public HwDescriptorSet {
|
||||
|
||||
void finalize(MetalDriver* driver);
|
||||
|
||||
void setLabel(const utils::CString& l) {
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
if (l.empty()) {
|
||||
return;
|
||||
}
|
||||
label = l;
|
||||
#endif
|
||||
}
|
||||
|
||||
id<MTLBuffer> finalizeAndGetBuffer(MetalDriver* driver, ShaderStage stage);
|
||||
|
||||
MetalDescriptorSetLayout* layout;
|
||||
@@ -525,22 +505,6 @@ struct MetalDescriptorSet : public HwDescriptorSet {
|
||||
std::vector<std::shared_ptr<MetalExternalImage>> externalImages;
|
||||
|
||||
std::array<TrackedMetalBuffer, Program::SHADER_TYPE_COUNT> cachedBuffer = { nil };
|
||||
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
utils::CString label;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct MetalMemoryMappedBuffer : public HwMemoryMappedBuffer {
|
||||
MetalMemoryMappedBuffer(BufferObjectHandle boh, size_t const offset,
|
||||
size_t const size, MapBufferAccessFlags const access)
|
||||
: boh(boh), access(access), size(size), offset(offset) {
|
||||
}
|
||||
BufferObjectHandle boh{};
|
||||
MapBufferAccessFlags access{};
|
||||
uint32_t size = 0;
|
||||
uint32_t offset = 0;
|
||||
};
|
||||
|
||||
} // namespace backend
|
||||
|
||||
@@ -295,12 +295,6 @@ void MetalSwapChain::ensureDepthStencilTexture() {
|
||||
|
||||
void MetalSwapChain::setFrameScheduledCallback(
|
||||
CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags) {
|
||||
if (!callback) {
|
||||
frameScheduled.handler = nullptr;
|
||||
frameScheduled.callback.reset();
|
||||
frameScheduled.flags = 0;
|
||||
return;
|
||||
}
|
||||
frameScheduled.handler = handler;
|
||||
frameScheduled.callback = std::make_shared<FrameScheduledCallback>(std::move(callback));
|
||||
frameScheduled.flags = flags;
|
||||
@@ -1492,11 +1486,6 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
|
||||
buffer = { [context.device newBufferWithLength:encoder.encodedLength
|
||||
options:MTLResourceStorageModeShared],
|
||||
TrackedMetalBuffer::Type::DESCRIPTOR_SET };
|
||||
#if FILAMENT_METAL_DEBUG_LABELS
|
||||
if (!label.empty()) {
|
||||
buffer.get().label = @(label.c_str_safe());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
[encoder setArgumentBuffer:buffer.get() offset:0];
|
||||
|
||||
|
||||
@@ -134,7 +134,6 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
case ShaderLanguage::ESSL3:
|
||||
case ShaderLanguage::SPIRV:
|
||||
case ShaderLanguage::WGSL:
|
||||
case ShaderLanguage::UNSPECIFIED:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -40,12 +40,10 @@ struct GLBufferObject : public HwBufferObject {
|
||||
GLenum binding;
|
||||
void* buffer;
|
||||
};
|
||||
} gl{};
|
||||
BufferUsage usage : 4;
|
||||
BufferObjectBinding bindingType : 4;
|
||||
uint8_t mappingCount = 0;
|
||||
} gl;
|
||||
BufferUsage usage;
|
||||
BufferObjectBinding bindingType;
|
||||
uint16_t age = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "GLMemoryMappedBuffer.h"
|
||||
|
||||
#include "GLBufferObject.h"
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "OpenGLDriver.h"
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <private/backend/HandleAllocator.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
GLMemoryMappedBuffer::GLMemoryMappedBuffer() = default;
|
||||
|
||||
GLMemoryMappedBuffer::~GLMemoryMappedBuffer() = default;
|
||||
|
||||
GLMemoryMappedBuffer::GLMemoryMappedBuffer(OpenGLContext& glc, HandleAllocatorGL& handleAllocator,
|
||||
BufferObjectHandle boh,
|
||||
size_t const offset, size_t const size, MapBufferAccessFlags const access)
|
||||
: boh(boh), access(access) {
|
||||
|
||||
GLBufferObject* const bo = handleAllocator.handle_cast<GLBufferObject*>(boh);
|
||||
|
||||
assert_invariant(bo);
|
||||
assert_invariant(bo->mappingCount < std::numeric_limits<uint8_t>::max());
|
||||
assert_invariant(offset + size <= bo->byteCount);
|
||||
|
||||
if (any(access & MapBufferAccessFlags::WRITE_BIT)) {
|
||||
assert_invariant(any(bo->usage & BufferUsage::SHARED_WRITE_BIT));
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(glc.isES2())) {
|
||||
gl.vaddr = static_cast<char*>(bo->gl.buffer) + offset;
|
||||
gl.size = size;
|
||||
gl.offset = offset;
|
||||
gl.binding = 0; // in ES2 mode, bo->gl.binding is not available
|
||||
gl.id = bo->gl.id;
|
||||
bo->age++; // technically we could do this only in copy()
|
||||
bo->mappingCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
#if !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
void* addr = nullptr;
|
||||
|
||||
# if !defined(__EMSCRIPTEN__)
|
||||
// unsynchronized is implied (but is incompatible with reading)
|
||||
GLbitfield gl_access = GL_MAP_UNSYNCHRONIZED_BIT;
|
||||
if (any(access & MapBufferAccessFlags::WRITE_BIT)) {
|
||||
gl_access |= GL_MAP_WRITE_BIT;
|
||||
}
|
||||
if (any(access & MapBufferAccessFlags::INVALIDATE_RANGE_BIT)) {
|
||||
// note: GL_MAP_INVALIDATE_RANGE_BIT is incompatible with GL_MAP_READ_BIT
|
||||
gl_access |= GL_MAP_INVALIDATE_RANGE_BIT;
|
||||
}
|
||||
|
||||
glc.bindBuffer(bo->gl.binding, bo->gl.id);
|
||||
|
||||
addr = glMapBufferRange(bo->gl.binding, GLsizeiptr(offset), GLsizeiptr(size), gl_access);
|
||||
|
||||
CHECK_GL_ERROR();
|
||||
# endif
|
||||
|
||||
// if we failed here, addr would be nullptr
|
||||
gl.vaddr = addr;
|
||||
gl.size = size;
|
||||
gl.offset = offset;
|
||||
gl.binding = bo->gl.binding;
|
||||
gl.id = bo->gl.id;
|
||||
bo->mappingCount++;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLMemoryMappedBuffer::unmap(OpenGLContext& glc, HandleAllocatorGL& handleAllocator) const {
|
||||
GLBufferObject* const bo = handleAllocator.handle_cast<GLBufferObject*>(boh);
|
||||
assert_invariant(bo);
|
||||
assert_invariant(bo->mappingCount > 0);
|
||||
|
||||
bo->mappingCount--;
|
||||
|
||||
if (UTILS_UNLIKELY(glc.isES2())) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
glc.bindBuffer(gl.binding, gl.id);
|
||||
# if !defined(__EMSCRIPTEN__)
|
||||
// don't unmap if we don't have a mapping or it didn't work
|
||||
if (UTILS_LIKELY(gl.vaddr)) {
|
||||
if (UTILS_UNLIKELY(glUnmapBuffer(gl.binding) == GL_FALSE)) {
|
||||
// TODO: According to the spec, UnmapBuffer can return FALSE in rare conditions (e.g.
|
||||
// during a screen mode change). Note that this is not a GL error, but the whole
|
||||
// mapping is lost. This is very problematic for us here.
|
||||
}
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLMemoryMappedBuffer::copy(OpenGLContext& glc, OpenGLDriver& gld,
|
||||
size_t const offset, BufferDescriptor&& data) const {
|
||||
assert_invariant(any(access & MapBufferAccessFlags::WRITE_BIT));
|
||||
assert_invariant(offset + data.size <= gl.size);
|
||||
|
||||
if (UTILS_LIKELY(gl.vaddr)) {
|
||||
memcpy(static_cast<char *>(gl.vaddr) + offset, data.buffer, data.size);
|
||||
} else {
|
||||
assert_invariant(!glc.isES2());
|
||||
// we couldn't map (WebGL or error), revert to glBufferSubData. In the future we can
|
||||
// improve this by keeping around the BufferDescriptor and coalescing the calls
|
||||
// to glBufferSubData.
|
||||
glc.bindBuffer(gl.binding, gl.id);
|
||||
glBufferSubData(gl.binding, GLintptr(gl.offset + offset), GLsizeiptr(data.size), data.buffer);
|
||||
CHECK_GL_ERROR();
|
||||
}
|
||||
|
||||
gld.scheduleDestroy(std::move(data));
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include <private/backend/HandleAllocator.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class OpenGLContext;
|
||||
class OpenGLDriver;
|
||||
class BufferDescriptor;
|
||||
|
||||
struct GLBufferObject;
|
||||
|
||||
struct GLMemoryMappedBuffer : public HwMemoryMappedBuffer {
|
||||
BufferObjectHandle boh;
|
||||
MapBufferAccessFlags access;
|
||||
struct {
|
||||
void* vaddr = nullptr;
|
||||
uint32_t size = 0;
|
||||
uint32_t offset = 0;
|
||||
GLenum binding = 0;
|
||||
GLuint id = 0;
|
||||
} gl;
|
||||
|
||||
GLMemoryMappedBuffer();
|
||||
|
||||
GLMemoryMappedBuffer(OpenGLContext& glc, HandleAllocatorGL& handleAllocator,
|
||||
BufferObjectHandle boh, size_t offset, size_t size, MapBufferAccessFlags access);
|
||||
|
||||
~GLMemoryMappedBuffer();
|
||||
|
||||
void unmap(OpenGLContext& gl, HandleAllocatorGL& handleAllocator) const;
|
||||
|
||||
void copy(OpenGLContext& glc, OpenGLDriver& gld,
|
||||
size_t offset, BufferDescriptor&& data) const;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif //TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
|
||||
@@ -21,14 +21,12 @@
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -103,8 +101,6 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
initBugs(&bugs, ext, state.major, state.minor,
|
||||
state.vendor, state.renderer, state.version, state.shader);
|
||||
|
||||
initWorkarounds(bugs, &ext);
|
||||
|
||||
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -39,12 +39,7 @@ Cleanup::~Cleanup() {
|
||||
for (const auto descriptorSetLayout : mDescriptorSetLayouts) {
|
||||
mDriverApi.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
}
|
||||
for (const auto memoryMappedBuffer : mMemoryMappedBuffers) {
|
||||
// destroy those before buffer objects
|
||||
mDriverApi.unmapBuffer(memoryMappedBuffer);
|
||||
}
|
||||
for (const auto bufferObject : mBufferObjects) {
|
||||
// destroy those after memory mapped buffers
|
||||
mDriverApi.destroyBufferObject(bufferObject);
|
||||
}
|
||||
for (const auto program : mPrograms) {
|
||||
@@ -56,18 +51,6 @@ Cleanup::~Cleanup() {
|
||||
for (const auto renderTarget : mRenderTargets) {
|
||||
mDriverApi.destroyRenderTarget(renderTarget);
|
||||
}
|
||||
for (const auto renderPrimitive : mRenderPrimitives) {
|
||||
mDriverApi.destroyRenderPrimitive(renderPrimitive);
|
||||
}
|
||||
for (const auto vertexBufferInfo : mVertexBufferInfos) {
|
||||
mDriverApi.destroyVertexBufferInfo(vertexBufferInfo);
|
||||
}
|
||||
for (const auto indexBuffer : mIndexBuffers) {
|
||||
mDriverApi.destroyIndexBuffer(indexBuffer);
|
||||
}
|
||||
for (const auto vertexBuffer : mVertexBuffers) {
|
||||
mDriverApi.destroyVertexBuffer(vertexBuffer);
|
||||
}
|
||||
|
||||
for (const auto& callback : mCallbacks) {
|
||||
callback();
|
||||
@@ -103,26 +86,6 @@ template <>
|
||||
void Cleanup::addInternal(filament::backend::RenderTargetHandle handle) {
|
||||
mRenderTargets.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::MemoryMappedBufferHandle handle) {
|
||||
mMemoryMappedBuffers.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::RenderPrimitiveHandle handle) {
|
||||
mRenderPrimitives.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::VertexBufferInfoHandle handle) {
|
||||
mVertexBufferInfos.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::VertexBufferHandle handle) {
|
||||
mVertexBuffers.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::IndexBufferHandle handle) {
|
||||
mIndexBuffers.push_back(handle);
|
||||
}
|
||||
|
||||
void Cleanup::addPostCall(std::function<void()> callback){
|
||||
mCallbacks.emplace_back(std::move(callback));
|
||||
|
||||
@@ -68,11 +68,6 @@ private:
|
||||
std::vector<filament::backend::ProgramHandle> mPrograms;
|
||||
std::vector<filament::backend::TextureHandle> mTextures;
|
||||
std::vector<filament::backend::RenderTargetHandle> mRenderTargets;
|
||||
std::vector<filament::backend::RenderPrimitiveHandle> mRenderPrimitives;
|
||||
std::vector<filament::backend::VertexBufferInfoHandle> mVertexBufferInfos;
|
||||
std::vector<filament::backend::VertexBufferHandle> mVertexBuffers;
|
||||
std::vector<filament::backend::IndexBufferHandle> mIndexBuffers;
|
||||
std::vector<filament::backend::MemoryMappedBufferHandle> mMemoryMappedBuffers;
|
||||
|
||||
std::vector<std::function<void()>> mCallbacks;
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -269,10 +269,6 @@ TEST_F(BlitTest, ColorResolve) {
|
||||
SKIP_IF(Backend::WEBGPU, "test cases fail in WebGPU, see b/424157731");
|
||||
auto& api = getDriverApi();
|
||||
|
||||
if (api.getFeatureLevel() < FeatureLevel::FEATURE_LEVEL_2) {
|
||||
GTEST_SKIP() << "Skipping test because multi-sampled textures are not supported at Feature level < 2";
|
||||
}
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
constexpr int kDstTexWidth = 256;
|
||||
|
||||
@@ -25,6 +25,8 @@ using namespace filament::backend;
|
||||
namespace test {
|
||||
|
||||
TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
|
||||
SKIP_IF(Backend::VULKAN, "Frame callbacks are unsupported in Vulkan, see b/417254479");
|
||||
SKIP_IF(Backend::WEBGPU, "Frame callbacks are unsupported in WebGPU");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
@@ -66,18 +68,7 @@ TEST_F(BackendTest, FrameScheduledCallback) {
|
||||
callbackCountB++;
|
||||
}, 0);
|
||||
|
||||
// Render another frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
api.endRenderPass(0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Now, unset the callback
|
||||
api.setFrameScheduledCallback(swapChain, nullptr, {}, 0);
|
||||
|
||||
// Render a final frame. This time no callback should be called.
|
||||
// Render one final frame.
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.beginRenderPass(renderTarget, {});
|
||||
|
||||
@@ -392,6 +392,10 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
||||
}
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Crashing when reading pixels without a redundant call to makeCurrent right before the"
|
||||
"render pass. b/422798473");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
@@ -476,6 +480,10 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
||||
}
|
||||
|
||||
TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||
"Crashing when reading pixels without a redundant call to makeCurrent right before the"
|
||||
"render pass. b/422798473");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
api.startCapture();
|
||||
@@ -511,6 +519,8 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, kTexSize);
|
||||
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, kTexSize, kTexSize, 1, std::move(descriptor));
|
||||
|
||||
api.beginFrame(0, 0, 0);
|
||||
|
||||
// Update samplers.
|
||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||
api.updateDescriptorSetTexture(descriptorSet, 0, texture, {
|
||||
@@ -534,12 +544,13 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
||||
api.bindRenderPrimitive(mTriangle.getRenderPrimitive());
|
||||
api.draw2(0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(kTexSize, kTexSize, "UpdateImageMipLevel", 1875922935));
|
||||
}
|
||||
|
||||
EXPECT_IMAGE(defaultRenderTarget,
|
||||
ScreenshotParams(kTexSize, kTexSize, "UpdateImageMipLevel", 1875922935));
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
api.stopCapture();
|
||||
}
|
||||
|
||||
@@ -1,388 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "ImageExpectations.h"
|
||||
#include "Lifetimes.h"
|
||||
#include "Shader.h"
|
||||
#include "SharedShaders.h"
|
||||
#include "SharedShadersConstants.h"
|
||||
#include "Skip.h"
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/PipelineState.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace test {
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
|
||||
class MemoryMappedTest : public BackendTest {
|
||||
protected:
|
||||
RenderTargetHandle makeRenderTarget(Cleanup& cleanup) {
|
||||
auto& api = getDriverApi();
|
||||
auto colorTexture = cleanup.add(
|
||||
api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
|
||||
screenWidth(), screenHeight(), 1,
|
||||
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
||||
auto renderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR0,
|
||||
screenWidth(), screenHeight(), 1, 1, { { colorTexture } }, {}, {}));
|
||||
return renderTarget;
|
||||
}
|
||||
|
||||
std::tuple<VertexBufferInfoHandle, VertexBufferHandle, IndexBufferHandle>
|
||||
setupGeometryBuffer(Cleanup& cleanup,
|
||||
uint32_t const count, uint8_t const stride, uint32_t const baseVertex,
|
||||
BufferObjectHandle buffer) {
|
||||
auto& api = getDriverApi();
|
||||
AttributeArray attributes = {};
|
||||
attributes[0] = { .offset = 0, .stride = stride, .buffer = 0, .type = ElementType::FLOAT2 };
|
||||
auto vbih = cleanup.add(api.createVertexBufferInfo(1, 1, attributes));
|
||||
// The vertex buffer must be large enough to hold the "garbage" vertices created by the offset.
|
||||
auto vbh = cleanup.add(api.createVertexBuffer(count + baseVertex, vbih));
|
||||
api.setVertexBufferObject(vbh, 0, buffer);
|
||||
|
||||
auto* const indices = new(std::nothrow)uint16_t[count];
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
indices[i] = i + baseVertex;
|
||||
}
|
||||
|
||||
auto ibh = cleanup.add(
|
||||
api.createIndexBuffer(ElementType::USHORT, count, BufferUsage::STATIC));
|
||||
|
||||
api.updateIndexBuffer(ibh, {
|
||||
indices, count * sizeof(uint16_t),
|
||||
[](void* b, size_t, void*) {
|
||||
delete [] static_cast<uint16_t*>(b);
|
||||
}, indices }, 0);
|
||||
|
||||
return { vbih, vbh, ibh };
|
||||
}
|
||||
|
||||
Shader getSimpleShader(Cleanup& cleanup, math::float4 const& color) {
|
||||
auto& api = getDriverApi();
|
||||
Shader const shader = SharedShaders::makeShader(getDriverApi(), cleanup, ShaderRequest{
|
||||
.mVertexType = VertexShaderType::Simple,
|
||||
.mFragmentType = FragmentShaderType::SolidColored,
|
||||
.mUniformType = ShaderUniformType::Simple
|
||||
});
|
||||
|
||||
auto const ubuffer = cleanup.add(api.createBufferObject(sizeof(SimpleMaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC_BIT));
|
||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer);
|
||||
shader.uploadUniform(api, ubuffer, SimpleMaterialParams{ .color = color });
|
||||
return shader;
|
||||
}
|
||||
|
||||
void copyData(const void* data, size_t const size, size_t const offset,
|
||||
MemoryMappedBufferHandle const& mmb, int& callbacksExecuted) {
|
||||
auto& api = getDriverApi();
|
||||
void* p = malloc(size);
|
||||
memcpy(p, data, size);
|
||||
api.copyToMemoryMappedBuffer(mmb, offset, {
|
||||
p, size, [](void* buffer, size_t, void* user) {
|
||||
free(buffer);
|
||||
(*static_cast<int*>(user))++;
|
||||
}, &callbacksExecuted
|
||||
});
|
||||
}
|
||||
|
||||
void render(uint32_t count,
|
||||
int64_t const frame,
|
||||
SwapChainHandle swapChain,
|
||||
RenderTargetHandle renderTarget,
|
||||
RenderPrimitiveHandle renderPrimitive, PipelineState const& state) {
|
||||
auto& api = getDriverApi();
|
||||
RenderPassParams params = getClearColorRenderPass({0,0,0,1});
|
||||
params.viewport = getFullViewport();
|
||||
|
||||
api.beginFrame(frame, 0, 0);
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.bindPipeline(state);
|
||||
api.bindRenderPrimitive(renderPrimitive);
|
||||
api.draw2(0, count, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
void runOffsetRenderTest(size_t const mapOffset,
|
||||
size_t const copyOffset,
|
||||
const math::float4& color,
|
||||
const char* screenshotName) {
|
||||
|
||||
SKIP_IF(Backend::WEBGPU, "HwMemoryMappedBuffer APIs not yet implemented");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
auto const swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
auto const renderTarget = makeRenderTarget(cleanup);
|
||||
|
||||
constexpr std::array<math::float2, 3> vertices = {{
|
||||
{-0.25f, -0.25f},
|
||||
{ 0.25f, -0.25f},
|
||||
{-0.25f, 0.25f},
|
||||
}};
|
||||
constexpr size_t vertexDataSize = vertices.size() * sizeof(math::float2);
|
||||
constexpr size_t stride = sizeof(math::float2);
|
||||
|
||||
// The total offset is the sum of the map and copy offsets.
|
||||
const size_t totalOffset = mapOffset + copyOffset;
|
||||
|
||||
// Calculate how many vertices we need to skip to account for the total offset.
|
||||
const uint32_t baseVertex = totalOffset / stride;
|
||||
|
||||
// Create a buffer large enough to hold the offset and the vertex data.
|
||||
auto const bufferObject = cleanup.add(api.createBufferObject(vertexDataSize + totalOffset,
|
||||
BufferObjectBinding::VERTEX, BufferUsage::SHARED_WRITE_BIT));
|
||||
|
||||
int callbackExecuted = 0;
|
||||
// Map the buffer with a specific offset.
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer =
|
||||
api.mapBuffer(bufferObject, mapOffset, vertexDataSize + copyOffset,
|
||||
MapBufferAccessFlags::WRITE_BIT, utils::CString{ screenshotName });
|
||||
|
||||
copyData(vertices.data(), vertexDataSize, copyOffset, memoryMappedBuffer, callbackExecuted);
|
||||
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
|
||||
flushAndWait();
|
||||
EXPECT_EQ(callbackExecuted, 1);
|
||||
|
||||
Shader const shader = getSimpleShader(cleanup, color);
|
||||
|
||||
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 3, stride, baseVertex, bufferObject);
|
||||
|
||||
auto const renderPrimitive = cleanup.add(api.createRenderPrimitive(vbh, ibh, PrimitiveType::TRIANGLES));
|
||||
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(state);
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = vbih;
|
||||
|
||||
render(3, 0, swapChain, renderTarget, renderPrimitive, state);
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), screenshotName, 0));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MemoryMappedTest, MapCopyUnmap) {
|
||||
SKIP_IF(Backend::WEBGPU, "HwMemoryMappedBuffer APIs not yet implemented");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
// Create a buffer object.
|
||||
BufferObjectHandle const bufferObject = cleanup.add(api.createBufferObject(1024,
|
||||
BufferObjectBinding::VERTEX, BufferUsage::DYNAMIC_BIT | BufferUsage::SHARED_WRITE_BIT));
|
||||
|
||||
// Map the buffer.
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer = api.mapBuffer(bufferObject, 0, 1024,
|
||||
MapBufferAccessFlags::WRITE_BIT, "MemoryMappedBuffer");
|
||||
|
||||
if (memoryMappedBuffer) {
|
||||
// Create some data to copy.
|
||||
constexpr size_t dataSize = 256;
|
||||
auto* const data = new uint8_t[dataSize];
|
||||
for (size_t i = 0; i < dataSize; ++i) {
|
||||
data[i] = uint8_t(i);
|
||||
}
|
||||
|
||||
// Copy the data into the mapped buffer.
|
||||
BufferDescriptor bufferDescriptor(data, dataSize, [](void* buffer, size_t, void*) {
|
||||
delete[] static_cast<uint8_t*>(buffer);
|
||||
});
|
||||
api.copyToMemoryMappedBuffer(memoryMappedBuffer, 0, std::move(bufferDescriptor));
|
||||
|
||||
// Unmap the buffer.
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, WriteAndRender) {
|
||||
runOffsetRenderTest(0, 0, {1, 0, 0, 1}, "WriteAndRender");
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, MapWithOffset) {
|
||||
runOffsetRenderTest(16, 0, {0, 1, 0, 1}, "MapWithOffset");
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, CopyWithOffset) {
|
||||
runOffsetRenderTest(0, 16, {0, 0, 1, 1}, "CopyWithOffset");
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, MapAndCopyWithOffsets) {
|
||||
runOffsetRenderTest(16, 32, {1, 0, 1, 1}, "MapAndCopyWithOffsets");
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, MultipleCopies) {
|
||||
SKIP_IF(Backend::WEBGPU, "HwMemoryMappedBuffer APIs not yet implemented");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
auto const swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
auto const renderTarget = makeRenderTarget(cleanup);
|
||||
|
||||
constexpr std::array<math::float2, 3> triangle1 = {{{-0.5f, 0.5f}, {-0.8f, 0.2f}, {-0.2f, 0.2f}}};
|
||||
constexpr std::array<math::float2, 3> triangle2 = {{{0.5f, 0.5f}, {0.2f, 0.2f}, {0.8f, 0.2f}}};
|
||||
constexpr std::array<math::float2, 3> triangle3 = {{{-0.5f, -0.5f}, {-0.8f, -0.8f}, {-0.2f, -0.8f}}};
|
||||
constexpr size_t singleTriangleSize = triangle1.size() * sizeof(math::float2);
|
||||
constexpr size_t totalDataSize = singleTriangleSize * 3;
|
||||
|
||||
auto const bufferObject = cleanup.add(api.createBufferObject(totalDataSize,
|
||||
BufferObjectBinding::VERTEX, BufferUsage::SHARED_WRITE_BIT));
|
||||
|
||||
int callbacksExecuted = 0;
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer = api.mapBuffer(bufferObject, 0, totalDataSize,
|
||||
MapBufferAccessFlags::WRITE_BIT, "MultipleCopies");
|
||||
|
||||
copyData(triangle1.data(), singleTriangleSize, 0,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
copyData(triangle2.data(), singleTriangleSize, singleTriangleSize,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
copyData(triangle3.data(), singleTriangleSize, singleTriangleSize * 2,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
|
||||
flushAndWait();
|
||||
EXPECT_EQ(callbacksExecuted, 3);
|
||||
|
||||
Shader const shader = getSimpleShader(cleanup, { 1, 1, 0, 1 });
|
||||
|
||||
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 9, sizeof(math::float2), 0, bufferObject);
|
||||
|
||||
auto const renderPrimitive = cleanup.add(api.createRenderPrimitive(vbh, ibh, PrimitiveType::TRIANGLES));
|
||||
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(state);
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = vbih;
|
||||
|
||||
render(9, 0, swapChain, renderTarget, renderPrimitive, state);
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "MultipleCopies", 0));
|
||||
}
|
||||
|
||||
TEST_F(MemoryMappedTest, UpdatePartial) {
|
||||
SKIP_IF(Backend::WEBGPU, "HwMemoryMappedBuffer APIs not yet implemented");
|
||||
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(api);
|
||||
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
auto renderTarget = makeRenderTarget(cleanup);
|
||||
|
||||
constexpr std::array<math::float2, 3> triangle1 = {{{-0.5f, 0.5f}, {-0.8f, 0.2f}, {-0.2f, 0.2f}}};
|
||||
constexpr std::array<math::float2, 3> triangle2 = {{{0.5f, 0.5f}, {0.2f, 0.2f}, {0.8f, 0.2f}}};
|
||||
constexpr std::array<math::float2, 3> triangle3 = {{{-0.5f, -0.5f}, {-0.8f, -0.8f}, {-0.2f, -0.8f}}};
|
||||
constexpr size_t singleTriangleSize = triangle1.size() * sizeof(math::float2);
|
||||
constexpr size_t totalDataSize = singleTriangleSize * 3;
|
||||
|
||||
auto bufferObject = cleanup.add(api.createBufferObject(totalDataSize,
|
||||
BufferObjectBinding::VERTEX, BufferUsage::SHARED_WRITE_BIT));
|
||||
|
||||
int callbacksExecuted = 0;
|
||||
|
||||
// Initial data upload
|
||||
{
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer = api.mapBuffer(bufferObject, 0, totalDataSize,
|
||||
MapBufferAccessFlags::WRITE_BIT, "UpdatePartial_initial");
|
||||
|
||||
copyData(triangle1.data(), singleTriangleSize, 0,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
copyData(triangle2.data(), singleTriangleSize, singleTriangleSize,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
copyData(triangle3.data(), singleTriangleSize, singleTriangleSize * 2,
|
||||
memoryMappedBuffer, callbacksExecuted);
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
|
||||
flushAndWait();
|
||||
EXPECT_EQ(callbacksExecuted, 3);
|
||||
}
|
||||
|
||||
|
||||
Shader const shader = getSimpleShader(cleanup, { 1, 1, 0, 1 });
|
||||
|
||||
auto [vbih, vbh, ibh] = setupGeometryBuffer(cleanup, 9, sizeof(math::float2), 0, bufferObject);
|
||||
|
||||
auto renderPrimitive = cleanup.add(api.createRenderPrimitive(vbh, ibh, PrimitiveType::TRIANGLES));
|
||||
|
||||
PipelineState state = getColorWritePipelineState();
|
||||
shader.addProgramToPipelineState(state);
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||
state.vertexBufferInfo = vbih;
|
||||
|
||||
render(9, 0, swapChain, renderTarget, renderPrimitive, state);
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "UpdatePartial_before", 0));
|
||||
|
||||
// Now, update the middle triangle
|
||||
constexpr std::array<math::float2, 3> triangle2_updated = {{{0.5f, -0.5f}, {0.2f, -0.8f}, {0.8f, -0.8f}}};
|
||||
callbacksExecuted = 0;
|
||||
{
|
||||
MemoryMappedBufferHandle const memoryMappedBuffer =
|
||||
api.mapBuffer(bufferObject, singleTriangleSize, singleTriangleSize,
|
||||
MapBufferAccessFlags::WRITE_BIT, "UpdatePartial_update");
|
||||
|
||||
copyData(triangle2_updated.data(), singleTriangleSize, 0, memoryMappedBuffer, callbacksExecuted);
|
||||
api.unmapBuffer(memoryMappedBuffer);
|
||||
|
||||
flushAndWait();
|
||||
EXPECT_EQ(callbacksExecuted, 1);
|
||||
}
|
||||
|
||||
// need this because render() above calls commit()
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Second render, after update
|
||||
render(9, 1, swapChain, renderTarget, renderPrimitive, state);
|
||||
|
||||
EXPECT_IMAGE(renderTarget, ScreenshotParams(screenWidth(), screenHeight(), "UpdatePartial_after", 0));
|
||||
}
|
||||
|
||||
} // namespace test
|
||||