Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edb77fe6a9 | ||
|
|
954d1021c7 | ||
|
|
bf598071a3 | ||
|
|
5cbc2321c7 | ||
|
|
6bee5fb56f | ||
|
|
eb0fcc065b | ||
|
|
117535364b | ||
|
|
b15ae15f39 | ||
|
|
74445904f2 | ||
|
|
678ad4ff98 | ||
|
|
b6d7ed2b35 | ||
|
|
725e33763a | ||
|
|
f24146c169 | ||
|
|
67288d483f | ||
|
|
c80c504a2a | ||
|
|
1f8cacdd70 | ||
|
|
a3ae9431f1 | ||
|
|
3d0653604f | ||
|
|
42a0a41be0 | ||
|
|
468d9cccb7 | ||
|
|
6cfd058e3d | ||
|
|
8234549cb9 | ||
|
|
c84d04254e | ||
|
|
573a51c430 | ||
|
|
b4d26bd32b | ||
|
|
5227ea36ce | ||
|
|
6f29ff988c | ||
|
|
3424222d6e | ||
|
|
a9ab9b6cd4 | ||
|
|
5d11dd653a | ||
|
|
7d1cb3ee85 | ||
|
|
8304cd9c32 | ||
|
|
6d1c337363 | ||
|
|
e59036c731 | ||
|
|
dd7d78b2a6 | ||
|
|
f096c4d8de | ||
|
|
da5ea821a8 | ||
|
|
1b944a6582 | ||
|
|
318a9002e7 | ||
|
|
eb5d9ac15d | ||
|
|
5cae0ee082 | ||
|
|
75d641ae8a | ||
|
|
c2c744b06d | ||
|
|
1e246d1332 | ||
|
|
7d242341f2 | ||
|
|
e625c7024c | ||
|
|
192a61a06b | ||
|
|
6a93e3a765 | ||
|
|
e1fb1391f9 |
@@ -54,7 +54,6 @@ jobs:
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
@@ -73,7 +72,6 @@ jobs:
|
||||
build-ios:
|
||||
name: build-iOS
|
||||
runs-on: macos-14-xlarge
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
@@ -89,7 +87,6 @@ jobs:
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
@@ -107,11 +104,12 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- name: Check for manual edits to /docs
|
||||
run: |
|
||||
echo "${{ github.event.pull_request.head.sha }} -- ${{ github.event.pull_request.head.sha }}"
|
||||
# disable for now
|
||||
# bash docs_src/build/presubmit_check.sh ${{ github.event.pull_request.head.sha }}
|
||||
COMMIT_ID=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
|
||||
bash docs_src/build/presubmit_check.sh ${COMMIT_ID}
|
||||
|
||||
test-renderdiff:
|
||||
name: test-renderdiff
|
||||
@@ -138,7 +136,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: presubmit-renderdiff-result
|
||||
path: ./out/renderdiff_tests
|
||||
path: ./out/renderdiff
|
||||
|
||||
validate-wgsl-webgpu:
|
||||
name: validate-wgsl-webgpu
|
||||
|
||||
@@ -6,4 +6,4 @@ We are chaning the way Vulkan buffers are handled. We need to switch over to a m
|
||||
**If you are cherry-picking a commit into an rc/ branch**: add the release note under the
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
## Release notes for next branch cut
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.61.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.62.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.61.1'
|
||||
pod 'Filament', '~> 1.62.0'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,6 +7,15 @@ 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.62.1
|
||||
|
||||
- Add new shader define `VARIANT_DEPTH`, defined when a material is compiled for depth variants
|
||||
(e.g., shadows) [**Requires recompiling materials**]
|
||||
|
||||
## v1.62.0
|
||||
|
||||
- Add new `unfilterable` field to Filament Material's `sampler` [⚠️ **New Material Version**]
|
||||
|
||||
## v1.61.2
|
||||
|
||||
- samples: samples now have a CLI to select backend api
|
||||
|
||||
@@ -556,7 +556,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge,
|
||||
jboolean disableHandleUseAfterFreeCheck,
|
||||
jint preferredShaderLanguage,
|
||||
jboolean forceGLES2Context, jboolean assertNativeWindowIsValid) {
|
||||
jboolean forceGLES2Context, jboolean assertNativeWindowIsValid,
|
||||
jint gpuContextPriority) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
Engine::Config config = {
|
||||
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
|
||||
@@ -574,6 +575,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
.preferredShaderLanguage = (Engine::Config::ShaderLanguage) preferredShaderLanguage,
|
||||
.forceGLES2Context = (bool) forceGLES2Context,
|
||||
.assertNativeWindowIsValid = (bool) assertNativeWindowIsValid,
|
||||
.gpuContextPriority = (Engine::GpuContextPriority) gpuContextPriority,
|
||||
};
|
||||
builder->config(&config);
|
||||
}
|
||||
|
||||
@@ -175,6 +175,36 @@ public class Engine {
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
/**
|
||||
* This controls the priority level for GPU work scheduling, which helps prioritize the
|
||||
* submitted GPU work and enables preemption.
|
||||
*/
|
||||
public enum GpuContextPriority {
|
||||
/**
|
||||
* Backend default GPU context priority (typically MEDIUM)
|
||||
*/
|
||||
DEFAULT,
|
||||
/**
|
||||
* For non-interactive, deferrable workloads. This should not interfere with standard
|
||||
* applications.
|
||||
*/
|
||||
LOW,
|
||||
/**
|
||||
* The default priority level for standard applications.
|
||||
*/
|
||||
MEDIUM,
|
||||
/**
|
||||
* For high-priority, latency-sensitive workloads that are more important than standard
|
||||
* applications.
|
||||
*/
|
||||
HIGH,
|
||||
/**
|
||||
* The highest priority, intended for system-critical, real-time applications where missing
|
||||
* deadlines is unacceptable (e.g., VR/AR compositors or other system-critical tasks).
|
||||
*/
|
||||
REALTIME,
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs <code>Engine</code> objects using a builder pattern.
|
||||
*/
|
||||
@@ -233,7 +263,8 @@ public class Engine {
|
||||
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge,
|
||||
config.disableHandleUseAfterFreeCheck,
|
||||
config.preferredShaderLanguage.ordinal(),
|
||||
config.forceGLES2Context, config.assertNativeWindowIsValid);
|
||||
config.forceGLES2Context, config.assertNativeWindowIsValid,
|
||||
config.gpuContextPriority.ordinal());
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -489,6 +520,11 @@ public class Engine {
|
||||
* @Deprecated use "backend.opengl.assert_native_window_is_valid" feature flag instead
|
||||
*/
|
||||
public boolean assertNativeWindowIsValid = false;
|
||||
|
||||
/**
|
||||
* GPU context priority level. Controls GPU work scheduling and preemption.
|
||||
*/
|
||||
public GpuContextPriority gpuContextPriority = GpuContextPriority.DEFAULT;
|
||||
}
|
||||
|
||||
private Engine(long nativeEngine, Config config) {
|
||||
@@ -1492,7 +1528,8 @@ public class Engine {
|
||||
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge,
|
||||
boolean disableHandleUseAfterFreeCheck,
|
||||
int preferredShaderLanguage,
|
||||
boolean forceGLES2Context, boolean assertNativeWindowIsValid);
|
||||
boolean forceGLES2Context, boolean assertNativeWindowIsValid,
|
||||
int gpuContextPriority);
|
||||
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
|
||||
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
|
||||
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.61.1
|
||||
VERSION_NAME=1.62.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
BIN
Binary file not shown.
+2
-1
@@ -1,6 +1,7 @@
|
||||
#Wed Nov 17 10:40:18 PST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
Vendored
+179
-115
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -17,67 +17,99 @@
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
@@ -87,9 +119,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
@@ -98,88 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
||||
Vendored
+19
-16
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
Binary file not shown.
+19
-1
@@ -18,6 +18,7 @@ package com.google.android.filament.gltf
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
@@ -50,6 +51,7 @@ class MainActivity : Activity() {
|
||||
// Load the library for the utility layer, which in turn loads gltfio and the Filament core.
|
||||
init { Utils.init() }
|
||||
private const val TAG = "gltf-viewer"
|
||||
private const val STATIC_MODEL_TAG = "use-static-model"
|
||||
}
|
||||
|
||||
private lateinit var surfaceView: SurfaceView
|
||||
@@ -69,6 +71,7 @@ class MainActivity : Activity() {
|
||||
private var loadStartTime = 0L
|
||||
private var loadStartFence: Fence? = null
|
||||
private val viewerContent = AutomationEngine.ViewerContent()
|
||||
private var useStaticModel = false
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -83,6 +86,12 @@ class MainActivity : Activity() {
|
||||
doubleTapDetector = GestureDetector(applicationContext, doubleTapListener)
|
||||
singleTapDetector = GestureDetector(applicationContext, singleTapListener)
|
||||
|
||||
val intent: Intent = intent
|
||||
val bundle: Bundle? = intent.extras
|
||||
bundle?.let {
|
||||
useStaticModel = it.getBoolean(STATIC_MODEL_TAG, false)
|
||||
}
|
||||
|
||||
modelViewer = ModelViewer(surfaceView)
|
||||
viewerContent.view = modelViewer.view
|
||||
viewerContent.sunlight = modelViewer.light
|
||||
@@ -141,7 +150,16 @@ class MainActivity : Activity() {
|
||||
}
|
||||
|
||||
private fun createDefaultRenderables() {
|
||||
val buffer = assets.open("models/scene.gltf").use { input ->
|
||||
// Sometimes it's useful to set to the default model to something static. You can enable
|
||||
// the static model by launching the app from adb, as in
|
||||
// `adb shell am start -n com.google.android.filament.gltf/.MainActivity --ez "use-static-model" true`
|
||||
val modelPath = if (useStaticModel) {
|
||||
"models/helmet.glb"
|
||||
} else {
|
||||
"models/scene.gltf"
|
||||
}
|
||||
|
||||
val buffer = assets.open(modelPath).use { input ->
|
||||
val bytes = ByteArray(input.available())
|
||||
input.read(bytes)
|
||||
ByteBuffer.wrap(bytes)
|
||||
|
||||
@@ -1027,8 +1027,11 @@ samplerCubemap | Cubemap texture
|
||||
[Table [materialParamsTypes]: Material parameter types]
|
||||
|
||||
Samplers
|
||||
: Sampler types can also specify a `format` which can be either `int` or `float` (defaults to
|
||||
`float`).
|
||||
: Sampler types can have the following fields:
|
||||
- `format` : which can be either `int` or `float` (defaults to `float`).
|
||||
- `multisample` : a boolean to indicate whether the sampler is meant for multisampling (defaults to `false`)
|
||||
- `unfilterable` : a boolean to indicate whether the sampling is not filtered (defaults to `false`)
|
||||
|
||||
|
||||
Arrays
|
||||
: A parameter can define an array of values by appending `[size]` after the type name, where
|
||||
|
||||
@@ -30,11 +30,6 @@ from utils import execute, ArgParseImpl
|
||||
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT_DIR = os.path.join(CUR_DIR, '../../')
|
||||
|
||||
def get_last_commit_hash():
|
||||
res, ret = execute('git rev-parse HEAD', cwd=ROOT_DIR)
|
||||
assert res == 0, 'Failed to get the last commit hash'
|
||||
return ret.strip()
|
||||
|
||||
def get_edited_files(commit_hash):
|
||||
INSERT = '#####?????'
|
||||
res, ret = execute(f'git show --name-only --pretty=%b{INSERT} {commit_hash}')
|
||||
|
||||
@@ -39,13 +39,13 @@ function download_mdbook() {
|
||||
fi
|
||||
|
||||
local CHECK_UNAME="
|
||||
import sys;
|
||||
parts=[a.lower() for a in sys.stdin.read().strip().split(' ')];
|
||||
import sys
|
||||
parts=[a.lower() for a in sys.stdin.read().strip().split(' ')]
|
||||
def get_type():
|
||||
if 'darwin' in parts:
|
||||
if 'x86_64' in parts:
|
||||
return ${DARWIN_X86}
|
||||
elif 'aarch' in parts:
|
||||
elif 'aarch' in parts or 'arm64' in parts:
|
||||
return ${DARWIN_ARM}
|
||||
elif 'linux' in parts:
|
||||
if 'x86_64' in parts:
|
||||
@@ -63,11 +63,12 @@ print(get_type())
|
||||
echo "*** Need to install Rust ***"
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
fi
|
||||
source "${HOME}/.cargo/env"
|
||||
if ! (command -v cargo >/dev/null 2>&1); then
|
||||
echo "*** Still cannot find `cargo` ***"
|
||||
echo "*** Still cannot find cargo ***"
|
||||
exit 1
|
||||
fi
|
||||
cargo install mdbook
|
||||
cargo install --force mdbook
|
||||
else
|
||||
# Download prebuilts from github
|
||||
mkdir -p ${HOME}/.cargo/bin
|
||||
|
||||
@@ -13,7 +13,7 @@ This document is part of the [Filament project](https://github.com/google/filame
|
||||
## Authors
|
||||
|
||||
- [Romain Guy](https://github.com/romainguy), [@romainguy](https://twitter.com/romainguy)
|
||||
- [Mathias Agopian](https://github.com/pixelflinger), [@darthmoosious](https://twitter.com/darthmoosious)
|
||||
- [Mathias Agopian](https://github.com/pixelflinger), [@pixelflinger](https://bsky.app/profile/pixelflinger.bsky.social)
|
||||
|
||||
# Overview
|
||||
|
||||
@@ -78,26 +78,27 @@ in table [standardProperties].
|
||||
Property | Definition
|
||||
-----------------------:|:---------------------
|
||||
**baseColor** | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces
|
||||
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
|
||||
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
|
||||
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
|
||||
**reflectance** | Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections
|
||||
**sheenColor** | Strength of the sheen layer
|
||||
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer
|
||||
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
|
||||
**clearCoat** | Strength of the clear coat layer
|
||||
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
|
||||
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
|
||||
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
|
||||
**anisotropyDirection** | Local surface direction in tangent space
|
||||
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
|
||||
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
|
||||
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
|
||||
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
|
||||
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
|
||||
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
|
||||
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
|
||||
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
|
||||
**absorption** | Absorption factor for refractive objects
|
||||
**microThickness** | Thickness of the thin layer of refractive objects
|
||||
**thickness** | Thickness of the solid volume of refractive objects
|
||||
**sheenColor** | Strength of the sheen layer
|
||||
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer
|
||||
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
|
||||
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
|
||||
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
|
||||
**absorption** | Absorption factor for refractive objects
|
||||
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
|
||||
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
|
||||
**microThickness** | Thickness of the thin layer of refractive objects
|
||||
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
|
||||
**shadowStrength** | Strength factor between 0 and 1 for all shadows received by this material
|
||||
[Table [standardProperties]: Properties of the standard model]
|
||||
|
||||
The type and range of each property is described in table [standardPropertiesTypes].
|
||||
@@ -991,10 +992,13 @@ Type
|
||||
|
||||
Value
|
||||
: Each entry is an object with the properties `name` and `type`, both of `string` type. The
|
||||
name must be a valid GLSL identifier. Entries also have an optional `precision`, which can be
|
||||
name must be a valid GLSL identifier. Entries have an optional `precision`, which can be
|
||||
one of `default` (best precision for the platform, typically `high` on desktop, `medium` on
|
||||
mobile), `low`, `medium`, `high`. The type must be one of the types described in
|
||||
table [materialParamsTypes].
|
||||
table [materialParamsTypes]. For Android external textures, entries also have an optional
|
||||
transformName parameter to specify the name of the material parameter that will be
|
||||
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
|
||||
this will always be identity.
|
||||
|
||||
Type | Description
|
||||
:----------------------|:---------------------------------
|
||||
@@ -1023,12 +1027,8 @@ samplerCubemap | Cubemap texture
|
||||
[Table [materialParamsTypes]: Material parameter types]
|
||||
|
||||
Samplers
|
||||
: Sampler types can specify additional options:
|
||||
|
||||
- `format`: either `int` or `float` (defaults to `float`).
|
||||
- `stages`: array of strings containing the list of shader stages this
|
||||
sampler can be accessed from. Each entry must be either `vertex` or
|
||||
`fragment` (defaults to both).
|
||||
: Sampler types can also specify a `format` which can be either `int` or `float` (defaults to
|
||||
`float`).
|
||||
|
||||
Arrays
|
||||
: A parameter can define an array of values by appending `[size]` after the type name, where
|
||||
@@ -1273,6 +1273,9 @@ Description
|
||||
when selecting any shading model that is not `unlit`. See the shader sections of this document
|
||||
for more information on how to access these attributes from the shaders.
|
||||
|
||||
!!! Note: Interaction with custom variables
|
||||
When the `color` attribute is specified, only four custom variables are available instead of five.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
parameters : [
|
||||
@@ -1303,7 +1306,7 @@ Type
|
||||
: array of `string`
|
||||
|
||||
Value
|
||||
: Up to 4 strings, each must be a valid GLSL identifier.
|
||||
: Up to 5 strings, each must be a valid GLSL identifier.
|
||||
|
||||
Description
|
||||
: Defines custom interpolants (or variables) that are output by the material's vertex shader.
|
||||
@@ -1319,6 +1322,10 @@ Description
|
||||
particular if `default` is specified the default precision is used is the fragment shader
|
||||
(`mediump`) and in the vertex shader (`highp`).
|
||||
|
||||
!!! Warning: Interaction with required attributes
|
||||
If the `color` attribute is specified in the `required` list, then only four variables can be used
|
||||
instead of five.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
name : Skybox,
|
||||
|
||||
@@ -146,7 +146,7 @@ This document is part of the <a href="https://github.com/google/filament">Filame
|
||||
<ul>
|
||||
<li class="minus"><a href="https://github.com/romainguy">Romain Guy</a>, <a href="https://twitter.com/romainguy">@romainguy</a>
|
||||
</li>
|
||||
<li class="minus"><a href="https://github.com/pixelflinger">Mathias Agopian</a>, <a href="https://twitter.com/darthmoosious">@darthmoosious</a></li></ul>
|
||||
<li class="minus"><a href="https://github.com/pixelflinger">Mathias Agopian</a>, <a href="https://bsky.app/profile/pixelflinger.bsky.social">@pixelflinger</a></li></ul>
|
||||
</p>
|
||||
<a class="target" name="overview"> </a><a class="target" name="overview"> </a><a class="target" name="toc2"> </a><h1>Overview</h1>
|
||||
<p>
|
||||
@@ -217,26 +217,27 @@ in <a href="#table_standardproperties" target="_self">table 1</a>.
|
||||
<div class='table'>
|
||||
<a class="target" name="table_standardproperties"> </a><table class="table longtable"><tr><th style="text-align:right"> Property </th><th style="text-align:left"> Definition </th></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">baseColor</strong> </td><td style="text-align:left"> Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">metallic</strong> </td><td style="text-align:left"> Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1) </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">roughness</strong> </td><td style="text-align:left"> Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">metallic</strong> </td><td style="text-align:left"> Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1) </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">reflectance</strong> </td><td style="text-align:left"> Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">sheenColor</strong> </td><td style="text-align:left"> Strength of the sheen layer </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">sheenRoughness</strong> </td><td style="text-align:left"> Perceived smoothness or roughness of the sheen layer </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">ambientOcclusion</strong> </td><td style="text-align:left"> Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0 </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">clearCoat</strong> </td><td style="text-align:left"> Strength of the clear coat layer </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">clearCoatRoughness</strong> </td><td style="text-align:left"> Perceived smoothness or roughness of the clear coat layer </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">clearCoatNormal</strong> </td><td style="text-align:left"> A detail normal used to perturb the clear coat layer using <em class="underscore">bump mapping</em> (<em class="underscore">normal mapping</em>) </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">anisotropy</strong> </td><td style="text-align:left"> Amount of anisotropy in either the tangent or bitangent direction </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">anisotropyDirection</strong> </td><td style="text-align:left"> Local surface direction in tangent space </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">ambientOcclusion</strong> </td><td style="text-align:left"> Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0 </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">normal</strong> </td><td style="text-align:left"> A detail normal used to perturb the surface using <em class="underscore">bump mapping</em> (<em class="underscore">normal mapping</em>) </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">bentNormal</strong> </td><td style="text-align:left"> A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">clearCoatNormal</strong> </td><td style="text-align:left"> A detail normal used to perturb the clear coat layer using <em class="underscore">bump mapping</em> (<em class="underscore">normal mapping</em>) </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">emissive</strong> </td><td style="text-align:left"> Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">postLightingColor</strong> </td><td style="text-align:left"> Additional color that can be blended with the result of the lighting computations. See <code>postLightingBlending</code> </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">ior</strong> </td><td style="text-align:left"> Index of refraction, either for refractive objects or as an alternative to reflectance </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">transmission</strong> </td><td style="text-align:left"> Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">absorption</strong> </td><td style="text-align:left"> Absorption factor for refractive objects </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">microThickness</strong> </td><td style="text-align:left"> Thickness of the thin layer of refractive objects </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">thickness</strong> </td><td style="text-align:left"> Thickness of the solid volume of refractive objects </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">sheenColor</strong> </td><td style="text-align:left"> Strength of the sheen layer </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">sheenRoughness</strong> </td><td style="text-align:left"> Perceived smoothness or roughness of the sheen layer </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">emissive</strong> </td><td style="text-align:left"> Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">normal</strong> </td><td style="text-align:left"> A detail normal used to perturb the surface using <em class="underscore">bump mapping</em> (<em class="underscore">normal mapping</em>) </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">postLightingColor</strong> </td><td style="text-align:left"> Additional color that can be blended with the result of the lighting computations. See <code>postLightingBlending</code> </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">absorption</strong> </td><td style="text-align:left"> Absorption factor for refractive objects </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">transmission</strong> </td><td style="text-align:left"> Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">ior</strong> </td><td style="text-align:left"> Index of refraction, either for refractive objects or as an alternative to reflectance </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">microThickness</strong> </td><td style="text-align:left"> Thickness of the thin layer of refractive objects </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">bentNormal</strong> </td><td style="text-align:left"> A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality </td></tr>
|
||||
<tr><td style="text-align:right"> <strong class="asterisk">shadowStrength</strong> </td><td style="text-align:left"> Strength factor between 0 and 1 for all shadows received by this material </td></tr>
|
||||
</table><center><div class="tablecaption"><b style="font-style:normal;">Table 1:</b> Properties of the standard model</div></center></div>
|
||||
</p><p>
|
||||
The type and range of each property is described in <a href="#table_standardpropertiestypes" target="_self">table 2</a>.
|
||||
@@ -1097,10 +1098,13 @@ non-shader data.
|
||||
|
||||
<dl><dt>Type</dt><dd><p> array of parameter objects
|
||||
</p></dd><dt>Value</dt><dd><p> Each entry is an object with the properties <code>name</code> and <code>type</code>, both of <code>string</code> type. The
|
||||
name must be a valid GLSL identifier. Entries also have an optional <code>precision</code>, which can be
|
||||
name must be a valid GLSL identifier. Entries have an optional <code>precision</code>, which can be
|
||||
one of <code>default</code> (best precision for the platform, typically <code>high</code> on desktop, <code>medium</code> on
|
||||
mobile), <code>low</code>, <code>medium</code>, <code>high</code>. The type must be one of the types described in
|
||||
<a href="#table_materialparamstypes" target="_self">table 14</a>.
|
||||
<a href="#table_materialparamstypes" target="_self">table 14</a>. For Android external textures, entries also have an optional
|
||||
transformName parameter to specify the name of the material parameter that will be
|
||||
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
|
||||
this will always be identity.
|
||||
</p></dd></dl><div class='table'>
|
||||
<a class="target" name="table_materialparamstypes"> </a><table class="table longtable"><tr><th style="text-align:left"> Type </th><th style="text-align:left"> Description </th></tr>
|
||||
<tr><td style="text-align:left"> bool </td><td style="text-align:left"> Single boolean </td></tr>
|
||||
@@ -1322,7 +1326,10 @@ non-shader data.
|
||||
required and does not need to be specified. The <code>tangents</code> attribute is automatically required
|
||||
when selecting any shading model that is not <code>unlit</code>. See the shader sections of this document
|
||||
for more information on how to access these attributes from the shaders.
|
||||
</p></dd></dl></p><pre class="listing tilde"><code><span class="line"></span>material <span class="hljs-punctuation">{</span>
|
||||
</p></dd></dl><div class="admonition note"><div class="admonition-title"> Interaction with custom variables</div>
|
||||
</p><p>
|
||||
When the <code>color</code> attribute is specified, only four custom variables are available instead of five.</div>
|
||||
</p><pre class="listing tilde"><code><span class="line"></span>material <span class="hljs-punctuation">{</span>
|
||||
<span class="line"></span> parameters <span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
|
||||
<span class="line"></span> <span class="hljs-punctuation">{</span>
|
||||
<span class="line"></span> type <span class="hljs-punctuation">:</span> sampler2d<span class="hljs-punctuation">,</span>
|
||||
@@ -1347,7 +1354,7 @@ non-shader data.
|
||||
<p>
|
||||
|
||||
<dl><dt>Type</dt><dd><p> array of <code>string</code>
|
||||
</p></dd><dt>Value</dt><dd><p> Up to 4 strings, each must be a valid GLSL identifier.
|
||||
</p></dd><dt>Value</dt><dd><p> Up to 5 strings, each must be a valid GLSL identifier.
|
||||
</p></dd><dt>Description</dt><dd><p> Defines custom interpolants (or variables) that are output by the material's vertex shader.
|
||||
Each entry of the array defines the name of an interpolant. The full name in the fragment
|
||||
shader is the name of the interpolant with the <code>variable_</code> prefix. For instance, if you
|
||||
@@ -1360,7 +1367,11 @@ non-shader data.
|
||||
In this case the specified precision is used as-is in both fragment and vertex stages, in
|
||||
particular if <code>default</code> is specified the default precision is used is the fragment shader
|
||||
(<code>mediump</code>) and in the vertex shader (<code>highp</code>).
|
||||
</p></dd></dl></p><pre class="listing tilde"><code><span class="line"></span>material <span class="hljs-punctuation">{</span>
|
||||
</p></dd></dl><div class="admonition warning"><div class="admonition-title"> Interaction with required attributes</div>
|
||||
</p><p>
|
||||
If the <code>color</code> attribute is specified in the <code>required</code> list, then only four variables can be used
|
||||
instead of five.</div>
|
||||
</p><pre class="listing tilde"><code><span class="line"></span>material <span class="hljs-punctuation">{</span>
|
||||
<span class="line"></span> name <span class="hljs-punctuation">:</span> Skybox<span class="hljs-punctuation">,</span>
|
||||
<span class="line"></span> parameters <span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>
|
||||
<span class="line"></span> <span class="hljs-punctuation">{</span>
|
||||
|
||||
@@ -269,12 +269,18 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
src/webgpu/WebGPUDescriptorSetLayout.h
|
||||
src/webgpu/WebGPUDriver.cpp
|
||||
src/webgpu/WebGPUDriver.h
|
||||
src/webgpu/WebGPUFence.cpp
|
||||
src/webgpu/WebGPUFence.h
|
||||
src/webgpu/WebGPUIndexBuffer.cpp
|
||||
src/webgpu/WebGPUIndexBuffer.h
|
||||
src/webgpu/WebGPUMsaaTextureResolver.cpp
|
||||
src/webgpu/WebGPUMsaaTextureResolver.h
|
||||
src/webgpu/WebGPUPipelineCreation.cpp
|
||||
src/webgpu/WebGPUPipelineCreation.h
|
||||
src/webgpu/WebGPUProgram.cpp
|
||||
src/webgpu/WebGPUProgram.h
|
||||
src/webgpu/WebGPURenderPassMipmapGenerator.cpp
|
||||
src/webgpu/WebGPURenderPassMipmapGenerator.h
|
||||
src/webgpu/WebGPURenderPrimitive.h
|
||||
src/webgpu/WebGPURenderTarget.cpp
|
||||
src/webgpu/WebGPURenderTarget.h
|
||||
@@ -503,11 +509,20 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Linker flags
|
||||
# ==================================================================================================
|
||||
|
||||
target_link_libraries(${TARGET} PRIVATE
|
||||
${OSMESA_LINKER_FLAGS}
|
||||
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
|
||||
)
|
||||
|
||||
if (LINUX AND FILAMENT_SUPPORTS_WEBGPU)
|
||||
# Unclear why this needs to be explicit while other linux libs do not, but it is necessary for
|
||||
# WebGPU to build on linux.
|
||||
target_link_libraries(${TARGET} PRIVATE -lxcb)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Installation
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -500,18 +500,12 @@ struct DescriptorSetLayoutBinding {
|
||||
DescriptorFlags flags = DescriptorFlags::NONE;
|
||||
uint16_t count = 0;
|
||||
|
||||
// TODO: uncomment when needed. Note that this class is used as hash key. We need to ensure
|
||||
// no uninitialized padding bytes.
|
||||
// uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
|
||||
|
||||
friend bool operator==(DescriptorSetLayoutBinding const& lhs,
|
||||
DescriptorSetLayoutBinding const& rhs) noexcept {
|
||||
return lhs.type == rhs.type &&
|
||||
lhs.flags == rhs.flags &&
|
||||
lhs.count == rhs.count &&
|
||||
lhs.stageFlags == rhs.stageFlags;
|
||||
// lhs.stageFlags == rhs.stageFlags &&
|
||||
// lhs.externalSamplerDataIndex == rhs.externalSamplerDataIndex;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1259,26 +1253,6 @@ enum class SamplerCompareFunc : uint8_t {
|
||||
N //!< Never. The depth / stencil test always fails.
|
||||
};
|
||||
|
||||
//! this API is copied from (and only applies to) the Vulkan spec.
|
||||
//! These specify YUV to RGB conversions.
|
||||
enum class SamplerYcbcrModelConversion : uint8_t {
|
||||
RGB_IDENTITY = 0,
|
||||
YCBCR_IDENTITY = 1,
|
||||
YCBCR_709 = 2,
|
||||
YCBCR_601 = 3,
|
||||
YCBCR_2020 = 4,
|
||||
};
|
||||
|
||||
enum class SamplerYcbcrRange : uint8_t {
|
||||
ITU_FULL = 0,
|
||||
ITU_NARROW = 1,
|
||||
};
|
||||
|
||||
enum class ChromaLocation : uint8_t {
|
||||
COSITED_EVEN = 0,
|
||||
MIDPOINT = 1,
|
||||
};
|
||||
|
||||
//! Sampler parameters
|
||||
struct SamplerParams { // NOLINT
|
||||
SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST)
|
||||
@@ -1347,94 +1321,9 @@ static_assert(sizeof(SamplerParams) == 4);
|
||||
static_assert(sizeof(SamplerParams) <= sizeof(uint64_t),
|
||||
"SamplerParams must be no more than 64 bits");
|
||||
|
||||
//! Sampler parameters
|
||||
struct SamplerYcbcrConversion {// NOLINT
|
||||
SamplerYcbcrModelConversion ycbcrModel : 4;
|
||||
TextureSwizzle r : 4;
|
||||
TextureSwizzle g : 4;
|
||||
TextureSwizzle b : 4;
|
||||
TextureSwizzle a : 4;
|
||||
SamplerYcbcrRange ycbcrRange : 1;
|
||||
ChromaLocation xChromaOffset : 1;
|
||||
ChromaLocation yChromaOffset : 1;
|
||||
SamplerMagFilter chromaFilter : 1;
|
||||
uint8_t padding;
|
||||
|
||||
struct Hasher {
|
||||
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
|
||||
// we don't use std::hash<> here, so we don't have to include <functional>
|
||||
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
|
||||
}
|
||||
};
|
||||
|
||||
struct EqualTo {
|
||||
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
|
||||
assert_invariant(lhs.padding == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs == *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct LessThan {
|
||||
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
|
||||
assert_invariant(lhs.padding == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs < *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
friend bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SamplerYcbcrConversion) == 4);
|
||||
|
||||
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
|
||||
"SamplerYcbcrConversion must be no more than 64 bits");
|
||||
|
||||
struct ExternalSamplerDatum {
|
||||
ExternalSamplerDatum(SamplerYcbcrConversion ycbcr, SamplerParams spm, uint32_t extFmt)
|
||||
: YcbcrConversion(ycbcr),
|
||||
samplerParams(spm),
|
||||
externalFormat(extFmt) {}
|
||||
bool operator==(ExternalSamplerDatum const& rhs) const {
|
||||
return (YcbcrConversion == rhs.YcbcrConversion && samplerParams == rhs.samplerParams &&
|
||||
externalFormat == rhs.externalFormat);
|
||||
}
|
||||
struct EqualTo {
|
||||
bool operator()(const ExternalSamplerDatum& lhs,
|
||||
const ExternalSamplerDatum& rhs) const noexcept {
|
||||
return (lhs.YcbcrConversion == rhs.YcbcrConversion &&
|
||||
lhs.samplerParams == rhs.samplerParams &&
|
||||
lhs.externalFormat == rhs.externalFormat);
|
||||
}
|
||||
};
|
||||
SamplerYcbcrConversion YcbcrConversion;
|
||||
SamplerParams samplerParams;
|
||||
uint32_t externalFormat;
|
||||
};
|
||||
// No implicit padding allowed due to it being a hash key.
|
||||
static_assert(sizeof(ExternalSamplerDatum) == 12);
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
std::variant<utils::StaticString, utils::CString, std::monostate> label;
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
|
||||
// TODO: uncomment when needed
|
||||
// utils::FixedCapacityVector<ExternalSamplerDatum> externalSamplerData;
|
||||
};
|
||||
|
||||
//! blending equation function
|
||||
|
||||
@@ -107,6 +107,36 @@ public:
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
/**
|
||||
* This controls the priority level for GPU work scheduling, which helps prioritize the
|
||||
* submitted GPU work and enables preemption.
|
||||
*/
|
||||
enum class GpuContextPriority : uint8_t {
|
||||
/**
|
||||
* Backend default GPU context priority (typically MEDIUM)
|
||||
*/
|
||||
DEFAULT,
|
||||
/**
|
||||
* For non-interactive, deferrable workloads. This should not interfere with standard
|
||||
* applications.
|
||||
*/
|
||||
LOW,
|
||||
/**
|
||||
* The default priority level for standard applications.
|
||||
*/
|
||||
MEDIUM,
|
||||
/**
|
||||
* For high-priority, latency-sensitive workloads that are more important than standard
|
||||
* applications.
|
||||
*/
|
||||
HIGH,
|
||||
/**
|
||||
* The highest priority, intended for system-critical, real-time applications where missing
|
||||
* deadlines is unacceptable (e.g., VR/AR compositors or other system-critical tasks).
|
||||
*/
|
||||
REALTIME,
|
||||
};
|
||||
|
||||
struct DriverConfig {
|
||||
/**
|
||||
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||
@@ -156,6 +186,13 @@ public:
|
||||
* - PlatformMetal
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
|
||||
/**
|
||||
* GPU context priority level. Controls GPU work scheduling and preemption.
|
||||
* This is only supported for:
|
||||
* - PlatformEGL
|
||||
*/
|
||||
GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -0,0 +1,764 @@
|
||||
/** @file DriverAPI.dox
|
||||
* @brief External documentation for the backend::Driver API.
|
||||
*
|
||||
* This file contains Doxygen documentation for the functions declared in DriverAPI.inc.
|
||||
* This is used to keep the documentation separate from the macro-heavy header file.
|
||||
*/
|
||||
|
||||
// General lifecycle and frame management
|
||||
|
||||
/** @fn Driver::tick()
|
||||
* @brief Called periodically by the system to perform maintenance tasks.
|
||||
*/
|
||||
|
||||
/** @fn Driver::beginFrame(int64_t monotonic_clock_ns, int64_t refreshIntervalNs, uint32_t frameId)
|
||||
* @brief Signals the beginning of a new frame to the driver.
|
||||
* @param monotonic_clock_ns The current monotonic clock time in nanoseconds.
|
||||
* @param refreshIntervalNs The display's refresh interval in nanoseconds.
|
||||
* @param frameId A unique identifier for the new frame.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setFrameScheduledCallback(backend::SwapChainHandle sch, backend::CallbackHandler* handler, backend::FrameScheduledCallback&& callback, uint64_t flags)
|
||||
* @brief Schedules a callback to be executed when the frame associated with the given swap chain has been scheduled for presentation.
|
||||
* @param sch The handle of the swap chain.
|
||||
* @param handler The callback handler that will execute the callback.
|
||||
* @param callback The function to be called.
|
||||
* @param flags Flags to control the callback behavior.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setFrameCompletedCallback(backend::SwapChainHandle sch, backend::CallbackHandler* handler, utils::Invocable<void(void)>&& callback)
|
||||
* @brief Sets a callback to be executed when rendering to the given swap chain is complete for the current frame.
|
||||
* @param sch The handle of the swap chain.
|
||||
* @param handler The callback handler that will execute the callback.
|
||||
* @param callback The function to be called upon frame completion.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setPresentationTime(int64_t monotonic_clock_ns)
|
||||
* @brief Informs the driver of the intended presentation time for the current frame.
|
||||
* @param monotonic_clock_ns The desired presentation time in nanoseconds, based on the monotonic clock.
|
||||
*/
|
||||
|
||||
/** @fn Driver::endFrame(uint32_t frameId)
|
||||
* @brief Signals the end of a frame to the driver.
|
||||
* @param frameId The unique identifier of the frame that is ending.
|
||||
*/
|
||||
|
||||
/** @fn Driver::flush()
|
||||
* @brief Submits all pending commands to the GPU for execution, without waiting for them to complete.
|
||||
*/
|
||||
|
||||
/** @fn Driver::finish()
|
||||
* @brief Submits all pending commands and waits for the GPU to finish executing them.
|
||||
*/
|
||||
|
||||
/** @fn Driver::resetState()
|
||||
* @brief Resets any cached or tracked driver state, forcing the driver to re-evaluate and set all states.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setDebugTag(backend::HandleBase::HandleId handleId, utils::CString tag)
|
||||
* @brief Associates a user-provided string tag with a driver handle for debugging purposes.
|
||||
* @param handleId The ID of the handle to tag.
|
||||
* @param tag The debug string to associate with the handle.
|
||||
*/
|
||||
|
||||
// Resource creation
|
||||
|
||||
/** @fn backend::VertexBufferInfoHandle Driver::createVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount, backend::AttributeArray attributes)
|
||||
* @brief Creates a VertexBufferInfo object which describes the layout of a vertex buffer.
|
||||
* @param bufferCount The number of buffer objects in the vertex buffer.
|
||||
* @param attributeCount The number of attributes in the vertex buffer.
|
||||
* @param attributes An array describing each vertex attribute.
|
||||
* @return A handle to the new VertexBufferInfo object.
|
||||
*/
|
||||
|
||||
/** @fn backend::VertexBufferHandle Driver::createVertexBuffer(uint32_t vertexCount, backend::VertexBufferInfoHandle vbih)
|
||||
* @brief Creates a vertex buffer with a specified vertex count and layout.
|
||||
* @param vertexCount The number of vertices in the buffer.
|
||||
* @param vbih A handle to a VertexBufferInfo object describing the vertex layout.
|
||||
* @return A handle to the new vertex buffer.
|
||||
*/
|
||||
|
||||
/** @fn backend::IndexBufferHandle Driver::createIndexBuffer(backend::ElementType elementType, uint32_t indexCount, backend::BufferUsage usage)
|
||||
* @brief Creates an index buffer for indexed drawing.
|
||||
* @param elementType The data type of an index (e.g., USHORT, UINT).
|
||||
* @param indexCount The number of indices in the buffer.
|
||||
* @param usage The expected usage pattern of the buffer (e.g., STATIC, DYNAMIC).
|
||||
* @return A handle to the new index buffer.
|
||||
*/
|
||||
|
||||
/** @fn backend::BufferObjectHandle Driver::createBufferObject(uint32_t byteCount, backend::BufferObjectBinding bindingType, backend::BufferUsage usage)
|
||||
* @brief Creates a generic buffer object for storing arbitrary data on the GPU.
|
||||
* @param byteCount The size of the buffer in bytes.
|
||||
* @param bindingType The purpose of the buffer object (e.g., VERTEX, UNIFORM).
|
||||
* @param usage The expected usage pattern of the buffer.
|
||||
* @return A handle to the new buffer object.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTexture(backend::SamplerType target, uint8_t levels, backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, backend::TextureUsage usage)
|
||||
* @brief Creates a new texture object.
|
||||
* @param target The type of the texture (e.g. 2D, 3D, CUBEMAP).
|
||||
* @param levels The number of mipmap levels.
|
||||
* @param format The internal format of the texture's pixels.
|
||||
* @param samples The number of samples for multisampling (1 for non-MSAA).
|
||||
* @param width The width of the texture in texels.
|
||||
* @param height The height of the texture in texels.
|
||||
* @param depth The depth of the texture in texels (for 3D or array textures).
|
||||
* @param usage A bitmask specifying the intended usage of the texture.
|
||||
* @return A handle to the new texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureView(backend::TextureHandle texture, uint8_t baseLevel, uint8_t levelCount)
|
||||
* @brief Creates a new texture view from an existing texture, allowing access to a subset of its mipmap levels.
|
||||
* @param texture The handle of the original texture.
|
||||
* @param baseLevel The first mipmap level to include in the view.
|
||||
* @param levelCount The number of mipmap levels to include in the view.
|
||||
* @return A handle to the new texture view.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureViewSwizzle(backend::TextureHandle texture, backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b, backend::TextureSwizzle a)
|
||||
* @brief Creates a new texture view with remapped color channels (swizzling).
|
||||
* @param texture The handle of the original texture.
|
||||
* @param r The swizzle operation for the red channel.
|
||||
* @param g The swizzle operation for the green channel.
|
||||
* @param b The swizzle operation for the blue channel.
|
||||
* @param a The swizzle operation for the alpha channel.
|
||||
* @return A handle to the new texture view.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureExternalImage2(backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, backend::Platform::ExternalImageHandleRef image)
|
||||
* @brief Creates a texture from a platform-specific external image handle.
|
||||
* @param target The type of the texture.
|
||||
* @param format The format of the texture's pixels.
|
||||
* @param width The width of the texture.
|
||||
* @param height The height of the texture.
|
||||
* @param usage The expected usage of the texture.
|
||||
* @param image A reference to the platform-specific external image.
|
||||
* @return A handle to the new texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureExternalImage(backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image)
|
||||
* @brief Creates a texture from a platform-specific external image pointer.
|
||||
* @param target The type of the texture.
|
||||
* @param format The format of the texture's pixels.
|
||||
* @param width The width of the texture.
|
||||
* @param height The height of the texture.
|
||||
* @param usage The expected usage of the texture.
|
||||
* @param image A pointer to the external image data.
|
||||
* @return A handle to the new texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::createTextureExternalImagePlane(backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image, uint32_t plane)
|
||||
* @brief Creates a texture from a single plane of a multi-planar external image.
|
||||
* @param format The format of the texture's pixels.
|
||||
* @param width The width of the texture.
|
||||
* @param height The height of the texture.
|
||||
* @param usage The expected usage of the texture.
|
||||
* @param image A pointer to the external image data.
|
||||
* @param plane The index of the plane to create the texture from.
|
||||
* @return A handle to the new texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::TextureHandle Driver::importTexture(intptr_t id, backend::SamplerType target, uint8_t levels, backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, backend::TextureUsage usage)
|
||||
* @brief Imports an existing backend-native texture into Filament.
|
||||
* @param id The backend-specific texture ID or handle.
|
||||
* @param target The type of the texture.
|
||||
* @param levels The number of mipmap levels.
|
||||
* @param format The format of the texture's pixels.
|
||||
* @param samples The number of samples for multisampling.
|
||||
* @param width The width of the texture.
|
||||
* @param height The height of the texture.
|
||||
* @param depth The depth of the texture.
|
||||
* @param usage The expected usage of the texture.
|
||||
* @return A handle to the imported texture.
|
||||
*/
|
||||
|
||||
/** @fn backend::RenderPrimitiveHandle Driver::createRenderPrimitive(backend::VertexBufferHandle vbh, backend::IndexBufferHandle ibh, backend::PrimitiveType pt)
|
||||
* @brief Creates a render primitive, which represents a piece of geometry to be rendered.
|
||||
* @param vbh A handle to a vertex buffer.
|
||||
* @param ibh A handle to an index buffer.
|
||||
* @param pt The type of primitive to render (e.g., TRIANGLES, LINES).
|
||||
* @return A handle to the new render primitive.
|
||||
*/
|
||||
|
||||
/** @fn backend::ProgramHandle Driver::createProgram(backend::Program&& program)
|
||||
* @brief Creates a shader program from a backend-specific program object.
|
||||
* @param program A Program object containing the shader code and metadata.
|
||||
* @return A handle to the new program.
|
||||
*/
|
||||
|
||||
/** @fn backend::RenderTargetHandle Driver::createDefaultRenderTarget()
|
||||
* @brief Creates a render target that represents the default framebuffer (e.g., the screen).
|
||||
* @return A handle to the default render target.
|
||||
*/
|
||||
|
||||
/** @fn backend::RenderTargetHandle Driver::createRenderTarget(backend::TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, backend::MRT color, backend::TargetBufferInfo depth, backend::TargetBufferInfo stencil)
|
||||
* @brief Creates an offscreen render target.
|
||||
* @param targetBufferFlags A bitmask specifying which buffers (color, depth, stencil) are included.
|
||||
* @param width The width of the render target in pixels.
|
||||
* @param height The height of the render target in pixels.
|
||||
* @param samples The number of samples for multisampling.
|
||||
* @param layerCount The number of layers for layered rendering.
|
||||
* @param color An array of color attachments.
|
||||
* @param depth The depth attachment.
|
||||
* @param stencil The stencil attachment.
|
||||
* @return A handle to the new render target.
|
||||
*/
|
||||
|
||||
/** @fn backend::FenceHandle Driver::createFence()
|
||||
* @brief Creates a fence for synchronization between the CPU and GPU.
|
||||
* @return A handle to the new fence.
|
||||
*/
|
||||
|
||||
/** @fn backend::SwapChainHandle Driver::createSwapChain(void* nativeWindow, uint64_t flags)
|
||||
* @brief Creates a swap chain for a native window, used for presenting rendered frames.
|
||||
* @param nativeWindow A platform-specific pointer to the native window.
|
||||
* @param flags Configuration flags for the swap chain.
|
||||
* @return A handle to the new swap chain.
|
||||
*/
|
||||
|
||||
/** @fn backend::SwapChainHandle Driver::createSwapChainHeadless(uint32_t width, uint32_t height, uint64_t flags)
|
||||
* @brief Creates a headless swap chain for offscreen rendering without a native window.
|
||||
* @param width The width of the swap chain.
|
||||
* @param height The height of the swap chain.
|
||||
* @param flags Configuration flags for the swap chain.
|
||||
* @return A handle to the new swap chain.
|
||||
*/
|
||||
|
||||
/** @fn backend::TimerQueryHandle Driver::createTimerQuery()
|
||||
* @brief Creates a timer query object for measuring GPU execution time.
|
||||
* @return A handle to the new timer query.
|
||||
*/
|
||||
|
||||
/** @fn backend::DescriptorSetLayoutHandle Driver::createDescriptorSetLayout(backend::DescriptorSetLayout&& info)
|
||||
* @brief Creates a descriptor set layout, which defines the layout of bindings in a descriptor set.
|
||||
* @param info The layout information for the descriptor set.
|
||||
* @return A handle to the new descriptor set layout.
|
||||
*/
|
||||
|
||||
/** @fn backend::DescriptorSetHandle Driver::createDescriptorSet(backend::DescriptorSetLayoutHandle dslh)
|
||||
* @brief Creates a descriptor set based on a given layout.
|
||||
* @param dslh A handle to the descriptor set layout.
|
||||
* @return A handle to the new descriptor set.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateDescriptorSetBuffer(backend::DescriptorSetHandle dsh, backend::descriptor_binding_t binding, backend::BufferObjectHandle boh, uint32_t offset, uint32_t size)
|
||||
* @brief Updates a buffer descriptor within a descriptor set.
|
||||
* @param dsh The descriptor set to update.
|
||||
* @param binding The binding point of the descriptor.
|
||||
* @param boh The handle of the buffer object to bind.
|
||||
* @param offset The offset into the buffer.
|
||||
* @param size The size of the buffer region to bind.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateDescriptorSetTexture(backend::DescriptorSetHandle dsh, backend::descriptor_binding_t binding, backend::TextureHandle th, SamplerParams params)
|
||||
* @brief Updates a texture descriptor within a descriptor set.
|
||||
* @param dsh The descriptor set to update.
|
||||
* @param binding The binding point of the descriptor.
|
||||
* @param th The handle of the texture to bind.
|
||||
* @param params The sampler parameters for the texture.
|
||||
*/
|
||||
|
||||
/** @fn Driver::bindDescriptorSet(backend::DescriptorSetHandle dsh, backend::descriptor_set_t set, backend::DescriptorSetOffsetArray&& offsets)
|
||||
* @brief Binds a descriptor set to the current pipeline.
|
||||
* @param dsh The descriptor set to bind.
|
||||
* @param set The index of the descriptor set to bind.
|
||||
* @param offsets An array of dynamic offsets for the descriptor set.
|
||||
*/
|
||||
|
||||
// Resource destruction
|
||||
|
||||
/** @fn Driver::destroyVertexBuffer(backend::VertexBufferHandle vbh)
|
||||
* @brief Destroys a vertex buffer and releases its resources.
|
||||
* @param vbh The handle of the vertex buffer to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyVertexBufferInfo(backend::VertexBufferInfoHandle vbih)
|
||||
* @brief Destroys a VertexBufferInfo object.
|
||||
* @param vbih The handle of the VertexBufferInfo object to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyIndexBuffer(backend::IndexBufferHandle ibh)
|
||||
* @brief Destroys an index buffer and releases its resources.
|
||||
* @param ibh The handle of the index buffer to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyBufferObject(backend::BufferObjectHandle boh)
|
||||
* @brief Destroys a buffer object and releases its resources.
|
||||
* @param boh The handle of the buffer object to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyRenderPrimitive(backend::RenderPrimitiveHandle rph)
|
||||
* @brief Destroys a render primitive.
|
||||
* @param rph The handle of the render primitive to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyProgram(backend::ProgramHandle ph)
|
||||
* @brief Destroys a shader program.
|
||||
* @param ph The handle of the program to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyTexture(backend::TextureHandle th)
|
||||
* @brief Destroys a texture and releases its resources.
|
||||
* @param th The handle of the texture to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyRenderTarget(backend::RenderTargetHandle rth)
|
||||
* @brief Destroys a render target.
|
||||
* @param rth The handle of the render target to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroySwapChain(backend::SwapChainHandle sch)
|
||||
* @brief Destroys a swap chain.
|
||||
* @param sch The handle of the swap chain to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyStream(backend::StreamHandle sh)
|
||||
* @brief Destroys a stream.
|
||||
* @param sh The handle of the stream to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyTimerQuery(backend::TimerQueryHandle tqh)
|
||||
* @brief Destroys a timer query object.
|
||||
* @param tqh The handle of the timer query to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyFence(backend::FenceHandle fh)
|
||||
* @brief Destroys a fence.
|
||||
* @param fh The handle of the fence to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyDescriptorSetLayout(backend::DescriptorSetLayoutHandle dslh)
|
||||
* @brief Destroys a descriptor set layout.
|
||||
* @param dslh The handle of the descriptor set layout to destroy.
|
||||
*/
|
||||
|
||||
/** @fn Driver::destroyDescriptorSet(backend::DescriptorSetHandle dsh)
|
||||
* @brief Destroys a descriptor set.
|
||||
* @param dsh The handle of the descriptor set to destroy.
|
||||
*/
|
||||
|
||||
// Synchronous APIs
|
||||
|
||||
/** @fn void Driver::terminate()
|
||||
* @brief Terminates the driver and releases all associated resources. This is a synchronous operation.
|
||||
*/
|
||||
|
||||
/** @fn backend::StreamHandle Driver::createStreamNative(void* stream)
|
||||
* @brief Creates a stream from a native stream object (e.g., a SurfaceTexture on Android).
|
||||
* @param stream A pointer to the native stream object.
|
||||
* @return A handle to the new stream.
|
||||
*/
|
||||
|
||||
/** @fn backend::StreamHandle Driver::createStreamAcquired()
|
||||
* @brief Creates a stream that will be populated with images acquired via `setAcquiredImage`.
|
||||
* @return A handle to the new stream.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::setAcquiredImage(backend::StreamHandle stream, void* image, const math::mat3f& transform, backend::CallbackHandler* handler, backend::StreamCallback cb, void* userData)
|
||||
* @brief Provides an image to an acquired stream, with a callback for when the image is no longer in use.
|
||||
* @param stream The handle of the stream.
|
||||
* @param image A pointer to the image data.
|
||||
* @param transform The transformation matrix for the image.
|
||||
* @param handler The callback handler.
|
||||
* @param cb The callback function to be invoked when the image is released.
|
||||
* @param userData User data for the callback.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::setStreamDimensions(backend::StreamHandle stream, uint32_t width, uint32_t height)
|
||||
* @brief Sets the dimensions of a stream, which may be necessary for certain stream types.
|
||||
* @param stream The handle of the stream.
|
||||
* @param width The new width of the stream.
|
||||
* @param height The new height of the stream.
|
||||
*/
|
||||
|
||||
/** @fn int64_t Driver::getStreamTimestamp(backend::StreamHandle stream)
|
||||
* @brief Gets the timestamp of the last frame from a stream, if available.
|
||||
* @param stream The handle of the stream.
|
||||
* @return The timestamp in nanoseconds, or 0 if not available.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::updateStreams(backend::DriverApi* driver)
|
||||
* @brief Updates all active streams, typically called once per frame.
|
||||
* @param driver The driver API pointer.
|
||||
*/
|
||||
|
||||
/** @fn backend::FenceStatus Driver::getFenceStatus(backend::FenceHandle fh)
|
||||
* @brief Gets the current status of a fence.
|
||||
* @param fh The handle of the fence.
|
||||
* @return The status of the fence (e.g., SIGNALED, TIMEOUT_EXPIRED).
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isTextureFormatSupported(backend::TextureFormat format)
|
||||
* @brief Checks if a specific texture format is supported by the driver.
|
||||
* @param format The texture format to check.
|
||||
* @return True if the format is supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isTextureSwizzleSupported()
|
||||
* @brief Checks if texture channel swizzling is supported by the driver.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isTextureFormatMipmappable(backend::TextureFormat format)
|
||||
* @brief Checks if a texture format can have mipmaps automatically generated.
|
||||
* @param format The texture format to check.
|
||||
* @return True if the format is mipmappable, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isRenderTargetFormatSupported(backend::TextureFormat format)
|
||||
* @brief Checks if a texture format is supported for use as a render target attachment.
|
||||
* @param format The texture format to check.
|
||||
* @return True if the format is supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isFrameBufferFetchSupported()
|
||||
* @brief Checks if framebuffer fetch (reading from the framebuffer in a shader) is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isFrameBufferFetchMultiSampleSupported()
|
||||
* @brief Checks if multisampled framebuffer fetch is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isFrameTimeSupported()
|
||||
* @brief Checks if frame time queries are supported for performance measurement.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isAutoDepthResolveSupported()
|
||||
* @brief Checks if automatic resolution of multisampled depth buffers is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isSRGBSwapChainSupported()
|
||||
* @brief Checks if sRGB swap chains are supported for correct color space handling.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isProtectedContentSupported()
|
||||
* @brief Checks if rendering protected content (e.g., for DRM) is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isStereoSupported()
|
||||
* @brief Checks if stereoscopic rendering is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isParallelShaderCompileSupported()
|
||||
* @brief Checks if the driver can compile shaders in parallel for improved performance.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isDepthStencilResolveSupported()
|
||||
* @brief Checks if resolving multisampled depth/stencil buffers is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isDepthStencilBlitSupported(backend::TextureFormat format)
|
||||
* @brief Checks if blitting (copying) depth/stencil data is supported for a given format.
|
||||
* @param format The texture format.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isProtectedTexturesSupported()
|
||||
* @brief Checks if creating protected textures is supported.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isDepthClampSupported()
|
||||
* @brief Checks if depth clamping is supported by the hardware.
|
||||
* @return True if supported, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn uint8_t Driver::getMaxDrawBuffers()
|
||||
* @brief Gets the maximum number of simultaneous draw buffers (Multiple Render Targets).
|
||||
* @return The maximum number of draw buffers.
|
||||
*/
|
||||
|
||||
/** @fn size_t Driver::getMaxUniformBufferSize()
|
||||
* @brief Gets the maximum size of a uniform buffer in bytes.
|
||||
* @return The maximum size in bytes.
|
||||
*/
|
||||
|
||||
/** @fn size_t Driver::getMaxTextureSize(backend::SamplerType target)
|
||||
* @brief Gets the maximum texture dimension (width, height, or depth) for a given target type.
|
||||
* @param target The texture target type.
|
||||
* @return The maximum size in texels.
|
||||
*/
|
||||
|
||||
/** @fn size_t Driver::getMaxArrayTextureLayers()
|
||||
* @brief Gets the maximum number of layers in an array texture.
|
||||
* @return The maximum number of layers.
|
||||
*/
|
||||
|
||||
/** @fn math::float2 Driver::getClipSpaceParams()
|
||||
* @brief Gets the clip space parameters for the current backend.
|
||||
* @return A float2 containing clip space parameters.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::setupExternalImage2(backend::Platform::ExternalImageHandleRef image)
|
||||
* @brief Performs any necessary setup for an external image before it can be used.
|
||||
* @param image A reference to the external image.
|
||||
*/
|
||||
|
||||
/** @fn void Driver::setupExternalImage(void* image)
|
||||
* @brief Performs any necessary setup for an external image before it can be used.
|
||||
* @param image A pointer to the external image.
|
||||
*/
|
||||
|
||||
/** @fn backend::TimerQueryResult Driver::getTimerQueryValue(backend::TimerQueryHandle query, uint64_t* elapsedTime)
|
||||
* @brief Gets the result of a timer query, providing the elapsed GPU time.
|
||||
* @param query The timer query handle.
|
||||
* @param elapsedTime A pointer to store the elapsed time in nanoseconds.
|
||||
* @return The result of the query (e.g., AVAILABLE, NOT_READY).
|
||||
*/
|
||||
|
||||
/** @fn bool Driver::isWorkaroundNeeded(backend::Workaround workaround)
|
||||
* @brief Checks if a specific driver or hardware workaround is needed.
|
||||
* @param workaround The workaround to check.
|
||||
* @return True if the workaround is needed, false otherwise.
|
||||
*/
|
||||
|
||||
/** @fn backend::FeatureLevel Driver::getFeatureLevel()
|
||||
* @brief Gets the feature level supported by the driver.
|
||||
* @return The driver's feature level.
|
||||
*/
|
||||
|
||||
// Resource updates
|
||||
|
||||
/** @fn Driver::setVertexBufferObject(backend::VertexBufferHandle vbh, uint32_t index, backend::BufferObjectHandle bufferObject)
|
||||
* @brief Associates a buffer object with a specific buffer slot in a vertex buffer.
|
||||
* @param vbh The handle of the vertex buffer.
|
||||
* @param index The index of the buffer slot to set.
|
||||
* @param bufferObject The handle of the buffer object to associate.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateIndexBuffer(backend::IndexBufferHandle ibh, backend::BufferDescriptor&& data, uint32_t byteOffset)
|
||||
* @brief Updates the data of an index buffer.
|
||||
* @param ibh The handle of the index buffer.
|
||||
* @param data The new data to upload.
|
||||
* @param byteOffset The offset in bytes into the buffer to start writing to.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateBufferObject(backend::BufferObjectHandle boh, backend::BufferDescriptor&& data, uint32_t byteOffset)
|
||||
* @brief Updates the data of a buffer object.
|
||||
* @param boh The handle of the buffer object.
|
||||
* @param data The new data to upload.
|
||||
* @param byteOffset The offset in bytes into the buffer to start writing to.
|
||||
*/
|
||||
|
||||
/** @fn Driver::registerBufferObjectStreams(backend::BufferObjectHandle boh, backend::BufferObjectStreamDescriptor&& streams)
|
||||
* @brief Registers external streams with a buffer object for efficient data transfer.
|
||||
* @param boh The handle of the buffer object.
|
||||
* @param streams The stream descriptors.
|
||||
*/
|
||||
|
||||
/** @fn Driver::updateBufferObjectUnsynchronized(backend::BufferObjectHandle boh, backend::BufferDescriptor&& data, uint32_t byteOffset)
|
||||
* @brief Updates a buffer object without synchronization, for use when manual synchronization is handled.
|
||||
* @param boh The handle of the buffer object.
|
||||
* @param data The new data to upload.
|
||||
* @param byteOffset The offset in bytes into the buffer to start writing to.
|
||||
*/
|
||||
|
||||
/** @fn Driver::resetBufferObject(backend::BufferObjectHandle boh)
|
||||
* @brief Resets a buffer object, potentially discarding its contents.
|
||||
* @param boh The handle of the buffer object.
|
||||
*/
|
||||
|
||||
/** @fn Driver::update3DImage(backend::TextureHandle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth, backend::PixelBufferDescriptor&& data)
|
||||
* @brief Updates a sub-region of a 3D texture.
|
||||
* @param th The handle of the texture.
|
||||
* @param level The mipmap level to update.
|
||||
* @param xoffset The x offset of the sub-region.
|
||||
* @param yoffset The y offset of the sub-region.
|
||||
* @param zoffset The z offset of the sub-region.
|
||||
* @param width The width of the sub-region.
|
||||
* @param height The height of the sub-region.
|
||||
* @param depth The depth of the sub-region.
|
||||
* @param data The pixel data to upload.
|
||||
*/
|
||||
|
||||
/** @fn Driver::generateMipmaps(backend::TextureHandle th)
|
||||
* @brief Generates mipmaps for a texture automatically.
|
||||
* @param th The handle of the texture.
|
||||
*/
|
||||
|
||||
/** @fn Driver::setExternalStream(backend::TextureHandle th, backend::StreamHandle sh)
|
||||
* @brief Associates an external stream with a texture for video or camera input.
|
||||
* @param th The handle of the texture.
|
||||
* @param sh The handle of the stream.
|
||||
*/
|
||||
|
||||
// Render passes
|
||||
|
||||
/** @fn Driver::beginRenderPass(backend::RenderTargetHandle rth, const backend::RenderPassParams& params)
|
||||
* @brief Begins a new render pass, targeting a specific render target.
|
||||
* @param rth The handle of the render target.
|
||||
* @param params The parameters for the render pass (e.g., clear values, load/store actions).
|
||||
*/
|
||||
|
||||
/** @fn Driver::endRenderPass()
|
||||
* @brief Ends the current render pass.
|
||||
*/
|
||||
|
||||
/** @fn Driver::nextSubpass()
|
||||
* @brief Moves to the next subpass within the current render pass.
|
||||
*/
|
||||
|
||||
// Timer queries
|
||||
|
||||
/** @fn Driver::beginTimerQuery(backend::TimerQueryHandle query)
|
||||
* @brief Begins a timer query to measure GPU time.
|
||||
* @param query The handle of the timer query.
|
||||
*/
|
||||
|
||||
/** @fn Driver::endTimerQuery(backend::TimerQueryHandle query)
|
||||
* @brief Ends a timer query.
|
||||
* @param query The handle of the timer query.
|
||||
*/
|
||||
|
||||
/** @fn Driver::compilePrograms(backend::CompilerPriorityQueue priority, backend::CallbackHandler* handler, backend::CallbackHandler::Callback callback, void* user)
|
||||
* @brief Compiles a queue of pending shader programs.
|
||||
* @param priority The priority queue to use for compilation.
|
||||
* @param handler The callback handler.
|
||||
* @param callback The callback function to be invoked upon completion.
|
||||
* @param user User data for the callback.
|
||||
*/
|
||||
|
||||
// Swap chain
|
||||
|
||||
/** @fn Driver::makeCurrent(backend::SwapChainHandle schDraw, backend::SwapChainHandle schRead)
|
||||
* @brief Makes a swap chain current for drawing and reading.
|
||||
* @param schDraw The draw swap chain.
|
||||
* @param schRead The read swap chain.
|
||||
*/
|
||||
|
||||
/** @fn Driver::commit(backend::SwapChainHandle sch)
|
||||
* @brief Commits the back buffer of a swap chain, making it visible.
|
||||
* @param sch The handle of the swap chain.
|
||||
*/
|
||||
|
||||
// Rendering state
|
||||
|
||||
/** @fn Driver::setPushConstant(backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant value)
|
||||
* @brief Sets a push constant value for a specific shader stage.
|
||||
* @param stage The shader stage to set the constant for.
|
||||
* @param index The index of the push constant.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
|
||||
/** @fn Driver::insertEventMarker(const char* string)
|
||||
* @brief Inserts a debug event marker into the command stream.
|
||||
* @param string The marker string.
|
||||
*/
|
||||
|
||||
/** @fn Driver::pushGroupMarker(const char* string)
|
||||
* @brief Pushes a debug group marker onto the command stream stack.
|
||||
* @param string The marker string.
|
||||
*/
|
||||
|
||||
/** @fn Driver::popGroupMarker()
|
||||
* @brief Pops a debug group marker from the command stream stack.
|
||||
*/
|
||||
|
||||
/** @fn Driver::startCapture()
|
||||
* @brief Starts a graphics capture session.
|
||||
*/
|
||||
|
||||
/** @fn Driver::stopCapture()
|
||||
* @brief Stops a graphics capture session.
|
||||
*/
|
||||
|
||||
// Read-back
|
||||
|
||||
/** @fn Driver::readPixels(backend::RenderTargetHandle src, uint32_t x, uint32_t y, uint32_t width, uint32_t height, backend::PixelBufferDescriptor&& data)
|
||||
* @brief Reads a block of pixels from a render target into a client-side buffer.
|
||||
* @param src The source render target.
|
||||
* @param x The x coordinate of the region to read.
|
||||
* @param y The y coordinate of the region to read.
|
||||
* @param width The width of the region to read.
|
||||
* @param height The height of the region to read.
|
||||
* @param data The buffer to store the pixel data.
|
||||
*/
|
||||
|
||||
/** @fn Driver::readBufferSubData(backend::BufferObjectHandle src, uint32_t offset, uint32_t size, backend::BufferDescriptor&& data)
|
||||
* @brief Reads a sub-region of a buffer object into a client-side buffer.
|
||||
* @param src The source buffer object.
|
||||
* @param offset The offset to start reading from.
|
||||
* @param size The number of bytes to read.
|
||||
* @param data The buffer to store the data.
|
||||
*/
|
||||
|
||||
// Rendering
|
||||
|
||||
/** @fn Driver::blitDEPRECATED(backend::TargetBufferFlags buffers, backend::RenderTargetHandle dst, backend::Viewport dstRect, backend::RenderTargetHandle src, backend::Viewport srcRect, backend::SamplerMagFilter filter)
|
||||
* @brief Blits (copies) a region from one render target to another (deprecated).
|
||||
* @param buffers The buffers to blit (e.g., color, depth).
|
||||
* @param dst The destination render target.
|
||||
* @param dstRect The destination rectangle.
|
||||
* @param src The source render target.
|
||||
* @param srcRect The source rectangle.
|
||||
* @param filter The filter to use for scaling.
|
||||
*/
|
||||
|
||||
/** @fn Driver::resolve(backend::TextureHandle dst, uint8_t dstLevel, uint8_t dstLayer, backend::TextureHandle src, uint8_t srcLevel, uint8_t srcLayer)
|
||||
* @brief Resolves a multisampled texture into a non-multisampled texture.
|
||||
* @param dst The destination texture.
|
||||
* @param dstLevel The destination mipmap level.
|
||||
* @param dstLayer The destination layer.
|
||||
* @param src The source multisampled texture.
|
||||
* @param srcLevel The source mipmap level.
|
||||
* @param srcLayer The source layer.
|
||||
*/
|
||||
|
||||
/** @fn Driver::blit(backend::TextureHandle dst, uint8_t dstLevel, uint8_t dstLayer, math::uint2 dstOrigin, backend::TextureHandle src, uint8_t srcLevel, uint8_t srcLayer, math::uint2 srcOrigin, math::uint2 size)
|
||||
* @brief Blits (copies) a region from one texture to another.
|
||||
* @param dst The destination texture.
|
||||
* @param dstLevel The destination mipmap level.
|
||||
* @param dstLayer The destination layer.
|
||||
* @param dstOrigin The origin of the destination region.
|
||||
* @param src The source texture.
|
||||
* @param srcLevel The source mipmap level.
|
||||
* @param srcLayer The source layer.
|
||||
* @param srcOrigin The origin of the source region.
|
||||
* @param size The size of the region to blit.
|
||||
*/
|
||||
|
||||
/** @fn Driver::bindPipeline(const backend::PipelineState& state)
|
||||
* @brief Binds a pipeline state object, including shaders and render states.
|
||||
* @param state The pipeline state to bind.
|
||||
*/
|
||||
|
||||
/** @fn Driver::bindRenderPrimitive(backend::RenderPrimitiveHandle rph)
|
||||
* @brief Binds a render primitive for subsequent draw calls.
|
||||
* @param rph The handle of the render primitive.
|
||||
*/
|
||||
|
||||
/** @fn Driver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount)
|
||||
* @brief Draws a render primitive using the currently bound pipeline and primitive.
|
||||
* @param indexOffset The offset into the index buffer.
|
||||
* @param indexCount The number of indices to draw.
|
||||
* @param instanceCount The number of instances to draw.
|
||||
*/
|
||||
|
||||
/** @fn Driver::draw(backend::PipelineState state, backend::RenderPrimitiveHandle rph, uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount)
|
||||
* @brief A combined call to bind a pipeline, bind a primitive, and draw.
|
||||
* @param state The pipeline state to bind.
|
||||
* @param rph The render primitive to bind.
|
||||
* @param indexOffset The offset into the index buffer.
|
||||
* @param indexCount The number of indices to draw.
|
||||
* @param instanceCount The number of instances to draw.
|
||||
*/
|
||||
|
||||
/** @fn Driver::dispatchCompute(backend::ProgramHandle program, math::uint3 workGroupCount)
|
||||
* @brief Dispatches a compute shader.
|
||||
* @param program The handle of the compute program.
|
||||
* @param workGroupCount The number of work groups to dispatch in each dimension.
|
||||
*/
|
||||
|
||||
/** @fn Driver::scissor(Viewport scissor)
|
||||
* @brief Sets the scissor rectangle for clipping.
|
||||
* @param scissor The scissor rectangle.
|
||||
*/
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
* Destroys the object D at Handle<B> and construct a new D in its place
|
||||
* e.g.:
|
||||
* Handle<ConcreteTexture> h = allocateAndConstruct(w, h);
|
||||
* ConcreteTexture* p = reconstruct(h, w, h);
|
||||
* ConcreteTexture* p = destroyAndConstruct(h, w, h);
|
||||
*/
|
||||
template<typename D, typename B, typename ... ARGS>
|
||||
std::enable_if_t<std::is_base_of_v<B, D>, D>*
|
||||
|
||||
@@ -66,7 +66,7 @@ static std::string to_string(int const i) { return std::to_string(i); }
|
||||
static std::string to_string(float const f) { return "float(" + std::to_string(f) + ")"; }
|
||||
|
||||
static void logCompilationError(ShaderStage shaderType, const char* name, GLuint shaderId,
|
||||
CString const& sourceCode) noexcept;
|
||||
Program::ShaderBlob const& sourceCode) noexcept;
|
||||
static void logProgramLinkError(char const* name, GLuint program) noexcept;
|
||||
|
||||
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext const& context, char* source,
|
||||
@@ -88,7 +88,7 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
||||
ShaderCompilerService& compiler;
|
||||
CString const& name;
|
||||
FixedCapacityVector<std::pair<CString, uint8_t>> attributes;
|
||||
shaders_source_t shaderSourceCode;
|
||||
Program::ShaderSource shaderSourceCode;
|
||||
void* user = nullptr;
|
||||
struct {
|
||||
shaders_t shaders{};
|
||||
@@ -612,7 +612,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
#ifndef NDEBUG
|
||||
// for debugging we return the original shader source (without the modifications we
|
||||
// made here), otherwise the line numbers wouldn't match.
|
||||
token->shaderSourceCode[i] = { shader_src, shader_len };
|
||||
token->shaderSourceCode[i] = std::move(shader);
|
||||
#endif
|
||||
token->gl.shaders[i] = shaderId;
|
||||
}
|
||||
@@ -758,7 +758,8 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
|
||||
UTILS_NOINLINE
|
||||
/* static */ void logCompilationError(ShaderStage shaderType, const char* name,
|
||||
GLuint const shaderId, UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
|
||||
GLuint const shaderId,
|
||||
UTILS_UNUSED_IN_RELEASE Program::ShaderBlob const& sourceCode) noexcept {
|
||||
|
||||
{ // scope for the temporary string storage
|
||||
auto to_string = [](ShaderStage type) -> const char* {
|
||||
@@ -785,7 +786,8 @@ UTILS_NOINLINE
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::string_view const shader{ sourceCode.data(), sourceCode.size() };
|
||||
std::string_view const shader{ reinterpret_cast<const char*>(sourceCode.data()),
|
||||
sourceCode.size() };
|
||||
size_t lc = 1;
|
||||
size_t start = 0;
|
||||
std::string line;
|
||||
|
||||
@@ -58,7 +58,6 @@ class ShaderCompilerService {
|
||||
public:
|
||||
using program_token_t = std::shared_ptr<OpenGLProgramToken>;
|
||||
using shaders_t = std::array<GLuint, Program::SHADER_TYPE_COUNT>;
|
||||
using shaders_source_t = std::array<utils::CString, Program::SHADER_TYPE_COUNT>;
|
||||
|
||||
explicit ShaderCompilerService(OpenGLDriver& driver);
|
||||
|
||||
|
||||
@@ -209,10 +209,42 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& drive
|
||||
}
|
||||
#endif
|
||||
|
||||
// Configure GPU context priority level for scheduling and preemption
|
||||
if (driverConfig.gpuContextPriority != Platform::GpuContextPriority::DEFAULT) {
|
||||
if (extensions.has("EGL_IMG_context_priority")) {
|
||||
EGLint priorityLevel = EGL_CONTEXT_PRIORITY_MEDIUM_IMG;
|
||||
const char* priorityName;
|
||||
switch (driverConfig.gpuContextPriority) {
|
||||
case Platform::GpuContextPriority::DEFAULT:
|
||||
assert_invariant(false);
|
||||
break;
|
||||
case Platform::GpuContextPriority::LOW:
|
||||
priorityLevel = EGL_CONTEXT_PRIORITY_LOW_IMG;
|
||||
priorityName = "LOW";
|
||||
break;
|
||||
case Platform::GpuContextPriority::MEDIUM:
|
||||
priorityLevel = EGL_CONTEXT_PRIORITY_MEDIUM_IMG;
|
||||
priorityName = "MEDIUM";
|
||||
break;
|
||||
case Platform::GpuContextPriority::HIGH:
|
||||
priorityLevel = EGL_CONTEXT_PRIORITY_HIGH_IMG;
|
||||
priorityName = "HIGH";
|
||||
break;
|
||||
case Platform::GpuContextPriority::REALTIME:
|
||||
priorityLevel = EGL_CONTEXT_PRIORITY_HIGH_IMG;
|
||||
priorityName = "REALTIME(=HIGH)";
|
||||
break;
|
||||
}
|
||||
contextAttribs[EGL_CONTEXT_PRIORITY_LEVEL_IMG] = priorityLevel;
|
||||
LOG(INFO) << "EGL: Enabling GPU context priority: " << priorityName;
|
||||
} else {
|
||||
LOG(WARNING) << "EGL: GPU context priority requested but not supported";
|
||||
}
|
||||
}
|
||||
|
||||
// config use for creating the context
|
||||
EGLConfig eglConfig = EGL_NO_CONFIG_KHR;
|
||||
|
||||
|
||||
if (UTILS_UNLIKELY(!ext.egl.KHR_no_config_context)) {
|
||||
// find a config we can use if we don't have "EGL_KHR_no_config_context" and that we can use
|
||||
// for the dummy pbuffer surface.
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
|
||||
#include "VulkanBufferProxy.h"
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanMemory.h"
|
||||
|
||||
#include "VulkanBufferCache.h"
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanMemory.h"
|
||||
|
||||
using namespace bluevk;
|
||||
@@ -36,6 +36,38 @@ VulkanBufferProxy::VulkanBufferProxy(VmaAllocator allocator, VulkanStagePool& st
|
||||
|
||||
void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* cpuData,
|
||||
uint32_t byteOffset, uint32_t numBytes) {
|
||||
// The VulkanBuffer is available if the only object reference is hold by the
|
||||
// `VulkanBufferProxy`. This means that the buffer is not currently referenced by a
|
||||
// `VulkanCommandBuffer`.
|
||||
bool const isAvailable = mBuffer->getCount() == 1;
|
||||
|
||||
if (isAvailable) {
|
||||
// We are up to date with all the update operations, so no need to synchronize with previous
|
||||
// updates.
|
||||
mUpdatedBytes = 0;
|
||||
mUpdatedOffset = 0;
|
||||
}
|
||||
|
||||
// Keep track of the VulkanBuffer usage
|
||||
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;
|
||||
bool const isUniform = getUsage() == VulkanBufferUsage::UNIFORM;
|
||||
bool const useMemcpy =
|
||||
isUniform && isMemcopyable && isAvailable && !FVK_FORCE_STAGING_FOR_BUFFER_UPDATES;
|
||||
if (useMemcpy) {
|
||||
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
|
||||
byteOffset;
|
||||
memcpy(dest, cpuData, numBytes);
|
||||
vmaFlushAllocation(mAllocator, mBuffer->getGpuBuffer()->vmaAllocation, byteOffset,
|
||||
numBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: this should be stored within the command buffer before going out of
|
||||
// scope, so that the command buffer can manage its lifecycle.
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> stage = mStagePool.acquireStage(numBytes);
|
||||
@@ -128,4 +160,4 @@ VulkanBufferUsage VulkanBufferProxy::getUsage() const noexcept {
|
||||
return mBuffer->getGpuBuffer()->usage;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -49,6 +49,6 @@ private:
|
||||
uint32_t mUpdatedBytes = 0;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_VULKANBUFFERPROXY_H
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANBUFFERPROXY_H
|
||||
|
||||
@@ -214,4 +214,8 @@ constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
|
||||
// destroying any unused pipeline object.
|
||||
static_assert(FVK_MAX_PIPELINE_AGE >= FVK_MAX_COMMAND_BUFFERS);
|
||||
|
||||
// Whether the buffer updates always use staging or not. Otherwise its allowed to also use memcpy
|
||||
// for buffer updates. Default is false
|
||||
constexpr static const bool FVK_FORCE_STAGING_FOR_BUFFER_UPDATES = false;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -394,7 +394,13 @@ void VulkanDriver::collectGarbage() {
|
||||
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||
// Do nothing.
|
||||
|
||||
// Check if any command have finished and reset all its used resources. The resources
|
||||
// wont be destroyed but their reference count will decreased if the command is already
|
||||
// completed.
|
||||
//
|
||||
// This will let us check if any VulkanBuffer is currently in flight or not.
|
||||
mCommands.gc();
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
mExternalImageManager.onBeginFrame();
|
||||
@@ -1236,7 +1242,8 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void VulkanDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
|
||||
void VulkanDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh,
|
||||
BufferObjectStreamDescriptor&& streams) {
|
||||
// Noop
|
||||
}
|
||||
|
||||
|
||||
@@ -34,16 +34,23 @@ namespace {
|
||||
static constexpr uint32_t MAX_EMPTY_STAGES_TO_RETAIN = 1;
|
||||
constexpr uint32_t STAGE_SIZE = 1048576;
|
||||
|
||||
uint32_t alignValue(uint32_t valueToAlign, uint32_t alignment) {
|
||||
if (alignment == 0) {
|
||||
return valueToAlign;
|
||||
}
|
||||
|
||||
uint32_t remainder = valueToAlign % alignment;
|
||||
return remainder == 0 ? valueToAlign : valueToAlign + (alignment - remainder);
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStage::acquireSegment(
|
||||
fvkmemory::ResourceManager* resManager, uint32_t numBytes) {
|
||||
auto segment = fvkmemory::resource_ptr<Segment>::construct(
|
||||
resManager, this, numBytes, mCurrentOffset, [this](uint32_t offset) {
|
||||
mSegments.erase(offset);
|
||||
});
|
||||
mSegments.insert({mCurrentOffset, segment.get()});
|
||||
mCurrentOffset += numBytes;
|
||||
fvkmemory::ResourceManager* resManager, uint32_t segmentOffset, uint32_t numBytes) {
|
||||
auto segment = fvkmemory::resource_ptr<Segment>::construct(resManager, this, numBytes,
|
||||
segmentOffset, [this](uint32_t offset) { mSegments.erase(offset); });
|
||||
mSegments.insert({ segmentOffset, segment.get() });
|
||||
mCurrentOffset = segmentOffset + numBytes;
|
||||
return segment;
|
||||
}
|
||||
|
||||
@@ -54,7 +61,8 @@ VulkanStagePool::VulkanStagePool(VmaAllocator allocator, fvkmemory::ResourceMana
|
||||
mCommands(commands),
|
||||
mDeviceLimits(deviceLimits) {}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStagePool::acquireStage(uint32_t numBytes,
|
||||
uint32_t alignment) {
|
||||
// Apply alignment to the byte count to ensure that, when we later flush
|
||||
// data written by the host, we only flush the atoms that we modified, and
|
||||
// no adjacent atoms.
|
||||
@@ -64,17 +72,35 @@ fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStagePool::acquireStage(uint
|
||||
// equal to the requested size.
|
||||
auto iter = mStages.lower_bound(numBytes);
|
||||
|
||||
VulkanStage* pStage;
|
||||
if (iter != mStages.end()) {
|
||||
pStage = iter->second;
|
||||
mStages.erase(iter);
|
||||
} else {
|
||||
// If we have potential buffer candidates, loop through and try to find one
|
||||
// that works with our alignment. Also, set the offset for the buffer.
|
||||
// If we have no buffer, create a new one.
|
||||
VulkanStage* pStage = nullptr;
|
||||
uint32_t segmentOffset = 0;
|
||||
while (iter != mStages.end()) {
|
||||
segmentOffset = alignValue(iter->second->currentOffset(), alignment);
|
||||
|
||||
// Check for overflow + if there's space.
|
||||
if (segmentOffset >= iter->second->currentOffset() &&
|
||||
iter->second->capacity() - numBytes >= segmentOffset) {
|
||||
// This buffer has enough space.
|
||||
pStage = iter->second;
|
||||
mStages.erase(iter);
|
||||
break;
|
||||
}
|
||||
|
||||
// This buffer wasn't suitable. Check the next larger one.
|
||||
++iter;
|
||||
}
|
||||
|
||||
if (!pStage) {
|
||||
pStage = allocateNewStage(std::max(numBytes, STAGE_SIZE));
|
||||
}
|
||||
|
||||
// Note: this allocation updates `currentOffset` and `segments` within
|
||||
// the parent stage. When destroyed, it will update `segments`.
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> pSegment = pStage->acquireSegment(mResManager, numBytes);
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> pSegment =
|
||||
pStage->acquireSegment(mResManager, segmentOffset, numBytes);
|
||||
|
||||
// Update the stage's metadata, and reinsert it with the remaining segment
|
||||
// capacity.
|
||||
@@ -86,12 +112,7 @@ fvkmemory::resource_ptr<VulkanStage::Segment> VulkanStagePool::acquireStage(uint
|
||||
|
||||
uint32_t VulkanStagePool::alignToNonCoherentAtomSize(uint32_t bytes) {
|
||||
VkDeviceSize alignment = mDeviceLimits->nonCoherentAtomSize;
|
||||
if (alignment == 0) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
uint32_t remainder = bytes % alignment;
|
||||
return remainder == 0 ? bytes : bytes + (alignment - remainder);
|
||||
return alignValue(bytes, alignment);
|
||||
}
|
||||
|
||||
VulkanStage* VulkanStagePool::allocateNewStage(uint32_t capacity) {
|
||||
|
||||
@@ -105,8 +105,11 @@ public:
|
||||
// Marks a region of the block as "in-use", and provides information about
|
||||
// the allocated region to the caller. Note: this assumes that numBytes
|
||||
// is aligned to the physical device's nonCoherentAtomSize.
|
||||
// Takes the offset to use as the segment's start offset. It is assumed
|
||||
// that this is greater than the segment's current offset, and it is
|
||||
// undefined behavior if not.
|
||||
fvkmemory::resource_ptr<Segment> acquireSegment(fvkmemory::ResourceManager* resManager,
|
||||
uint32_t numBytes);
|
||||
uint32_t segmentOffset, uint32_t numBytes);
|
||||
|
||||
private:
|
||||
const VmaAllocation mMemory;
|
||||
@@ -144,7 +147,10 @@ public:
|
||||
// buffers so that we have less objects around that we have to keep track
|
||||
// of.
|
||||
// This function is NOT thread-safe.
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> acquireStage(uint32_t numBytes);
|
||||
// numBytes - the number of bytes required by this segment.
|
||||
// alignment - the multiple to align the buffer offset to
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> acquireStage(uint32_t numBytes,
|
||||
uint32_t alignment = 0);
|
||||
|
||||
// Images have VK_IMAGE_LAYOUT_GENERAL and must not be transitioned to any other layout
|
||||
VulkanStageImage const* acquireImage(PixelDataFormat format, PixelDataType type,
|
||||
|
||||
@@ -227,7 +227,7 @@ void adjustedMemcpy(void* mapped, PixelBufferDescriptor const& p, size_t width,
|
||||
|
||||
// Slow path of copying row by row
|
||||
assert_invariant(pbdStride >= width);
|
||||
if (UTILS_UNLIKELY(p.left > 0 || p.top > 0 || pbdStride > width)) {
|
||||
if (UTILS_UNLIKELY(pixelSize > 0 && (p.left > 0 || p.top > 0 || pbdStride > width))) {
|
||||
size_t const pbdRowSize =
|
||||
PixelBufferDescriptor::computeDataSize(p.format, p.type, pbdStride, 1, p.alignment);
|
||||
size_t const pbdHeight = p.size / pixelSize / pbdStride / depth;
|
||||
@@ -248,7 +248,9 @@ void adjustedMemcpy(void* mapped, PixelBufferDescriptor const& p, size_t width,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t const writeSize = pixelSize * (width * height * depth);
|
||||
// Note: if pixelSize = 0, we're dealing with a compressed format; we
|
||||
// should fall back to the buffer's size.
|
||||
size_t const writeSize = pixelSize > 0 ? pixelSize * (width * height * depth) : p.size;
|
||||
memcpy(mapped, buf, writeSize);
|
||||
}
|
||||
}
|
||||
@@ -517,13 +519,17 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
// Otherwise, use vkCmdCopyBufferToImage.
|
||||
size_t const bpp =
|
||||
PixelBufferDescriptor::computeDataSize(hostData->format, hostData->type, 1, 1, 1);
|
||||
size_t const writeSize = width * height * depth * bpp;
|
||||
// Note: if bpp = 0, we're dealing with a compressed format; we should fall
|
||||
// back to the buffer's size.
|
||||
size_t const writeSize = bpp > 0 ? width * height * depth * bpp : hostData->size;
|
||||
|
||||
// Note: the following stageSegment must be stored within the command buffer
|
||||
// before going out of scope, to ensure proper bookkeeping within the
|
||||
// staging buffer pool.
|
||||
uint8_t alignment =
|
||||
fvkutils::getTexelBlockSize(fvkutils::getVkFormat(hostData->format, hostData->type));
|
||||
fvkmemory::resource_ptr<VulkanStage::Segment> stageSegment =
|
||||
mState->mStagePool.acquireStage(writeSize);
|
||||
mState->mStagePool.acquireStage(writeSize, alignment);
|
||||
assert_invariant(stageSegment->memory());
|
||||
adjustedMemcpy(stageSegment->mapping(), *hostData, width, height, depth);
|
||||
vmaFlushAllocation(mState->mAllocator, stageSegment->memory(), stageSegment->offset(),
|
||||
@@ -591,7 +597,9 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& data, uint3
|
||||
uint32_t height, uint32_t depth, uint32_t miplevel) {
|
||||
// Otherwise, use vkCmdCopyBufferToImage.
|
||||
size_t const bpp = PixelBufferDescriptor::computeDataSize(data.format, data.type, 1, 1, 1);
|
||||
size_t const writeSize = width * height * depth * bpp;
|
||||
// Note: if bpp = 0, we're dealing with a compressed format; we should fall
|
||||
// back to the buffer's size.
|
||||
size_t const writeSize = bpp > 0 ? width * height * depth * bpp : data.size;
|
||||
|
||||
void* mapped = nullptr;
|
||||
VulkanStageImage const* stage
|
||||
|
||||
@@ -29,6 +29,8 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace fvkutils;
|
||||
|
||||
VulkanYcbcrConversionCache::VulkanYcbcrConversionCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
|
||||
#include "utils/Definitions.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
@@ -30,7 +32,7 @@ namespace filament::backend {
|
||||
class VulkanYcbcrConversionCache {
|
||||
public:
|
||||
struct Params {
|
||||
SamplerYcbcrConversion conversion = {}; // 4
|
||||
fvkutils::SamplerYcbcrConversion conversion = {}; // 4
|
||||
VkFormat format; // 4
|
||||
uint64_t externalFormat = 0; // 8
|
||||
};
|
||||
@@ -45,16 +47,15 @@ private:
|
||||
|
||||
struct ConversionEqualTo {
|
||||
bool operator()(Params lhs, Params rhs) const noexcept {
|
||||
SamplerYcbcrConversion::EqualTo equal;
|
||||
fvkutils::SamplerYcbcrConversion::EqualTo equal;
|
||||
return equal(lhs.conversion, rhs.conversion) &&
|
||||
lhs.externalFormat == rhs.externalFormat &&
|
||||
lhs.format == rhs.format;
|
||||
lhs.externalFormat == rhs.externalFormat && lhs.format == rhs.format;
|
||||
}
|
||||
};
|
||||
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
|
||||
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
|
||||
@@ -71,6 +71,8 @@ struct Resource {
|
||||
restype(ResourceType::UNDEFINED_TYPE),
|
||||
mHandleConsideredDestroyed(false) {}
|
||||
|
||||
uint32_t getCount() const { return mCount; }
|
||||
|
||||
private:
|
||||
inline void inc() noexcept {
|
||||
mCount++;
|
||||
|
||||
@@ -832,6 +832,9 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE;
|
||||
}
|
||||
|
||||
// TODO: Add support of VK_KHR_global_priority with `driverConfig.gpuContextPriority`
|
||||
// in VulkanPlatform::createDriver.
|
||||
|
||||
ExtensionSet deviceExts;
|
||||
// If using a shared context, we do not assume any extensions.
|
||||
if (!mImpl->mSharedContext) {
|
||||
@@ -923,7 +926,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
// Keep a copy of context for swapchains.
|
||||
mImpl->mContext = context;
|
||||
|
||||
return VulkanDriver::create(this, context, driverConfig);
|
||||
return VulkanDriver::create(this, mImpl->mContext, driverConfig);
|
||||
}
|
||||
|
||||
// This needs to be explictly written for
|
||||
|
||||
@@ -233,6 +233,212 @@ VkFormat getVkFormat(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
// As per VUID-vkCmdCopyBufferToImage-dstImage-07975 and
|
||||
// VUID-vkCmdCopyBufferToImage-dstImage-07978, these provide the texel block
|
||||
// sizes for each format (as confirmed using the table at
|
||||
// https://registry.khronos.org/vulkan/specs/latest/html/vkspec.html#formats-compatibility-classes).
|
||||
// We have not listed values for multi-plane formats, as we do not support them
|
||||
// unless they're externally provided.
|
||||
uint8_t getTexelBlockSize(VkFormat format) {
|
||||
switch (format) {
|
||||
// 8-bit formats.
|
||||
case VK_FORMAT_R8_UNORM:
|
||||
case VK_FORMAT_R8_SNORM:
|
||||
case VK_FORMAT_R8_USCALED:
|
||||
case VK_FORMAT_R8_SSCALED:
|
||||
case VK_FORMAT_R8_UINT:
|
||||
case VK_FORMAT_R8_SINT:
|
||||
case VK_FORMAT_R8_SRGB:
|
||||
return 1;
|
||||
|
||||
// 16-bit formats.
|
||||
case VK_FORMAT_R16_UNORM:
|
||||
case VK_FORMAT_R16_SNORM:
|
||||
case VK_FORMAT_R16_USCALED:
|
||||
case VK_FORMAT_R16_SSCALED:
|
||||
case VK_FORMAT_R16_UINT:
|
||||
case VK_FORMAT_R16_SINT:
|
||||
case VK_FORMAT_R16_SFLOAT:
|
||||
case VK_FORMAT_R8G8_UNORM:
|
||||
case VK_FORMAT_R8G8_SNORM:
|
||||
case VK_FORMAT_R8G8_USCALED:
|
||||
case VK_FORMAT_R8G8_SSCALED:
|
||||
case VK_FORMAT_R8G8_UINT:
|
||||
case VK_FORMAT_R8G8_SINT:
|
||||
case VK_FORMAT_R8G8_SRGB:
|
||||
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
||||
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
|
||||
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
|
||||
return 2;
|
||||
|
||||
// 24-bit formats.
|
||||
case VK_FORMAT_R8G8B8_UNORM:
|
||||
case VK_FORMAT_R8G8B8_SNORM:
|
||||
case VK_FORMAT_R8G8B8_USCALED:
|
||||
case VK_FORMAT_R8G8B8_SSCALED:
|
||||
case VK_FORMAT_R8G8B8_UINT:
|
||||
case VK_FORMAT_R8G8B8_SINT:
|
||||
case VK_FORMAT_R8G8B8_SRGB:
|
||||
case VK_FORMAT_B8G8R8_UNORM:
|
||||
case VK_FORMAT_B8G8R8_SNORM:
|
||||
case VK_FORMAT_B8G8R8_USCALED:
|
||||
case VK_FORMAT_B8G8R8_SSCALED:
|
||||
case VK_FORMAT_B8G8R8_UINT:
|
||||
case VK_FORMAT_B8G8R8_SINT:
|
||||
case VK_FORMAT_B8G8R8_SRGB:
|
||||
return 3;
|
||||
|
||||
// 32-bit formats.
|
||||
case VK_FORMAT_R32_UINT:
|
||||
case VK_FORMAT_R32_SINT:
|
||||
case VK_FORMAT_R32_SFLOAT:
|
||||
case VK_FORMAT_R16G16_UNORM:
|
||||
case VK_FORMAT_R16G16_SNORM:
|
||||
case VK_FORMAT_R16G16_USCALED:
|
||||
case VK_FORMAT_R16G16_SSCALED:
|
||||
case VK_FORMAT_R16G16_UINT:
|
||||
case VK_FORMAT_R16G16_SINT:
|
||||
case VK_FORMAT_R16G16_SFLOAT:
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
case VK_FORMAT_R8G8B8A8_SNORM:
|
||||
case VK_FORMAT_R8G8B8A8_USCALED:
|
||||
case VK_FORMAT_R8G8B8A8_SSCALED:
|
||||
case VK_FORMAT_R8G8B8A8_UINT:
|
||||
case VK_FORMAT_R8G8B8A8_SINT:
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_SNORM:
|
||||
case VK_FORMAT_B8G8R8A8_USCALED:
|
||||
case VK_FORMAT_B8G8R8A8_SSCALED:
|
||||
case VK_FORMAT_B8G8R8A8_UINT:
|
||||
case VK_FORMAT_B8G8R8A8_SINT:
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
|
||||
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
|
||||
// Depth and stencil formats.
|
||||
case VK_FORMAT_S8_UINT:
|
||||
case VK_FORMAT_D16_UNORM:
|
||||
case VK_FORMAT_X8_D24_UNORM_PACK32:
|
||||
case VK_FORMAT_D32_SFLOAT:
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
|
||||
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
|
||||
return 4;
|
||||
|
||||
// 48-bit formats.
|
||||
case VK_FORMAT_R16G16B16_UNORM:
|
||||
case VK_FORMAT_R16G16B16_SNORM:
|
||||
case VK_FORMAT_R16G16B16_USCALED:
|
||||
case VK_FORMAT_R16G16B16_SSCALED:
|
||||
case VK_FORMAT_R16G16B16_UINT:
|
||||
case VK_FORMAT_R16G16B16_SINT:
|
||||
case VK_FORMAT_R16G16B16_SFLOAT:
|
||||
return 6;
|
||||
|
||||
// 64-bit formats.
|
||||
case VK_FORMAT_R32G32_UINT:
|
||||
case VK_FORMAT_R32G32_SINT:
|
||||
case VK_FORMAT_R32G32_SFLOAT:
|
||||
case VK_FORMAT_R16G16B16A16_UNORM:
|
||||
case VK_FORMAT_R16G16B16A16_SNORM:
|
||||
case VK_FORMAT_R16G16B16A16_USCALED:
|
||||
case VK_FORMAT_R16G16B16A16_SSCALED:
|
||||
case VK_FORMAT_R16G16B16A16_UINT:
|
||||
case VK_FORMAT_R16G16B16A16_SINT:
|
||||
case VK_FORMAT_R16G16B16A16_SFLOAT:
|
||||
// Compressed formats.
|
||||
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC4_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC4_SNORM_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
|
||||
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
|
||||
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
|
||||
return 8;
|
||||
|
||||
// 96-bit formats.
|
||||
case VK_FORMAT_R32G32B32_UINT:
|
||||
case VK_FORMAT_R32G32B32_SINT:
|
||||
case VK_FORMAT_R32G32B32_SFLOAT:
|
||||
return 12;
|
||||
|
||||
// 128-bit formats.
|
||||
case VK_FORMAT_R32G32B32A32_UINT:
|
||||
case VK_FORMAT_R32G32B32A32_SINT:
|
||||
case VK_FORMAT_R32G32B32A32_SFLOAT:
|
||||
// Compressed formats.
|
||||
case VK_FORMAT_BC2_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC2_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC3_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC3_SRGB_BLOCK:
|
||||
case VK_FORMAT_BC5_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC5_SNORM_BLOCK:
|
||||
case VK_FORMAT_BC6H_SFLOAT_BLOCK:
|
||||
case VK_FORMAT_BC6H_UFLOAT_BLOCK:
|
||||
case VK_FORMAT_BC7_UNORM_BLOCK:
|
||||
case VK_FORMAT_BC7_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
|
||||
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
|
||||
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
|
||||
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
|
||||
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
|
||||
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
|
||||
return 16;
|
||||
|
||||
case VK_FORMAT_UNDEFINED:
|
||||
// In cases where we've explicitly already determined that the
|
||||
// format is not supported, let the rest of the system handle
|
||||
// that explicitly. Return '1' for no alignment.
|
||||
return 1;
|
||||
|
||||
default:
|
||||
// This is the case where there's a format that we added support
|
||||
// for in getVkFormat that we have not added support for in this
|
||||
// function, and should be treated as an error.
|
||||
assert_invariant(false && "Unknown data type, conversion is not supported.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
VkFormat getVkFormat(PixelDataFormat format, PixelDataType type) {
|
||||
if (type == PixelDataType::USHORT_565) return VK_FORMAT_R5G6B5_UNORM_PACK16;
|
||||
if (type == PixelDataType::UINT_2_10_10_10_REV) return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKAN_UTILS_CONVERSION_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKAN_UTILS_CONVERSION_H
|
||||
|
||||
#include "Definitions.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <private/backend/BackendUtils.h> // for getFormatSize()
|
||||
@@ -47,6 +49,9 @@ VkFormat getVkFormatLinear(VkFormat format);
|
||||
// than a driver-level Texture format, and can account for a specified byte alignment.
|
||||
uint32_t getBytesPerPixel(TextureFormat format);
|
||||
|
||||
// Required for aligning textures in staging buffers.
|
||||
uint8_t getTexelBlockSize(VkFormat format);
|
||||
|
||||
VkCompareOp getCompareOp(SamplerCompareFunc func);
|
||||
VkBlendFactor getBlendFactor(BlendFunction mode);
|
||||
VkCullModeFlags getCullMode(CullingMode mode);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKAN_UTILS_DEFINITIONS_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKAN_UTILS_DEFINITIONS_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
@@ -379,6 +381,81 @@ static constexpr uint8_t getFragmentStageShift() noexcept {
|
||||
// We have at most 4 descriptor sets. This is to indicate which ones are active.
|
||||
using DescriptorSetMask = utils::bitset8;
|
||||
|
||||
//! this API is copied from (and only applies to) the Vulkan spec.
|
||||
//! These specify YUV to RGB conversions.
|
||||
enum class SamplerYcbcrModelConversion : uint8_t {
|
||||
RGB_IDENTITY = 0,
|
||||
YCBCR_IDENTITY = 1,
|
||||
YCBCR_709 = 2,
|
||||
YCBCR_601 = 3,
|
||||
YCBCR_2020 = 4,
|
||||
};
|
||||
|
||||
enum class SamplerYcbcrRange : uint8_t {
|
||||
ITU_FULL = 0,
|
||||
ITU_NARROW = 1,
|
||||
};
|
||||
|
||||
enum class ChromaLocation : uint8_t {
|
||||
COSITED_EVEN = 0,
|
||||
MIDPOINT = 1,
|
||||
};
|
||||
|
||||
//! Sampler parameters
|
||||
struct SamplerYcbcrConversion { // NOLINT
|
||||
SamplerYcbcrModelConversion ycbcrModel : 4;
|
||||
TextureSwizzle r : 4;
|
||||
TextureSwizzle g : 4;
|
||||
TextureSwizzle b : 4;
|
||||
TextureSwizzle a : 4;
|
||||
SamplerYcbcrRange ycbcrRange : 1;
|
||||
ChromaLocation xChromaOffset : 1;
|
||||
ChromaLocation yChromaOffset : 1;
|
||||
SamplerMagFilter chromaFilter : 1;
|
||||
uint8_t padding;
|
||||
|
||||
struct Hasher {
|
||||
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
|
||||
// we don't use std::hash<> here, so we don't have to include <functional>
|
||||
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
|
||||
}
|
||||
};
|
||||
|
||||
struct EqualTo {
|
||||
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
|
||||
assert_invariant(lhs.padding == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs == *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct LessThan {
|
||||
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
|
||||
assert_invariant(lhs.padding == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs < *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
friend bool operator==(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) noexcept {
|
||||
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend bool operator!=(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) noexcept {
|
||||
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend bool operator<(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) noexcept {
|
||||
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SamplerYcbcrConversion) == 4);
|
||||
|
||||
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
|
||||
"SamplerYcbcrConversion must be no more than 64 bits");
|
||||
|
||||
} // namespace filament::backend::fvkutils
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKAN_UTILS_DEFINITIONS_H
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
#include "WebGPUDescriptorSetLayout.h"
|
||||
#include "WebGPUConstants.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
|
||||
@@ -18,11 +18,13 @@
|
||||
#include "WebGPUBufferObject.h"
|
||||
#include "WebGPUDescriptorSet.h"
|
||||
#include "WebGPUDescriptorSetLayout.h"
|
||||
#include "WebGPUFence.h"
|
||||
#include "WebGPUIndexBuffer.h"
|
||||
#include "WebGPUPipelineCreation.h"
|
||||
#include "WebGPUProgram.h"
|
||||
#include "WebGPURenderPrimitive.h"
|
||||
#include "WebGPURenderTarget.h"
|
||||
#include "WebGPUStrings.h"
|
||||
#include "WebGPUSwapChain.h"
|
||||
#include "WebGPUTexture.h"
|
||||
#include "WebGPUVertexBuffer.h"
|
||||
@@ -32,7 +34,6 @@
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
@@ -42,7 +43,7 @@
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Hash.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
@@ -54,6 +55,8 @@
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
@@ -69,7 +72,8 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
|
||||
mAdapter{ mPlatform.requestAdapter(nullptr) },
|
||||
mDevice{ mPlatform.requestDevice(mAdapter) },
|
||||
mQueue{ mDevice.GetQueue() },
|
||||
mMipMapGenerator{ mDevice },
|
||||
mRenderPassMipmapGenerator{ mDevice },
|
||||
mSpdComputePassMipmapGenerator{ mDevice },
|
||||
mHandleAllocator{ "Handles", driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags } {
|
||||
mDevice.GetLimits(&mDeviceLimits);
|
||||
@@ -102,6 +106,7 @@ void WebGPUDriver::terminate() {
|
||||
|
||||
void WebGPUDriver::tick(int) {
|
||||
mDevice.Tick();
|
||||
mAdapter.GetInstance().ProcessEvents();
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
@@ -263,7 +268,9 @@ Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||
}
|
||||
|
||||
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
|
||||
return Handle<HwFence>((Handle<HwFence>::HandleId) mNextFakeHandle++);
|
||||
// The handle must be constructed here, as a synchronous call to get the status
|
||||
// might happen before createFenceR is executed.
|
||||
return allocAndConstructHandle<WebGPUFence, HwFence>();
|
||||
}
|
||||
|
||||
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
|
||||
@@ -336,9 +343,6 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
mDevice, flags);
|
||||
assert_invariant(mSwapChain);
|
||||
|
||||
FWGPU_LOGW << "WebGPU support is highly experimental, in development, and tested for only a "
|
||||
"small set of simple samples (e.g. hellotriangle and texturedquad), thus issues "
|
||||
"are likely to be encountered at this stage.";
|
||||
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
|
||||
char printSystemHex[16];
|
||||
snprintf(printSystemHex, sizeof(printSystemHex), "%#x", FWGPU_PRINT_SYSTEM);
|
||||
@@ -450,15 +454,23 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> renderTarge
|
||||
}
|
||||
|
||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle,
|
||||
const TargetBufferFlags targets, const uint32_t width, const uint32_t height,
|
||||
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) {
|
||||
constructHandle<WebGPURenderTarget>(renderTargetHandle, width, height, samples, layerCount,
|
||||
color, depth, stencil);
|
||||
constructHandle<WebGPURenderTarget>(
|
||||
renderTargetHandle, width, height, samples, layerCount, color, depth, stencil,
|
||||
targetFlags,
|
||||
[&](const Handle<HwTexture> textureHandle) {
|
||||
return handleCast<WebGPUTexture>(textureHandle);
|
||||
},
|
||||
mDevice);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {
|
||||
//todo
|
||||
void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, const int /* dummy */) {
|
||||
// the handle was already constructed in createFenceS
|
||||
const auto fence = handleCast<WebGPUFence>(fenceHandle);
|
||||
assert_invariant(mQueue);
|
||||
fence->addMarkerToQueueState(mQueue);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
||||
@@ -511,13 +523,18 @@ void WebGPUDriver::updateStreams(CommandStream* driver) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyFence(Handle<HwFence> fh) {
|
||||
//todo
|
||||
void WebGPUDriver::destroyFence(Handle<HwFence> fenceHandle) {
|
||||
if (fenceHandle) {
|
||||
destructHandle<WebGPUFence>(fenceHandle);
|
||||
}
|
||||
}
|
||||
|
||||
FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fh) {
|
||||
//todo
|
||||
return FenceStatus::CONDITION_SATISFIED;
|
||||
FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fenceHandle) {
|
||||
const auto fence = handleCast<WebGPUFence>(fenceHandle);
|
||||
if (!fence) {
|
||||
return FenceStatus::ERROR;
|
||||
}
|
||||
return fence->getStatus();
|
||||
}
|
||||
|
||||
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
|
||||
@@ -607,15 +624,28 @@ uint8_t WebGPUDriver::getMaxDrawBuffers() {
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxUniformBufferSize() {
|
||||
return 16384u;
|
||||
return mDeviceLimits.maxUniformBufferBindingSize;
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxTextureSize(const SamplerType target) {
|
||||
return 2048u;
|
||||
size_t result = 2048u;
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
result = mDeviceLimits.maxTextureDimension2D;
|
||||
break;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
result = mDeviceLimits.maxTextureDimension3D;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxArrayTextureLayers() {
|
||||
return 256u;
|
||||
return mDeviceLimits.maxTextureArrayLayers;
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
@@ -694,27 +724,6 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
|
||||
|
||||
const uint8_t* dataBuff = static_cast<const uint8_t*>(data->buffer);
|
||||
size_t dataSize = data->size;
|
||||
std::unique_ptr<uint8_t[]> paddedBuffer;
|
||||
|
||||
if (bytesPerRow % 256 != 0) {
|
||||
uint32_t padding = 256 - (bytesPerRow % 256);
|
||||
uint32_t paddedBytesPerRow = bytesPerRow + padding;
|
||||
|
||||
size_t paddedBufferSize = static_cast<size_t>(paddedBytesPerRow) * height * depth;
|
||||
paddedBuffer = std::make_unique<uint8_t[]>(paddedBufferSize);
|
||||
uint8_t* dest = paddedBuffer.get();
|
||||
|
||||
for (uint32_t z = 0; z < depth; ++z) {
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
std::memcpy(dest, dataBuff, bytesPerRow);
|
||||
dest += paddedBytesPerRow;
|
||||
dataBuff += bytesPerRow;
|
||||
}
|
||||
}
|
||||
dataBuff = paddedBuffer.get();
|
||||
dataSize = paddedBufferSize;
|
||||
bytesPerRow = paddedBytesPerRow;
|
||||
}
|
||||
|
||||
auto layout = wgpu::TexelCopyBufferLayout{ .bytesPerRow = bytesPerRow, .rowsPerImage = height };
|
||||
|
||||
@@ -745,41 +754,58 @@ void WebGPUDriver::generateMipmaps(Handle<HwTexture> textureHandle) {
|
||||
wgpu::Texture wgpuTexture = texture->getTexture();
|
||||
assert_invariant(wgpuTexture);
|
||||
|
||||
const auto usage = wgpuTexture.GetUsage();
|
||||
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::TextureBinding)
|
||||
<< "Texture for mipmap generation must have TextureBinding usage. "
|
||||
"supportsMultipleMipLevels for texture format "
|
||||
<< static_cast<uint32_t>(wgpuTexture.GetFormat()) << " is "
|
||||
<< WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(wgpuTexture.GetFormat());
|
||||
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::StorageBinding)
|
||||
<< "Texture for mipmap generation must have StorageBinding usage. "
|
||||
"supportsMultipleMipLevels for texture format "
|
||||
<< static_cast<uint32_t>(wgpuTexture.GetFormat()) << " is "
|
||||
<< WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(wgpuTexture.GetFormat());
|
||||
|
||||
const uint32_t totalMipLevels = wgpuTexture.GetMipLevelCount();
|
||||
if (totalMipLevels <= 1) {
|
||||
return;
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
// We will record all passes into a single command encoder.
|
||||
wgpu::CommandEncoderDescriptor encoderDesc = {};
|
||||
encoderDesc.label = "Mipmap Command Encoder";
|
||||
wgpu::CommandEncoder encoder = mDevice.CreateCommandEncoder(&encoderDesc);
|
||||
const auto usage = wgpuTexture.GetUsage();
|
||||
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::TextureBinding)
|
||||
<< "Texture for mipmap generation must have TextureBinding usage.";
|
||||
|
||||
spd::SPDPassConfig spdConfig = { .filter = spd::SPDFilter::Average,
|
||||
.targetTexture = wgpuTexture,
|
||||
.numMips = totalMipLevels,
|
||||
.halfPrecision = false,
|
||||
.sourceMipLevel = 0 };
|
||||
switch (texture->getMipmapGenerationStrategy()) {
|
||||
case WebGPUTexture::MipmapGenerationStrategy::RENDER_PASS:
|
||||
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::RenderAttachment)
|
||||
<< "Texture for mipmap generation via render pass blit must have "
|
||||
"RenderAttachment usage.";
|
||||
mRenderPassMipmapGenerator.generateMipmaps(mQueue, texture->getTexture());
|
||||
break;
|
||||
case WebGPUTexture::MipmapGenerationStrategy::NONE:
|
||||
FWGPU_LOGW << "Trying to generate mipmaps for a texture we already know we cannot do "
|
||||
"such generation. Texture sample type "
|
||||
<< to_string(texture->target) << " WebGPU texture format "
|
||||
<< webGPUTextureFormatToString(texture->getViewFormat()) << " samples "
|
||||
<< static_cast<uint32_t>(texture->samples)
|
||||
<< ". Going to try using the SPD_COMPUTE_PASS approach.";
|
||||
UTILS_FALLTHROUGH;
|
||||
case WebGPUTexture::MipmapGenerationStrategy::SPD_COMPUTE_PASS:
|
||||
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::StorageBinding)
|
||||
<< "Texture for mipmap generation must have StorageBinding usage "
|
||||
"(as a compute pass strategy was selected for use). "
|
||||
"supportsMultipleMipLevels for WebGPU texture format "
|
||||
<< webGPUTextureFormatToString(wgpuTexture.GetFormat()) << " is "
|
||||
<< WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(
|
||||
wgpuTexture.GetFormat());
|
||||
// We will record all passes into a single command encoder.
|
||||
wgpu::CommandEncoderDescriptor encoderDesc = {};
|
||||
encoderDesc.label = "Mipmap Command Encoder";
|
||||
wgpu::CommandEncoder encoder = mDevice.CreateCommandEncoder(&encoderDesc);
|
||||
|
||||
mMipMapGenerator.Generate(encoder, wgpuTexture, spdConfig);
|
||||
spd::SPDPassConfig spdConfig = { .filter = spd::SPDFilter::Average,
|
||||
.targetTexture = wgpuTexture,
|
||||
.numMips = totalMipLevels,
|
||||
.halfPrecision = false,
|
||||
.sourceMipLevel = 0 };
|
||||
|
||||
// Finish the encoder and submit all the passes at once.
|
||||
wgpu::CommandBufferDescriptor cmdBufferDesc = {};
|
||||
cmdBufferDesc.label = "Mipmap Command Buffer";
|
||||
wgpu::CommandBuffer commandBuffer = encoder.Finish(&cmdBufferDesc);
|
||||
mQueue.Submit(1, &commandBuffer);
|
||||
mSpdComputePassMipmapGenerator.Generate(encoder, wgpuTexture, spdConfig);
|
||||
|
||||
// Finish the encoder and submit all the passes at once.
|
||||
wgpu::CommandBufferDescriptor cmdBufferDesc = {};
|
||||
cmdBufferDesc.label = "Mipmap Command Buffer";
|
||||
wgpu::CommandBuffer commandBuffer = encoder.Finish(&cmdBufferDesc);
|
||||
mQueue.Submit(1, &commandBuffer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
@@ -798,12 +824,16 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
wgpu::TextureView defaultDepthStencilView = nullptr;
|
||||
wgpu::TextureFormat defaultDepthStencilFormat = wgpu::TextureFormat::Undefined;
|
||||
|
||||
const bool msaaSidecarsRequired{ renderTarget->getSamples() > 1 &&
|
||||
renderTarget->getSampleCountPerAttachment() <= 1 };
|
||||
|
||||
std::array<wgpu::TextureView, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT> customColorViews{};
|
||||
std::array<wgpu::TextureView, customColorViews.size()> customColorMsaaSidecarViews{};
|
||||
uint32_t customColorViewCount = 0;
|
||||
wgpu::TextureView customDepthView = nullptr;
|
||||
wgpu::TextureFormat customDepthFormat = wgpu::TextureFormat::Undefined;
|
||||
wgpu::TextureFormat customStencilFormat = wgpu::TextureFormat::Undefined;
|
||||
wgpu::TextureView customStencilView = nullptr;
|
||||
|
||||
wgpu::TextureView customDepthStencilView = nullptr;
|
||||
wgpu::TextureView customDepthStencilMsaaSidecarTextureView = nullptr;
|
||||
wgpu::TextureFormat customDepthStencilFormat = wgpu::TextureFormat::Undefined;
|
||||
|
||||
mCurrentRenderTarget = renderTarget;
|
||||
if (renderTarget->isDefaultRenderTarget()) {
|
||||
@@ -811,6 +841,20 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
defaultColorView = mTextureView;
|
||||
defaultDepthStencilView = mSwapChain->getDepthTextureView();
|
||||
defaultDepthStencilFormat = mSwapChain->getDepthFormat();
|
||||
|
||||
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::STENCIL) &&
|
||||
!(hasStencil(defaultDepthStencilFormat))) {
|
||||
FILAMENT_CHECK_POSTCONDITION(false)
|
||||
<< "Default render target requested stencil, but swap chain's depth format "
|
||||
<< (uint32_t) defaultDepthStencilFormat << " does not have a stencil aspect.";
|
||||
}
|
||||
|
||||
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::DEPTH) &&
|
||||
!(hasDepth(defaultDepthStencilFormat))) {
|
||||
FILAMENT_CHECK_POSTCONDITION(false)
|
||||
<< "Default render target requested depth, but swap chain's depth format "
|
||||
<< (uint32_t) defaultDepthStencilFormat << " does not have a depth aspect.";
|
||||
}
|
||||
} else {
|
||||
// Resolve views for custom render target
|
||||
const auto& colorInfos = renderTarget->getColorAttachmentInfos();
|
||||
@@ -825,41 +869,78 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
<< ".";
|
||||
const uint8_t mipLevel = colorInfos[i].level;
|
||||
const uint32_t arrayLayer = colorInfos[i].layer;
|
||||
customColorViews[customColorViewCount++] =
|
||||
customColorViews[customColorViewCount] =
|
||||
colorTexture->getOrMakeTextureView(mipLevel, arrayLayer);
|
||||
if (msaaSidecarsRequired) {
|
||||
const wgpu::TextureView msaaSidecarView{
|
||||
colorTexture->makeMsaaSidecarTextureViewIfTextureSidecarExists(
|
||||
renderTarget->getSamples(), mipLevel, arrayLayer)
|
||||
};
|
||||
FILAMENT_CHECK_POSTCONDITION(msaaSidecarView)
|
||||
<< "Could not get a required MSAA sidecar texture view for color "
|
||||
<< customColorViewCount << "?";
|
||||
customColorMsaaSidecarViews[customColorViewCount] = msaaSidecarView;
|
||||
}
|
||||
customColorViewCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto& depthInfo = renderTarget->getDepthAttachmentInfo();
|
||||
if (depthInfo.handle) {
|
||||
FILAMENT_CHECK_POSTCONDITION(depthInfo.layer < renderTarget->getLayerCount())
|
||||
<< "Depth attachment requests layer " << depthInfo.layer
|
||||
<< "but render target has only " << renderTarget->getLayerCount() << ".";
|
||||
auto depthTexture = handleCast<WebGPUTexture>(depthInfo.handle);
|
||||
if (depthTexture) {
|
||||
const uint8_t depthMipLevel = depthInfo.level;
|
||||
const uint32_t depthArrayLayer = depthInfo.layer;
|
||||
customDepthView =
|
||||
depthTexture->getOrMakeTextureView(depthMipLevel, depthArrayLayer);
|
||||
customDepthFormat = depthTexture->getViewFormat();
|
||||
}
|
||||
const auto& stencilInfo = renderTarget->getStencilAttachmentInfo();
|
||||
|
||||
Handle<HwTexture> depthStencilSourceHandle = {};
|
||||
uint8_t depthStencilMipLevel = 0;
|
||||
uint32_t depthStencilArrayLayer = 0;
|
||||
|
||||
if (depthInfo.handle && stencilInfo.handle) {
|
||||
auto const depthTexture{handleCast<WebGPUTexture>(depthInfo.handle)->getTexture()};
|
||||
auto const stencilTexture{handleCast<WebGPUTexture>(stencilInfo.handle)->getTexture()};
|
||||
FILAMENT_CHECK_POSTCONDITION(depthTexture.Get() == stencilTexture.Get())
|
||||
<< "Filament must reference the same resource if both depth and stencil "
|
||||
"textures are present";
|
||||
}
|
||||
|
||||
const auto& stencilInfo = renderTarget->getStencilAttachmentInfo();
|
||||
if (stencilInfo.handle) {
|
||||
// If depth and stencil use the same texture handle, this will re-cast but that's fine.
|
||||
auto stencilTexture = handleCast<WebGPUTexture>(stencilInfo.handle);
|
||||
if (stencilTexture) {
|
||||
FILAMENT_CHECK_POSTCONDITION(stencilInfo.layer < renderTarget->getLayerCount())
|
||||
<< "Stencil attachment requests layer " << stencilInfo.layer
|
||||
<< " but render target has only " << renderTarget->getLayerCount()
|
||||
<< " layers.";
|
||||
const uint8_t stencilMipLevel = stencilInfo.level;
|
||||
const uint32_t stencilArrayLayer = stencilInfo.layer;
|
||||
customStencilView =
|
||||
stencilTexture->getOrMakeTextureView(stencilMipLevel, stencilArrayLayer);
|
||||
customStencilFormat = stencilTexture->getViewFormat();
|
||||
if (depthInfo.handle) {
|
||||
depthStencilSourceHandle = depthInfo.handle;
|
||||
depthStencilMipLevel = depthInfo.level;
|
||||
depthStencilArrayLayer = depthInfo.layer;
|
||||
} else if (stencilInfo.handle) {
|
||||
depthStencilSourceHandle = stencilInfo.handle;
|
||||
depthStencilMipLevel = stencilInfo.level;
|
||||
depthStencilArrayLayer = stencilInfo.layer;
|
||||
}
|
||||
|
||||
if (depthStencilSourceHandle) {
|
||||
auto dsTexture = handleCast<WebGPUTexture>(depthStencilSourceHandle);
|
||||
if (dsTexture) {
|
||||
customDepthStencilView = dsTexture->getOrMakeTextureView(depthStencilMipLevel,
|
||||
depthStencilArrayLayer);
|
||||
if (msaaSidecarsRequired) {
|
||||
customDepthStencilMsaaSidecarTextureView =
|
||||
dsTexture->makeMsaaSidecarTextureViewIfTextureSidecarExists(
|
||||
renderTarget->getSamples(), depthStencilMipLevel,
|
||||
depthStencilArrayLayer);
|
||||
FILAMENT_CHECK_POSTCONDITION(customDepthStencilMsaaSidecarTextureView)
|
||||
<< "Could not get a required MSAA sidecar texture view for "
|
||||
"depth/stencil?";
|
||||
}
|
||||
customDepthStencilFormat = dsTexture->getViewFormat();
|
||||
|
||||
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::STENCIL) &&
|
||||
!(hasStencil(customDepthStencilFormat))) {
|
||||
FILAMENT_CHECK_POSTCONDITION(false)
|
||||
<< "Custom render target requested stencil, but the provided texture"
|
||||
"format number"
|
||||
<< (uint32_t) customDepthStencilFormat
|
||||
<< " does not have a stencil aspect.";
|
||||
}
|
||||
if (any(renderTarget->getTargetFlags() & TargetBufferFlags::DEPTH) &&
|
||||
!(hasDepth(customDepthStencilFormat))) {
|
||||
FILAMENT_CHECK_POSTCONDITION(false) << "Custom render target requested depth, "
|
||||
"but the provided texture format number"
|
||||
<< (uint32_t) customDepthStencilFormat
|
||||
<< " does not have a depth aspect.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -868,13 +949,11 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
params,
|
||||
defaultColorView,
|
||||
defaultDepthStencilView,
|
||||
defaultDepthStencilFormat,
|
||||
customColorViews.data(),
|
||||
customColorMsaaSidecarViews.data(),
|
||||
customColorViewCount,
|
||||
customDepthView,
|
||||
customStencilView,
|
||||
customDepthFormat,
|
||||
customStencilFormat);
|
||||
customDepthStencilView,
|
||||
customDepthStencilMsaaSidecarTextureView);
|
||||
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||
|
||||
@@ -909,9 +988,25 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
||||
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
|
||||
mTextureView = mSwapChain->getCurrentSurfaceTextureView(surfaceSize);
|
||||
assert_invariant(mTextureView);
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
|
||||
.label = "command_encoder"
|
||||
};
|
||||
|
||||
assert_invariant(mDefaultRenderTarget);
|
||||
|
||||
wgpu::TextureFormat depthFormat = mSwapChain->getDepthFormat();
|
||||
TargetBufferFlags newTargetFlags = filament::backend::TargetBufferFlags::NONE;
|
||||
|
||||
//Assuming Color always present in default render target.
|
||||
newTargetFlags |= filament::backend::TargetBufferFlags::COLOR;
|
||||
if (depthFormat != wgpu::TextureFormat::Undefined) {
|
||||
if (hasDepth(depthFormat)) {
|
||||
newTargetFlags |= filament::backend::TargetBufferFlags::DEPTH;
|
||||
}
|
||||
if (hasStencil(depthFormat)) {
|
||||
newTargetFlags |= filament::backend::TargetBufferFlags::STENCIL;
|
||||
}
|
||||
}
|
||||
mDefaultRenderTarget->setTargetFlags(newTargetFlags);
|
||||
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = { .label = "frame_command_encoder" };
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
assert_invariant(mCommandEncoder);
|
||||
}
|
||||
@@ -924,6 +1019,22 @@ void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
|
||||
assert_invariant(mCommandBuffer);
|
||||
mCommandEncoder = nullptr;
|
||||
mQueue.Submit(1, &mCommandBuffer);
|
||||
|
||||
static bool firstRender = true;
|
||||
// For the first frame rendered, we need to make sure the work is done before presenting or we
|
||||
// get a purple flash
|
||||
if (firstRender) {
|
||||
auto f = mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
|
||||
[=](wgpu::QueueWorkDoneStatus) {});
|
||||
const wgpu::Instance instance = mAdapter.GetInstance();
|
||||
auto wStatus = instance.WaitAny(f,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count());
|
||||
if (wStatus != wgpu::WaitStatus::Success) {
|
||||
FWGPU_LOGW << "Waiting for first frame work to finish resulted in an error"
|
||||
<< static_cast<uint32_t>(wStatus);
|
||||
}
|
||||
firstRender = false;
|
||||
}
|
||||
mCommandBuffer = nullptr;
|
||||
mTextureView = nullptr;
|
||||
assert_invariant(mSwapChain);
|
||||
@@ -976,7 +1087,33 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
void WebGPUDriver::resolve(Handle<HwTexture> destinationTextureHandle, const uint8_t sourceLevel,
|
||||
const uint8_t sourceLayer, Handle<HwTexture> sourceTextureHandle,
|
||||
const uint8_t destinationLevel, const uint8_t destinationLayer) {
|
||||
// todo
|
||||
FILAMENT_CHECK_PRECONDITION(mCommandEncoder)
|
||||
<< "Resolve assumes there is a valid command encoder to piggyback on.";
|
||||
FILAMENT_CHECK_PRECONDITION(mRenderPassEncoder == nullptr)
|
||||
<< "Resolve cannot be called during an existing render pass";
|
||||
const auto sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
|
||||
const auto destinationTexture{ handleCast<WebGPUTexture>(destinationTextureHandle) };
|
||||
assert_invariant(sourceTexture);
|
||||
assert_invariant(destinationTexture);
|
||||
const WebGPUMsaaTextureResolver::ResolveRequest request{
|
||||
.commandEncoder = mCommandEncoder,
|
||||
.viewFormat = sourceTexture->getViewFormat(),
|
||||
.source = {
|
||||
.texture = sourceTexture->getTexture(),
|
||||
.viewDimension = sourceTexture->getViewDimension(),
|
||||
.mipLevel = sourceLevel,
|
||||
.layer = sourceLayer,
|
||||
.aspect = sourceTexture->getAspect(),
|
||||
},
|
||||
.destination = {
|
||||
.texture = destinationTexture->getTexture(),
|
||||
.viewDimension = destinationTexture->getViewDimension(),
|
||||
.mipLevel = destinationLevel,
|
||||
.layer = destinationLayer,
|
||||
.aspect = destinationTexture->getAspect(),
|
||||
},
|
||||
};
|
||||
mMsaaTextureResolver.resolve(request);
|
||||
}
|
||||
|
||||
void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_t sourceLevel,
|
||||
@@ -986,13 +1123,24 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
|
||||
// todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
size_t WebGPUDriver::computePipelineKey(PipelineState const& pipelineState,
|
||||
WebGPURenderTarget const* const renderTarget) const {
|
||||
// TODO Investigate implications of this hash more closely. Vulkan has a whole class
|
||||
// VulkanPipelineCache to handle this, may be missing nuance
|
||||
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
|
||||
auto hash = pipleineStateHasher(pipelineState);
|
||||
if (mPipelineMap.find(hash) != mPipelineMap.end()) {
|
||||
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
|
||||
// VulkanPipelineCache to handle this, may be missing nuance
|
||||
static const auto pipelineStateHasher{
|
||||
utils::hash::MurmurHashFn<filament::backend::PipelineState>()
|
||||
};
|
||||
const std::hash<uint32_t> intHasher{};
|
||||
const std::hash<WebGPURenderTarget const*> addressHasher{};
|
||||
const size_t pipelineStateHash{ intHasher(pipelineStateHasher(pipelineState)) };
|
||||
const size_t renderTargetHash{ addressHasher(renderTarget) };
|
||||
return utils::hash::combine(pipelineStateHash, renderTargetHash);
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
auto pipelineKey{ computePipelineKey(pipelineState, mCurrentRenderTarget) };
|
||||
if (mPipelineMap.find(pipelineKey) != mPipelineMap.end()) {
|
||||
mRenderPassEncoder.SetPipeline(mPipelineMap[pipelineKey]);
|
||||
return;
|
||||
}
|
||||
const auto program = handleCast<WebGPUProgram>(pipelineState.program);
|
||||
@@ -1030,14 +1178,15 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
assert_invariant(vertexBufferInfo);
|
||||
|
||||
std::vector<wgpu::TextureFormat> pipelineColorFormats;
|
||||
wgpu::TextureFormat pipelineDepthFormat = wgpu::TextureFormat::Undefined;
|
||||
wgpu::TextureFormat pipelineDepthStencilFormat = wgpu::TextureFormat::Undefined;
|
||||
uint8_t pipelineSamples = 1;
|
||||
bool const requestedDepth = any(mCurrentRenderTarget->getTargetFlags() & TargetBufferFlags::DEPTH);
|
||||
bool const requestedStencil = any(mCurrentRenderTarget->getTargetFlags() & TargetBufferFlags::STENCIL);
|
||||
pipelineSamples = mCurrentRenderTarget->getSamples();
|
||||
|
||||
if (mCurrentRenderTarget->isDefaultRenderTarget()) {
|
||||
pipelineColorFormats.push_back(mSwapChain->getColorFormat());
|
||||
pipelineDepthFormat = mSwapChain->getDepthFormat();
|
||||
pipelineSamples =
|
||||
mCurrentRenderTarget->getSamples();// Default RT should have samples (usually 1)
|
||||
pipelineDepthStencilFormat = mSwapChain->getDepthFormat();
|
||||
} else {
|
||||
const auto& mrtColorAttachments = mCurrentRenderTarget->getColorAttachmentInfos();
|
||||
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
|
||||
@@ -1051,21 +1200,19 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
|
||||
const auto& depthInfo = mCurrentRenderTarget->getDepthAttachmentInfo();
|
||||
const auto& stencilInfo = mCurrentRenderTarget->getStencilAttachmentInfo();
|
||||
Handle<HwTexture> depthStencilHandle = {};
|
||||
if (depthInfo.handle) {
|
||||
FILAMENT_CHECK_POSTCONDITION(!stencilInfo.handle)
|
||||
<< "depth and stencil attachments cannot both be provided for WebGPU";
|
||||
const auto depthTexture = handleCast<WebGPUTexture>(depthInfo.handle);
|
||||
if (depthTexture) pipelineDepthFormat = depthTexture->getTexture().GetFormat();
|
||||
} else {
|
||||
if (stencilInfo.handle) {
|
||||
const auto stencilTexture = handleCast<WebGPUTexture>(stencilInfo.handle);
|
||||
// Assuming combined depth/stencil format if only stencil is present
|
||||
if (stencilTexture) {
|
||||
pipelineDepthFormat = stencilTexture->getTexture().GetFormat();
|
||||
}
|
||||
depthStencilHandle = depthInfo.handle;
|
||||
} else if (stencilInfo.handle) {
|
||||
depthStencilHandle = stencilInfo.handle;
|
||||
}
|
||||
|
||||
if (depthStencilHandle) {
|
||||
const auto dsTexture = handleCast<WebGPUTexture>(depthStencilHandle);
|
||||
if (dsTexture) {
|
||||
pipelineDepthStencilFormat = dsTexture->getTexture().GetFormat();
|
||||
}
|
||||
}
|
||||
pipelineSamples = mCurrentRenderTarget->getSamples();
|
||||
}
|
||||
|
||||
// TODO: We expected this to be a sane check, however it complains when running shadowtest.
|
||||
@@ -1077,9 +1224,9 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program, *vertexBufferInfo,
|
||||
layout, pipelineState.rasterState, pipelineState.stencilState,
|
||||
pipelineState.polygonOffset, pipelineState.primitiveType, pipelineColorFormats,
|
||||
pipelineDepthFormat, pipelineSamples);
|
||||
pipelineDepthStencilFormat, pipelineSamples, requestedDepth, requestedStencil);
|
||||
assert_invariant(pipeline);
|
||||
mPipelineMap[hash] = pipeline;
|
||||
mPipelineMap[pipelineKey] = pipeline;
|
||||
mRenderPassEncoder.SetPipeline(pipeline);
|
||||
}
|
||||
|
||||
@@ -1167,7 +1314,6 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> descriptorS
|
||||
void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> descriptorSetHandle,
|
||||
const backend::descriptor_binding_t binding, Handle<HwTexture> textureHandle,
|
||||
const SamplerParams params) {
|
||||
|
||||
auto bindGroup = handleCast<WebGPUDescriptorSet>(descriptorSetHandle);
|
||||
auto texture = handleCast<WebGPUTexture>(textureHandle);
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include "WebGPURenderTarget.h"
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
#include "webgpu/WebGPUMsaaTextureResolver.h"
|
||||
#include "webgpu/WebGPURenderPassMipmapGenerator.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
@@ -55,7 +57,7 @@ public:
|
||||
[[nodiscard]] static Driver* create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
|
||||
private:
|
||||
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept;
|
||||
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
|
||||
@@ -77,9 +79,11 @@ private:
|
||||
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
||||
WebGPURenderTarget* mDefaultRenderTarget = nullptr;
|
||||
WebGPURenderTarget* mCurrentRenderTarget = nullptr;
|
||||
spd::MipmapGenerator mMipMapGenerator;
|
||||
WebGPURenderPassMipmapGenerator mRenderPassMipmapGenerator;
|
||||
spd::MipmapGenerator mSpdComputePassMipmapGenerator;
|
||||
WebGPUMsaaTextureResolver mMsaaTextureResolver{};
|
||||
|
||||
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
|
||||
tsl::robin_map<size_t, wgpu::RenderPipeline> mPipelineMap;
|
||||
|
||||
struct DescriptorSetBindingInfo{
|
||||
wgpu::BindGroup bindGroup;
|
||||
@@ -87,6 +91,9 @@ private:
|
||||
backend::DescriptorSetOffsetArray offsets;
|
||||
};
|
||||
std::array<DescriptorSetBindingInfo,MAX_DESCRIPTOR_SET_COUNT> mCurrentDescriptorSets;
|
||||
|
||||
[[nodiscard]] size_t computePipelineKey(PipelineState const&, WebGPURenderTarget const*) const;
|
||||
|
||||
/*
|
||||
* Driver interface
|
||||
*/
|
||||
@@ -121,6 +128,11 @@ private:
|
||||
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
|
||||
}
|
||||
|
||||
template<typename D, typename B, typename... ARGS>
|
||||
Handle<B> allocAndConstructHandle(ARGS&&... args) {
|
||||
return mHandleAllocator.allocateAndConstruct<D>(std::forward<ARGS>(args)...);
|
||||
}
|
||||
|
||||
template<typename D, typename B>
|
||||
D* handleCast(Handle<B> handle) noexcept {
|
||||
return mHandleAllocator.handle_cast<D*>(handle);
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 "WebGPUFence.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
FenceStatus WebGPUFence::getStatus() { return mStatus.load(); }
|
||||
|
||||
void WebGPUFence::addMarkerToQueueState(wgpu::Queue const& queue) {
|
||||
queue.OnSubmittedWorkDone(
|
||||
wgpu::CallbackMode::AllowSpontaneous,
|
||||
[](const wgpu::QueueWorkDoneStatus status,
|
||||
std::atomic<FenceStatus>* const fenceStatus) {
|
||||
if (!fenceStatus) {
|
||||
return;
|
||||
}
|
||||
switch (status) {
|
||||
case wgpu::QueueWorkDoneStatus::Success:
|
||||
fenceStatus->store(FenceStatus::CONDITION_SATISFIED);
|
||||
break;
|
||||
case wgpu::QueueWorkDoneStatus::CallbackCancelled:
|
||||
case wgpu::QueueWorkDoneStatus::Error:
|
||||
fenceStatus->store(FenceStatus::ERROR);
|
||||
break;
|
||||
}
|
||||
},
|
||||
&mStatus);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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_WEBGPUFENCE_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUFENCE_H
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace wgpu {
|
||||
class Queue;
|
||||
} // namespace wgpu
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUFence final : public HwFence {
|
||||
public:
|
||||
[[nodiscard]] FenceStatus getStatus();
|
||||
|
||||
void addMarkerToQueueState(wgpu::Queue const&);
|
||||
|
||||
private:
|
||||
std::atomic<FenceStatus> mStatus{ FenceStatus::TIMEOUT_EXPIRED };
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUFENCE_H
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 "WebGPUMsaaTextureResolver.h"
|
||||
|
||||
#include "WebGPUTexture.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
void resolveColorTextures(wgpu::CommandEncoder const& commandEncoder,
|
||||
wgpu::TextureView const& sourceTextureView,
|
||||
wgpu::TextureView const& destinationTextureView) {
|
||||
const wgpu::RenderPassColorAttachment colorAttachment{
|
||||
.view = sourceTextureView,
|
||||
.depthSlice = wgpu::kDepthSliceUndefined, // being explicit for consistent behavior
|
||||
.resolveTarget = destinationTextureView,
|
||||
.loadOp = wgpu::LoadOp::Load,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
.clearValue = {}, // being explicit for consistent behavior
|
||||
};
|
||||
const wgpu::RenderPassDescriptor renderPassDescriptor{
|
||||
.label = "resolve_render_pass",
|
||||
.colorAttachmentCount = 1,
|
||||
.colorAttachments = &colorAttachment,
|
||||
.depthStencilAttachment = nullptr, // being explicit for consistent behavior
|
||||
.occlusionQuerySet = nullptr, // being explicit for consistent behavior
|
||||
.timestampWrites = nullptr, // being explicit for consistent behavior
|
||||
};
|
||||
const wgpu::RenderPassEncoder renderPassEncoder{ commandEncoder.BeginRenderPass(
|
||||
&renderPassDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(renderPassEncoder)
|
||||
<< "Failed to create wgpu::RenderPassEncoder for WebGPUDriver::resolve";
|
||||
renderPassEncoder.End(); // only the implicit resolve is happening in the pass
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void WebGPUMsaaTextureResolver::resolve(ResolveRequest const& request) {
|
||||
ResolveRequest::TextureInfo const& source{ request.source };
|
||||
ResolveRequest::TextureInfo const& destination{ request.destination };
|
||||
FILAMENT_CHECK_PRECONDITION(destination.texture.GetWidth() == source.texture.GetWidth() &&
|
||||
destination.texture.GetHeight() == source.texture.GetHeight())
|
||||
<< "invalid resolve: source and destination sizes don't match";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
source.texture.GetSampleCount() > 1 && destination.texture.GetSampleCount() == 1)
|
||||
<< "invalid resolve: source.samples=" << source.texture.GetSampleCount()
|
||||
<< ", destination.samples=" << destination.texture.GetSampleCount();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(source.texture.GetFormat() == destination.texture.GetFormat())
|
||||
<< "source and destination texture format don't match";
|
||||
const wgpu::TextureFormat format{ source.texture.GetFormat() };
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!hasDepth(format)) << "can't resolve depth formats";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(!hasStencil(format)) << "can't resolve stencil formats";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(source.texture.GetUsage() & wgpu::TextureUsage::RenderAttachment)
|
||||
<< "source texture usage doesn't have wgpu::TextureUsage::RenderAttachment";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
destination.texture.GetUsage() & wgpu::TextureUsage::RenderAttachment)
|
||||
<< "destination texture usage doesn't have wgpu::TextureUsage::RenderAttachment";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(destination.texture.GetUsage() & wgpu::TextureUsage::TextureBinding)
|
||||
<< "destination texture usage doesn't have wgpu::TextureUsage::TextureBinding";
|
||||
|
||||
const wgpu::TextureViewDescriptor sourceTextureViewDescriptor{
|
||||
.label = "resolve_source_texture_view",
|
||||
.format = request.viewFormat,
|
||||
.dimension = source.viewDimension,
|
||||
.baseMipLevel = source.mipLevel,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = source.layer,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = source.aspect,
|
||||
.usage = source.texture.GetUsage(),
|
||||
};
|
||||
const wgpu::TextureView sourceTextureView{ source.texture.CreateView(
|
||||
&sourceTextureViewDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(sourceTextureView)
|
||||
<< "Failed to create wgpu::TextureView sourceTextureView";
|
||||
const wgpu::TextureViewDescriptor destinationTextureViewDescriptor{
|
||||
.label = "resolve_destination_texture_view",
|
||||
.format = request.viewFormat,
|
||||
.dimension = destination.viewDimension,
|
||||
.baseMipLevel = destination.mipLevel,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = destination.layer,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = destination.aspect,
|
||||
.usage = destination.texture.GetUsage(),
|
||||
};
|
||||
const wgpu::TextureView destinationTextureView{ destination.texture.CreateView(
|
||||
&destinationTextureViewDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(destinationTextureView)
|
||||
<< "Failed to create wgpu::TextureView destinationTextureView.";
|
||||
|
||||
if (hasDepth(format)) {
|
||||
PANIC_PRECONDITION("DEPTH RESOLVE NOT IMPLEMENTED YET");
|
||||
} else {
|
||||
resolveColorTextures(request.commandEncoder, sourceTextureView, destinationTextureView);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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_WEBGPUMSAATEXTURERESOLVER_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUMSAATEXTURERESOLVER_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace wgpu {
|
||||
class CommandEncoder;
|
||||
class Texture;
|
||||
enum class TextureAspect : uint32_t;
|
||||
enum class TextureFormat : uint32_t;
|
||||
enum class TextureViewDimension : uint32_t;
|
||||
} // namespace wgpu
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUTexture;
|
||||
|
||||
class WebGPUMsaaTextureResolver final {
|
||||
public:
|
||||
struct ResolveRequest final {
|
||||
struct TextureInfo final {
|
||||
wgpu::Texture const& texture;
|
||||
wgpu::TextureViewDimension viewDimension;
|
||||
uint8_t mipLevel;
|
||||
uint8_t layer;
|
||||
wgpu::TextureAspect aspect;
|
||||
};
|
||||
wgpu::CommandEncoder const& commandEncoder;
|
||||
wgpu::TextureFormat viewFormat;
|
||||
TextureInfo source;
|
||||
TextureInfo destination;
|
||||
};
|
||||
|
||||
void resolve(ResolveRequest const&);
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUMSAATEXTURERESOLVER_H
|
||||
@@ -169,39 +169,52 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
wgpu::PipelineLayout const& layout, RasterState const& rasterState,
|
||||
StencilState const& stencilState, PolygonOffset const& polygonOffset,
|
||||
const PrimitiveType primitiveType, std::vector<wgpu::TextureFormat> const& colorFormats,
|
||||
const wgpu::TextureFormat depthFormat, const uint8_t samplesCount) {
|
||||
wgpu::TextureFormat const& depthStencilFormat, const uint8_t samplesCount, const bool requestedDepth, const bool requestedStencil) {
|
||||
assert_invariant(program.vertexShaderModule);
|
||||
wgpu::DepthStencilState depthStencilState{};
|
||||
if (depthFormat != wgpu::TextureFormat::Undefined) {
|
||||
depthStencilState.format = depthFormat;
|
||||
depthStencilState.depthWriteEnabled = rasterState.depthWrite;
|
||||
depthStencilState.depthCompare = toWebGPU(rasterState.depthFunc);
|
||||
depthStencilState.stencilFront = {
|
||||
.compare = toWebGPU(stencilState.front.stencilFunc),
|
||||
.failOp = toWebGPU(stencilState.front.stencilOpStencilFail),
|
||||
.depthFailOp = toWebGPU(stencilState.front.stencilOpDepthFail),
|
||||
.passOp = toWebGPU(stencilState.front.stencilOpDepthStencilPass),
|
||||
};
|
||||
depthStencilState.stencilBack = {
|
||||
.compare = toWebGPU(stencilState.back.stencilFunc),
|
||||
.failOp = toWebGPU(stencilState.back.stencilOpStencilFail),
|
||||
.depthFailOp = toWebGPU(stencilState.back.stencilOpDepthFail),
|
||||
.passOp = toWebGPU(stencilState.back.stencilOpDepthStencilPass),
|
||||
};
|
||||
depthStencilState.stencilReadMask =
|
||||
stencilState.front.readMask; // Use front face's comparison mask for read mask
|
||||
depthStencilState.stencilWriteMask = stencilState.stencilWrite ? 0xFFFFFFFF : 0u;
|
||||
depthStencilState.depthBias = static_cast<int32_t>(polygonOffset.constant);
|
||||
depthStencilState.depthBiasSlopeScale = polygonOffset.slope;
|
||||
depthStencilState.depthBiasClamp = 0.0f;
|
||||
const bool depthOrStencilRequested = (requestedDepth || requestedStencil);
|
||||
|
||||
if (!hasStencilAspect(depthFormat)) {
|
||||
depthStencilState.stencilFront.compare = wgpu::CompareFunction::Always;
|
||||
if (depthOrStencilRequested) {
|
||||
FILAMENT_CHECK_PRECONDITION(depthStencilFormat != wgpu::TextureFormat::Undefined)
|
||||
<< "Depth or Stencil requested for pipeline, but depthStencilFormat is "
|
||||
"wgpu::TextureFormat::Undefined.";
|
||||
depthStencilState.format = depthStencilFormat;
|
||||
|
||||
if (requestedDepth) {
|
||||
depthStencilState.depthWriteEnabled = rasterState.depthWrite;
|
||||
depthStencilState.depthCompare = toWebGPU(rasterState.depthFunc);
|
||||
depthStencilState.depthBias = static_cast<int32_t>(polygonOffset.constant);
|
||||
depthStencilState.depthBiasSlopeScale = polygonOffset.slope;
|
||||
depthStencilState.depthBiasClamp = 0.0f;
|
||||
} else {
|
||||
depthStencilState.depthWriteEnabled = false;
|
||||
depthStencilState.depthCompare = wgpu::CompareFunction::Undefined;
|
||||
depthStencilState.depthBias = 0;
|
||||
depthStencilState.depthBiasSlopeScale = 0.0f;
|
||||
depthStencilState.depthBiasClamp = 0.0f;
|
||||
}
|
||||
|
||||
if (hasStencilAspect(depthStencilFormat) && requestedStencil) {
|
||||
depthStencilState.stencilFront = {
|
||||
.compare = toWebGPU(stencilState.front.stencilFunc),
|
||||
.failOp = toWebGPU(stencilState.front.stencilOpStencilFail),
|
||||
.depthFailOp = toWebGPU(stencilState.front.stencilOpDepthFail),
|
||||
.passOp = toWebGPU(stencilState.front.stencilOpDepthStencilPass),
|
||||
};
|
||||
depthStencilState.stencilBack = {
|
||||
.compare = toWebGPU(stencilState.back.stencilFunc),
|
||||
.failOp = toWebGPU(stencilState.back.stencilOpStencilFail),
|
||||
.depthFailOp = toWebGPU(stencilState.back.stencilOpDepthFail),
|
||||
.passOp = toWebGPU(stencilState.back.stencilOpDepthStencilPass),
|
||||
};
|
||||
depthStencilState.stencilReadMask = stencilState.front.readMask;
|
||||
depthStencilState.stencilWriteMask = stencilState.stencilWrite ? stencilState.front.writeMask : 0u;
|
||||
} else {
|
||||
depthStencilState.stencilFront.compare = wgpu::CompareFunction::Undefined;
|
||||
depthStencilState.stencilFront.failOp = wgpu::StencilOperation::Keep;
|
||||
depthStencilState.stencilFront.depthFailOp = wgpu::StencilOperation::Keep;
|
||||
depthStencilState.stencilFront.passOp = wgpu::StencilOperation::Keep;
|
||||
depthStencilState.stencilBack =
|
||||
depthStencilState.stencilFront; // Keep back and front consistent
|
||||
depthStencilState.stencilBack = depthStencilState.stencilFront;
|
||||
depthStencilState.stencilReadMask = 0;
|
||||
depthStencilState.stencilWriteMask = 0;
|
||||
}
|
||||
@@ -245,11 +258,11 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
.unclippedDepth = !rasterState.depthClamp &&
|
||||
device.HasFeature(wgpu::FeatureName::DepthClipControl)
|
||||
},
|
||||
.depthStencil = depthFormat != wgpu::TextureFormat::Undefined ? &depthStencilState: nullptr,
|
||||
.depthStencil = depthOrStencilRequested ? &depthStencilState: nullptr,
|
||||
.multisample = {
|
||||
.count = samplesCount,
|
||||
.mask = 0xFFFFFFFF,
|
||||
.alphaToCoverageEnabled = rasterState.alphaToCoverage
|
||||
.alphaToCoverageEnabled = (samplesCount > 1) && rasterState.alphaToCoverage
|
||||
},
|
||||
.fragment = nullptr // will add below if fragment module is included
|
||||
};
|
||||
|
||||
@@ -40,8 +40,8 @@ class WebGPUProgram;
|
||||
[[nodiscard]] wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const&,
|
||||
WebGPUProgram const&, WebGPUVertexBufferInfo const&, wgpu::PipelineLayout const&,
|
||||
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType primitiveType,
|
||||
std::vector<wgpu::TextureFormat> const& colorFormats, wgpu::TextureFormat depthFormat,
|
||||
uint8_t samplesCount);
|
||||
std::vector<wgpu::TextureFormat> const& colorFormats, wgpu::TextureFormat const& depthStencilFormat,
|
||||
uint8_t samplesCount, bool requestedDepth, bool requestedStencil);
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
/*
|
||||
* 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 "WebGPURenderPassMipmapGenerator.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
// The shaders expect a single-sampled 2D texture
|
||||
// with a format compatible with scalar sample format f32
|
||||
// (this is checked with the getCompatibilityFor function)
|
||||
constexpr uint32_t TEXTURE_BIND_GROUP_INDEX{ 0 };
|
||||
constexpr size_t TEXTURE_BIND_GROUP_ENTRY_SIZE{ 2 }; // sampler and texture
|
||||
constexpr uint32_t SAMPLER_BINDING_INDEX{ 0 };
|
||||
constexpr uint32_t TEXTURE_BINDING_INDEX{ 1 };
|
||||
constexpr std::string_view VERTEX_SHADER_ENTRY_POINT{ "vertexShaderMain" };
|
||||
constexpr std::string_view FRAGMENT_SHADER_ENTRY_POINT{ "fragmentShaderMain" };
|
||||
// Note: would be nice to generate this constexpr string from the constexpr variables above
|
||||
// to avoid them getting out of sync while still having the compile/build time optimization
|
||||
constexpr std::string_view SHADER_SOURCE{ R"(
|
||||
@group(0) @binding(0) var previousMipLevelSampler: sampler;
|
||||
@group(0) @binding(1) var previousMipLevelTexture: texture_2d<f32>;
|
||||
|
||||
struct VertexShaderOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) textureCoordinate: vec2<f32>,
|
||||
};
|
||||
|
||||
@vertex
|
||||
fn vertexShaderMain(@builtin(vertex_index) vertexIndex: u32) -> VertexShaderOutput {
|
||||
let fullScreenTriangleVertices = array<vec2<f32>, 3>(
|
||||
vec2<f32>(-1.0, -1.0),
|
||||
vec2<f32>(3.0, -1.0),
|
||||
vec2<f32>(-1.0, 3.0)
|
||||
);
|
||||
var out: VertexShaderOutput;
|
||||
let vertex = fullScreenTriangleVertices[vertexIndex];
|
||||
out.position = vec4<f32>(vertex * 2.0 - 1.0, 0.0, 1.0);
|
||||
out.textureCoordinate = vec2<f32>(vertex.x, 1.0 - vertex.y);
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragmentShaderMain(in: VertexShaderOutput) -> @location(0) vec4<f32> {
|
||||
return textureSample(previousMipLevelTexture,
|
||||
previousMipLevelSampler,
|
||||
in.textureCoordinate);
|
||||
}
|
||||
)" };
|
||||
|
||||
[[nodiscard]] wgpu::Sampler createPreviousMipLevelSampler(wgpu::Device const& device) {
|
||||
const wgpu::SamplerDescriptor descriptor{
|
||||
.label = "previous_mip_level_sampler",
|
||||
.addressModeU = wgpu::AddressMode::ClampToEdge,
|
||||
.addressModeV = wgpu::AddressMode::ClampToEdge,
|
||||
.addressModeW = wgpu::AddressMode::Undefined, // only 2D textures supported for now
|
||||
.magFilter = wgpu::FilterMode::Linear,
|
||||
.minFilter = wgpu::FilterMode::Linear,
|
||||
.mipmapFilter = wgpu::MipmapFilterMode::Nearest, // unused for this purpose
|
||||
.lodMinClamp = 0.0f, // should not matter, just being consistently defined
|
||||
.lodMaxClamp = 32.0f, // should not matter, just being consistently defined
|
||||
.compare = wgpu::CompareFunction::Undefined, // should not matter, just being consistently
|
||||
// defined
|
||||
.maxAnisotropy = 1, // should not matter, just being consistently defined
|
||||
};
|
||||
const wgpu::Sampler sampler{ device.CreateSampler(&descriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(sampler) << "Failed to create previous mip level sampler?";
|
||||
return sampler;
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device const& device) {
|
||||
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
|
||||
wgslDescriptor.code = SHADER_SOURCE.data();
|
||||
const wgpu::ShaderModuleDescriptor shaderModuleDescriptor{
|
||||
.nextInChain = &wgslDescriptor,
|
||||
.label = "render_pass_mipmap_generation_shaders",
|
||||
};
|
||||
const wgpu::ShaderModule shaderModule{ device.CreateShaderModule(&shaderModuleDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(shaderModule)
|
||||
<< "Failed to create shader module for render pass mipmap generation?";
|
||||
return shaderModule;
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::BindGroupLayout createTextureBindGroupLayout(wgpu::Device const& device) {
|
||||
const wgpu::BindGroupLayoutEntry bindGroupLayoutEntries[TEXTURE_BIND_GROUP_ENTRY_SIZE] {
|
||||
{
|
||||
.binding = SAMPLER_BINDING_INDEX,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.sampler = {
|
||||
.type = wgpu::SamplerBindingType::Filtering, // only hardware accelerated filtering
|
||||
// supported
|
||||
},
|
||||
},
|
||||
{
|
||||
.binding = TEXTURE_BINDING_INDEX,
|
||||
.visibility = wgpu::ShaderStage::Fragment,
|
||||
.texture = {
|
||||
.sampleType = wgpu::TextureSampleType::Float, // only F32 scalar sample
|
||||
// type supported for now
|
||||
.viewDimension = wgpu::TextureViewDimension::e2D, // only 2D supported for now
|
||||
.multisampled = false, // MSAA textures not supported for now
|
||||
},
|
||||
},
|
||||
};
|
||||
const wgpu::BindGroupLayoutDescriptor textureBindGroupLayoutDescriptor{
|
||||
.label = "render_pass_mipmap_generation_texture_bind_group_layout",
|
||||
.entryCount = TEXTURE_BIND_GROUP_ENTRY_SIZE,
|
||||
.entries = bindGroupLayoutEntries,
|
||||
};
|
||||
const wgpu::BindGroupLayout textureBindGroupLayout{ device.CreateBindGroupLayout(
|
||||
&textureBindGroupLayoutDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(textureBindGroupLayout)
|
||||
<< "Failed to create texture bind group layout for render pass mipmap generation?";
|
||||
return textureBindGroupLayout;
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::PipelineLayout createPipelineLayout(wgpu::Device const& device,
|
||||
wgpu::BindGroupLayout const& textureBindGroupLayout) {
|
||||
const wgpu::PipelineLayoutDescriptor pipelineLayoutDescriptor{
|
||||
.label = "render_pass_mipmap_generation_pipeline_layout",
|
||||
.bindGroupLayoutCount = 1,
|
||||
.bindGroupLayouts = &textureBindGroupLayout,
|
||||
};
|
||||
const wgpu::PipelineLayout pipelineLayout{ device.CreatePipelineLayout(
|
||||
&pipelineLayoutDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(pipelineLayout)
|
||||
<< "Failed to create pipeline layout for render pass mipmap generation?";
|
||||
return pipelineLayout;
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::RenderPipeline createPipeline(wgpu::Device const& device,
|
||||
wgpu::ShaderModule const& shaderModule, wgpu::PipelineLayout const& pipelineLayout,
|
||||
const wgpu::TextureFormat textureFormat) {
|
||||
const wgpu::ColorTargetState colorTargetState{ .format = textureFormat };
|
||||
const wgpu::FragmentState fragmentState{
|
||||
.module = shaderModule,
|
||||
.entryPoint = FRAGMENT_SHADER_ENTRY_POINT,
|
||||
.constantCount = 0, // should not matter, just being consistently defined
|
||||
.constants = nullptr, // should not matter, just being consistently defined
|
||||
.targetCount = 1,
|
||||
.targets = &colorTargetState,
|
||||
};
|
||||
const wgpu::RenderPipelineDescriptor pipelineDescriptor{
|
||||
.label = "render_pass_mipmap_generation_pipeline",
|
||||
.layout = pipelineLayout,
|
||||
.vertex = {
|
||||
.module = shaderModule,
|
||||
.entryPoint = VERTEX_SHADER_ENTRY_POINT,
|
||||
.constantCount = 0, // should not matter, just being consistently defined
|
||||
.constants = nullptr, // should not matter, just being consistently defined
|
||||
.bufferCount = 0, // using hardcoded vertices in the shader for
|
||||
// performance and simplicity
|
||||
.buffers = nullptr,
|
||||
},
|
||||
.primitive = {
|
||||
.topology = wgpu::PrimitiveTopology::TriangleList,
|
||||
.stripIndexFormat = wgpu::IndexFormat::Undefined, // should not matter
|
||||
// (not using a strip topology),
|
||||
// just being consistently defined
|
||||
.frontFace = wgpu::FrontFace::Undefined, // should not matter (no cull mode),
|
||||
// just being consistently defined
|
||||
.cullMode = wgpu::CullMode::None,
|
||||
.unclippedDepth = false, // should not matter, just being consistently defined
|
||||
},
|
||||
.depthStencil = nullptr, // not applicable, but explicitly set here for
|
||||
// consistent definition
|
||||
.multisample = {
|
||||
.count = 1, // no multi-sampling
|
||||
.mask = 0xFFFFFFFF,
|
||||
.alphaToCoverageEnabled = false,
|
||||
},
|
||||
.fragment = &fragmentState,
|
||||
};
|
||||
const wgpu::RenderPipeline pipeline{ device.CreateRenderPipeline(&pipelineDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(pipeline)
|
||||
<< "Failed to create pipeline for render pass mipmap generation?";
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WebGPURenderPassMipmapGenerator::WebGPURenderPassMipmapGenerator(wgpu::Device const& device)
|
||||
: mDevice{ device },
|
||||
mPreviousMipLevelSampler{ createPreviousMipLevelSampler(mDevice) },
|
||||
mShaderModule{ createShaderModule(mDevice) },
|
||||
mTextureBindGroupLayout{ createTextureBindGroupLayout(mDevice) },
|
||||
mPipelineLayout{ createPipelineLayout(mDevice, mTextureBindGroupLayout) } {}
|
||||
|
||||
WebGPURenderPassMipmapGenerator::FormatCompatibility
|
||||
WebGPURenderPassMipmapGenerator::getCompatibilityFor(const wgpu::TextureFormat format,
|
||||
const wgpu::TextureDimension dimension, const uint32_t sampleCount) {
|
||||
// check that the format is compatible (currently expects that the scalar sample type is
|
||||
// f32)...
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::Depth16Unorm:
|
||||
case wgpu::TextureFormat::Depth24Plus:
|
||||
case wgpu::TextureFormat::Depth24PlusStencil8:
|
||||
case wgpu::TextureFormat::Depth32Float:
|
||||
case wgpu::TextureFormat::Depth32FloatStencil8:
|
||||
return {
|
||||
.compatible = false,
|
||||
.reason = "A depth texture format requires special sampler treatment and does "
|
||||
"not "
|
||||
"generally support linear filtering needed for render pass based "
|
||||
"mipmap "
|
||||
"generation, thus this texture is not supported, as it is a kind of "
|
||||
"depth texture.",
|
||||
};
|
||||
case wgpu::TextureFormat::Undefined:
|
||||
case wgpu::TextureFormat::External:
|
||||
return {
|
||||
.compatible = false,
|
||||
.reason = "Undefined or External textures are not supported for render pass "
|
||||
"based "
|
||||
"mipmap generation",
|
||||
};
|
||||
default:
|
||||
const ScalarSampleType scalarSampleType{ getScalarSampleTypeFrom(format) };
|
||||
switch (scalarSampleType) {
|
||||
case ScalarSampleType::U32:
|
||||
return {
|
||||
.compatible = false,
|
||||
.reason = "The provided texture format requires an unsigned integer "
|
||||
"sampler, which does not natively support linear filtering, "
|
||||
"which is needed for render pass based mipmap generation.",
|
||||
};
|
||||
case ScalarSampleType::I32:
|
||||
return {
|
||||
.compatible = false,
|
||||
.reason = "The provided texture format requires a signed integer "
|
||||
"sampler, which does not natively support linear filtering, "
|
||||
"which is needed for render pass based mipmap generation.",
|
||||
};
|
||||
case ScalarSampleType::F32:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// check that the dimensionality is compatible (currently expects 2D textures)...
|
||||
switch (dimension) {
|
||||
case wgpu::TextureDimension::Undefined:
|
||||
return {
|
||||
.compatible = false,
|
||||
.reason = "Texture has an undefined texture dimension (expected 2D), thus it is "
|
||||
"not "
|
||||
"supported by the render pass based mipmap generation implementation.",
|
||||
};
|
||||
case wgpu::TextureDimension::e1D:
|
||||
return {
|
||||
.compatible = false,
|
||||
.reason = "Texture has a 1D texture dimension (expected 2D), thus it is not "
|
||||
"current supported by the render pass based mipmap generation "
|
||||
"implementation (but could be extended to later if needed).",
|
||||
};
|
||||
case wgpu::TextureDimension::e3D:
|
||||
return {
|
||||
.compatible = false,
|
||||
.reason = "Texture has a 3D texture dimension (expected 2D), thus it is not "
|
||||
"current supported by the render pass based mipmap generation "
|
||||
"implementation (but could be extended to later if REALLY needed).",
|
||||
};
|
||||
case wgpu::TextureDimension::e2D:
|
||||
break;
|
||||
}
|
||||
// check that the texture is single-sampled (for now)...
|
||||
if (sampleCount > 1) {
|
||||
return {
|
||||
.compatible = false,
|
||||
.reason = "Multi-sampled textures are not currently supported with the render pass "
|
||||
"based mipmap generation implementation (but could be extended to later if "
|
||||
"REALLY needed. The idea would be to resolve the texture to a single "
|
||||
"sampled "
|
||||
"texture before generating single-sample mipmaps, but that may still not "
|
||||
"necessarily be what is desired.).",
|
||||
};
|
||||
}
|
||||
return { .compatible = true, .reason = "" };
|
||||
}
|
||||
|
||||
constexpr WebGPURenderPassMipmapGenerator::ScalarSampleType
|
||||
WebGPURenderPassMipmapGenerator::getScalarSampleTypeFrom(const wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::R8Uint:
|
||||
case wgpu::TextureFormat::R16Uint:
|
||||
case wgpu::TextureFormat::R32Uint:
|
||||
case wgpu::TextureFormat::Stencil8:
|
||||
case wgpu::TextureFormat::RG8Uint:
|
||||
case wgpu::TextureFormat::RG16Uint:
|
||||
case wgpu::TextureFormat::RGBA8Uint:
|
||||
case wgpu::TextureFormat::RGB10A2Uint:
|
||||
case wgpu::TextureFormat::RG32Uint:
|
||||
case wgpu::TextureFormat::RGBA16Uint:
|
||||
case wgpu::TextureFormat::RGBA32Uint:
|
||||
return ScalarSampleType::U32;
|
||||
case wgpu::TextureFormat::R8Sint:
|
||||
case wgpu::TextureFormat::R16Sint:
|
||||
case wgpu::TextureFormat::R32Sint:
|
||||
case wgpu::TextureFormat::RG8Sint:
|
||||
case wgpu::TextureFormat::RG16Sint:
|
||||
case wgpu::TextureFormat::RGBA8Sint:
|
||||
case wgpu::TextureFormat::RG32Sint:
|
||||
case wgpu::TextureFormat::RGBA16Sint:
|
||||
case wgpu::TextureFormat::RGBA32Sint:
|
||||
return ScalarSampleType::I32;
|
||||
case wgpu::TextureFormat::R8Snorm:
|
||||
case wgpu::TextureFormat::R8Unorm:
|
||||
case wgpu::TextureFormat::R16Float:
|
||||
case wgpu::TextureFormat::RG16Float:
|
||||
case wgpu::TextureFormat::RG8Unorm:
|
||||
case wgpu::TextureFormat::RG8Snorm:
|
||||
case wgpu::TextureFormat::R32Float:
|
||||
case wgpu::TextureFormat::RGBA8Unorm:
|
||||
case wgpu::TextureFormat::RGBA8UnormSrgb:
|
||||
case wgpu::TextureFormat::RGBA8Snorm:
|
||||
case wgpu::TextureFormat::BGRA8Unorm:
|
||||
case wgpu::TextureFormat::BGRA8UnormSrgb:
|
||||
case wgpu::TextureFormat::RGB10A2Unorm:
|
||||
case wgpu::TextureFormat::RG11B10Ufloat:
|
||||
case wgpu::TextureFormat::RGB9E5Ufloat:
|
||||
case wgpu::TextureFormat::RG32Float:
|
||||
case wgpu::TextureFormat::RGBA16Float:
|
||||
case wgpu::TextureFormat::RGBA32Float:
|
||||
case wgpu::TextureFormat::BC1RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC1RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC2RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC2RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC3RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC3RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::BC4RUnorm:
|
||||
case wgpu::TextureFormat::BC4RSnorm:
|
||||
case wgpu::TextureFormat::BC5RGUnorm:
|
||||
case wgpu::TextureFormat::BC5RGSnorm:
|
||||
case wgpu::TextureFormat::BC6HRGBUfloat:
|
||||
case wgpu::TextureFormat::BC6HRGBFloat:
|
||||
case wgpu::TextureFormat::BC7RGBAUnorm:
|
||||
case wgpu::TextureFormat::BC7RGBAUnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGB8Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGB8UnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGB8A1Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb:
|
||||
case wgpu::TextureFormat::ETC2RGBA8Unorm:
|
||||
case wgpu::TextureFormat::ETC2RGBA8UnormSrgb:
|
||||
case wgpu::TextureFormat::EACR11Unorm:
|
||||
case wgpu::TextureFormat::EACR11Snorm:
|
||||
case wgpu::TextureFormat::EACRG11Unorm:
|
||||
case wgpu::TextureFormat::EACRG11Snorm:
|
||||
case wgpu::TextureFormat::ASTC4x4Unorm:
|
||||
case wgpu::TextureFormat::ASTC4x4UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC5x4Unorm:
|
||||
case wgpu::TextureFormat::ASTC5x4UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC5x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC5x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC6x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC6x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC6x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC6x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC8x8Unorm:
|
||||
case wgpu::TextureFormat::ASTC8x8UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x5Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x5UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x6Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x6UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x8Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x8UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC10x10Unorm:
|
||||
case wgpu::TextureFormat::ASTC10x10UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC12x10Unorm:
|
||||
case wgpu::TextureFormat::ASTC12x10UnormSrgb:
|
||||
case wgpu::TextureFormat::ASTC12x12Unorm:
|
||||
case wgpu::TextureFormat::ASTC12x12UnormSrgb:
|
||||
case wgpu::TextureFormat::R16Unorm:
|
||||
case wgpu::TextureFormat::RG16Unorm:
|
||||
case wgpu::TextureFormat::RGBA16Unorm:
|
||||
case wgpu::TextureFormat::R16Snorm:
|
||||
case wgpu::TextureFormat::RG16Snorm:
|
||||
case wgpu::TextureFormat::RGBA16Snorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
|
||||
case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar422Unorm:
|
||||
case wgpu::TextureFormat::R8BG8Biplanar444Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm:
|
||||
return ScalarSampleType::F32;
|
||||
case wgpu::TextureFormat::Depth16Unorm:
|
||||
case wgpu::TextureFormat::Depth24Plus:
|
||||
case wgpu::TextureFormat::Depth24PlusStencil8:
|
||||
case wgpu::TextureFormat::Depth32Float:
|
||||
case wgpu::TextureFormat::Depth32FloatStencil8:
|
||||
PANIC_POSTCONDITION("A depth texture format requires special sampler treatment and "
|
||||
"does not generally support linear filtering (needed for mipmap "
|
||||
"generation). Thus no applicable scalar sample type for %d",
|
||||
format);
|
||||
break;
|
||||
case wgpu::TextureFormat::Undefined:
|
||||
case wgpu::TextureFormat::External:
|
||||
PANIC_POSTCONDITION("No scalar sample type for texture format %d", format);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WebGPURenderPassMipmapGenerator::generateMipmaps(wgpu::Queue const& queue,
|
||||
wgpu::Texture const& texture) {
|
||||
const uint32_t mipLevelCount{ texture.GetMipLevelCount() };
|
||||
if (mipLevelCount < 2) {
|
||||
return; // nothing to do
|
||||
}
|
||||
wgpu::RenderPipeline const& pipeline{ getOrCreatePipelineFor(texture.GetFormat()) };
|
||||
const wgpu::CommandEncoderDescriptor commandEncoderDescriptor{
|
||||
.label = "mipmap_generation_render_pass_cmd_encoder",
|
||||
};
|
||||
const wgpu::CommandEncoder commandEncoder{ mDevice.CreateCommandEncoder(
|
||||
&commandEncoderDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(commandEncoder)
|
||||
<< "Failed to create command encoder for layer for render pass mipmap generation?";
|
||||
const uint32_t layerCount{ texture.GetDepthOrArrayLayers() };
|
||||
for (uint32_t layer = 0; layer < layerCount; layer++) {
|
||||
for (uint32_t mipLevel = 1; mipLevel < mipLevelCount; mipLevel++) {
|
||||
generateMipmap(commandEncoder, texture, pipeline, layer, mipLevel);
|
||||
}
|
||||
}
|
||||
// submit the command buffer...
|
||||
const wgpu::CommandBufferDescriptor commandBufferDescriptor{
|
||||
.label = "mipmap_generation_render_pass_cmd_buffer",
|
||||
};
|
||||
const wgpu::CommandBuffer commandBuffer{ commandEncoder.Finish(&commandBufferDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(commandBuffer)
|
||||
<< "Failed to create command buffer for layer for render pass mipmap generation?";
|
||||
queue.Submit(1, &commandBuffer);
|
||||
}
|
||||
|
||||
void WebGPURenderPassMipmapGenerator::generateMipmap(wgpu::CommandEncoder const& commandEncoder,
|
||||
const wgpu::Texture& texture, const wgpu::RenderPipeline& pipeline, const uint32_t layer,
|
||||
const uint32_t mipLevel) {
|
||||
// create texture views for this pass...
|
||||
const wgpu::TextureFormat format{ texture.GetFormat() };
|
||||
// only 2D textures supported at this time...
|
||||
const wgpu::TextureViewDimension dimension{ wgpu::TextureViewDimension::e2D };
|
||||
const wgpu::TextureViewDescriptor sourceViewDescriptor{
|
||||
.label = "previous_mip_level_texture_view",
|
||||
.format = format,
|
||||
.dimension = dimension,
|
||||
.baseMipLevel = mipLevel - 1,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = layer,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = wgpu::TextureAspect::All,
|
||||
.usage = wgpu::TextureUsage::TextureBinding,
|
||||
};
|
||||
const wgpu::TextureView sourceView{ texture.CreateView(&sourceViewDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(pipeline)
|
||||
<< "Failed to create source texture view for layer " << layer << " and mip level "
|
||||
<< mipLevel << " for render pass mipmap generation?";
|
||||
const wgpu::TextureViewDescriptor destinationViewDescriptor{
|
||||
.label = "next_mip_level_texture_view",
|
||||
.format = format,
|
||||
.dimension = dimension,
|
||||
.baseMipLevel = mipLevel,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = layer,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = wgpu::TextureAspect::All,
|
||||
.usage = wgpu::TextureUsage::RenderAttachment,
|
||||
};
|
||||
const wgpu::TextureView destinationView{ texture.CreateView(&destinationViewDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(pipeline)
|
||||
<< "Failed to create destination texture view for layer " << layer << " and mip level "
|
||||
<< mipLevel << " for render pass mipmap generation?";
|
||||
// create the render pass...
|
||||
const wgpu::BindGroupEntry textureBindGroupEntries[TEXTURE_BIND_GROUP_ENTRY_SIZE]{
|
||||
{
|
||||
.binding = SAMPLER_BINDING_INDEX,
|
||||
.sampler = mPreviousMipLevelSampler,
|
||||
},
|
||||
{
|
||||
.binding = TEXTURE_BINDING_INDEX,
|
||||
.textureView = sourceView,
|
||||
},
|
||||
};
|
||||
const wgpu::BindGroupDescriptor textureBindGroupDescriptor{
|
||||
.label = "render_pass_mipmap_generation_texture_bind_group",
|
||||
.layout = mTextureBindGroupLayout,
|
||||
.entryCount = TEXTURE_BIND_GROUP_ENTRY_SIZE,
|
||||
.entries = textureBindGroupEntries,
|
||||
};
|
||||
const wgpu::BindGroup textureBindGroup{ mDevice.CreateBindGroup(&textureBindGroupDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(pipeline)
|
||||
<< "Failed to create texture bind group for layer " << layer << " and mip level "
|
||||
<< mipLevel << " for render pass mipmap generation?";
|
||||
const wgpu::RenderPassColorAttachment colorAttachment{
|
||||
.view = destinationView,
|
||||
.depthSlice = wgpu::kDepthSliceUndefined, // not applicable
|
||||
.resolveTarget = nullptr, // not applicable
|
||||
.loadOp = wgpu::LoadOp::Clear,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
.clearValue = { .r = 0.0, .g = 0.0, .b = 0.0, .a = 1.0 },
|
||||
};
|
||||
const wgpu::RenderPassDescriptor renderPassDescriptor{
|
||||
.label = "mipmap_generation_render_pass",
|
||||
.colorAttachmentCount = 1,
|
||||
.colorAttachments = &colorAttachment,
|
||||
.depthStencilAttachment = nullptr, // not applicable
|
||||
.occlusionQuerySet = nullptr, // not applicable
|
||||
.timestampWrites = nullptr, // not applicable
|
||||
};
|
||||
const wgpu::RenderPassEncoder renderPassEncoder{ commandEncoder.BeginRenderPass(
|
||||
&renderPassDescriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(renderPassEncoder)
|
||||
<< "Failed to create render pass encoder for layer " << layer << " and mip level "
|
||||
<< mipLevel << " for render pass mipmap generation?";
|
||||
renderPassEncoder.SetPipeline(pipeline);
|
||||
renderPassEncoder.SetBindGroup(TEXTURE_BIND_GROUP_INDEX, textureBindGroup);
|
||||
renderPassEncoder.Draw(3); // draw the full-screen triangle
|
||||
// with hard-coded vertices in the shader
|
||||
renderPassEncoder.End();
|
||||
}
|
||||
|
||||
wgpu::RenderPipeline& WebGPURenderPassMipmapGenerator::getOrCreatePipelineFor(
|
||||
const wgpu::TextureFormat textureFormat) {
|
||||
const uint32_t key{ static_cast<uint32_t>(textureFormat) };
|
||||
if (mPipelineByTextureFormat.find(key) == mPipelineByTextureFormat.end()) {
|
||||
mPipelineByTextureFormat[key] =
|
||||
createPipeline(mDevice, mShaderModule, mPipelineLayout, textureFormat);
|
||||
}
|
||||
return mPipelineByTextureFormat[key];
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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_WEBGPURENDERPASSMIPMAPGENERATOR_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPURENDERPASSMIPMAPGENERATOR_H
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPURenderPassMipmapGenerator final {
|
||||
public:
|
||||
struct FormatCompatibility final {
|
||||
bool compatible{ false };
|
||||
std::string_view reason;
|
||||
};
|
||||
|
||||
[[nodiscard]] static FormatCompatibility getCompatibilityFor(wgpu::TextureFormat,
|
||||
wgpu::TextureDimension, uint32_t sampleCount);
|
||||
|
||||
explicit WebGPURenderPassMipmapGenerator(wgpu::Device const&);
|
||||
|
||||
void generateMipmaps(wgpu::Queue const&, wgpu::Texture const&);
|
||||
|
||||
private:
|
||||
enum class ScalarSampleType : uint8_t {
|
||||
F32,
|
||||
I32,
|
||||
U32,
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr static ScalarSampleType getScalarSampleTypeFrom(
|
||||
wgpu::TextureFormat format);
|
||||
|
||||
[[nodiscard]] wgpu::RenderPipeline& getOrCreatePipelineFor(wgpu::TextureFormat);
|
||||
|
||||
void generateMipmap(wgpu::CommandEncoder const&, wgpu::Texture const&,
|
||||
wgpu::RenderPipeline const&, uint32_t layer, uint32_t mipLevel);
|
||||
|
||||
wgpu::Device const& mDevice;
|
||||
const wgpu::Sampler mPreviousMipLevelSampler{ nullptr };
|
||||
const wgpu::ShaderModule mShaderModule{ nullptr };
|
||||
const wgpu::BindGroupLayout mTextureBindGroupLayout{ nullptr };
|
||||
const wgpu::PipelineLayout mPipelineLayout{ nullptr };
|
||||
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineByTextureFormat;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPURENDERPASSMIPMAPGENERATOR_H
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
#include "WebGPURenderTarget.h"
|
||||
|
||||
#include "WebGPUTexture.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <private/backend/BackendUtils.h>
|
||||
@@ -26,30 +30,120 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Panics if the number of samples in the original attachment textures do not match.
|
||||
* @return The number of samples in the original attachment textures (the number/count in each one).
|
||||
* Returns 0 if there are no attachments.
|
||||
*/
|
||||
[[nodiscard]] uint8_t createMsaaSidecarTextures(const uint8_t renderTargetSampleCount,
|
||||
const TargetBufferFlags targetFlags, MRT const& colorAttachments,
|
||||
TargetBufferInfo const& depthAttachment, TargetBufferInfo const& stencilAttachment,
|
||||
std::function<WebGPUTexture*(const Handle<HwTexture>)> const& getWebGPUTexture,
|
||||
wgpu::Device const& device) {
|
||||
struct Target final {
|
||||
std::string_view name;
|
||||
TargetBufferFlags flag{ TargetBufferFlags::NONE };
|
||||
size_t colorIndex{ 0 };
|
||||
};
|
||||
// assuming 8 colors. hopefully the following static_assert will get tripped if another
|
||||
// color ever gets added
|
||||
static_assert(
|
||||
(TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1 | TargetBufferFlags::COLOR2 |
|
||||
TargetBufferFlags::COLOR3 | TargetBufferFlags::COLOR4 |
|
||||
TargetBufferFlags::COLOR5 | TargetBufferFlags::COLOR6 |
|
||||
TargetBufferFlags::COLOR7) == TargetBufferFlags::COLOR_ALL);
|
||||
const static std::array TARGETS{
|
||||
Target{ .name = "COLOR0", .flag = TargetBufferFlags::COLOR0, .colorIndex = 0 },
|
||||
Target{ .name = "COLOR1", .flag = TargetBufferFlags::COLOR1, .colorIndex = 1 },
|
||||
Target{ .name = "COLOR2", .flag = TargetBufferFlags::COLOR2, .colorIndex = 2 },
|
||||
Target{ .name = "COLOR3", .flag = TargetBufferFlags::COLOR3, .colorIndex = 3 },
|
||||
Target{ .name = "COLOR4", .flag = TargetBufferFlags::COLOR4, .colorIndex = 4 },
|
||||
Target{ .name = "COLOR5", .flag = TargetBufferFlags::COLOR5, .colorIndex = 5 },
|
||||
Target{ .name = "COLOR6", .flag = TargetBufferFlags::COLOR6, .colorIndex = 6 },
|
||||
Target{ .name = "COLOR7", .flag = TargetBufferFlags::COLOR7, .colorIndex = 7 },
|
||||
Target{ .name = "DEPTH", .flag = TargetBufferFlags::DEPTH },
|
||||
Target{ .name = "STENCIL", .flag = TargetBufferFlags::STENCIL },
|
||||
};
|
||||
#ifndef NDEBUG
|
||||
for (auto const& target: TARGETS) {
|
||||
assert_invariant(target.colorIndex <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT &&
|
||||
"color index is >= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT, which should "
|
||||
"not be possible and could lead to us indexing into the colorAttachments "
|
||||
"array beyond its bounds.");
|
||||
}
|
||||
#endif
|
||||
bool firstAttachment{ true };
|
||||
uint8_t sampleCountPerAttachment{ 0 };
|
||||
for (auto const& target: TARGETS) {
|
||||
if (any(targetFlags & target.flag)) {
|
||||
const Handle<HwTexture> textureHandle{
|
||||
target.flag == TargetBufferFlags::DEPTH
|
||||
? depthAttachment.handle
|
||||
: (target.flag == TargetBufferFlags::STENCIL
|
||||
? stencilAttachment.handle
|
||||
: colorAttachments[target.colorIndex].handle)
|
||||
};
|
||||
WebGPUTexture* const texture{ getWebGPUTexture(textureHandle) };
|
||||
assert_invariant(texture != nullptr && "target flag indicate the use of an attachment "
|
||||
"for which we do not have a texture?");
|
||||
if (firstAttachment) {
|
||||
sampleCountPerAttachment = texture->samples;
|
||||
firstAttachment = false;
|
||||
}
|
||||
FILAMENT_CHECK_PRECONDITION(texture->samples == sampleCountPerAttachment)
|
||||
<< target.name << " attachment texture has " << +texture->samples
|
||||
<< " but the other attachment(s) have " << +sampleCountPerAttachment;
|
||||
if (renderTargetSampleCount > 1 && sampleCountPerAttachment == 1) {
|
||||
texture->createMsaaSidecarTextureIfNotAlreadyCreated(renderTargetSampleCount,
|
||||
device);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_invariant(sampleCountPerAttachment);
|
||||
return sampleCountPerAttachment;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WebGPURenderTarget::WebGPURenderTarget(const uint32_t width, const uint32_t height,
|
||||
const uint8_t samples, const uint8_t layerCount, MRT const& colorAttachmentsMRT,
|
||||
Attachment const& depthAttachmentInfo, Attachment const& stencilAttachmentInfo)
|
||||
Attachment const& depthAttachmentInfo, Attachment const& stencilAttachmentInfo,
|
||||
TargetBufferFlags const& targetFlags,
|
||||
std::function<WebGPUTexture*(const Handle<HwTexture>)> const& getWebGPUTexture,
|
||||
wgpu::Device const& device)
|
||||
: HwRenderTarget{ width, height },
|
||||
mDefaultRenderTarget{ false },
|
||||
mTargetFlags{ targetFlags },
|
||||
mSamples{ samples },
|
||||
mLayerCount{ layerCount },
|
||||
mColorAttachments{ colorAttachmentsMRT },
|
||||
mDepthAttachment{ depthAttachmentInfo },
|
||||
mStencilAttachment{ stencilAttachmentInfo } {
|
||||
// TODO consider making this an array
|
||||
mColorAttachmentDescriptors.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
mStencilAttachment{ stencilAttachmentInfo },
|
||||
mSampleCountPerAttachment{ createMsaaSidecarTextures(mSamples, mTargetFlags,
|
||||
mColorAttachments, mDepthAttachment, mStencilAttachment, getWebGPUTexture, device) } {
|
||||
// TODO consider possibly making this an array (that would avoid a heap allocation, but the
|
||||
// concern is the size limitation on the handle itself)
|
||||
mColorAttachmentDesc.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
}
|
||||
|
||||
// Default constructor for the default render target
|
||||
WebGPURenderTarget::WebGPURenderTarget()
|
||||
: HwRenderTarget{ 0, 0 },
|
||||
mDefaultRenderTarget{ true },
|
||||
mTargetFlags{ TargetBufferFlags::NONE },
|
||||
mSamples{ 1 },
|
||||
mLayerCount{ 1 } {}
|
||||
mLayerCount{ 1 },
|
||||
mSampleCountPerAttachment{ 1 } {}
|
||||
|
||||
wgpu::LoadOp WebGPURenderTarget::getLoadOperation(RenderPassParams const& params,
|
||||
const TargetBufferFlags bufferToOperateOn) {
|
||||
@@ -57,7 +151,7 @@ wgpu::LoadOp WebGPURenderTarget::getLoadOperation(RenderPassParams const& params
|
||||
return wgpu::LoadOp::Clear;
|
||||
}
|
||||
if (any(params.flags.discardStart & bufferToOperateOn)) {
|
||||
return wgpu::LoadOp::Clear; // Or wgpu::LoadOp::Undefined if clear is not desired on discard
|
||||
return wgpu::LoadOp::Clear;
|
||||
}
|
||||
return wgpu::LoadOp::Load;
|
||||
}
|
||||
@@ -73,137 +167,137 @@ wgpu::StoreOp WebGPURenderTarget::getStoreOperation(RenderPassParams const& para
|
||||
void WebGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& outDescriptor,
|
||||
RenderPassParams const& params, wgpu::TextureView const& defaultColorTextureView,
|
||||
wgpu::TextureView const& defaultDepthStencilTextureView,
|
||||
wgpu::TextureFormat const& defaultDepthStencilFormat,
|
||||
wgpu::TextureView const* customColorTextureViews, uint32_t customColorTextureViewCount,
|
||||
wgpu::TextureView const& customDepthTextureView,
|
||||
wgpu::TextureView const& customStencilTextureView,
|
||||
const wgpu::TextureFormat customDepthFormat,
|
||||
const wgpu::TextureFormat customStencilFormat) {
|
||||
mColorAttachmentDescriptors.clear();
|
||||
mHasDepthStencilAttachment = false;
|
||||
wgpu::TextureView const* customColorTextureViews,
|
||||
wgpu::TextureView const* customColorMsaaSidecarTextureViews,
|
||||
uint32_t customColorTextureViewCount,
|
||||
wgpu::TextureView const& customDepthStencilTextureView,
|
||||
wgpu::TextureView const& customDepthStencilMsaaSidecarTextureView) {
|
||||
const bool hasMsaaSidecars{ (customColorTextureViewCount > 0 &&
|
||||
customColorMsaaSidecarTextureViews[0]) ||
|
||||
customDepthStencilMsaaSidecarTextureView };
|
||||
// either all the textures have MSAA sidecars or none of them do
|
||||
if (hasMsaaSidecars) {
|
||||
FILAMENT_CHECK_PRECONDITION(std::all_of(customColorMsaaSidecarTextureViews,
|
||||
customColorMsaaSidecarTextureViews + customColorTextureViewCount,
|
||||
[](wgpu::TextureView const& msaaView) { return msaaView != nullptr; }))
|
||||
<< "A color or depth/stencil attachment texture has a MSAA sidecar but at least "
|
||||
"one other color attachment texture does not.";
|
||||
FILAMENT_CHECK_PRECONDITION(customDepthStencilMsaaSidecarTextureView != nullptr)
|
||||
<< "The color attachment texture(s) have MSAA sidecar(s) but the depth/stencil "
|
||||
"texture does not.";
|
||||
} else {
|
||||
FILAMENT_CHECK_PRECONDITION(std::all_of(customColorMsaaSidecarTextureViews,
|
||||
customColorMsaaSidecarTextureViews + customColorTextureViewCount,
|
||||
[](wgpu::TextureView const& msaaView) { return msaaView == nullptr; }))
|
||||
<< "A color or depth/stencil attachment texture does not have a MSAA sidecar but "
|
||||
"at least one color attachment texture does.";
|
||||
FILAMENT_CHECK_PRECONDITION(customDepthStencilMsaaSidecarTextureView == nullptr)
|
||||
<< "Custom color textures for the render target do not have MSAA sidecar(s) but "
|
||||
"the depth/stencil texture does.";
|
||||
}
|
||||
|
||||
mColorAttachmentDesc.clear();
|
||||
|
||||
const bool hasDepth = any(mTargetFlags & TargetBufferFlags::DEPTH);
|
||||
const bool hasStencil = any(mTargetFlags & TargetBufferFlags::STENCIL);
|
||||
mHasDepthStencilAttachment = hasDepth || hasStencil;
|
||||
bool const depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
|
||||
|
||||
// Color attachments
|
||||
if (mDefaultRenderTarget) {
|
||||
assert_invariant(defaultColorTextureView);
|
||||
mColorAttachmentDescriptors.push_back({ .view = defaultColorTextureView,
|
||||
mColorAttachmentDesc.push_back({
|
||||
.view = defaultColorTextureView,
|
||||
.resolveTarget = nullptr,
|
||||
.loadOp = WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0),
|
||||
.storeOp = WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0),
|
||||
.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b,
|
||||
params.clearColor.a } });
|
||||
|
||||
bool const depthReadonly =
|
||||
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
|
||||
|
||||
if (defaultDepthStencilTextureView) {
|
||||
mDepthStencilAttachmentDescriptor = {
|
||||
.view = defaultDepthStencilTextureView,
|
||||
.depthLoadOp = depthReadonly ? wgpu::LoadOp::Undefined
|
||||
: WebGPURenderTarget::getLoadOperation(params,
|
||||
TargetBufferFlags::DEPTH),
|
||||
.depthStoreOp = depthReadonly ? wgpu::StoreOp::Undefined
|
||||
: WebGPURenderTarget::getStoreOperation(params,
|
||||
TargetBufferFlags::DEPTH),
|
||||
.depthClearValue = static_cast<float>(params.clearDepth),
|
||||
.depthReadOnly = depthReadonly,
|
||||
.stencilLoadOp = wgpu::LoadOp::Undefined,
|
||||
.stencilStoreOp = wgpu::StoreOp::Undefined,
|
||||
.stencilClearValue = params.clearStencil,
|
||||
.stencilReadOnly =
|
||||
(params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0,
|
||||
};
|
||||
|
||||
if (defaultDepthStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
defaultDepthStencilFormat == wgpu::TextureFormat::Depth32FloatStencil8 ||
|
||||
defaultDepthStencilFormat == wgpu::TextureFormat::Stencil8) {
|
||||
mDepthStencilAttachmentDescriptor.stencilLoadOp =
|
||||
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
|
||||
mDepthStencilAttachmentDescriptor.stencilStoreOp =
|
||||
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
|
||||
}
|
||||
mHasDepthStencilAttachment = true;
|
||||
}
|
||||
} else { // Custom Render Target
|
||||
.clearValue = {
|
||||
.r = params.clearColor.r,
|
||||
.g = params.clearColor.g,
|
||||
.b = params.clearColor.b,
|
||||
.a = params.clearColor.a,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
for (uint32_t i = 0; i < customColorTextureViewCount; ++i) {
|
||||
if (customColorTextureViews[i]) {
|
||||
mColorAttachmentDescriptors.push_back({ .view = customColorTextureViews[i],
|
||||
// .resolveTarget = nullptr; // TODO: MSAA resolve for custom RT
|
||||
const wgpu::TextureView msaaSidecar{ customColorMsaaSidecarTextureViews[i] };
|
||||
mColorAttachmentDesc.push_back({
|
||||
.view = hasMsaaSidecars ? msaaSidecar : customColorTextureViews[i],
|
||||
.resolveTarget = hasMsaaSidecars ? customColorTextureViews[i] : nullptr,
|
||||
.loadOp =
|
||||
WebGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i)),
|
||||
.storeOp = WebGPURenderTarget::getStoreOperation(params,
|
||||
getTargetBufferFlagsAt(i)),
|
||||
.clearValue = { .r = params.clearColor.r,
|
||||
.clearValue = {
|
||||
.r = params.clearColor.r,
|
||||
.g = params.clearColor.g,
|
||||
.b = params.clearColor.b,
|
||||
.a = params.clearColor.a } });
|
||||
.a = params.clearColor.a,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
outDescriptor.colorAttachmentCount = mColorAttachmentDesc.size();
|
||||
outDescriptor.colorAttachments = mColorAttachmentDesc.data();
|
||||
|
||||
// Depth/Stencil Attachments
|
||||
if (mHasDepthStencilAttachment) {
|
||||
wgpu::TextureView depthStencilViewToUse{ nullptr };
|
||||
if (mDefaultRenderTarget) {
|
||||
depthStencilViewToUse = defaultDepthStencilTextureView;
|
||||
assert_invariant(depthStencilViewToUse);
|
||||
} else {
|
||||
if (customDepthStencilTextureView) {
|
||||
depthStencilViewToUse = hasMsaaSidecars ? customDepthStencilMsaaSidecarTextureView
|
||||
: customDepthStencilTextureView;
|
||||
}
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(!(customDepthTextureView && customStencilTextureView))
|
||||
<< "WebGPU CANNOT support separate texture views for depth + stencil. depth + "
|
||||
"stencil needs to be in one texture view";
|
||||
|
||||
const bool hasStencil =
|
||||
customStencilTextureView ||
|
||||
(customDepthFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
customDepthFormat == wgpu::TextureFormat::Depth32FloatStencil8);
|
||||
|
||||
const bool hasDepth =
|
||||
customDepthTextureView ||
|
||||
(customStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
customDepthFormat == wgpu::TextureFormat::Depth32FloatStencil8);
|
||||
|
||||
if (customDepthTextureView || customStencilTextureView) {
|
||||
assert_invariant((hasDepth || hasStencil) &&
|
||||
"Depth or Texture view without a valid texture format");
|
||||
mDepthStencilAttachmentDescriptor = {};
|
||||
mDepthStencilAttachmentDescriptor.view =
|
||||
customDepthTextureView ? customDepthTextureView : customStencilTextureView;
|
||||
if (depthStencilViewToUse) {
|
||||
mDepthStencilAttachmentDesc = {};
|
||||
mDepthStencilAttachmentDesc.view = depthStencilViewToUse;
|
||||
|
||||
if (hasDepth) {
|
||||
bool const depthReadonly =
|
||||
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
|
||||
|
||||
|
||||
mDepthStencilAttachmentDescriptor.depthLoadOp =
|
||||
depthReadonly ? wgpu::LoadOp::Undefined :
|
||||
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH);
|
||||
mDepthStencilAttachmentDescriptor.depthStoreOp =
|
||||
depthReadonly ? wgpu::StoreOp::Undefined :
|
||||
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH);
|
||||
mDepthStencilAttachmentDescriptor.depthClearValue =
|
||||
static_cast<float>(params.clearDepth);
|
||||
mDepthStencilAttachmentDescriptor.depthReadOnly = depthReadonly;
|
||||
mDepthStencilAttachmentDesc.depthLoadOp =
|
||||
depthReadOnly ? wgpu::LoadOp::Undefined
|
||||
: getLoadOperation(params, TargetBufferFlags::DEPTH);
|
||||
mDepthStencilAttachmentDesc.depthStoreOp =
|
||||
depthReadOnly ? wgpu::StoreOp::Undefined
|
||||
: getStoreOperation(params, TargetBufferFlags::DEPTH);
|
||||
mDepthStencilAttachmentDesc.depthClearValue = static_cast<float>(params.clearDepth);
|
||||
mDepthStencilAttachmentDesc.depthReadOnly = depthReadOnly;
|
||||
} else {
|
||||
mDepthStencilAttachmentDescriptor.depthLoadOp = wgpu::LoadOp::Undefined;
|
||||
mDepthStencilAttachmentDescriptor.depthStoreOp = wgpu::StoreOp::Undefined;
|
||||
mDepthStencilAttachmentDescriptor.depthReadOnly = true;
|
||||
mDepthStencilAttachmentDesc.depthLoadOp = wgpu::LoadOp::Undefined;
|
||||
mDepthStencilAttachmentDesc.depthStoreOp = wgpu::StoreOp::Undefined;
|
||||
mDepthStencilAttachmentDesc.depthReadOnly = true;
|
||||
}
|
||||
|
||||
if (hasStencil) {
|
||||
mDepthStencilAttachmentDescriptor.stencilLoadOp =
|
||||
WebGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
|
||||
mDepthStencilAttachmentDescriptor.stencilStoreOp =
|
||||
WebGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
|
||||
mDepthStencilAttachmentDescriptor.stencilClearValue = params.clearStencil;
|
||||
mDepthStencilAttachmentDescriptor.stencilReadOnly =
|
||||
mDepthStencilAttachmentDesc.stencilLoadOp =
|
||||
getLoadOperation(params, TargetBufferFlags::STENCIL);
|
||||
mDepthStencilAttachmentDesc.stencilStoreOp =
|
||||
getStoreOperation(params, TargetBufferFlags::STENCIL);
|
||||
mDepthStencilAttachmentDesc.stencilClearValue = params.clearStencil;
|
||||
mDepthStencilAttachmentDesc.stencilReadOnly =
|
||||
(params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0;
|
||||
} else {
|
||||
mDepthStencilAttachmentDescriptor.stencilLoadOp = wgpu::LoadOp::Undefined;
|
||||
mDepthStencilAttachmentDescriptor.stencilStoreOp = wgpu::StoreOp::Undefined;
|
||||
mDepthStencilAttachmentDescriptor.stencilReadOnly = true;
|
||||
mDepthStencilAttachmentDesc.stencilLoadOp = wgpu::LoadOp::Undefined;
|
||||
mDepthStencilAttachmentDesc.stencilStoreOp = wgpu::StoreOp::Undefined;
|
||||
mDepthStencilAttachmentDesc.stencilReadOnly = true;
|
||||
}
|
||||
mHasDepthStencilAttachment = true;
|
||||
outDescriptor.depthStencilAttachment = &mDepthStencilAttachmentDesc;
|
||||
|
||||
} else {
|
||||
outDescriptor.depthStencilAttachment = nullptr;
|
||||
mDepthStencilAttachmentDesc = {};
|
||||
}
|
||||
|
||||
} else {
|
||||
outDescriptor.depthStencilAttachment = nullptr;
|
||||
mDepthStencilAttachmentDesc = {};
|
||||
}
|
||||
|
||||
outDescriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
|
||||
outDescriptor.colorAttachments = mColorAttachmentDescriptors.data();
|
||||
outDescriptor.depthStencilAttachment =
|
||||
mHasDepthStencilAttachment ? &mDepthStencilAttachmentDescriptor : nullptr;
|
||||
|
||||
// descriptor.sampleCount was removed from the core spec. If your webgpu.h still has it,
|
||||
// and your Dawn version expects it, you might need to set it here based on this->samples.
|
||||
// e.g., descriptor.sampleCount = this->samples;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
|
||||
#include "WebGPUTexture.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
@@ -24,6 +26,7 @@
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -34,44 +37,44 @@ public:
|
||||
|
||||
WebGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount,
|
||||
MRT const& colorAttachments, Attachment const& depthAttachment,
|
||||
Attachment const& stencilAttachment);
|
||||
Attachment const& stencilAttachment, TargetBufferFlags const& targetFlags,
|
||||
std::function<WebGPUTexture*(const Handle<HwTexture>)> const&, wgpu::Device const&);
|
||||
|
||||
// Default constructor for the default render target
|
||||
WebGPURenderTarget();
|
||||
|
||||
// Updated signature: takes resolved views for custom RTs, and default views for default RT
|
||||
void setUpRenderPassAttachments(
|
||||
wgpu::RenderPassDescriptor& outDescriptor,
|
||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& outDescriptor,
|
||||
RenderPassParams const& params,
|
||||
// For default render target:
|
||||
wgpu::TextureView const& defaultColorTextureView,
|
||||
wgpu::TextureView const& defaultDepthStencilTextureView,
|
||||
wgpu::TextureFormat const& defaultDepthStencilFormat,
|
||||
// For custom render targets:
|
||||
wgpu::TextureView const* customColorTextureViews, // Array of views
|
||||
wgpu::TextureView const* customColorTextureViews, // Array of views
|
||||
wgpu::TextureView const* customColorMsaaSidecarTextureViews, // nullptrs if N/A
|
||||
uint32_t customColorTextureViewCount,
|
||||
wgpu::TextureView const& customDepthTextureView,
|
||||
wgpu::TextureView const& customStencilTextureView,
|
||||
wgpu::TextureFormat customDepthFormat,
|
||||
wgpu::TextureFormat customStencilFormat);
|
||||
wgpu::TextureView const& customDepthStencilTextureView,
|
||||
wgpu::TextureView const& customDepthStencilMsaaSidecarTextureView /* nullptr if N/A */);
|
||||
|
||||
[[nodiscard]] bool isDefaultRenderTarget() const { return mDefaultRenderTarget; }
|
||||
[[nodiscard]] uint8_t getSamples() const { return mSamples; }
|
||||
[[nodiscard]] uint8_t getSampleCountPerAttachment() const { return mSampleCountPerAttachment; }
|
||||
[[nodiscard]] uint8_t getLayerCount() const { return mLayerCount; }
|
||||
|
||||
// Accessors for the driver to get stored attachment info
|
||||
[[nodiscard]] MRT const& getColorAttachmentInfos() const { return mColorAttachments; }
|
||||
[[nodiscard]] Attachment const& getDepthAttachmentInfo() const { return mDepthAttachment; }
|
||||
[[nodiscard]] Attachment const& getStencilAttachmentInfo() const { return mStencilAttachment; }
|
||||
|
||||
// Static helpers for load/store operations
|
||||
[[nodiscard]] static wgpu::LoadOp getLoadOperation(RenderPassParams const& params,
|
||||
TargetBufferFlags buffer);
|
||||
[[nodiscard]] static wgpu::StoreOp getStoreOperation(RenderPassParams const& params,
|
||||
TargetBufferFlags buffer);
|
||||
|
||||
[[nodiscard]] TargetBufferFlags getTargetFlags() const { return mTargetFlags; }
|
||||
void setTargetFlags( TargetBufferFlags value) { mTargetFlags = value; }
|
||||
|
||||
private:
|
||||
bool mDefaultRenderTarget = false;
|
||||
TargetBufferFlags mTargetFlags = TargetBufferFlags::NONE;
|
||||
uint8_t mSamples = 1;
|
||||
uint8_t mLayerCount = 1;
|
||||
|
||||
@@ -81,9 +84,11 @@ private:
|
||||
Attachment mDepthAttachment{};
|
||||
Attachment mStencilAttachment{};
|
||||
|
||||
uint8_t mSampleCountPerAttachment = 0;
|
||||
|
||||
// Cached descriptors for the render pass
|
||||
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDescriptors;
|
||||
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{};
|
||||
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDesc;
|
||||
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDesc{};
|
||||
bool mHasDepthStencilAttachment = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ template<typename WebGPUPrintable>
|
||||
return out.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string adapterInfoToString(wgpu::AdapterInfo const& info) {
|
||||
[[nodiscard]] inline std::string adapterInfoToString(wgpu::AdapterInfo const& info) {
|
||||
std::stringstream out;
|
||||
out << "vendor (" << info.vendorID << ") '" << info.vendor
|
||||
<< "' device (" << info.deviceID << ") '" << info.device
|
||||
@@ -123,6 +123,122 @@ template<typename WebGPUPrintable>
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view webGPUTextureFormatToString(
|
||||
const wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::Undefined: return "Undefined";
|
||||
case wgpu::TextureFormat::R8Unorm: return "R8Unorm";
|
||||
case wgpu::TextureFormat::R8Snorm: return "R8Snorm";
|
||||
case wgpu::TextureFormat::R8Uint: return "R8Uint";
|
||||
case wgpu::TextureFormat::R8Sint: return "R8Sint";
|
||||
case wgpu::TextureFormat::R16Uint: return "R16Uint";
|
||||
case wgpu::TextureFormat::R16Sint: return "R16Sint";
|
||||
case wgpu::TextureFormat::R16Float: return "R16Float";
|
||||
case wgpu::TextureFormat::RG8Unorm: return "RG8Unorm";
|
||||
case wgpu::TextureFormat::RG8Snorm: return "RG8Snorm";
|
||||
case wgpu::TextureFormat::RG8Uint: return "RG8Uint";
|
||||
case wgpu::TextureFormat::RG8Sint: return "RG8Sint";
|
||||
case wgpu::TextureFormat::R32Float: return "R32Float";
|
||||
case wgpu::TextureFormat::R32Uint: return "R32Uint";
|
||||
case wgpu::TextureFormat::R32Sint: return "R32Sint";
|
||||
case wgpu::TextureFormat::RG16Uint: return "RG16Uint";
|
||||
case wgpu::TextureFormat::RG16Sint: return "RG16Sint";
|
||||
case wgpu::TextureFormat::RG16Float: return "RG16Float";
|
||||
case wgpu::TextureFormat::RGBA8Unorm: return "RGBA8Unorm";
|
||||
case wgpu::TextureFormat::RGBA8UnormSrgb: return "RGBA8UnormSrgb";
|
||||
case wgpu::TextureFormat::RGBA8Snorm: return "RGBA8Snorm";
|
||||
case wgpu::TextureFormat::RGBA8Uint: return "RGBA8Uint";
|
||||
case wgpu::TextureFormat::RGBA8Sint: return "RGBA8Sint";
|
||||
case wgpu::TextureFormat::BGRA8Unorm: return "BGRA8Unorm";
|
||||
case wgpu::TextureFormat::BGRA8UnormSrgb: return "BGRA8UnormSrgb";
|
||||
case wgpu::TextureFormat::RGB10A2Uint: return "RGB10A2Uint";
|
||||
case wgpu::TextureFormat::RGB10A2Unorm: return "RGB10A2Unorm";
|
||||
case wgpu::TextureFormat::RG11B10Ufloat: return "RG11B10Ufloat";
|
||||
case wgpu::TextureFormat::RGB9E5Ufloat: return "RGB9E5Ufloat";
|
||||
case wgpu::TextureFormat::RG32Float: return "RG32Float";
|
||||
case wgpu::TextureFormat::RG32Uint: return "RG32Uint";
|
||||
case wgpu::TextureFormat::RG32Sint: return "RG32Sint";
|
||||
case wgpu::TextureFormat::RGBA16Uint: return "RGBA16Uint";
|
||||
case wgpu::TextureFormat::RGBA16Sint: return "RGBA16Sint";
|
||||
case wgpu::TextureFormat::RGBA16Float: return "RGBA16Float";
|
||||
case wgpu::TextureFormat::RGBA32Float: return "RGBA32Float";
|
||||
case wgpu::TextureFormat::RGBA32Uint: return "RGBA32Uint";
|
||||
case wgpu::TextureFormat::RGBA32Sint: return "RGBA32Sint";
|
||||
case wgpu::TextureFormat::Stencil8: return "Stencil8";
|
||||
case wgpu::TextureFormat::Depth16Unorm: return "Depth16Unorm";
|
||||
case wgpu::TextureFormat::Depth24Plus: return "Depth24Plus";
|
||||
case wgpu::TextureFormat::Depth24PlusStencil8: return "Depth24PlusStencil8";
|
||||
case wgpu::TextureFormat::Depth32Float: return "Depth32Float";
|
||||
case wgpu::TextureFormat::Depth32FloatStencil8: return "Depth32FloatStencil8";
|
||||
case wgpu::TextureFormat::BC1RGBAUnorm: return "BC1RGBAUnorm";
|
||||
case wgpu::TextureFormat::BC1RGBAUnormSrgb: return "BC1RGBAUnormSrgb";
|
||||
case wgpu::TextureFormat::BC2RGBAUnorm: return "BC2RGBAUnorm";
|
||||
case wgpu::TextureFormat::BC2RGBAUnormSrgb: return "BC2RGBAUnormSrgb";
|
||||
case wgpu::TextureFormat::BC3RGBAUnorm: return "BC3RGBAUnorm";
|
||||
case wgpu::TextureFormat::BC3RGBAUnormSrgb: return "BC3RGBAUnormSrgb";
|
||||
case wgpu::TextureFormat::BC4RUnorm: return "BC4RUnorm";
|
||||
case wgpu::TextureFormat::BC4RSnorm: return "BC4RSnorm";
|
||||
case wgpu::TextureFormat::BC5RGUnorm: return "BC5RGUnorm";
|
||||
case wgpu::TextureFormat::BC5RGSnorm: return "BC5RGSnorm";
|
||||
case wgpu::TextureFormat::BC6HRGBUfloat: return "BC6HRGBUfloat";
|
||||
case wgpu::TextureFormat::BC6HRGBFloat: return "BC6HRGBFloat";
|
||||
case wgpu::TextureFormat::BC7RGBAUnorm: return "BC7RGBAUnorm";
|
||||
case wgpu::TextureFormat::BC7RGBAUnormSrgb: return "BC7RGBAUnormSrgb";
|
||||
case wgpu::TextureFormat::ETC2RGB8Unorm: return "ETC2RGB8Unorm";
|
||||
case wgpu::TextureFormat::ETC2RGB8UnormSrgb: return "ETC2RGB8UnormSrgb";
|
||||
case wgpu::TextureFormat::ETC2RGB8A1Unorm: return "ETC2RGB8A1Unorm";
|
||||
case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb: return "ETC2RGB8A1UnormSrgb";
|
||||
case wgpu::TextureFormat::ETC2RGBA8Unorm: return "ETC2RGBA8Unorm";
|
||||
case wgpu::TextureFormat::ETC2RGBA8UnormSrgb: return "ETC2RGBA8UnormSrgb";
|
||||
case wgpu::TextureFormat::EACR11Unorm: return "EACR11Unorm";
|
||||
case wgpu::TextureFormat::EACR11Snorm: return "EACR11Snorm";
|
||||
case wgpu::TextureFormat::EACRG11Unorm: return "EACRG11Unorm";
|
||||
case wgpu::TextureFormat::EACRG11Snorm: return "EACRG11Snorm";
|
||||
case wgpu::TextureFormat::ASTC4x4Unorm: return "ASTC4x4Unorm";
|
||||
case wgpu::TextureFormat::ASTC4x4UnormSrgb: return "ASTC4x4UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC5x4Unorm: return "ASTC5x4Unorm";
|
||||
case wgpu::TextureFormat::ASTC5x4UnormSrgb: return "ASTC5x4UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC5x5Unorm: return "ASTC5x5Unorm";
|
||||
case wgpu::TextureFormat::ASTC5x5UnormSrgb: return "ASTC5x5UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC6x5Unorm: return "ASTC6x5Unorm";
|
||||
case wgpu::TextureFormat::ASTC6x5UnormSrgb: return "ASTC6x5UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC6x6Unorm: return "ASTC6x6Unorm";
|
||||
case wgpu::TextureFormat::ASTC6x6UnormSrgb: return "ASTC6x6UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC8x5Unorm: return "ASTC8x5Unorm";
|
||||
case wgpu::TextureFormat::ASTC8x5UnormSrgb: return "ASTC8x5UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC8x6Unorm: return "ASTC8x6Unorm";
|
||||
case wgpu::TextureFormat::ASTC8x6UnormSrgb: return "ASTC8x6UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC8x8Unorm: return "ASTC8x8Unorm";
|
||||
case wgpu::TextureFormat::ASTC8x8UnormSrgb: return "ASTC8x8UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC10x5Unorm: return "ASTC10x5Unorm";
|
||||
case wgpu::TextureFormat::ASTC10x5UnormSrgb: return "ASTC10x5UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC10x6Unorm: return "ASTC10x6Unorm";
|
||||
case wgpu::TextureFormat::ASTC10x6UnormSrgb: return "ASTC10x6UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC10x8Unorm: return "ASTC10x8Unorm";
|
||||
case wgpu::TextureFormat::ASTC10x8UnormSrgb: return "ASTC10x8UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC10x10Unorm: return "ASTC10x10Unorm";
|
||||
case wgpu::TextureFormat::ASTC10x10UnormSrgb: return "ASTC10x10UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC12x10Unorm: return "ASTC12x10Unorm";
|
||||
case wgpu::TextureFormat::ASTC12x10UnormSrgb: return "ASTC12x10UnormSrgb";
|
||||
case wgpu::TextureFormat::ASTC12x12Unorm: return "ASTC12x12Unorm";
|
||||
case wgpu::TextureFormat::ASTC12x12UnormSrgb: return "ASTC12x12UnormSrgb";
|
||||
case wgpu::TextureFormat::R16Unorm: return "R16Unorm";
|
||||
case wgpu::TextureFormat::RG16Unorm: return "RG16Unorm";
|
||||
case wgpu::TextureFormat::RGBA16Unorm: return "RGBA16Unorm";
|
||||
case wgpu::TextureFormat::R16Snorm: return "R16Snorm";
|
||||
case wgpu::TextureFormat::RG16Snorm: return "RG16Snorm";
|
||||
case wgpu::TextureFormat::RGBA16Snorm: return "RGBA16Snorm";
|
||||
case wgpu::TextureFormat::R8BG8Biplanar420Unorm: return "R8BG8Biplanar420Unorm";
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm: return "R10X6BG10X6Biplanar420Unorm";
|
||||
case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm: return "R8BG8A8Triplanar420Unorm";
|
||||
case wgpu::TextureFormat::R8BG8Biplanar422Unorm: return "R8BG8Biplanar422Unorm";
|
||||
case wgpu::TextureFormat::R8BG8Biplanar444Unorm: return "R8BG8Biplanar444Unorm";
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm: return "R10X6BG10X6Biplanar422Unorm";
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm: return "R10X6BG10X6Biplanar444Unorm";
|
||||
case wgpu::TextureFormat::External: return "External";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view filamentShaderStageToString(const ShaderStage stage) {
|
||||
switch (stage) {
|
||||
case ShaderStage::VERTEX: return "vertex";
|
||||
|
||||
@@ -42,7 +42,7 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
|
||||
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
||||
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
||||
[](wgpu::TextureFormat const format) {
|
||||
FWGPU_LOGI << " " << webGPUPrintableToString(format);
|
||||
FWGPU_LOGI << " " << webGPUTextureFormatToString(format);
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount << "):";
|
||||
@@ -66,7 +66,7 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
|
||||
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
wgpu::TextureFormat depthFormat) {
|
||||
FWGPU_LOGI << "WebGPU surface configuration:";
|
||||
FWGPU_LOGI << " surface format: " << webGPUPrintableToString(config.format);
|
||||
FWGPU_LOGI << " surface format: " << webGPUTextureFormatToString(config.format);
|
||||
FWGPU_LOGI << " surface usage: " << webGPUPrintableToString(config.usage);
|
||||
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):";
|
||||
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
|
||||
@@ -79,7 +79,7 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
FWGPU_LOGI << " surface width: " << config.width;
|
||||
FWGPU_LOGI << " surface height: " << config.height;
|
||||
FWGPU_LOGI << " surface present mode: " << webGPUPrintableToString(config.presentMode);
|
||||
FWGPU_LOGI << "WebGPU selected depth format: " << webGPUPrintableToString(depthFormat);
|
||||
FWGPU_LOGI << "WebGPU selected depth format: " << webGPUTextureFormatToString(depthFormat);
|
||||
}
|
||||
#endif// FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
|
||||
@@ -110,7 +110,6 @@ void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
return wgpu::TextureFormat::Depth24PlusStencil8;
|
||||
}
|
||||
} else {
|
||||
// other options: Depth16Unorm or Depth24Plus
|
||||
return wgpu::TextureFormat::Depth32Float;
|
||||
}
|
||||
}
|
||||
@@ -217,10 +216,9 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::TextureView createDepthTextureView(wgpu::Texture const& depthTexture,
|
||||
wgpu::TextureFormat depthFormat, bool needStencil) {
|
||||
wgpu::TextureFormat const& depthFormat, bool const needStencil) {
|
||||
wgpu::TextureViewDescriptor descriptor{
|
||||
.label = "depth_texture_view",
|
||||
.format = depthFormat,
|
||||
.dimension = wgpu::TextureViewDimension::e2D,
|
||||
.baseMipLevel = 0,
|
||||
.mipLevelCount = 1,
|
||||
@@ -231,6 +229,16 @@ void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
|
||||
};
|
||||
if (needStencil) {
|
||||
descriptor.aspect = wgpu::TextureAspect::All;
|
||||
descriptor.format = depthFormat;
|
||||
} else {
|
||||
descriptor.aspect = wgpu::TextureAspect::DepthOnly;
|
||||
if (depthFormat == wgpu::TextureFormat::Depth32FloatStencil8) {
|
||||
descriptor.format = wgpu::TextureFormat::Depth32Float;
|
||||
} else if (depthFormat == wgpu::TextureFormat::Depth24PlusStencil8) {
|
||||
descriptor.format = wgpu::TextureFormat::Depth24Plus;
|
||||
} else {
|
||||
descriptor.format = depthFormat;
|
||||
}
|
||||
}
|
||||
wgpu::TextureView depthTextureView = depthTexture.CreateView(&descriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(depthTextureView) << "Failed to create depth texture view";
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "WebGPUTexture.h"
|
||||
|
||||
#include "WebGPUConstants.h"
|
||||
#include "WebGPURenderPassMipmapGenerator.h"
|
||||
#include "WebGPUStrings.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/BackendUtils.h"
|
||||
@@ -140,10 +142,13 @@ namespace {
|
||||
* @param samples How many samples to use for MSAA
|
||||
* @param needsComputeStorageSupport if we need to use this texture as storage binding in something
|
||||
* like a compute shader
|
||||
* @param needsRenderAttachmentSupport if we need to use this texture as a render pass attachment
|
||||
* in something like a render pass blit (e.g. mipmap generation)
|
||||
* @return The appropriate texture usage flags for the underlying texture
|
||||
*/
|
||||
[[nodiscard]] wgpu::TextureUsage fToWGPUTextureUsage(TextureUsage const& fUsage,
|
||||
const uint8_t samples, const bool needsComputeStorageSupport) {
|
||||
const uint8_t samples, const bool needsComputeStorageSupport,
|
||||
const bool needsRenderAttachmentSupport) {
|
||||
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
|
||||
|
||||
// if needsComputeStorageSupport we need to read and write to the texture in a shader and thus
|
||||
@@ -164,6 +169,16 @@ namespace {
|
||||
if (needsComputeStorageSupport) {
|
||||
retUsage |= wgpu::TextureUsage::StorageBinding;
|
||||
}
|
||||
if (needsRenderAttachmentSupport) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
if (any(TextureUsage::BLIT_SRC & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
}
|
||||
if (any(TextureUsage::BLIT_DST & fUsage)) {
|
||||
retUsage |= wgpu::TextureUsage::RenderAttachment;
|
||||
retUsage |= wgpu::TextureUsage::TextureBinding;
|
||||
}
|
||||
// WGPU Render attachment covers either color or stencil situation dependant
|
||||
// NOTE: Depth attachment isn't used this way in Vulkan but logically maps to WGPU docs. If
|
||||
// issues, investigate here.
|
||||
@@ -258,6 +273,26 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] WebGPUTexture::MipmapGenerationStrategy determineMipmapGenerationStrategy(
|
||||
const wgpu::TextureFormat format, const SamplerType samplerType, const uint8_t sampleCount,
|
||||
const uint8_t mipmapLevels) {
|
||||
if (mipmapLevels <= 1) {
|
||||
return WebGPUTexture::MipmapGenerationStrategy::NONE; // no mipmap generation needed
|
||||
}
|
||||
const WebGPURenderPassMipmapGenerator::FormatCompatibility renderPassCompatibility{
|
||||
WebGPURenderPassMipmapGenerator::getCompatibilityFor(format,
|
||||
toWebGPUTextureDimension(samplerType), sampleCount)
|
||||
};
|
||||
if (renderPassCompatibility.compatible) {
|
||||
return WebGPUTexture::MipmapGenerationStrategy::RENDER_PASS;
|
||||
}
|
||||
if (WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(format)) {
|
||||
return WebGPUTexture::MipmapGenerationStrategy::SPD_COMPUTE_PASS;
|
||||
}
|
||||
// we don't have a way to generate mipmaps for this texture, at least at this time
|
||||
return WebGPUTexture::MipmapGenerationStrategy::NONE;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::Extent3D toTextureSize(const SamplerType samplerType,
|
||||
const uint32_t width, const uint32_t height, const uint32_t depth) {
|
||||
switch (samplerType) {
|
||||
@@ -295,14 +330,17 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
|
||||
wgpu::Device const& device) noexcept
|
||||
: HwTexture{ samplerType, levels, samples, width, height, depth, format, usage },
|
||||
mViewFormat{ fToWGPUTextureFormat(format) },
|
||||
mSupportsMultipleMipLevels{ levels > 1 &&
|
||||
supportsMultipleMipLevelsViaStorageBinding(mViewFormat) },
|
||||
mWebGPUFormat{ mSupportsMultipleMipLevels
|
||||
mMipmapGenerationStrategy{ determineMipmapGenerationStrategy(mViewFormat, samplerType,
|
||||
samples, levels) },
|
||||
mWebGPUFormat{ mMipmapGenerationStrategy == MipmapGenerationStrategy::SPD_COMPUTE_PASS
|
||||
? storageBindingCompatibleFormatForViewFormat(mViewFormat)
|
||||
: mViewFormat },
|
||||
mAspect{ fToWGPUTextureViewAspect(usage, format) },
|
||||
mWebGPUUsage{ fToWGPUTextureUsage(usage, samples, mSupportsMultipleMipLevels) },
|
||||
mViewUsage{ fToWGPUTextureUsage(usage, samples, false) },
|
||||
mWebGPUUsage{ fToWGPUTextureUsage(usage, samples,
|
||||
mMipmapGenerationStrategy == MipmapGenerationStrategy::SPD_COMPUTE_PASS,
|
||||
mMipmapGenerationStrategy == MipmapGenerationStrategy::RENDER_PASS) },
|
||||
mViewUsage{ fToWGPUTextureUsage(usage, samples, false, false) },
|
||||
mDimension{toWebGPUTextureViewDimension(samplerType)},
|
||||
mBlockWidth{ filament::backend::getBlockWidth(format) },
|
||||
mBlockHeight{ filament::backend::getBlockHeight(format) },
|
||||
mDefaultMipLevel{ 0 },
|
||||
@@ -352,7 +390,7 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
|
||||
: HwTexture{ src->target, levelCount, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage },
|
||||
mViewFormat{ src->mViewFormat },
|
||||
mSupportsMultipleMipLevels{ src->mSupportsMultipleMipLevels },
|
||||
mMipmapGenerationStrategy{ src->mMipmapGenerationStrategy },
|
||||
mWebGPUFormat{ src->mWebGPUFormat },
|
||||
mAspect{ src->mAspect },
|
||||
mWebGPUUsage{ src->mWebGPUUsage },
|
||||
@@ -364,7 +402,21 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
|
||||
mDefaultMipLevel{ baseLevel },
|
||||
mDefaultBaseArrayLayer{ src->mArrayLayerCount },
|
||||
mDefaultTextureView{ makeTextureView(mDefaultMipLevel, levelCount, 0, mDefaultBaseArrayLayer,
|
||||
src->target) } {}
|
||||
src->target) },
|
||||
mMsaaSidecarTexture{ src->mMsaaSidecarTexture } {}
|
||||
|
||||
wgpu::Texture const& WebGPUTexture::getMsaaSidecarTexture(const uint8_t sampleCount) const {
|
||||
if (mMsaaSidecarTexture == nullptr) {
|
||||
return mMsaaSidecarTexture; // nullptr (no such sidecar)
|
||||
}
|
||||
FILAMENT_CHECK_PRECONDITION(sampleCount == mMsaaSidecarTexture.GetSampleCount())
|
||||
<< "The MSAA sidecar texture has a different sample count ("
|
||||
<< mMsaaSidecarTexture.GetSampleCount() << ") than requested (" << +sampleCount
|
||||
<< "). Note that this restriction was written when WebGPU only supported msaa "
|
||||
"textures with 4 samples. If that has changed, this implementation should be "
|
||||
"updated (e.g. map of sidecar textures by sampleCount or something).";
|
||||
return mMsaaSidecarTexture;
|
||||
}
|
||||
|
||||
bool WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(const wgpu::TextureFormat format) {
|
||||
return storageBindingCompatibleFormatForViewFormat(format) != wgpu::TextureFormat::Undefined;
|
||||
@@ -379,6 +431,68 @@ wgpu::TextureView WebGPUTexture::getOrMakeTextureView(const uint8_t mipLevel,
|
||||
return makeTextureView(mipLevel, 1, arrayLayer, 1, target);
|
||||
}
|
||||
|
||||
void WebGPUTexture::createMsaaSidecarTextureIfNotAlreadyCreated(const uint8_t samples,
|
||||
wgpu::Device const& device) {
|
||||
FILAMENT_CHECK_PRECONDITION(samples > 1) << "Requesting to create a MSAA sidecar texture for "
|
||||
<< +samples << " samples? Invalid request.";
|
||||
if (mMsaaSidecarTexture) {
|
||||
FILAMENT_CHECK_PRECONDITION(mMsaaSidecarTexture.GetSampleCount() == samples)
|
||||
<< "An MSAA sidecar texture has already been created for this texture, but with a "
|
||||
"different sample count ("
|
||||
<< mMsaaSidecarTexture.GetSampleCount() << ") than requested (" << +samples
|
||||
<< "). Note that this restriction was written when WebGPU only supported msaa "
|
||||
"textures with 4 samples. If that has changed, this implementation should be "
|
||||
"updated (e.g. map of sidecar textures by sampleCount or something).";
|
||||
return; // we already have the sidecar created
|
||||
}
|
||||
const wgpu::TextureDescriptor descriptor{
|
||||
.label = "msaa_sidecar_texture",
|
||||
.usage = mTexture.GetUsage(),
|
||||
.dimension = mTexture.GetDimension(),
|
||||
.size = {
|
||||
.width = mTexture.GetWidth(),
|
||||
.height = mTexture.GetHeight(),
|
||||
.depthOrArrayLayers = mTexture.GetDepthOrArrayLayers(),
|
||||
},
|
||||
.format = mTexture.GetFormat(),
|
||||
.mipLevelCount = mTexture.GetMipLevelCount(),
|
||||
.sampleCount = samples,
|
||||
.viewFormatCount = 1,
|
||||
.viewFormats = &mViewFormat,
|
||||
};
|
||||
mMsaaSidecarTexture = device.CreateTexture(&descriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(mMsaaSidecarTexture) << "Failed to create MSAA sidecar texture";
|
||||
}
|
||||
|
||||
wgpu::TextureView WebGPUTexture::makeMsaaSidecarTextureViewIfTextureSidecarExists(
|
||||
const uint8_t samples, const uint8_t mipLevel, const uint32_t arrayLayer) const {
|
||||
if (mMsaaSidecarTexture == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
FILAMENT_CHECK_PRECONDITION(mMsaaSidecarTexture.GetSampleCount() == samples)
|
||||
<< "An MSAA sidecar texture has already been created for this texture, but with a "
|
||||
"different sample count ("
|
||||
<< mMsaaSidecarTexture.GetSampleCount() << ") than requested for view (" << +samples
|
||||
<< "). Note that this restriction was written when WebGPU only supported msaa "
|
||||
"textures with 4 samples. If that has changed, this implementation should be "
|
||||
"updated (e.g. map of sidecar textures by sampleCount or something).";
|
||||
const wgpu::TextureViewDescriptor descriptor{
|
||||
.label = "msaa_sidecar_texture_view",
|
||||
.format = mViewFormat,
|
||||
.dimension = mDimension,
|
||||
.baseMipLevel = mipLevel,
|
||||
.mipLevelCount = 1,
|
||||
.baseArrayLayer = arrayLayer,
|
||||
.arrayLayerCount = 1,
|
||||
.aspect = mAspect,
|
||||
.usage = mViewUsage,
|
||||
};
|
||||
const wgpu::TextureView textureView{ mMsaaSidecarTexture.CreateView(&descriptor) };
|
||||
FILAMENT_CHECK_POSTCONDITION(mMsaaSidecarTexture)
|
||||
<< "Failed to create MSAA sidecar texture view (" << +samples << " samples)";
|
||||
return textureView;
|
||||
}
|
||||
|
||||
wgpu::TextureFormat WebGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFormat) {
|
||||
switch (fFormat) {
|
||||
case TextureFormat::R8: return wgpu::TextureFormat::R8Unorm;
|
||||
@@ -553,13 +667,6 @@ wgpu::TextureFormat WebGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFo
|
||||
wgpu::TextureView WebGPUTexture::makeTextureView(const uint8_t& baseLevel,
|
||||
const uint8_t& levelCount, const uint32_t& baseArrayLayer, const uint32_t& arrayLayerCount,
|
||||
const SamplerType samplerType) const noexcept {
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
|
||||
if (baseLevel > 0 && !mSupportsMultipleMipLevels) {
|
||||
FWGPU_LOGW << "Trying to make a texture view into a level ("
|
||||
<< static_cast<uint32_t>(baseLevel)
|
||||
<< ") for which we cannot generate mip levels.";
|
||||
}
|
||||
#endif
|
||||
const wgpu::TextureViewDescriptor textureViewDescriptor{
|
||||
.label = getUserTextureViewLabel(target),
|
||||
.format = mViewFormat,
|
||||
|
||||
@@ -26,8 +26,28 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
[[nodiscard]] constexpr bool hasStencil(const wgpu::TextureFormat textureFormat) {
|
||||
return textureFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32FloatStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Stencil8;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool hasDepth(const wgpu::TextureFormat textureFormat) {
|
||||
return textureFormat == wgpu::TextureFormat::Depth16Unorm ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32Float ||
|
||||
textureFormat == wgpu::TextureFormat::Depth24Plus ||
|
||||
textureFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
|
||||
textureFormat == wgpu::TextureFormat::Depth32FloatStencil8;
|
||||
}
|
||||
|
||||
class WebGPUTexture : public HwTexture {
|
||||
public:
|
||||
enum class MipmapGenerationStrategy : uint8_t {
|
||||
RENDER_PASS,
|
||||
SPD_COMPUTE_PASS,
|
||||
NONE,
|
||||
};
|
||||
|
||||
WebGPUTexture(SamplerType, uint8_t levels, TextureFormat, uint8_t samples, uint32_t width,
|
||||
uint32_t height, uint32_t depth, TextureUsage, wgpu::Device const&) noexcept;
|
||||
|
||||
@@ -41,17 +61,49 @@ public:
|
||||
|
||||
[[nodiscard]] wgpu::Texture const& getTexture() const { return mTexture; }
|
||||
|
||||
[[nodiscard]] wgpu::Texture const& getMsaaSidecarTexture(uint8_t sampleCount) const;
|
||||
|
||||
[[nodiscard]] wgpu::TextureView const& getDefaultTextureView() const {
|
||||
return mDefaultTextureView;
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::TextureView getOrMakeTextureView(uint8_t mipLevel, uint32_t arrayLayer);
|
||||
|
||||
[[nodiscard]] wgpu::TextureViewDimension getViewDimension() const { return mDimension; }
|
||||
[[nodiscard]] wgpu::TextureFormat getViewFormat() const { return mViewFormat; }
|
||||
|
||||
[[nodiscard]] uint32_t getArrayLayerCount() const { return mArrayLayerCount; }
|
||||
|
||||
[[nodiscard]] bool supportsMultipleMipLevels() const { return mSupportsMultipleMipLevels; }
|
||||
[[nodiscard]] MipmapGenerationStrategy getMipmapGenerationStrategy() const {
|
||||
return mMipmapGenerationStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the MSAA sidecar texture if it has not already been created.
|
||||
* If the sidecar already exists with a different number of samples this will panic
|
||||
* (at the time of writing this WebGPU only supports MSAA with 4 samples. If this changes, then
|
||||
* multiple sidecars with different number of samples could be supported if needed, but that
|
||||
* level of complexity is not warranted at this time).
|
||||
* Additionally, if samples is <= 1 this will panic as well, as that is not an MSAA texture.
|
||||
* @param samples The number of samples the texture will have
|
||||
*/
|
||||
void createMsaaSidecarTextureIfNotAlreadyCreated(uint8_t samples, wgpu::Device const&);
|
||||
|
||||
/**
|
||||
* @param samples The number of samples the underlying texture supports
|
||||
* @param mipLevel The mip level into the underyling texture for which this view will reference
|
||||
* (this view will only have one mip level)
|
||||
* @param arrayLayer The layer into the underyling texture for which this view will reference
|
||||
* (this view will only have one layer)
|
||||
* @return A texture view for the MSAA sidecar texture
|
||||
*/
|
||||
wgpu::TextureView makeMsaaSidecarTextureViewIfTextureSidecarExists(uint8_t samples,
|
||||
uint8_t mipLevel, uint32_t arrayLayer) const;
|
||||
|
||||
/**
|
||||
* @return nullptr if a MSAA sidecar texture is not appliable, otherwise a view to one
|
||||
*/
|
||||
[[nodiscard]] wgpu::TextureView makeMsaaSidecarTextureView(wgpu::Texture const&, uint8_t mipLevel, uint32_t arrayLayer) const;
|
||||
|
||||
[[nodiscard]] static wgpu::TextureFormat fToWGPUTextureFormat(
|
||||
filament::backend::TextureFormat const& fFormat);
|
||||
@@ -71,8 +123,7 @@ private:
|
||||
// certain formats (e.g. compute shaders), but we need a view to that format elsewhere
|
||||
// (e.g. another shader or render target)
|
||||
wgpu::TextureFormat mViewFormat = wgpu::TextureFormat::Undefined;
|
||||
// this is true IFF levels > 1 AND we can actually generate mipmaps for the texture
|
||||
bool mSupportsMultipleMipLevels = false;
|
||||
MipmapGenerationStrategy mMipmapGenerationStrategy = MipmapGenerationStrategy::NONE;
|
||||
// format is inherited from HwTexture. This naming is to distinguish it from Filament's format
|
||||
// this is the underlying texture's format, not necessarily the "view" of it... see mViewFormat
|
||||
wgpu::TextureFormat mWebGPUFormat = wgpu::TextureFormat::Undefined;
|
||||
@@ -80,6 +131,7 @@ private:
|
||||
// usage is inherited from HwTexture. This naming is to distinguish it from Filament's usage
|
||||
wgpu::TextureUsage mWebGPUUsage = wgpu::TextureUsage::None;
|
||||
wgpu::TextureUsage mViewUsage = wgpu::TextureUsage::None;
|
||||
wgpu::TextureViewDimension mDimension = wgpu::TextureViewDimension::Undefined;
|
||||
size_t mBlockWidth = 0;
|
||||
size_t mBlockHeight = 0;
|
||||
uint32_t mArrayLayerCount = 1;
|
||||
@@ -87,6 +139,11 @@ private:
|
||||
uint32_t mDefaultMipLevel = 0;
|
||||
uint32_t mDefaultBaseArrayLayer = 0;
|
||||
wgpu::TextureView mDefaultTextureView = nullptr;
|
||||
// At the time of writing this, WebGPU only supported 4 samples in a multi-sampled texture.
|
||||
// If that has changed, then consider updating the implementation to have a map of msaa textures
|
||||
// by sampleCount or something like that.
|
||||
// For now that complexity and cost is not warranted due to WebGPU's restrictions.
|
||||
wgpu::Texture mMsaaSidecarTexture = nullptr;
|
||||
|
||||
[[nodiscard]] wgpu::TextureView makeTextureView(const uint8_t& baseLevel,
|
||||
const uint8_t& levelCount, const uint32_t& baseArrayLayer,
|
||||
|
||||
@@ -56,10 +56,8 @@ namespace {
|
||||
// The SPD Algorithm can make use of up to 12 storage texture attachments
|
||||
constexpr uint32_t MAX_MIPMAP_STORAGE_TEXTURES_PER_STAGE = 12u;
|
||||
|
||||
constexpr std::array REQUIRED_FEATURES = { wgpu::FeatureName::TransientAttachments,
|
||||
/*To make filtering assumptions like we want while waiting for Filament to provide that info,
|
||||
float 32 needs to be filterable*/
|
||||
wgpu::FeatureName::Float32Filterable,
|
||||
constexpr std::array REQUIRED_FEATURES = {
|
||||
wgpu::FeatureName::TransientAttachments,
|
||||
// Qualcomm 500 and 600 GPUs do not support this so it is not part of core webgpu spec. To
|
||||
// support such devices, we will either need Filament to not attempt this, or find another
|
||||
// workaround. https://github.com/gpuweb/gpuweb/issues/2648
|
||||
|
||||
@@ -40,18 +40,24 @@ using namespace filament::math;
|
||||
|
||||
using namespace image;
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
namespace test {
|
||||
|
||||
Backend BackendTest::sBackend = Backend::NOOP;
|
||||
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
|
||||
bool BackendTest::sIsMobilePlatform = false;
|
||||
int BackendTest::sArgc = 0;
|
||||
char** BackendTest::sArgv = nullptr;
|
||||
std::vector<std::string> BackendTest::sFailedImages;
|
||||
|
||||
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
|
||||
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform,
|
||||
int argc, char** argv) {
|
||||
sBackend = backend;
|
||||
sOperatingSystem = operatingSystem;
|
||||
sIsMobilePlatform = isMobilePlatform;
|
||||
sArgc = argc;
|
||||
sArgv = argv;
|
||||
}
|
||||
|
||||
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
|
||||
@@ -160,6 +166,11 @@ void BackendTest::markImageAsFailure(std::string failedImageName) {
|
||||
sFailedImages.emplace_back(std::move(failedImageName));
|
||||
}
|
||||
|
||||
std::filesystem::path BackendTest::binaryDirectory() {
|
||||
assert(sArgc >= 1);
|
||||
return std::filesystem::path(sArgv[0]).remove_filename().string();
|
||||
}
|
||||
|
||||
void BackendTest::recordFailedImages() {
|
||||
if (!sFailedImages.empty()) {
|
||||
std::string failedImages;
|
||||
@@ -187,8 +198,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]) {
|
||||
BackendTest::init(backend, operatingSystem, isMobile);
|
||||
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc,
|
||||
char* argv[]) {
|
||||
BackendTest::init(backend, operatingSystem, isMobile, argc, argv);
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
::testing::AddGlobalTestEnvironment(new Environment);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include "private/backend/CommandBufferQueue.h"
|
||||
@@ -31,16 +33,20 @@ namespace test {
|
||||
|
||||
class BackendTest : public ::testing::Test {
|
||||
public:
|
||||
|
||||
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform);
|
||||
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform,
|
||||
int argc, char** argv);
|
||||
|
||||
static Backend sBackend;
|
||||
static OperatingSystem sOperatingSystem;
|
||||
static bool sIsMobilePlatform;
|
||||
static int sArgc;
|
||||
static char** sArgv;
|
||||
|
||||
// Takes the name of the image that wasn't correct, without the .png suffix
|
||||
static void markImageAsFailure(std::string failedImageName);
|
||||
|
||||
static std::filesystem::path binaryDirectory();
|
||||
|
||||
protected:
|
||||
|
||||
BackendTest();
|
||||
|
||||
@@ -59,28 +59,28 @@ uint32_t ScreenshotParams::expectedHash() const {
|
||||
return mExpectedPixelHash;
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::actualDirectoryPath() {
|
||||
return "images/actual_images";
|
||||
std::filesystem::path ScreenshotParams::actualDirectoryPath() {
|
||||
return BackendTest::binaryDirectory().append("images/actual_images");
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::actualFileName() const {
|
||||
return absl::StrFormat("%s_actual.png", mFileName);
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::actualFilePath() const {
|
||||
return absl::StrFormat("%s/%s", actualDirectoryPath(), actualFileName());
|
||||
std::filesystem::path ScreenshotParams::actualFilePath() const {
|
||||
return actualDirectoryPath().append(actualFileName());
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::expectedDirectoryPath() {
|
||||
return "images/expected_images";
|
||||
std::filesystem::path ScreenshotParams::expectedDirectoryPath() {
|
||||
return BackendTest::binaryDirectory().append("images/expected_images");
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::expectedFileName() const {
|
||||
return absl::StrFormat("%s.png", mFileName);
|
||||
}
|
||||
|
||||
std::string ScreenshotParams::expectedFilePath() const {
|
||||
return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName());
|
||||
std::filesystem::path ScreenshotParams::expectedFilePath() const {
|
||||
return expectedDirectoryPath().append(expectedFileName());
|
||||
}
|
||||
|
||||
const std::string ScreenshotParams::filePrefix() const {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef TNT_IMAGE_EXPECTATIONS_H
|
||||
#define TNT_IMAGE_EXPECTATIONS_H
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
@@ -51,12 +52,12 @@ public:
|
||||
bool isSrgb() const;
|
||||
uint32_t expectedHash() const;
|
||||
|
||||
static std::string actualDirectoryPath();
|
||||
static std::filesystem::path actualDirectoryPath();
|
||||
std::string actualFileName() const;
|
||||
std::string actualFilePath() const;
|
||||
static std::string expectedDirectoryPath();
|
||||
std::filesystem::path actualFilePath() const;
|
||||
static std::filesystem::path expectedDirectoryPath();
|
||||
std::string expectedFileName() const;
|
||||
std::string expectedFilePath() const;
|
||||
std::filesystem::path expectedFilePath() const;
|
||||
const std::string filePrefix() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -139,6 +139,24 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
return bufferDimension;
|
||||
}
|
||||
|
||||
void exportScreenshot(void* pixelData) const {
|
||||
#ifndef FILAMENT_IOS
|
||||
const size_t width = readRect.width, height = readRect.height;
|
||||
LinearImage image(width, height, 4);
|
||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) {
|
||||
image = toLinearWithAlpha<uint8_t>(width, height, width * 4, (uint8_t*)pixelData);
|
||||
}
|
||||
if (format == PixelDataFormat::RGBA && type == PixelDataType::FLOAT) {
|
||||
memcpy(image.getPixelRef(), pixelData, width * height * sizeof(math::float4));
|
||||
}
|
||||
std::string png = std::string(testName) + ".png";
|
||||
std::filesystem::path path = ScreenshotParams::actualDirectoryPath();
|
||||
path.append(png);
|
||||
std::ofstream outputStream(path.c_str(), std::ios::binary | std::ios::trunc);
|
||||
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "", png);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The format and type for the readPixels call.
|
||||
PixelDataFormat format = PixelDataFormat::RGBA;
|
||||
PixelDataType type = PixelDataType::UBYTE;
|
||||
@@ -311,6 +329,8 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
||||
const auto* test = (const TestCase*)user;
|
||||
assert_invariant(test);
|
||||
|
||||
test->exportScreenshot(buffer);
|
||||
|
||||
// Hash the contents of the buffer and check that they match.
|
||||
uint32_t hash = utils::hash::murmur3((const uint32_t*)buffer, size / 4, 0);
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ INPUT = ../libs/filabridge/include \
|
||||
../libs/gltfio/include \
|
||||
../libs/utils/include \
|
||||
backend/include \
|
||||
backend/include/private/backend/DriverAPI.dox \
|
||||
include
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
|
||||
@@ -190,6 +190,7 @@ public:
|
||||
using FeatureLevel = backend::FeatureLevel;
|
||||
using StereoscopicType = backend::StereoscopicType;
|
||||
using Driver = backend::Driver;
|
||||
using GpuContextPriority = backend::Platform::GpuContextPriority;
|
||||
|
||||
/**
|
||||
* Config is used to define the memory footprint used by the engine, such as the
|
||||
@@ -410,6 +411,11 @@ public:
|
||||
* @deprecated use "backend.opengl.assert_native_window_is_valid" feature flag instead
|
||||
*/
|
||||
bool assertNativeWindowIsValid = false;
|
||||
|
||||
/**
|
||||
* GPU context priority level. Controls GPU work scheduling and preemption.
|
||||
*/
|
||||
GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI {
|
||||
|
||||
public:
|
||||
using CullingMode = backend::CullingMode;
|
||||
using TransparencyMode = TransparencyMode;
|
||||
// ReSharper disable once CppRedundantQualifier
|
||||
using TransparencyMode = filament::TransparencyMode;
|
||||
using DepthFunc = backend::SamplerCompareFunc;
|
||||
using StencilCompareFunc = backend::SamplerCompareFunc;
|
||||
using StencilOperation = backend::StencilOperation;
|
||||
|
||||
@@ -2989,7 +2989,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
|
||||
}
|
||||
|
||||
if (dsrOptions.quality == QualityLevel::LOW) {
|
||||
return upscaleBilinear(fg, translucent, dsrOptions, input, vp, outDesc, filter);
|
||||
return upscaleBilinear(fg, dsrOptions, input, vp, outDesc, filter);
|
||||
}
|
||||
if (dsrOptions.quality == QualityLevel::MEDIUM) {
|
||||
return upscaleSGSR1(fg, sourceHasLuminance, dsrOptions, input, vp, outDesc);
|
||||
@@ -2997,7 +2997,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
|
||||
return upscaleFSR1(fg, dsrOptions, input, vp, outDesc);
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph& fg, bool translucent,
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph& fg,
|
||||
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> const input,
|
||||
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
|
||||
SamplerMagFilter filter) noexcept {
|
||||
@@ -3016,18 +3016,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
|
||||
.attachments = { .color = { data.output } },
|
||||
.clearFlags = TargetBufferFlags::DEPTH });
|
||||
},
|
||||
[this, vp, translucent, filter](FrameGraphResources const& resources,
|
||||
[this, vp, filter](FrameGraphResources const& resources,
|
||||
auto const& data, DriverApi& driver) {
|
||||
bindPostProcessDescriptorSet(driver);
|
||||
|
||||
// helper to enable blending
|
||||
auto enableTranslucentBlending = [](PipelineState& pipeline) {
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA;
|
||||
pipeline.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
|
||||
};
|
||||
|
||||
auto color = resources.getTexture(data.input);
|
||||
auto const& inputDesc = resources.getDescriptor(data.input);
|
||||
|
||||
@@ -3055,9 +3047,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
|
||||
auto out = resources.getRenderPassInfo();
|
||||
|
||||
auto pipeline = getPipelineState(material.getMaterial(mEngine));
|
||||
if (translucent) {
|
||||
enableTranslucentBlending(pipeline);
|
||||
}
|
||||
renderFullScreenQuad(out, pipeline, driver);
|
||||
});
|
||||
|
||||
@@ -3065,7 +3054,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
|
||||
|
||||
// if we had to take the low quality fallback, we still do the "sharpen pass"
|
||||
if (dsrOptions.sharpness > 0.0f) {
|
||||
output = rcas(fg, dsrOptions.sharpness, output, outDesc, translucent);
|
||||
output = rcas(fg, dsrOptions.sharpness, output, outDesc,
|
||||
false /* translucent=false, because dst buffer is allocated */);
|
||||
}
|
||||
|
||||
// we rely on automatic culling of unused render passes
|
||||
@@ -3443,9 +3433,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
|
||||
return input;
|
||||
}
|
||||
|
||||
// we currently don't support stencil resolve
|
||||
assert_invariant(!isStencilFormat(inDesc.format));
|
||||
|
||||
// The Metal / Vulkan backends currently don't support depth/stencil resolve.
|
||||
if (isDepthFormat(inDesc.format) && (!mEngine.getDriverApi().isDepthStencilResolveSupported())) {
|
||||
return resolveDepth(fg, outputBufferName, input, outDesc);
|
||||
@@ -3463,6 +3450,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolve(FrameGraph& fg,
|
||||
|
||||
auto const& ppResolve = fg.addPass<ResolveData>("resolve",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
// we currently don't support stencil resolve.
|
||||
assert_invariant(!isStencilFormat(inDesc.format));
|
||||
|
||||
data.input = builder.read(input, FrameGraphTexture::Usage::BLIT_SRC);
|
||||
data.output = builder.createTexture(outputBufferName, outDesc);
|
||||
data.output = builder.write(data.output, FrameGraphTexture::Usage::BLIT_DST);
|
||||
@@ -3514,6 +3504,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::resolveDepth(FrameGraph& fg,
|
||||
|
||||
auto const& ppResolve = fg.addPass<ResolveData>("resolveDepth",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
// we currently don't support stencil resolve
|
||||
assert_invariant(!isStencilFormat(inDesc.format));
|
||||
|
||||
data.input = builder.sample(input);
|
||||
data.output = builder.createTexture(outputBufferName, outDesc);
|
||||
data.output = builder.write(data.output, FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
|
||||
|
||||
@@ -257,7 +257,7 @@ public:
|
||||
FrameGraphId<FrameGraphTexture> input, Viewport const& vp,
|
||||
FrameGraphTexture::Descriptor const& outDesc, backend::SamplerMagFilter filter) noexcept;
|
||||
|
||||
FrameGraphId<FrameGraphTexture> upscaleBilinear(FrameGraph& fg, bool translucent,
|
||||
FrameGraphId<FrameGraphTexture> upscaleBilinear(FrameGraph& fg,
|
||||
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> input,
|
||||
Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
|
||||
backend::SamplerMagFilter filter) noexcept;
|
||||
|
||||
@@ -141,6 +141,7 @@ Engine* FEngine::create(Builder const& builder) {
|
||||
.stereoscopicType = instance->getConfig().stereoscopicType,
|
||||
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
|
||||
.gpuContextPriority = instance->getConfig().gpuContextPriority,
|
||||
};
|
||||
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
|
||||
|
||||
@@ -761,6 +762,7 @@ int FEngine::loop() {
|
||||
.stereoscopicType = mConfig.stereoscopicType,
|
||||
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
|
||||
.gpuContextPriority = mConfig.gpuContextPriority,
|
||||
};
|
||||
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
|
||||
|
||||
@@ -822,9 +824,9 @@ int FEngine::loop() {
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
if(debug.server) {
|
||||
delete debug.server;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if FILAMENT_ENABLE_FGVIEWER
|
||||
#if FILAMENT_ENABLE_FGVIEWER
|
||||
if(debug.fgviewerServer) {
|
||||
delete debug.fgviewerServer;
|
||||
}
|
||||
|
||||
@@ -98,6 +98,14 @@ namespace filament::fgviewer {
|
||||
} // namespace filament::fgviewer
|
||||
#endif
|
||||
|
||||
// We have added correctness assertions that breaks clients' projects. We add this define to allow
|
||||
// for the client's to address these assertions at a more gradual pace.
|
||||
#if defined(FILAMENT_RELAXED_CORRECTNESS_ASSERTIONS)
|
||||
#define CORRECTNESS_ASSERTION_DEFAULT false
|
||||
#else
|
||||
#define CORRECTNESS_ASSERTION_DEFAULT true
|
||||
#endif
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Renderer;
|
||||
@@ -715,10 +723,14 @@ public:
|
||||
bool use_shadow_atlas = false;
|
||||
} shadows;
|
||||
struct {
|
||||
// TODO: default the following two flags to true.
|
||||
bool assert_material_instance_in_use = false;
|
||||
bool assert_destroy_material_before_material_instance = false;
|
||||
bool assert_vertex_buffer_count_exceeds_8 = false;
|
||||
// TODO: clean-up the following flags (equivalent to setting them to true) when
|
||||
// clients have addressed their usages.
|
||||
bool assert_material_instance_in_use = CORRECTNESS_ASSERTION_DEFAULT;
|
||||
bool assert_destroy_material_before_material_instance =
|
||||
CORRECTNESS_ASSERTION_DEFAULT;
|
||||
bool assert_vertex_buffer_count_exceeds_8 = CORRECTNESS_ASSERTION_DEFAULT;
|
||||
bool assert_vertex_buffer_attribute_stride_mult_of_4 =
|
||||
CORRECTNESS_ASSERTION_DEFAULT;
|
||||
} debug;
|
||||
} engine;
|
||||
struct {
|
||||
@@ -759,6 +771,9 @@ public:
|
||||
{ "features.engine.debug.assert_vertex_buffer_count_exceeds_8",
|
||||
"Assert when a client's number of buffers for a VertexBuffer exceeds 8.",
|
||||
&features.engine.debug.assert_vertex_buffer_count_exceeds_8, false },
|
||||
{ "features.engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4",
|
||||
"Assert that the attribute stride of a vertex buffer is a multiple of 4.",
|
||||
&features.engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4, false },
|
||||
}};
|
||||
|
||||
utils::Slice<const FeatureFlag> getFeatureFlags() const noexcept {
|
||||
|
||||
@@ -386,8 +386,9 @@ void FTexture::setImage(FEngine& engine, size_t const level,
|
||||
FILAMENT_CHECK_PRECONDITION(any(mUsage & Texture::Usage::UPLOADABLE))
|
||||
<< "Texture is not uploadable.";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mSampleCount <= 1) << "Operation not supported with multisample ("
|
||||
<< unsigned(mSampleCount) << ") texture.";
|
||||
FILAMENT_CHECK_PRECONDITION(mSampleCount <= 1)
|
||||
<< "Operation not supported with multisample ("
|
||||
<< unsigned(mSampleCount) << ") texture.";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(xoffset + width <= valueForLevel(level, mWidth))
|
||||
<< "xoffset (" << unsigned(xoffset) << ") + width (" << unsigned(width)
|
||||
@@ -428,28 +429,46 @@ void FTexture::setImage(FEngine& engine, size_t const level,
|
||||
<< ") > texture depth (" << effectiveTextureDepthOrLayers << ") at level ("
|
||||
<< unsigned(level) << ")";
|
||||
|
||||
if (UTILS_UNLIKELY(!width || !height || !depth)) {
|
||||
// The operation is a no-op, return immediately. The PixelBufferDescriptor callback
|
||||
// should be called automatically when the object is destroyed.
|
||||
// The precondition check below assumes width, height, depth non null.
|
||||
return;
|
||||
}
|
||||
|
||||
if (p.type != PixelDataType::COMPRESSED) {
|
||||
using PBD = PixelBufferDescriptor;
|
||||
size_t const stride = p.stride ? p.stride : width;
|
||||
size_t const bpp = PBD::computePixelSize(p.format, p.type);
|
||||
size_t const bpp = PBD::computeDataSize(p.format, p.type, 1, 1, 1);
|
||||
size_t const bpr = PBD::computeDataSize(p.format, p.type, stride, 1, p.alignment);
|
||||
size_t const pbdWidth = stride;
|
||||
// TODO: PBD should have a "layer stride" (using "depth" as a substitute).
|
||||
size_t const pbdDepth = depth;
|
||||
size_t const pbdHeight = p.size / bpr / pbdDepth;
|
||||
assert_invariant(p.size % bpr == 0 && (p.size / bpr) % pbdDepth == 0);
|
||||
|
||||
size_t const bpl = bpr * height; // TODO: PBD should have a "layer stride"
|
||||
// TODO: PBD should have a p.depth (# layers to skip)
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
bpp * (pbdWidth - p.left) * (pbdHeight - p.top) * (pbdDepth - 0) >=
|
||||
bpp * (width - xoffset) * (height - yoffset) * (depth - zoffset))
|
||||
|
||||
/* Calculates the byte offset of the last pixel in a 3D sub-region. */
|
||||
auto const calculateLastPixelOffset = [bpp, bpr, bpl](
|
||||
size_t xoff, size_t yoff, size_t zoff,
|
||||
size_t width, size_t height, size_t depth) {
|
||||
// The 0-indexed coordinates of the last pixel are:
|
||||
// x = xoff + width - 1
|
||||
// y = yoff + height - 1
|
||||
// z = zoff + depth - 1
|
||||
// The offset is calculated as: (z * bpl) + (y * bpr) + (x * bpp)
|
||||
return ((zoff + depth - 1) * bpl) +
|
||||
((yoff + height - 1) * bpr) +
|
||||
((xoff + width - 1) * bpp);
|
||||
};
|
||||
|
||||
size_t const lastPixelOffset = calculateLastPixelOffset(
|
||||
p.left, p.top, 0, width, height, depth);
|
||||
|
||||
// make sure the whole last pixel is in the buffer
|
||||
FILAMENT_CHECK_PRECONDITION(lastPixelOffset + bpp <= p.size)
|
||||
<< "buffer overflow: (size=" << size_t(p.size) << ", stride=" << size_t(p.stride)
|
||||
<< ", left=" << unsigned(p.left) << ", top=" << unsigned(p.top)
|
||||
<< ") smaller than specified region "
|
||||
"{{"
|
||||
<< unsigned(xoffset) << "," << unsigned(yoffset) << "," << unsigned(zoffset)
|
||||
<< "},{" << unsigned(width) << "," << unsigned(height) << "," << unsigned(depth)
|
||||
<< ")}}";
|
||||
<< unsigned(xoffset) << "," << unsigned(yoffset) << "," << unsigned(zoffset) << "},{"
|
||||
<< unsigned(width) << "," << unsigned(height) << "," << unsigned(depth) << ")}}";
|
||||
}
|
||||
|
||||
engine.getDriverApi().update3DImage(mHandle, uint8_t(level), xoffset, yoffset, zoffset, width,
|
||||
|
||||
@@ -162,8 +162,7 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
|
||||
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount <= DOCUMENTED_MAX_VERTEX_BUFFER_COUNT)
|
||||
<< "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
|
||||
} else if (mImpl->mBufferCount > DOCUMENTED_MAX_VERTEX_BUFFER_COUNT) {
|
||||
utils::slog.w << "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT
|
||||
<< utils::io::endl;
|
||||
LOG(WARNING) << "bufferCount cannot be more than " << DOCUMENTED_MAX_VERTEX_BUFFER_COUNT;
|
||||
}
|
||||
|
||||
// Next we check if any unused buffer slots have been allocated. This helps prevent errors
|
||||
@@ -178,9 +177,15 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
|
||||
<< "attribute " << j << " offset=" << attributes[j].offset
|
||||
<< " is not multiple of 4";
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION((attributes[j].stride & 0x3u) == 0)
|
||||
<< "attribute " << j << " stride=" << attributes[j].stride
|
||||
<< " is not multiple of 4";
|
||||
FEngine* fengine = static_cast<FEngine*>(&engine);
|
||||
if (fengine->features.engine.debug.assert_vertex_buffer_attribute_stride_mult_of_4) {
|
||||
FILAMENT_CHECK_PRECONDITION((attributes[j].stride & 0x3u) == 0)
|
||||
<< "attribute " << j << " stride=" << attributes[j].stride
|
||||
<< " is not multiple of 4";
|
||||
} else if ((attributes[j].stride & 0x3u) != 0) {
|
||||
LOG(WARNING) << "attribute " << j << " stride=" << attributes[j].stride
|
||||
<< " is not multiple of 4";
|
||||
}
|
||||
|
||||
if (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0) {
|
||||
FILAMENT_CHECK_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET))
|
||||
|
||||
@@ -66,6 +66,7 @@ DescriptorSet& DescriptorSet::operator=(DescriptorSet&& rhs) noexcept {
|
||||
mDirty = rhs.mDirty;
|
||||
mValid = rhs.mValid;
|
||||
mSetAfterCommitWarning = rhs.mSetAfterCommitWarning;
|
||||
mSetUndefinedParameterWarning = rhs.mSetUndefinedParameterWarning;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -106,11 +107,12 @@ void DescriptorSet::commitSlow(DescriptorSetLayout const& layout,
|
||||
});
|
||||
|
||||
auto const unsetValidDescriptors = layout.getValidDescriptors() & ~mValid;
|
||||
if (UTILS_VERY_UNLIKELY(!unsetValidDescriptors.empty())) {
|
||||
if (UTILS_VERY_UNLIKELY(!unsetValidDescriptors.empty() && !mSetUndefinedParameterWarning)) {
|
||||
unsetValidDescriptors.forEachSetBit([&](auto i) {
|
||||
LOG(WARNING) << (layout.isSampler(i) ? "Sampler" : "Buffer") << " descriptor " << i
|
||||
<< " of " << mName.c_str() << " is not set. Please report this issue.";
|
||||
});
|
||||
mSetUndefinedParameterWarning = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ private:
|
||||
mutable utils::bitset64 mValid; // 8
|
||||
backend::DescriptorSetHandle mDescriptorSetHandle; // 4
|
||||
mutable bool mSetAfterCommitWarning = false; // 1
|
||||
mutable bool mSetUndefinedParameterWarning = false; // 1
|
||||
utils::StaticString mName; // 16
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.61.1"
|
||||
spec.version = "1.62.0"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.61.1/filament-v1.61.1-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.62.0/filament-v1.62.0-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace filament {
|
||||
|
||||
// update this when a new version of filament wouldn't work with older materials
|
||||
static constexpr size_t MATERIAL_VERSION = 61;
|
||||
static constexpr size_t MATERIAL_VERSION = 62;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -96,7 +96,7 @@ static constexpr std::initializer_list<DescriptorSetLayoutBinding> perRenderable
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::OBJECT_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::BONES_UNIFORMS, DescriptorFlags::DYNAMIC_OFFSET },
|
||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, +PerRenderableBindingPoints::MORPHING_UNIFORMS },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_POSITIONS, DescriptorFlags::UNFILTERABLE },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_INT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::MORPH_TARGET_TANGENTS },
|
||||
{ DescriptorType::SAMPLER_2D_ARRAY_FLOAT, ShaderStageFlags::VERTEX , +PerRenderableBindingPoints::BONES_INDICES_AND_WEIGHTS },
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
@@ -664,19 +665,33 @@ public:
|
||||
precision(p),
|
||||
samplerType(t),
|
||||
format(f),
|
||||
parameterType(SAMPLER),
|
||||
unfilterable(unfilterable),
|
||||
multisample(ms),
|
||||
transformName(tn),
|
||||
stages(s) {}
|
||||
stages(s),
|
||||
parameterType(SAMPLER) {}
|
||||
|
||||
// Uniform
|
||||
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
|
||||
: name(paramName), size(typeSize), uniformType(t), precision(p), parameterType(UNIFORM) { }
|
||||
: name(paramName),
|
||||
size(typeSize),
|
||||
uniformType(t),
|
||||
precision(p),
|
||||
format{ 0 },
|
||||
unfilterable(false),
|
||||
multisample(false),
|
||||
parameterType(UNIFORM) {}
|
||||
|
||||
// Subpass
|
||||
Parameter(const char* paramName, SubpassType t, SamplerFormat f, ParameterPrecision p)
|
||||
: name(paramName), size(1), precision(p), subpassType(t), format(f), parameterType(SUBPASS) { }
|
||||
: name(paramName),
|
||||
size(1),
|
||||
precision(p),
|
||||
subpassType(t),
|
||||
format(f),
|
||||
unfilterable(false),
|
||||
multisample(false),
|
||||
parameterType(SUBPASS) {}
|
||||
|
||||
utils::CString name;
|
||||
size_t size;
|
||||
@@ -684,9 +699,9 @@ public:
|
||||
ParameterPrecision precision;
|
||||
SamplerType samplerType;
|
||||
SubpassType subpassType;
|
||||
SamplerFormat format = SamplerFormat::INT; // 0 of the enum
|
||||
bool unfilterable = false;
|
||||
bool multisample = false;
|
||||
SamplerFormat format;
|
||||
bool unfilterable;
|
||||
bool multisample;
|
||||
utils::CString transformName;
|
||||
ShaderStageFlags stages;
|
||||
enum {
|
||||
|
||||
@@ -63,6 +63,8 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(io::sstream& out,
|
||||
filament::Variant::isVSMVariant(variant));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
|
||||
hasStereo(variant, featureLevel));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_DEPTH",
|
||||
filament::Variant::isValidDepthVariant(variant));
|
||||
|
||||
switch (stage) {
|
||||
case ShaderStage::VERTEX:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "TangentsJob.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
#include <geometry/SurfaceOrientation.h>
|
||||
|
||||
@@ -120,6 +120,7 @@ static bool printMaterial(ostream& text, const ChunkContainer& container) {
|
||||
printFloatChunk(text, container, MaterialSpecularAntiAliasingVariance, " Variance: ");
|
||||
printFloatChunk(text, container, MaterialSpecularAntiAliasingThreshold, " Threshold: ");
|
||||
printChunk<bool, bool>(text, container, MaterialClearCoatIorChange, "Clear coat IOR change: ");
|
||||
printChunk<bool, bool>(text, container, MaterialHasCustomDepthShader, "Has custom depth: ");
|
||||
|
||||
text << endl;
|
||||
|
||||
@@ -196,6 +197,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
|
||||
uint64_t fieldSize;
|
||||
uint8_t fieldType;
|
||||
uint8_t fieldPrecision;
|
||||
uint8_t fieldAssociatedSampler;
|
||||
|
||||
if (!uib.read(&fieldName)) {
|
||||
return false;
|
||||
@@ -213,6 +215,10 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uib.read(&fieldAssociatedSampler)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
text << " "
|
||||
<< setw(alignment) << fieldName.c_str()
|
||||
<< setw(shortAlignment) << toString(UniformType(fieldType))
|
||||
@@ -227,6 +233,7 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
|
||||
uint8_t fieldType;
|
||||
uint8_t fieldFormat;
|
||||
uint8_t fieldPrecision;
|
||||
bool fieldUnfilterable;
|
||||
bool fieldMultisample;
|
||||
|
||||
if (!sib.read(&fieldName)) {
|
||||
@@ -248,6 +255,10 @@ static bool printParametersInfo(ostream& text, const ChunkContainer& container)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sib.read(&fieldUnfilterable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sib.read(&fieldMultisample)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -383,10 +383,8 @@ ViewerGui::ViewerGui(filament::Engine* engine, filament::Scene* scene, filament:
|
||||
mSettings.view.vsmShadowOptions.anisotropy = 0;
|
||||
mSettings.view.dithering = Dithering::TEMPORAL;
|
||||
mSettings.view.antiAliasing = AntiAliasing::FXAA;
|
||||
// mSettings.view.msaa = { .enabled = true, .sampleCount = 4 };
|
||||
mSettings.view.msaa = { .enabled = false, .sampleCount = 4 };
|
||||
// mSettings.view.ssao.enabled = true;
|
||||
mSettings.view.ssao.enabled = false;
|
||||
mSettings.view.msaa = { .enabled = true, .sampleCount = 4 };
|
||||
mSettings.view.ssao.enabled = true;
|
||||
mSettings.view.bloom.enabled = true;
|
||||
|
||||
DebugRegistry& debug = mEngine->getDebugRegistry();
|
||||
|
||||
@@ -82,10 +82,14 @@ class GoldenManager:
|
||||
self._git_exec('checkout main')
|
||||
self._git_exec('rebase')
|
||||
|
||||
def _git_exec(self, cmd):
|
||||
return execute(f'git {cmd}', cwd=self._assets_dir(), capture_output=False)
|
||||
def _git_exec(self, cmd, capture_output=False):
|
||||
return execute(f'git {cmd}', cwd=self._assets_dir(),
|
||||
capture_output=capture_output)
|
||||
|
||||
# tag represent a hash in the filament repo that this merge is associated with
|
||||
# Merge a local branch to the main branch.
|
||||
# - `tag` is a hash in the filament repo that this merge is associated with
|
||||
# - `push_to_remote` indicates that the local changes should be pushed to the
|
||||
# remote branch.
|
||||
def merge_to_main(self, branch, tag, push_to_remote=False):
|
||||
self.update()
|
||||
assets_dir = self._assets_dir()
|
||||
@@ -108,6 +112,12 @@ class GoldenManager:
|
||||
self._git_exec(f'push origin main')
|
||||
self.update()
|
||||
|
||||
# Create a branch on the local repo by copying the content from a source directory.
|
||||
# The change/diff is indicated by the 'updates' and the 'deletes' list,
|
||||
# - Files in 'updates' need to be present in both src_dir and in the repo.
|
||||
# - Files in 'deletes' only need to be present in the repo.
|
||||
# If caller wants the changes to be pushed to remote, set the `push_to_remote`
|
||||
# value to True.
|
||||
def source_from(self, src_dir, commit_msg, branch,
|
||||
updates=[], deletes=[], push_to_remote=False):
|
||||
assets_dir = self._assets_dir()
|
||||
@@ -139,12 +149,16 @@ class GoldenManager:
|
||||
self._git_exec(f'push -f origin {branch}')
|
||||
self.update()
|
||||
|
||||
# Download the content of a branch to a directory.
|
||||
def download_to(self, dest_dir, branch='main'):
|
||||
self._git_exec(f'checkout {branch}')
|
||||
assets_dir = self._assets_dir()
|
||||
mkdir_p(dest_dir)
|
||||
rdiff_dir = os.path.join(assets_dir, GOLDENS_DIR)
|
||||
shutil.copytree(rdiff_dir, dest_dir, dirs_exist_ok=True)
|
||||
code, o = self._git_exec(f'rev-parse HEAD', capture_output=True)
|
||||
with open(os.path.join(dest_dir, 'GIT_COMMIT_HASH'), 'w') as f:
|
||||
f.write(o)
|
||||
|
||||
# The main entry point will enable download content of a branch to a directory
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.61.1",
|
||||
"version": "1.62.0",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user