Compare commits
73
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 | ||
|
|
a3922ac3a9 | ||
|
|
5f1112d85e | ||
|
|
09545690bf | ||
|
|
3ce752c47b | ||
|
|
1f9f3ddb91 | ||
|
|
8e94688fbf | ||
|
|
438ee40f40 | ||
|
|
7f22d733d7 | ||
|
|
77d77f0a5c | ||
|
|
0aabd3dd6a | ||
|
|
72a54b30f9 | ||
|
|
00b2ab7ac4 | ||
|
|
39e15d67fc | ||
|
|
921dbddad9 | ||
|
|
6406d4602c | ||
|
|
880cd66166 | ||
|
|
f27ff203cf | ||
|
|
3ea8e529cc | ||
|
|
572dd233d7 | ||
|
|
c19070e477 | ||
|
|
64328b7388 | ||
|
|
1414fdcd38 | ||
|
|
e607a035ae | ||
|
|
0496e089c1 |
@@ -17,15 +17,17 @@ runs:
|
||||
echo "Date: ${TSTAMP}" >> /tmp/commit_msg.txt
|
||||
echo "" >> /tmp/commit_msg.txt
|
||||
echo "${{ github.event.head_commit.message }}" >> /tmp/commit_msg.txt
|
||||
- name: Checkout code
|
||||
- name: Find commit message (PR)
|
||||
shell: bash
|
||||
id: checkout_code
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
echo "+++++ head commit message +++++"
|
||||
echo "$(git log -1 --no-merges)"
|
||||
echo "+++++++++++++++++++++++++++++++"
|
||||
echo "hash=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
git checkout ${{ github.event.pull_request.head.ref }}
|
||||
COMMIT_MESSAGE=$(git log -1)
|
||||
echo "$(git log -1)" >> /tmp/commit_msg.txt
|
||||
git checkout ${{ github.event.pull_request.head.sha }}
|
||||
echo "$(git log -1 --no-merges)" >> /tmp/commit_msg.txt
|
||||
- shell: bash
|
||||
id: action_output
|
||||
run: |
|
||||
@@ -36,8 +38,8 @@ runs:
|
||||
echo "----- got commit message ---"
|
||||
cat /tmp/commit_msg.txt
|
||||
echo "----------------------------"
|
||||
- name: Uncheckout code
|
||||
- name: Cleanup Find commit message (PR)
|
||||
shell: bash
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
git checkout ${{ steps.checkout_code.outputs.hash }}
|
||||
git checkout ${{ steps.checkout_code.outputs.hash }}
|
||||
@@ -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
|
||||
@@ -122,9 +120,6 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- id: get_commit_msg
|
||||
uses: ./.github/actions/get-commit-msg
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Cache Mesa and deps
|
||||
uses: actions/cache@v4
|
||||
@@ -141,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
|
||||
|
||||
@@ -49,6 +49,8 @@ option(FILAMENT_SUPPORTS_OSMESA "Enable OSMesa (headless GL context) for Filamen
|
||||
|
||||
option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF)
|
||||
|
||||
option(FILAMENT_USE_ABSEIL_LOGGING "Use Abseil to log, may increase binary size" OFF)
|
||||
|
||||
# This is to disable GTAO for the short-term while we investigate a way to better manage size increases.
|
||||
# On the regular filament build (where size is of less concern), we enable GTAO by default.
|
||||
option(FILAMENT_DISABLE_GTAO "Disable GTAO" OFF)
|
||||
@@ -591,6 +593,10 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT FILAMENT_BACKEND_DEBUG_FLAG STREQU
|
||||
unset(FILAMENT_BACKEND_DEBUG_FLAG)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_USE_ABSEIL_LOGGING)
|
||||
add_definitions(-DFILAMENT_USE_ABSEIL_LOGGING)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Material compilation flags
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -7,7 +7,3 @@ We are chaning the way Vulkan buffers are handled. We need to switch over to a m
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
- samples: samples now have a CLI to select backend api
|
||||
- materials: sampler now export their type in the material binary [⚠️ **New Material Version**]
|
||||
- materials: new mutable specialization constants feature. See the [materials documentation](https://google.github.io/filament/Materials.html) for details. [⚠️ **New Material Version**]
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.61.0'
|
||||
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.0'
|
||||
pod 'Filament', '~> 1.62.0'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,6 +7,19 @@ 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
|
||||
|
||||
## v1.61.1
|
||||
|
||||
|
||||
|
||||
@@ -59,6 +59,10 @@ add_library(smol-v STATIC IMPORTED)
|
||||
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
|
||||
|
||||
add_library(abseil STATIC IMPORTED)
|
||||
set_target_properties(abseil PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libabseil.a)
|
||||
|
||||
if (FILAMENT_ENABLE_FGVIEWER)
|
||||
add_library(fgviewer STATIC IMPORTED)
|
||||
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
|
||||
@@ -128,6 +132,7 @@ target_link_libraries(filament-jni
|
||||
PRIVATE jnigraphics
|
||||
PRIVATE utils
|
||||
PRIVATE perfetto
|
||||
PRIVATE abseil
|
||||
|
||||
# libgeometry is PUBLIC because gltfio uses it.
|
||||
PUBLIC geometry
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -60,14 +60,6 @@ static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void setConstant(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
instance->setConstant(name, v);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetParameterBool(JNIEnv *env, jclass,
|
||||
@@ -272,13 +264,6 @@ Java_com_google_android_filament_MaterialInstance_nSetParameterTexture(
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetConstantBool(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_, jboolean x) {
|
||||
setConstant(env, nativeMaterialInstance, name_, bool(x));
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetScissor(
|
||||
|
||||
@@ -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);
|
||||
|
||||
-13
@@ -402,16 +402,6 @@ public class MaterialInstance {
|
||||
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a bool constant.
|
||||
*
|
||||
* @param name the name of the material constant
|
||||
* @param x the value of the material constant
|
||||
*/
|
||||
public void setConstant(@NonNull String name, boolean x) {
|
||||
nSetConstantBool(getNativeObject(), name, x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
@@ -931,9 +921,6 @@ public class MaterialInstance {
|
||||
@NonNull String name, int element, @NonNull @Size(min = 1) float[] v,
|
||||
@IntRange(from = 0) int offset, @IntRange(from = 1) int count);
|
||||
|
||||
private static native void nSetConstantBool(long nativeMaterialInstance,
|
||||
@NonNull String name, boolean x);
|
||||
|
||||
private static native void nSetParameterTexture(long nativeMaterialInstance,
|
||||
@NonNull String name, long nativeTexture, long sampler);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.61.0
|
||||
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)
|
||||
|
||||
+7
-6
@@ -204,9 +204,10 @@
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -409,9 +410,9 @@
|
||||
"integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"version": "1.1.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
|
||||
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
||||
@@ -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].
|
||||
@@ -211,7 +212,7 @@ when using textures.
|
||||
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
|
||||
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
|
||||
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
|
||||
(reflected light takes on the color of the surfaced as defined by `baseColor`).
|
||||
(reflected light takes on the color of the surfaced as defined by `baseColor`).
|
||||
|
||||
The effect of `metallic` is shown in figure [metallicProperty] (click on the image to see a
|
||||
larger version).
|
||||
@@ -247,7 +248,7 @@ The effect of `roughness` on metallic surfaces is shown in figure [roughnessCond
|
||||
When refraction through an object is enabled (using a `refractonType` of `thin` or `solid`), the
|
||||
`roughness` property will also affect the refractions, as shown in figure
|
||||
[roughnessRefractionProperty] (click on the image to see a larger version).
|
||||
|
||||
|
||||
![Figure [roughnessRefractionProperty]: Refractive sphere with `roughness` varying from 0.0
|
||||
(left) to 1.0 (right)](images/materials/refraction_roughness.png)
|
||||
|
||||
@@ -306,7 +307,7 @@ The sheen color controls the color appearance and strength of an optional sheen
|
||||
base layer described by the properties above. The sheen layer always sits below the clear coat layer
|
||||
if such a layer is present.
|
||||
|
||||
The sheen layer can be used to represent cloth and fabric materials. Please refer to
|
||||
The sheen layer can be used to represent cloth and fabric materials. Please refer to
|
||||
section [Cloth model] for more information about cloth and fabric materials.
|
||||
|
||||
The effect of `sheenColor` is shown in figure [materialSheenColor]
|
||||
@@ -519,14 +520,14 @@ light to bend further away from the initial path.
|
||||
|
||||
Table [commonMatIOR] describes acceptable refractive indices for various types of materials.
|
||||
|
||||
Material | IOR
|
||||
Material | IOR
|
||||
--------------------------:|:-----------------
|
||||
Air | 1.0
|
||||
Water | 1.33
|
||||
Common liquids | 1.33 to 1.5
|
||||
Common gemstones | 1.58 to 2.33
|
||||
Plastics, glass | 1.5 to 1.58
|
||||
Other dielectric materials | 1.33 to 1.58
|
||||
Water | 1.33
|
||||
Common liquids | 1.33 to 1.5
|
||||
Common gemstones | 1.58 to 2.33
|
||||
Plastics, glass | 1.5 to 1.58
|
||||
Other dielectric materials | 1.33 to 1.58
|
||||
[Table [commonMatIOR]: Index of refraction of common materials]
|
||||
|
||||
The appearance of a refractive material will greatly depend on the `refractionType` and
|
||||
@@ -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
|
||||
@@ -1091,37 +1091,30 @@ Value
|
||||
`bool` or `number`, depending on the `type` of the constant. The type must be one of the types
|
||||
described in table [materialConstantsTypes].
|
||||
|
||||
Constants may also be specified as mutable by setting the `mutable` property to `true`. Only
|
||||
`bool` spec constants may be specified as mutable.
|
||||
|
||||
| Type | Description | Default | May be mutable? |
|
||||
|:------|:------------------------------|:--------|-----------------|
|
||||
| int | A signed, 32 bit GLSL int | | no |
|
||||
| float | A single-precision GLSL float | 0.0 | no |
|
||||
| bool | A GLSL bool | false | yes |
|
||||
Type | Description | Default
|
||||
:----------------------|:-----------------------------------------|:------------------
|
||||
int | A signed, 32 bit GLSL int | 0
|
||||
float | A single-precision GLSL float | 0.0
|
||||
bool | A GLSL bool | false
|
||||
[Table [materialConstantsTypes]: Material constants types]
|
||||
|
||||
Description
|
||||
: Lists the constant parameters accepted by your material. These constants can be set, or
|
||||
"specialized", at runtime when loading a material package. Multiple materials can be loaded from
|
||||
the same material package with differing constant parameter specializations. If a constant
|
||||
parameter is specialized as mutable, it may be changed at any time on a per-instance basis via
|
||||
`MaterialInstance::setConstant(name, value)`. Otherwise, once a material is loaded from a
|
||||
material package, its constant parameters cannot be changed.
|
||||
|
||||
Compared to regular parameters, constant parameters allow the compiler to generate more
|
||||
efficient code. Access constant parameters from the shader by prefixing the name with
|
||||
`materialConstant_`. For example, a constant parameter named `myConstant` is accessed in the
|
||||
shader as `materialConstant_myConstant`. If a constant parameter is not set at runtime, the
|
||||
default is used.
|
||||
the same material package with differing constant parameter specializations. Once a material is
|
||||
loaded from a material package, its constant parameters cannot be changed. Compared to regular
|
||||
parameters, constant parameters allow the compiler to generate more efficient code. Access
|
||||
constant parameters from the shader by prefixing the name with `materialConstant_`. For example,
|
||||
a constant parameter named `myConstant` is accessed in the shader as
|
||||
`materialConstant_myConstant`. If a constant parameter is not set at runtime, the default is
|
||||
used.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
constants : [
|
||||
{
|
||||
name : overrideAlpha,
|
||||
type : bool,
|
||||
mutable : true,
|
||||
type : bool
|
||||
},
|
||||
{
|
||||
name : customAlpha,
|
||||
@@ -1280,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 : [
|
||||
@@ -1310,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.
|
||||
@@ -1326,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,
|
||||
@@ -2560,7 +2560,7 @@ standard skybox material. It produces a list of 2 parameters, named `showSun` an
|
||||
respectively a boolean and a cubemap texture.
|
||||
|
||||
```text
|
||||
$ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
$ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
{
|
||||
"parameters": [
|
||||
{
|
||||
@@ -2576,7 +2576,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
### --variant-filter
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -601,6 +601,10 @@ target_link_libraries(${TARGET} PUBLIC filaflat)
|
||||
target_link_libraries(${TARGET} PUBLIC filabridge)
|
||||
target_link_libraries(${TARGET} PUBLIC ibl-lite)
|
||||
|
||||
if (FILAMENT_USE_ABSEIL_LOGGING)
|
||||
target_link_libraries(${TARGET} PUBLIC absl::log)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_ENABLE_FGVIEWER)
|
||||
target_link_libraries(${TARGET} PUBLIC fgviewer)
|
||||
add_definitions(-DFILAMENT_ENABLE_FGVIEWER=1)
|
||||
|
||||
+5
-4
@@ -92,7 +92,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory.
|
||||
### Linux
|
||||
|
||||
```make
|
||||
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
|
||||
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl -labseil
|
||||
CC=clang++
|
||||
|
||||
main: main.o
|
||||
@@ -110,12 +110,13 @@ clean:
|
||||
### macOS
|
||||
|
||||
```make
|
||||
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl
|
||||
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl -labseil
|
||||
FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo
|
||||
CC=clang++
|
||||
ARCH ?= $(shell uname -m)
|
||||
|
||||
main: main.o
|
||||
$(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main
|
||||
$(CC) -Llib/$(ARCH)/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main
|
||||
|
||||
main.o: main.cpp
|
||||
$(CC) -Iinclude/ -std=c++17 -c main.cpp
|
||||
@@ -139,7 +140,7 @@ used to change the run-time library version.
|
||||
|
||||
```make
|
||||
FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \
|
||||
utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib
|
||||
utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib abseil.lib
|
||||
CC=cl.exe
|
||||
|
||||
main.exe: main.obj
|
||||
|
||||
@@ -257,6 +257,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/WebGPUPlatform.h
|
||||
src/webgpu/platform/WebGPUPlatform.cpp
|
||||
src/webgpu/SpdMipmapGenerator/SpdMipmapGenerator.cpp
|
||||
src/webgpu/WebGPUBufferBase.cpp
|
||||
src/webgpu/WebGPUBufferBase.h
|
||||
src/webgpu/WebGPUBufferObject.cpp
|
||||
@@ -268,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
|
||||
@@ -286,7 +293,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
src/webgpu/WebGPUVertexBuffer.h
|
||||
src/webgpu/WebGPUVertexBufferInfo.cpp
|
||||
src/webgpu/WebGPUVertexBufferInfo.h
|
||||
src/webgpu/SpdMipmapGenerator.cpp
|
||||
)
|
||||
if (WIN32)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
||||
@@ -404,6 +410,10 @@ endif()
|
||||
target_link_libraries(${TARGET} PUBLIC math)
|
||||
target_link_libraries(${TARGET} PUBLIC utils)
|
||||
|
||||
if (FILAMENT_USE_ABSEIL_LOGGING)
|
||||
target_link_libraries(${TARGET} PRIVATE absl::log)
|
||||
endif()
|
||||
|
||||
# Android, iOS, and WebGL do not use bluegl.
|
||||
if(FILAMENT_SUPPORTS_OPENGL AND NOT IOS AND NOT ANDROID AND NOT WEBGL)
|
||||
target_link_libraries(${TARGET} PRIVATE bluegl)
|
||||
@@ -499,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
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -474,7 +474,12 @@ constexpr std::string_view to_string(DescriptorType type) noexcept {
|
||||
|
||||
enum class DescriptorFlags : uint8_t {
|
||||
NONE = 0x00,
|
||||
DYNAMIC_OFFSET = 0x01
|
||||
|
||||
// Indicate a UNIFORM_BUFFER will have dynamic offsets.
|
||||
DYNAMIC_OFFSET = 0x01,
|
||||
|
||||
// To indicate a texture/sampler type should be unfiltered.
|
||||
UNFILTERABLE = 0x02,
|
||||
};
|
||||
|
||||
using descriptor_set_t = uint8_t;
|
||||
@@ -495,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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1254,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)
|
||||
@@ -1342,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.
|
||||
@@ -149,6 +179,20 @@ public:
|
||||
* - PlatformEGLAndroid
|
||||
*/
|
||||
bool assertNativeWindowIsValid = false;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired. If true, the
|
||||
* frame is aborted instead of panic. This is only supported for:
|
||||
* - 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;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -67,7 +66,6 @@ public:
|
||||
using DescriptorBindingsInfo = utils::FixedCapacityVector<Descriptor>;
|
||||
using DescriptorSetInfo = std::array<DescriptorBindingsInfo, MAX_DESCRIPTOR_SET_COUNT>;
|
||||
using SpecializationConstantsInfo = utils::FixedCapacityVector<SpecializationConstant>;
|
||||
using MutableSpecConstantsInfo = utils::bitset8;
|
||||
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
|
||||
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
|
||||
|
||||
@@ -104,8 +102,7 @@ public:
|
||||
Program& descriptorBindings(backend::descriptor_set_t set,
|
||||
DescriptorBindingsInfo descriptorBindings) noexcept;
|
||||
|
||||
Program& specializationConstants(SpecializationConstantsInfo specConstants,
|
||||
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept;
|
||||
Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept;
|
||||
|
||||
struct PushConstant {
|
||||
utils::CString name;
|
||||
|
||||
@@ -128,7 +128,7 @@ protected:
|
||||
bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
|
||||
|
||||
/**
|
||||
* Logs glGetError() to slog.e
|
||||
* Logs glGetError() to LOG(ERROR)
|
||||
* @param name a string giving some context on the error. Typically __func__.
|
||||
*/
|
||||
static void logEglError(const char* name) noexcept;
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
#define FILAMENT_DEBUG_COMMANDS_NONE 0x0
|
||||
// Command debugging enabled. No logging by default.
|
||||
#define FILAMENT_DEBUG_COMMANDS_ENABLE 0x1
|
||||
// Command debugging enabled. Every command logged to slog.d
|
||||
// Command debugging enabled. Every command logged to DLOG(INFO)
|
||||
#define FILAMENT_DEBUG_COMMANDS_LOG 0x2
|
||||
// Command debugging enabled. Every command logged to systrace
|
||||
#define FILAMENT_DEBUG_COMMANDS_SYSTRACE 0x4
|
||||
|
||||
@@ -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>*
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "private/backend/CircularBuffer.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/architecture.h>
|
||||
#include <utils/ashmem.h>
|
||||
@@ -127,7 +127,7 @@ void* CircularBuffer::alloc(size_t size) {
|
||||
"couldn't allocate " << (size * 2 / 1024) <<
|
||||
" KiB of virtual address space for the command buffer";
|
||||
|
||||
slog.w << "Using 'soft' CircularBuffer (" << (size * 2 / 1024) << " KiB)" << io::endl;
|
||||
LOG(WARNING) << "Using 'soft' CircularBuffer (" << (size * 2 / 1024) << " KiB)";
|
||||
|
||||
// guard page at the end
|
||||
void* guard = (void*)(uintptr_t(data) + size * 2);
|
||||
|
||||
@@ -20,12 +20,12 @@
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
@@ -121,11 +121,10 @@ void CommandBufferQueue::flush() {
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
|
||||
slog.d << "CommandStream used too much space (will block): "
|
||||
<< "needed space " << requiredSize << " out of " << mFreeSpace
|
||||
<< ", totalUsed=" << totalUsed << ", current=" << used
|
||||
<< ", queue size=" << mCommandBuffersToExecute.size() << " buffers"
|
||||
<< io::endl;
|
||||
DLOG(INFO) << "CommandStream used too much space (will block): "
|
||||
<< "needed space " << requiredSize << " out of " << mFreeSpace
|
||||
<< ", totalUsed=" << totalUsed << ", current=" << used
|
||||
<< ", queue size=" << mCommandBuffersToExecute.size() << " buffers";
|
||||
|
||||
mHighWatermark = std::max(mHighWatermark, totalUsed);
|
||||
#endif
|
||||
|
||||
@@ -22,10 +22,13 @@
|
||||
#include <utils/CallStack.h>
|
||||
#endif
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Profiler.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/sstream.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
@@ -129,9 +132,10 @@ void CommandType<void (Driver::*)(ARGS...)>::Command<METHOD>::log(std::index_seq
|
||||
#if DEBUG_COMMAND_STREAM
|
||||
static_assert(UTILS_HAS_RTTI, "DEBUG_COMMAND_STREAM can only be used with RTTI");
|
||||
std::string command = utils::CallStack::demangleTypeName(typeid(Command).name()).c_str();
|
||||
slog.d << extractMethodName(command) << " : size=" << sizeof(Command) << "\n\t";
|
||||
printParameterPack(slog.d, std::get<I>(mArgs)...);
|
||||
slog.d << io::endl;
|
||||
DLOG(INFO) << extractMethodName(command) << " : size=" << sizeof(Command);
|
||||
utils::io::sstream parameterPack;
|
||||
printParameterPack(parameterPack, std::get<I>(mArgs)...);
|
||||
DLOG(INFO) << "\t" << parameterPack.c_str();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <math/half.h>
|
||||
@@ -149,7 +149,7 @@ void DriverBase::scheduleRelease(AcquiredImage const& image) noexcept {
|
||||
void DriverBase::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
|
||||
if constexpr (bool(FILAMENT_DEBUG_COMMANDS > FILAMENT_DEBUG_COMMANDS_NONE)) {
|
||||
if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_LOG)) {
|
||||
utils::slog.d << methodName << utils::io::endl;
|
||||
DLOG(INFO) << methodName;
|
||||
}
|
||||
if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_SYSTRACE)) {
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
@@ -57,8 +57,8 @@ HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& ar
|
||||
size_t const maxHeapSize = std::min(area.size(), HANDLE_INDEX_MASK * getAlignment());
|
||||
|
||||
if (UTILS_UNLIKELY(maxHeapSize != area.size())) {
|
||||
slog.w << "HandleAllocator heap size reduced to "
|
||||
<< maxHeapSize << " from " << area.size() << io::endl;
|
||||
LOG(WARNING) << "HandleAllocator heap size reduced to " << maxHeapSize << " from "
|
||||
<< area.size();
|
||||
}
|
||||
|
||||
// make sure we start with a clean arena. This is needed to ensure that all blocks start
|
||||
|
||||
@@ -80,18 +80,8 @@ Program& Program::attributes(AttributesInfo attributes) noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants,
|
||||
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept {
|
||||
// String the two lists together.
|
||||
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants) noexcept {
|
||||
mSpecializationConstants = std::move(specConstants);
|
||||
uint32_t firstMutableIndex = specConstants.size();
|
||||
mSpecializationConstants.reserve(specConstants.size() + mutableSpecConstants.size());
|
||||
for (uint32_t i = 0; i < mutableSpecConstants.size(); i++) {
|
||||
mSpecializationConstants[i + firstMutableIndex] = SpecializationConstant {
|
||||
.id = i + firstMutableId,
|
||||
.value = mutableSpecConstants[i],
|
||||
};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
#include "MetalContext.h"
|
||||
#include "MetalUtils.h"
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -323,7 +323,7 @@ id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) const
|
||||
if (!library || !function) {
|
||||
if (error) {
|
||||
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
LOG(ERROR) << description;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(library && function)
|
||||
@@ -349,7 +349,7 @@ id<MTLFunction> MetalBlitter::getBlitVertexFunction() {
|
||||
if (!library || !function) {
|
||||
if (error) {
|
||||
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
LOG(ERROR) << description;
|
||||
}
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(library && function)
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
#include "MetalContext.h"
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
#include <thread>
|
||||
@@ -77,8 +77,7 @@ void MetalBufferPool::releaseBuffer(MetalBufferPoolEntry const *stage) noexcept
|
||||
|
||||
auto iter = mUsedStages.find(stage);
|
||||
if (iter == mUsedStages.end()) {
|
||||
utils::slog.e << "Unknown Metal buffer: " << stage->capacity << " bytes"
|
||||
<< utils::io::endl;
|
||||
LOG(ERROR) << "Unknown Metal buffer: " << stage->capacity << " bytes";
|
||||
return;
|
||||
}
|
||||
stage->lastAccessed = mCurrentFrame;
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
|
||||
#include "MetalHandles.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -112,8 +113,8 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
|
||||
auto errorCode = (MTLCommandBufferError)buffer.error.code;
|
||||
if (@available(macOS 11.0, *)) {
|
||||
if (errorCode == MTLCommandBufferErrorMemoryless) {
|
||||
utils::slog.w << "Metal: memoryless geometry limit reached. "
|
||||
"Continuing with private storage mode." << utils::io::endl;
|
||||
LOG(WARNING) << "Metal: memoryless geometry limit reached. Continuing with private "
|
||||
"storage mode.";
|
||||
context->memorylessLimitsReached = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@
|
||||
#include <Metal/Metal.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/sstream.h>
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -78,20 +78,18 @@ Driver* MetalDriverFactory::create(PlatformMetal* const platform, const Platform
|
||||
// MetalVertexBufferInfo : 552 moderate
|
||||
// -- less than or equal to 552 bytes
|
||||
|
||||
utils::slog.d
|
||||
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
|
||||
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
|
||||
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
|
||||
<< "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo)
|
||||
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
|
||||
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
|
||||
<< "\nMetalTexture: " << sizeof(MetalTexture)
|
||||
<< "\nMetalTimerQuery: " << sizeof(MetalTimerQuery)
|
||||
<< "\nHwStream: " << sizeof(HwStream)
|
||||
<< "\nMetalRenderTarget: " << sizeof(MetalRenderTarget)
|
||||
<< "\nMetalFence: " << sizeof(MetalFence)
|
||||
<< "\nMetalProgram: " << sizeof(MetalProgram)
|
||||
<< utils::io::endl;
|
||||
DLOG(INFO) << "MetalSwapChain: " << sizeof(MetalSwapChain);
|
||||
DLOG(INFO) << "MetalBufferObject: " << sizeof(MetalBufferObject);
|
||||
DLOG(INFO) << "MetalVertexBuffer: " << sizeof(MetalVertexBuffer);
|
||||
DLOG(INFO) << "MetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo);
|
||||
DLOG(INFO) << "MetalIndexBuffer: " << sizeof(MetalIndexBuffer);
|
||||
DLOG(INFO) << "MetalRenderPrimitive: " << sizeof(MetalRenderPrimitive);
|
||||
DLOG(INFO) << "MetalTexture: " << sizeof(MetalTexture);
|
||||
DLOG(INFO) << "MetalTimerQuery: " << sizeof(MetalTimerQuery);
|
||||
DLOG(INFO) << "HwStream: " << sizeof(HwStream);
|
||||
DLOG(INFO) << "MetalRenderTarget: " << sizeof(MetalRenderTarget);
|
||||
DLOG(INFO) << "MetalFence: " << sizeof(MetalFence);
|
||||
DLOG(INFO) << "MetalProgram: " << sizeof(MetalProgram);
|
||||
#endif
|
||||
return MetalDriver::create(platform, driverConfig);
|
||||
}
|
||||
@@ -135,19 +133,18 @@ MetalDriver::MetalDriver(
|
||||
|
||||
initializeSupportedGpuFamilies(mContext);
|
||||
|
||||
utils::slog.v << "Supported GPU families: " << utils::io::endl;
|
||||
LOG(INFO) << "Supported GPU families: ";
|
||||
if (mContext->highestSupportedGpuFamily.common > 0) {
|
||||
utils::slog.v << " MTLGPUFamilyCommon" << (int) mContext->highestSupportedGpuFamily.common << utils::io::endl;
|
||||
LOG(INFO) << " MTLGPUFamilyCommon" << (int) mContext->highestSupportedGpuFamily.common;
|
||||
}
|
||||
if (mContext->highestSupportedGpuFamily.apple > 0) {
|
||||
utils::slog.v << " MTLGPUFamilyApple" << (int) mContext->highestSupportedGpuFamily.apple << utils::io::endl;
|
||||
LOG(INFO) << " MTLGPUFamilyApple" << (int) mContext->highestSupportedGpuFamily.apple;
|
||||
}
|
||||
if (mContext->highestSupportedGpuFamily.mac > 0) {
|
||||
utils::slog.v << " MTLGPUFamilyMac" << (int) mContext->highestSupportedGpuFamily.mac << utils::io::endl;
|
||||
LOG(INFO) << " MTLGPUFamilyMac" << (int) mContext->highestSupportedGpuFamily.mac;
|
||||
}
|
||||
utils::slog.v << "Features:" << utils::io::endl;
|
||||
utils::slog.v << " readWriteTextureSupport: " <<
|
||||
(bool) mContext->device.readWriteTextureSupport << utils::io::endl;
|
||||
LOG(INFO) << "Features:";
|
||||
LOG(INFO) << " readWriteTextureSupport: " << (bool) mContext->device.readWriteTextureSupport;
|
||||
|
||||
// In order to support texture swizzling, the GPU needs to support it and the system be running
|
||||
// iOS 13+.
|
||||
@@ -588,7 +585,7 @@ void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
|
||||
#if FILAMENT_METAL_DEBUG_LOG
|
||||
auto handleId = rph.getId();
|
||||
DEBUG_LOG("createProgramR(rph = %d, program = ", handleId);
|
||||
utils::slog.d << program << utils::io::endl;
|
||||
DLOG(INFO) << program;
|
||||
#endif
|
||||
construct_handle<MetalProgram>(rph, *mContext, std::move(program));
|
||||
}
|
||||
@@ -2077,7 +2074,7 @@ void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGro
|
||||
error:&error];
|
||||
if (error) {
|
||||
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
LOG(ERROR) << description;
|
||||
}
|
||||
assert_invariant(!error);
|
||||
|
||||
|
||||
@@ -20,14 +20,14 @@
|
||||
#include "MetalEnums.h"
|
||||
#include "MetalUtils.h"
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
#define NSERROR_CHECK(message) \
|
||||
if (error) { \
|
||||
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding]; \
|
||||
utils::slog.e << description << utils::io::endl; \
|
||||
LOG(ERROR) << description; \
|
||||
} \
|
||||
FILAMENT_CHECK_POSTCONDITION(error == nil) << message;
|
||||
|
||||
|
||||
@@ -27,10 +27,11 @@
|
||||
|
||||
#include "private/backend/BackendUtils.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/trap.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
|
||||
@@ -115,14 +116,14 @@ MetalSwapChain::MetalSwapChain(
|
||||
type(SwapChainType::CAMETALLAYER) {
|
||||
|
||||
if (!(flags & SwapChain::CONFIG_TRANSPARENT) && !nativeWindow.opaque) {
|
||||
utils::slog.w << "Warning: Filament SwapChain has no CONFIG_TRANSPARENT flag, "
|
||||
"but the CAMetaLayer(" << (__bridge void*) nativeWindow << ")"
|
||||
" has .opaque set to NO." << utils::io::endl;
|
||||
LOG(WARNING) << "Warning: Filament SwapChain has no CONFIG_TRANSPARENT flag, but the "
|
||||
"CAMetaLayer("
|
||||
<< (__bridge void*) nativeWindow << ") has .opaque set to NO.";
|
||||
}
|
||||
if ((flags & SwapChain::CONFIG_TRANSPARENT) && nativeWindow.opaque) {
|
||||
utils::slog.w << "Warning: Filament SwapChain has the CONFIG_TRANSPARENT flag, "
|
||||
"but the CAMetaLayer(" << (__bridge void*) nativeWindow << ")"
|
||||
" has .opaque set to YES." << utils::io::endl;
|
||||
LOG(WARNING) << "Warning: Filament SwapChain has the CONFIG_TRANSPARENT flag, but the "
|
||||
"CAMetaLayer("
|
||||
<< (__bridge void*) nativeWindow << ") has .opaque set to YES.";
|
||||
}
|
||||
|
||||
// Needed so we can use the SwapChain as a blit source.
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <chrono>
|
||||
@@ -147,7 +148,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
if (error) {
|
||||
auto description =
|
||||
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.w << description << utils::io::endl;
|
||||
LOG(WARNING) << description;
|
||||
errorMessage = error.localizedDescription;
|
||||
}
|
||||
PANIC_LOG("Failed to compile Metal program.");
|
||||
@@ -178,7 +179,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
if (error) {
|
||||
auto description =
|
||||
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.w << description << utils::io::endl;
|
||||
LOG(WARNING) << description;
|
||||
errorMessage = error.localizedDescription;
|
||||
}
|
||||
PANIC_LOG("Failed to load main0 in Metal program.");
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "MetalEnums.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
@@ -95,7 +95,7 @@ id<MTLRenderPipelineState> PipelineStateCreator::operator()(id<MTLDevice> device
|
||||
[NSString stringWithFormat:@"Could not create Metal pipeline state: %@",
|
||||
error ? error.localizedDescription : @"unknown error"];
|
||||
auto description = [errorMessage cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.e << description << utils::io::endl;
|
||||
LOG(ERROR) << description;
|
||||
[[NSException exceptionWithName:@"MetalRenderPipelineFailure"
|
||||
reason:errorMessage
|
||||
userInfo:nil] raise];
|
||||
@@ -155,7 +155,7 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
|
||||
// MTLSamplerDescriptor.
|
||||
// In practice, this means shadows are not supported when running in the simulator.
|
||||
if (![device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
|
||||
utils::slog.w << "Warning: sample comparison not supported by this GPU" << utils::io::endl;
|
||||
LOG(WARNING) << "Warning: sample comparison not supported by this GPU";
|
||||
samplerDescriptor.compareFunction = MTLCompareFunctionNever;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "MetalDriverFactory.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -53,6 +53,9 @@ PlatformMetal::~PlatformMetal() noexcept {
|
||||
}
|
||||
|
||||
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
|
||||
? DrawableFailureBehavior::ABORT_FRAME
|
||||
: DrawableFailureBehavior::PANIC;
|
||||
return MetalDriverFactory::create(this, driverConfig);
|
||||
}
|
||||
|
||||
@@ -129,9 +132,8 @@ void PlatformMetalImpl::createDeviceImpl(MetalDevice& outDevice) {
|
||||
result = MTLCreateSystemDefaultDevice();
|
||||
}
|
||||
|
||||
utils::slog.i << "Selected physical device '"
|
||||
<< [result.name cStringUsingEncoding:NSUTF8StringEncoding] << "'"
|
||||
<< utils::io::endl;
|
||||
LOG(INFO) << "Selected physical device '"
|
||||
<< [result.name cStringUsingEncoding:NSUTF8StringEncoding] << "'";
|
||||
|
||||
outDevice.device = result;
|
||||
mDevice = result;
|
||||
|
||||
@@ -360,7 +360,7 @@ void GLDescriptorSet::bind(
|
||||
}
|
||||
}, entry.desc);
|
||||
});
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void GLDescriptorSet::validate(HandleAllocatorGL& allocator,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/trap.h>
|
||||
@@ -25,6 +26,7 @@
|
||||
#include <string_view>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cstdio>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -55,19 +57,21 @@ std::string_view getGLErrorString(GLenum error) noexcept {
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcept {
|
||||
GLenum checkGLError(const char* function, size_t line) noexcept {
|
||||
GLenum const error = glGetError();
|
||||
if (UTILS_VERY_UNLIKELY(error != GL_NO_ERROR)) {
|
||||
auto const string = getGLErrorString(error);
|
||||
out << "OpenGL error " << io::hex << error << " (" << string << ") in \""
|
||||
<< function << "\" at line " << io::dec << line << io::endl;
|
||||
char hexError[16];
|
||||
snprintf(hexError, sizeof(hexError), "%#x", error);
|
||||
LOG(ERROR) << "OpenGL error " << hexError << " (" << string << ") in \"" << function
|
||||
<< "\" at line " << line;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void assertGLError(io::ostream& out, const char* function, size_t line) noexcept {
|
||||
GLenum const err = checkGLError(out, function, line);
|
||||
void assertGLError(const char* function, size_t line) noexcept {
|
||||
GLenum const err = checkGLError(function, line);
|
||||
if (UTILS_VERY_UNLIKELY(err != GL_NO_ERROR)) {
|
||||
debug_trap();
|
||||
}
|
||||
@@ -97,19 +101,21 @@ std::string_view getFramebufferStatusString(GLenum status) noexcept {
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
|
||||
GLenum checkFramebufferStatus(GLenum target, const char* function, size_t line) noexcept {
|
||||
GLenum const status = glCheckFramebufferStatus(target);
|
||||
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
|
||||
auto const string = getFramebufferStatusString(status);
|
||||
out << "OpenGL framebuffer error " << io::hex << status << " (" << string << ") in \""
|
||||
<< function << "\" at line " << io::dec << line << io::endl;
|
||||
char hexStatus[16];
|
||||
snprintf(hexStatus, sizeof(hexStatus), "%#x", status);
|
||||
LOG(ERROR) << "OpenGL framebuffer error " << hexStatus << " (" << string << ") in \""
|
||||
<< function << "\" at line " << line;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void assertFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
|
||||
GLenum const status = checkFramebufferStatus(out, target, function, line);
|
||||
void assertFramebufferStatus(GLenum target, const char* function, size_t line) noexcept {
|
||||
GLenum const status = checkFramebufferStatus(target, function, line);
|
||||
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
|
||||
debug_trap();
|
||||
}
|
||||
|
||||
@@ -33,21 +33,21 @@
|
||||
namespace filament::backend::GLUtils {
|
||||
|
||||
std::string_view getGLErrorString(GLenum error) noexcept;
|
||||
GLenum checkGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
|
||||
void assertGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
|
||||
GLenum checkGLError(const char* function, size_t line) noexcept;
|
||||
void assertGLError(const char* function, size_t line) noexcept;
|
||||
|
||||
std::string_view getFramebufferStatusString(GLenum err) noexcept;
|
||||
GLenum checkFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
|
||||
void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
|
||||
GLenum checkFramebufferStatus(GLenum target, const char* function, size_t line) noexcept;
|
||||
void assertFramebufferStatus(GLenum target, const char* function, size_t line) noexcept;
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define CHECK_GL_ERROR(out)
|
||||
# define CHECK_GL_ERROR_NON_FATAL(out)
|
||||
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target)
|
||||
# define CHECK_GL_ERROR()
|
||||
# define CHECK_GL_ERROR_NON_FATAL()
|
||||
# define CHECK_GL_FRAMEBUFFER_STATUS(target)
|
||||
#else
|
||||
# define CHECK_GL_ERROR(out) { GLUtils::assertGLError(out, __func__, __LINE__); }
|
||||
# define CHECK_GL_ERROR_NON_FATAL(out) { GLUtils::checkGLError(out, __func__, __LINE__); }
|
||||
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target) { GLUtils::checkFramebufferStatus(out, target, __func__, __LINE__); }
|
||||
# define CHECK_GL_ERROR() { GLUtils::assertGLError(__func__, __LINE__); }
|
||||
# define CHECK_GL_ERROR_NON_FATAL() { GLUtils::checkGLError(__func__, __LINE__); }
|
||||
# define CHECK_GL_FRAMEBUFFER_STATUS(target) { GLUtils::checkFramebufferStatus( target, __func__, __LINE__); }
|
||||
#endif
|
||||
|
||||
constexpr GLuint getComponentCount(ElementType const type) noexcept {
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <functional>
|
||||
@@ -77,8 +77,8 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
state.version = (char const*)glGetString(GL_VERSION);
|
||||
state.shader = (char const*)glGetString(GL_SHADING_LANGUAGE_VERSION);
|
||||
|
||||
slog.v << "[" << state.vendor << "], [" << state.renderer << "], "
|
||||
"[" << state.version << "], [" << state.shader << "]" << io::endl;
|
||||
LOG(INFO) << "[" << state.vendor << "], [" << state.renderer << "], "
|
||||
"[" << state.version << "], [" << state.shader << "]";
|
||||
|
||||
/*
|
||||
* Figure out GL / GLES version, extensions and capabilities we need to
|
||||
@@ -164,51 +164,33 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
}
|
||||
#endif
|
||||
|
||||
slog.v << "Feature level: " << +mFeatureLevel << '\n';
|
||||
slog.v << "Active workarounds: " << '\n';
|
||||
LOG(INFO) << "Feature level: " << +mFeatureLevel;
|
||||
LOG(INFO) << "Active workarounds: ";
|
||||
UTILS_NOUNROLL
|
||||
for (auto [enabled, name, _] : mBugDatabase) {
|
||||
if (enabled) {
|
||||
slog.v << name << '\n';
|
||||
LOG(INFO) << name;
|
||||
}
|
||||
}
|
||||
flush(slog.v);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// this is useful for development
|
||||
slog.v
|
||||
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = "
|
||||
<< gets.max_anisotropy << '\n'
|
||||
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
|
||||
<< gets.max_combined_texture_image_units << '\n'
|
||||
<< "GL_MAX_TEXTURE_SIZE = "
|
||||
<< gets.max_texture_size << '\n'
|
||||
<< "GL_MAX_CUBE_MAP_TEXTURE_SIZE = "
|
||||
<< gets.max_cubemap_texture_size << '\n'
|
||||
<< "GL_MAX_3D_TEXTURE_SIZE = "
|
||||
<< gets.max_3d_texture_size << '\n'
|
||||
<< "GL_MAX_ARRAY_TEXTURE_LAYERS = "
|
||||
<< gets.max_array_texture_layers << '\n'
|
||||
<< "GL_MAX_DRAW_BUFFERS = "
|
||||
<< gets.max_draw_buffers << '\n'
|
||||
<< "GL_MAX_RENDERBUFFER_SIZE = "
|
||||
<< gets.max_renderbuffer_size << '\n'
|
||||
<< "GL_MAX_SAMPLES = "
|
||||
<< gets.max_samples << '\n'
|
||||
<< "GL_MAX_TEXTURE_IMAGE_UNITS = "
|
||||
<< gets.max_texture_image_units << '\n'
|
||||
<< "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = "
|
||||
<< gets.max_transform_feedback_separate_attribs << '\n'
|
||||
<< "GL_MAX_UNIFORM_BLOCK_SIZE = "
|
||||
<< gets.max_uniform_block_size << '\n'
|
||||
<< "GL_MAX_UNIFORM_BUFFER_BINDINGS = "
|
||||
<< gets.max_uniform_buffer_bindings << '\n'
|
||||
<< "GL_NUM_PROGRAM_BINARY_FORMATS = "
|
||||
<< gets.num_program_binary_formats << '\n'
|
||||
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = "
|
||||
<< gets.uniform_buffer_offset_alignment << '\n'
|
||||
;
|
||||
flush(slog.v);
|
||||
LOG(INFO) << "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = " << gets.max_anisotropy;
|
||||
LOG(INFO) << "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = " << gets.max_combined_texture_image_units;
|
||||
LOG(INFO) << "GL_MAX_TEXTURE_SIZE = " << gets.max_texture_size;
|
||||
LOG(INFO) << "GL_MAX_CUBE_MAP_TEXTURE_SIZE = " << gets.max_cubemap_texture_size;
|
||||
LOG(INFO) << "GL_MAX_3D_TEXTURE_SIZE = " << gets.max_3d_texture_size;
|
||||
LOG(INFO) << "GL_MAX_ARRAY_TEXTURE_LAYERS = " << gets.max_array_texture_layers;
|
||||
LOG(INFO) << "GL_MAX_DRAW_BUFFERS = " << gets.max_draw_buffers;
|
||||
LOG(INFO) << "GL_MAX_RENDERBUFFER_SIZE = " << gets.max_renderbuffer_size;
|
||||
LOG(INFO) << "GL_MAX_SAMPLES = " << gets.max_samples;
|
||||
LOG(INFO) << "GL_MAX_TEXTURE_IMAGE_UNITS = " << gets.max_texture_image_units;
|
||||
LOG(INFO) << "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = "
|
||||
<< gets.max_transform_feedback_separate_attribs;
|
||||
LOG(INFO) << "GL_MAX_UNIFORM_BLOCK_SIZE = " << gets.max_uniform_block_size;
|
||||
LOG(INFO) << "GL_MAX_UNIFORM_BUFFER_BINDINGS = " << gets.max_uniform_buffer_bindings;
|
||||
LOG(INFO) << "GL_NUM_PROGRAM_BINARY_FORMATS = " << gets.num_program_binary_formats;
|
||||
LOG(INFO) << "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = " << gets.uniform_buffer_offset_alignment;
|
||||
#endif
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
@@ -242,15 +224,14 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
if (ext.KHR_debug) {
|
||||
auto cb = +[](GLenum, GLenum type, GLuint, GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void *) {
|
||||
io::ostream* stream = &slog.i;
|
||||
auto logSeverity = utils::LogSeverity::kInfo;
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH: stream = &slog.e; break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM: stream = &slog.w; break;
|
||||
case GL_DEBUG_SEVERITY_LOW: stream = &slog.d; break;
|
||||
case GL_DEBUG_SEVERITY_HIGH: logSeverity = utils::LogSeverity::kError; break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM: logSeverity = utils::LogSeverity::kWarning; break;
|
||||
case GL_DEBUG_SEVERITY_LOW: logSeverity = utils::LogSeverity::kInfo; break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
default: break;
|
||||
}
|
||||
io::ostream& out = *stream;
|
||||
const char* level = ": ";
|
||||
switch (type) {
|
||||
case GL_DEBUG_TYPE_ERROR: level = "ERROR: "; break;
|
||||
@@ -262,7 +243,7 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
case GL_DEBUG_TYPE_MARKER: level = "MARKER: "; break;
|
||||
default: break;
|
||||
}
|
||||
out << "KHR_debug " << level << std::string_view{ message, size_t(length) } << io::endl;
|
||||
LOG(LEVEL(logSeverity)) << "KHR_debug " << level << std::string_view{ message, size_t(length) };
|
||||
};
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
@@ -707,9 +688,8 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
GLUtils::unordered_string_set const exts = GLUtils::split(extensions);
|
||||
if constexpr (DEBUG_PRINT_EXTENSIONS) {
|
||||
for (auto extension: exts) {
|
||||
slog.d << "\"" << std::string_view(extension) << "\"\n";
|
||||
DLOG(INFO) << "\"" << std::string_view(extension) << "\"";
|
||||
}
|
||||
flush(slog.d);
|
||||
}
|
||||
|
||||
// figure out and initialize the extensions we need
|
||||
@@ -783,9 +763,8 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
}
|
||||
if constexpr (DEBUG_PRINT_EXTENSIONS) {
|
||||
for (auto extension: exts) {
|
||||
slog.d << "\"" << std::string_view(extension) << "\"\n";
|
||||
DLOG(INFO) << "\"" << std::string_view(extension) << "\"";
|
||||
}
|
||||
flush(slog.d);
|
||||
}
|
||||
|
||||
using namespace std::literals;
|
||||
@@ -1045,7 +1024,7 @@ GLuint OpenGLContext::getSamplerSlow(SamplerParams params) const noexcept {
|
||||
std::min(gets.max_anisotropy, anisotropy));
|
||||
}
|
||||
#endif
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
mSamplerMap[params] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -48,12 +48,13 @@
|
||||
|
||||
#include <type_traits>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
@@ -184,20 +185,18 @@ OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
|
||||
// GLVertexBufferInfo : 132 moderate
|
||||
// -- less than or equal to 136 bytes
|
||||
|
||||
slog.d
|
||||
<< "\nGLSwapChain: " << sizeof(GLSwapChain)
|
||||
<< "\nGLBufferObject: " << sizeof(GLBufferObject)
|
||||
<< "\nGLVertexBuffer: " << sizeof(GLVertexBuffer)
|
||||
<< "\nGLVertexBufferInfo: " << sizeof(GLVertexBufferInfo)
|
||||
<< "\nGLIndexBuffer: " << sizeof(GLIndexBuffer)
|
||||
<< "\nGLRenderPrimitive: " << sizeof(GLRenderPrimitive)
|
||||
<< "\nGLTexture: " << sizeof(GLTexture)
|
||||
<< "\nGLTimerQuery: " << sizeof(GLTimerQuery)
|
||||
<< "\nGLStream: " << sizeof(GLStream)
|
||||
<< "\nGLRenderTarget: " << sizeof(GLRenderTarget)
|
||||
<< "\nGLFence: " << sizeof(GLFence)
|
||||
<< "\nOpenGLProgram: " << sizeof(OpenGLProgram)
|
||||
<< io::endl;
|
||||
DLOG(INFO) << "GLSwapChain: " << sizeof(GLSwapChain);
|
||||
DLOG(INFO) << "GLBufferObject: " << sizeof(GLBufferObject);
|
||||
DLOG(INFO) << "GLVertexBuffer: " << sizeof(GLVertexBuffer);
|
||||
DLOG(INFO) << "GLVertexBufferInfo: " << sizeof(GLVertexBufferInfo);
|
||||
DLOG(INFO) << "GLIndexBuffer: " << sizeof(GLIndexBuffer);
|
||||
DLOG(INFO) << "GLRenderPrimitive: " << sizeof(GLRenderPrimitive);
|
||||
DLOG(INFO) << "GLTexture: " << sizeof(GLTexture);
|
||||
DLOG(INFO) << "GLTimerQuery: " << sizeof(GLTimerQuery);
|
||||
DLOG(INFO) << "GLStream: " << sizeof(GLStream);
|
||||
DLOG(INFO) << "GLRenderTarget: " << sizeof(GLRenderTarget);
|
||||
DLOG(INFO) << "GLFence: " << sizeof(GLFence);
|
||||
DLOG(INFO) << "OpenGLProgram: " << sizeof(OpenGLProgram);
|
||||
#endif
|
||||
|
||||
// here we check we're on a supported version of GL before initializing the driver
|
||||
@@ -289,7 +288,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
|
||||
mStreamsWithPendingAcquiredImage.reserve(8);
|
||||
|
||||
#ifndef NDEBUG
|
||||
slog.i << "OS version: " << mPlatform.getOSVersion() << io::endl;
|
||||
LOG(INFO) << "OS version: " << mPlatform.getOSVersion();
|
||||
#endif
|
||||
|
||||
// Timer queries are core in GL 3.3, otherwise we need EXT_disjoint_timer_query
|
||||
@@ -668,7 +667,7 @@ void OpenGLDriver::createIndexBufferR(
|
||||
gl.bindVertexArray(nullptr);
|
||||
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, getBufferUsage(usage));
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
|
||||
@@ -693,7 +692,7 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
|
||||
glBufferData(bo->gl.binding, byteCount, nullptr, getBufferUsage(usage));
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
@@ -730,7 +729,7 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
// this records the index buffer into the currently bound VAO
|
||||
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||
@@ -760,7 +759,7 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||
}
|
||||
|
||||
construct<OpenGLProgram>(ph, *this, std::move(program));
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
@@ -953,7 +952,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
renderBufferStorage(t->gl.id, internalFormat, w, h, samples);
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
|
||||
@@ -999,7 +998,7 @@ void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
|
||||
assert_invariant(ref);
|
||||
ref->count++;
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
@@ -1062,7 +1061,7 @@ void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
|
||||
assert_invariant(ref);
|
||||
ref->count++;
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureExternalImage2R(Handle<HwTexture> th, SamplerType target,
|
||||
@@ -1225,7 +1224,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
#endif
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) {
|
||||
@@ -1485,7 +1484,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
// we shouldn't be here
|
||||
break;
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
} else
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_multisampled_render_to_texture
|
||||
@@ -1506,7 +1505,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
|
||||
GL_RENDERBUFFER, t->gl.id);
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
} else
|
||||
#endif // GL_EXT_multisampled_render_to_texture
|
||||
#endif // __EMSCRIPTEN__
|
||||
@@ -1588,13 +1587,13 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
break;
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
rt->gl.resolve |= resolveFlags;
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
|
||||
CHECK_GL_ERROR()
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_FRAMEBUFFER)
|
||||
}
|
||||
|
||||
void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32_t width, // NOLINT(readability-convert-member-functions-to-static)
|
||||
@@ -1623,7 +1622,7 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
|
||||
// unbind the renderbuffer, to avoid any later confusion
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::createDefaultRenderTargetR(
|
||||
@@ -1720,7 +1719,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
if (UTILS_LIKELY(!getContext().isES2())) {
|
||||
glDrawBuffers((GLsizei)maxDrawBuffers, bufs);
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1760,7 +1759,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
|
||||
assert_invariant(any(targets & TargetBufferFlags::ALL));
|
||||
assert_invariant(tmin == tmax);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::createFenceR(Handle<HwFence> fh, int) {
|
||||
@@ -2111,7 +2110,7 @@ void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer, const m
|
||||
|
||||
if (UTILS_UNLIKELY(glstream->user_thread.pending.image)) {
|
||||
scheduleRelease(glstream->user_thread.pending);
|
||||
slog.w << "Acquired image is set more than once per frame." << io::endl;
|
||||
LOG(WARNING) << "Acquired image is set more than once per frame.";
|
||||
}
|
||||
|
||||
glstream->user_thread.pending = mPlatform.transformAcquiredImage({
|
||||
@@ -2560,7 +2559,7 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
|
||||
|
||||
// OpenGL context has changed, resynchronize the state with the cache
|
||||
mContext.synchronizeStateAndCache(index);
|
||||
slog.d << "*** OpenGL context change : " << (index ? "protected" : "default") << io::endl;
|
||||
DLOG(INFO) << "*** OpenGL context change : " << (index ? "protected" : "default");
|
||||
});
|
||||
|
||||
mCurrentDrawSwapChain = scDraw;
|
||||
@@ -2601,7 +2600,7 @@ void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
|
||||
vb->bufferObjectsVersion = (version + 1) % kMaxVersion;
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateIndexBuffer(
|
||||
@@ -2618,7 +2617,7 @@ void OpenGLDriver::updateIndexBuffer(
|
||||
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
|
||||
@@ -2698,7 +2697,7 @@ void OpenGLDriver::updateBufferObject(
|
||||
|
||||
scheduleDestroy(std::move(bd));
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateBufferObjectUnsynchronized(
|
||||
@@ -2745,7 +2744,7 @@ retry:
|
||||
scheduleDestroy(std::move(bd));
|
||||
}
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2795,7 +2794,7 @@ void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
|
||||
glGenerateMipmap(t->gl.target);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::setTextureData(GLTexture const* t, uint32_t level,
|
||||
@@ -2904,7 +2903,7 @@ void OpenGLDriver::setTextureData(GLTexture const* t, uint32_t level,
|
||||
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::setCompressedTextureData(GLTexture const* t, uint32_t level,
|
||||
@@ -2990,7 +2989,7 @@ void OpenGLDriver::setCompressedTextureData(GLTexture const* t, uint32_t level,
|
||||
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::setupExternalImage2(Platform::ExternalImageHandleRef image) {
|
||||
@@ -3153,7 +3152,7 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
TargetBufferFlags discardFlags = params.flags.discardStart & rt->targets;
|
||||
|
||||
GLuint const fbo = gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_FRAMEBUFFER)
|
||||
|
||||
// each render-pass starts with a disabled scissor
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
@@ -3165,7 +3164,7 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
|
||||
if (attachmentCount) {
|
||||
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
} else {
|
||||
// It's important to clear the framebuffer before drawing, as it resets
|
||||
// the fb to a known state (resets fb compression and possibly other things).
|
||||
@@ -3252,7 +3251,7 @@ void OpenGLDriver::endRenderPass(int) {
|
||||
if (attachmentCount) {
|
||||
gl.procs.invalidateFramebuffer(GL_FRAMEBUFFER, attachmentCount, attachments.data());
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3298,13 +3297,13 @@ void OpenGLDriver::resolvePass(ResolveAction action, GLRenderTarget const* rt,
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, read);
|
||||
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, draw);
|
||||
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_READ_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_DRAW_FRAMEBUFFER)
|
||||
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(0, 0, (GLint)rt->width, (GLint)rt->height,
|
||||
0, 0, (GLint)rt->width, (GLint)rt->height, mask, GL_NEAREST);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -3483,7 +3482,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
if (buffer) {
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, s->gl.fbo_read ? s->gl.fbo_read : s->gl.fbo);
|
||||
glReadPixels(GLint(x), GLint(y), GLint(width), GLint(height), glFormat, glType, buffer);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
// now we need to flip the buffer vertically to match our API
|
||||
size_t const stride = p.stride ? p.stride : width;
|
||||
@@ -3515,7 +3514,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
glBufferData(GL_PIXEL_PACK_BUFFER, pboSize, nullptr, GL_STATIC_DRAW);
|
||||
glReadPixels(GLint(x), GLint(y), GLint(width), GLint(height), glFormat, glType, nullptr);
|
||||
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
// we're forced to make a copy on the heap because otherwise it deletes std::function<> copy
|
||||
// constructor.
|
||||
@@ -3555,7 +3554,7 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
|
||||
glDeleteBuffers(1, &pbo);
|
||||
scheduleDestroy(std::move(p));
|
||||
delete pUserBuffer;
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
});
|
||||
#endif
|
||||
}
|
||||
@@ -3580,7 +3579,7 @@ void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
|
||||
glCopyBufferSubData(bo->gl.binding, GL_PIXEL_PACK_BUFFER, offset, 0, size);
|
||||
gl.bindBuffer(bo->gl.binding, 0);
|
||||
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
// then, we schedule a mapBuffer of the PBO later, once the fence has signaled
|
||||
auto* pUserBuffer = new BufferDescriptor(std::move(p));
|
||||
@@ -3597,7 +3596,7 @@ void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
|
||||
glDeleteBuffers(1, &pbo);
|
||||
scheduleDestroy(std::move(p));
|
||||
delete pUserBuffer;
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
});
|
||||
} else {
|
||||
gl.bindBuffer(bo->gl.binding, bo->gl.id);
|
||||
@@ -3610,7 +3609,7 @@ void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
|
||||
}
|
||||
gl.bindBuffer(bo->gl.binding, 0);
|
||||
scheduleDestroy(std::move(p));
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -3640,7 +3639,7 @@ void OpenGLDriver::whenFrameComplete(const std::function<void()>& fn) noexcept {
|
||||
void OpenGLDriver::whenGpuCommandsComplete(const std::function<void()>& fn) noexcept {
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
mGpuCommandCompleteOps.emplace_back(sync, fn);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
|
||||
@@ -3858,7 +3857,7 @@ void OpenGLDriver::clearWithRasterPipe(TargetBufferFlags clearFlags,
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::resolve(
|
||||
@@ -3969,7 +3968,7 @@ void OpenGLDriver::blit(
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
break;
|
||||
}
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_DRAW_FRAMEBUFFER)
|
||||
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo[1]);
|
||||
switch (s->target) {
|
||||
@@ -3995,14 +3994,14 @@ void OpenGLDriver::blit(
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
break;
|
||||
}
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_READ_FRAMEBUFFER)
|
||||
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(
|
||||
srcOrigin.x, srcOrigin.y, srcOrigin.x + size.x, srcOrigin.y + size.y,
|
||||
dstOrigin.x, dstOrigin.y, dstOrigin.x + size.x, dstOrigin.y + size.y,
|
||||
mask, GL_NEAREST);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
gl.unbindFramebuffer(GL_DRAW_FRAMEBUFFER);
|
||||
gl.unbindFramebuffer(GL_READ_FRAMEBUFFER);
|
||||
@@ -4065,15 +4064,15 @@ void OpenGLDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, s->gl.fbo);
|
||||
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, d->gl.fbo);
|
||||
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_READ_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_DRAW_FRAMEBUFFER)
|
||||
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(
|
||||
srcRect.left, srcRect.bottom, srcRect.right(), srcRect.top(),
|
||||
dstRect.left, dstRect.bottom, dstRect.right(), dstRect.top(),
|
||||
GL_COLOR_BUFFER_BIT, glFilterMode);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4200,9 +4199,9 @@ void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
#endif
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
|
||||
CHECK_GL_ERROR_NON_FATAL()
|
||||
#else
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4232,9 +4231,9 @@ void OpenGLDriver::draw2GLES2(uint32_t indexOffset, uint32_t indexCount, uint32_
|
||||
reinterpret_cast<const void*>(indexOffset << rp->gl.indicesShift));
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
|
||||
CHECK_GL_ERROR_NON_FATAL()
|
||||
#else
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4284,9 +4283,9 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, uint3 workGroupCou
|
||||
#endif // BACKEND_OPENGL_LEVEL_GLES31
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
|
||||
CHECK_GL_ERROR_NON_FATAL()
|
||||
#else
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
case DescriptorType::INPUT_ATTACHMENT:
|
||||
break;
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,14 +114,14 @@ void TimerQueryNativeFactory::createTimerQuery(GLTimerQuery* tq) {
|
||||
|
||||
tq->state = std::make_shared<GLTimerQuery::State>();
|
||||
mContext.procs.genQueries(1u, &tq->state->gl.query);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void TimerQueryNativeFactory::destroyTimerQuery(GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
|
||||
mContext.procs.deleteQueries(1u, &tq->state->gl.query);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
tq->state.reset();
|
||||
}
|
||||
@@ -131,14 +131,14 @@ void TimerQueryNativeFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
|
||||
tq->state->elapsed.store(int64_t(TimerQueryResult::NOT_READY), std::memory_order_relaxed);
|
||||
mContext.procs.beginQuery(GL_TIME_ELAPSED, tq->state->gl.query);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* tq) {
|
||||
assert_invariant(tq->state);
|
||||
|
||||
mContext.procs.endQuery(GL_TIME_ELAPSED);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
|
||||
|
||||
@@ -153,7 +153,7 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
|
||||
|
||||
GLuint available = 0;
|
||||
context.procs.getQueryObjectuiv(state->gl.query, GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
if (!available) {
|
||||
// we need to try this one again later
|
||||
return false;
|
||||
|
||||
@@ -31,14 +31,14 @@
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -65,9 +65,9 @@ static std::string to_string(bool const b) { return b ? "true" : "false"; }
|
||||
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(io::ostream& out, ShaderStage shaderType, const char* name,
|
||||
GLuint shaderId, CString const& sourceCode) noexcept;
|
||||
static void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept;
|
||||
static void logCompilationError(ShaderStage shaderType, const char* name, GLuint shaderId,
|
||||
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,
|
||||
size_t len) noexcept;
|
||||
@@ -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{};
|
||||
@@ -122,12 +122,18 @@ struct ShaderCompilerService::OpenGLProgramToken : ProgramToken {
|
||||
}
|
||||
|
||||
std::optional<CallbackManager::Handle> handle{};
|
||||
|
||||
// Only valid when the blob functions are provided by users. The validity of this variable
|
||||
// doesn't guarantee that the program was created from the cache blob.
|
||||
BlobCacheKey key;
|
||||
|
||||
// Used for the `THREAD_POOL` mode.
|
||||
mutable Mutex lock;
|
||||
mutable Condition cond;
|
||||
bool signaled = false;
|
||||
|
||||
// Indicate this program was created from the cache blob.
|
||||
bool retrievedFromBlobCache = false;
|
||||
};
|
||||
|
||||
ShaderCompilerService::OpenGLProgramToken::~OpenGLProgramToken() {
|
||||
@@ -259,6 +265,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
// Try retrieving the cached program blob if available.
|
||||
token->gl.program = mBlobCache.retrieve(&token->key, mDriver.mPlatform, program);
|
||||
if (token->gl.program) {
|
||||
token->retrievedFromBlobCache = true;
|
||||
return token;
|
||||
}
|
||||
|
||||
@@ -337,12 +344,15 @@ GLuint ShaderCompilerService::getProgram(program_token_t& token) {
|
||||
|
||||
assert_invariant(token);// This function should be called when the token is still alive.
|
||||
|
||||
if (token->compiler.mMode == Mode::THREAD_POOL) {
|
||||
auto const job = token->compiler.mCompilerThreadPool.dequeue(token);
|
||||
if (!job) {
|
||||
// It's likely that the job was already completed. But it may be still being
|
||||
// executed at this moment. Just try waiting for it to avoid a race.
|
||||
token->wait();
|
||||
// Finalize any pending shader compilation tasks only when the token was created without cache.
|
||||
if (!token->retrievedFromBlobCache) {
|
||||
if (token->compiler.mMode == Mode::THREAD_POOL) {
|
||||
auto const job = token->compiler.mCompilerThreadPool.dequeue(token);
|
||||
if (!job) {
|
||||
// It's likely that the job was already completed. But it may be still being
|
||||
// executed at this moment. Just try waiting for it to avoid a race.
|
||||
token->wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,8 +443,9 @@ void ShaderCompilerService::ensureTokenIsReady(program_token_t const& token) {
|
||||
// just log warnings here instead of repeatedly checking compile status. If this turns
|
||||
// out to be a real issue later, we would need to consider doing the canonical way.
|
||||
if (!isCompileCompleted(token)) {
|
||||
slog.w << "Shader compilation for OpenGL program " << token->name.c_str_safe()
|
||||
<< " is not completed yet. The following program link may not succeed.";
|
||||
LOG(WARNING)
|
||||
<< "Shader compilation for OpenGL program " << token->name.c_str_safe()
|
||||
<< " is not completed yet. The following program link may not succeed.";
|
||||
}
|
||||
|
||||
linkProgram(mDriver.getContext(), token);
|
||||
@@ -601,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;
|
||||
}
|
||||
@@ -644,8 +655,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
}
|
||||
// Something went wrong. Log the error message.
|
||||
const ShaderStage type = static_cast<ShaderStage>(i);
|
||||
logCompilationError(slog.e, type, token->name.c_str_safe(), shader,
|
||||
token->shaderSourceCode[i]);
|
||||
logCompilationError(type, token->name.c_str_safe(), shader, token->shaderSourceCode[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,7 +707,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
glGetProgramiv(token->gl.program, GL_LINK_STATUS, &status);
|
||||
if (UTILS_UNLIKELY(status != GL_TRUE)) {
|
||||
// Something went wrong. Log the error message.
|
||||
logProgramLinkError(slog.e, token->name.c_str_safe(), token->gl.program);
|
||||
logProgramLinkError(token->name.c_str_safe(), token->gl.program);
|
||||
linked = false;
|
||||
}
|
||||
// No need to keep the shaders around regardless of the result of the program linking.
|
||||
@@ -747,8 +757,9 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
UTILS_NOINLINE
|
||||
/* static */ void logCompilationError(io::ostream& out, ShaderStage shaderType, const char* name,
|
||||
GLuint const shaderId, UTILS_UNUSED_IN_RELEASE CString const& sourceCode) noexcept {
|
||||
/* static */ void logCompilationError(ShaderStage shaderType, const char* name,
|
||||
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* {
|
||||
@@ -769,12 +780,14 @@ UTILS_NOINLINE
|
||||
CString infoLog(length);
|
||||
glGetShaderInfoLog(shaderId, length, nullptr, infoLog.data());
|
||||
|
||||
out << "Compilation error in " << to_string(shaderType) << " shader \"" << name << "\":\n"
|
||||
<< "\"" << infoLog.c_str() << "\"" << io::endl;
|
||||
LOG(ERROR) << "Compilation error in " << to_string(shaderType) << " shader \"" << name
|
||||
<< "\":";
|
||||
LOG(ERROR) << "\"" << infoLog.c_str() << "\"";
|
||||
}
|
||||
|
||||
#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;
|
||||
@@ -785,26 +798,25 @@ UTILS_NOINLINE
|
||||
} else {
|
||||
line = shader.substr(start, end - start);
|
||||
}
|
||||
out << lc++ << ": " << line.c_str() << '\n';
|
||||
LOG(ERROR) << lc++ << ": " << line.c_str();
|
||||
if (end == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
start = end + 1;
|
||||
}
|
||||
out << io::endl;
|
||||
LOG(ERROR) << "";
|
||||
#endif
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
/* static */ void logProgramLinkError(io::ostream& out, char const* name, GLuint program) noexcept {
|
||||
/* static */ void logProgramLinkError(char const* name, GLuint program) noexcept {
|
||||
GLint length = 0;
|
||||
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
|
||||
|
||||
CString infoLog(length);
|
||||
glGetProgramInfoLog(program, length, nullptr, infoLog.data());
|
||||
|
||||
out << "Link error in \"" << name << "\":\n"
|
||||
<< "\"" << infoLog.c_str() << "\"" << io::endl;
|
||||
LOG(ERROR) << "Link error in \"" << name << "\":\n" << "\"" << infoLog.c_str() << "\"";
|
||||
}
|
||||
|
||||
// If usages of the Google-style line directive are present, remove them, as some
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ CocoaExternalImage::SharedGl::~SharedGl() noexcept {
|
||||
CocoaExternalImage::CocoaExternalImage(const CVOpenGLTextureCacheRef textureCache,
|
||||
const SharedGl &sharedGl) noexcept : mSharedGl(sharedGl), mTextureCache(textureCache) {
|
||||
glGenFramebuffers(1, &mFBO);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
CocoaExternalImage::~CocoaExternalImage() noexcept {
|
||||
@@ -125,7 +125,7 @@ bool CocoaExternalImage::set(CVPixelBufferRef image) noexcept {
|
||||
mTexture = createTextureFromImage(image);
|
||||
mRgbaTexture = encodeCopyRectangleToTexture2D(CVOpenGLTextureGetName(mTexture),
|
||||
CVPixelBufferGetWidth(image), CVPixelBufferGetHeight(image));
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -182,33 +182,33 @@ GLuint CocoaExternalImage::encodeCopyRectangleToTexture2D(GLuint rectangle,
|
||||
// Create a texture to hold the result of the blit image.
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
// source textures
|
||||
glBindSampler(0, mSharedGl.sampler);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_RECTANGLE, rectangle);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
// destination texture
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_FRAMEBUFFER)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
// draw
|
||||
glViewport(0, 0, width, height);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
glUseProgram(mSharedGl.program);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
glDisableVertexAttribArray(0);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
mState.restore();
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ CocoaTouchExternalImage::CocoaTouchExternalImage(const CVOpenGLESTextureCacheRef
|
||||
|
||||
glGenFramebuffers(1, &mFBO);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
CocoaTouchExternalImage::~CocoaTouchExternalImage() noexcept {
|
||||
@@ -247,7 +247,7 @@ GLuint CocoaTouchExternalImage::encodeColorConversionPass(GLuint yPlaneTexture,
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
// source textures
|
||||
glBindSampler(0, mSharedGl.sampler);
|
||||
@@ -261,8 +261,8 @@ GLuint CocoaTouchExternalImage::encodeColorConversionPass(GLuint yPlaneTexture,
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_FRAMEBUFFER)
|
||||
CHECK_GL_ERROR()
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(GL_FRAMEBUFFER)
|
||||
|
||||
// geometry
|
||||
glBindVertexArray(0);
|
||||
@@ -275,7 +275,7 @@ GLuint CocoaTouchExternalImage::encodeColorConversionPass(GLuint yPlaneTexture,
|
||||
glUseProgram(mSharedGl.program);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
CHECK_GL_ERROR()
|
||||
|
||||
mState.restore();
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#if __has_include(<android/surface_texture.h>)
|
||||
@@ -59,7 +59,7 @@ void ExternalStreamManagerAndroid::destroy(ExternalStreamManagerAndroid* pExtern
|
||||
ExternalStreamManagerAndroid::ExternalStreamManagerAndroid() noexcept
|
||||
: mVm(VirtualMachineEnv::get()) {
|
||||
if (__builtin_available(android 28, *)) {
|
||||
slog.d << "Using ASurfaceTexture" << io::endl;
|
||||
DLOG(INFO) << "Using ASurfaceTexture";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,9 @@
|
||||
#endif
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -74,7 +74,7 @@ void PlatformEGL::logEglError(const char* name) noexcept {
|
||||
}
|
||||
|
||||
void PlatformEGL::logEglError(const char* name, EGLint error) noexcept {
|
||||
slog.e << name << " failed with " << getEglErrorName(error) << io::endl;
|
||||
LOG(ERROR) << name << " failed with " << getEglErrorName(error);
|
||||
}
|
||||
|
||||
const char* PlatformEGL::getEglErrorName(EGLint error) noexcept {
|
||||
@@ -101,7 +101,7 @@ void PlatformEGL::clearGlError() noexcept {
|
||||
// clear GL error that may have been set by previous calls
|
||||
GLenum const error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
slog.w << "Ignoring pending GL error " << io::hex << error << io::endl;
|
||||
LOG(WARNING) << "Ignoring pending GL error " << io::hex << error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& drive
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(!initialized)) {
|
||||
slog.e << "eglInitialize failed" << io::endl;
|
||||
LOG(ERROR) << "eglInitialize failed";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -29,13 +29,12 @@
|
||||
#include <android/native_window.h>
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/android/PerformanceHintManager.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
@@ -327,7 +326,7 @@ bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImag
|
||||
EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
|
||||
if (eglImage == EGL_NO_IMAGE_KHR) {
|
||||
// Handle error
|
||||
slog.e << "Failed to create EGL image" << io::endl;
|
||||
LOG(ERROR) << "Failed to create EGL image";
|
||||
glDeleteTextures(1, &texture->id);
|
||||
return false;
|
||||
}
|
||||
@@ -340,7 +339,7 @@ bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImag
|
||||
glBindTexture(texture->target, texture->id);
|
||||
GLenum error = glGetError();
|
||||
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
|
||||
slog.e << "Error after glBindTexture: " << error << io::endl;
|
||||
LOG(ERROR) << "Error after glBindTexture: " << error;
|
||||
glDeleteTextures(1, &texture->id);
|
||||
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
|
||||
glActiveTexture(prevActiveTexture);
|
||||
@@ -350,7 +349,7 @@ bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImag
|
||||
glEGLImageTargetTexture2DOES(texture->target, static_cast<GLeglImageOES>(eglImage));
|
||||
error = glGetError();
|
||||
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
|
||||
slog.e << "Error after glEGLImageTargetTexture2DOES: " << error << io::endl;
|
||||
LOG(ERROR) << "Error after glEGLImageTargetTexture2DOES: " << error;
|
||||
glDeleteTextures(1, &texture->id);
|
||||
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
|
||||
glActiveTexture(prevActiveTexture);
|
||||
@@ -408,7 +407,7 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
|
||||
|
||||
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer);
|
||||
if (!clientBuffer) {
|
||||
slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl;
|
||||
LOG(ERROR) << "Unable to get EGLClientBuffer from AHardwareBuffer.";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -427,7 +426,7 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
|
||||
EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay,
|
||||
EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data());
|
||||
if (eglImage == EGL_NO_IMAGE_KHR) {
|
||||
slog.e << "eglCreateImageKHR returned no image." << io::endl;
|
||||
LOG(ERROR) << "eglCreateImageKHR returned no image.";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -442,7 +441,7 @@ AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) n
|
||||
auto patchedCallback = [](void* image, void* userdata) {
|
||||
Closure* closure = (Closure*)userdata;
|
||||
if (eglDestroyImageKHR(closure->display, (EGLImageKHR) image) == EGL_FALSE) {
|
||||
slog.e << "eglDestroyImageKHR failed." << io::endl;
|
||||
LOG(ERROR) << "eglDestroyImageKHR failed.";
|
||||
}
|
||||
closure->acquiredImage.callback(closure->acquiredImage.image, closure->acquiredImage.userData);
|
||||
delete closure;
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -42,12 +42,12 @@ backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept {
|
||||
EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_API);
|
||||
if (UTILS_UNLIKELY(!bindAPI)) {
|
||||
slog.e << "eglBindAPI EGL_OPENGL_API failed" << io::endl;
|
||||
LOG(ERROR) << "eglBindAPI EGL_OPENGL_API failed";
|
||||
return nullptr;
|
||||
}
|
||||
int bindBlueGL = bluegl::bind();
|
||||
if (UTILS_UNLIKELY(bindBlueGL != 0)) {
|
||||
slog.e << "bluegl bind failed" << io::endl;
|
||||
LOG(ERROR) << "bluegl bind failed";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <backend/platforms/PlatformGLX.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
@@ -84,7 +84,7 @@ static PFNGLXGETPROCADDRESSPROC getProcAddress;
|
||||
static bool loadLibraries() {
|
||||
g_glx.library = dlopen(LIBRARY_GLX, RTLD_LOCAL | RTLD_NOW);
|
||||
if (!g_glx.library) {
|
||||
utils::slog.e << "Could not find library " << LIBRARY_GLX << utils::io::endl;
|
||||
LOG(ERROR) << "Could not find library " << LIBRARY_GLX;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ static bool loadLibraries() {
|
||||
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
if (!g_x11.library) {
|
||||
utils::slog.e << "Could not find library " << LIBRARY_X11 << utils::io::endl;
|
||||
LOG(ERROR) << "Could not find library " << LIBRARY_X11;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
|
||||
// Get the display device
|
||||
mGLXDisplay = g_x11.openDisplay(NULL);
|
||||
if (mGLXDisplay == nullptr) {
|
||||
utils::slog.e << "Failed to open X display. (exiting)." << utils::io::endl;
|
||||
LOG(ERROR) << "Failed to open X display. (exiting).";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -145,8 +145,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
|
||||
|
||||
r = g_glx.queryContext(mGLXDisplay, sharedCtx, GLX_FBCONFIG_ID, &usedFbId);
|
||||
if (r != 0) {
|
||||
utils::slog.e << "Failed to get GLX_FBCONFIG_ID from shared GL context."
|
||||
<< utils::io::endl;
|
||||
LOG(ERROR) << "Failed to get GLX_FBCONFIG_ID from shared GL context.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -154,7 +153,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
|
||||
GLXFBConfig* fbConfigs = g_glx.getFbConfigs(mGLXDisplay, 0, &numConfigs);
|
||||
|
||||
if (fbConfigs == nullptr) {
|
||||
utils::slog.e << "Failed to get the available GLXFBConfigs." << utils::io::endl;
|
||||
LOG(ERROR) << "Failed to get the available GLXFBConfigs.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -164,8 +163,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
|
||||
for (int i = 0; i < numConfigs; ++i) {
|
||||
r = g_glx.getFbConfigAttrib(mGLXDisplay, fbConfigs[i], GLX_FBCONFIG_ID, &fbId);
|
||||
if (r != 0) {
|
||||
utils::slog.e << "Failed to get GLX_FBCONFIG_ID for entry " << i << "."
|
||||
<< utils::io::endl;
|
||||
LOG(ERROR) << "Failed to get GLX_FBCONFIG_ID for entry " << i << ".";
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -176,8 +174,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
|
||||
}
|
||||
|
||||
if (fbIndex < 0) {
|
||||
utils::slog.e << "Failed to find an `GLXFBConfig` with the requested ID."
|
||||
<< utils::io::endl;
|
||||
LOG(ERROR) << "Failed to find an `GLXFBConfig` with the requested ID.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -202,8 +199,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
|
||||
getProcAddress((GLubyte*)"glXCreateContextAttribsARB");
|
||||
|
||||
if (glXCreateContextAttribs == nullptr) {
|
||||
utils::slog.i << "Unable to retrieve function pointer for `glXCreateContextAttribs()`."
|
||||
<< utils::io::endl;
|
||||
LOG(INFO) << "Unable to retrieve function pointer for `glXCreateContextAttribs()`.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "GL/glext.h"
|
||||
#include "GL/wglext.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
namespace {
|
||||
@@ -55,8 +55,7 @@ void reportWindowsError(DWORD dwError) {
|
||||
0, nullptr
|
||||
);
|
||||
|
||||
utils::slog.e << "Windows error code: " << dwError << ". " << lpMessageBuffer
|
||||
<< utils::io::endl;
|
||||
LOG(ERROR) << "Windows error code: " << dwError << ". " << lpMessageBuffer;
|
||||
|
||||
LocalFree(lpMessageBuffer);
|
||||
}
|
||||
@@ -106,7 +105,7 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
|
||||
HDC whdc = mWhdc = GetDC(mHWnd);
|
||||
if (whdc == NULL) {
|
||||
dwError = GetLastError();
|
||||
utils::slog.e << "CreateWindowA() failed" << utils::io::endl;
|
||||
LOG(ERROR) << "CreateWindowA() failed";
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -117,8 +116,7 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
|
||||
tempContext = wglCreateContext(whdc);
|
||||
if (!wglMakeCurrent(whdc, tempContext)) {
|
||||
dwError = GetLastError();
|
||||
utils::slog.e << "wglMakeCurrent() failed, whdc=" << whdc << ", tempContext=" <<
|
||||
tempContext << utils::io::endl;
|
||||
LOG(ERROR) << "wglMakeCurrent() failed, whdc=" << whdc << ", tempContext=" << tempContext;
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -142,7 +140,7 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
|
||||
}
|
||||
|
||||
if (!mContext) {
|
||||
utils::slog.e << "wglCreateContextAttribs() failed, whdc=" << whdc << utils::io::endl;
|
||||
LOG(ERROR) << "wglCreateContextAttribs() failed, whdc=" << whdc;
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -152,8 +150,7 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
|
||||
|
||||
if (!wglMakeCurrent(whdc, mContext)) {
|
||||
dwError = GetLastError();
|
||||
utils::slog.e << "wglMakeCurrent() failed, whdc=" << whdc << ", mContext=" <<
|
||||
mContext << utils::io::endl;
|
||||
LOG(ERROR) << "wglMakeCurrent() failed, whdc=" << whdc << ", mContext=" << mContext;
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ inline void blitFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect, V
|
||||
FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
<< " layout=" << src.getLayout()
|
||||
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
|
||||
<< " layout=" << dst.getLayout() << utils::io::endl;
|
||||
<< " layout=" << dst.getLayout();
|
||||
}
|
||||
|
||||
VkImageSubresourceRange const srcRange = src.getSubresourceRange();
|
||||
@@ -80,7 +80,7 @@ inline void resolveFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect
|
||||
FVK_LOGD << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
<< " layout=" << src.getLayout()
|
||||
<< " to=" << dst.texture->getVkImage() << ",level=" << (int) dst.level
|
||||
<< " layout=" << dst.getLayout() << utils::io::endl;
|
||||
<< " layout=" << dst.getLayout();
|
||||
}
|
||||
|
||||
VkImageSubresourceRange const srcRange = src.getSubresourceRange();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -215,7 +215,7 @@ VkSemaphore VulkanCommandBuffer::submit() {
|
||||
}
|
||||
FVK_LOGI << ") "
|
||||
<< " signal=" << mSubmission
|
||||
<< " fence=" << mFence << utils::io::endl;
|
||||
<< " fence=" << mFence;
|
||||
#endif
|
||||
|
||||
mFenceStatus->setStatus(VK_NOT_READY);
|
||||
@@ -224,7 +224,7 @@ VkSemaphore VulkanCommandBuffer::submit() {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
|
||||
if (result != VK_SUCCESS) {
|
||||
FVK_LOGD << "Failed command buffer submission result: " << result << utils::io::endl;
|
||||
FVK_LOGD << "Failed command buffer submission result: " << result;
|
||||
}
|
||||
#endif
|
||||
assert_invariant(result == VK_SUCCESS);
|
||||
@@ -490,7 +490,7 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
|
||||
mProtectedPool->pushMarker(str, timestamp);
|
||||
}
|
||||
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
|
||||
FVK_LOGD << "----> " << str << utils::io::endl;
|
||||
FVK_LOGD << "----> " << str;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -502,8 +502,7 @@ void VulkanCommands::popGroupMarker() {
|
||||
auto const& startTime = ret.second;
|
||||
auto const endTime = std::chrono::high_resolution_clock::now();
|
||||
std::chrono::duration<double> diff = endTime - startTime;
|
||||
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
|
||||
<< utils::io::endl;
|
||||
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms";
|
||||
#else
|
||||
mPool->popMarker();
|
||||
#endif // FVK_DEBUG_PRINT_GROUP_MARKERS
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANCONSTANTS_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANCONSTANTS_H
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -73,8 +73,8 @@
|
||||
#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
|
||||
|
||||
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
|
||||
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
|
||||
// order of calls).
|
||||
// to debug with print statements and want ordered logging (e.g LOG(INFO) and LOG(ERROR) will not
|
||||
// appear in order of calls).
|
||||
#define FVK_DEBUG_FORCE_LOG_TO_I 0x00020000
|
||||
|
||||
// Enable a minimal set of traces to assess the performance of the backend.
|
||||
@@ -174,15 +174,15 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
#endif
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_FORCE_LOG_TO_I)
|
||||
#define FVK_LOGI (utils::slog.i)
|
||||
#define FVK_LOGI LOG(INFO)
|
||||
#define FVK_LOGD FVK_LOGI
|
||||
#define FVK_LOGE FVK_LOGI
|
||||
#define FVK_LOGW FVK_LOGI
|
||||
#else
|
||||
#define FVK_LOGE (utils::slog.e)
|
||||
#define FVK_LOGW (utils::slog.w)
|
||||
#define FVK_LOGD (utils::slog.d)
|
||||
#define FVK_LOGI (utils::slog.i)
|
||||
#define FVK_LOGE LOG(ERROR)
|
||||
#define FVK_LOGW LOG(WARNING)
|
||||
#define FVK_LOGD DLOG(INFO)
|
||||
#define FVK_LOGI LOG(INFO)
|
||||
#endif
|
||||
|
||||
// All vkCreate* functions take an optional allocator. For now we select the default allocator by
|
||||
@@ -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
|
||||
|
||||
@@ -104,15 +104,15 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
|
||||
VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location,
|
||||
int32_t messageCode, const char* pLayerPrefix, const char* pMessage, void* pUserData) {
|
||||
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
|
||||
FVK_LOGE << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
|
||||
FVK_LOGE << "VULKAN ERROR: (" << pLayerPrefix << ") " << pMessage;
|
||||
} else {
|
||||
// TODO: emit best practices warnings about aggressive pipeline barriers.
|
||||
if (strstr(pMessage, "ALL_GRAPHICS_BIT") || strstr(pMessage, "ALL_COMMANDS_BIT")) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
FVK_LOGW << "VULKAN WARNING: (" << pLayerPrefix << ") " << pMessage << utils::io::endl;
|
||||
FVK_LOGW << "VULKAN WARNING: (" << pLayerPrefix << ") " << pMessage;
|
||||
}
|
||||
FVK_LOGE << utils::io::endl;
|
||||
FVK_LOGE;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
@@ -122,18 +122,16 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
|
||||
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
|
||||
void* pUserData) {
|
||||
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
||||
FVK_LOGE << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
|
||||
<< utils::io::endl;
|
||||
FVK_LOGE << "VULKAN ERROR: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage;
|
||||
} else {
|
||||
// TODO: emit best practices warnings about aggressive pipeline barriers.
|
||||
if (strstr(cbdata->pMessage, "ALL_GRAPHICS_BIT")
|
||||
|| strstr(cbdata->pMessage, "ALL_COMMANDS_BIT")) {
|
||||
return VK_FALSE;
|
||||
}
|
||||
FVK_LOGW << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage
|
||||
<< utils::io::endl;
|
||||
FVK_LOGW << "VULKAN WARNING: (" << cbdata->pMessageIdName << ") " << cbdata->pMessage;
|
||||
}
|
||||
FVK_LOGE << utils::io::endl;
|
||||
FVK_LOGE << "";
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
@@ -276,20 +274,18 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont
|
||||
// VulkanRenderTarget : 312 few
|
||||
// -- less than or equal to 312 bytes
|
||||
|
||||
FVK_LOGD
|
||||
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
|
||||
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
|
||||
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
|
||||
<< "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo)
|
||||
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
|
||||
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
|
||||
<< "\nVulkanTexture: " << sizeof(VulkanTexture)
|
||||
<< "\nVulkanTimerQuery: " << sizeof(VulkanTimerQuery)
|
||||
<< "\nHwStream: " << sizeof(HwStream)
|
||||
<< "\nVulkanRenderTarget: " << sizeof(VulkanRenderTarget)
|
||||
<< "\nVulkanFence: " << sizeof(VulkanFence)
|
||||
<< "\nVulkanProgram: " << sizeof(VulkanProgram)
|
||||
<< utils::io::endl;
|
||||
FVK_LOGD << "VulkanSwapChain: " << sizeof(VulkanSwapChain);
|
||||
FVK_LOGD << "VulkanBufferObject: " << sizeof(VulkanBufferObject);
|
||||
FVK_LOGD << "VulkanVertexBuffer: " << sizeof(VulkanVertexBuffer);
|
||||
FVK_LOGD << "VulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo);
|
||||
FVK_LOGD << "VulkanIndexBuffer: " << sizeof(VulkanIndexBuffer);
|
||||
FVK_LOGD << "VulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive);
|
||||
FVK_LOGD << "VulkanTexture: " << sizeof(VulkanTexture);
|
||||
FVK_LOGD << "VulkanTimerQuery: " << sizeof(VulkanTimerQuery);
|
||||
FVK_LOGD << "HwStream: " << sizeof(HwStream);
|
||||
FVK_LOGD << "VulkanRenderTarget: " << sizeof(VulkanRenderTarget);
|
||||
FVK_LOGD << "VulkanFence: " << sizeof(VulkanFence);
|
||||
FVK_LOGD << "VulkanProgram: " << sizeof(VulkanProgram);
|
||||
#endif
|
||||
|
||||
assert_invariant(platform);
|
||||
@@ -398,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();
|
||||
@@ -799,14 +801,12 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
mResourceManager.gc();
|
||||
|
||||
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
|
||||
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"
|
||||
<< utils::io::endl;
|
||||
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
}
|
||||
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
|
||||
if (!isProtectedContentSupported()) {
|
||||
FVK_LOGW << "protected swapchain requested, but Platform does not support it"
|
||||
<< utils::io::endl;
|
||||
FVK_LOGW << "protected swapchain requested, but Platform does not support it";
|
||||
}
|
||||
}
|
||||
auto swapChain = resource_ptr<VulkanSwapChain>::make(&mResourceManager, sch, mPlatform,
|
||||
@@ -817,8 +817,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
uint32_t height, uint64_t flags) {
|
||||
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
|
||||
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it"
|
||||
<< utils::io::endl;
|
||||
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
|
||||
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
|
||||
}
|
||||
assert_invariant(width > 0 && height > 0 && "Vulkan requires non-zero swap chain dimensions.");
|
||||
@@ -1243,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
|
||||
}
|
||||
|
||||
@@ -1306,7 +1306,7 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
|
||||
uint64_t const end = results.endTime;
|
||||
if (begin >= end) {
|
||||
// TODO: queries might have ran on different command buffers.
|
||||
FVK_LOGW << "Timestamps are not monotonically increasing. " << utils::io::endl;
|
||||
FVK_LOGW << "Timestamps are not monotonically increasing. ";
|
||||
*elapsedTime = 0;
|
||||
return TimerQueryResult::ERROR;
|
||||
}
|
||||
@@ -2044,7 +2044,7 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
|
||||
assert_invariant(inRenderPass);
|
||||
inRenderPass = false;
|
||||
} else if (inRenderPass && OUTSIDE_COMMANDS.find(command) != OUTSIDE_COMMANDS.end()) {
|
||||
FVK_LOGE << command.data() << " issued inside a render pass." << utils::io::endl;
|
||||
FVK_LOGE << command.data() << " issued inside a render pass.";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -100,8 +100,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
|
||||
<< "for render pass " << config.renderPass << ", "
|
||||
<< "samples = " << int(config.samples) << ", "
|
||||
<< "depth = " << (config.depth ? 1 : 0) << ", "
|
||||
<< "attachmentCount = " << attachmentCount
|
||||
<< utils::io::endl;
|
||||
<< "attachmentCount = " << attachmentCount;
|
||||
#endif
|
||||
|
||||
VkFramebufferCreateInfo info {
|
||||
@@ -341,8 +340,7 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
|
||||
<< "needsResolveMask = " << int(config.needsResolveMask) << ", "
|
||||
<< "usesLazilyAllocatedMemory = " << int(config.usesLazilyAllocatedMemory) << ", "
|
||||
<< "viewCount = " << int(config.viewCount) << ", "
|
||||
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
|
||||
<< utils::io::endl;
|
||||
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount;
|
||||
#endif
|
||||
|
||||
return renderPass;
|
||||
|
||||
@@ -287,7 +287,7 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
|
||||
<< ", " << modules[1] << ")" << utils::io::endl;
|
||||
<< ", " << modules[1] << ")";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -218,8 +218,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = ("
|
||||
<< shaderStages[0].module << ", " << shaderStages[1].module << ")"
|
||||
<< utils::io::endl;
|
||||
<< shaderStages[0].module << ", " << shaderStages[1].module << ")";
|
||||
#endif
|
||||
PipelineCacheEntry cacheEntry = {
|
||||
.lastUsed = mCurrentTime,
|
||||
@@ -228,7 +227,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
VKALLOC, &cacheEntry.handle);
|
||||
assert_invariant(error == VK_SUCCESS);
|
||||
if (error != VK_SUCCESS) {
|
||||
FVK_LOGE << "vkCreateGraphicsPipelines error " << error << utils::io::endl;
|
||||
FVK_LOGE << "vkCreateGraphicsPipelines error " << error;
|
||||
return nullptr;
|
||||
}
|
||||
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
|
||||
@@ -242,7 +241,7 @@ void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> pro
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
|
||||
mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
|
||||
FVK_LOGE << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
|
||||
FVK_LOGE << "Binding missing shader: " << program->name.c_str();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ fvkmemory::resource_ptr<VulkanTimerQuery> VulkanQueryManager::getNextQuery(
|
||||
fvkmemory::ResourceManager* resourceManager) {
|
||||
auto unused = ~mUsed;
|
||||
if (unused.empty()) {
|
||||
FVK_LOGE << "More than " << mUsed.size() << " timers are not supported." << utils::io::endl;
|
||||
FVK_LOGE << "More than " << mUsed.size() << " timers are not supported.";
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
|
||||
#if FVK_ENABLED(FVK_DEBUG_READ_PIXELS)
|
||||
FVK_LOGD << "readPixels created image=" << stagingImage
|
||||
<< " to copy from image=" << srcTexture->getVkImage()
|
||||
<< " src-layout=" << srcTexture->getLayout(0, 0) << utils::io::endl;
|
||||
<< " src-layout=" << srcTexture->getLayout(0, 0);
|
||||
#endif
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
@@ -188,8 +188,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
|
||||
memoryTypeIndex = selectMemoryFunc(memReqs.memoryTypeBits,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||
FVK_LOGW
|
||||
<< "readPixels is slow because VK_MEMORY_PROPERTY_HOST_CACHED_BIT is not available"
|
||||
<< utils::io::endl;
|
||||
<< "readPixels is slow because VK_MEMORY_PROPERTY_HOST_CACHED_BIT is not available";
|
||||
}
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
|
||||
@@ -305,7 +304,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
|
||||
fence = readCompleteFence]() mutable {
|
||||
VkResult status = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
|
||||
if (status != VK_SUCCESS) {
|
||||
FVK_LOGE << "Failed to wait for readPixels fence" << utils::io::endl;
|
||||
FVK_LOGE << "Failed to wait for readPixels fence";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -323,7 +322,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
|
||||
fvkutils::getComponentCount(srcFormat), srcPixels,
|
||||
static_cast<int>(subResourceLayout.rowPitch), static_cast<int>(width),
|
||||
static_cast<int>(height), swizzle)) {
|
||||
FVK_LOGE << "Unsupported PixelDataFormat or PixelDataType" << utils::io::endl;
|
||||
FVK_LOGE << "Unsupported PixelDataFormat or PixelDataType";
|
||||
}
|
||||
|
||||
vkUnmapMemory(device, stagingMemory);
|
||||
|
||||
@@ -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) {
|
||||
@@ -108,9 +129,9 @@ VulkanStage* VulkanStagePool::allocateNewStage(uint32_t capacity) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_STAGING_ALLOCATION)
|
||||
if (result != VK_SUCCESS) {
|
||||
FVK_LOGE << "Allocation error: " << result << utils::io::endl;
|
||||
FVK_LOGE << "Allocation error: " << result;
|
||||
} else {
|
||||
FVK_LOGD << "Allocated stage with hndl " << buffer << utils::io::endl;
|
||||
FVK_LOGD << "Allocated stage with hndl " << buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -188,8 +188,8 @@ VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props);
|
||||
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << vkFormat << " is not "
|
||||
"sampleable with optimal tiling." << utils::io::endl;
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << vkFormat
|
||||
<< " is not sampleable with optimal tiling.";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -361,7 +363,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
imageInfo.flags = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
|
||||
} else {
|
||||
FVK_LOGW << "Note: creating 2D views on 3D image is not available on this platform. "
|
||||
<< "i.e. we cannot render to slices of a 3D image" << utils::io::endl;
|
||||
<< "i.e. we cannot render to slices of a 3D image";
|
||||
}
|
||||
} else if (target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
imageInfo.arrayLayers = 6;
|
||||
@@ -420,7 +422,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
<< "type = " << imageInfo.imageType << ", "
|
||||
<< "flags = " << imageInfo.flags << ", "
|
||||
<< "target = " << static_cast<int>(target) <<", "
|
||||
<< "format = " << vkFormat << utils::io::endl;
|
||||
<< "format = " << vkFormat;
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create image."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
@@ -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
|
||||
@@ -731,14 +739,14 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
|
||||
<< range.levelCount << ")" << " from=" << oldLayout << " to=" << newLayout
|
||||
<< " format=" << mState->mVkFormat << " depth="
|
||||
<< fvkutils::isVkDepthFormat(mState->mVkFormat)
|
||||
<< " slice-by-slice=" << transitionSliceBySlice << utils::io::endl;
|
||||
<< " slice-by-slice=" << transitionSliceBySlice;
|
||||
#endif
|
||||
} else {
|
||||
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION)
|
||||
FVK_LOGD << "transition texture=" << mState->mTextureImage << " (" << range.baseArrayLayer
|
||||
<< "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << ","
|
||||
<< range.levelCount << ")" << " to=" << newLayout
|
||||
<< " is skipped because of no change in layout" << utils::io::endl;
|
||||
<< " is skipped because of no change in layout";
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -849,8 +857,7 @@ void VulkanTexture::print() const {
|
||||
level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount);
|
||||
FVK_LOGD << "[" << mState->mTextureImage << "]: (" << layer << "," << level
|
||||
<< ")=" << getLayout(layer, level)
|
||||
<< " primary=" << primary
|
||||
<< utils::io::endl;
|
||||
<< " primary=" << primary;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,8 +866,7 @@ void VulkanTexture::print() const {
|
||||
FVK_LOGD << "[" << mState->mTextureImage << ", imageView=" << view.second << "]=>"
|
||||
<< " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")"
|
||||
<< " count=(" << range.layerCount << "," << range.levelCount << ")"
|
||||
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type
|
||||
<< utils::io::endl;
|
||||
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/VulkanHandles.h"
|
||||
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
namespace filament::backend::fvkmemory {
|
||||
@@ -127,11 +128,11 @@ void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
|
||||
|
||||
void ResourceManager::print() const noexcept {
|
||||
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
utils::slog.e << "-------------------" << utils::io::endl;
|
||||
LOG(ERROR) << "-------------------";
|
||||
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
|
||||
utils::slog.e <<" " << getTypeStr((ResourceType) i) << "=" << COUNTER[i] << utils::io::endl;
|
||||
LOG(ERROR) << " " << getTypeStr((ResourceType) i) << "=" << COUNTER[i];
|
||||
}
|
||||
utils::slog.e << "+++++++++++++++++++" << utils::io::endl;
|
||||
LOG(ERROR) << "+++++++++++++++++++";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,9 @@
|
||||
#include "vulkan/utils/Helper.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <utils/PrivateImplementation-impl.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/PrivateImplementation-impl.h>
|
||||
|
||||
#define SWAPCHAIN_RET_FUNC(func, handle, ...) \
|
||||
if (mImpl->mSurfaceSwapChains.find(handle) != mImpl->mSurfaceSwapChains.end()) { \
|
||||
@@ -114,7 +115,7 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
|
||||
chainStruct(&physicalDeviceProperties2, &driverProperties);
|
||||
vkGetPhysicalDeviceProperties2(device, &physicalDeviceProperties2);
|
||||
FVK_LOGI << "Vulkan device driver: " << driverProperties.driverName << " "
|
||||
<< driverProperties.driverInfo << utils::io::endl;
|
||||
<< driverProperties.driverInfo;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceProperties deviceProperties;
|
||||
@@ -151,7 +152,7 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
|
||||
<< "(vendor " << utils::io::hex << vendorID << ", "
|
||||
<< "device " << deviceID << ", "
|
||||
<< "driver " << driverVersion << ", " << utils::io::dec << "api " << major << "."
|
||||
<< minor << ")" << utils::io::endl;
|
||||
<< minor << ")";
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
@@ -174,7 +175,7 @@ void printDepthFormats(VkPhysicalDevice device) {
|
||||
FVK_LOGI << format << " ";
|
||||
}
|
||||
}
|
||||
FVK_LOGI << utils::io::endl;
|
||||
FVK_LOGI << "";
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -294,10 +295,10 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
|
||||
} else {
|
||||
#if defined(__ANDROID__)
|
||||
FVK_LOGD << "Validation layers are not available; did you set jniLibs in your "
|
||||
<< "gradle file?" << utils::io::endl;
|
||||
<< "gradle file?";
|
||||
#else
|
||||
FVK_LOGD << "Validation layer not available; did you install the Vulkan SDK?\n"
|
||||
<< "Please ensure that VK_LAYER_PATH is set correctly." << utils::io::endl;
|
||||
<< "Please ensure that VK_LAYER_PATH is set correctly.";
|
||||
#endif // __ANDROID__
|
||||
|
||||
}
|
||||
@@ -831,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) {
|
||||
@@ -922,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
|
||||
@@ -984,8 +988,7 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
|
||||
|
||||
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
|
||||
if (!mImpl->mContext.mProtectedMemorySupported) {
|
||||
utils::slog.w << "protected swapchain requested, but VulkanPlatform does not support it"
|
||||
<< utils::io::endl;
|
||||
LOG(WARNING) << "protected swapchain requested, but VulkanPlatform does not support it";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,8 +163,7 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
// the number of images, though there may be limits related to the total amount of memory used
|
||||
// by presentable images."
|
||||
if (maxImageCount != 0 && desiredImageCount > maxImageCount) {
|
||||
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images."
|
||||
<< utils::io::endl;
|
||||
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images.";
|
||||
desiredImageCount = caps.minImageCount;
|
||||
}
|
||||
|
||||
@@ -266,8 +265,7 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
<< "swapchain-size=" << mSwapChainBundle.colors.size() << ", "
|
||||
<< "identity-transform=" << (caps.currentTransform == 1) << ", "
|
||||
<< "depth=" << mSwapChainBundle.depthFormat << ", "
|
||||
<< "protected=" << mSwapChainBundle.isProtected
|
||||
<< io::endl;
|
||||
<< "protected=" << mSwapChainBundle.isProtected;
|
||||
|
||||
VkSemaphoreCreateInfo const semaphoreCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
@@ -293,7 +291,7 @@ VkResult VulkanPlatformSurfaceSwapChain::acquire(VulkanPlatform::ImageSyncData*
|
||||
// Users should be notified of a suboptimal surface, but it should not cause a cascade of
|
||||
// log messages or a loop of re-creations.
|
||||
if (result == VK_SUBOPTIMAL_KHR && !mSuboptimal) {
|
||||
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain." << io::endl;
|
||||
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain.";
|
||||
mSuboptimal = true;
|
||||
}
|
||||
return result;
|
||||
@@ -315,7 +313,7 @@ VkResult VulkanPlatformSurfaceSwapChain::present(uint32_t index, VkSemaphore fin
|
||||
// On Android Q and above, a suboptimal surface is always reported after screen rotation:
|
||||
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
|
||||
if (result == VK_SUBOPTIMAL_KHR && !mSuboptimal) {
|
||||
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain." << utils::io::endl;
|
||||
FVK_LOGW << "Vulkan Driver: Suboptimal swap chain.";
|
||||
mSuboptimal = true;
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <optional>
|
||||
// C++ port of https://github.com/JolifantoBambla/webgpu-spd for early experiments- do not merge like this
|
||||
namespace spd {
|
||||
|
||||
// Enum for selecting the downsampling filter.
|
||||
enum class SPDFilter {
|
||||
Average,
|
||||
Min,
|
||||
Max,
|
||||
MinMax
|
||||
};
|
||||
|
||||
// Enum for shader scalar types.
|
||||
enum class SPDScalarType {
|
||||
F32,
|
||||
F16,
|
||||
I32,
|
||||
U32
|
||||
};
|
||||
|
||||
// Configuration for a single mipmap generation pass.
|
||||
struct SPDPassConfig {
|
||||
SPDFilter filter = SPDFilter::Average;
|
||||
wgpu::Texture targetTexture = nullptr;
|
||||
uint32_t numMips = 0;
|
||||
bool halfPrecision = false;
|
||||
uint32_t sourceMipLevel = 0;
|
||||
};
|
||||
|
||||
// Holds a pipeline and its corresponding bind group layout.
|
||||
struct SPDPipeline {
|
||||
wgpu::BindGroupLayout mipsBindGroupLayout = nullptr;
|
||||
wgpu::ComputePipeline pipeline = nullptr;
|
||||
};
|
||||
|
||||
// Manages pipeline creation, caching, and execution for mipmap generation.
|
||||
class MipmapGenerator {
|
||||
public:
|
||||
MipmapGenerator(const wgpu::Device& device);
|
||||
|
||||
// Pre-creates pipelines for specified formats and filters.
|
||||
void PreparePipelines(wgpu::TextureFormat format, SPDFilter filter, bool halfPrecision = false);
|
||||
|
||||
// Generates a compute pass for creating mipmaps.
|
||||
void Generate(
|
||||
wgpu::CommandEncoder& commandEncoder,
|
||||
wgpu::Texture srcTexture,
|
||||
const SPDPassConfig& config
|
||||
);
|
||||
|
||||
private:
|
||||
wgpu::Device m_device;
|
||||
wgpu::BindGroupLayout m_internalResourcesBindGroupLayout;
|
||||
|
||||
// Cached pipelines: Map<TextureFormat, Map<SPDScalarType, Map<Filter, Map<NumMips, Pipeline>>>>
|
||||
std::unordered_map<wgpu::TextureFormat,
|
||||
std::unordered_map<SPDScalarType,
|
||||
std::unordered_map<SPDFilter,
|
||||
std::unordered_map<uint32_t, SPDPipeline>>>> m_pipelines;
|
||||
|
||||
// Helper methods
|
||||
SPDPipeline& GetOrCreatePipeline(wgpu::TextureFormat format, SPDFilter filter, uint32_t numMips, SPDScalarType scalarType);
|
||||
SPDScalarType SanitizeScalarType(wgpu::TextureFormat format, bool halfPrecision);
|
||||
std::string GetFilterCode(SPDFilter filter);
|
||||
};
|
||||
// Assuming SPD_FILTER_AVERAGE is a string constant
|
||||
const std::string SPD_FILTER_AVERAGE = "value * 0.25"; // Original filter operation
|
||||
|
||||
// Generates the WGSL shader code dynamically.
|
||||
std::string MakeShaderCode(
|
||||
wgpu::TextureFormat outputFormat,
|
||||
const std::string& filterOp = SPD_FILTER_AVERAGE,
|
||||
uint32_t numMips = 0,
|
||||
SPDScalarType scalarType = SPDScalarType::F32
|
||||
);
|
||||
|
||||
} // namespace spd
|
||||
@@ -0,0 +1,5 @@
|
||||
WebGPU does not include a trivial way to create mipmaps, and thus we must provide a render pass or compute pass to generate them.
|
||||
|
||||
This directory is the result of porting https://github.com/JolifantoBambla/webgpu-spd to C++, which itself is a WebGPU port of https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK/blob/main/docs/samples/single-pass-downsampler.md.
|
||||
|
||||
The first version of our port is primarily from a Gemini "Convert this to C++" request, which worked as an MVP with very few changes. Future commits will likely alter this version significantly, but it is being merged in this early state to facilitate rapid iteration.
|
||||
+373
-212
@@ -1,257 +1,406 @@
|
||||
#include "SpdMipmapGenerator.h"
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
// C++ port of https://github.com/JolifantoBambla/webgpu-spd for early experiments- do not merge like this
|
||||
// C++ port of https://github.com/JolifantoBambla/webgpu-spd for early experiments
|
||||
namespace spd {
|
||||
|
||||
// Helper to convert enums to strings for map keys or shader code
|
||||
const char* to_string(wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::RGBA8Unorm: return "rgba8unorm";
|
||||
case wgpu::TextureFormat::BGRA8Unorm: return "bgra8unorm";
|
||||
case wgpu::TextureFormat::R32Float: return "r32float";
|
||||
case wgpu::TextureFormat::RG32Float: return "rg32float";
|
||||
case wgpu::TextureFormat::RGBA32Float: return "rgba32float";
|
||||
case wgpu::TextureFormat::R16Float: return "r16float";
|
||||
case wgpu::TextureFormat::RG16Float: return "rg16float";
|
||||
case wgpu::TextureFormat::RGBA16Float: return "rgba16float";
|
||||
// Add other formats as needed
|
||||
default: return "rgba8unorm";
|
||||
}
|
||||
// Helper to convert enums to strings for map keys or shader code
|
||||
const char* to_string(wgpu::TextureFormat format) {
|
||||
switch (format) {
|
||||
case wgpu::TextureFormat::RGBA8Unorm:
|
||||
return "rgba8unorm";
|
||||
case wgpu::TextureFormat::BGRA8Unorm:
|
||||
return "bgra8unorm";
|
||||
case wgpu::TextureFormat::R32Float:
|
||||
return "r32float";
|
||||
case wgpu::TextureFormat::RG32Float:
|
||||
return "rg32float";
|
||||
case wgpu::TextureFormat::RGBA32Float:
|
||||
return "rgba32float";
|
||||
case wgpu::TextureFormat::R16Float:
|
||||
return "r16float";
|
||||
case wgpu::TextureFormat::RG16Float:
|
||||
return "rg16float";
|
||||
case wgpu::TextureFormat::RGBA16Float:
|
||||
return "rgba16float";
|
||||
// Add other formats as needed
|
||||
default:
|
||||
return "rgba8unorm";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string MipmapGenerator::GetFilterCode(SPDFilter filter) {
|
||||
switch (filter) {
|
||||
case SPDFilter::Min:
|
||||
return R"(
|
||||
std::string MipmapGenerator::GetFilterCode(SPDFilter filter) {
|
||||
switch (filter) {
|
||||
case SPDFilter::Min:
|
||||
return R"(
|
||||
fn spd_reduce_4(v0: vec4<SPDScalar>, v1: vec4<SPDScalar>, v2: vec4<SPDScalar>, v3: vec4<SPDScalar>) -> vec4<SPDScalar> {
|
||||
return min(min(v0, v1), min(v2, v3));
|
||||
}
|
||||
)";
|
||||
case SPDFilter::Max:
|
||||
return R"(
|
||||
case SPDFilter::Max:
|
||||
return R"(
|
||||
fn spd_reduce_4(v0: vec4<SPDScalar>, v1: vec4<SPDScalar>, v2: vec4<SPDScalar>, v3: vec4<SPDScalar>) -> vec4<SPDScalar> {
|
||||
return max(max(v0, v1), max(v2, v3));
|
||||
}
|
||||
)";
|
||||
case SPDFilter::MinMax:
|
||||
return R"(
|
||||
case SPDFilter::MinMax:
|
||||
return R"(
|
||||
fn spd_reduce_4(v0: vec4<SPDScalar>, v1: vec4<SPDScalar>, v2: vec4<SPDScalar>, v3: vec4<SPDScalar>) -> vec4<SPDScalar> {
|
||||
let max4 = max(max(v0.xy, v1.xy), max(v2.xy, v3.xy));
|
||||
return vec4<SPDScalar>(min(min(v0.x, v1.x), min(v2.x, v3.x)), max(max4.x, max4.y), 0.0, 0.0);
|
||||
}
|
||||
)";
|
||||
case SPDFilter::Average:
|
||||
default:
|
||||
return R"(
|
||||
case SPDFilter::Average:
|
||||
default:
|
||||
return R"(
|
||||
fn spd_reduce_4(v0: vec4<SPDScalar>, v1: vec4<SPDScalar>, v2: vec4<SPDScalar>, v3: vec4<SPDScalar>) -> vec4<SPDScalar> {
|
||||
return (v0 + v1 + v2 + v3) * 0.25;
|
||||
}
|
||||
)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MipmapGenerator::MipmapGenerator(const wgpu::Device& device)
|
||||
: m_device(device) {
|
||||
wgpu::BindGroupLayoutEntry bglEntry{};
|
||||
bglEntry.binding = 0;
|
||||
bglEntry.visibility = wgpu::ShaderStage::Compute;
|
||||
bglEntry.buffer.type = wgpu::BufferBindingType::Uniform;
|
||||
bglEntry.buffer.minBindingSize = 16;
|
||||
|
||||
wgpu::BindGroupLayoutDescriptor bglDesc{};
|
||||
bglDesc.entryCount = 1;
|
||||
bglDesc.entries = &bglEntry;
|
||||
m_internalResourcesBindGroupLayout =
|
||||
m_device.CreateBindGroupLayout(&bglDesc); // Basic BGL for <= 6 mips
|
||||
|
||||
wgpu::Limits deviceLimits;
|
||||
m_device.GetLimits(&deviceLimits);
|
||||
// Max number of *output* mips we can generate in a single pass.
|
||||
// This is device.maxStorageTexturesPerShaderStage - 1 (for the source texture binding),
|
||||
// further capped at 11 output mips (which means 12 total texture bindings: 1 source + 11
|
||||
// outputs). WebGPU minspec for maxStorageTexturesPerShaderStage is 4, so
|
||||
// deviceLimits.maxStorageTexturesPerShaderStage >= 4. Thus, m_maxMipsPerPass will be at least
|
||||
// std::min(4u - 1, 11u) = 3.
|
||||
m_maxMipsPerPass =
|
||||
std::min(static_cast<uint32_t>(deviceLimits.maxStorageTexturesPerShaderStage) - 1, 11u);
|
||||
m_maxArrayLayers = deviceLimits.maxStorageTexturesPerShaderStage;
|
||||
|
||||
|
||||
// If we can generate more than 6 mips in a pass, we need an advanced BGL
|
||||
// that includes atomic counter and mid-mip buffer for levels > 5.
|
||||
if (m_maxMipsPerPass > 6) {
|
||||
std::vector<wgpu::BindGroupLayoutEntry> entriesAdvanced(3);
|
||||
// Entry 0: Uniform buffer (DownsamplePassMeta)
|
||||
entriesAdvanced[0].binding = 0;
|
||||
entriesAdvanced[0].visibility = wgpu::ShaderStage::Compute;
|
||||
entriesAdvanced[0].buffer.type = wgpu::BufferBindingType::Uniform;
|
||||
entriesAdvanced[0].buffer.minBindingSize = 16; // sizeof(DownsamplePassMeta) effectively
|
||||
|
||||
// Entry 1: Storage buffer (spd_global_counter)
|
||||
entriesAdvanced[1].binding = 1;
|
||||
entriesAdvanced[1].visibility = wgpu::ShaderStage::Compute;
|
||||
entriesAdvanced[1].buffer.type = wgpu::BufferBindingType::Storage;
|
||||
entriesAdvanced[1].buffer.minBindingSize = 4; // sizeof(atomic<u32>)
|
||||
|
||||
// Entry 2: Storage buffer (mip_dst_6_buffer)
|
||||
entriesAdvanced[2].binding = 2;
|
||||
entriesAdvanced[2].visibility = wgpu::ShaderStage::Compute;
|
||||
entriesAdvanced[2].buffer.type = wgpu::BufferBindingType::Storage;
|
||||
// Size for array<array<array<vec4<f32>, 64>, 64>> which is 16 (bytes for vec4f32) * 64 * 64
|
||||
// * arrayLayers. The shader defines it as [slice][uv.y][uv.x], so 64x64 per slice.
|
||||
// minBindingSize here is for one slice as a baseline. Actual buffer will be larger for
|
||||
// multiple array layers.
|
||||
entriesAdvanced[2].buffer.minBindingSize = 16 * 64 * 64;
|
||||
|
||||
wgpu::BindGroupLayoutDescriptor bglDescAdvanced{};
|
||||
bglDescAdvanced.label = "SPD Internal BGL (Advanced)";
|
||||
bglDescAdvanced.entryCount = static_cast<uint32_t>(entriesAdvanced.size());
|
||||
bglDescAdvanced.entries = entriesAdvanced.data();
|
||||
m_internalResourcesBindGroupLayout_Advanced =
|
||||
m_device.CreateBindGroupLayout(&bglDescAdvanced);
|
||||
}
|
||||
}
|
||||
|
||||
void MipmapGenerator::PreparePipelines(wgpu::TextureFormat format, SPDFilter filter,
|
||||
bool halfPrecision) {
|
||||
SPDScalarType scalarType = SanitizeScalarType(format, halfPrecision);
|
||||
// Prepare for a reasonable number of mips, up to m_maxMipsPerPass
|
||||
// The loop should go from 1 up to and including m_maxMipsPerPass.
|
||||
// If m_maxMipsPerPass is 0 (e.g. maxStorageTexturesPerShaderStage <=1 ), this loop won't run.
|
||||
for (uint32_t i = 1; i <= m_maxMipsPerPass; ++i) {
|
||||
GetOrCreatePipeline({ format, scalarType, filter, i });
|
||||
}
|
||||
}
|
||||
|
||||
SPDScalarType MipmapGenerator::SanitizeScalarType(wgpu::TextureFormat format, bool halfPrecision) {
|
||||
std::string formatStr = to_string(format);
|
||||
std::transform(formatStr.begin(), formatStr.end(), formatStr.begin(), ::tolower);
|
||||
|
||||
SPDScalarType texelType = SPDScalarType::F32; // Default to F32
|
||||
if (formatStr.find("sint") != std::string::npos) {
|
||||
texelType = SPDScalarType::I32;
|
||||
} else if (formatStr.find("uint") != std::string::npos) {
|
||||
texelType = SPDScalarType::U32;
|
||||
}
|
||||
|
||||
|
||||
MipmapGenerator::MipmapGenerator(const wgpu::Device& device) : m_device(device) {
|
||||
wgpu::BindGroupLayoutEntry bglEntry{};
|
||||
bglEntry.binding = 0;
|
||||
bglEntry.visibility = wgpu::ShaderStage::Compute;
|
||||
bglEntry.buffer.type = wgpu::BufferBindingType::Uniform;
|
||||
bglEntry.buffer.minBindingSize = 16;
|
||||
|
||||
wgpu::BindGroupLayoutDescriptor bglDesc{};
|
||||
bglDesc.entryCount = 1;
|
||||
bglDesc.entries = &bglEntry;
|
||||
m_internalResourcesBindGroupLayout = m_device.CreateBindGroupLayout(&bglDesc);
|
||||
}
|
||||
|
||||
void MipmapGenerator::PreparePipelines(wgpu::TextureFormat format, SPDFilter filter, bool halfPrecision) {
|
||||
SPDScalarType scalarType = SanitizeScalarType(format, halfPrecision);
|
||||
// Prepare for a reasonable number of mips
|
||||
for (uint32_t i = 1; i <= 4; ++i) {
|
||||
GetOrCreatePipeline(format, filter, i, scalarType);
|
||||
if (halfPrecision && texelType == SPDScalarType::F32) {
|
||||
if (m_device.HasFeature(wgpu::FeatureName::ShaderF16)) {
|
||||
return SPDScalarType::F16;
|
||||
} else {
|
||||
// Optional: Log a warning here that halfPrecision was requested for a float format
|
||||
// but the device does not support ShaderF16, so F32 will be used.
|
||||
}
|
||||
} else if (halfPrecision && texelType != SPDScalarType::F32) {
|
||||
// Optional: Log a warning here that halfPrecision was requested for a non-float format.
|
||||
}
|
||||
|
||||
SPDScalarType MipmapGenerator::SanitizeScalarType(wgpu::TextureFormat format, bool halfPrecision) {
|
||||
std::string formatStr = to_string(format);
|
||||
std::transform(formatStr.begin(), formatStr.end(), formatStr.begin(), ::tolower);
|
||||
return texelType;
|
||||
}
|
||||
|
||||
SPDScalarType texelType = SPDScalarType::F32;
|
||||
if (formatStr.find("sint") != std::string::npos) {
|
||||
texelType = SPDScalarType::I32;
|
||||
} else if (formatStr.find("uint") != std::string::npos) {
|
||||
texelType = SPDScalarType::U32;
|
||||
}
|
||||
SPDPipeline& MipmapGenerator::GetOrCreatePipeline(const PipelineCacheKey& key) {
|
||||
uint32_t numMipsForPipeline = std::min(key.numMips, m_maxMipsPerPass);
|
||||
if (m_pipelines.find(key) == m_pipelines.end()) {
|
||||
// Create the pipeline
|
||||
SPDPipeline spdPipeline;
|
||||
|
||||
if (halfPrecision) {
|
||||
bool hasF16 = false;
|
||||
// In a real Dawn app, you would check device.GetSupportedFeatures()
|
||||
// For now, let's assume it's available if requested.
|
||||
hasF16 = true;
|
||||
|
||||
if (!hasF16) {
|
||||
// Log warning: half precision requested but not supported
|
||||
}
|
||||
if (texelType != SPDScalarType::F32) {
|
||||
// Log warning: half precision for non-float format
|
||||
}
|
||||
if (hasF16 && texelType == SPDScalarType::F32) {
|
||||
return SPDScalarType::F16;
|
||||
std::vector<wgpu::BindGroupLayoutEntry> mipsBglEntries;
|
||||
// Bindings are 0 for source, 1 to numMipsForPipeline for destinations
|
||||
for (uint32_t i = 0; i <= numMipsForPipeline; ++i) {
|
||||
wgpu::BindGroupLayoutEntry entry{};
|
||||
entry.binding = i;
|
||||
entry.visibility = wgpu::ShaderStage::Compute;
|
||||
if (i == 0) {
|
||||
entry.texture.sampleType = (key.scalarType == SPDScalarType::I32)
|
||||
? wgpu::TextureSampleType::Sint
|
||||
: (key.scalarType == SPDScalarType::U32)
|
||||
? wgpu::TextureSampleType::Uint
|
||||
: wgpu::TextureSampleType::UnfilterableFloat;
|
||||
entry.texture.viewDimension = wgpu::TextureViewDimension::e2DArray;
|
||||
} else {
|
||||
entry.storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
|
||||
entry.storageTexture.format = key.format;
|
||||
entry.storageTexture.viewDimension = wgpu::TextureViewDimension::e2DArray;
|
||||
}
|
||||
mipsBglEntries.push_back(entry);
|
||||
}
|
||||
return texelType;
|
||||
|
||||
wgpu::BindGroupLayoutDescriptor mipsBglDesc{};
|
||||
mipsBglDesc.entryCount = mipsBglEntries.size();
|
||||
mipsBglDesc.entries = mipsBglEntries.data();
|
||||
spdPipeline.mipsBindGroupLayout = m_device.CreateBindGroupLayout(&mipsBglDesc);
|
||||
|
||||
std::string shaderCode = MakeShaderCode(key.format, GetFilterCode(key.filter),
|
||||
numMipsForPipeline, key.scalarType);
|
||||
wgpu::ShaderModuleWGSLDescriptor wgslDesc{};
|
||||
wgslDesc.code = shaderCode.c_str();
|
||||
|
||||
wgpu::ShaderModuleDescriptor shaderModuleDesc{};
|
||||
shaderModuleDesc.nextInChain = &wgslDesc;
|
||||
wgpu::ShaderModule shaderModule = m_device.CreateShaderModule(&shaderModuleDesc);
|
||||
|
||||
wgpu::BindGroupLayout chosenInternalBGL =
|
||||
(numMipsForPipeline > 6 && m_internalResourcesBindGroupLayout_Advanced)
|
||||
? m_internalResourcesBindGroupLayout_Advanced
|
||||
: m_internalResourcesBindGroupLayout;
|
||||
|
||||
wgpu::BindGroupLayout bgls[] = { spdPipeline.mipsBindGroupLayout, chosenInternalBGL };
|
||||
wgpu::PipelineLayoutDescriptor layoutDesc{};
|
||||
layoutDesc.bindGroupLayoutCount = 2;
|
||||
layoutDesc.bindGroupLayouts = bgls;
|
||||
|
||||
wgpu::ComputePipelineDescriptor pipelineDesc{};
|
||||
pipelineDesc.layout = m_device.CreatePipelineLayout(&layoutDesc);
|
||||
pipelineDesc.compute.module = shaderModule;
|
||||
pipelineDesc.compute.entryPoint = "downsample";
|
||||
|
||||
spdPipeline.pipeline = m_device.CreateComputePipeline(&pipelineDesc);
|
||||
m_pipelines[key] = std::move(spdPipeline);
|
||||
}
|
||||
return m_pipelines[key];
|
||||
}
|
||||
|
||||
|
||||
void MipmapGenerator::Generate(wgpu::CommandEncoder& commandEncoder, wgpu::Texture srcTexture,
|
||||
const SPDPassConfig& config) {
|
||||
if (config.numMips <= 1 || config.numMips <= config.sourceMipLevel + 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
SPDPipeline& MipmapGenerator::GetOrCreatePipeline(wgpu::TextureFormat format, SPDFilter filter, uint32_t numMips, SPDScalarType scalarType) {
|
||||
if (m_pipelines[format][scalarType][filter].count(numMips) == 0) {
|
||||
// Create the pipeline
|
||||
SPDPipeline spdPipeline;
|
||||
|
||||
std::vector<wgpu::BindGroupLayoutEntry> mipsBglEntries;
|
||||
for (uint32_t i = 0; i <= numMips; ++i) {
|
||||
wgpu::BindGroupLayoutEntry entry{};
|
||||
entry.binding = i;
|
||||
entry.visibility = wgpu::ShaderStage::Compute;
|
||||
if (i == 0) {
|
||||
entry.texture.sampleType = (scalarType == SPDScalarType::I32) ? wgpu::TextureSampleType::Sint :
|
||||
(scalarType == SPDScalarType::U32) ? wgpu::TextureSampleType::Uint :
|
||||
wgpu::TextureSampleType::UnfilterableFloat;
|
||||
entry.texture.viewDimension = wgpu::TextureViewDimension::e2DArray;
|
||||
} else {
|
||||
entry.storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
|
||||
entry.storageTexture.format = format;
|
||||
entry.storageTexture.viewDimension = wgpu::TextureViewDimension::e2DArray;
|
||||
}
|
||||
mipsBglEntries.push_back(entry);
|
||||
}
|
||||
|
||||
wgpu::BindGroupLayoutDescriptor mipsBglDesc{};
|
||||
mipsBglDesc.entryCount = mipsBglEntries.size();
|
||||
mipsBglDesc.entries = mipsBglEntries.data();
|
||||
spdPipeline.mipsBindGroupLayout = m_device.CreateBindGroupLayout(&mipsBglDesc);
|
||||
|
||||
std::string shaderCode = MakeShaderCode(format, GetFilterCode(filter), numMips, scalarType);
|
||||
wgpu::ShaderModuleWGSLDescriptor wgslDesc{};
|
||||
wgslDesc.code = shaderCode.c_str();
|
||||
|
||||
wgpu::ShaderModuleDescriptor shaderModuleDesc{};
|
||||
shaderModuleDesc.nextInChain = &wgslDesc;
|
||||
wgpu::ShaderModule shaderModule = m_device.CreateShaderModule(&shaderModuleDesc);
|
||||
|
||||
wgpu::BindGroupLayout bgls[] = { spdPipeline.mipsBindGroupLayout, m_internalResourcesBindGroupLayout };
|
||||
wgpu::PipelineLayoutDescriptor layoutDesc{};
|
||||
layoutDesc.bindGroupLayoutCount = 2;
|
||||
layoutDesc.bindGroupLayouts = bgls;
|
||||
|
||||
wgpu::ComputePipelineDescriptor pipelineDesc{};
|
||||
pipelineDesc.layout = m_device.CreatePipelineLayout(&layoutDesc);
|
||||
pipelineDesc.compute.module = shaderModule;
|
||||
pipelineDesc.compute.entryPoint = "downsample";
|
||||
|
||||
spdPipeline.pipeline = m_device.CreateComputePipeline(&pipelineDesc);
|
||||
m_pipelines[format][scalarType][filter][numMips] = std::move(spdPipeline);
|
||||
}
|
||||
return m_pipelines[format][scalarType][filter][numMips];
|
||||
if (m_maxMipsPerPass == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
void MipmapGenerator::Generate(
|
||||
wgpu::CommandEncoder& commandEncoder,
|
||||
wgpu::Texture srcTexture,
|
||||
const SPDPassConfig& config)
|
||||
{
|
||||
uint32_t width = srcTexture.GetWidth();
|
||||
uint32_t height = srcTexture.GetHeight();
|
||||
uint32_t arrayLayerCount = srcTexture.GetDepthOrArrayLayers();
|
||||
|
||||
wgpu::Texture target = config.targetTexture ? config.targetTexture : srcTexture;
|
||||
uint32_t numMips = config.numMips > 0 ? config.numMips : target.GetMipLevelCount() - 1;
|
||||
PreparePipelines(srcTexture.GetFormat(), config.filter, config.halfPrecision);
|
||||
|
||||
if (numMips == 0) return;
|
||||
uint32_t totalSrcArrayLayers = srcTexture.GetDepthOrArrayLayers();
|
||||
|
||||
SPDScalarType scalarType = SanitizeScalarType(srcTexture.GetFormat(), config.halfPrecision);
|
||||
SPDPipeline& spdPipeline = GetOrCreatePipeline(target.GetFormat(), config.filter, numMips, scalarType);
|
||||
|
||||
// --- Create Bind Group 0 (Mips) ---
|
||||
std::vector<wgpu::BindGroupEntry> mipEntries;
|
||||
// Loop over array layer chunks
|
||||
for (uint32_t currentBaseArrayLayer = 0; currentBaseArrayLayer < totalSrcArrayLayers;
|
||||
currentBaseArrayLayer += m_maxArrayLayers) {
|
||||
|
||||
wgpu::TextureViewDescriptor srcViewDesc{};
|
||||
srcViewDesc.dimension = wgpu::TextureViewDimension::e2DArray;
|
||||
srcViewDesc.baseMipLevel = config.sourceMipLevel;
|
||||
srcViewDesc.mipLevelCount = 1;
|
||||
srcViewDesc.baseArrayLayer = 0;
|
||||
srcViewDesc.arrayLayerCount = arrayLayerCount;
|
||||
|
||||
wgpu::BindGroupEntry srcEntry{};
|
||||
srcEntry.binding = 0;
|
||||
srcEntry.textureView = srcTexture.CreateView(&srcViewDesc);
|
||||
mipEntries.push_back(srcEntry);
|
||||
uint32_t arrayLayersThisPass =
|
||||
std::min(m_maxArrayLayers, totalSrcArrayLayers - currentBaseArrayLayer);
|
||||
|
||||
for (uint32_t i = 0; i < numMips; ++i) {
|
||||
wgpu::TextureViewDescriptor dstViewDesc{};
|
||||
dstViewDesc.dimension = wgpu::TextureViewDimension::e2DArray;
|
||||
dstViewDesc.baseMipLevel = config.sourceMipLevel + i + 1;
|
||||
dstViewDesc.mipLevelCount = 1;
|
||||
dstViewDesc.baseArrayLayer = 0;
|
||||
dstViewDesc.arrayLayerCount = arrayLayerCount;
|
||||
|
||||
wgpu::BindGroupEntry dstEntry{};
|
||||
dstEntry.binding = i + 1;
|
||||
dstEntry.textureView = target.CreateView(&dstViewDesc);
|
||||
mipEntries.push_back(dstEntry);
|
||||
uint32_t mipsToGenerateCount = config.numMips - (config.sourceMipLevel + 1);
|
||||
uint32_t currentSourceMipLevel = config.sourceMipLevel;
|
||||
|
||||
while (mipsToGenerateCount > 0) {
|
||||
uint32_t mipsThisPass = std::min(mipsToGenerateCount, m_maxMipsPerPass);
|
||||
|
||||
SPDPassConfig passSpecificConfig = {};
|
||||
passSpecificConfig.filter = config.filter;
|
||||
passSpecificConfig.halfPrecision = config.halfPrecision;
|
||||
passSpecificConfig.targetTexture =
|
||||
config.targetTexture ? config.targetTexture : srcTexture;
|
||||
passSpecificConfig.numMips = mipsThisPass;
|
||||
passSpecificConfig.sourceMipLevel = currentSourceMipLevel;
|
||||
|
||||
// Pass baseArrayLayer and numArrayLayers directly to generatePass
|
||||
generatePass(commandEncoder, srcTexture, passSpecificConfig, currentBaseArrayLayer,
|
||||
arrayLayersThisPass);
|
||||
|
||||
mipsToGenerateCount -= mipsThisPass;
|
||||
currentSourceMipLevel += mipsThisPass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::BindGroupDescriptor mipBindGroupDesc{};
|
||||
mipBindGroupDesc.layout = spdPipeline.mipsBindGroupLayout;
|
||||
mipBindGroupDesc.entryCount = mipEntries.size();
|
||||
mipBindGroupDesc.entries = mipEntries.data();
|
||||
wgpu::BindGroup mipsBindGroup = m_device.CreateBindGroup(&mipBindGroupDesc);
|
||||
void MipmapGenerator::generatePass(wgpu::CommandEncoder& commandEncoder, wgpu::Texture srcTexture,
|
||||
const SPDPassConfig& passConfig, uint32_t baseArrayLayer, uint32_t numArrayLayers) {
|
||||
uint32_t width = srcTexture.GetWidth() >> passConfig.sourceMipLevel;
|
||||
uint32_t height = srcTexture.GetHeight() >> passConfig.sourceMipLevel;
|
||||
|
||||
// --- Create Bind Group 1 (Internal Resources) ---
|
||||
uint32_t numWorkGroupsX = (width + 63) / 64;
|
||||
uint32_t numWorkGroupsY = (height + 63) / 64;
|
||||
uint32_t numWorkGroups = numWorkGroupsX * numWorkGroupsY;
|
||||
uint32_t arrayLayerCountForThisPass = numArrayLayers;
|
||||
|
||||
struct DownsamplePassMeta {
|
||||
uint32_t work_group_offset[2] = {0, 0};
|
||||
uint32_t num_work_groups;
|
||||
uint32_t mips;
|
||||
uint32_t padding[12]; // Ensure size is multiple of 16
|
||||
} meta;
|
||||
meta.num_work_groups = numWorkGroups;
|
||||
meta.mips = numMips;
|
||||
|
||||
wgpu::BufferDescriptor metaBufferDesc{};
|
||||
metaBufferDesc.size = sizeof(DownsamplePassMeta);
|
||||
metaBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
|
||||
wgpu::Buffer metaBuffer = m_device.CreateBuffer(&metaBufferDesc);
|
||||
m_device.GetQueue().WriteBuffer(metaBuffer, 0, &meta, sizeof(meta));
|
||||
wgpu::Texture target = passConfig.targetTexture ? passConfig.targetTexture : srcTexture;
|
||||
uint32_t numMipsThisPass = passConfig.numMips;
|
||||
|
||||
if (numMipsThisPass == 0) return;
|
||||
|
||||
SPDScalarType scalarType = SanitizeScalarType(srcTexture.GetFormat(), passConfig.halfPrecision);
|
||||
SPDPipeline& spdPipeline = GetOrCreatePipeline(
|
||||
{ target.GetFormat(), scalarType, passConfig.filter, numMipsThisPass });
|
||||
|
||||
// --- Create Bind Group 0 (Mips) ---
|
||||
std::vector<wgpu::BindGroupEntry> mipEntries;
|
||||
|
||||
wgpu::TextureViewDescriptor srcViewDesc{};
|
||||
srcViewDesc.dimension = wgpu::TextureViewDimension::e2DArray;
|
||||
srcViewDesc.baseMipLevel = passConfig.sourceMipLevel;
|
||||
srcViewDesc.mipLevelCount = 1;
|
||||
srcViewDesc.baseArrayLayer = baseArrayLayer;
|
||||
srcViewDesc.arrayLayerCount = arrayLayerCountForThisPass;
|
||||
|
||||
wgpu::BindGroupEntry srcEntry{};
|
||||
srcEntry.binding = 0;
|
||||
srcEntry.textureView = srcTexture.CreateView(&srcViewDesc);
|
||||
mipEntries.push_back(srcEntry);
|
||||
|
||||
for (uint32_t i = 0; i < numMipsThisPass; ++i) {
|
||||
wgpu::TextureViewDescriptor dstViewDesc{};
|
||||
dstViewDesc.dimension = wgpu::TextureViewDimension::e2DArray;
|
||||
dstViewDesc.baseMipLevel = passConfig.sourceMipLevel + i + 1;
|
||||
dstViewDesc.mipLevelCount = 1;
|
||||
dstViewDesc.baseArrayLayer = baseArrayLayer;
|
||||
dstViewDesc.arrayLayerCount = arrayLayerCountForThisPass;
|
||||
|
||||
wgpu::BindGroupEntry dstEntry{};
|
||||
dstEntry.binding = i + 1;
|
||||
dstEntry.textureView = target.CreateView(&dstViewDesc);
|
||||
mipEntries.push_back(dstEntry);
|
||||
}
|
||||
|
||||
wgpu::BindGroupDescriptor mipBindGroupDesc{};
|
||||
mipBindGroupDesc.layout = spdPipeline.mipsBindGroupLayout;
|
||||
mipBindGroupDesc.entryCount = mipEntries.size();
|
||||
mipBindGroupDesc.entries = mipEntries.data();
|
||||
wgpu::BindGroup mipsBindGroup = m_device.CreateBindGroup(&mipBindGroupDesc);
|
||||
|
||||
// --- Create Bind Group 1 (Internal Resources) ---
|
||||
uint32_t widthDiv64 = (width + 63) / 64;
|
||||
uint32_t heightDiv64 = (height + 63) / 64;
|
||||
uint32_t numWorkGroups = widthDiv64 * heightDiv64;
|
||||
|
||||
struct DownsamplePassMeta {
|
||||
uint32_t work_group_offset[2] = { 0, 0 };
|
||||
uint32_t num_work_groups;
|
||||
uint32_t mips;
|
||||
uint32_t padding[12];
|
||||
} meta;
|
||||
meta.num_work_groups = numWorkGroups;
|
||||
meta.mips = numMipsThisPass;
|
||||
|
||||
wgpu::BufferDescriptor metaBufferDesc{};
|
||||
metaBufferDesc.size = sizeof(DownsamplePassMeta);
|
||||
metaBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
|
||||
wgpu::Buffer metaBuffer = m_device.CreateBuffer(&metaBufferDesc);
|
||||
m_device.GetQueue().WriteBuffer(metaBuffer, 0, &meta, sizeof(meta));
|
||||
|
||||
wgpu::BindGroup internalBindGroup;
|
||||
wgpu::BindGroupLayout chosenInternalBGL =
|
||||
(numMipsThisPass > 6 && m_internalResourcesBindGroupLayout_Advanced)
|
||||
? m_internalResourcesBindGroupLayout_Advanced
|
||||
: m_internalResourcesBindGroupLayout;
|
||||
|
||||
if (numMipsThisPass > 6 && m_internalResourcesBindGroupLayout_Advanced) {
|
||||
wgpu::BufferDescriptor atomicCounterBufferDesc{};
|
||||
atomicCounterBufferDesc.size = sizeof(uint32_t) * arrayLayerCountForThisPass;
|
||||
atomicCounterBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst;
|
||||
wgpu::Buffer atomicCounterBuffer = m_device.CreateBuffer(&atomicCounterBufferDesc);
|
||||
|
||||
commandEncoder.ClearBuffer(atomicCounterBuffer, 0, WGPU_WHOLE_SIZE);
|
||||
|
||||
wgpu::BufferDescriptor midMipBufferDesc{};
|
||||
midMipBufferDesc.size = 64 * 64 * 16 * arrayLayerCountForThisPass;
|
||||
midMipBufferDesc.usage = wgpu::BufferUsage::Storage;
|
||||
wgpu::Buffer midMipBuffer = m_device.CreateBuffer(&midMipBufferDesc);
|
||||
|
||||
std::vector<wgpu::BindGroupEntry> internalEntries(3);
|
||||
internalEntries[0].binding = 0;
|
||||
internalEntries[0].buffer = metaBuffer;
|
||||
internalEntries[0].size = sizeof(DownsamplePassMeta);
|
||||
|
||||
internalEntries[1].binding = 1;
|
||||
internalEntries[1].buffer = atomicCounterBuffer;
|
||||
internalEntries[1].size = atomicCounterBuffer.GetSize();
|
||||
|
||||
internalEntries[2].binding = 2;
|
||||
internalEntries[2].buffer = midMipBuffer;
|
||||
internalEntries[2].size = midMipBuffer.GetSize();
|
||||
|
||||
wgpu::BindGroupDescriptor internalBindGroupDesc{};
|
||||
internalBindGroupDesc.layout = chosenInternalBGL;
|
||||
internalBindGroupDesc.entryCount = static_cast<uint32_t>(internalEntries.size());
|
||||
internalBindGroupDesc.entries = internalEntries.data();
|
||||
internalBindGroup = m_device.CreateBindGroup(&internalBindGroupDesc);
|
||||
} else {
|
||||
wgpu::BindGroupEntry metaEntry{};
|
||||
metaEntry.binding = 0;
|
||||
metaEntry.buffer = metaBuffer;
|
||||
|
||||
metaEntry.size = sizeof(DownsamplePassMeta);
|
||||
|
||||
wgpu::BindGroupDescriptor internalBindGroupDesc{};
|
||||
internalBindGroupDesc.layout = m_internalResourcesBindGroupLayout;
|
||||
internalBindGroupDesc.layout = chosenInternalBGL;
|
||||
internalBindGroupDesc.entryCount = 1;
|
||||
internalBindGroupDesc.entries = &metaEntry;
|
||||
wgpu::BindGroup internalBindGroup = m_device.CreateBindGroup(&internalBindGroupDesc);
|
||||
|
||||
// --- Dispatch ---
|
||||
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
|
||||
pass.SetPipeline(spdPipeline.pipeline);
|
||||
pass.SetBindGroup(0, mipsBindGroup);
|
||||
pass.SetBindGroup(1, internalBindGroup);
|
||||
pass.DispatchWorkgroups(numWorkGroupsX, numWorkGroupsY, arrayLayerCount);
|
||||
pass.End();
|
||||
internalBindGroup = m_device.CreateBindGroup(&internalBindGroupDesc);
|
||||
}
|
||||
|
||||
// Main shader generation logic
|
||||
// --- Dispatch ---
|
||||
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
|
||||
pass.SetPipeline(spdPipeline.pipeline);
|
||||
pass.SetBindGroup(0, mipsBindGroup);
|
||||
pass.SetBindGroup(1, internalBindGroup);
|
||||
pass.DispatchWorkgroups(widthDiv64, heightDiv64, arrayLayerCountForThisPass);
|
||||
pass.End();
|
||||
}
|
||||
// Main shader generation logic
|
||||
|
||||
// Helper function to check if a string is in a vector of strings
|
||||
bool includes(const std::vector<std::string>& vec, const std::string& str) {
|
||||
for (const auto& s : vec) {
|
||||
for (const auto& s: vec) {
|
||||
if (s == str) {
|
||||
return true;
|
||||
}
|
||||
@@ -259,10 +408,9 @@ bool includes(const std::vector<std::string>& vec, const std::string& str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
|
||||
const std::string& filterOp,
|
||||
unsigned int numMips, // Assuming a default value for numMips
|
||||
SPDScalarType scalarType) { // Default scalarType
|
||||
std::string MakeShaderCode(wgpu::TextureFormat outputFormat, const std::string& filterOp,
|
||||
unsigned int numMips, // Assuming a default value for numMips
|
||||
SPDScalarType scalarType) { // Default scalarType
|
||||
std::stringstream ss;
|
||||
|
||||
std::string texelType;
|
||||
@@ -277,7 +425,7 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
|
||||
bool useF16 = (scalarType == SPDScalarType::F16);
|
||||
|
||||
std::string filterCode = filterOp;
|
||||
if (filterOp == SPD_FILTER_AVERAGE && !includes({"f32", "f16"}, texelType)) {
|
||||
if (filterOp == SPD_FILTER_AVERAGE && !includes({ "f32", "f16" }, texelType)) {
|
||||
// Replace "* 0.25" with "/ 4"
|
||||
size_t pos = filterCode.find("* 0.25");
|
||||
if (pos != std::string::npos) {
|
||||
@@ -288,7 +436,9 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
|
||||
// Generate mipsBindings
|
||||
std::string mipsBindings;
|
||||
for (unsigned int i = 0; i < numMips; ++i) {
|
||||
mipsBindings += "@group(0) @binding(" + std::to_string(i + 1) + ") var dst_mip_" + std::to_string(i + 1) + ": texture_storage_2d_array<" + to_string(outputFormat) + ", write>;\n";
|
||||
mipsBindings += "@group(0) @binding(" + std::to_string(i + 1) + ") var dst_mip_" +
|
||||
std::to_string(i + 1) + ": texture_storage_2d_array<" +
|
||||
to_string(outputFormat) + ", write>;\n";
|
||||
}
|
||||
|
||||
// Generate mipsAccessorBody
|
||||
@@ -296,7 +446,8 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
|
||||
for (unsigned int i = 0; i < numMips; ++i) {
|
||||
if (i == 5 && numMips > 6) {
|
||||
mipsAccessorBody += " else if mip == 6 {\n";
|
||||
mipsAccessorBody += " textureStore(dst_mip_6, uv, slice, " + (useF16 ? "vec4<" + texelType + ">(value)" : "value") + ");\n";
|
||||
mipsAccessorBody += " textureStore(dst_mip_6, uv, slice, " +
|
||||
(useF16 ? "vec4<" + texelType + ">(value)" : "value") + ");\n";
|
||||
mipsAccessorBody += " mip_dst_6_buffer[slice][uv.y][uv.x] = value;\n";
|
||||
mipsAccessorBody += " }";
|
||||
} else {
|
||||
@@ -304,12 +455,16 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
|
||||
mipsAccessorBody += " else ";
|
||||
}
|
||||
mipsAccessorBody += "if mip == " + std::to_string(i + 1) + " {\n";
|
||||
mipsAccessorBody += " textureStore(dst_mip_" + std::to_string(i + 1) + ", uv, slice, " + (useF16 ? "vec4<" + texelType + ">(value)" : "value") + ");\n";
|
||||
mipsAccessorBody += " textureStore(dst_mip_" + std::to_string(i + 1) +
|
||||
", uv, slice, " +
|
||||
(useF16 ? "vec4<" + texelType + ">(value)" : "value") + ");\n";
|
||||
mipsAccessorBody += " }";
|
||||
}
|
||||
}
|
||||
|
||||
std::string mipsAccessor = "fn store_dst_mip(value: vec4<SPDScalar>, uv: vec2<u32>, slice: u32, mip: u32) {\n" + mipsAccessorBody + "\n}";
|
||||
std::string mipsAccessor =
|
||||
"fn store_dst_mip(value: vec4<SPDScalar>, uv: vec2<u32>, slice: u32, mip: u32) {\n" +
|
||||
mipsAccessorBody + "\n}";
|
||||
std::string midMipAccessor = "return mip_dst_6_buffer[slice][uv.y][uv.x];";
|
||||
|
||||
// Start building the final shader code string
|
||||
@@ -343,7 +498,8 @@ std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
|
||||
if (useF16) {
|
||||
ss << "enable f16;\n";
|
||||
}
|
||||
ss << "alias SPDScalar = " << texelType << ";\n\n"; // Using texelType here, assuming SPDScalar maps to it.
|
||||
ss << "alias SPDScalar = " << texelType
|
||||
<< ";\n\n"; // Using texelType here, assuming SPDScalar maps to it.
|
||||
|
||||
ss << R"(
|
||||
// Helpers ------------------------------------------------------------------------------------------------------------
|
||||
@@ -400,8 +556,10 @@ struct DownsamplePassMeta {
|
||||
|
||||
// In the original version dst_mip_i is an image2Darray [SPD_MAX_MIP_LEVELS+1], i.e., 12+1, but WGSL doesn't support arrays of textures yet
|
||||
// Also these are read_write because for mips 7-13, the workgroup reads from mip level 6 - since most formats don't support read_write access in WGSL yet, we use a single read_write buffer in such cases instead
|
||||
@group(0) @binding(0) var src_mip_0: texture_2d_array<)" << texelType << R"(>;
|
||||
)" << mipsBindings << R"(
|
||||
@group(0) @binding(0) var src_mip_0: texture_2d_array<)"
|
||||
<< texelType << R"(>;
|
||||
)" << mipsBindings
|
||||
<< R"(
|
||||
|
||||
@group(1) @binding(0) var<uniform> downsample_pass_meta : DownsamplePassMeta;
|
||||
@group(1) @binding(1) var<storage, read_write> spd_global_counter: array<atomic<u32>>;
|
||||
@@ -433,7 +591,8 @@ fn load_mid_mip_image(uv: vec2<u32>, slice: u32) -> vec4<SPDScalar> {
|
||||
ss << R"(
|
||||
}
|
||||
|
||||
)" << mipsAccessor << R"(
|
||||
)" << mipsAccessor
|
||||
<< R"(
|
||||
|
||||
// Workgroup -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -471,7 +630,8 @@ fn spd_exit_workgroup(num_work_groups: u32, local_invocation_index: u32, slice:
|
||||
|
||||
// Pixel access --------------------------------------------------------------------------------------------------------
|
||||
|
||||
)" << filterCode << R"(
|
||||
)" << filterCode
|
||||
<< R"(
|
||||
|
||||
fn spd_store(pix: vec2<u32>, out_value: vec4<SPDScalar>, mip: u32, slice: u32) {
|
||||
store_dst_mip(out_value, pix, slice, mip + 1);
|
||||
@@ -770,7 +930,8 @@ fn spd_downsample(workgroup_id: vec2<u32>, local_invocation_index: u32, mips: u3
|
||||
spd_downsample_next_four(xy.x, xy.y, workgroup_id, local_invocation_index, 2, mips, slice);
|
||||
)";
|
||||
if (numMips > 6) {
|
||||
ss << " spd_downsample_last_6(xy.x, xy.y, local_invocation_index, mips, num_work_groups, slice);\n";
|
||||
ss << " spd_downsample_last_6(xy.x, xy.y, local_invocation_index, mips, "
|
||||
"num_work_groups, slice);\n";
|
||||
}
|
||||
ss << R"(}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <tsl/robin_map.h>
|
||||
#include <vector>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
// C++ port of https://github.com/JolifantoBambla/webgpu-spd for early experiments
|
||||
namespace spd {
|
||||
|
||||
// Enum for selecting the downsampling filter.
|
||||
enum class SPDFilter { Average, Min, Max, MinMax };
|
||||
|
||||
// Enum for shader scalar types.
|
||||
enum class SPDScalarType { F32, F16, I32, U32 };
|
||||
|
||||
// Configuration for a single mipmap generation pass.
|
||||
struct SPDPassConfig {
|
||||
SPDFilter filter = SPDFilter::Average;
|
||||
wgpu::Texture targetTexture = nullptr;
|
||||
uint32_t numMips = 0; // For the public API, this is total desired mips. Internally, mips for
|
||||
// the current pass.
|
||||
bool halfPrecision = false;
|
||||
uint32_t sourceMipLevel = 0;
|
||||
};
|
||||
|
||||
// Holds a pipeline and its corresponding bind group layout.
|
||||
struct SPDPipeline {
|
||||
wgpu::BindGroupLayout mipsBindGroupLayout = nullptr;
|
||||
wgpu::ComputePipeline pipeline = nullptr;
|
||||
};
|
||||
|
||||
// Manages pipeline creation, caching, and execution for mipmap generation.
|
||||
class MipmapGenerator {
|
||||
public:
|
||||
MipmapGenerator(const wgpu::Device& device);
|
||||
|
||||
// Pre-creates pipelines for specified formats and filters.
|
||||
void PreparePipelines(wgpu::TextureFormat format, SPDFilter filter, bool halfPrecision = false);
|
||||
|
||||
// Generates a compute pass for creating mipmaps.
|
||||
void Generate(wgpu::CommandEncoder& commandEncoder, wgpu::Texture srcTexture,
|
||||
const SPDPassConfig& config);
|
||||
|
||||
private:
|
||||
void generatePass(wgpu::CommandEncoder& commandEncoder, wgpu::Texture srcTexture,
|
||||
const SPDPassConfig& passConfig, uint32_t baseArrayLayer,
|
||||
uint32_t numArrayLayers); // Internal pass config might differ slightly if needed
|
||||
|
||||
wgpu::Device m_device;
|
||||
wgpu::BindGroupLayout m_internalResourcesBindGroupLayout;
|
||||
wgpu::BindGroupLayout m_internalResourcesBindGroupLayout_Advanced = nullptr;
|
||||
|
||||
// Maximum number of mips that can be generated in a single pass.
|
||||
uint32_t m_maxMipsPerPass;
|
||||
uint32_t m_maxArrayLayers;
|
||||
|
||||
// Key for the pipeline cache.
|
||||
struct PipelineCacheKey {
|
||||
wgpu::TextureFormat format;
|
||||
SPDScalarType scalarType;
|
||||
SPDFilter filter;
|
||||
uint32_t numMips;
|
||||
|
||||
bool operator==(const PipelineCacheKey& other) const {
|
||||
return format == other.format && scalarType == other.scalarType &&
|
||||
filter == other.filter && numMips == other.numMips;
|
||||
}
|
||||
};
|
||||
|
||||
// Hash function for PipelineCacheKey.
|
||||
struct PipelineCacheKeyHash {
|
||||
std::size_t operator()(const PipelineCacheKey& key) const {
|
||||
std::size_t h1 = std::hash<wgpu::TextureFormat>()(key.format);
|
||||
std::size_t h2 = std::hash<SPDScalarType>()(key.scalarType);
|
||||
std::size_t h3 = std::hash<SPDFilter>()(key.filter);
|
||||
std::size_t h4 = std::hash<uint32_t>()(key.numMips);
|
||||
// A simple way to combine hashes.
|
||||
return h1 ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3);
|
||||
}
|
||||
};
|
||||
|
||||
// Cached pipelines.
|
||||
tsl::robin_map<PipelineCacheKey, SPDPipeline, PipelineCacheKeyHash> m_pipelines;
|
||||
|
||||
// Helper methods
|
||||
SPDPipeline& GetOrCreatePipeline(const PipelineCacheKey& key);
|
||||
SPDScalarType SanitizeScalarType(wgpu::TextureFormat format, bool halfPrecision);
|
||||
std::string GetFilterCode(SPDFilter filter);
|
||||
};
|
||||
// Assuming SPD_FILTER_AVERAGE is a string constant
|
||||
const std::string SPD_FILTER_AVERAGE = "value * 0.25"; // Original filter operation
|
||||
|
||||
// Generates the WGSL shader code dynamically.
|
||||
std::string MakeShaderCode(wgpu::TextureFormat outputFormat,
|
||||
const std::string& filterOp = SPD_FILTER_AVERAGE, uint32_t numMips = 0,
|
||||
SPDScalarType scalarType = SPDScalarType::F32);
|
||||
|
||||
} // namespace spd
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Lukas Herzberger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Logger.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -32,8 +32,8 @@ constexpr size_t WEBGPU_BUFFER_SIZE_MODULUS = 4;
|
||||
#define FWGPU_PRINT_SYSTEM 0x00000002
|
||||
|
||||
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
|
||||
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
|
||||
// order of calls).
|
||||
// to debug with print statements and want ordered logging (e.g LOG(INFO) and LOG(ERROR) will not
|
||||
// appear in order of calls).
|
||||
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
|
||||
|
||||
// Useful default combinations
|
||||
@@ -54,15 +54,15 @@ constexpr size_t WEBGPU_BUFFER_SIZE_MODULUS = 4;
|
||||
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
|
||||
#define FWGPU_LOGI (utils::slog.i)
|
||||
#define FWGPU_LOGI LOG(INFO)
|
||||
#define FWGPU_LOGD FWGPU_LOGI
|
||||
#define FWGPU_LOGE FWGPU_LOGI
|
||||
#define FWGPU_LOGW FWGPU_LOGI
|
||||
#else
|
||||
#define FWGPU_LOGE (utils::slog.e)
|
||||
#define FWGPU_LOGW (utils::slog.w)
|
||||
#define FWGPU_LOGD (utils::slog.d)
|
||||
#define FWGPU_LOGI (utils::slog.i)
|
||||
#define FWGPU_LOGE LOG(ERROR)
|
||||
#define FWGPU_LOGW LOG(WARNING)
|
||||
#define FWGPU_LOGD DLOG(INFO)
|
||||
#define FWGPU_LOGI LOG(INFO)
|
||||
#endif
|
||||
|
||||
constexpr uint64_t REQUEST_ADAPTER_TIMEOUT_NANOSECONDS =
|
||||
|
||||
@@ -117,10 +117,10 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
samplerEntryInfo.binding = samplerEntry.binding;
|
||||
samplerEntry.visibility = wEntry.visibility;
|
||||
wEntry.texture.multisampled = isMultiSampledTypeDescriptor(fEntry.type);
|
||||
// TODO: Set once we have the filtering values
|
||||
if (isDepthDescriptor(fEntry.type)) {
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::Comparison;
|
||||
} else if (isIntDescriptor(fEntry.type)) {
|
||||
} else if (isIntDescriptor(fEntry.type) ||
|
||||
any(fEntry.flags & DescriptorFlags::UNFILTERABLE)) {
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::NonFiltering;
|
||||
} else {
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::Filtering;
|
||||
@@ -151,8 +151,11 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
if (isDepthDescriptor(fEntry.type)) {
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Depth;
|
||||
} else if (isFloatDescriptor(fEntry.type)) {
|
||||
// TODO: Set once we have the filtering values
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Float;
|
||||
if (any(fEntry.flags & DescriptorFlags::UNFILTERABLE)) {
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
|
||||
} else {
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Float;
|
||||
}
|
||||
} else if (isIntDescriptor(fEntry.type)) {
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Sint;
|
||||
} else if (isUnsignedIntDescriptor(fEntry.type)) {
|
||||
|
||||
@@ -18,17 +18,19 @@
|
||||
#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"
|
||||
#include "WebGPUVertexBufferInfo.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
#include "SpdMipmapGenerator.h"
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
@@ -41,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>
|
||||
|
||||
@@ -53,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 {
|
||||
@@ -64,13 +68,15 @@ Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverCon
|
||||
|
||||
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
|
||||
const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(platform),
|
||||
mHandleAllocator("Handles", driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags) {
|
||||
mAdapter = mPlatform.requestAdapter(nullptr);
|
||||
mDevice = mPlatform.requestDevice(mAdapter);
|
||||
: mPlatform{ platform },
|
||||
mAdapter{ mPlatform.requestAdapter(nullptr) },
|
||||
mDevice{ mPlatform.requestDevice(mAdapter) },
|
||||
mQueue{ mDevice.GetQueue() },
|
||||
mRenderPassMipmapGenerator{ mDevice },
|
||||
mSpdComputePassMipmapGenerator{ mDevice },
|
||||
mHandleAllocator{ "Handles", driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags } {
|
||||
mDevice.GetLimits(&mDeviceLimits);
|
||||
mQueue = mDevice.GetQueue();
|
||||
}
|
||||
|
||||
WebGPUDriver::~WebGPUDriver() noexcept = default;
|
||||
@@ -100,6 +106,7 @@ void WebGPUDriver::terminate() {
|
||||
|
||||
void WebGPUDriver::tick(int) {
|
||||
mDevice.Tick();
|
||||
mAdapter.GetInstance().ProcessEvents();
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
@@ -261,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 {
|
||||
@@ -334,17 +343,14 @@ 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."
|
||||
<< utils::io::endl;
|
||||
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
|
||||
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
|
||||
<< FWGPU_PRINT_SYSTEM << utils::io::dec
|
||||
char printSystemHex[16];
|
||||
snprintf(printSystemHex, sizeof(printSystemHex), "%#x", FWGPU_PRINT_SYSTEM);
|
||||
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << printSystemHex
|
||||
<< " bit flag on during build time the application would print system details "
|
||||
"about the selected graphics device, surface, etc. To see this try "
|
||||
"rebuilding Filament with that flag, e.g. ./build.sh -x "
|
||||
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
|
||||
<< FWGPU_PRINT_SYSTEM << " ...";
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -448,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) {}
|
||||
@@ -509,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
|
||||
@@ -529,8 +548,8 @@ bool WebGPUDriver::isTextureSwizzleSupported() {
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureFormatMipmappable(const TextureFormat format) {
|
||||
//todo
|
||||
return true;
|
||||
return WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(
|
||||
WebGPUTexture::fToWGPUTextureFormat(format));
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isRenderTargetFormatSupported(const TextureFormat format) {
|
||||
@@ -605,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,
|
||||
@@ -692,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 };
|
||||
|
||||
@@ -743,58 +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.";
|
||||
// FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::StorageBinding)
|
||||
// << "Texture for mipmap generation must have StorageBinding usage.";
|
||||
|
||||
const uint32_t totalMipLevels = wgpuTexture.GetMipLevelCount();
|
||||
if (totalMipLevels <= 1) {
|
||||
return;
|
||||
return; // nothing to do
|
||||
}
|
||||
|
||||
// Determine the maximum number of mips we can generate in a single pass.
|
||||
// The limit is on *storage* textures, and we need 1 binding for the source texture.
|
||||
const uint32_t maxMipsPerPass = 2;//mDeviceLimits.maxStorageTexturesPerShaderStage - 1;
|
||||
FILAMENT_CHECK_POSTCONDITION(maxMipsPerPass > 0)
|
||||
<< "Device does not support enough storage textures for mipmapping.";
|
||||
const auto usage = wgpuTexture.GetUsage();
|
||||
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::TextureBinding)
|
||||
<< "Texture for mipmap generation must have TextureBinding usage.";
|
||||
|
||||
// The generator can be created once.
|
||||
spd::MipmapGenerator mipmapGenerator(mDevice);
|
||||
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);
|
||||
|
||||
// We will record all passes into a single command encoder.
|
||||
wgpu::CommandEncoderDescriptor encoderDesc = {};
|
||||
encoderDesc.label = "Mipmap Command Encoder";
|
||||
wgpu::CommandEncoder encoder = mDevice.CreateCommandEncoder(&encoderDesc);
|
||||
spd::SPDPassConfig spdConfig = { .filter = spd::SPDFilter::Average,
|
||||
.targetTexture = wgpuTexture,
|
||||
.numMips = totalMipLevels,
|
||||
.halfPrecision = false,
|
||||
.sourceMipLevel = 0 };
|
||||
|
||||
uint32_t mipsLeftToGenerate = totalMipLevels - 1;
|
||||
uint32_t currentSourceMipLevel = 0;
|
||||
mSpdComputePassMipmapGenerator.Generate(encoder, wgpuTexture, spdConfig);
|
||||
|
||||
while (mipsLeftToGenerate > 0) {
|
||||
uint32_t mipsThisPass = std::min(mipsLeftToGenerate, maxMipsPerPass);
|
||||
|
||||
spd::SPDPassConfig config = {};
|
||||
config.filter = spd::SPDFilter::Average;
|
||||
config.targetTexture = wgpuTexture;
|
||||
config.numMips = mipsThisPass;
|
||||
config.sourceMipLevel = currentSourceMipLevel;
|
||||
|
||||
// The generator needs to create pipelines for the number of mips in THIS pass.
|
||||
mipmapGenerator.PreparePipelines(wgpuTexture.GetFormat(), config.filter);
|
||||
|
||||
// Generate one batch of mipmaps.
|
||||
mipmapGenerator.Generate(encoder, wgpuTexture, config);
|
||||
|
||||
mipsLeftToGenerate -= mipsThisPass;
|
||||
currentSourceMipLevel += mipsThisPass;
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
@@ -811,19 +822,39 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
|
||||
wgpu::TextureView defaultColorView = nullptr;
|
||||
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()) {
|
||||
assert_invariant(mSwapChain && mTextureView);
|
||||
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();
|
||||
@@ -838,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->getWebGPUFormat();
|
||||
}
|
||||
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->getWebGPUFormat();
|
||||
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.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -882,11 +950,10 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
defaultColorView,
|
||||
defaultDepthStencilView,
|
||||
customColorViews.data(),
|
||||
customColorMsaaSidecarViews.data(),
|
||||
customColorViewCount,
|
||||
customDepthView,
|
||||
customStencilView,
|
||||
customDepthFormat,
|
||||
customStencilFormat);
|
||||
customDepthStencilView,
|
||||
customDepthStencilMsaaSidecarTextureView);
|
||||
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||
|
||||
@@ -921,16 +988,26 @@ 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"
|
||||
};
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
if (!mMipQueue.empty()) {
|
||||
for (auto& handle: mMipQueue) {
|
||||
generateMipmaps(handle);
|
||||
|
||||
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;
|
||||
}
|
||||
mMipQueue.clear();
|
||||
}
|
||||
mDefaultRenderTarget->setTargetFlags(newTargetFlags);
|
||||
|
||||
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = { .label = "frame_command_encoder" };
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
assert_invariant(mCommandEncoder);
|
||||
}
|
||||
|
||||
@@ -942,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);
|
||||
@@ -994,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,
|
||||
@@ -1004,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);
|
||||
@@ -1048,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) {
|
||||
@@ -1069,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.
|
||||
@@ -1095,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
@@ -29,6 +31,8 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include "SpdMipmapGenerator/SpdMipmapGenerator.h"
|
||||
#include <tsl/robin_map.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -53,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);
|
||||
@@ -70,14 +74,16 @@ private:
|
||||
WebGPUSwapChain* mSwapChain = nullptr;
|
||||
uint64_t mNextFakeHandle = 1;
|
||||
wgpu::CommandEncoder mCommandEncoder = nullptr;
|
||||
std::vector<Handle<HwTexture>> mMipQueue;
|
||||
wgpu::TextureView mTextureView = nullptr;
|
||||
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
|
||||
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
||||
WebGPURenderTarget* mDefaultRenderTarget = nullptr;
|
||||
WebGPURenderTarget* mCurrentRenderTarget = nullptr;
|
||||
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;
|
||||
@@ -85,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
|
||||
*/
|
||||
@@ -119,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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user